View Javadoc

1   /*
2    * $Id: SimpleRegistryBootstrap.java 20321 2010-11-24 15:21:24Z dfeist $
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.config.bootstrap;
11  
12  import org.mule.api.MuleContext;
13  import org.mule.api.context.MuleContextAware;
14  import org.mule.api.lifecycle.Initialisable;
15  import org.mule.api.lifecycle.InitialisationException;
16  import org.mule.api.registry.MuleRegistry;
17  import org.mule.api.registry.ObjectProcessor;
18  import org.mule.api.registry.RegistrationException;
19  import org.mule.api.registry.Registry;
20  import org.mule.api.transformer.DiscoverableTransformer;
21  import org.mule.api.transformer.Transformer;
22  import org.mule.api.util.StreamCloser;
23  import org.mule.config.i18n.CoreMessages;
24  import org.mule.transformer.types.DataTypeFactory;
25  import org.mule.util.ClassUtils;
26  import org.mule.util.ExceptionUtils;
27  import org.mule.util.PropertiesUtils;
28  import org.mule.util.UUID;
29  
30  import java.lang.reflect.InvocationTargetException;
31  import java.net.URL;
32  import java.util.Enumeration;
33  import java.util.LinkedList;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.Properties;
37  
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  
41  /**
42   * This object will load objects defined in a file called <code>registry-bootstrap.properties</code> into the local registry.
43   * This allows modules and transports to make certain objects available by default.  The most common use case is for a
44   * module or transport to load stateless transformers into the registry.
45   * For this file to be located it must be present in the modules META-INF directory under
46   * <pre>META-INF/services/org/mule/config/</pre>
47   * <p/>
48   * The format of this file is a simple key / value pair. i.e.
49   * <pre>
50   * myobject=org.foo.MyObject
51   * </pre>
52   * Will register an instance of MyObject with a key of 'myobject'. If you don't care about the object name and want to
53   * ensure that the ojbect gets a unique name you can use -
54   * <pre>
55   * object.1=org.foo.MyObject
56   * object.2=org.bar.MyObject
57   * </pre>
58   * or
59   * <pre>
60   * myFoo=org.foo.MyObject
61   * myBar=org.bar.MyObject
62   * </pre>
63   * Loading transformers has a slightly different notation since you can define the 'returnClass' with optional mime type, and 'name'of
64   * the transformer as parameters i.e.
65   * <pre>
66   * transformer.1=org.mule.transport.jms.transformers.JMSMessageToObject,returnClass=byte[]
67   * transformer.2=org.mule.transport.jms.transformers.JMSMessageToObject,returnClass=java.lang.String:text/xml, name=JMSMessageToString
68   * transformer.3=org.mule.transport.jms.transformers.JMSMessageToObject,returnClass=java.util.Hashtable)
69   * </pre>
70   * Note that the key used for transformers must be 'transformer.x' where 'x' is a sequential number.  The transformer name will be
71   * automatically generated as JMSMessageToXXX where XXX is the return class name i.e. JMSMessageToString unless a 'name'
72   * parameter is specified. If no 'returnClass' is specified the default in the transformer will be used.
73   * <p/>
74   * Note that all objects defined have to have a default constructor. They can implement injection interfaces such as
75   * {@link org.mule.api.context.MuleContextAware} and lifecycle interfaces such as {@link org.mule.api.lifecycle.Initialisable}.
76   */
77  public class SimpleRegistryBootstrap implements Initialisable, MuleContextAware
78  {
79      public static final String SERVICE_PATH = "META-INF/services/org/mule/config/";
80  
81      public static final String REGISTRY_PROPERTIES = "registry-bootstrap.properties";
82  
83      public String TRANSFORMER_KEY = ".transformer.";
84      public String OBJECT_KEY = ".object.";
85  
86      protected final transient Log logger = LogFactory.getLog(getClass());
87  
88      protected MuleContext context;
89  
90      /** {@inheritDoc} */
91      public void setMuleContext(MuleContext context)
92      {
93          this.context = context;
94      }
95  
96      /** {@inheritDoc} */
97      public void initialise() throws InitialisationException
98      {
99          Enumeration<?> e = ClassUtils.getResources(SERVICE_PATH + REGISTRY_PROPERTIES, getClass());
100         List<Properties> bootstraps = new LinkedList<Properties>();
101 
102         // load ALL of the bootstrap files first
103         while (e.hasMoreElements())
104         {
105             try
106             {
107                 URL url = (URL) e.nextElement();
108                 if (logger.isDebugEnabled())
109                 {
110                     logger.debug("Reading bootstrap file: " + url.toString());
111                 }
112                 Properties p = new Properties();
113                 p.load(url.openStream());
114                 bootstraps.add(p);
115             }
116             catch (Exception e1)
117             {
118                 throw new InitialisationException(e1, this);
119             }
120         }
121 
122         // ... and only then merge and process them
123         int objectCounter = 1;
124         int transformerCounter = 1;
125         Properties transformers = new Properties();
126         Properties namedObjects = new Properties();
127         Properties unnamedObjects = new Properties();
128 
129         for (Properties bootstrap : bootstraps)
130         {
131             for (Map.Entry entry : bootstrap.entrySet())
132             {
133                 final String key = (String) entry.getKey();
134                 if (key.contains(OBJECT_KEY))
135                 {
136                     String newKey = key.substring(0, key.lastIndexOf(".")) + objectCounter++;
137                     unnamedObjects.put(newKey, entry.getValue());
138                 }
139                 else if (key.contains(TRANSFORMER_KEY))
140                 {
141                     String newKey = key.substring(0, key.lastIndexOf(".")) + transformerCounter++;
142                     transformers.put(newKey, entry.getValue());
143                 }
144                 else
145                 {
146                     // we allow arbitrary keys in the registry-bootstrap.properties but since we're
147                     // aggregating multiple files here we must make sure that the keys are unique
148 //                    if (accumulatedProps.getProperty(key) != null)
149 //                    {
150 //                        throw new IllegalStateException(
151 //                                "more than one registry-bootstrap.properties file contains a key " + key);
152 //                    }
153 //                    else
154                     {
155                         namedObjects.put(key, entry.getValue());
156                     }
157                 }
158             }
159         }
160 
161         try
162         {
163             registerUnnamedObjects(unnamedObjects, context.getRegistry());
164             registerTransformers(transformers, context.getRegistry());
165             registerObjects(namedObjects, context.getRegistry());
166         }
167         catch (Exception e1)
168         {
169             throw new InitialisationException(e1, this);
170         }
171     }
172 
173     private void registerTransformers(Properties props, MuleRegistry registry) throws Exception
174     {
175         String transString;
176         String name = null;
177         String returnClassString;
178         boolean optional = false;
179 
180         for (Map.Entry<Object, Object> entry : props.entrySet())
181         {
182             transString = (String)entry.getValue();
183             // reset
184             Class<?> returnClass = null;
185             returnClassString = null;
186             int x = transString.indexOf(",");
187             if (x > -1)
188             {
189                 Properties p = PropertiesUtils.getPropertiesFromString(transString.substring(x + 1), ',');
190                 name = p.getProperty("name", null);
191                 returnClassString = p.getProperty("returnClass", null);
192                 optional = p.containsKey("optional");
193             }
194 
195             final String transClass = (x == -1 ? transString : transString.substring(0, x));
196             try
197             {
198                 String mime = null;
199                 if (returnClassString != null)
200                 {
201                     int i = returnClassString.indexOf(":");
202                     if(i > -1)
203                     {
204                         mime = returnClassString.substring(i + 1);
205                         returnClassString = returnClassString.substring(0, i);
206                     }
207                     if (returnClassString.equals("byte[]"))
208                     {
209                         returnClass = byte[].class;
210                     }
211                     else
212                     {
213                         returnClass = ClassUtils.loadClass(returnClassString, getClass());
214                     }
215                 }
216                 Transformer trans = (Transformer) ClassUtils.instanciateClass(transClass);
217                 if (!(trans instanceof DiscoverableTransformer))
218                 {
219                     throw new RegistrationException(CoreMessages.transformerNotImplementDiscoverable(trans));
220                 }
221                 if (returnClass != null)
222                 {
223                     trans.setReturnDataType(DataTypeFactory.create(returnClass, mime));
224                 }
225                 if (name != null)
226                 {
227                     trans.setName(name);
228                 }
229                 else
230                 {
231                     //This will generate a default name for the transformer
232                     name = trans.getName();
233                     //We then prefix the name to ensure there is less chance of conflict if the user registers
234                     // the transformer with the same name
235                     trans.setName("_" + name);
236                 }
237                 registry.registerTransformer(trans);
238             }
239             catch (InvocationTargetException itex)
240             {
241                 Throwable cause = ExceptionUtils.getCause(itex);
242                 if (cause instanceof NoClassDefFoundError && optional)
243                 {
244                     if (logger.isDebugEnabled())
245                     {
246                         logger.debug("Ignoring optional transformer: " + transClass);
247                     }
248                 }
249                 else
250                 {
251                     throw new Exception(cause);
252                 }
253             }
254             catch (NoClassDefFoundError ncdfe)
255             {
256                 if (optional)
257                 {
258                     if (logger.isDebugEnabled())
259                     {
260                         logger.debug("Ignoring optional transformer: " + transClass);
261                     }
262                 }
263                 else
264                 {
265                     throw ncdfe;
266                 }
267             }
268             catch (ClassNotFoundException cnfe)
269             {
270                 if (optional)
271                 {
272                     if (logger.isDebugEnabled())
273                     {
274                         logger.debug("Ignoring optional transformer: " + transClass);
275                     }
276                 }
277                 else
278                 {
279                     throw cnfe;
280                 }
281             }
282 
283             name = null;
284             returnClass = null;
285         }
286     }
287 
288     private void registerObjects(Properties props, Registry registry) throws Exception
289     {
290         for (Map.Entry<Object, Object> entry : props.entrySet())
291         {
292             registerObject((String)entry.getKey(), (String)entry.getValue(), registry);
293         }
294         props.clear();
295     }
296 
297     private void registerUnnamedObjects(Properties props, Registry registry) throws Exception
298     {
299         for (Map.Entry<Object, Object> entry : props.entrySet())
300         {
301             final String key = String.format("%s#%s", entry.getKey(), UUID.getUUID());
302             registerObject(key, (String) entry.getValue(), registry);
303         }
304         props.clear();
305     }
306 
307     private void registerObject(String key, String value, Registry registry) throws Exception
308     {
309         boolean optional = false;
310         String className = null;
311 
312         try
313         {
314             int x = value.indexOf(",");
315             if (x > -1)
316             {
317                 Properties p = PropertiesUtils.getPropertiesFromString(value.substring(x + 1), ',');
318                 optional = p.containsKey("optional");
319                 className = value.substring(0, x);
320             }
321             else
322             {
323                 className = value;
324             }
325             Object o = ClassUtils.instanciateClass(className);
326             Class<?> meta = Object.class;
327 
328             if (o instanceof ObjectProcessor)
329             {
330                 meta = ObjectProcessor.class;
331             }
332             else if (o instanceof StreamCloser)
333             {
334                 meta = StreamCloser.class;
335             }
336             else if (o instanceof BootstrapObjectFactory)
337             {
338                 o = ((BootstrapObjectFactory)o).create();
339             }
340             registry.registerObject(key, o, meta);
341         }
342         catch (InvocationTargetException itex)
343         {
344             Throwable cause = ExceptionUtils.getCause(itex);
345             if (cause instanceof NoClassDefFoundError && optional)
346             {
347                 if (logger.isDebugEnabled())
348                 {
349                     logger.debug("Ignoring optional object: " + className);
350                 }
351             }
352             else
353             {
354                 throw new Exception(cause);
355             }
356         }
357         catch (NoClassDefFoundError ncdfe)
358         {
359             if (optional)
360             {
361                 if (logger.isDebugEnabled())
362                 {
363                     logger.debug("Ignoring optional object: " + className);
364                 }
365             }
366             else
367             {
368                 throw ncdfe;
369             }
370         }
371         catch (ClassNotFoundException cnfe)
372         {
373             if (optional)
374             {
375                 if (logger.isDebugEnabled())
376                 {
377                     logger.debug("Ignoring optional object: " + className);
378                 }
379             }
380             else
381             {
382                 throw cnfe;
383             }
384         }
385     }
386 }