001: /*****************************************************************************
002: * Java Plug-in Framework (JPF)
003: * Copyright (C) 2004-2007 Dmitry Olshansky
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: *****************************************************************************/package org.java.plugin;
019:
020: import java.io.BufferedReader;
021: import java.io.File;
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.io.InputStreamReader;
025: import java.net.MalformedURLException;
026: import java.net.URL;
027:
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030: import org.java.plugin.registry.PluginRegistry;
031: import org.java.plugin.standard.StandardObjectFactory;
032: import org.java.plugin.util.ExtendedProperties;
033: import org.java.plugin.util.IoUtil;
034:
035: /**
036: * Factory class to help creating base Framework objects: plug-in registry, path
037: * resolver and plug-in manager.
038: *
039: * @version $Id$
040: */
041: public abstract class ObjectFactory {
042: /**
043: * Creates and configures new instance of object factory.
044: *
045: * @return configured instance of object factory
046: *
047: * @see #newInstance(ExtendedProperties)
048: */
049: public static ObjectFactory newInstance() {
050: return newInstance(null);
051: }
052:
053: /**
054: * Creates and configures new instance of object factory. Factory
055: * implementation class discovery procedure is following:
056: * <ul>
057: * <li>Use the <code>org.java.plugin.ObjectFactory</code> property from
058: * the given properties collection (if it is provided).</li>
059: * <li>Use the <code>org.java.plugin.ObjectFactory</code> system
060: * property.</li>
061: * <li>Use the properties file "jpf.properties" in the JRE "lib" directory
062: * or in the CLASSPATH. This configuration file is in standard
063: * <code>java.util.Properties</code> format and contains among others the
064: * fully qualified name of the implementation class with the key being the
065: * system property defined above.</li>
066: * <li>Use the Services API (as detailed in the JAR specification), if
067: * available, to determine the class name. The Services API will look for a
068: * class name in the file
069: * <code>META-INF/services/org.java.plugin.ObjectFactory</code> in jars
070: * available to the runtime.</li>
071: * <li>Framework default <code>ObjectFactory</code> implementation.</li>
072: * </ul>
073: *
074: * @param config
075: * factory configuration data, may be <code>null</code>
076: * @return configured instance of object factory
077: */
078: public static ObjectFactory newInstance(
079: final ExtendedProperties config) {
080: Log log = LogFactory.getLog(ObjectFactory.class);
081: ClassLoader cl = Thread.currentThread().getContextClassLoader();
082: if (cl == null) {
083: cl = ObjectFactory.class.getClassLoader();
084: }
085: ExtendedProperties props;
086: if (config != null) {
087: props = config;
088: } else {
089: props = loadProperties(cl);
090: }
091: String className = findProperty(cl, props);
092: ObjectFactory result;
093: try {
094: if (className == null) {
095: className = "org.java.plugin.standard.StandardObjectFactory"; //$NON-NLS-1$
096: }
097: result = (ObjectFactory) loadClass(cl, className)
098: .newInstance();
099: } catch (ClassNotFoundException cnfe) {
100: log.fatal("failed instantiating object factory " //$NON-NLS-1$
101: + className, cnfe);
102: throw new Error("failed instantiating object factory " //$NON-NLS-1$
103: + className, cnfe);
104: } catch (IllegalAccessException iae) {
105: log.fatal("failed instantiating object factory " //$NON-NLS-1$
106: + className, iae);
107: throw new Error("failed instantiating object factory " //$NON-NLS-1$
108: + className, iae);
109: } catch (SecurityException se) {
110: log.fatal("failed instantiating object factory " //$NON-NLS-1$
111: + className, se);
112: throw new Error("failed instantiating object factory " //$NON-NLS-1$
113: + className, se);
114: } catch (InstantiationException ie) {
115: log.fatal("failed instantiating object factory " //$NON-NLS-1$
116: + className, ie);
117: throw new Error("failed instantiating object factory " //$NON-NLS-1$
118: + className, ie);
119: }
120: result.configure(props);
121: log.debug("object factory instance created - " + result); //$NON-NLS-1$
122: return result;
123: }
124:
125: private static Class<?> loadClass(final ClassLoader cl,
126: final String className) throws ClassNotFoundException {
127: if (cl != null) {
128: try {
129: return cl.loadClass(className);
130: } catch (ClassNotFoundException cnfe) {
131: // ignore
132: }
133: }
134: ClassLoader cl2 = ObjectFactory.class.getClassLoader();
135: if (cl2 != null) {
136: try {
137: return cl2.loadClass(className);
138: } catch (ClassNotFoundException cnfe) {
139: // ignore
140: }
141: }
142: return ClassLoader.getSystemClassLoader().loadClass(className);
143: }
144:
145: private static ExtendedProperties loadProperties(
146: final ClassLoader cl) {
147: Log log = LogFactory.getLog(ObjectFactory.class);
148: File file = new File(System.getProperty("java.home") //$NON-NLS-1$
149: + File.separator + "lib" + File.separator //$NON-NLS-1$
150: + "jpf.properties"); //$NON-NLS-1$
151: URL url = null;
152: if (file.canRead()) {
153: try {
154: url = IoUtil.file2url(file);
155: } catch (MalformedURLException mue) {
156: log.error("failed converting file " + file //$NON-NLS-1$
157: + " to URL", mue); //$NON-NLS-1$
158: }
159: }
160: if (url == null) {
161: if (cl != null) {
162: url = cl.getResource("jpf.properties"); //$NON-NLS-1$
163: if (url == null) {
164: url = ClassLoader
165: .getSystemResource("jpf.properties"); //$NON-NLS-1$
166: }
167: } else {
168: url = ClassLoader.getSystemResource("jpf.properties"); //$NON-NLS-1$
169: }
170: if (url == null) {
171: log
172: .debug("no jpf.properties file found in ${java.home}/lib (" //$NON-NLS-1$
173: + file
174: + ") nor in CLASSPATH, using standard properties"); //$NON-NLS-1$
175: url = StandardObjectFactory.class
176: .getResource("jpf.properties"); //$NON-NLS-1$
177: }
178: }
179: try {
180: InputStream strm = IoUtil.getResourceInputStream(url);
181: try {
182: ExtendedProperties props = new ExtendedProperties();
183: props.load(strm);
184: log.debug("loaded jpf.properties from " + url); //$NON-NLS-1$
185: return props;
186: } finally {
187: try {
188: strm.close();
189: } catch (IOException ioe) {
190: // ignore
191: }
192: }
193: } catch (Exception e) {
194: log.error("failed loading jpf.properties from CLASSPATH", //$NON-NLS-1$
195: e);
196: }
197: return null;
198: }
199:
200: private static String findProperty(final ClassLoader cl,
201: final ExtendedProperties props) {
202: Log log = LogFactory.getLog(ObjectFactory.class);
203: String name = ObjectFactory.class.getName();
204: String result = System.getProperty(name);
205: if (result != null) {
206: log.debug("property " + name //$NON-NLS-1$
207: + " found as system property"); //$NON-NLS-1$
208: return result;
209: }
210: if (props != null) {
211: result = props.getProperty(name);
212: if (result != null) {
213: log.debug("property " + name //$NON-NLS-1$
214: + " found in properties file"); //$NON-NLS-1$
215: return result;
216: }
217: }
218: String serviceId = "META-INF/services/" //$NON-NLS-1$
219: + ObjectFactory.class.getName();
220: InputStream strm;
221: if (cl == null) {
222: strm = ClassLoader.getSystemResourceAsStream(serviceId);
223: } else {
224: strm = cl.getResourceAsStream(serviceId);
225: }
226: if (strm != null) {
227: try {
228: BufferedReader reader = new BufferedReader(
229: new InputStreamReader(strm, "UTF-8")); //$NON-NLS-1$
230: try {
231: result = reader.readLine();
232: } finally {
233: try {
234: reader.close();
235: } catch (IOException ioe) {
236: // ignore
237: }
238: }
239: } catch (IOException ioe) {
240: try {
241: strm.close();
242: } catch (IOException ioe2) {
243: // ignore
244: }
245: }
246: }
247: if (result != null) {
248: log.debug("property " + name //$NON-NLS-1$
249: + " found as service"); //$NON-NLS-1$
250: return result;
251: }
252: log.debug("no property " + name //$NON-NLS-1$
253: + " found"); //$NON-NLS-1$
254: return result;
255: }
256:
257: /**
258: * Configures this factory instance. This method is called from
259: * {@link #newInstance(ExtendedProperties)}.
260: *
261: * @param config
262: * factory configuration data, may be <code>null</code>
263: */
264: protected abstract void configure(ExtendedProperties config);
265:
266: /**
267: * Creates new instance of plug-in manager using new instances of registry
268: * and path resolver.
269: *
270: * @return new plug-in manager instance
271: *
272: * @see #createRegistry()
273: * @see #createPathResolver()
274: */
275: public final PluginManager createManager() {
276: return createManager(createRegistry(), createPathResolver());
277: }
278:
279: /**
280: * Creates new instance of plug-in manager.
281: *
282: * @param registry
283: * @param pathResolver
284: * @return new plug-in manager instance
285: */
286: public abstract PluginManager createManager(
287: PluginRegistry registry, PathResolver pathResolver);
288:
289: /**
290: * Creates new instance of plug-in registry implementation class using
291: * standard discovery algorithm to determine which registry implementation
292: * class should be instantiated.
293: *
294: * @return new registry instance
295: */
296: public abstract PluginRegistry createRegistry();
297:
298: /**
299: * Creates new instance of path resolver implementation class using standard
300: * discovery algorithm to determine which resolver implementation class
301: * should be instantiated.
302: *
303: * @return new path resolver instance
304: */
305: public abstract PathResolver createPathResolver();
306: }
|