View Javadoc

1   /*
2    * $Id: AbstractRegistry.java 12269 2008-07-10 04:19:03Z dfeist $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.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  
11  package org.mule.registry;
12  
13  import org.mule.MuleServer;
14  import org.mule.RegistryContext;
15  import org.mule.api.MuleException;
16  import org.mule.api.MuleRuntimeException;
17  import org.mule.api.agent.Agent;
18  import org.mule.api.config.MuleProperties;
19  import org.mule.api.context.MuleContextAware;
20  import org.mule.api.endpoint.EndpointBuilder;
21  import org.mule.api.endpoint.EndpointFactory;
22  import org.mule.api.endpoint.ImmutableEndpoint;
23  import org.mule.api.lifecycle.Disposable;
24  import org.mule.api.lifecycle.Initialisable;
25  import org.mule.api.lifecycle.InitialisationException;
26  import org.mule.api.lifecycle.LifecycleManager;
27  import org.mule.api.model.Model;
28  import org.mule.api.registry.RegistrationException;
29  import org.mule.api.registry.Registry;
30  import org.mule.api.service.Service;
31  import org.mule.api.transformer.DiscoverableTransformer;
32  import org.mule.api.transformer.Transformer;
33  import org.mule.api.transformer.TransformerException;
34  import org.mule.api.transport.Connector;
35  import org.mule.config.i18n.CoreMessages;
36  import org.mule.transformer.TransformerCollection;
37  import org.mule.transformer.TransformerWeighting;
38  import org.mule.transformer.simple.ObjectToByteArray;
39  import org.mule.transformer.simple.ObjectToString;
40  import org.mule.util.CollectionUtils;
41  import org.mule.util.UUID;
42  import org.mule.util.expression.ExpressionEvaluatorManager;
43  
44  import java.util.ArrayList;
45  import java.util.Collection;
46  import java.util.Iterator;
47  import java.util.List;
48  import java.util.Map;
49  
50  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
51  
52  import org.apache.commons.logging.Log;
53  import org.apache.commons.logging.LogFactory;
54  
55  
56  public abstract class AbstractRegistry implements Registry
57  {
58      private static final ObjectToString objectToString = new ObjectToString();
59      private static final ObjectToByteArray objectToByteArray = new ObjectToByteArray();
60  
61      private Registry parent;
62      /** the unique id for this Registry */
63      private String id;
64  
65      private int defaultScope = DEFAULT_SCOPE;
66  
67      protected transient Log logger = LogFactory.getLog(getClass());
68  
69      protected LifecycleManager lifecycleManager;
70      protected Map transformerListCache = new ConcurrentHashMap(8);
71      protected Map exactTransformerCache = new ConcurrentHashMap(8);
72  
73      /** Default Constructor */
74      protected AbstractRegistry(String id)
75      {
76          if (id == null)
77          {
78              throw new MuleRuntimeException(CoreMessages.objectIsNull("RegistryID"));
79          }
80          this.id = id;
81          lifecycleManager = createLifecycleManager();
82      }
83  
84      protected AbstractRegistry(String id, Registry parent)
85      {
86          this(id);
87          setParent(parent);
88      }
89  
90      protected abstract LifecycleManager createLifecycleManager();
91  
92      protected LifecycleManager getLifecycleManager()
93      {
94          return lifecycleManager;
95      }
96  
97      public final synchronized void dispose()
98      {
99          // TODO lifecycleManager.checkPhase(Disposable.PHASE_NAME);
100 
101         if (isDisposed())
102         {
103             return;
104         }
105 
106         try
107         {
108             exactTransformerCache.clear();
109             transformerListCache.clear();
110 
111             doDispose();
112             lifecycleManager.firePhase(MuleServer.getMuleContext(), Disposable.PHASE_NAME);
113             if (getParent() != null)
114             {
115                 parent.dispose();
116             }
117             else
118             {
119                 // remove this reference once there is no one else left to dispose
120                 RegistryContext.setRegistry(null);
121                 ExpressionEvaluatorManager.clearEvaluators();
122             }
123         }
124         catch (MuleException e)
125         {
126             // TODO
127             logger.error("Failed to cleanly dispose: " + e.getMessage(), e);
128         }
129     }
130 
131     protected void doDispose()
132     {
133         // hook for subclasses to do their disposal
134     }
135 
136     public boolean isDisposed()
137     {
138         return lifecycleManager.isPhaseComplete(Disposable.PHASE_NAME);
139     }
140 
141     public boolean isDisposing()
142     {
143         return Disposable.PHASE_NAME.equals(lifecycleManager.getExecutingPhase());
144     }
145 
146     public boolean isInitialised()
147     {
148         return lifecycleManager.isPhaseComplete(Initialisable.PHASE_NAME);
149     }
150 
151     public boolean isInitialising()
152     {
153         return Initialisable.PHASE_NAME.equals(lifecycleManager.getExecutingPhase());
154     }
155 
156     public final void initialise() throws InitialisationException
157     {
158         lifecycleManager.checkPhase(Initialisable.PHASE_NAME);
159 
160 //        if (getParent() != null)
161 //        {
162 //            parent.initialise();
163 //        }
164 
165         // I don't think it makes sense for the Registry to know about the MuleContext at this point.
166         // MuleContext mc = MuleServer.getMuleContext();
167         // if (mc != null)
168         // {
169         // mc.fireNotification(new RegistryNotification(this, RegistryNotification.REGISTRY_INITIALISING));
170         // }
171 
172         if (id == null)
173         {
174             logger.warn("No unique id has been set on this registry");
175             id = UUID.getUUID();
176         }
177         try
178         {
179             doInitialise();
180             lifecycleManager.firePhase(MuleServer.getMuleContext(), Initialisable.PHASE_NAME);
181         }
182         catch (InitialisationException e)
183         {
184             throw e;
185         }
186         catch (Exception e)
187         {
188             throw new InitialisationException(e, this);
189         }
190     }
191 
192     protected void doInitialise() throws InitialisationException
193     {
194         // hook for subclasses do to their initialisation
195     }
196 
197 
198     public Connector lookupConnector(String name)
199     {
200         return (Connector) lookupObject(name);
201     }
202 
203     /**
204      * Removed this method from {@link Registry} API as it should only be used
205      * internally and may confuse users. The {@link EndpointFactory} should be used
206      * for creating endpoints.<br/><br/> Looks up an returns endpoints registered in the
207      * registry by their idendifier (currently endpoint name)<br/><br/ <b>NOTE:
208      * This method does not create new endpoint instances, but rather returns
209      * existing endpoint instances that have been registered. This lookup method
210      * should be avoided and the intelligent, role specific endpoint lookup methods
211      * should be used instead.<br/><br/>
212      * 
213      * @param name the idendtifer/name used to register endpoint in registry
214      * @see #lookupInboundEndpoint(String, org.mule.api.MuleContext)
215      * @see #lookupResponseEndpoint(String, org.mule.api.MuleContext)
216      */
217     public ImmutableEndpoint lookupEndpoint(String name)
218     {
219         Object obj = lookupObject(name);
220         if (obj instanceof ImmutableEndpoint)
221         {
222             return (ImmutableEndpoint) obj;
223         }
224         else
225         {
226             logger.debug("No endpoint with the name: "
227                     + name
228                     + "found.  If "
229                     + name
230                     + " is a global endpoint you should use the EndpointFactory to create endpoint instances from global endpoints.");
231             return null;
232         }
233     }
234 
235     public EndpointBuilder lookupEndpointBuilder(String name)
236     {
237         Object o = lookupObject(name);
238         if (o instanceof EndpointBuilder)
239         {
240             logger.debug("Global endpoint EndpointBuilder for name: " + name + " found");
241             return (EndpointBuilder) o;
242         }
243         else
244         {
245             logger.debug("No endpoint builder with the name: " + name + " found.");
246             return null;
247         }
248     }
249 
250     public EndpointFactory lookupEndpointFactory()
251     {
252         return (EndpointFactory) lookupObject(MuleProperties.OBJECT_MULE_ENDPOINT_FACTORY);
253     }
254 
255     public Transformer lookupTransformer(String name)
256     {
257         return (Transformer) lookupObject(name);
258     }
259 
260     /** {@inheritDoc} */
261     public Transformer lookupTransformer(Class inputType, Class outputType) throws TransformerException
262     {
263         Transformer result = (Transformer) exactTransformerCache.get(inputType.getName() + outputType.getName());
264         if (result != null)
265         {
266             return result;
267         }
268         List trans = lookupTransformers(inputType, outputType);
269 
270         result = getNearestTransformerMatch(trans, inputType, outputType);
271         //If an exact mach is not found, we have a 'second pass' transformer that can be used to converting to String or
272         //byte[]
273         Transformer secondPass = null;
274 
275         if (result == null)
276         {
277             //If no transformers were found but the outputType type is String or byte[] we can perform a more general search
278             // using Object.class and then convert to String or byte[] using the second pass transformer
279             if (outputType.equals(String.class))
280             {
281                 secondPass = objectToString;
282             }
283             else if (outputType.equals(byte[].class))
284             {
285                 secondPass = objectToByteArray;
286             }
287             else
288             {
289                 throw new TransformerException(CoreMessages.noTransformerFoundForMessage(inputType, outputType));
290             }
291             //Perform a more general search
292             trans = lookupTransformers(inputType, Object.class);
293 
294             result = getNearestTransformerMatch(trans, inputType, outputType);
295             if (result != null)
296             {
297                 result = new TransformerCollection(new Transformer[]{result, secondPass});
298             }
299         }
300 
301         if (result != null)
302         {
303             exactTransformerCache.put(inputType.getName() + outputType.getName(), result);
304         }
305         return result;
306     }
307 
308     protected Transformer getNearestTransformerMatch(List trans, Class input, Class output) throws TransformerException
309     {
310         if (trans.size() > 1)
311         {
312             TransformerWeighting weighting = null;
313             for (Iterator iterator = trans.iterator(); iterator.hasNext();)
314             {
315                 Transformer transformer = (Transformer) iterator.next();
316                 TransformerWeighting current = new TransformerWeighting(input, output, transformer);
317                 if (weighting == null)
318                 {
319                     weighting = current;
320                 }
321                 else
322                 {
323                     int compare = current.compareTo(weighting);
324                     if (compare == 1)
325                     {
326                         weighting = current;
327                     }
328                     else if (compare == 0)
329                     {
330                         //We may have two transformers that are exactly the same, in which case we can use either i.e. use the current
331                         if (!weighting.getTransformer().getClass().equals(current.getTransformer().getClass()))
332                         {
333                             throw new TransformerException(CoreMessages.transformHasMultipleMatches(input, output,
334                                     current.getTransformer(), weighting.getTransformer()));
335                         }
336                     }
337                 }
338             }
339             return weighting.getTransformer();
340         }
341         else if (trans.size() == 0)
342         {
343             return null;
344         }
345         else
346         {
347             return (Transformer) trans.get(0);
348         }
349     }
350 
351     /** {@inheritDoc} */
352     public List lookupTransformers(Class input, Class output)
353     {
354         List results = (List) transformerListCache.get(input.getName() + output.getName());
355         if (results != null)
356         {
357             return results;
358         }
359 
360         results = new ArrayList(2);
361         Collection transformers = getTransformers();
362         for (Iterator itr = transformers.iterator(); itr.hasNext();)
363         {
364             Transformer t = (Transformer) itr.next();
365             //The transformer must have the DiscoveryTransformer interface if we are going to
366             //find it here
367             if (!(t instanceof DiscoverableTransformer))
368             {
369                 continue;
370             }
371             Class c = t.getReturnClass();
372             //TODO RM* this sohuld be an exception
373             if (c == null)
374             {
375                 c = Object.class;
376             }
377             if (output.isAssignableFrom(c)
378                     && t.isSourceTypeSupported(input))
379             {
380                 results.add(t);
381             }
382         }
383 
384         transformerListCache.put(input.getName() + output.getName(), results);
385         return results;
386     }
387 
388     public Model lookupModel(String name)
389     {
390         return (Model) lookupObject(name);
391     }
392 
393     public Model lookupSystemModel()
394     {
395         return lookupModel(MuleProperties.OBJECT_SYSTEM_MODEL);
396     }
397 
398     public Collection getModels()
399     {
400         return lookupObjects(Model.class);
401     }
402 
403     public Collection getConnectors()
404     {
405         return lookupObjects(Connector.class);
406     }
407 
408     public Collection getAgents()
409     {
410         return lookupObjects(Agent.class);
411     }
412 
413     public Collection getEndpoints()
414     {
415         return lookupObjects(ImmutableEndpoint.class);
416     }
417 
418     public Collection getTransformers()
419     {
420         return lookupObjects(Transformer.class);
421     }
422 
423     public Agent lookupAgent(String name)
424     {
425         return (Agent) lookupObject(name);
426     }
427 
428     public Service lookupService(String name)
429     {
430         return (Service) lookupObject(name);
431     }
432 
433     public Collection/*<Service>*/ lookupServices()
434     {
435         return lookupObjects(Service.class);
436     }
437 
438     public Collection/*<Service>*/ lookupServices(String model)
439     {
440         Collection/*<Service>*/ components = lookupServices();
441         List modelComponents = new ArrayList();
442         Iterator it = components.iterator();
443         Service service;
444         while (it.hasNext())
445         {
446             service = (Service) it.next();
447             // TODO Make this comparison more robust.
448             if (model.equals(service.getModel().getName()))
449             {
450                 modelComponents.add(service);
451             }
452         }
453         return modelComponents;
454     }
455 
456     public final Object lookupObject(String key, int scope)
457     {
458         logger.debug("lookupObject: key=" + key + " scope=" + scope);
459         Object o = doLookupObject(key);
460 
461         if (o == null)
462         {
463             if (logger.isDebugEnabled())
464             {
465                 logger.debug("Failed to find object in Registry ID: " + getRegistryId());
466             }
467             if (getParent() != null && scope > SCOPE_IMMEDIATE)
468             {
469                 if (getParent().isRemote() && scope == SCOPE_REMOTE)
470                 {
471                     o = getParent().lookupObject(key);
472                 }
473                 else if (!getParent().isRemote() && scope >= SCOPE_LOCAL)
474                 {
475                     o = getParent().lookupObject(key);
476                 }
477             }
478         }
479         return o;
480     }
481 
482     public final Object lookupObject(Class type) throws RegistrationException
483     {
484         return lookupObject(type, getDefaultScope());
485     }
486 
487     /** 
488      * Look up a single object by type.  
489      * @return null if no object is found
490      * @throws RegistrationException if more than one object is found
491      */
492     public final Object lookupObject(Class type, int scope) throws RegistrationException
493     {
494         Collection collection = lookupObjects(type, scope);
495         if (collection == null || collection.size() < 1)
496         {
497             return null;
498         }
499         else if (collection.size() > 1)
500         {
501             throw new RegistrationException("More than one object of type " + type + " was found in registry, but only 1 was expected.");
502         }
503         else
504         {
505             return collection.iterator().next();
506         }
507     }
508 
509     public final Collection lookupObjects(Class type)
510     {
511         return lookupObjects(type, getDefaultScope());
512     }
513 
514     public final Collection lookupObjects(Class type, int scope)
515     {
516         logger.debug("lookupObjects: type=" + type + " scope=" + scope);
517         Collection collection = doLookupObjects(type);
518         if (collection == null)
519         {
520             collection = new ArrayList();
521         }
522 
523         if (getParent() != null && scope > SCOPE_IMMEDIATE)
524         {
525             if (getParent().isRemote() && scope == SCOPE_REMOTE)
526             {
527                 Collection collection2 = getParent().lookupObjects(type);
528                 if (collection2 != null)
529                 {
530                     collection.addAll(collection2);
531                 }
532             }
533             else if (!getParent().isRemote() && scope >= SCOPE_LOCAL)
534             {
535                 Collection collection2 = getParent().lookupObjects(type);
536                 if (collection2 != null)
537                 {
538                     collection = CollectionUtils.union(collection, collection2);
539                 }
540             }
541         }
542 
543         return collection;
544     }
545 
546     protected abstract Collection doLookupObjects(Class type);
547 
548     public Object lookupObject(String key)
549     {
550         return lookupObject(key, getDefaultScope());
551     }
552 
553     /**
554      * Initialises all registered agents
555      *
556      * @throws org.mule.api.lifecycle.InitialisationException
557      */
558     // TODO: Spring is now taking care of the initialisation lifecycle, need to check that we still get this
559     // problem
560     // protected void initialiseAgents() throws InitialisationException
561     // {
562     // logger.info("Initialising agents...");
563     //
564     // // Do not iterate over the map directly, as 'complex' agents
565     // // may spawn extra agents during initialisation. This will
566     // // cause a ConcurrentModificationException.
567     // // Use a cursorable iteration, which supports on-the-fly underlying
568     // // data structure changes.
569     // Collection agentsSnapshot = lookupCollection(Agent.class).values();
570     // CursorableLinkedList agentRegistrationQueue = new CursorableLinkedList(agentsSnapshot);
571     // CursorableLinkedList.Cursor cursor = agentRegistrationQueue.cursor();
572     //
573     // // the actual agent object refs are the same, so we are just
574     // // providing different views of the same underlying data
575     //
576     // try
577     // {
578     // while (cursor.hasNext())
579     // {
580     // Agent umoAgent = (Agent) cursor.next();
581     //
582     // int originalSize = agentsSnapshot.size();
583     // logger.debug("Initialising agent: " + umoAgent.getName());
584     // umoAgent.initialise();
585     // // thank you, we are done with you
586     // cursor.remove();
587     //
588     // // Direct calls to MuleManager.registerAgent() modify the original
589     // // agents map, re-check if the above agent registered any
590     // // 'child' agents.
591     // int newSize = agentsSnapshot.size();
592     // int delta = newSize - originalSize;
593     // if (delta > 0)
594     // {
595     // // TODO there's some mess going on in
596     // // http://issues.apache.org/jira/browse/COLLECTIONS-219
597     // // watch out when upgrading the commons-collections.
598     // Collection tail = CollectionUtils.retainAll(agentsSnapshot, agentRegistrationQueue);
599     // Collection head = CollectionUtils.subtract(agentsSnapshot, tail);
600     //
601     // // again, above are only refs, all going back to the original agents map
602     //
603     // // re-order the queue
604     // agentRegistrationQueue.clear();
605     // // 'spawned' agents first
606     // agentRegistrationQueue.addAll(head);
607     // // and the rest
608     // agentRegistrationQueue.addAll(tail);
609     //
610     // // update agents map with a new order in case we want to re-initialise
611     // // MuleManager on the fly
612     // for (Iterator it = agentRegistrationQueue.iterator(); it.hasNext();)
613     // {
614     // Agent theAgent = (Agent) it.next();
615     // theAgent.initialise();
616     // }
617     // }
618     // }
619     // }
620     // finally
621     // {
622     // // close the cursor as per JavaDoc
623     // cursor.close();
624     // }
625     // logger.info("Agents Successfully Initialised");
626     // }
627     /** @return null if object not found */
628     protected abstract Object doLookupObject(String key);
629 
630     protected void unsupportedOperation(String operation, Object o) throws UnsupportedOperationException
631     {
632         throw new UnsupportedOperationException(
633                 "Registry: "
634                         + getRegistryId()
635                         + " is read-only so objects cannot be registered or unregistered. Failed to execute operation "
636                         + operation + " on object: " + o);
637     }
638 
639     public final void registerObject(String key, Object value) throws RegistrationException
640     {
641         registerObject(key, value, null);
642     }
643 
644     public final void registerObject(String key,
645                                      Object value,
646                                      Object metadata) throws RegistrationException
647     {
648         
649         logger.debug("registerObject: key=" + key + " value=" + value + " metadata=" + metadata);
650         if (value instanceof MuleContextAware)
651         {
652             ((MuleContextAware) value).setMuleContext(MuleServer.getMuleContext());
653         }
654         doRegisterObject(key, value, metadata);
655     }
656 
657     protected abstract void doRegisterObject(String key,
658                                             Object value,
659                                             Object metadata) throws RegistrationException;
660     
661     public final void registerTransformer(Transformer transformer) throws MuleException
662     {
663         if (transformer instanceof DiscoverableTransformer)
664         {
665             exactTransformerCache.clear();
666             transformerListCache.clear();
667         }
668         doRegisterTransformer(transformer);
669 
670     }
671 
672     protected abstract void doRegisterTransformer(Transformer transformer) throws MuleException;
673 
674     // /////////////////////////////////////////////////////////////////////////
675     // Registry Metadata
676     // /////////////////////////////////////////////////////////////////////////
677 
678     public final String getRegistryId()
679     {
680         return id;
681     }
682 
683     public Registry getParent()
684     {
685         return parent;
686     }
687 
688     public void setParent(Registry registry)
689     {
690         this.parent = registry;
691     }
692 
693     public int getDefaultScope()
694     {
695         return defaultScope;
696     }
697 
698     public void setDefaultScope(int scope)
699     {
700         if (scope < SCOPE_IMMEDIATE || scope > SCOPE_REMOTE)
701         {
702             throw new IllegalArgumentException("Invalid value for scope: " + scope);
703         }
704         defaultScope = scope;
705     }
706 }