View Javadoc
1   package org.codehaus.plexus;
2   
3   import static org.codehaus.plexus.component.CastUtils.isAssignableFrom;
4   
5   import java.util.ArrayList;
6   import java.util.Collections;
7   import java.util.Comparator;
8   import java.util.IdentityHashMap;
9   import java.util.Iterator;
10  import java.util.LinkedHashMap;
11  import java.util.List;
12  import java.util.Map;
13  import java.util.Set;
14  import java.util.TreeMap;
15  import java.util.Map.Entry;
16  
17  import org.codehaus.plexus.classworlds.realm.ClassRealm;
18  import org.codehaus.plexus.component.composition.CycleDetectedInComponentGraphException;
19  import org.codehaus.plexus.component.factory.ComponentInstantiationException;
20  import org.codehaus.plexus.component.manager.ComponentManager;
21  import org.codehaus.plexus.component.manager.ComponentManagerFactory;
22  import org.codehaus.plexus.component.repository.ComponentDescriptor;
23  import org.codehaus.plexus.component.repository.ComponentRepository;
24  import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
25  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
26  import org.codehaus.plexus.lifecycle.LifecycleHandler;
27  import org.codehaus.plexus.lifecycle.LifecycleHandlerManager;
28  import org.codehaus.plexus.lifecycle.UndefinedLifecycleHandlerException;
29  import org.codehaus.plexus.logging.Logger;
30  import org.codehaus.plexus.util.StringUtils;
31  
32  public class DefaultComponentRegistry implements ComponentRegistry
33  {
34      private static final String DEFAULT_INSTANTIATION_STRATEGY = "singleton";
35  
36      private final MutablePlexusContainer container;
37      private final ComponentRepository repository;
38      private final LifecycleHandlerManager lifecycleHandlerManager;
39      private final Logger logger;
40  
41      private boolean disposingComponents; 
42  
43      private final Map<String, ComponentManagerFactory> componentManagerFactories =
44          Collections.synchronizedMap( new TreeMap<String, ComponentManagerFactory>() );
45  
46      private final Map<Key, ComponentManager<?>> componentManagers = new TreeMap<Key, ComponentManager<?>>();
47      private final Map<Object, ComponentManager<?>> componentManagersByComponent = new IdentityHashMap<Object, ComponentManager<?>>();
48  
49      private final Map<Key, Object> unmanagedComponents = new TreeMap<Key, Object>();
50  
51      public DefaultComponentRegistry( MutablePlexusContainer container,
52                                       ComponentRepository repository,
53                                       LifecycleHandlerManager lifecycleHandlerManager )
54      {
55          this.container = container;
56          this.repository = repository;
57          this.lifecycleHandlerManager = lifecycleHandlerManager;
58          logger = container.getLogger();
59      }
60  
61      public void dispose()
62      {
63          List<ComponentManager<?>> managers;
64          synchronized ( this )
65          {
66              managers = new ArrayList<ComponentManager<?>>( componentManagers.values() );
67              componentManagers.clear();
68              componentManagersByComponent.clear();
69              unmanagedComponents.clear();
70  
71              disposingComponents = true;
72          }
73  
74          // reverse sort the managers by startId
75          Collections.sort( managers, new Comparator<ComponentManager<?>>() {
76              public int compare( ComponentManager<?> left, ComponentManager<?> right )
77              {
78                  if (left.getStartId() < right.getStartId() )
79                  {
80                      return 1;
81                  }
82                  else if (left.getStartId() == right.getStartId() )
83                  {
84                      return 0;
85                  }
86                  else
87                  {
88                      return -1;
89                  }
90              }
91          });
92  
93          // Call dispose callback outside of synchronized lock to avoid deadlocks
94          try
95          {
96              for ( ComponentManager<?> componentManager : managers )
97              {
98                  try
99                  {
100                     componentManager.dispose();
101                 }
102                 catch ( Exception e )
103                 {
104                     // todo dain use a monitor instead of a logger
105                     logger.error( "Error while disposing component manager. Continuing with the rest", e );
106                 }
107             }
108         }
109         finally 
110         {
111             synchronized ( this )
112             {
113                 disposingComponents = false;
114             }
115         }
116     }
117 
118     public void registerComponentManagerFactory( ComponentManagerFactory componentManagerFactory )
119     {
120         componentManagerFactories.put( componentManagerFactory.getId(), componentManagerFactory );
121     }
122 
123     public void addComponentDescriptor( ComponentDescriptor<?> componentDescriptor ) 
124         throws CycleDetectedInComponentGraphException
125     {
126         repository.addComponentDescriptor( componentDescriptor );
127     }
128 
129     @SuppressWarnings("unchecked")
130     public synchronized <T> void addComponent( T component, String role, String roleHint )
131     {
132         ComponentDescriptor descriptor = new ComponentDescriptor(component.getClass(), null );
133         descriptor.setRole( role );
134         descriptor.setRoleHint( roleHint );
135 
136         Key key = new Key( descriptor.getRealm(), role, roleHint );
137 
138         unmanagedComponents.put( key, component );
139     }
140 
141     public <T> ComponentDescriptor<T> getComponentDescriptor( Class<T> type, String role, String roleHint )
142     {
143         return repository.getComponentDescriptor( type, role, roleHint );
144     }
145 
146     @Deprecated
147     public ComponentDescriptor<?> getComponentDescriptor( String role, String roleHint, ClassRealm realm )
148     {
149         return repository.getComponentDescriptor( role, roleHint, realm );
150     }
151 
152     public <T> Map<String, ComponentDescriptor<T>> getComponentDescriptorMap( Class<T> type, String role )
153     {
154         return repository.getComponentDescriptorMap( type, role );
155     }
156 
157     public <T> List<ComponentDescriptor<T>> getComponentDescriptorList( Class<T> type, String role )
158     {
159         return repository.getComponentDescriptorList( type, role );
160     }
161 
162     public <T> T lookup( Class<T> type, String role, String roleHint ) throws ComponentLookupException
163     {
164         // verify arguments
165         if ( type == null )
166         {
167             throw new NullPointerException( "type is null" );
168         }
169         if ( role == null )
170         {
171             throw new NullPointerException( "role is null" );
172         }
173         if ( roleHint == null )
174         {
175             roleHint = "";
176         }
177 
178         return getComponent( type, role, roleHint, null );
179     }
180 
181     public <T> T lookup( ComponentDescriptor<T> componentDescriptor )
182         throws ComponentLookupException
183     {
184         return getComponent( componentDescriptor.getRoleClass(), componentDescriptor.getRole(),
185                              componentDescriptor.getRoleHint(), componentDescriptor );
186     }
187 
188     public <T> Map<String, T> lookupMap( Class<T> type, String role, List<String> roleHints )
189         throws ComponentLookupException
190     {
191         // verify arguments
192         if ( type == null )
193         {
194             throw new NullPointerException( "type is null" );
195         }
196         if ( role == null )
197         {
198             throw new NullPointerException( "role is null" );
199         }
200 
201         // if no hints provided, get all valid hints for this role
202         Map<String, T> components = new LinkedHashMap<String, T>();
203         if ( roleHints == null )
204         {
205             Map<String, ComponentDescriptor<T>> componentDescriptors = getComponentDescriptorMap( type, role );
206             for ( Entry<String, ComponentDescriptor<T>> entry : componentDescriptors.entrySet() )
207             {
208                 String roleHint = entry.getKey();
209                 ComponentDescriptor<T> componentDescriptor = entry.getValue();
210                 // todo dain catch the exception... it isn't the callers problem when one component in a collection fails
211                 T component = getComponent( type, role, roleHint, componentDescriptor );
212                 components.put( roleHint, component );
213             }
214         }
215         else
216         {
217             for ( String roleHint : roleHints )
218             {
219                 // todo dain catch the exception... it isn't the callers problem when one component in a collection fails
220                 T component = getComponent( type, role, roleHint, null );
221                 components.put( roleHint, component );
222             }
223         }
224 
225         return components;
226     }
227 
228     public <T> List<T> lookupList( Class<T> type, String role, List<String> roleHints ) throws ComponentLookupException
229     {
230         // verify arguments
231         if ( type == null )
232         {
233             throw new NullPointerException( "type is null" );
234         }
235         if ( role == null )
236         {
237             throw new NullPointerException( "role is null" );
238         }
239 
240         // if no hints provided, get all valid hints for this role
241         List<T> components = new ArrayList<T>();
242         if ( roleHints == null )
243         {
244             List<ComponentDescriptor<T>> componentDescriptors = getComponentDescriptorList( type, role );
245             for ( ComponentDescriptor<T> componentDescriptor : componentDescriptors )
246             {
247                 // todo dain catch the exception... it isn't the callers problem when one component in a collection fails
248                 T component = getComponent( type, role, componentDescriptor.getRoleHint(), componentDescriptor );
249                 components.add( component );
250             }
251         }
252         else
253         {
254             for ( String roleHint : roleHints )
255             {
256                 // todo dain catch the exception... it isn't the callers problem when one component in a collection fails
257                 T component = getComponent( type, role, roleHint, null );
258                 components.add( component );
259             }
260         }
261 
262         return components;
263     }
264 
265     public void release( Object component )
266         throws ComponentLifecycleException
267     {
268         if ( component == null )
269         {
270             return;
271         }
272 
273         // get the component manager
274         ComponentManager<?> componentManager;
275         synchronized ( this )
276         {
277             componentManager = componentManagersByComponent.get( component );
278             if ( componentManager == null )
279             {
280                 // This needs to be tracked down but the user doesn't need to see this
281                 // during the maven bootstrap this logger is null.
282                 //logger.debug( "Component manager not found for returned component. Ignored. component=" + component );
283                 return;
284             }
285         }
286 
287         // release the component from the manager
288         componentManager.release( component );
289 
290         // only drop the reference to this component if there are no other uses of the component
291         // multiple uses of a component is common with singleton beans
292         if ( componentManager.getConnections() <= 0 )
293         {
294             synchronized ( this )
295             {
296                 componentManagersByComponent.remove( component );
297             }
298         }
299     }
300 
301     public void removeComponentRealm( ClassRealm classRealm ) throws PlexusContainerException
302     {
303         repository.removeComponentRealm( classRealm );
304         
305         List<ComponentManager<?>> dispose = new ArrayList<ComponentManager<?>>();
306         try
307         {
308             synchronized ( this )
309             {
310                 for ( Iterator<Entry<Key, ComponentManager<?>>> it = componentManagers.entrySet().iterator(); it.hasNext(); )
311                 {
312                     Entry<Key, ComponentManager<?>> entry = it.next();
313                     Key key = entry.getKey();
314 
315                     ComponentManager<?> componentManager = entry.getValue();
316 
317                     if ( key.realm.equals( classRealm ) )
318                     {
319                         dispose.add( componentManager );
320                         it.remove();
321                     }
322                     else
323                     {
324                         componentManager.dissociateComponentRealm( classRealm );
325                     }
326                 }
327             }
328 
329             // Call dispose callback outside of synchronized lock to avoid deadlocks
330             for ( ComponentManager<?> componentManager : dispose )
331             {
332                 componentManager.dispose();
333             }
334         }
335         catch ( ComponentLifecycleException e )
336         {
337             throw new PlexusContainerException( "Failed to dissociate component realm: " + classRealm.getId(), e );
338         }
339     }
340 
341     private <T> T getComponent( Class<T> type, String role, String roleHint, ComponentDescriptor<T> descriptor )
342         throws ComponentLookupException
343     {
344         // lookup for unmanaged components first
345 
346         T component = this.<T>getUnmanagedComponent( role, roleHint ); // weird syntax due to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6302954
347 
348         if ( component != null )
349         {
350             return component;
351         }
352 
353         ComponentManager<T> componentManager = getComponentManager( type, role, roleHint, descriptor );
354 
355         // Get instance from manager... may result in creation
356         try
357         {
358             component = componentManager.getComponent();
359             synchronized ( this )
360             {
361                 componentManagersByComponent.put( component, componentManager );
362             }
363             return component;
364         }
365         catch ( ComponentInstantiationException e )
366         {
367             throw new ComponentLookupException(
368                 "Unable to lookup component '" + componentManager.getRole() + "', it could not be created.",
369                 componentManager.getRole(), componentManager.getRoleHint(), componentManager.getRealm(), e );
370         }
371         catch ( ComponentLifecycleException e )
372         {
373             throw new ComponentLookupException(
374                 "Unable to lookup component '" + componentManager.getRole() + "', it could not be started.",
375                 componentManager.getRole(), componentManager.getRoleHint(), componentManager.getRealm(), e );
376         }
377     }
378 
379     @SuppressWarnings("unchecked")
380     private synchronized <T> T getUnmanagedComponent( String role, String roleHint )
381     {
382         Set<ClassRealm> realms = getSearchRealms( true );
383 
384         if ( realms != null )
385         {
386             // ignore unmanaged components, they are not associated with realms 
387             // but lookup realm is provided via thread context classloader 
388             return null;
389         }
390         else
391         {
392             if ( StringUtils.isEmpty( roleHint ) )
393             {
394                 roleHint = PlexusConstants.PLEXUS_DEFAULT_HINT;
395             }
396 
397             return (T) unmanagedComponents.get( new Key( null, role, roleHint ) );
398         }
399     }
400 
401     private synchronized <T> ComponentManager<T> getComponentManager( Class<T> type, String role, String roleHint, ComponentDescriptor<T> descriptor )
402         throws ComponentLookupException
403     {
404         if ( disposingComponents )
405         {
406             throw new ComponentLookupException("ComponentRegistry is not active",
407                 role,
408                 roleHint );
409         }
410 
411         if ( descriptor == null )
412         {
413             descriptor = getComponentDescriptor( type, role, roleHint );
414         }
415 
416         ComponentManager<T> componentManager = null;
417 
418         if ( descriptor != null )
419         {
420             componentManager = getComponentManager( type, role, descriptor.getRoleHint(), descriptor.getRealm() );
421         }
422         else
423         {
424             componentManager = getComponentManager( type, role, roleHint );
425         }
426 
427         if ( componentManager == null )
428         {
429             // we need to create a component manager, but first we must have a descriptor
430             if ( descriptor == null )
431             {
432                 descriptor = getComponentDescriptor( type, role, roleHint );
433                 if ( descriptor == null )
434                 {
435                     throw new ComponentLookupException(
436                         "Component descriptor cannot be found in the component repository",
437                         role,
438                         roleHint );
439                 }
440                 // search also into descriptor realm as the key of a created component is per descriptor realm
441                 componentManager = getComponentManager( type, role, descriptor.getRoleHint(), descriptor.getRealm() );
442             }
443 
444             if ( componentManager == null )
445             {
446                 componentManager = createComponentManager( descriptor, role, descriptor.getRoleHint() );
447             }
448         }
449 
450         return componentManager;
451     }
452 
453     @SuppressWarnings( "unchecked" )
454     private <T> ComponentManager<T> getComponentManager( Class<T> type, String role, String roleHint )
455     {
456         Set<ClassRealm> realms = getSearchRealms( false );
457 
458         // return the component in the first realm
459         for ( ClassRealm realm : realms )
460         {
461             ComponentManager<?> manager = componentManagers.get( new Key( realm, role, roleHint ) );
462             if ( manager != null && isAssignableFrom( type, manager.getType() ) )
463             {
464                 return (ComponentManager<T>) manager;
465             }
466         }
467         return null;
468     }
469 
470     @SuppressWarnings( "unchecked" )
471     private <T> ComponentManager<T> getComponentManager( Class<T> type, String role, String roleHint, ClassRealm realm )
472     {
473         ComponentManager<?> manager = componentManagers.get( new Key( realm, role, roleHint ) );
474         if ( manager != null && isAssignableFrom( type, manager.getType() ) )
475         {
476             return (ComponentManager<T>) manager;
477         }
478         return null;
479     }
480 
481     @SuppressWarnings( "unchecked" )
482     private Set<ClassRealm> getSearchRealms( boolean specifiedOnly )
483     {
484         // determine realms to search
485         Set<ClassRealm> realms = ClassRealmUtil.getContextRealms( container.getClassWorld() );
486 
487         if ( realms.isEmpty() )
488         {
489             if ( specifiedOnly )
490             {
491                 return null;
492             }
493 
494             realms.addAll( container.getClassWorld().getRealms() );
495         }
496 
497         return realms;
498     }
499 
500     private <T> ComponentManager<T> createComponentManager( ComponentDescriptor<T> descriptor,
501                                                             String role,
502                                                             String roleHint )
503         throws ComponentLookupException
504     {
505         // Get the ComponentManagerFactory
506         String instantiationStrategy = descriptor.getInstantiationStrategy();
507         if ( instantiationStrategy == null )
508         {
509             instantiationStrategy = DEFAULT_INSTANTIATION_STRATEGY;
510         }
511         ComponentManagerFactory componentManagerFactory = componentManagerFactories.get( instantiationStrategy );
512         if ( componentManagerFactory == null )
513         {
514             throw new ComponentLookupException( "Unsupported instantiation strategy: " + instantiationStrategy,
515                 role,
516                 roleHint,
517                 descriptor.getRealm() );
518         }
519 
520         // Get the LifecycleHandler
521         LifecycleHandler lifecycleHandler;
522         try
523         {
524             lifecycleHandler = lifecycleHandlerManager.getLifecycleHandler( descriptor.getLifecycleHandler() );
525         }
526         catch ( UndefinedLifecycleHandlerException e )
527         {
528             throw new ComponentLookupException( "Undefined lifecycle handler: " + descriptor.getLifecycleHandler(),
529                 role,
530                 roleHint,
531                 descriptor.getRealm() );
532         }
533 
534         // Create the ComponentManager
535         ComponentManager<T> componentManager = componentManagerFactory.createComponentManager( container,
536             lifecycleHandler,
537             descriptor,
538             role,
539             roleHint );
540 
541         // Add componentManager to indexes
542         Key key = new Key( descriptor.getRealm(), role, roleHint );
543         componentManagers.put( key, componentManager );
544 
545         return componentManager;
546     }
547 
548     private static class Key implements Comparable<Key>
549     {
550         private final ClassRealm realm;
551         private final String role;
552         private final String roleHint;
553         private final int hashCode;
554 
555         private Key( ClassRealm realm, String role, String roleHint )
556         {
557             this.realm = realm;
558 
559             if ( role == null )
560             {
561                 role = "null";
562             }
563             this.role = role;
564 
565             if ( roleHint == null )
566             {
567                 roleHint = "null";
568             }
569             this.roleHint = roleHint;
570 
571             int hashCode;
572             hashCode = ( realm != null ? realm.hashCode() : 0 );
573             hashCode = 31 * hashCode + role.hashCode();
574             hashCode = 31 * hashCode + roleHint.hashCode();
575             this.hashCode = hashCode;
576         }
577 
578         public boolean equals( Object o )
579         {
580             if ( this == o )
581             {
582                 return true;
583             }
584             if ( o == null || getClass() != o.getClass() )
585             {
586                 return false;
587             }
588 
589             Key key = (Key) o;
590 
591             return !( realm != null ? !realm.equals( key.realm ) : key.realm != null ) &&
592                 role.equals( key.role ) &&
593                 roleHint.equals( key.roleHint );
594 
595         }
596 
597         public int hashCode()
598         {
599             return hashCode;
600         }
601 
602         public String toString()
603         {
604             return realm + "/" + role + "/" + roleHint;
605         }
606 
607         public int compareTo( Key o )
608         {
609             int value;
610             if ( realm != null )
611             {
612                 value = o.realm == null? -1 : realm.getId().compareTo( o.realm.getId() );
613             }
614             else
615             {
616                 value = o.realm == null ? 0 : 1;
617             }
618 
619             if ( value == 0 )
620             {
621                 value = role.compareTo( o.role );
622                 if ( value == 0 )
623                 {
624                     value = roleHint.compareTo( o.roleHint );
625                 }
626             }
627             return value;
628         }
629     }
630 }