View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.registry;
8   
9   import org.mule.api.MuleContext;
10  import org.mule.api.MuleException;
11  import org.mule.api.agent.Agent;
12  import org.mule.api.endpoint.ImmutableEndpoint;
13  import org.mule.api.lifecycle.Disposable;
14  import org.mule.api.lifecycle.InitialisationException;
15  import org.mule.api.model.Model;
16  import org.mule.api.registry.InjectProcessor;
17  import org.mule.api.registry.MuleRegistry;
18  import org.mule.api.registry.ObjectProcessor;
19  import org.mule.api.registry.PreInitProcessor;
20  import org.mule.api.registry.RegistrationException;
21  import org.mule.api.service.Service;
22  import org.mule.api.transformer.Transformer;
23  import org.mule.api.transport.Connector;
24  import org.mule.config.i18n.MessageFactory;
25  import org.mule.lifecycle.phases.NotInLifecyclePhase;
26  import org.mule.util.CollectionUtils;
27  import org.mule.util.StringUtils;
28  
29  import java.util.Collection;
30  import java.util.HashMap;
31  import java.util.Map;
32  import java.util.Map.Entry;
33  import java.util.Set;
34  import java.util.concurrent.locks.Lock;
35  import java.util.concurrent.locks.ReadWriteLock;
36  import java.util.concurrent.locks.ReentrantReadWriteLock;
37  
38  import org.apache.commons.collections.Predicate;
39  import org.apache.commons.collections.functors.InstanceofPredicate;
40  import org.apache.commons.logging.Log;
41  
42  /**
43   * Use the registryLock when reading/writing/iterating over the contents of the registry hashmap.
44   */
45  //@ThreadSafe
46  public class TransientRegistry extends AbstractRegistry
47  {
48      public static final String REGISTRY_ID = "org.mule.Registry.Transient";
49  
50      private final RegistryMap registryMap = new RegistryMap(logger);
51  
52      public TransientRegistry(MuleContext muleContext)
53      {
54          this(REGISTRY_ID, muleContext);
55      }
56  
57      public TransientRegistry(String id, MuleContext muleContext)
58      {
59          super(id, muleContext);
60          putDefaultEntriesIntoRegistry();
61      }
62  
63      private void putDefaultEntriesIntoRegistry()
64      {
65          Map<String, Object> processors = new HashMap<String, Object>();
66          processors.put("_muleContextProcessor", new MuleContextProcessor(muleContext));
67          //processors("_muleNotificationProcessor", new NotificationListenersProcessor(muleContext));
68          processors.put("_muleExpressionEvaluatorProcessor", new ExpressionEvaluatorProcessor(muleContext));
69          processors.put("_muleExpressionEnricherProcessor", new ExpressionEnricherProcessor(muleContext));
70          processors.put("_muleLifecycleStateInjectorProcessor", new LifecycleStateInjectorProcessor(getLifecycleManager().getState()));
71          processors.put("_muleLifecycleManager", getLifecycleManager());
72          registryMap.putAll(processors);
73      }
74  
75      @Override
76      protected void doInitialise() throws InitialisationException
77      {
78          applyProcessors(lookupObjects(Connector.class), null);
79          applyProcessors(lookupObjects(Transformer.class), null);
80          applyProcessors(lookupObjects(ImmutableEndpoint.class), null);
81          applyProcessors(lookupObjects(Agent.class), null);
82          applyProcessors(lookupObjects(Model.class), null);
83          applyProcessors(lookupObjects(Service.class), null);
84          applyProcessors(lookupObjects(Object.class), null);
85      }
86  
87      @Override
88      protected void doDispose()
89      {
90          registryMap.clear();
91      }
92  
93      protected Map<String, Object> applyProcessors(Map<String, Object> objects)
94      {
95          if (objects == null)
96          {
97              return null;
98          }
99  
100         Map<String, Object> results = new HashMap<String, Object>();
101         for (Map.Entry<String, Object> entry : objects.entrySet())
102         {
103             // We do this inside the loop in case the map contains ObjectProcessors
104             Collection<ObjectProcessor> processors = lookupObjects(ObjectProcessor.class);
105             for (ObjectProcessor processor : processors)
106             {
107                 Object result = processor.process(entry.getValue());
108                 if (result != null)
109                 {
110                     results.put(entry.getKey(), result);
111                 }
112             }
113         }
114         return results;
115     }
116 
117 
118     public void registerObjects(Map<String, Object> objects) throws RegistrationException
119     {
120         if (objects == null)
121         {
122             return;
123         }
124 
125         for (Map.Entry<String, Object> entry : objects.entrySet())
126         {
127             registerObject(entry.getKey(), entry.getValue());
128         }
129     }
130 
131     @SuppressWarnings("unchecked")
132     public <T> Map<String, T> lookupByType(Class<T> type)
133     {
134         final Map<String, T> results = new HashMap<String, T>();
135         try
136         {
137             registryMap.lockForReading();
138 
139             for (Map.Entry<String, Object> entry : registryMap.entrySet())
140             {
141                 final Class<?> clazz = entry.getValue().getClass();
142                 if (type.isAssignableFrom(clazz))
143                 {
144                     results.put(entry.getKey(), (T) entry.getValue());
145                 }
146             }
147         }
148         finally
149         {
150             registryMap.unlockForReading();
151         }
152 
153         return results;
154     }
155 
156     public <T> T lookupObject(String key)
157     {
158         return registryMap.<T>get(key);
159     }
160 
161     @SuppressWarnings("unchecked")
162     public <T> Collection<T> lookupObjects(Class<T> returntype)
163     {
164         return (Collection<T>) registryMap.select(new InstanceofPredicate(returntype));
165     }
166 
167     /**
168      * Will fire any lifecycle methods according to the current lifecycle without actually
169      * registering the object in the registry.  This is useful for prototype objects that are created per request and would
170      * clutter the registry with single use objects.
171      *
172      * @param object the object to process
173      * @return the same object with lifecycle methods called (if it has any)
174      * @throws org.mule.api.MuleException if the registry fails to perform the lifecycle change for the object.
175      */
176     Object applyLifecycle(Object object) throws MuleException
177     {
178         getLifecycleManager().applyCompletedPhases(object);
179         return object;
180     }
181 
182     Object applyLifecycle(Object object, String phase) throws MuleException
183     {
184         getLifecycleManager().applyPhase(object, NotInLifecyclePhase.PHASE_NAME, phase);
185         return object;
186     }
187 
188     Object applyProcessors(Object object, Object metadata)
189     {
190         Object theObject = object;
191 
192         if(!hasFlag(metadata, MuleRegistry.INJECT_PROCESSORS_BYPASS_FLAG))
193         {
194             //Process injectors first
195             Collection<InjectProcessor> injectProcessors = lookupObjects(InjectProcessor.class);
196             for (InjectProcessor processor : injectProcessors)
197             {
198                 theObject = processor.process(theObject);
199             }
200         }
201 
202         if(!hasFlag(metadata, MuleRegistry.PRE_INIT_PROCESSORS_BYPASS_FLAG))
203         {
204             //Then any other processors
205             Collection<PreInitProcessor> processors = lookupObjects(PreInitProcessor.class);
206             for (PreInitProcessor processor : processors)
207             {
208                 theObject = processor.process(theObject);
209                 if(theObject==null)
210                 {
211                     return null;
212                 }
213             }
214         }
215         return theObject;
216     }
217 
218     /**
219      * Allows for arbitary registration of transient objects
220      *
221      * @param key
222      * @param value
223      */
224     public void registerObject(String key, Object value) throws RegistrationException
225     {
226         registerObject(key, value, Object.class);
227     }
228 
229     /**
230      * Allows for arbitrary registration of transient objects
231      */
232     public void registerObject(String key, Object object, Object metadata) throws RegistrationException
233     {
234         checkDisposed();
235         if (StringUtils.isBlank(key))
236         {
237             throw new RegistrationException(MessageFactory.createStaticMessage("Attempt to register object with no key"));
238         }
239 
240         if (logger.isDebugEnabled())
241         {
242             logger.debug(String.format("registering key/object %s/%s", key, object));
243         }
244 
245         logger.debug("applying processors");
246         object = applyProcessors(object, metadata);
247         if (object == null)
248         {
249             return;
250         }
251 
252         registryMap.putAndLogWarningIfDuplicate(key, object);
253 
254         try
255         {
256             if (!hasFlag(metadata, MuleRegistry.LIFECYCLE_BYPASS_FLAG))
257             {
258                 if(logger.isDebugEnabled())
259                 {
260                     logger.debug("applying lifecycle to object: " + object);
261                 }
262                 getLifecycleManager().applyCompletedPhases(object);
263             }
264         }
265         catch (MuleException e)
266         {
267             throw new RegistrationException(e);
268         }
269     }
270 
271     protected void checkDisposed() throws RegistrationException
272     {
273         if(getLifecycleManager().isPhaseComplete(Disposable.PHASE_NAME))
274         {
275             throw new RegistrationException(MessageFactory.createStaticMessage("Cannot register objects on the registry as the context is disposed"));
276         }
277     }
278 
279     protected boolean hasFlag(Object metaData, int flag)
280     {
281         return !(metaData == null || !(metaData instanceof Integer)) && ((Integer) metaData & flag) != 0;
282     }
283 
284     /**
285      * Will remove an object by name from the registry. By default the registry will apply all remaining lifecycle phases
286      * to the object when it is removed.
287      *
288      * @param key the name or key of the object to remove from the registry
289      * @param metadata Meta data flags supported are {@link org.mule.api.registry.MuleRegistry#LIFECYCLE_BYPASS_FLAG}
290      * @throws RegistrationException if there is a problem unregistering the object. Typically this will be because
291      * the object's lifecycle threw an exception
292      */
293     public void unregisterObject(String key, Object metadata) throws RegistrationException
294     {
295         Object obj = registryMap.remove(key);
296 
297         try
298         {
299             if (!hasFlag(metadata, MuleRegistry.LIFECYCLE_BYPASS_FLAG))
300             {
301                 getLifecycleManager().applyPhase(obj, lifecycleManager.getCurrentPhase(), Disposable.PHASE_NAME);
302             }
303         }
304         catch (MuleException e)
305         {
306             throw new RegistrationException(e);
307         }
308 
309     }
310 
311     public void unregisterObject(String key) throws RegistrationException
312     {
313         unregisterObject(key, Object.class);
314     }
315 
316     // /////////////////////////////////////////////////////////////////////////
317     // Registry Metadata
318     // /////////////////////////////////////////////////////////////////////////
319 
320     public boolean isReadOnly()
321     {
322         return false;
323     }
324 
325     public boolean isRemote()
326     {
327         return false;
328     }
329 
330     /**
331      * This class encapsulates the {@link HashMap} that's used for storing the objects in the
332      * transient registry and also shields client code from having to deal with locking the
333      * {@link ReadWriteLock} for the exposed Map operations.
334      */
335     private static class RegistryMap
336     {
337         private final Map<String, Object> registry = new HashMap<String, Object>();
338         private final ReadWriteLock registryLock = new ReentrantReadWriteLock();
339 
340         private Log logger;
341 
342         public RegistryMap(Log log)
343         {
344             super();
345             logger = log;
346         }
347 
348         public Collection<?> select(Predicate predicate)
349         {
350             Lock readLock = registryLock.readLock();
351             try
352             {
353                 readLock.lock();
354                 return CollectionUtils.select(registry.values(), predicate);
355             }
356             finally
357             {
358                 readLock.unlock();
359             }
360         }
361 
362         public void clear()
363         {
364             Lock writeLock = registryLock.writeLock();
365             try
366             {
367                 writeLock.lock();
368                 registry.clear();
369             }
370             finally
371             {
372                 writeLock.unlock();
373             }
374         }
375 
376         public void putAndLogWarningIfDuplicate(String key, Object object)
377         {
378             Lock writeLock = registryLock.writeLock();
379             try
380             {
381                 writeLock.lock();
382 
383                 if (registry.containsKey(key))
384                 {
385                     // registry.put(key, value) would overwrite a previous entity with the same name.  Is this really what we want?
386                     // Not sure whether to throw an exception or log a warning here.
387                     //throw new RegistrationException("TransientRegistry already contains an object named '" + key + "'.  The previous object would be overwritten.");
388                     logger.warn("TransientRegistry already contains an object named '" + key + "'.  The previous object will be overwritten.");
389                 }
390                 registry.put(key, object);
391             }
392             finally
393             {
394                 writeLock.unlock();
395             }
396         }
397 
398         public void putAll(Map<String, Object> map)
399         {
400             Lock writeLock = registryLock.writeLock();
401             try
402             {
403                 writeLock.lock();
404                 registry.putAll(map);
405             }
406             finally
407             {
408                 writeLock.unlock();
409             }
410         }
411 
412         @SuppressWarnings("unchecked")
413         public <T> T get(String key)
414         {
415             Lock readLock = registryLock.readLock();
416             try
417             {
418                 readLock.lock();
419                 return (T) registry.get(key);
420             }
421             finally
422             {
423                 readLock.unlock();
424             }
425         }
426 
427         public Object remove(String key)
428         {
429             Lock writeLock = registryLock.writeLock();
430             try
431             {
432                 writeLock.lock();
433                 return registry.remove(key);
434             }
435             finally
436             {
437                 writeLock.unlock();
438             }
439         }
440 
441         public Set<Entry<String, Object>> entrySet()
442         {
443             return registry.entrySet();
444         }
445 
446         public void lockForReading()
447         {
448             registryLock.readLock().lock();
449         }
450 
451         public void unlockForReading()
452         {
453             registryLock.readLock().unlock();
454         }
455     }
456 }