001package org.apache.fulcrum.yaafi.framework.factory;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.File;
023import java.io.IOException;
024import java.io.InputStream;
025import java.util.Enumeration;
026import java.util.Hashtable;
027
028import org.apache.avalon.framework.configuration.Configuration;
029import org.apache.avalon.framework.configuration.DefaultConfiguration;
030import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
031import org.apache.avalon.framework.context.Context;
032import org.apache.avalon.framework.context.DefaultContext;
033import org.apache.avalon.framework.logger.ConsoleLogger;
034import org.apache.avalon.framework.logger.Logger;
035import org.apache.avalon.framework.service.ServiceManager;
036import org.apache.commons.lang3.StringUtils;
037import org.apache.fulcrum.yaafi.framework.constant.AvalonMerlinConstants;
038import org.apache.fulcrum.yaafi.framework.container.ServiceConstants;
039import org.apache.fulcrum.yaafi.framework.crypto.CryptoStreamFactory;
040import org.apache.fulcrum.yaafi.framework.util.InputStreamLocator;
041import org.apache.fulcrum.yaafi.framework.util.Validate;
042
043/**
044 * Helper class to capture configuration related stuff. The are two ways for
045 * setting up the configuration:
046 * <ul>
047 * <li>set all parameters manually</li>
048 * <li>use a containerConfiguration file and provide the remaining settings</li>
049 * </ul>
050 *
051 * The Avalon context and configuration are created by
052 * <ul>
053 * <li>createFinalContext()</li>
054 * <li>createFinalConfiguration()</li>
055 * </ul>
056 *
057 * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
058 */
059
060public class ServiceContainerConfiguration {
061        /** our default implementation class of the service container */
062        private String serviceContainerClazzName;
063
064        /** the location of the component role file */
065        private String componentRolesLocation;
066
067        /** is the component role file encrypted? */
068        private String isComponentRolesEncrypted;
069
070        /** the location of the component configuration file */
071        private String componentConfigurationLocation;
072
073        /** is the component configuration file encrypted? */
074        private String isComponentConfigurationEncrypted;
075
076        /** the location of the paramaters file */
077        private String parametersLocation;
078
079        /** is the parameters file encrypted? */
080        private String isParametersEncrypted;
081
082        /** the user-supplied Avalon context */
083        private DefaultContext context;
084
085        /** the Avalon logger */
086        private Logger logger;
087
088        /** the application directory */
089        private String applicationRootDir;
090
091        /** the temporary directory */
092        private String tempRootDir;
093
094        /** the class loader passed in the Avalon Context */
095        private ClassLoader componentClassLoader;
096
097        /** the type of container where YAAFI is embedded */
098        private String containerFlavour;
099
100        /** the caller-supplied container configuration */
101        private Configuration containerConfiguration;
102
103        /** to lookup service in the parent container */
104        private ServiceManager parentServiceManager;
105
106        /** a list of ServiceManager maintaining their own services */
107        private String[] serviceManagerList;
108
109        /** Constructor */
110        public ServiceContainerConfiguration() {
111                this(ConsoleLogger.LEVEL_DEBUG);
112        }
113
114        /**
115         * Constructor.
116         *
117         * @param logLevel the log level for the console logger.
118         */
119        public ServiceContainerConfiguration(int logLevel) {
120                this.logger = new ConsoleLogger(logLevel);
121                this.containerFlavour = ServiceConstants.AVALON_CONTAINER_YAAFI;
122                this.serviceContainerClazzName = ServiceConstants.CLAZZ_NAME;
123                this.componentRolesLocation = ServiceConstants.COMPONENT_ROLE_VALUE;
124                this.isComponentRolesEncrypted = "false";
125                this.componentConfigurationLocation = ServiceConstants.COMPONENT_CONFIG_VALUE;
126                this.isComponentConfigurationEncrypted = "false";
127                this.parametersLocation = ServiceConstants.COMPONENT_PARAMETERS_VALUE;
128                this.isParametersEncrypted = "false";
129                this.context = new DefaultContext();
130                this.applicationRootDir = new File("").getAbsolutePath();
131                this.tempRootDir = System.getProperty("java.io.tmpdir", ".");
132                this.componentClassLoader = this.getClass().getClassLoader();
133        }
134
135        /**
136         * Add a new entry to the context by creating a new one.
137         * 
138         * @param name  the name of the new entry
139         * @param value the value of the new entry
140         */
141        public void addToContext(String name, Object value) {
142                Validate.notEmpty(name, "name");
143                Validate.notNull(value, "value");
144                this.getContext().put(name, value);
145        }
146
147        /**
148         * Add a hashtable to the context
149         * 
150         * @param hashtable the Hashtable to be added
151         */
152        public void addToContext(Hashtable<?, ?> hashtable) {
153                Validate.notNull(hashtable, "hashtable");
154
155                String name = null;
156                Object value = null;
157                Enumeration<?> keys = hashtable.keys();
158
159                while (keys.hasMoreElements()) {
160                        name = (String) keys.nextElement();
161                        value = hashtable.get(name);
162                        this.addToContext(name, value);
163                }
164        }
165
166        /**
167         * Create the final Avalon context passed to YAAFI containing
168         * <ul>
169         * <li>user-supplied context</li>
170         * <li>urn:avalon:home</li>
171         * <li>urn:avalon:temp</li>
172         * <li>urn:avalon:name</li>
173         * <li>urn:avalon:partition</li>
174         * <li>urn:avalon:classloader</li>
175         * </ul>
176         *
177         * @return the final Context
178         * @throws Exception if filename not defined
179         * @throws IOException if file not found
180         */
181
182        public Context createFinalContext() throws IOException, Exception {
183                // 1) add the application root dir
184
185                this.addToContext(AvalonMerlinConstants.URN_AVALON_HOME, this.getApplicationRootDir());
186
187                // 2) add the temp root dir
188
189                this.addToContext(AvalonMerlinConstants.URN_AVALON_TEMP, this.getTempRootDir());
190
191                // 3) add the Avalon name
192
193                this.addToContext(AvalonMerlinConstants.URN_AVALON_NAME, ServiceConstants.ROLE_NAME);
194
195                // 4) add the Avalon partition name
196
197                this.addToContext(AvalonMerlinConstants.URN_AVALON_PARTITION, "root");
198
199                // 5) add the class loader
200
201                this.addToContext(AvalonMerlinConstants.URN_AVALON_CLASSLOADER, this.getComponentClassLoader());
202
203                return this.getContext();
204        }
205
206        /**
207         * Create a final configuration.
208         *
209         * @return the configuration
210         */
211        public Configuration createFinalConfiguration() {
212                DefaultConfiguration result = null;
213
214                if (this.getContainerConfiguration() != null) {
215                        return this.getContainerConfiguration();
216                } else {
217                        // the root element is "fulcrum-yaafi"
218
219                        result = new DefaultConfiguration(ServiceConstants.ROLE_NAME);
220
221                        // add the following fragement
222                        //
223                        // <containerFlavour>merlin</containerFlavour>
224
225                        DefaultConfiguration containerFlavourConfig = new DefaultConfiguration(
226                                        ServiceConstants.CONTAINERFLAVOUR_CONFIG_KEY);
227
228                        containerFlavourConfig.setValue(this.getContainerFlavour());
229
230                        result.addChild(containerFlavourConfig);
231
232                        // add the following fragement
233                        //
234                        // <containerClazzName>...</containerClazzName>
235
236                        DefaultConfiguration containerClazzNameConfig = new DefaultConfiguration(
237                                        ServiceConstants.CONTAINERCLAZZNAME_CONFIG_KEY);
238
239                        containerClazzNameConfig.setValue(this.getServiceContainerClazzName());
240
241                        result.addChild(containerClazzNameConfig);
242
243                        // add the following fragement
244                        //
245                        // <componentRoles>
246                        // <location>../conf/componentRoles.xml</location>
247                        // <isEncrypted>true</isEncrypted>
248                        // </componentRoles>
249
250                        DefaultConfiguration componentRolesConfig = new DefaultConfiguration(ServiceConstants.COMPONENT_ROLE_KEYS);
251
252                        DefaultConfiguration componentRolesLocation = new DefaultConfiguration(
253                                        ServiceConstants.COMPONENT_LOCATION_KEY);
254
255                        componentRolesLocation.setValue(this.getComponentRolesLocation());
256
257                        DefaultConfiguration componentRolesIsEncrypted = new DefaultConfiguration(
258                                        ServiceConstants.COMPONENT_ISENCRYPTED_KEY);
259
260                        componentRolesIsEncrypted.setValue(this.isComponentRolesEncrypted());
261
262                        componentRolesConfig.addChild(componentRolesLocation);
263                        componentRolesConfig.addChild(componentRolesIsEncrypted);
264
265                        result.addChild(componentRolesConfig);
266
267                        // add the following fragement
268                        //
269                        // <componentConfiguration>
270                        // <location>../conf/componentRoles.xml</location>
271                        // <isEncrypted>true</isEncrypted>
272                        // </componentConfiguration>
273
274                        DefaultConfiguration componentConfigurationConfig = new DefaultConfiguration(
275                                        ServiceConstants.COMPONENT_CONFIG_KEY);
276
277                        DefaultConfiguration componentConfigurationLocation = new DefaultConfiguration(
278                                        ServiceConstants.COMPONENT_LOCATION_KEY);
279
280                        componentConfigurationLocation.setValue(this.getComponentConfigurationLocation());
281
282                        DefaultConfiguration componentConfigurationIsEncrypted = new DefaultConfiguration(
283                                        ServiceConstants.COMPONENT_ISENCRYPTED_KEY);
284
285                        componentConfigurationIsEncrypted.setValue(this.isComponentConfigurationEncrypted());
286
287                        componentConfigurationConfig.addChild(componentConfigurationLocation);
288                        componentConfigurationConfig.addChild(componentConfigurationIsEncrypted);
289
290                        result.addChild(componentConfigurationConfig);
291
292                        // Add the following fragement
293                        //
294                        // <parameters>
295                        // <location>../conf/parameters.properties</location>
296                        // <isEncrypted>true</isEncrypted>
297                        // </parameters>
298
299                        DefaultConfiguration parameterConfigurationConfig = new DefaultConfiguration(
300                                        ServiceConstants.COMPONENT_PARAMETERS_KEY);
301
302                        DefaultConfiguration parameterConfigurationLocation = new DefaultConfiguration(
303                                        ServiceConstants.COMPONENT_LOCATION_KEY);
304
305                        parameterConfigurationLocation.setValue(this.getParametersLocation());
306
307                        DefaultConfiguration parameterConfigurationIsEncrypted = new DefaultConfiguration(
308                                        ServiceConstants.COMPONENT_ISENCRYPTED_KEY);
309
310                        parameterConfigurationIsEncrypted.setValue(this.isParametersEncrypted());
311
312                        parameterConfigurationConfig.addChild(parameterConfigurationLocation);
313                        parameterConfigurationConfig.addChild(parameterConfigurationIsEncrypted);
314
315                        result.addChild(parameterConfigurationConfig);
316
317                        // Add the following fragement
318                        //
319                        // <serviceManagers>
320                        // <serviceManagers>springFrameworkService</serviceManager>
321                        // </serviceManagers>
322
323                        if (this.hasServiceManagerList()) {
324                                DefaultConfiguration serviceManagerListConfig = new DefaultConfiguration(
325                                                ServiceConstants.SERVICEMANAGER_LIST_KEY);
326
327                                for (int i = 0; i < this.serviceManagerList.length; i++) {
328                                        DefaultConfiguration serviceManagerConfig = new DefaultConfiguration(
329                                                        ServiceConstants.SERVICEMANAGER_KEY);
330
331                                        serviceManagerConfig.setValue(this.serviceManagerList[i]);
332
333                                        serviceManagerListConfig.addChild(serviceManagerConfig);
334                                }
335
336                                result.addChild(serviceManagerListConfig);
337                        }
338
339                        return result;
340                }
341        }
342
343        /////////////////////////////////////////////////////////////////////////
344        // Generated Getters/Setters
345        /////////////////////////////////////////////////////////////////////////
346
347        /**
348         * @return Returns the serviceContainerClazzName.
349         */
350        private String getServiceContainerClazzName() {
351                return this.serviceContainerClazzName;
352        }
353
354        /**
355         * @return Returns the componentConfigurationLocation.
356         */
357        private String getComponentConfigurationLocation() {
358                return componentConfigurationLocation;
359        }
360
361        /**
362         * @param componentConfigurationLocation The componentConfigurationLocation to
363         *                                       set.
364         */
365        public void setComponentConfigurationLocation(String componentConfigurationLocation) {
366                Validate.notNull(componentConfigurationLocation, "componentConfigurationLocation");
367                this.componentConfigurationLocation = componentConfigurationLocation;
368        }
369
370        /**
371         * @return Returns the componentRolesLocation.
372         */
373        private String getComponentRolesLocation() {
374                return componentRolesLocation;
375        }
376
377        /**
378         * @param componentRolesLocation The componentRolesLocation to set.
379         */
380        public void setComponentRolesLocation(String componentRolesLocation) {
381                this.componentRolesLocation = componentRolesLocation;
382        }
383
384        /**
385         * @return Returns the context.
386         */
387        private DefaultContext getContext() {
388                return context;
389        }
390
391        /**
392         * @param context The context to set.
393         */
394        public void setContext(Context context) {
395                if (context instanceof DefaultContext) {
396                        this.context = (DefaultContext) context;
397                } else {
398                        this.context = new DefaultContext(context);
399                }
400        }
401
402        /**
403         * @return Returns the isComponentConfigurationEncrypted.
404         */
405        private String isComponentConfigurationEncrypted() {
406                return isComponentConfigurationEncrypted;
407        }
408
409        /**
410         * @param isComponentConfigurationEncrypted The
411         *                                          isComponentConfigurationEncrypted to
412         *                                          set.
413         */
414        public void setComponentConfigurationEncrypted(String isComponentConfigurationEncrypted) {
415                this.isComponentConfigurationEncrypted = isComponentConfigurationEncrypted;
416        }
417
418        /**
419         * @return Returns the isComponentRolesEncrypted.
420         */
421        private String isComponentRolesEncrypted() {
422                return isComponentRolesEncrypted;
423        }
424
425        /**
426         * @param isComponentRolesEncrypted The isComponentRolesEncrypted to set.
427         */
428        public void setComponentRolesEncrypted(String isComponentRolesEncrypted) {
429                this.isComponentRolesEncrypted = isComponentRolesEncrypted;
430        }
431
432        /**
433         * @return Returns the isParametersEncrypted.
434         */
435        private String isParametersEncrypted() {
436                return isParametersEncrypted;
437        }
438
439        /**
440         * @param isParametersEncrypted The isParametersEncrypted to set.
441         */
442        public void setParametersEncrypted(String isParametersEncrypted) {
443                Validate.notEmpty(isParametersEncrypted, "isParametersEncrypted");
444                this.isParametersEncrypted = isParametersEncrypted;
445        }
446
447        /**
448         * @return Returns the logger.
449         */
450        public Logger getLogger() {
451                return logger;
452        }
453
454        /**
455         * @param logger The logger to set.
456         */
457        public void setLogger(Logger logger) {
458                this.logger = logger;
459        }
460
461        /**
462         * @return Returns the parametersLocation.
463         */
464        private String getParametersLocation() {
465                return parametersLocation;
466        }
467
468        /**
469         * @param parametersLocation The parametersLocation to set.
470         */
471        public void setParametersLocation(String parametersLocation) {
472                this.parametersLocation = parametersLocation;
473        }
474
475        /**
476         * @return Returns the applicationRootDir.
477         */
478        private File getApplicationRootDir() {
479                return new File(applicationRootDir);
480        }
481
482        /**
483         * @param applicationRootDir The applicationRootDir to set.
484         */
485        public void setApplicationRootDir(String applicationRootDir) {
486                Validate.notNull(applicationRootDir, "applicationRootDir");
487
488                if (applicationRootDir.equals(".")) {
489                        this.applicationRootDir = new File("").getAbsolutePath();
490                } else {
491                        this.applicationRootDir = new File(applicationRootDir).getAbsolutePath();
492                }
493        }
494
495        /**
496         * @return Returns the tempRootDir.
497         * @throws Exception 
498         * @throws IOException 
499         */
500        private File getTempRootDir() throws IOException, Exception {
501                return makeAbsoluteFile(this.getApplicationRootDir(), this.tempRootDir);
502        }
503
504        /**
505         * @param tempRootDir The tempRootDir to set.
506         */
507        public void setTempRootDir(String tempRootDir) {
508                Validate.notNull(tempRootDir, "tempRootDir");
509                this.tempRootDir = tempRootDir;
510        }
511
512        /**
513         * @return Returns the classLoader.
514         */
515        private ClassLoader getComponentClassLoader() {
516                return componentClassLoader;
517        }
518
519        /**
520         * @param componentClassLoader The classLoader to set.
521         */
522        public void setComponentClassLoader(ClassLoader componentClassLoader) {
523                Validate.notNull(componentClassLoader, "componentClassLoader");
524                this.componentClassLoader = componentClassLoader;
525        }
526
527        /**
528         * @return Returns the containerFlavour.
529         */
530        private String getContainerFlavour() {
531                return containerFlavour;
532        }
533
534        /**
535         * @param containerFlavour The containerFlavour to set.
536         */
537        public void setContainerFlavour(String containerFlavour) {
538                this.containerFlavour = containerFlavour;
539        }
540
541        /**
542         * @return Returns the containerConfiguration.
543         */
544        private Configuration getContainerConfiguration() {
545                return containerConfiguration;
546        }
547
548        /**
549         * @param containerConfiguration The containerConfiguration to set.
550         */
551        public void setContainerConfiguration(Configuration containerConfiguration) {
552                this.containerConfiguration = containerConfiguration;
553        }
554
555        /**
556         * Get the parent service manager to find service managed by the parent
557         * container.
558         *
559         * @return the parent container
560         */
561
562        public ServiceManager getParentServiceManager() {
563                return parentServiceManager;
564        }
565
566        /**
567         * Set the parent service manager to find service managed by the parent
568         * container.
569         *
570         * @param parentServiceManager the parent container
571         */
572        public void setParentServiceManager(ServiceManager parentServiceManager) {
573                this.parentServiceManager = parentServiceManager;
574        }
575
576        /**
577         * Get a list of service manager managing their own set of services.
578         *
579         * @return a list of service implementing the ServiceManager interface
580         */
581        public String[] getServiceManagerList() {
582                return serviceManagerList;
583        }
584
585        /**
586         * Set a list of service manager managing their own set of services.
587         *
588         * @param serviceManagerList a list of service implementing the ServiceManager
589         *                           interface
590         */
591        public void setServiceManagerList(String[] serviceManagerList) {
592                this.serviceManagerList = serviceManagerList;
593        }
594
595        /**
596         * @return true if there is a service manager defined
597         */
598        public boolean hasServiceManagerList() {
599                if (this.serviceManagerList != null && this.serviceManagerList.length > 0)
600                        return true;
601                return false;
602        }
603
604        /**
605         * Loads a containerConfiguration file and set is as the Avalon configuration to
606         * be used for Configurable.configure(). Take care that the implementation uses
607         * an InputStreamLocator to find the containerConfiguration which uses the
608         * previously set application root directory.
609         *
610         * @param location the location of the containerConfiguration
611         * @throws IOException loading the configuration failed
612         */
613        public void loadContainerConfiguration(String location) throws IOException {
614                this.loadContainerConfiguration(location, "false");
615        }
616
617        /**
618         * Loads a containerConfiguration file and set is as the Avalon configuration to
619         * be used for Configurable.configure(). Take care that the implementation uses
620         * an InputStreamLocator to find the containerConfiguration which uses the
621         * previously set application root directory.
622         *
623         * @param location    the location of the containerConfiguration
624         * @param isEncrypted is the file encrypted
625         * @throws IOException loading the configuration failed
626         */
627        public void loadContainerConfiguration(String location, String isEncrypted) throws IOException {
628                Configuration result = null;
629
630                InputStreamLocator locator = new InputStreamLocator(this.getApplicationRootDir(), this.getLogger());
631
632                DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
633                InputStream is = locator.locate(location);
634
635                if (is != null) {
636                        try {
637                                is = CryptoStreamFactory.getDecryptingInputStream(is, isEncrypted);
638                                result = builder.build(is);
639                                this.setContainerConfiguration(result);
640                        } catch (Exception e) {
641                                String msg = "Unable to parse the following file : " + location;
642                                this.getLogger().error(msg, e);
643                                throw new IOException(msg);
644                        }
645                } else {
646                        String msg = "Unable to locate the containerConfiguration file : " + location;
647                        this.getLogger().error(msg);
648                        throw new IOException(msg);
649                }
650        }
651
652        /**
653         * Determines the absolute file.
654         * 
655         * @param baseDir  the base directory
656         * @param fileName the filename
657         * @return the absolute path
658         */
659        private static File makeAbsoluteFile(File baseDir, String fileName) throws IOException, Exception {
660                if (baseDir.exists()) {
661                        if (!StringUtils.isEmpty(fileName)) {
662                                File result = new File(fileName);
663                                if (!result.isAbsolute()) {
664                                        result = new File(baseDir, fileName);
665                                }
666                                return result;
667                        } else {
668                                throw new Exception("No filename specified");
669                        }
670                } else {
671                        throw new IOException("The directory does not exist");
672                }
673        }
674}