001 package org.apache.fulcrum.yaafi.framework.container;
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
022 import java.io.File;
023 import java.io.InputStream;
024 import java.util.ArrayList;
025 import java.util.HashMap;
026 import java.util.List;
027 import java.util.Properties;
028
029 import org.apache.avalon.framework.configuration.Configuration;
030 import org.apache.avalon.framework.configuration.ConfigurationException;
031 import org.apache.avalon.framework.configuration.DefaultConfiguration;
032 import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
033 import org.apache.avalon.framework.container.ContainerUtil;
034 import org.apache.avalon.framework.context.Context;
035 import org.apache.avalon.framework.context.ContextException;
036 import org.apache.avalon.framework.context.DefaultContext;
037 import org.apache.avalon.framework.logger.Logger;
038 import org.apache.avalon.framework.parameters.ParameterException;
039 import org.apache.avalon.framework.parameters.Parameters;
040 import org.apache.avalon.framework.service.ServiceException;
041 import org.apache.avalon.framework.service.ServiceManager;
042 import org.apache.fulcrum.yaafi.framework.component.AvalonServiceComponentImpl;
043 import org.apache.fulcrum.yaafi.framework.component.ServiceComponent;
044 import org.apache.fulcrum.yaafi.framework.configuration.ComponentConfigurationPropertiesResolver;
045 import org.apache.fulcrum.yaafi.framework.configuration.ComponentConfigurationPropertiesResolverImpl;
046 import org.apache.fulcrum.yaafi.framework.constant.AvalonYaafiConstants;
047 import org.apache.fulcrum.yaafi.framework.context.AvalonToYaafiContextMapper;
048 import org.apache.fulcrum.yaafi.framework.context.YaafiToAvalonContextMapper;
049 import org.apache.fulcrum.yaafi.framework.role.RoleConfigurationParser;
050 import org.apache.fulcrum.yaafi.framework.role.RoleConfigurationParserImpl;
051 import org.apache.fulcrum.yaafi.framework.role.RoleEntry;
052 import org.apache.fulcrum.yaafi.framework.util.ConfigurationUtil;
053 import org.apache.fulcrum.yaafi.framework.util.InputStreamLocator;
054 import org.apache.fulcrum.yaafi.framework.util.StringUtils;
055 import org.apache.fulcrum.yaafi.framework.util.ToStringBuilder;
056 import org.apache.fulcrum.yaafi.framework.util.Validate;
057 import org.apache.fulcrum.yaafi.framework.crypto.CryptoStreamFactory;
058
059 /**
060 * Yet another avalon framework implementation (YAAFI).
061 *
062 * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
063 */
064
065 public class ServiceContainerImpl
066 implements ServiceContainer, ServiceConstants
067 {
068 /** the timeout before reconfiguring the container or services */
069 private static final int RECONFIGURATION_DELAY = 2000;
070
071 /** The role configuration file to be used */
072 private String componentRolesLocation;
073
074 /** is the component role file encrypted? */
075 private String isComponentRolesEncrypted;
076
077 /** which flavour of component role file we have to parse? */
078 private String componentRolesFlavour;
079
080 /** The service configuration file to be used */
081 private String componentConfigurationLocation;
082
083 /** is the component configuration file encrypted? */
084 private String isComponentConfigurationEncrypted;
085
086 /** The parameters file to be used */
087 private String parametersLocation;
088
089 /** is the parameters file encrypted? */
090 private String isParametersEncrypted;
091
092 /** The application directory aka the current woring directory */
093 private File applicationRootDir;
094
095 /** The directory for storing temporary files */
096 private File tempRootDir;
097
098 /** The logger to be used passed by the caller */
099 private Logger logger;
100
101 /** The service manager passed to the container */
102 private ServiceManager parentServiceManager;
103
104 /** The list of services instantiated */
105 private List serviceList;
106
107 /** The map of services used for the lookup */
108 private HashMap serviceMap;
109
110 /** The Avalon role configuration loaded by this class */
111 private Configuration roleConfiguration;
112
113 /** The Avalon service configuration loaded by this class */
114 private Configuration serviceConfiguration;
115
116 /** The temporary Avalon context passed to the implementation */
117 private Context callerContext;
118
119 /** The default Avalon context passed to the services */
120 private Context context;
121
122 /** The default Avalon parameters */
123 private Parameters parameters;
124
125 /** Is this instance already disposed? */
126 private boolean isDisposed;
127
128 /** The type of container where YAAFI is embedded */
129 private String containerFlavour;
130
131 /** The ms to wait before triggering a reconfiguration of the container os service */
132 private int reconfigurationDelay;
133
134 /** global flag for enabling/disabling dynamic proxies */
135 private boolean hasDynamicProxies;
136
137 /** The list of interceptor services applied to all services */
138 private ArrayList defaultInterceptorServiceList;
139
140 /** The list of ServiceManagers as fallback service lookup */
141 private ArrayList fallbackServiceManagerList;
142
143 /** the configuration for running the ComponentConfigurationPropertiesResolver */
144 private Configuration componentConfigurationPropertiesResolverConfig;
145
146 /////////////////////////////////////////////////////////////////////////
147 // Avalon Service Lifecycle
148 /////////////////////////////////////////////////////////////////////////
149
150 /**
151 * Constructor using sensible defaults.
152 */
153 public ServiceContainerImpl()
154 {
155 super();
156
157 this.reconfigurationDelay = RECONFIGURATION_DELAY;
158 this.containerFlavour = COMPONENT_CONTAINERFLAVOUR_VALUE;
159 this.componentRolesFlavour = COMPONENT_ROLECONFIGFLAVOUR_VALUE;
160
161 this.componentRolesLocation = COMPONENT_ROLE_VALUE;
162 this.componentConfigurationLocation = COMPONENT_CONFIG_VALUE;
163 this.parametersLocation = COMPONENT_PARAMETERS_VALUE;
164
165 this.isComponentConfigurationEncrypted = "false";
166 this.isComponentRolesEncrypted = "false";
167 this.isParametersEncrypted = "false";
168
169 this.isDisposed = false;
170 this.serviceList = new ArrayList();
171 this.serviceMap = new HashMap();
172
173 this.applicationRootDir = new File( new File("").getAbsolutePath() );
174 this.tempRootDir = new File( System.getProperty("java.io.tmpdir",".") );
175
176 this.fallbackServiceManagerList = new ArrayList();
177 this.defaultInterceptorServiceList = new ArrayList();
178
179 this.reconfigurationDelay = RECONFIGURATION_DELAY;
180 }
181
182 /**
183 * @see org.apache.avalon.framework.logger.LogEnabled#enableLogging(org.apache.avalon.framework.logger.Logger)
184 */
185 public void enableLogging(Logger logger)
186 {
187 Validate.notNull( logger, "logger" );
188 this.logger = logger;
189 }
190
191 /**
192 * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
193 */
194 public void contextualize(Context context) throws ContextException
195 {
196 Validate.notNull( context, "context" );
197 // Argghhh - I need to to parse the Configuration before I can map the Context
198 this.callerContext = context;
199 }
200
201
202 /**
203 * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
204 */
205 public void service(ServiceManager serviceManager) throws ServiceException
206 {
207 this.parentServiceManager = serviceManager;
208 }
209
210 /**
211 * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
212 */
213 public void configure(Configuration configuration) throws ConfigurationException
214 {
215 Validate.notNull( configuration, "configuration" );
216
217 // retrieve the reconfigurationDelay
218
219 this.reconfigurationDelay =
220 configuration.getChild(RECONFIGURATION_DELAY_KEY).getValueAsInteger(
221 RECONFIGURATION_DELAY
222 );
223
224 // evaluate if we are using dynamic proxies
225
226 this.hasDynamicProxies =
227 configuration.getChild(DYNAMICPROXY_ENABLED_KEY).getValueAsBoolean(false);
228
229 // retrieve the container flavour
230
231 this.setContainerFlavour(
232 configuration.getChild(CONTAINERFLAVOUR_CONFIG_KEY).getValue(
233 COMPONENT_CONTAINERFLAVOUR_VALUE )
234 );
235
236 this.getLogger().debug( "Using the following container type : " + this.getContainerFlavour() );
237
238 // process the caller-suppied context here
239
240 try
241 {
242 // instantiate a mapper using the existing context - it might
243 // contain application specific entries we are not aware of
244
245 AvalonToYaafiContextMapper mapper = new AvalonToYaafiContextMapper(
246 this.getTempRootDir(),
247 this.callerContext,
248 this.getClassLoader()
249 );
250
251 // do the magic mapping
252
253 this.context = mapper.mapFrom(
254 this.callerContext,
255 this.getContainerFlavour()
256 );
257
258 // don't keep a reference of the caller-supplide context
259
260 this.callerContext = null;
261 }
262 catch( ContextException e )
263 {
264 String msg = "Failed to parse the caller-supplied context";
265 this.getLogger().error( msg, e );
266 throw new ConfigurationException( msg );
267 }
268
269 // evaluate componentRoles
270
271 Configuration currComponentRoles = configuration.getChild(COMPONENT_ROLE_KEYS);
272
273 this.setComponentRolesLocation(
274 currComponentRoles.getChild(COMPONENT_LOCATION_KEY).getValue(
275 COMPONENT_ROLE_VALUE )
276 );
277
278 this.setComponentRolesFlavour(
279 currComponentRoles.getChild(CONTAINERFLAVOUR_CONFIG_KEY).getValue(
280 COMPONENT_ROLECONFIGFLAVOUR_VALUE )
281 );
282
283 this.setComponentRolesEncrypted(
284 currComponentRoles.getChild(COMPONENT_ISENCRYPTED_KEY).getValue(
285 "false" )
286 );
287
288 // evaluate componentConfiguraion
289
290 Configuration currComponentConfiguration = configuration.getChild(COMPONENT_CONFIG_KEY);
291
292 this.setComponentConfigurationLocation(
293 currComponentConfiguration.getChild(COMPONENT_LOCATION_KEY).getValue(
294 COMPONENT_CONFIG_VALUE )
295 );
296
297 this.setComponentConfigurationEncrypted(
298 currComponentConfiguration.getChild(COMPONENT_ISENCRYPTED_KEY).getValue(
299 "false" )
300 );
301
302 // get the configuration for componentConfigurationPropertiesResolver
303
304 this.componentConfigurationPropertiesResolverConfig = configuration.getChild(
305 COMPONENT_CONFIG_PROPERTIES_KEY
306 );
307
308 // evaluate parameters
309
310 Configuration currParameters = configuration.getChild(COMPONENT_PARAMETERS_KEY);
311
312 this.setParametersLocation(
313 currParameters.getChild(COMPONENT_LOCATION_KEY).getValue(
314 COMPONENT_PARAMETERS_VALUE )
315 );
316
317 this.setParametersEncrypted(
318 currParameters.getChild(COMPONENT_ISENCRYPTED_KEY).getValue(
319 "false" )
320 );
321
322 // evaluate the default interceptors
323
324 Configuration currInterceptorList = configuration.getChild(
325 INTERCEPTOR_LIST_KEY
326 );
327
328 Configuration[] interceptorConfigList = currInterceptorList.getChildren(
329 INTERCEPTOR_KEY
330 );
331
332 for( int i=0; i<interceptorConfigList.length; i++ )
333 {
334 String interceptorServiceName = interceptorConfigList[i].getValue(null);
335
336 if( !StringUtils.isEmpty(interceptorServiceName) && this.hasDynamicProxies())
337 {
338 this.defaultInterceptorServiceList.add( interceptorServiceName );
339
340 this.getLogger().debug("Using the following default interceptor service : "
341 + interceptorServiceName
342 );
343 }
344 }
345
346 // evaluate a list of service managers managing their own set of services
347 // independent from the Avalon container. This service managers are used
348 // to find services implemented as Spring bean or remote webservices.
349
350 Configuration currServiceManagerList = configuration.getChild(
351 SERVICEMANAGER_LIST_KEY
352 );
353
354 Configuration[] serviceManagerConfigList = currServiceManagerList.getChildren(
355 SERVICEMANAGER_KEY
356 );
357
358 for( int i=0; i<serviceManagerConfigList.length; i++ )
359 {
360 String serviceManagerName = serviceManagerConfigList[i].getValue(null);
361
362 if( !StringUtils.isEmpty(serviceManagerName) )
363 {
364 this.fallbackServiceManagerList.add( serviceManagerName );
365
366 this.getLogger().debug("Using the following fallback service manager : "
367 + serviceManagerName
368 );
369 }
370 }
371 }
372
373 /**
374 * @see org.apache.avalon.framework.parameters.Parameterizable#parameterize(org.apache.avalon.framework.parameters.Parameters)
375 */
376 public void parameterize(Parameters parameters) throws ParameterException
377 {
378 this.parameters = parameters;
379 }
380
381 /**
382 * @see org.apache.avalon.framework.activity.Initializable#initialize()
383 */
384 public void initialize() throws Exception
385 {
386 this.getLogger().debug( "YAAFI Service Framework is starting up");
387
388 // set the directories being used
389
390 this.setApplicationRootDir(
391 (File) this.getContext().get(AvalonYaafiConstants.URN_AVALON_HOME )
392 );
393
394 this.setTempRootDir(
395 (File) this.getContext().get(AvalonYaafiConstants.URN_AVALON_TEMP )
396 );
397
398 // get the configuration files
399
400 this.roleConfiguration = loadConfiguration(
401 this.componentRolesLocation,
402 this.isComponentRolesEncrypted()
403 );
404
405 if( this.roleConfiguration == null )
406 {
407 String msg = "Unable to locate the role configuration : " + this.componentRolesLocation;
408 this.getLogger().error( msg );
409 throw new ConfigurationException( msg );
410 }
411
412 this.serviceConfiguration = loadConfiguration(
413 this.componentConfigurationLocation,
414 this.isComponentConfigurationEncrypted()
415 );
416
417 // create the configuration properties
418
419 Properties componentConfigurationProperties = this.loadComponentConfigurationProperties();
420
421 // expand the componentConfiguration using the componentConfigurationProperties
422
423 ConfigurationUtil.expand(
424 this.getLogger(),
425 (DefaultConfiguration) this.serviceConfiguration,
426 componentConfigurationProperties
427 );
428
429 // create the default parameters
430
431 if( this.getParameters() == null )
432 {
433 this.parameters= this.loadParameters(
434 this.parametersLocation,
435 this.isParametersEncrypted()
436 );
437 }
438
439 // create the service implementaion instances
440
441 List currServiceList = this.createServiceComponents(
442 this.roleConfiguration,
443 this.getLogger()
444 );
445
446 this.setServiceList( currServiceList );
447
448 // fill the service map mapping from a service name to an instance
449
450 for( int i=0; i<this.getServiceList().size(); i++ )
451 {
452 ServiceComponent serviceComponent = (ServiceComponent) this.getServiceList().get(i);
453 this.getServiceMap().put( serviceComponent.getName(), serviceComponent );
454 }
455
456 // ensure that fallback service managers are available
457
458 for(int i=0; i<this.fallbackServiceManagerList.size(); i++)
459 {
460 String currServiceManagerName = (String) this.fallbackServiceManagerList.get(i);
461 if(this.getServiceMap().get(currServiceManagerName) == null)
462 {
463 String msg = "The following fallback service manager was not found : " + currServiceManagerName;
464 throw new IllegalArgumentException(msg);
465 }
466 }
467
468 // run the various lifecycle stages
469
470 this.incarnateAll( this.getServiceList() );
471
472 // we are up and running
473
474 this.isDisposed = false;
475 this.getLogger().debug( "YAAFI Avalon Service Container is up and running");
476 }
477
478
479 /**
480 * Disposes the service container implementation.
481 *
482 * @see org.apache.avalon.framework.activity.Disposable#dispose()
483 */
484 public synchronized void dispose()
485 {
486 if( this.isDisposed )
487 {
488 return;
489 }
490
491 if( this.getLogger() != null )
492 {
493 this.getLogger().debug("Disposing all services");
494 }
495
496 // decommision all servcies
497
498 this.decommisionAll( this.getServiceList() );
499
500 // dispose all servcies
501
502 this.disposeAll( this.getServiceList() );
503
504 // clean up
505
506 this.getServiceList().clear();
507 this.getServiceMap().clear();
508
509 this.componentRolesLocation = null;
510 this.componentConfigurationLocation = null;
511 this.context = null;
512 this.parametersLocation = null;
513 this.roleConfiguration = null;
514 this.serviceConfiguration = null;
515 this.parameters = null;
516 this.fallbackServiceManagerList = null;
517 this.defaultInterceptorServiceList = null;
518 this.isDisposed = true;
519
520 if( this.getLogger() != null )
521 {
522 this.getLogger().debug( "All services are disposed" );
523 }
524 }
525
526 /**
527 * Reconfiguring the services. I'm not sure how to implement this properly since
528 * the Avalon docs is vague on this subject. For now we suspend, reconfigure and
529 * resume the services in the correct order.
530 *
531 * @see org.apache.avalon.framework.configuration.Reconfigurable#reconfigure(org.apache.avalon.framework.configuration.Configuration)
532 */
533 public synchronized void reconfigure(Configuration configuration)
534 throws ConfigurationException
535 {
536 Validate.notNull( configuration, "configuration" );
537
538 int exceptionCounter = 0;
539 ServiceComponent serviceComponent = null;
540
541 this.getLogger().warn("Reconfiguring all services ...");
542
543 // 1) wait for some time
544
545 this.waitForReconfiguration();
546
547 // 2) store the new configuration
548
549 this.serviceConfiguration = configuration;
550
551 Properties componentConfigurationProperties = this.loadComponentConfigurationProperties();
552
553 ConfigurationUtil.expand(
554 this.getLogger(),
555 (DefaultConfiguration) this.serviceConfiguration,
556 componentConfigurationProperties
557 );
558
559 // 3) reconfigure the services
560
561 for( int i=0; i<this.getServiceList().size(); i++ )
562 {
563 serviceComponent = (ServiceComponent) this.getServiceList().get(i);
564
565 Configuration serviceComponentConfiguraton = this.getServiceConfiguration().getChild(
566 serviceComponent.getShorthand()
567 );
568
569 try
570 {
571 serviceComponent.setConfiguration(serviceComponentConfiguraton);
572 serviceComponent.reconfigure();
573 }
574 catch(Throwable t)
575 {
576 String msg = "Reconfiguring of " + serviceComponent.getShorthand() + " failed";
577 this.getLogger().error(msg);
578 exceptionCounter++;
579 }
580 }
581
582 // 4) check the result
583
584 if( exceptionCounter > 0 )
585 {
586 String msg = "The reconfiguration failed with " + exceptionCounter + " exception(s)";
587 this.getLogger().error(msg);
588 throw new ConfigurationException(msg);
589 }
590 }
591
592 /////////////////////////////////////////////////////////////////////////
593 // Server Interface Implementation
594 /////////////////////////////////////////////////////////////////////////
595
596 /**
597 * @see org.apache.fulcrum.yaafi.framework.container.ServiceLifecycleManager#getRoleEntry(java.lang.String)
598 */
599 public synchronized RoleEntry getRoleEntry(String name)
600 throws ServiceException
601 {
602 return this.getServiceComponentEx(name).getRoleEntry();
603 }
604
605 /**
606 * @see org.apache.fulcrum.yaafi.framework.container.ServiceLifecycleManager#getRoleEntries()
607 */
608 public synchronized RoleEntry[] getRoleEntries()
609 {
610 List serviceList = this.getServiceList();
611 ServiceComponent serviceComponent = null;
612 RoleEntry[] result = new RoleEntry[serviceList.size()];
613
614 for( int i=0; i<result.length; i++ )
615 {
616 serviceComponent = (ServiceComponent) serviceList.get(i);
617 result[i] = serviceComponent.getRoleEntry();
618 }
619
620 return result;
621 }
622
623 /**
624 * @see org.apache.fulcrum.yaafi.framework.container.ServiceLifecycleManager#reconfigure(java.lang.String[])
625 */
626 public synchronized void reconfigure(String[] names) throws ServiceException,
627 ConfigurationException
628 {
629 Validate.notNull(names,"names");
630 Validate.noNullElements(names,"names");
631
632 this.waitForReconfiguration();
633
634 for( int i=0; i<names.length; i++ )
635 {
636 // ensure that the service exists since during our reconfiguration
637 // we might use a stle recofniguration entry
638
639 if( this.getServiceMap().get(names[i]) != null )
640 {
641 this.reconfigure(names[i]);
642 }
643 }
644 }
645
646 /**
647 * @see org.apache.avalon.framework.service.ServiceManager#hasService(java.lang.String)
648 */
649 public boolean hasService(String name)
650 {
651 Validate.notEmpty( name, "name" );
652
653 boolean result = false;
654
655 synchronized(this)
656 {
657 // look at our available service
658
659 result = (this.getLocalServiceComponent(name) != null);
660
661 // look at fallback service managers
662
663 if(!result)
664 {
665 result = this.hasFallbackService(name);
666 }
667 }
668
669 // if we haven't found anything ask the parent ServiceManager
670
671 if( (!result) && ( this.hasParentServiceManager() ) )
672 {
673 result = this.getParentServiceManager().hasService(name);
674 }
675
676 return result;
677 }
678
679 /**
680 * Lookup a service instance. The implementation uses the following
681 * mechanism
682 * <ul>
683 * <li>look for a matching local service
684 * <li>use the fallback service manager as they might know the service
685 * <li>ask the parent service manager
686 * </ul>
687 *
688 * @see org.apache.avalon.framework.service.ServiceManager#lookup(java.lang.String)
689 */
690 public Object lookup(String name) throws ServiceException
691 {
692 Validate.notEmpty( name, "name" );
693
694 Object result = null;
695 ServiceComponent serviceManagerComponent = null;
696
697 try
698 {
699 synchronized (this)
700 {
701 // 1) check our local services
702
703 serviceManagerComponent = this.getLocalServiceComponent(name);
704
705 if( serviceManagerComponent != null )
706 {
707 result = serviceManagerComponent.getInstance();
708
709 if((result != null) && this.getLogger().isDebugEnabled())
710 {
711 String msg = "Located the service '" + name + "' in the local container";
712 this.getLogger().debug(msg);
713 }
714 }
715
716 // 2) look at fallback service managers
717
718 if(result == null)
719 {
720 result = this.getFallbackService(name);
721 }
722 }
723 }
724 catch(ServiceException e)
725 {
726 String msg = "Failed to lookup a service " + name;
727 this.getLogger().error( msg, e );
728 throw e;
729 }
730 catch( Throwable t )
731 {
732 String msg = "Failed to lookup a service " + name;
733 this.getLogger().error( msg, t );
734 throw new ServiceException( name, msg, t );
735 }
736
737 // 3) if we haven't found anything ask the parent ServiceManager
738
739 if( (result == null) && this.hasParentServiceManager() )
740 {
741 result = this.getParentServiceManager().lookup(name);
742
743 if((result != null) && this.getLogger().isDebugEnabled())
744 {
745 String msg = "Located the service '" + name + "' using the parent service manager";
746 this.getLogger().debug(msg);
747 }
748 }
749
750 // if we still haven't found anything then complain
751
752 if( result == null )
753 {
754 String msg = "The following component does not exist : " + name;
755 this.getLogger().error(msg);
756 throw new ServiceException( AvalonYaafiConstants.AVALON_CONTAINER_YAAFI, name );
757 }
758
759 return result;
760 }
761
762 /**
763 * @see org.apache.avalon.framework.service.ServiceManager#release(java.lang.Object)
764 */
765 public void release(Object object)
766 {
767 // AFAIK this is only useful for lifecycle management regarding
768 // lifestyle other than singleton.
769 }
770
771 /**
772 * @see org.apache.fulcrum.yaafi.framework.container.ServiceContainer#decommision(java.lang.String)
773 */
774 public synchronized void decommision(String name) throws ServiceException
775 {
776 this.waitForReconfiguration();
777 ServiceComponent serviceComponent = this.getServiceComponentEx(name);
778 this.decommision(serviceComponent);
779 }
780
781 /**
782 * @see org.apache.fulcrum.yaafi.framework.container.ServiceContainer#getParameters()
783 */
784 public Parameters getParameters()
785 {
786 return this.parameters;
787 }
788
789 /////////////////////////////////////////////////////////////////////////
790 // Service Implementation
791 /////////////////////////////////////////////////////////////////////////
792
793 /**
794 * @see java.lang.Object#toString()
795 */
796 public String toString()
797 {
798 ToStringBuilder toStringBuilder = new ToStringBuilder(this);
799
800 toStringBuilder.append("applicationRootDir", this.getApplicationRootDir());
801 toStringBuilder.append("tempRootDir", this.getTempRootDir());
802 toStringBuilder.append("componentRolesLocation", this.componentRolesLocation);
803 toStringBuilder.append("componentConfigurationLocation", this.componentConfigurationLocation);
804 toStringBuilder.append("parametersLocation", parametersLocation);
805 toStringBuilder.append("logger", this.getLogger().getClass().getName());
806 toStringBuilder.append("hasDynamicProxies", this.hasDynamicProxies);
807 toStringBuilder.append("containerFlavour", this.containerFlavour);
808 toStringBuilder.append("componentRolesFlavour", this.componentRolesFlavour);
809 toStringBuilder.append("isComponentRolesEncrypted", this.isComponentRolesEncrypted);
810 toStringBuilder.append("isComponentConfigurationEncrypted", this.isComponentConfigurationEncrypted);
811 toStringBuilder.append("isParametersEncrypted", this.isParametersEncrypted);
812
813 return toStringBuilder.toString();
814 }
815
816 /**
817 * Create a role configuration parser based on the container flavour.
818 * @return the role configuration parser
819 */
820 private RoleConfigurationParser createRoleConfigurationParser()
821 {
822 return new RoleConfigurationParserImpl(
823 this.getComponentRolesFlavour()
824 );
825 }
826
827 /**
828 * Reconfigure a single service
829 *
830 * @param name the name of the service to be reconfigured
831 * @throws ServiceException the service was not found
832 * @throws ConfigurationException the reconfiguration failed
833 */
834 private void reconfigure(String name)
835 throws ServiceException, ConfigurationException
836 {
837 Validate.notEmpty( name, "name" );
838 ServiceComponent serviceComponent = this.getServiceComponentEx(name);
839
840 // reconfigure the component
841
842 try
843 {
844 serviceComponent.reconfigure();
845 }
846 catch(ConfigurationException e)
847 {
848 String msg = "Reconfiguring failed : " + serviceComponent.getShorthand();
849 this.getLogger().error(msg,e);
850 throw new ConfigurationException(msg,e);
851 }
852 catch(Throwable t)
853 {
854 String msg = "Reconfiguring failed : " + serviceComponent.getShorthand();
855 this.getLogger().error(msg,t);
856 throw new ConfigurationException(msg,t);
857 }
858 }
859
860 /**
861 * Enforce that a service is known to simplify error handling.
862 *
863 * @param name the name of the service component
864 * @return the service component
865 * @throws ServiceException the service was not found
866 */
867 private ServiceComponent getServiceComponentEx(String name)
868 throws ServiceException
869 {
870 Validate.notEmpty( name, "name" );
871 ServiceComponent result = (ServiceComponent) this. getServiceMap().get(name);
872
873 if( result == null )
874 {
875 String msg = "The following component does not exist : " + name;
876 this.getLogger().error(msg);
877 throw new ServiceException( AvalonYaafiConstants.AVALON_CONTAINER_YAAFI, name );
878 }
879
880 return result;
881 }
882
883 /**
884 * Try to get a local service component.
885 *
886 * @param name the name of the service component
887 * @return the service component if any
888 */
889 private ServiceComponent getLocalServiceComponent(String name)
890 {
891 Validate.notEmpty( name, "name" );
892 ServiceComponent result = (ServiceComponent) this. getServiceMap().get(name);
893 return result;
894 }
895
896 /**
897 * Try to get a service component provided by a fallback service
898 * manager.
899 *
900 * @param name the name of the service component
901 * @return the service component if any
902 * @throws Exception getting the service failed
903 */
904 private Object getFallbackService(String name) throws Exception
905 {
906
907 Validate.notEmpty( name, "name" );
908
909 Object result = null;
910 ServiceComponent serviceManagerComponent = null;
911
912 for(int i=0; i<this.fallbackServiceManagerList.size(); i++)
913 {
914 String serviceManagerComponentName = (String) fallbackServiceManagerList.get(i);
915 serviceManagerComponent = this.getLocalServiceComponent(serviceManagerComponentName);
916
917 if(serviceManagerComponent != null)
918 {
919 ServiceManager currServiceManager = (ServiceManager) serviceManagerComponent.getInstance();
920
921 if (currServiceManager.hasService(name))
922 {
923 result = currServiceManager.lookup(name);
924
925 if((result != null) && this.getLogger().isDebugEnabled())
926 {
927 String msg = "Located the service '" + name + "' using the fallback service manager '" + serviceManagerComponentName + "'";
928 this.getLogger().debug(msg);
929 }
930 }
931 }
932 }
933
934 return result;
935 }
936
937 /**
938 * Try to get a service provided by a fallback service manager.
939 *
940 * @param name the name of the service component
941 * @return the service component if any
942 */
943 private boolean hasFallbackService(String name)
944 {
945 Validate.notEmpty( name, "name" );
946
947 ServiceComponent serviceManagerComponent = null;
948
949 for(int i=0; i<this.fallbackServiceManagerList.size(); i++)
950 {
951 String serviceManagerComponentName = (String) fallbackServiceManagerList.get(i);
952 serviceManagerComponent = this.getLocalServiceComponent(serviceManagerComponentName);
953
954 if(serviceManagerComponent != null)
955 {
956 ServiceManager currServiceManager;
957
958 try
959 {
960 currServiceManager = (ServiceManager) serviceManagerComponent.getInstance();
961 if (currServiceManager.hasService(name))
962 {
963 return true;
964 }
965 }
966 catch (Exception e)
967 {
968 String msg = "Unable to invoke fallback service manager '" + serviceManagerComponentName + "'";
969 this.getLogger().error(msg, e);
970 throw new RuntimeException(msg);
971 }
972 }
973 }
974
975 return false;
976 }
977
978 /**
979 * @param string The location of the component configuration file
980 */
981 private void setComponentConfigurationLocation(String string)
982 {
983 this.componentConfigurationLocation = string;
984 }
985
986 /**
987 * @param string The location of the component role file
988 */
989 private void setComponentRolesLocation(String string)
990 {
991 this.componentRolesLocation = string;
992 }
993
994 /**
995 * @param string The location of the parameters file
996 */
997 private void setParametersLocation(String string)
998 {
999 this.parametersLocation = string;
1000 }
1001
1002 /**
1003 * @return The logger of the service containe
1004 */
1005 private Logger getLogger()
1006 {
1007 return this.logger;
1008 }
1009
1010 /**
1011 * @return Returns the serviceMap.
1012 */
1013 private HashMap getServiceMap()
1014 {
1015 return this.serviceMap;
1016 }
1017
1018 /**
1019 * Incarnation of a list of services.
1020 *
1021 * @param serviceList the list of available services
1022 * @throws Exception the incarnation of a service failed
1023 */
1024 private void incarnateAll(List serviceList)
1025 throws Exception
1026 {
1027 ServiceComponent serviceComponent = null;
1028
1029 // configure all services
1030
1031 for( int i=0; i<serviceList.size(); i++ )
1032 {
1033 serviceComponent = (ServiceComponent) this.getServiceList().get(i);
1034 this.configure( serviceComponent );
1035 }
1036
1037 // incarnate all services
1038
1039 for( int i=0; i<serviceList.size(); i++ )
1040 {
1041 serviceComponent = (ServiceComponent) this.getServiceList().get(i);
1042 this.incarnate( serviceComponent );
1043 }
1044
1045 }
1046
1047 /**
1048 * Configure a single service component. After the invocation
1049 * the service component is ready to be incarnated.
1050 *
1051 * @param serviceComponent The service component to be configured
1052 * @throws Exception the configuration failed
1053 */
1054 private void configure( ServiceComponent serviceComponent )
1055 throws Exception
1056 {
1057 this.getLogger().debug( "Configuring the service component "
1058 + serviceComponent.getShorthand()
1059 );
1060
1061 // map the context according to the Avalon component type
1062
1063 YaafiToAvalonContextMapper mapper = new YaafiToAvalonContextMapper(
1064 serviceComponent.getName(),
1065 this.getClassLoader()
1066 );
1067
1068 RoleEntry roleEntry = serviceComponent.getRoleEntry();
1069 String componentFlavour = roleEntry.getComponentFlavour();
1070
1071 DefaultContext serviceComponentContext = mapper.mapTo(
1072 this.getContext(),
1073 componentFlavour
1074 );
1075
1076 // create the remaining Avalon artifacts for the service component
1077
1078 Logger serviceComponentLogger = this.getLogger().getChildLogger(
1079 roleEntry.getLogCategory()
1080 );
1081
1082 Configuration serviceComponentConfiguraton = this.getServiceConfiguration().getChild(
1083 roleEntry.getShorthand()
1084 );
1085
1086 Parameters serviceComponentParameters = this.getParameters();
1087
1088 // configure the service component with all the artifacts
1089
1090 serviceComponent.setLogger(serviceComponentLogger);
1091 serviceComponent.setServiceManager(this);
1092 serviceComponent.setContext(serviceComponentContext);
1093 serviceComponent.setConfiguration(serviceComponentConfiguraton);
1094 serviceComponent.setParameters(serviceComponentParameters);
1095
1096 // load the implementation class of the service
1097
1098 serviceComponent.loadImplemtationClass(
1099 this.getClassLoader()
1100 );
1101 }
1102
1103 /**
1104 * Incarnation of a configured service component. After the
1105 * incarnation the service component is operational.
1106 *
1107 * @param serviceComponent The service component to incarnate
1108 * @exception Exception incarnating the service component failed
1109 */
1110 private void incarnate( ServiceComponent serviceComponent )
1111 throws Exception
1112 {
1113 this.getLogger().debug( "Incarnating the service "
1114 + serviceComponent.getShorthand()
1115 );
1116
1117 serviceComponent.incarnate();
1118 }
1119
1120 /**
1121 * Decommision a ist of services.
1122 *
1123 * @param serviceList the list of services to decommision
1124 */
1125 private void decommisionAll(List serviceList)
1126 {
1127 ServiceComponent serviceComponent = null;
1128
1129 for( int i=serviceList.size()-1; i>=0; i-- )
1130 {
1131 serviceComponent = (ServiceComponent) serviceList.get(i);
1132 this.decommision( serviceComponent );
1133 }
1134 }
1135
1136 /**
1137 * Decommision of a single service component. Decommision consists of running the
1138 * whole Avalon decommision lifecycle process for a service component. After
1139 * decommision the service is not operational any longer. During decommisioning
1140 * we ignore any exceptions since it is quite common that something goes wrong.
1141 *
1142 * @param serviceComponent The service component to decommision
1143 */
1144 private void decommision( ServiceComponent serviceComponent )
1145 {
1146 this.getLogger().debug( "Decommision the service " + serviceComponent.getShorthand() );
1147
1148 try
1149 {
1150 serviceComponent.decommision();
1151 }
1152 catch (Throwable e)
1153 {
1154 String msg = "Decommisioning the following service failed : " + serviceComponent.getName();
1155 this.getLogger().error( msg, e );
1156 }
1157 }
1158
1159 /**
1160 * Disposing a ist of services
1161 *
1162 * @param serviceList the list of services to dispose
1163 */
1164 private void disposeAll(List serviceList)
1165 {
1166 ServiceComponent serviceComponent = null;
1167
1168 for( int i=serviceList.size()-1; i>=0; i-- )
1169 {
1170 serviceComponent = (ServiceComponent) serviceList.get(i);
1171 this.dispose( serviceComponent );
1172 }
1173 }
1174
1175 /**
1176 * Disposing of a single service component.
1177
1178 * @param serviceComponent The service component to decommision
1179 */
1180 private void dispose( ServiceComponent serviceComponent )
1181 {
1182 this.getLogger().debug( "Disposing the service " + serviceComponent.getShorthand() );
1183
1184 try
1185 {
1186 serviceComponent.dispose();
1187 }
1188 catch (Throwable e)
1189 {
1190 String msg = "Disposing the following service failed : " + serviceComponent.getName();
1191 this.getLogger().error( msg, e );
1192 }
1193 }
1194
1195 /**
1196 * @return The list of currently know services
1197 */
1198 private List getServiceList()
1199 {
1200 return this.serviceList;
1201 }
1202
1203 /**
1204 * @param list The list of known services
1205 */
1206 private void setServiceList(List list)
1207 {
1208 this.serviceList = list;
1209 }
1210
1211 /**
1212 * @return The service configuration
1213 */
1214 private Configuration getServiceConfiguration()
1215 {
1216 return this.serviceConfiguration;
1217 }
1218
1219 /**
1220 * Factory method for creating services. The service
1221 * instances are not initialized at this point.
1222 *
1223 * @param roleConfiguration the role configuration file
1224 * @param logger the logger
1225 * @return the list of service components
1226 * @throws ConfigurationException creating the service instance failed
1227 */
1228 private List createServiceComponents(Configuration roleConfiguration, Logger logger )
1229 throws ConfigurationException
1230 {
1231 Validate.notNull(roleConfiguration,"roleConfiguration");
1232 Validate.notNull(logger,"logger");
1233
1234 ArrayList result = new ArrayList();
1235 ServiceComponent serviceComponent = null;
1236
1237 // create an appropriate instance of role configuration parser
1238
1239 RoleConfigurationParser roleConfigurationParser = this.createRoleConfigurationParser();
1240
1241 // extract the role entries
1242
1243 RoleEntry[] roleEntryList = roleConfigurationParser.parse(roleConfiguration);
1244
1245 // get the default interceptors defined for the container
1246
1247 ArrayList defaultInterceptorList = this.getDefaultInterceptorServiceList();
1248
1249 // create the service components based on the role entries
1250
1251 for ( int i=0; i<roleEntryList.length; i++ )
1252 {
1253 try
1254 {
1255 // add the default interceptors to all role entries
1256
1257 RoleEntry roleEntry = roleEntryList[i];
1258
1259 if( this.hasDynamicProxies() )
1260 {
1261 roleEntry.addInterceptors(defaultInterceptorList);
1262 }
1263 else
1264 {
1265 roleEntry.setHasDynamicProxy(false);
1266 }
1267
1268 serviceComponent = new AvalonServiceComponentImpl(
1269 roleEntry,
1270 this.getLogger(),
1271 logger
1272 );
1273
1274 result.add( serviceComponent );
1275 }
1276 catch( Throwable t )
1277 {
1278 String serviceComponentName = ( serviceComponent != null ? serviceComponent.getName() : "unknown" );
1279 String msg = "Failed to load the service " + serviceComponentName;
1280 this.getLogger().error( msg, t );
1281 throw new ConfigurationException( msg, t );
1282 }
1283 }
1284
1285 return result;
1286 }
1287
1288 /**
1289 * Load a configuration file either from a file or using the class loader.
1290 * @param location the location of the file
1291 * @param isEncrypted is the configuration encryped
1292 * @return The loaded configuration
1293 * @throws Exception Something went wrong
1294 */
1295 private Configuration loadConfiguration( String location, String isEncrypted )
1296 throws Exception
1297 {
1298 Configuration result = null;
1299 InputStreamLocator locator = this.createInputStreamLocator();
1300 InputStream is = locator.locate( location );
1301 DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
1302
1303 if( is != null )
1304 {
1305 try
1306 {
1307 is = this.getDecryptingInputStream( is, isEncrypted );
1308 result = builder.build( is );
1309 is.close();
1310 is = null;
1311 }
1312 catch ( Exception e )
1313 {
1314 String msg = "Unable to parse the following file : " + location;
1315 this.getLogger().error( msg , e );
1316 throw e;
1317 }
1318 }
1319
1320 return result;
1321 }
1322
1323 /**
1324 * Load a configuration property file either from a file or using the class loader.
1325 *
1326 * @return The loaded proeperty file
1327 * @throws ConfigurationException Something went wrong
1328 */
1329 private Properties loadComponentConfigurationProperties()
1330 throws ConfigurationException
1331 {
1332 Properties result = new Properties();
1333 ComponentConfigurationPropertiesResolver resolver = null;
1334
1335 String className = this.componentConfigurationPropertiesResolverConfig.getChild("resolver").getValue(
1336 ComponentConfigurationPropertiesResolverImpl.class.getName()
1337 );
1338
1339 try
1340 {
1341 Class resolverClass = this.getClassLoader().loadClass( className );
1342 resolver = (ComponentConfigurationPropertiesResolver) resolverClass.newInstance();
1343 ContainerUtil.enableLogging(resolver, this.getLogger());
1344 ContainerUtil.contextualize(resolver, this.getContext());
1345 ContainerUtil.configure(resolver, this.componentConfigurationPropertiesResolverConfig);
1346
1347 result = resolver.resolve(null);
1348
1349 this.getLogger().debug("Using the following componentConfigurationProperties: " + result);
1350 }
1351 catch (Exception e)
1352 {
1353 String msg = "Resolving componentConfigurationProperties failed using the following class : " + className;
1354 this.getLogger().error(msg, e);
1355 throw new ConfigurationException(msg, e);
1356 }
1357
1358 return result;
1359 }
1360
1361 /**
1362 * Load the parameters
1363 * @param location The location as a file
1364 * @param isEncrypted is the file encrypted
1365 * @return The loaded configuration
1366 * @throws Exception Something went wrong
1367 */
1368 private Parameters loadParameters( String location, String isEncrypted )
1369 throws Exception
1370 {
1371 InputStreamLocator locator = this.createInputStreamLocator();
1372 InputStream is = locator.locate( location );
1373 Parameters result = new Parameters();
1374
1375 if( is != null )
1376 {
1377 is = this.getDecryptingInputStream( is, isEncrypted );
1378 Properties props = new Properties();
1379 props.load( is );
1380 is.close();
1381 is = null;
1382 result = Parameters.fromProperties( props );
1383 }
1384
1385 return result;
1386 }
1387
1388
1389 /**
1390 * Creates a locator to find a resource either in the file system or in
1391 * the classpath.
1392 *
1393 * @return the locator
1394 */
1395 private InputStreamLocator createInputStreamLocator()
1396 {
1397 return new InputStreamLocator( this.getApplicationRootDir(), this.getLogger() );
1398 }
1399
1400 /**
1401 * Set the application directory of the container.
1402 *
1403 * @param dir The applicationRootDir to set.
1404 */
1405 private void setApplicationRootDir(File dir)
1406 {
1407 this.getLogger().debug( "Setting applicationRootDir to " + dir.getAbsolutePath() );
1408
1409 Validate.notNull(dir,"applicationRootDir is <null>");
1410 Validate.isTrue(dir.exists(),"applicationRootDir does not exist");
1411
1412 this.applicationRootDir = dir;
1413 }
1414
1415 /**
1416 * @return Returns the applicationRootDir.
1417 */
1418 private File getApplicationRootDir()
1419 {
1420 return this.applicationRootDir;
1421 }
1422
1423 /**
1424 * @return Returns the serviceManager of the parent container
1425 */
1426 private ServiceManager getParentServiceManager()
1427 {
1428 return this.parentServiceManager;
1429 }
1430
1431 /**
1432 * @return is a parent ServiceManager available
1433 */
1434 private boolean hasParentServiceManager()
1435 {
1436 return (this.getParentServiceManager() != null);
1437 }
1438 /**
1439 * Set the temporary directory of the container.
1440 *
1441 * @param dir The tempRootDir to set.
1442 */
1443 private void setTempRootDir(File dir)
1444 {
1445 this.getLogger().debug( "Setting tempRootDir to " + dir.getAbsolutePath() );
1446
1447 Validate.notNull(dir,"tempRootDir is <null>");
1448 Validate.isTrue(dir.exists(),"tempRootDir does not exist");
1449 Validate.isTrue(dir.canWrite(),"tempRootDir is not writeable");
1450
1451 this.tempRootDir = dir;
1452 }
1453
1454 /**
1455 * @return Returns the tempRootDir.
1456 */
1457 private File getTempRootDir()
1458 {
1459 return tempRootDir;
1460 }
1461
1462 /**
1463 * @return Returns the isComponentConfigurationEncrypted.
1464 */
1465 private String isComponentConfigurationEncrypted()
1466 {
1467 return isComponentConfigurationEncrypted;
1468 }
1469
1470 /**
1471 * @param isComponentConfigurationEncrypted The isComponentConfigurationEncrypted to set.
1472 */
1473 private void setComponentConfigurationEncrypted(
1474 String isComponentConfigurationEncrypted)
1475 {
1476 this.isComponentConfigurationEncrypted = isComponentConfigurationEncrypted;
1477 }
1478
1479 /**
1480 * @return Returns the isComponentRolesEncrypted.
1481 */
1482 private String isComponentRolesEncrypted()
1483 {
1484 return isComponentRolesEncrypted;
1485 }
1486
1487 /**
1488 * @param isComponentRolesEncrypted The isComponentRolesEncrypted to set.
1489 */
1490 private void setComponentRolesEncrypted(String isComponentRolesEncrypted)
1491 {
1492 this.isComponentRolesEncrypted = isComponentRolesEncrypted;
1493 }
1494
1495 /**
1496 * @return Returns the isParametersEncrypted.
1497 */
1498 private String isParametersEncrypted()
1499 {
1500 return isParametersEncrypted;
1501 }
1502
1503 /**
1504 * @param isParametersEncrypted The isParametersEncrypted to set.
1505 */
1506 private void setParametersEncrypted(String isParametersEncrypted)
1507 {
1508 this.isParametersEncrypted = isParametersEncrypted;
1509 }
1510
1511 /**
1512 * Create a decrypting input stream using the default password.
1513 *
1514 * @param is the input stream to be decrypted
1515 * @param isEncrypted the encryption mode (true|false|auto)
1516 * @return an decrypting input stream
1517 * @throws Exception reading the input stream failed
1518 */
1519 private InputStream getDecryptingInputStream( InputStream is, String isEncrypted )
1520 throws Exception
1521 {
1522 return CryptoStreamFactory.getDecryptingInputStream(is, isEncrypted);
1523 }
1524
1525 /**
1526 * @return Returns the containerFlavour.
1527 */
1528 private String getContainerFlavour()
1529 {
1530 return containerFlavour;
1531 }
1532
1533 /**
1534 * @param containerFlavour The containerFlavour to set.
1535 */
1536 private void setContainerFlavour(String containerFlavour)
1537 {
1538 this.containerFlavour = containerFlavour;
1539 }
1540
1541 /**
1542 * @return Returns the componentRolesFlavour.
1543 */
1544 private String getComponentRolesFlavour()
1545 {
1546 return componentRolesFlavour;
1547 }
1548
1549 /**
1550 * @param componentRolesFlavour The componentRolesFlavour to set.
1551 */
1552 private void setComponentRolesFlavour(String componentRolesFlavour)
1553 {
1554 this.componentRolesFlavour = componentRolesFlavour;
1555 }
1556
1557 /**
1558 * @return Returns the context.
1559 */
1560 private Context getContext()
1561 {
1562 return context;
1563 }
1564
1565 /**
1566 * @return Returns the hasDynamicProxies.
1567 */
1568 private boolean hasDynamicProxies()
1569 {
1570 return this.hasDynamicProxies;
1571 }
1572
1573 /**
1574 * @return Returns the defaultInterceptorServiceList.
1575 */
1576 private ArrayList getDefaultInterceptorServiceList()
1577 {
1578 return defaultInterceptorServiceList;
1579 }
1580
1581 /**
1582 * @return the containers class loader
1583 */
1584 private ClassLoader getClassLoader()
1585 {
1586 return this.getClass().getClassLoader();
1587 }
1588
1589 /**
1590 * Wait for the time configured as 'reconfigurationDelay' before
1591 * reconfiguring the container or services.
1592 */
1593 private void waitForReconfiguration()
1594 {
1595 try
1596 {
1597 Thread.sleep(this.reconfigurationDelay);
1598 }
1599 catch(InterruptedException e)
1600 {
1601 // nothing to do
1602 }
1603 }
1604 }