001: /*
002: * $Id: AbstractRegistry.java 11231 2008-03-06 21:17:37Z rossmason $
003: * --------------------------------------------------------------------------------------
004: * Copyright (c) MuleSource, Inc. All rights reserved. http://www.mulesource.com
005: *
006: * The software in this package is published under the terms of the CPAL v1.0
007: * license, a copy of which has been included with this distribution in the
008: * LICENSE.txt file.
009: */
010:
011: package org.mule.registry;
012:
013: import org.mule.MuleServer;
014: import org.mule.RegistryContext;
015: import org.mule.api.MuleException;
016: import org.mule.api.agent.Agent;
017: import org.mule.api.config.MuleProperties;
018: import org.mule.api.context.MuleContextAware;
019: import org.mule.api.endpoint.EndpointBuilder;
020: import org.mule.api.endpoint.EndpointFactory;
021: import org.mule.api.endpoint.ImmutableEndpoint;
022: import org.mule.api.lifecycle.Disposable;
023: import org.mule.api.lifecycle.Initialisable;
024: import org.mule.api.lifecycle.InitialisationException;
025: import org.mule.api.lifecycle.LifecycleManager;
026: import org.mule.api.lifecycle.LifecycleTransitionResult;
027: import org.mule.api.model.Model;
028: import org.mule.api.registry.RegistrationException;
029: import org.mule.api.registry.Registry;
030: import org.mule.api.service.Service;
031: import org.mule.api.transformer.DiscoverableTransformer;
032: import org.mule.api.transformer.Transformer;
033: import org.mule.api.transformer.TransformerException;
034: import org.mule.api.transport.Connector;
035: import org.mule.config.i18n.CoreMessages;
036: import org.mule.transformer.TransformerCollection;
037: import org.mule.transformer.TransformerWeighting;
038: import org.mule.transformer.simple.ObjectToByteArray;
039: import org.mule.transformer.simple.ObjectToString;
040: import org.mule.util.CollectionUtils;
041: import org.mule.util.UUID;
042: import org.mule.util.expression.ExpressionEvaluatorManager;
043:
044: import java.util.ArrayList;
045: import java.util.Collection;
046: import java.util.Iterator;
047: import java.util.List;
048: import java.util.Map;
049:
050: import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
051:
052: import org.apache.commons.logging.Log;
053: import org.apache.commons.logging.LogFactory;
054:
055: public abstract class AbstractRegistry implements Registry {
056: private static final ObjectToString objectToString = new ObjectToString();
057: private static final ObjectToByteArray objectToByteArray = new ObjectToByteArray();
058:
059: private Registry parent;
060: /** the unique id for this Registry */
061: private String id;
062:
063: private int defaultScope = DEFAULT_SCOPE;
064:
065: protected transient Log logger = LogFactory.getLog(getClass());
066:
067: protected LifecycleManager lifecycleManager;
068: protected Map transformerListCache = new ConcurrentHashMap(8);
069: protected Map exactTransformerCache = new ConcurrentHashMap(8);
070:
071: /** Default Constructor */
072: protected AbstractRegistry(String id) {
073: if (id == null) {
074: throw new NullPointerException(CoreMessages.objectIsNull(
075: "RegistryID").getMessage());
076: }
077: this .id = id;
078: lifecycleManager = createLifecycleManager();
079: }
080:
081: protected AbstractRegistry(String id, Registry parent) {
082: this (id);
083: setParent(parent);
084: }
085:
086: protected abstract LifecycleManager createLifecycleManager();
087:
088: protected LifecycleManager getLifecycleManager() {
089: return lifecycleManager;
090: }
091:
092: public final synchronized void dispose() {
093: // TODO lifecycleManager.checkPhase(Disposable.PHASE_NAME);
094:
095: if (isDisposed()) {
096: return;
097: }
098:
099: try {
100: exactTransformerCache.clear();
101: transformerListCache.clear();
102:
103: doDispose();
104: lifecycleManager.firePhase(MuleServer.getMuleContext(),
105: Disposable.PHASE_NAME);
106: if (getParent() != null) {
107: parent.dispose();
108: } else {
109: // remove this reference once there is no one else left to dispose
110: RegistryContext.setRegistry(null);
111: ExpressionEvaluatorManager.clearEvaluators();
112: }
113: } catch (MuleException e) {
114: // TODO
115: logger.error(
116: "Failed to cleanly dispose: " + e.getMessage(), e);
117: }
118: }
119:
120: protected void doDispose() {
121:
122: }
123:
124: public boolean isDisposed() {
125: return lifecycleManager.isPhaseComplete(Disposable.PHASE_NAME);
126: }
127:
128: public boolean isDisposing() {
129: return Disposable.PHASE_NAME.equals(lifecycleManager
130: .getExecutingPhase());
131: }
132:
133: public boolean isInitialised() {
134: return lifecycleManager
135: .isPhaseComplete(Initialisable.PHASE_NAME);
136: }
137:
138: public boolean isInitialising() {
139: return Initialisable.PHASE_NAME.equals(lifecycleManager
140: .getExecutingPhase());
141: }
142:
143: public final LifecycleTransitionResult initialise()
144: throws InitialisationException {
145: lifecycleManager.checkPhase(Initialisable.PHASE_NAME);
146:
147: // if (getParent() != null)
148: // {
149: // parent.initialise();
150: // }
151:
152: // I don't think it makes sense for the Registry to know about the MuleContext at this point.
153: // MuleContext mc = MuleServer.getMuleContext();
154: // if (mc != null)
155: // {
156: // mc.fireNotification(new RegistryNotification(this, RegistryNotification.REGISTRY_INITIALISING));
157: // }
158:
159: if (id == null) {
160: logger.warn("No unique id has been set on this registry");
161: id = UUID.getUUID();
162: }
163: try {
164: doInitialise();
165: lifecycleManager.firePhase(MuleServer.getMuleContext(),
166: Initialisable.PHASE_NAME);
167: } catch (InitialisationException e) {
168: throw e;
169: } catch (Exception e) {
170: throw new InitialisationException(e, this );
171: }
172: return LifecycleTransitionResult.OK;
173: }
174:
175: protected void doInitialise() throws InitialisationException {
176:
177: }
178:
179: public Connector lookupConnector(String name) {
180: return (Connector) lookupObject(name);
181: }
182:
183: /**
184: * Removed this method from {@link Registry} API as it should only be used
185: * internally and may confuse users. The {@link EndpointFactory} should be used
186: * for creating endpoints.<br/><br/> Looks up an returns endpoints registered in the
187: * registry by their idendifier (currently endpoint name)<br/><br/ <b>NOTE:
188: * This method does not create new endpoint instances, but rather returns
189: * existing endpoint instances that have been registered. This lookup method
190: * should be avoided and the intelligent, role specific endpoint lookup methods
191: * should be used instead.<br/><br/>
192: *
193: * @param name the idendtifer/name used to register endpoint in registry
194: * @see #lookupInboundEndpoint(String, org.mule.api.MuleContext)
195: * @see #lookupResponseEndpoint(String, org.mule.api.MuleContext)
196: */
197: public ImmutableEndpoint lookupEndpoint(String name) {
198: Object obj = lookupObject(name);
199: if (obj instanceof ImmutableEndpoint) {
200: return (ImmutableEndpoint) obj;
201: } else {
202: logger
203: .debug("No endpoint with the name: "
204: + name
205: + "found. If "
206: + name
207: + " is a global endpoint you should use the EndpointFactory to create endpoint instances from global endpoints.");
208: return null;
209: }
210: }
211:
212: public EndpointBuilder lookupEndpointBuilder(String name) {
213: Object o = lookupObject(name);
214: if (o instanceof EndpointBuilder) {
215: logger.debug("Global endpoint EndpointBuilder for name: "
216: + name + " found");
217: return (EndpointBuilder) o;
218: } else {
219: logger.debug("No endpoint builder with the name: " + name
220: + " found.");
221: return null;
222: }
223: }
224:
225: public EndpointFactory lookupEndpointFactory() {
226: return (EndpointFactory) lookupObject(MuleProperties.OBJECT_MULE_ENDPOINT_FACTORY);
227: }
228:
229: public Transformer lookupTransformer(String name) {
230: return (Transformer) lookupObject(name);
231: }
232:
233: /** {@inheritDoc} */
234: public Transformer lookupTransformer(Class inputType,
235: Class outputType) throws TransformerException {
236: Transformer result = (Transformer) exactTransformerCache
237: .get(inputType.getName() + outputType.getName());
238: if (result != null) {
239: return result;
240: }
241: List trans = lookupTransformers(inputType, outputType);
242:
243: result = getNearestTransformerMatch(trans, inputType,
244: outputType);
245: //If an exact mach is not found, we have a 'second pass' transformer that can be used to converting to String or
246: //byte[]
247: Transformer secondPass = null;
248:
249: if (result == null) {
250: //If no transformers were found but the outputType type is String or byte[] we can perform a more general search
251: // using Object.class and then convert to String or byte[] using the second pass transformer
252: if (outputType.equals(String.class)) {
253: secondPass = objectToString;
254: } else if (outputType.equals(byte[].class)) {
255: secondPass = objectToByteArray;
256: } else {
257: throw new TransformerException(CoreMessages
258: .noTransformerFoundForMessage(inputType,
259: outputType));
260: }
261: //Perform a more general search
262: trans = lookupTransformers(inputType, Object.class);
263:
264: result = getNearestTransformerMatch(trans, inputType,
265: outputType);
266: if (result != null) {
267: result = new TransformerCollection(new Transformer[] {
268: result, secondPass });
269: }
270: }
271:
272: if (result != null) {
273: exactTransformerCache.put(inputType.getName()
274: + outputType.getName(), result);
275: }
276: return result;
277: }
278:
279: protected Transformer getNearestTransformerMatch(List trans,
280: Class input, Class output) throws TransformerException {
281: if (trans.size() > 1) {
282: TransformerWeighting weighting = null;
283: for (Iterator iterator = trans.iterator(); iterator
284: .hasNext();) {
285: Transformer transformer = (Transformer) iterator.next();
286: TransformerWeighting current = new TransformerWeighting(
287: input, output, transformer);
288: if (weighting == null) {
289: weighting = current;
290: } else {
291: int compare = current.compareTo(weighting);
292: if (compare == 1) {
293: weighting = current;
294: } else if (compare == 0) {
295: //We may have two transformers that are exactly the same, in which case we can use either i.e. use the current
296: if (!weighting.getTransformer().getClass()
297: .equals(
298: current.getTransformer()
299: .getClass())) {
300: throw new TransformerException(CoreMessages
301: .transformHasMultipleMatches(input,
302: output, current
303: .getTransformer(),
304: weighting.getTransformer()));
305: }
306: }
307: }
308: }
309: return weighting.getTransformer();
310: } else if (trans.size() == 0) {
311: return null;
312: } else {
313: return (Transformer) trans.get(0);
314: }
315: }
316:
317: /** {@inheritDoc} */
318: public List lookupTransformers(Class input, Class output) {
319: List results = (List) transformerListCache.get(input.getName()
320: + output.getName());
321: if (results != null) {
322: return results;
323: }
324:
325: results = new ArrayList(2);
326: Collection transformers = getTransformers();
327: for (Iterator itr = transformers.iterator(); itr.hasNext();) {
328: Transformer t = (Transformer) itr.next();
329: //The transformer must have the DiscoveryTransformer interface if we are going to
330: //find it here
331: if (!(t instanceof DiscoverableTransformer)) {
332: continue;
333: }
334: Class c = t.getReturnClass();
335: //TODO RM* this sohuld be an exception
336: if (c == null) {
337: c = Object.class;
338: }
339: if (output.isAssignableFrom(c)
340: && t.isSourceTypeSupported(input)) {
341: results.add(t);
342: }
343: }
344:
345: transformerListCache.put(input.getName() + output.getName(),
346: results);
347: return results;
348: }
349:
350: public Model lookupModel(String name) {
351: return (Model) lookupObject(name);
352: }
353:
354: public Model lookupSystemModel() {
355: return lookupModel(MuleProperties.OBJECT_SYSTEM_MODEL);
356: }
357:
358: public Collection getModels() {
359: return lookupObjects(Model.class);
360: }
361:
362: public Collection getConnectors() {
363: return lookupObjects(Connector.class);
364: }
365:
366: public Collection getAgents() {
367: return lookupObjects(Agent.class);
368: }
369:
370: public Collection getEndpoints() {
371: return lookupObjects(ImmutableEndpoint.class);
372: }
373:
374: public Collection getTransformers() {
375: return lookupObjects(Transformer.class);
376: }
377:
378: public Agent lookupAgent(String name) {
379: return (Agent) lookupObject(name);
380: }
381:
382: public Service lookupService(String name) {
383: return (Service) lookupObject(name);
384: }
385:
386: public Collection/*<Service>*/lookupServices() {
387: return lookupObjects(Service.class);
388: }
389:
390: public Collection/*<Service>*/lookupServices(String model) {
391: Collection/*<Service>*/components = lookupServices();
392: List modelComponents = new ArrayList();
393: Iterator it = components.iterator();
394: Service service;
395: while (it.hasNext()) {
396: service = (Service) it.next();
397: // TODO Make this comparison more robust.
398: if (model.equals(service.getModel().getName())) {
399: modelComponents.add(service);
400: }
401: }
402: return modelComponents;
403: }
404:
405: public final Object lookupObject(String key, int scope) {
406: logger.debug("lookupObject: key=" + key + " scope=" + scope);
407: Object o = doLookupObject(key);
408:
409: if (o == null) {
410: if (logger.isDebugEnabled()) {
411: logger.debug("Failed to find object in Registry ID: "
412: + getRegistryId());
413: }
414: if (getParent() != null && scope > SCOPE_IMMEDIATE) {
415: if (getParent().isRemote() && scope == SCOPE_REMOTE) {
416: o = getParent().lookupObject(key);
417: } else if (!getParent().isRemote()
418: && scope >= SCOPE_LOCAL) {
419: o = getParent().lookupObject(key);
420: }
421: }
422: }
423: return o;
424: }
425:
426: public final Object lookupObject(Class type)
427: throws RegistrationException {
428: return lookupObject(type, getDefaultScope());
429: }
430:
431: /**
432: * Look up a single object by type.
433: * @return null if no object is found
434: * @throws RegistrationException if more than one object is found
435: */
436: public final Object lookupObject(Class type, int scope)
437: throws RegistrationException {
438: Collection collection = lookupObjects(type, scope);
439: if (collection == null || collection.size() < 1) {
440: return null;
441: } else if (collection.size() > 1) {
442: throw new RegistrationException(
443: "More than one object of type "
444: + type
445: + " was found in registry, but only 1 was expected.");
446: } else {
447: return collection.iterator().next();
448: }
449: }
450:
451: public final Collection lookupObjects(Class type) {
452: return lookupObjects(type, getDefaultScope());
453: }
454:
455: public final Collection lookupObjects(Class type, int scope) {
456: logger.debug("lookupObjects: type=" + type + " scope=" + scope);
457: Collection collection = doLookupObjects(type);
458: if (collection == null) {
459: collection = new ArrayList();
460: }
461:
462: if (getParent() != null && scope > SCOPE_IMMEDIATE) {
463: if (getParent().isRemote() && scope == SCOPE_REMOTE) {
464: Collection collection2 = getParent()
465: .lookupObjects(type);
466: if (collection2 != null) {
467: collection.addAll(collection2);
468: }
469: } else if (!getParent().isRemote() && scope >= SCOPE_LOCAL) {
470: Collection collection2 = getParent()
471: .lookupObjects(type);
472: if (collection2 != null) {
473: collection = CollectionUtils.union(collection,
474: collection2);
475: }
476: }
477: }
478:
479: return collection;
480: }
481:
482: protected abstract Collection doLookupObjects(Class type);
483:
484: public Object lookupObject(String key) {
485: return lookupObject(key, getDefaultScope());
486: }
487:
488: /**
489: * Initialises all registered agents
490: *
491: * @throws org.mule.api.lifecycle.InitialisationException
492: */
493: // TODO: Spring is now taking care of the initialisation lifecycle, need to check that we still get this
494: // problem
495: // protected void initialiseAgents() throws InitialisationException
496: // {
497: // logger.info("Initialising agents...");
498: //
499: // // Do not iterate over the map directly, as 'complex' agents
500: // // may spawn extra agents during initialisation. This will
501: // // cause a ConcurrentModificationException.
502: // // Use a cursorable iteration, which supports on-the-fly underlying
503: // // data structure changes.
504: // Collection agentsSnapshot = lookupCollection(Agent.class).values();
505: // CursorableLinkedList agentRegistrationQueue = new CursorableLinkedList(agentsSnapshot);
506: // CursorableLinkedList.Cursor cursor = agentRegistrationQueue.cursor();
507: //
508: // // the actual agent object refs are the same, so we are just
509: // // providing different views of the same underlying data
510: //
511: // try
512: // {
513: // while (cursor.hasNext())
514: // {
515: // Agent umoAgent = (Agent) cursor.next();
516: //
517: // int originalSize = agentsSnapshot.size();
518: // logger.debug("Initialising agent: " + umoAgent.getName());
519: // umoAgent.initialise();
520: // // thank you, we are done with you
521: // cursor.remove();
522: //
523: // // Direct calls to MuleManager.registerAgent() modify the original
524: // // agents map, re-check if the above agent registered any
525: // // 'child' agents.
526: // int newSize = agentsSnapshot.size();
527: // int delta = newSize - originalSize;
528: // if (delta > 0)
529: // {
530: // // TODO there's some mess going on in
531: // // http://issues.apache.org/jira/browse/COLLECTIONS-219
532: // // watch out when upgrading the commons-collections.
533: // Collection tail = CollectionUtils.retainAll(agentsSnapshot, agentRegistrationQueue);
534: // Collection head = CollectionUtils.subtract(agentsSnapshot, tail);
535: //
536: // // again, above are only refs, all going back to the original agents map
537: //
538: // // re-order the queue
539: // agentRegistrationQueue.clear();
540: // // 'spawned' agents first
541: // agentRegistrationQueue.addAll(head);
542: // // and the rest
543: // agentRegistrationQueue.addAll(tail);
544: //
545: // // update agents map with a new order in case we want to re-initialise
546: // // MuleManager on the fly
547: // for (Iterator it = agentRegistrationQueue.iterator(); it.hasNext();)
548: // {
549: // Agent theAgent = (Agent) it.next();
550: // theAgent.initialise();
551: // }
552: // }
553: // }
554: // }
555: // finally
556: // {
557: // // close the cursor as per JavaDoc
558: // cursor.close();
559: // }
560: // logger.info("Agents Successfully Initialised");
561: // }
562: /** @return null if object not found */
563: protected abstract Object doLookupObject(String key);
564:
565: protected void unsupportedOperation(String operation, Object o)
566: throws UnsupportedOperationException {
567: throw new UnsupportedOperationException(
568: "Registry: "
569: + getRegistryId()
570: + " is read-only so objects cannot be registered or unregistered. Failed to execute operation "
571: + operation + " on object: " + o);
572: }
573:
574: public final void registerObject(String key, Object value)
575: throws RegistrationException {
576: registerObject(key, value, null);
577: }
578:
579: public final void registerObject(String key, Object value,
580: Object metadata) throws RegistrationException {
581:
582: logger.debug("registerObject: key=" + key + " value=" + value
583: + " metadata=" + metadata);
584: if (value instanceof MuleContextAware) {
585: ((MuleContextAware) value).setMuleContext(MuleServer
586: .getMuleContext());
587: }
588: doRegisterObject(key, value, metadata);
589: }
590:
591: protected abstract void doRegisterObject(String key, Object value,
592: Object metadata) throws RegistrationException;
593:
594: public final void registerTransformer(Transformer transformer)
595: throws MuleException {
596: if (transformer instanceof DiscoverableTransformer) {
597: exactTransformerCache.clear();
598: transformerListCache.clear();
599: }
600: doRegisterTransformer(transformer);
601:
602: }
603:
604: protected abstract void doRegisterTransformer(
605: Transformer transformer) throws MuleException;
606:
607: // /////////////////////////////////////////////////////////////////////////
608: // Registry Metadata
609: // /////////////////////////////////////////////////////////////////////////
610:
611: public final String getRegistryId() {
612: return id;
613: }
614:
615: public Registry getParent() {
616: return parent;
617: }
618:
619: public void setParent(Registry registry) {
620: this .parent = registry;
621: }
622:
623: public int getDefaultScope() {
624: return defaultScope;
625: }
626:
627: public void setDefaultScope(int scope) {
628: if (scope < SCOPE_IMMEDIATE || scope > SCOPE_REMOTE) {
629: throw new IllegalArgumentException(
630: "Invalid value for scope: " + scope);
631: }
632: defaultScope = scope;
633: }
634: }
|