001: // The contents of this file are subject to the Mozilla Public License Version
002: // 1.1
003: //(the "License"); you may not use this file except in compliance with the
004: //License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
005: //
006: //Software distributed under the License is distributed on an "AS IS" basis,
007: //WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
008: //for the specific language governing rights and
009: //limitations under the License.
010: //
011: //The Original Code is "The Columba Project"
012: //
013: //The Initial Developers of the Original Code are Frederik Dietz and Timo
014: // Stich.
015: //Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
016: //
017: //All Rights Reserved.
018: package org.columba.core.plugin;
019:
020: import java.io.File;
021: import java.lang.reflect.Constructor;
022: import java.net.MalformedURLException;
023: import java.net.URL;
024: import java.util.List;
025: import java.util.Vector;
026:
027: import org.columba.api.plugin.ExtensionMetadata;
028: import org.columba.api.plugin.IExtension;
029: import org.columba.api.plugin.IExtensionInterface;
030: import org.columba.api.plugin.PluginException;
031: import org.columba.api.plugin.PluginMetadata;
032: import org.columba.core.logging.Logging;
033: import org.columba.core.main.Main;
034:
035: /**
036: * An extension providing the metadata of an extension and the runtime context
037: * for instanciation.
038: *
039: * @author fdietz
040: */
041: public class Extension implements IExtension {
042:
043: private static final String FILE_PLUGIN_JAR = "plugin.jar";
044:
045: private static final java.util.logging.Logger LOG = java.util.logging.Logger
046: .getLogger("org.columba.core.plugin");
047:
048: private ExtensionMetadata metadata;
049:
050: private PluginMetadata pluginMetadata;
051:
052: private boolean internalPlugin;
053:
054: private IExtensionInterface cachedInstance;
055:
056: /**
057: * Enabled by default. But, in case of an error on instanciation its
058: * disabled.
059: */
060: private boolean enabled = true;
061:
062: /**
063: * Constructor used by internal extensions
064: *
065: * @param metadata
066: */
067: public Extension(ExtensionMetadata metadata, boolean internal) {
068: this .metadata = metadata;
069:
070: internalPlugin = internal;
071: }
072:
073: /**
074: * Constructor used by external extensions.
075: *
076: * @param pluginMetadata
077: * @param metadata
078: */
079: public Extension(PluginMetadata pluginMetadata,
080: ExtensionMetadata metadata) {
081: this .metadata = metadata;
082: this .pluginMetadata = pluginMetadata;
083:
084: internalPlugin = false;
085: }
086:
087: /**
088: * @see org.columba.api.plugin.IExtension#getMetadata()
089: */
090: public ExtensionMetadata getMetadata() {
091: return metadata;
092: }
093:
094: /**
095: * @see org.columba.api.plugin.IExtension#instanciateExtension(java.lang.Object[])
096: */
097: public IExtensionInterface instanciateExtension(Object[] arguments)
098: throws PluginException {
099:
100: if (!enabled)
101: throw new PluginException(
102: "Extension <"
103: + getMetadata().getId()
104: + "> was disabled due to a former instanciation error");
105:
106: String id = null;
107: File pluginDirectory = null;
108:
109: String className = metadata.getClassname();
110:
111: if (pluginMetadata != null) {
112: id = pluginMetadata.getId();
113: // if external plugin, we need the directory of it
114: pluginDirectory = pluginMetadata.getDirectory();
115: }
116:
117: IExtensionInterface plugin = null;
118:
119: // if available, load cached instance
120: if ((metadata.isSingleton()) && (cachedInstance != null)) {
121: plugin = cachedInstance;
122:
123: } else {
124:
125: try {
126:
127: if (isInternal())
128:
129: // use default Java classlodaer
130: plugin = instanciateJavaClass(className, arguments);
131:
132: else {
133: //
134: // external plugin
135: //
136:
137: // just in case that someone who developers on a plugin
138: // adds the plugin files to his classpath, we try to
139: // load
140: // them with the default classloader
141:
142: // use default Java classlodaer
143:
144: // try {
145: // plugin = instanciateJavaClass(className, arguments);
146: // if (plugin != null)
147: // return plugin;
148: //
149: // } catch (Exception e) {
150: // //handleException(e);
151: // } catch (Error e) {
152: // //handleException(e);
153: // }
154:
155: // use external Java URL classloader
156: plugin = instanciateExternalJavaClass(arguments,
157: pluginDirectory, className);
158:
159: }
160:
161: // remember instance
162: if (metadata.isSingleton())
163: cachedInstance = plugin;
164:
165: } catch (Exception e) {
166: logErrorMessage(e);
167:
168: // disable extension
169: enabled = false;
170:
171: throw new PluginException(this , e);
172: } catch (Error e) {
173: logErrorMessage(e);
174:
175: // disable extension
176: enabled = false;
177:
178: throw new PluginException(this , e);
179: }
180:
181: }
182: return plugin;
183: }
184:
185: /**
186: * @param e
187: */
188: private void logErrorMessage(Throwable e) {
189: if (e.getCause() != null) {
190: LOG.severe(e.getCause().getMessage());
191: if (Logging.DEBUG)
192: e.getCause().printStackTrace();
193: } else {
194: LOG.severe(e.getMessage());
195: if (Logging.DEBUG)
196: e.printStackTrace();
197: }
198: }
199:
200: /**
201: * Instanciate external Java class which is specified using the
202: * <code>plugin.xml</code> descriptor file.
203: * <p>
204: * Currently, this is not used!
205: *
206: * @param arguments
207: * class constructor arguments
208: * @param pluginDirectory
209: * plugin directory
210: * @param className
211: * extension classname
212: * @return extension instance
213: * @throws Exception
214: */
215: private IExtensionInterface instanciateExternalJavaClass(
216: Object[] arguments, File pluginDirectory, String className)
217: throws Exception {
218: IExtensionInterface plugin;
219: URL[] urls = null;
220:
221: // all Java plugins package their class-files in "plugin.jar"
222: String jarFilename = Extension.FILE_PLUGIN_JAR;
223:
224: try {
225: urls = getURLs(pluginDirectory, jarFilename);
226: } catch (MalformedURLException e) {
227: // should never happen
228: e.printStackTrace();
229: }
230:
231: //
232: // @author: fdietz
233: // WORKAROUND:
234: // we simply append URLs to the existing global class loader
235: // and use the same as parent
236: //
237: // Note, that we create a new URL classloader for every class
238: // we instanciate. We might want to support hot-swapping
239: // of changed classes later.
240:
241: // append URLs to global classloader
242: Main.mainClassLoader.addURLs(urls);
243:
244: //plugin = instanciateJavaClass(className, arguments);
245:
246: // create new class loader using the global class loader as parent
247: ExternalClassLoader loader = new ExternalClassLoader(urls,
248: Main.mainClassLoader);
249: plugin = (IExtensionInterface) loader.instanciate(className,
250: arguments);
251:
252: return plugin;
253: }
254:
255: private IExtensionInterface instanciateJavaClass(String className,
256: Object[] arguments) throws Exception {
257:
258: if (className == null)
259: throw new IllegalArgumentException("className == null");
260:
261: IExtensionInterface plugin = null;
262:
263: // use our global class loader
264: ClassLoader loader = Main.mainClassLoader;
265:
266: Class actClass;
267:
268: actClass = loader.loadClass(className);
269:
270: //
271: // we can't just load the first constructor
272: // -> go find the correct constructor based
273: // -> based on the arguments
274: //
275: if ((arguments == null) || (arguments.length == 0)) {
276:
277: plugin = (IExtensionInterface) actClass.newInstance();
278:
279: } else {
280: Constructor constructor;
281:
282: constructor = ClassLoaderHelper.findConstructor(arguments,
283: actClass);
284:
285: // couldn't find correct constructor
286: if (constructor == null) {
287: LOG.severe("Couldn't find constructor for " + className
288: + " with matching argument-list: ");
289: for (int i = 0; i < arguments.length; i++) {
290: LOG.severe("argument[" + i + "]=" + arguments[i]);
291: }
292:
293: return null;
294: } else {
295:
296: plugin = (IExtensionInterface) constructor
297: .newInstance(arguments);
298:
299: }
300:
301: }
302:
303: return plugin;
304: }
305:
306: /**
307: * @see org.columba.api.plugin.IExtension#isInternal()
308: */
309: public boolean isInternal() {
310: return internalPlugin;
311: }
312:
313: /**
314: * @see java.lang.Object#toString()
315: */
316: public String toString() {
317: StringBuffer buf = new StringBuffer();
318: buf.append("id=" + metadata.getId());
319: buf.append("class=" + metadata.getClassname());
320:
321: return buf.toString();
322: }
323:
324: /**
325: * Generate array of all URLs which are used to prefill the URLClassloader.
326: * <p>
327: * All jar-files in /lib directory are automatically added.
328: *
329: * @param file
330: * plugin directory
331: * @param jarFile
332: * jar-file containing the extension code
333: * @return URL array
334: * @throws MalformedURLException
335: */
336: private URL[] getURLs(File file, String jarFile)
337: throws MalformedURLException {
338: if (file == null)
339: throw new IllegalArgumentException("file == null");
340: if (jarFile == null)
341: throw new IllegalArgumentException("jarFile == null");
342:
343: List urlList = new Vector();
344:
345: // plugin-directory
346: String path = file.getPath();
347:
348: if (jarFile != null) {
349: URL jarURL = new File(file, jarFile).toURL();
350: urlList.add(jarURL);
351: }
352:
353: URL newURL = new File(path).toURL();
354: urlList.add(newURL);
355:
356: // we add every jar-file in /lib, too
357: // plugin-directory
358:
359: File lib = new File(file, "lib");
360:
361: if (lib.exists()) {
362: File[] libList = lib.listFiles();
363:
364: for (int i = 0; i < libList.length; i++) {
365: File f = libList[i];
366:
367: if (f.getName().endsWith(".jar")) {
368: // jar-file found
369: urlList.add(f.toURL());
370: } else if (f.isDirectory()) {
371: urlList.add(f.toURL());
372: }
373: }
374: }
375:
376: URL[] url = new URL[urlList.size()];
377:
378: for (int i = 0; i < urlList.size(); i++) {
379: url[i] = (URL) urlList.get(i);
380: }
381:
382: if (Logging.DEBUG) {
383: for (int i = 0; i < url.length; i++) {
384: LOG.finest("url[" + i + "]=" + url[i]);
385: }
386: }
387:
388: return url;
389: }
390:
391: public void setInternal(boolean internal) {
392: this .internalPlugin = internal;
393: }
394:
395: /**
396: * Returns enabled by default. But, in case of an error on instanciation its
397: * disabled.
398: *
399: * @return enabled by default. But, in case of an error on instanciation its
400: */
401: public boolean isEnabled() {
402: // TODO Auto-generated method stub
403: return false;
404: }
405:
406: }
|