View Javadoc

1   /*
2    * Copyright (c) 2003 Peter Antman, Teknik i Media  <peter.antman@tim.se>
3    *
4    * $Id: Factory.java,v 1.1.1.1 2004/05/19 12:07:30 pra Exp $
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 2 of the License, or (at your option) any later version
10   * 
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   * 
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, write to the Free Software
18   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   */
20  package org.backsource.utils.lang;
21  import java.util.Enumeration;
22  import java.util.List;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.Properties;
26  import java.io.BufferedReader;
27  import java.io.InputStreamReader;
28  import java.io.IOException;
29  import java.net.URL;
30  
31  import org.apache.log4j.Logger;
32  /***
33   * A baseclass or helper to make it easier to create factories following the
34   * JAXP pattern; but with more configuration options.
35   *
36   *<p>The normal pattern is to have an abstract factory through which an implementing factory is obtainable through the newInstance() method. The implementing factory then has a method implementation to get the class the factory is meant to
37   * be able to produce. This class helps in defining the behaviour that is used by the abstract factory to lookup the implementing factory.</p>
38   * <p>Factories that uses this class to implement its factory handling must either inherit from this class or demand that the implementing factories have default constructors with public access.</p>
39   *
40   * @author <a href="mailto:pra@tim.se">Peter Antman</a>
41   * @version $Revision: 1.1.1.1 $
42   */
43  
44  public class Factory {
45     private static final Logger log = Logger.getLogger(Factory.class);
46     
47     public Factory() {
48  
49     }
50  
51     /***
52      * Find and instantiate an implementing factory.
53      *
54      * The following search order is used:
55      * <ul>
56      <li>Check if env is null and contains key.</li>
57      <li>if reverseOrder is true first use the Service API and look for the file
58      META-INF/services/key where key is the property name and check the leaf classloader first for the resource; then check the system propery named byte key.</lI>
59      <li>if reverseOrder is false, check the system property firt and then use the Service API and return the resource from the highest parent classloader that it is visable from.</li>
60      <li>Last use the default.</li>
61      </ul>
62      
63      <p>The reverse order mechanism makes it possible to specify component specific factory configurations in environments which deploys components int their owne classloader.</p>
64      
65      * @param key the property name of the factory; the normal pattern is to use the fully qualified classname of the abstract factory.
66      * @param env a possibly null Properties that contains a locally given value for key.
67      * @param defaultClass the default class to load if no configuration is found.
68      * @param reverseOrder in reverse order the service API will be used before any System properties are checked; and the resource file will be searched in leaf class loader order first.
69      * @throws ClassNotFoundException if not default or property was found to create the factory or if the configured factory was not available in the classpath.
70      
71      * @throws IllegalArgumentException thrown if key is null.
72      * @throws IllegalStateException if no configuration was found and defaultClass was also null.
73      
74      */
75     public static Object findInstance(String key, Properties env,String defaultClass, boolean reverseOrder) throws ClassNotFoundException{
76        String clazz =  null;
77        if ( key == null) {
78           throw new IllegalArgumentException("The key used to lookup the factory is not allowed to be null");
79        } // end of if ()
80        
81        // Check if key is in env 
82        if ( env != null) {
83           clazz = getProperty( env.getProperty(key) ); 
84        } // end of if ()
85  
86        // Check system and Service properties
87        // if reverse order is true, search the service api first; but start
88        // with resources in the leaf
89        // else use system property first and then take the first propertu found
90        // through the service api.
91        if ( clazz ==null) {
92           if ( reverseOrder) {
93              clazz = getProperty( jarService(key,reverseOrder) );
94              if ( clazz == null) {
95                 clazz = getProperty( System.getProperty(key) );
96              } // end of if ()
97              
98           } else {
99              clazz = getProperty( System.getProperty(key) );
100             if ( clazz == null) {
101                clazz = getProperty( jarService(key,reverseOrder) );
102             } // end of if ()
103          } // end of else
104       } // end of if ()
105       
106       // Goto default
107       if ( clazz == null) {
108          clazz = defaultClass;
109       } // end of if ()
110 
111       if ( clazz == null) {
112          throw new IllegalStateException("No valid configuration of " + key + " was found"); 
113       } // end of if ()
114       
115       return newInstance(clazz);      
116       
117    }
118 
119    /***
120     * The property is not null only if it is not null, and has a length of > 0 after its been trimed;
121     */
122    public static String getProperty(String prop) {
123       if ( prop != null) {
124          prop = prop.trim();
125          if ( prop.length() == 0) {
126             prop = null;
127          } // end of if ()
128          
129       } // end of if ()
130       return prop;
131    }
132    
133 
134    /***
135     * Load the given classname from the thread context classloader, or Class.forName if no context loader was found.
136     * @param className the fully qualified name of the class to load.
137     * @throws ClassNotFoundException if it was not possible to load the given class or if it was null.
138     */
139    public static Object newInstance(String className)
140       throws ClassNotFoundException{
141       if ( className == null) {
142          throw new ClassNotFoundException("The name of the class to load is not allowed to be null");
143       } // end of if ()
144       
145 
146       Class fac = null;
147       ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
148       if (classLoader == null) {
149          log.debug("newInstance: 'Thread.currentThread().getContextClassLoader()' returns null!");
150          fac = Class.forName(className);
151       }else {
152          fac =classLoader.loadClass(className);
153       } // end of else
154 
155       if ( fac == null) {
156          throw new ClassNotFoundException("Could not load " + className + " classloader returned null");
157       } // end of if ()
158       Object o = null;
159       try {
160           o = fac.newInstance();
161       } catch (Exception e) {
162          // There are sooo many exception
163          throw new ClassNotFoundException("Could not load class " + className,e);
164       } // end of try-catch
165       
166       return o;
167    }
168 
169    /***
170     * Lookup the resource META-INF/services/key where key is the name of the property file to look for.
171     * <p>The context classloader is searched first and if the service property was not found the classloader of this class is used.</p>
172     * @param key the name of the resource/property.
173     * @param reverseOrder if true return the last resource in  the enumeration returned by getResource.
174     * @return the value of the service resource or null of not found.
175     */
176    public static String jarService(String key, boolean reverseOrder) {
177       String name = jarService( Thread.currentThread().getContextClassLoader(), key, reverseOrder);
178       if ( name == null) {
179          log.debug("Key " + key + " not found in context classloader, using std classloader");
180          name = jarService( Factory.class.getClassLoader(), key, reverseOrder);
181       } // end of if ()
182       return name;
183    }
184 
185    /***
186     * Lookup the resource META-INF/services/key where key is the name of the property file to look for.
187     * @param cl the classloader to use to lookup the resource.
188     * @param key the name of the resource/property.
189     * @param reverseOrder if true return the last resource in  the enumeration returned by getResource.
190     * @return the value of the service resource or null of not found.
191     */
192    public static String jarService(ClassLoader cl, String key, boolean reverseOrder) {
193       BufferedReader rd = null;
194       String factoryClassName = null;
195       try {
196          //ClassLoaderUtil.printClassLoaderTree(cl);
197          log.debug("META-INF/services/" + key+"="+cl.getResource("META-INF/services/" + key));
198 
199          Enumeration res = cl.getResources("META-INF/services/" + key);
200          URL url = null;
201          if ( res != null) {
202             List l = list(res);
203             if ( log.isDebugEnabled()) {
204                log.debug("List of resources found " + l);
205             } // end of if ()
206             
207             if ( reverseOrder) {
208                Collections.reverse(l);
209                if ( log.isDebugEnabled()) {
210                   log.debug("Reverse list of resources found " + l);
211                } // end of if ()
212 
213             } // end of if ()
214             if ( l.size() >= 1) {
215                url = (URL)l.get(0);
216             } // end of if ()
217             
218          } // end of if ()
219          if ( url == null) {
220             return null;
221          } // end of if ()
222          
223          
224          // Ok, we had an url; get the value in the file
225          
226          
227          rd = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8") );
228          factoryClassName = rd.readLine();
229       } catch (IOException e) {
230          ;//Ignore
231       }finally {
232          if ( rd != null) {
233             try {
234                rd.close();
235             } catch (IOException ignore) {
236                ;//Ignore
237             } // end of try-catch
238             
239          } // end of if ()
240          
241       } // end of finally
242       return factoryClassName;
243    }
244 
245    /***
246     * Implement the jdk 1.4.1 Collection list behaviour.
247     */
248    public static List list(Enumeration e) {
249       ArrayList l = new ArrayList();
250       while (e.hasMoreElements()) {
251          l.add(e.nextElement());
252       }
253       return l;
254    }
255 
256 }// Factory