View Javadoc

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