View Javadoc

1   /*
2    * Copyright 2010 Capgemini
3    * Licensed under the Apache License, Version 2.0 (the "License"); 
4    * you may not use this file except in compliance with the License. 
5    * You may obtain a copy of the License at 
6    * 
7    * http://www.apache.org/licenses/LICENSE-2.0 
8    * 
9    * Unless required by applicable law or agreed to in writing, software 
10   * distributed under the License is distributed on an "AS IS" BASIS, 
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
12   * See the License for the specific language governing permissions and 
13   * limitations under the License. 
14   * 
15   */
16  package org.xmlfield.core.internal;
17  
18  import java.io.IOException;
19  import java.io.InputStream;
20  import java.net.URL;
21  import java.util.Enumeration;
22  import java.util.HashMap;
23  import java.util.Map;
24  import java.util.Properties;
25  
26  import org.slf4j.Logger;
27  import org.slf4j.LoggerFactory;
28  import org.xmlfield.core.api.XmlFieldNodeModifierFactory;
29  import org.xmlfield.core.api.XmlFieldNodeParserFactory;
30  import org.xmlfield.core.api.XmlFieldSelectorFactory;
31  import org.xmlfield.core.impl.dom.DomNodeModifierFactory;
32  import org.xmlfield.core.impl.dom.DomNodeParserFactory;
33  import org.xmlfield.core.impl.dom.DomSelectorFactory;
34  
35  /**
36   * XmlFieldFactory finder instanciate an XmlFieldFactory instance. This finder lookup in the classpath a file named
37   * xmlfield-factory.properties and use it. If more than one file is found, only the first file is loaded. If no file is
38   * found or the configuration file has some errors, the finder return the default implementation.
39   * 
40   * @author Guillaume Mary <guillaume.mary@capgemini.com>
41   */
42  public class XmlFieldFactoryFinder {
43  
44      /**
45       * Cached properties
46       */
47      private static Properties cachedProperties = new Properties();
48  
49      /**
50       * Config file name
51       */
52      private static final String CONFIG_LOCATION = "xmlfield-factory.properties";
53  
54      /**
55       * Map of the default factories implementations
56       */
57      private static Map<String, Class<?>> defaultFactoriesClass;
58  
59      private static boolean firstTime = true;
60  
61      private static Logger logger = LoggerFactory.getLogger(XmlFieldFactoryFinder.class);
62  
63      static {
64          defaultFactoriesClass = new HashMap<String, Class<?>>();
65          defaultFactoriesClass.put(XmlFieldSelectorFactory.class.getName(), DomSelectorFactory.class);
66          defaultFactoriesClass.put(XmlFieldNodeParserFactory.class.getName(), DomNodeParserFactory.class);
67          defaultFactoriesClass
68                  .put(XmlFieldNodeModifierFactory.class.getName(), DomNodeModifierFactory.class);
69      }
70  
71      private final ClassLoader classloader;
72  
73      /**
74       * Search the classpath for a configuration and instanciate a new factory.
75       * 
76       * @param classLoader
77       *            used to search a configuration file.
78       */
79      public XmlFieldFactoryFinder(final ClassLoader classLoader) {
80          this.classloader = classLoader;
81      }
82  
83      /**
84       * Instantiate a new factory instance. Search in the classpath a file named <code>xmlfield-factory.properties</code>
85       * where a specific factories implementation should be declared.
86       * 
87       * @param <T>
88       *            factory type
89       * @param factoryClass
90       *            lookup factory class
91       * @return intance of the requested factory class
92       */
93      @SuppressWarnings("unchecked")
94      public <T> T newFactory(Class<T> factoryClass) {
95          final String requestedFactoryClass = factoryClass.getName();
96          if (!defaultFactoriesClass.containsKey(requestedFactoryClass)) {
97              logger.error("This requseted factory class is not managed by this finder, factory class : {}",
98                      requestedFactoryClass);
99              return null;
100         }
101         if (firstTime) {
102             init();
103         }
104         String implementationFactoryClass = cachedProperties.getProperty(requestedFactoryClass);
105         if (implementationFactoryClass == null) {
106             logger.debug("The requested factory is not overriden ({}), we return the factory class : {}",
107                     requestedFactoryClass, defaultFactoriesClass.get(requestedFactoryClass));
108             return (T) createDefaultInstance(requestedFactoryClass);
109         }
110         logger.debug("The requested factory is overriden by : {}", implementationFactoryClass);
111         return (T) createInstance(implementationFactoryClass);
112     }
113 
114     /**
115      * <p>
116      * Create class using appropriate ClassLoader.
117      * </p>
118      * 
119      * @param className
120      *            Name of class to create.
121      * @return Created class or <code>null</code>.
122      */
123     private Class<?> createClass(String className) {
124         Class<?> clazz;
125 
126         // use approprite ClassLoader
127         try {
128             if (this.classloader != null) {
129                 clazz = this.classloader.loadClass(className);
130             } else {
131                 clazz = Class.forName(className);
132             }
133         } catch (Throwable t) {
134             logger.error("Error when creating the class named {}", className);
135             return null;
136         }
137 
138         return clazz;
139     }
140 
141     /**
142      * Create the default instance of the specified string factory class parameter.
143      * 
144      * @param <T>
145      *            factory type
146      * @param factoryClass
147      *            string factory class
148      * @return instance of the factory class
149      */
150     private Object createDefaultInstance(String factoryClass) {
151         Class<?> implementationFactoryClass = defaultFactoriesClass.get(factoryClass);
152         if (implementationFactoryClass == null) {
153             return null;
154         }
155         return createInstance(implementationFactoryClass);
156     }
157 
158     private Object createInstance(Class<?> clazz) {
159         Object factory;
160         try {
161             factory = clazz.newInstance();
162         } catch (ClassCastException classCastException) {
163             logger.error("could not instantiate {}", clazz.getName());
164             return null;
165         } catch (IllegalAccessException illegalAccessException) {
166             logger.error("could not instantiate {}", clazz.getName());
167             return null;
168         } catch (InstantiationException instantiationException) {
169             logger.error("could not instantiate {}", clazz.getName());
170             return null;
171         }
172         return factory;
173     }
174 
175     private Object createInstance(String className) {
176         Class<?> implementationFactoryClass = createClass(className);
177         if (implementationFactoryClass == null) {
178             return null;
179         }
180         return createInstance(implementationFactoryClass);
181     }
182 
183     /**
184      * XmlFieldFactory initialization
185      */
186     private void init() {
187         synchronized (cachedProperties) {
188             if (firstTime) {
189                 try {
190                     Enumeration<URL> configFiles;
191                     configFiles = classloader.getResources(CONFIG_LOCATION);
192 
193                     if (configFiles == null) {
194                         logger.info("No configuration file ({}) found in the classpath.", CONFIG_LOCATION);
195                         return;
196                     }
197                     firstTime = false;
198                     boolean alreadyLoaded = false;
199                     while (configFiles.hasMoreElements()) {
200                         final URL url = configFiles.nextElement();
201                         if (!alreadyLoaded) {
202                             final InputStream is = url.openStream();
203                             cachedProperties.load(is);
204                             is.close();
205                             logger.info("XmlFieldFactory configuration loaded from the file {}", url);
206                         } else {
207                             logger.info(
208                                     "An other XmlFieldFactory configuration file is found in the classpath. This file won't be loaded {}",
209                                     url);
210                         }
211                     }
212                 } catch (IOException e) {
213                     logger.error("An error occur during the XmlFieldFActory initialization", e);
214                 }
215             }
216         }
217     }
218 }