View Javadoc

1   /*
2    * This file is part of Domingo
3    * an Open Source Java-API to Lotus Notes/Domino
4    * hosted at http://domingo.sourceforge.net
5    *
6    * Copyright (c) 2003-2007 Beck et al. projects GmbH Munich, Germany (http://www.bea.de)
7    *
8    * This library is free software; you can redistribute it and/or
9    * modify it under the terms of the GNU Lesser General Public
10   * License as published by the Free Software Foundation; either
11   * version 2.1 of the License, or (at your option) any later version.
12   *
13   * This library is distributed in the hope that it will be useful,
14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   * Lesser General Public License for more details.
17   *
18   * You should have received a copy of the GNU Lesser General Public
19   * License along with this library; if not, write to the Free Software
20   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21   */
22  
23  package de.bea.domingo;
24  
25  import java.io.BufferedInputStream;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.util.Properties;
29  
30  /***
31   * Implementation finder for the abstract factory DNotesFactory.
32   *
33   * @author <a href=mailto:kriede@users.sourceforge.net>Kurt Riede</a>
34   */
35  final class DNotesFactoryFinder {
36  
37      /*** name of factory property, used with system properties and resource. */
38      private static final String FACTORY_PROPERTY = "de.bea.domingo.factory";
39  
40      /*** Class name of default implementation of this abstract factory. */
41      private static final String DEFAULT_IMPL = "de.bea.domingo.service.NotesServiceFactory";
42  
43      /*** name of resource file defining implementation. */
44      private static final String PROPERTIES_FILE = "de/bea/domingo/domingo.properties";
45  
46      /*** alternative name of resource file defining implementation. */
47      private static final String PROPERTIES_FILE2 = "domingo.properties";
48  
49      /*** Configuration properties. */
50      private static Configuration fConfiguration = new Configuration();
51  
52      /***
53       * Private constructor to prevent creation.
54       */
55      private DNotesFactoryFinder() {
56      }
57  
58      /***
59       * Find the class name of the implementation of an abstract factory.
60       *
61       * @return instance the new instance
62       * @throws  IllegalAccessException  if the class or initializer is
63       *               not accessible.
64       * @throws  InstantiationException
65       *               if this <code>Class</code> represents an abstract class,
66       *               an interface, an array class,
67       *               a primitive type, or void;
68       *               or if the instantiation fails for some other reason.
69       */
70      public static Object find() throws InstantiationException, IllegalAccessException {
71          ClassLoader classLoader = findClassLoader();
72          String implementingClassName = getProperty(FACTORY_PROPERTY, DEFAULT_IMPL);
73          return newInstance(implementingClassName, classLoader);
74      }
75  
76      /***
77       * Find the class name of the implementation of an abstract factory.
78       *
79       * @return instance the new instance
80       * @throws  IllegalAccessException  if the class or initializer is
81       *               not accessible.
82       * @throws  InstantiationException
83       *               if this <code>Class</code> represents an abstract class,
84       *               an interface, an array class,
85       *               a primitive type, or void;
86       *               or if the instantiation fails for some other reason.
87       */
88      public static Object find(final String implementingClassName) throws InstantiationException, IllegalAccessException {
89          ClassLoader classLoader = findClassLoader();
90          return newInstance(implementingClassName, classLoader);
91      }
92  
93      /***
94       * Create an instance of a class using the specified ClassLoader.
95       *
96       * @param className the fully qualified name of the desired class.
97       * @param classLoader the classloader to use
98       * @return instance the new instance
99       * @throws  IllegalAccessException  if the class or initializer is
100      *               not accessible.
101      * @throws  InstantiationException
102      *               if this <code>Class</code> represents an abstract class,
103      *               an interface, an array class,
104      *               a primitive type, or void;
105      *               or if the instantiation fails for some other reason.
106      */
107     private static Object newInstance(final String className, final ClassLoader classLoader)
108             throws InstantiationException, IllegalAccessException {
109         try {
110             Class clazz;
111             if (classLoader == null) {
112                 clazz = Class.forName(className);
113             } else {
114                 clazz = classLoader.loadClass(className);
115             }
116             return clazz.newInstance();
117         } catch (ClassNotFoundException e) {
118             throw new ConfigurationError("Factory not found: " + e.getLocalizedMessage());
119         }
120     }
121 
122     /***
123      * Figure out which ClassLoader to use.  For JDK 1.2 and later use the
124      * context ClassLoader if possible.  Note: we defer linking the class
125      * that calls an API only in JDK 1.2 until runtime so that we can catch
126      * LinkageError so that this code will run in older non-Sun JVMs such
127      * as the Microsoft JVM in IE.
128      * @return Classloader
129      */
130     public static ClassLoader findClassLoader() {
131         ClassLoader classLoader;
132         try {
133             // Construct the name of the concrete class to instantiate
134             Class clazz = Class.forName(DNotesFactoryFinder.class.getName() + "$ClassLoaderFinderConcrete");
135             ClassLoaderFinder clf = (ClassLoaderFinder) clazz.newInstance();
136             classLoader = clf.getContextClassLoader();
137         } catch (LinkageError le) {
138             // Assume that we are running JDK 1.1, use the current ClassLoader
139             classLoader = DNotesFactoryFinder.class.getClassLoader();
140         } catch (ClassNotFoundException x) {
141             // This case should not normally happen.  MS IE can throw this
142             // instead of a LinkageError the second time Class.forName() is
143             // called so assume that we are running JDK 1.1 and use the
144             // current ClassLoader
145             classLoader = DNotesFactoryFinder.class.getClassLoader();
146         } catch (Exception x) {
147             // Something abnormal happened so return null
148             return null;
149         }
150         return classLoader;
151     }
152 
153     /***
154      * Returns the value of a property or returns the given default value if no
155      * configuration found.
156      *
157      * @param key the key of the property
158      * @param defaultValue default value, if no configuration found
159      * @return the value of the property
160      */
161     public static String getProperty(final String key, final String defaultValue) {
162         return fConfiguration.getProperty(key, defaultValue);
163     }
164 
165     /***
166      * Sets a configuration value and overwrites any default or configuration settings.
167      *
168      * @param key the key of the property
169      * @param value the new value
170      */
171     public static void setProperty(final String key, final String value) {
172         fConfiguration.setProperty(key, value);
173     }
174 
175     /***
176      * Error class to indicate factory configuration errors.
177      */
178     static class ConfigurationError extends Error {
179 
180         /*** serial version ID for serialization. */
181         private static final long serialVersionUID = 3257849874517866546L;
182 
183         /***
184          * Construct a new instance with the specified detail string and exception.
185          * @param msg the error message
186          */
187         ConfigurationError(final String msg) {
188             super(msg);
189         }
190     }
191 
192     /***
193      * Nested class that allows getContextClassLoader() to be
194      * called only on JDK 1.2 and yet run in older JDK 1.1 JVMs.
195      */
196     private abstract static class ClassLoaderFinder {
197         abstract ClassLoader getContextClassLoader();
198     }
199 
200     /***
201      * Nested class that allows getContextClassLoader() to be
202      * called only on JDK 1.2 and yet run in older JDK 1.1 JVMs.
203      */
204     static class ClassLoaderFinderConcrete extends ClassLoaderFinder {
205         /*** @return Classloader */
206         final ClassLoader getContextClassLoader() {
207             return Thread.currentThread().getContextClassLoader();
208         }
209     }
210 
211     /***
212      * Interface to the configuration of domingo.
213      */
214     private static final class Configuration {
215 
216         private Properties fProperties;
217 
218         /***
219          * Loads the configuration.
220          */
221         private Configuration() {
222             ClassLoader classLoader = findClassLoader();
223             try {
224                 fProperties = loadProperties(classLoader, PROPERTIES_FILE);
225             } catch (RuntimeException e) {
226             }
227             if (fProperties == null) {
228                 try {
229                     fProperties = loadProperties(classLoader, PROPERTIES_FILE2);
230                 } catch (RuntimeException e) {
231                 }
232             }
233             if (fProperties == null) {
234                 fProperties = System.getProperties();
235             }
236             if (fProperties == null) {
237                 fProperties = new Properties();
238             }
239         }
240 
241         /***
242          * Returns the value of a property or returns the given default value if no
243          * configuration found.
244          *
245          * @param key the key of the property
246          * @param defaultValue default value, if no configuration found
247          * @return the value of the property
248          */
249         private String getProperty(final String key, final String defaultValue) {
250             return fProperties.getProperty(key, defaultValue);
251         }
252 
253         /***
254          * Sets a configuration value and overwrites any default or configuration settings.
255          *
256          * @param key the key of the property
257          * @param value the new value
258          */
259         public void setProperty(final String key, final String value) {
260             fProperties.setProperty(key, value);
261         }
262 
263         /***
264          * Load a properties resource.
265          * The path must include the package name(s), if any, in system directory
266          * format and include the properties extension.
267          *
268          * @param classLoader The class loader to use to find resources
269          * @param path The path to the resource
270          * @return The initialized Properties object or <code>null</code> if the
271          *         resource is not available to the given classloader
272          */
273         private static Properties loadProperties(final ClassLoader classLoader, final String path) {
274             if (classLoader == null) {
275                 return null;
276             }
277             InputStream inputStream = classLoader.getResourceAsStream(path);
278             if (inputStream == null) {
279                 return null;
280             }
281             try {
282                 return loadProperties(inputStream);
283             } catch (IOException e) {
284                 try {
285                     inputStream.close();
286                 } catch (IOException e1) {
287                 }
288             }
289             return null;
290         }
291 
292         /***
293          * Loads properties from a given input stream.
294          *
295          * @param inputStream input stream
296          * @return properties
297          * @throws IOException if an error occurred when reading from the input stream.
298          */
299         private static Properties loadProperties(final InputStream inputStream) throws IOException {
300             BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
301             Properties properties;
302             try {
303                 properties = new Properties();
304                 properties.load(bufferedInputStream);
305                 bufferedInputStream.close();
306                 inputStream.close();
307             } catch (IOException e) {
308                 properties = null;
309                 throw e;
310             } finally {
311                 try {
312                     bufferedInputStream.close();
313                 } catch (IOException e) {
314                 }
315             }
316             return properties;
317         }
318     }
319 //  inputStream.close();
320 }