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