1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
134 Class clazz = Class.forName(DNotesFactoryFinder.class.getName() + "$ClassLoaderFinderConcrete");
135 ClassLoaderFinder clf = (ClassLoaderFinder) clazz.newInstance();
136 classLoader = clf.getContextClassLoader();
137 } catch (LinkageError le) {
138
139 classLoader = DNotesFactoryFinder.class.getClassLoader();
140 } catch (ClassNotFoundException x) {
141
142
143
144
145 classLoader = DNotesFactoryFinder.class.getClassLoader();
146 } catch (Exception x) {
147
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
320 }