View Javadoc

1   /*
2    * $Id: TransportFactory.java 9539 2007-11-01 14:07:28Z akuzmin $
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.providers.service;
12  
13  import org.mule.MuleException;
14  import org.mule.MuleManager;
15  import org.mule.config.i18n.CoreMessages;
16  import org.mule.config.i18n.Message;
17  import org.mule.config.i18n.MessageFactory;
18  import org.mule.impl.endpoint.MuleEndpoint;
19  import org.mule.providers.AbstractConnector;
20  import org.mule.umo.endpoint.EndpointException;
21  import org.mule.umo.endpoint.UMOEndpoint;
22  import org.mule.umo.endpoint.UMOEndpointURI;
23  import org.mule.umo.endpoint.UMOImmutableEndpoint;
24  import org.mule.umo.provider.UMOConnector;
25  import org.mule.umo.transformer.UMOTransformer;
26  import org.mule.util.BeanUtils;
27  import org.mule.util.ClassUtils;
28  import org.mule.util.MuleObjectHelper;
29  import org.mule.util.ObjectFactory;
30  import org.mule.util.ObjectNameHelper;
31  import org.mule.util.PropertiesUtils;
32  import org.mule.util.SpiUtils;
33  
34  import java.io.IOException;
35  import java.io.InputStream;
36  import java.util.HashMap;
37  import java.util.Iterator;
38  import java.util.Map;
39  import java.util.Properties;
40  
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  
44  /**
45   * <code>TransportFactory</code> can be used for generically creating endpoints
46   * from an url. Note that for some endpoints, the url alone is not enough to create
47   * the endpoint if a connector for the endpoint has not already been configured with
48   * the Mule Manager.
49   * 
50   */
51  
52  public final class TransportFactory
53  {
54      public static final String PROVIDER_SERVICES_PATH = "org/mule/providers";
55  
56      /**
57       * logger used by this class
58       */
59      protected static final Log logger = LogFactory.getLog(TransportFactory.class);
60  
61      public static final int GET_OR_CREATE_CONNECTOR = 0;
62      public static final int ALWAYS_CREATE_CONNECTOR = 1;
63      public static final int NEVER_CREATE_CONNECTOR = 2;
64      public static final int USE_CONNECTOR = 3;
65  
66      // @GuardedBy("TransportFactory.class")
67      private static Map csdCache = new HashMap();
68  
69      /** Do not instanciate. */
70      private TransportFactory ()
71      {
72          // no-op
73      }
74  
75      public static UMOEndpoint createEndpoint(UMOEndpointURI uri, String type) throws EndpointException
76      {
77          UMOConnector connector = getOrCreateConnectorByProtocol(uri);
78  
79          if (connector == null)
80          {
81              Message m = CoreMessages.failedToCreateObjectWith("Endpoint", "Uri: " + uri);
82              m.setNextMessage(CoreMessages.objectIsNull("connector"));
83              throw new TransportFactoryException(m);
84  
85          }
86  
87          UMOEndpoint endpoint = new MuleEndpoint();
88          endpoint.setConnector(connector);
89          endpoint.setEndpointURI(uri);
90          if (uri.getEndpointName() != null)
91          {
92              endpoint.setName(uri.getEndpointName());
93          }
94          String name = ObjectNameHelper.getEndpointName(endpoint);
95  
96          endpoint.setName(name);
97  
98          if (type != null)
99          {
100             endpoint.setType(type);
101             UMOTransformer trans = getTransformer(uri, connector,
102                 (UMOEndpoint.ENDPOINT_TYPE_RECEIVER.equals(type) ? 0 : 1));
103             endpoint.setTransformer(trans);
104             if (UMOEndpoint.ENDPOINT_TYPE_RECEIVER.equals(type))
105             {
106                 // set the response transformer
107                 trans = getTransformer(uri, connector, 2);
108                 endpoint.setResponseTransformer(trans);
109             }
110         }
111         return endpoint;
112     }
113 
114     /**
115      * @param url
116      * @param cnn
117      * @param type 0=inbound, 1=outbound, 2=response
118      * @return
119      * @throws TransportFactoryException
120      */
121     private static UMOTransformer getTransformer(UMOEndpointURI url, UMOConnector cnn, int type)
122         throws TransportFactoryException
123     {
124         UMOTransformer trans = null;
125         String transId;
126         if (type == 2)
127         {
128             transId = url.getResponseTransformers();
129         }
130         else
131         {
132             transId = url.getTransformers();
133         }
134 
135         if (transId != null)
136         {
137             try
138             {
139                 trans = MuleObjectHelper.getTransformer(transId, ",");
140             }
141             catch (MuleException e)
142             {
143                 throw new TransportFactoryException(e);
144             }
145         }
146         else
147         {
148             // Get connector specific overrides to set on the descriptor
149             Properties overrides = new Properties();
150             if (cnn instanceof AbstractConnector)
151             {
152                 Map so = ((AbstractConnector) cnn).getServiceOverrides();
153                 if (so != null)
154                 {
155                     overrides.putAll(so);
156                 }
157             }
158 
159             String scheme = url.getSchemeMetaInfo();
160 
161             //check if there is a transformer associated with connector allready...
162             //if not, get it from the ServiceDescriptor
163             if (cnn instanceof AbstractConnector)
164             {
165                 AbstractConnector aconn = (AbstractConnector) cnn;
166                 // TODO MCR use the constants for the type, there should be on already
167                 if (type == 0)
168                 {
169                     trans = aconn.getDefaultInboundTransformer();
170                 }
171                 else if (type == 1)
172                 {
173                     trans = aconn.getDefaultOutboundTransformer();
174                 }
175                 else
176                 {
177                     trans = aconn.getDefaultResponseTransformer();
178                 }
179             }
180 
181             if (trans == null)
182             {
183                 TransportServiceDescriptor csd = getServiceDescriptor(scheme, overrides);
184                 if (type == 0)
185                 {
186                     trans = csd.createInboundTransformer();
187                 }
188                 else if (type == 1)
189                 {
190                     trans = csd.createOutboundTransformer();
191                 }
192                 else
193                 {
194                     trans = csd.createResponseTransformer();
195                 }
196             }
197         }
198         return trans;
199     }
200 
201     /**
202      * Creates an uninitialied connector from the provided MuleEndpointURI. The
203      * scheme is used to determine what kind of connector to create. Any params set
204      * on the uri can be used to initialise bean properties on the created connector.
205      * <p/> Note that the initalise method will need to be called on the connector
206      * returned. This is so that developers can control when the connector
207      * initialisation takes place as this is likely to initialse all connecotr
208      * resources.
209      * 
210      * @param url the MuleEndpointURI url to create the connector with
211      * @return a new Connector
212      * @throws TransportFactoryException
213      */
214     public static UMOConnector createConnector(UMOEndpointURI url) throws TransportFactoryException
215     {
216         String scheme = url.getSchemeMetaInfo();
217 
218         UMOConnector connector;
219         TransportServiceDescriptor csd = getServiceDescriptor(scheme);
220         // Make sure we can create the endpoint/connector using this service
221         // method
222         if (csd.getServiceError() != null)
223         {
224             throw new TransportServiceException(MessageFactory.createStaticMessage(csd.getServiceError()));
225         }
226 
227         // If this is a fineder service, lets find it before trying to create it
228         if (csd.getServiceFinder() != null)
229         {
230             csd = csd.createServiceFinder().findService(scheme, csd);
231         }
232         // if there is a factory, use it
233         try
234         {
235             if (csd.getConnectorFactory() != null)
236             {
237                 ObjectFactory factory = (ObjectFactory) ClassUtils.loadClass(csd.getConnectorFactory(),
238                                                                              TransportFactory.class).newInstance();
239                 connector = (UMOConnector) factory.create();
240             }
241             else
242             {
243                 if (csd.getConnector() != null)
244                 {
245                     connector = (UMOConnector) ClassUtils.loadClass(csd.getConnector(), TransportFactory.class)
246                         .newInstance();
247                     if (connector instanceof AbstractConnector)
248                     {
249                         ((AbstractConnector) connector).initialiseFromUrl(url);
250                     }
251                 }
252                 else
253                 {
254                     throw new TransportFactoryException(
255                         CoreMessages.objectNotSetInService("Connector", scheme));
256                 }
257             }
258         }
259         catch (TransportFactoryException e)
260         {
261             throw e;
262         }
263         catch (Exception e)
264         {
265             throw new TransportFactoryException(
266                 CoreMessages.failedToCreateObjectWith("Endpoint", url), e);
267         }
268 
269         connector.setName(ObjectNameHelper.getConnectorName(connector));
270 
271         // set any manager default properties for the connector
272         // these are set on the Manager with a protocol i.e.
273         // jms.specification=1.1
274         Map props = new HashMap();
275         PropertiesUtils.getPropertiesWithPrefix(MuleManager.getInstance().getProperties(),
276             connector.getProtocol().toLowerCase(), props);
277         if (props.size() > 0)
278         {
279             props = PropertiesUtils.removeNamespaces(props);
280             BeanUtils.populateWithoutFail(connector, props, true);
281         }
282 
283         return connector;
284     }
285 
286     public static TransportServiceDescriptor getServiceDescriptor(String protocol)
287         throws TransportFactoryException
288     {
289         return getServiceDescriptor(protocol, null);
290     }
291 
292     public static synchronized TransportServiceDescriptor getServiceDescriptor(String protocol, Properties overrides)
293         throws TransportFactoryException
294     {
295         TransportServiceDescriptor csd = (TransportServiceDescriptor) csdCache.get(new CSDKey(protocol, overrides));
296         if (csd == null)
297         {
298             String location = SpiUtils.SERVICE_ROOT + PROVIDER_SERVICES_PATH;
299             InputStream is = SpiUtils.findServiceDescriptor(PROVIDER_SERVICES_PATH, protocol + ".properties",
300                 TransportFactory.class);
301 
302             // TODO RM: this can be removed in Mule 2.0
303             if (is == null)
304             {
305                 //The legacy connector decriptors did did not use file extensions
306                 is = SpiUtils.findServiceDescriptor(PROVIDER_SERVICES_PATH, protocol,
307                 TransportFactory.class);
308                 if (is != null)
309                 {
310                     logger.warn("The transport " + protocol + " is using a legacy style descriptor."
311                                     + " This needs to be updated."
312                                     + " Future versions of Mule will not work with this descriptor.");
313                 }
314             }
315             try
316             {
317                 if (is != null)
318                 {
319                     Properties props = new Properties();
320                     props.load(is);
321                     csd = new TransportServiceDescriptor(protocol, location, props);
322                     // set any overides on the descriptor
323                     csd.setOverrides(overrides);
324                     if (csd.getServiceFinder() != null)
325                     {
326                         TransportServiceFinder finder = csd.createServiceFinder();
327                         csd = finder.findService(protocol, csd);
328                     }
329                     csdCache.put(new CSDKey(csd.getProtocol(), overrides), csd);
330                 }
331                 else
332                 {
333                     throw new TransportServiceNotFoundException(location + "/" + protocol);
334                 }
335             }
336             catch (IOException e)
337             {
338                 throw new TransportFactoryException(
339                     CoreMessages.failedToCreateEndpointFromLocation(location + "/" + protocol), e);
340             }
341         }
342         return csd;
343     }
344 
345     public static UMOConnector getOrCreateConnectorByProtocol(UMOEndpointURI uri)
346         throws TransportFactoryException
347     {
348         return getOrCreateConnectorByProtocol(uri, uri.getCreateConnector());
349     }
350 
351     public static UMOConnector getOrCreateConnectorByProtocol(UMOImmutableEndpoint endpoint)
352         throws TransportFactoryException
353     {
354         return getOrCreateConnectorByProtocol(endpoint.getEndpointURI(), endpoint.getCreateConnector());
355     }
356 
357     private static UMOConnector getOrCreateConnectorByProtocol(UMOEndpointURI uri, int create)
358         throws TransportFactoryException
359     {
360         final String connectorName = uri.getConnectorName();
361         UMOConnector connector = MuleManager.getInstance().lookupConnector(connectorName);
362         if (connector != null)
363         {
364             return connector;
365         }
366 
367         connector = getConnectorByProtocol(uri.getFullScheme());
368         if (ALWAYS_CREATE_CONNECTOR == create
369             || (connector == null && create == GET_OR_CREATE_CONNECTOR))
370         {
371             connector = createConnector(uri);
372             try
373             {
374                 BeanUtils.populate(connector, uri.getParams());
375                 MuleManager.getInstance().registerConnector(connector);
376 
377             }
378             catch (Exception e)
379             {
380                 throw new TransportFactoryException(
381                     CoreMessages.failedToSetPropertiesOn("Connector"), e);
382             }
383         }
384         else if (create == NEVER_CREATE_CONNECTOR && connector == null)
385         {
386             logger.warn("There is no connector for protocol: " + uri.getScheme()
387                         + " and 'createConnector' is set to NEVER.  Returning null");
388         }
389         return connector;
390     }
391 
392     public static UMOConnector getConnectorByProtocol(String protocol)
393     {
394         UMOConnector connector;
395         UMOConnector resultConnector = null;
396         Map connectors = MuleManager.getInstance().getConnectors();
397         for (Iterator iterator = connectors.values().iterator(); iterator.hasNext();)
398         {
399             connector = (UMOConnector) iterator.next();
400             if (connector.supportsProtocol(protocol))
401             {
402                 if (resultConnector == null)
403                 {
404                     resultConnector = connector;
405                 }
406                 else
407                 {
408                     throw new IllegalStateException(
409                         CoreMessages.moreThanOneConnectorWithProtocol(protocol).getMessage());
410                 }
411             }
412         }
413         return resultConnector;
414     }
415 
416     private static class CSDKey
417     {
418         private final Map overrides;
419         private final String protocol;
420 
421         public CSDKey(String protocol, Map overrides)
422         {
423             this.overrides = overrides;
424             this.protocol = protocol;
425         }
426 
427         // @Override
428         public boolean equals(Object o)
429         {
430             if (this == o)
431             {
432                 return true;
433             }
434             if (!(o instanceof CSDKey))
435             {
436                 return false;
437             }
438 
439             final CSDKey csdKey = (CSDKey) o;
440 
441             if (overrides != null ? !overrides.equals(csdKey.overrides) : csdKey.overrides != null)
442             {
443                 return false;
444             }
445             return protocol.equals(csdKey.protocol);
446 
447         }
448 
449         // @Override
450         public int hashCode()
451         {
452             return 29 * (overrides != null ? overrides.hashCode() : 0) + protocol.hashCode();
453         }
454     }
455 }