From 808a36a3aed19bed50df3ae91097152bfd44ffe2 Mon Sep 17 00:00:00 2001 From: Decebal Suiu Date: Tue, 15 Jul 2014 22:43:52 +0300 Subject: [PATCH] try to resolve issue #20 --- .../pf4j/DefaultExtensionFactory.java | 45 ++++++++++++ .../fortsoft/pf4j/DefaultExtensionFinder.java | 65 +++++------------ .../fortsoft/pf4j/DefaultPluginFactory.java | 69 +++++++++++++++++++ .../fortsoft/pf4j/DefaultPluginManager.java | 30 ++++++-- .../ro/fortsoft/pf4j/ExtensionDescriptor.java | 39 +++++++++++ .../ro/fortsoft/pf4j/ExtensionFactory.java | 22 ++++++ .../ro/fortsoft/pf4j/ExtensionWrapper.java | 44 ++++++++---- .../java/ro/fortsoft/pf4j/PluginFactory.java | 22 ++++++ .../java/ro/fortsoft/pf4j/PluginWrapper.java | 38 +++------- 9 files changed, 278 insertions(+), 96 deletions(-) create mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFactory.java create mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginFactory.java create mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionDescriptor.java create mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFactory.java create mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/PluginFactory.java diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFactory.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFactory.java new file mode 100644 index 0000000..5824e88 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFactory.java @@ -0,0 +1,45 @@ +/* + * Copyright 2014 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with + * the License. You may obtain a copy of the License in the LICENSE file, or at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package ro.fortsoft.pf4j; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The default implementation for ExtensionFactory. + * It uses Class.newInstance() method. + * + * @author Decebal Suiu + */ +public class DefaultExtensionFactory implements ExtensionFactory { + + private static final Logger log = LoggerFactory.getLogger(DefaultExtensionFactory.class); + + /** + * Creates an extension instance. If an error occurs than that error is logged and the method returns null. + * @param extensionClass + * @return + */ + @Override + public Object create(Class extensionClass) { + log.debug("Create instance for extension '{}'", extensionClass.getName()); + try { + return extensionClass.newInstance(); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + + return null; + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java index 0f04ed7..ab72658 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java @@ -36,9 +36,9 @@ public class DefaultExtensionFinder implements ExtensionFinder, PluginStateListe private ExtensionFactory extensionFactory; private volatile Map> entries; // cache by pluginId - public DefaultExtensionFinder(PluginManager pluginManager) { + public DefaultExtensionFinder(PluginManager pluginManager, ExtensionFactory extensionFactory) { this.pluginManager = pluginManager; - this.extensionFactory = createExtensionFactory(); + this.extensionFactory = extensionFactory; } @Override @@ -68,23 +68,26 @@ public class DefaultExtensionFinder implements ExtensionFinder, PluginStateListe for (String className : extensionClassNames) { try { - Class extensionType; + Class extensionClass; if (pluginId != null) { - extensionType = pluginManager.getPluginClassLoader(pluginId).loadClass(className); + extensionClass = pluginManager.getPluginClassLoader(pluginId).loadClass(className); } else { - extensionType = getClass().getClassLoader().loadClass(className); + extensionClass = getClass().getClassLoader().loadClass(className); } - log.debug("Checking extension type '{}'", extensionType.getName()); - if (type.isAssignableFrom(extensionType)) { - Object instance = extensionFactory.create(extensionType); - if (instance != null) { - Extension extension = extensionType.getAnnotation(Extension.class); - log.debug("Added extension '{}' with ordinal {}", extensionType.getName(), extension.ordinal()); - result.add(new ExtensionWrapper(type.cast(instance), extension.ordinal())); - } + log.debug("Checking extension type '{}'", className); + if (type.isAssignableFrom(extensionClass) && extensionClass.isAnnotationPresent(Extension.class)) { + Extension extension = extensionClass.getAnnotation(Extension.class); + ExtensionDescriptor descriptor = new ExtensionDescriptor(); + descriptor.setOrdinal(extension.ordinal()); + descriptor.setExtensionClass(extensionClass); + + ExtensionWrapper extensionWrapper = new ExtensionWrapper(descriptor); + extensionWrapper.setExtensionFactory(extensionFactory); + result.add(extensionWrapper); + log.debug("Added extension '{}' with ordinal {}", className, extension.ordinal()); } else { - log.debug("'{}' is not an extension for extension point '{}'", extensionType.getName(), type.getName()); + log.debug("'{}' is not an extension for extension point '{}'", className, type.getName()); } } catch (ClassNotFoundException e) { log.error(e.getMessage(), e); @@ -117,31 +120,6 @@ public class DefaultExtensionFinder implements ExtensionFinder, PluginStateListe entries = null; } - /** - * Add the possibility to override the ExtensionFactory. - * The default implementation uses Class.newInstance() method. - */ - protected ExtensionFactory createExtensionFactory() { - return new ExtensionFactory() { - - @Override - public Object create(Class extensionType) { - log.debug("Create instance for extension '{}'", extensionType.getName()); - - try { - return extensionType.newInstance(); - } catch (InstantiationException e) { - log.error(e.getMessage(), e); - } catch (IllegalAccessException e) { - log.error(e.getMessage(), e); - } - - return null; - } - - }; - } - private Map> readIndexFiles() { // checking cache if (entries != null) { @@ -219,13 +197,4 @@ public class DefaultExtensionFinder implements ExtensionFinder, PluginStateListe return ExtensionPoint.class.isAssignableFrom(type); } - /** - * Creates an extension instance. - */ - public static interface ExtensionFactory { - - public Object create(Class extensionType); - - } - } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginFactory.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginFactory.java new file mode 100644 index 0000000..6c4dc4f --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginFactory.java @@ -0,0 +1,69 @@ +/* + * Copyright 2014 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with + * the License. You may obtain a copy of the License in the LICENSE file, or at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package ro.fortsoft.pf4j; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; + +/** + * The default implementation for PluginFactory. + * It uses Class.newInstance() method. + * + * @author Decebal Suiu + */ +public class DefaultPluginFactory implements PluginFactory { + + private static final Logger log = LoggerFactory.getLogger(DefaultExtensionFactory.class); + + /** + * Creates a plugin instance. If an error occurs than that error is logged and the method returns null. + * @param pluginWrapper + * @return + */ + @Override + public Plugin create(final PluginWrapper pluginWrapper) { + String pluginClassName = pluginWrapper.getDescriptor().getPluginClass(); + log.debug("Create instance for plugin '{}'", pluginClassName); + + Class pluginClass; + try { + pluginClass = pluginWrapper.getPluginClassLoader().loadClass(pluginClassName); + } catch (ClassNotFoundException e) { + log.error(e.getMessage(), e); + return null; + } + + // once we have the class, we can do some checks on it to ensure + // that it is a valid implementation of a plugin. + int modifiers = pluginClass.getModifiers(); + if (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers) + || (!Plugin.class.isAssignableFrom(pluginClass))) { + log.error("The plugin class '{}' is not valid", pluginClassName); + return null; + } + + // create the plugin instance + try { + Constructor constructor = pluginClass.getConstructor(new Class[] { PluginWrapper.class }); + return (Plugin) constructor.newInstance(new Object[] { pluginWrapper }); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + + return null; + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java index 339c063..d2fcd3b 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java @@ -94,6 +94,9 @@ public class DefaultPluginManager implements PluginManager { */ private Version systemVersion = Version.ZERO; + private PluginFactory pluginFactory; + private ExtensionFactory extensionFactory; + /** * The plugins directory is supplied by System.getProperty("pf4j.pluginsDir", "plugins"). */ @@ -168,6 +171,8 @@ public class DefaultPluginManager implements PluginManager { throw new IllegalArgumentException(String.format("Specified plugin %s does not exist!", pluginArchiveFile)); } + log.debug("Loading plugin from '{}'", pluginArchiveFile); + File pluginDirectory = null; try { pluginDirectory = expandPluginArchive(pluginArchiveFile); @@ -361,7 +366,7 @@ public class DefaultPluginManager implements PluginManager { if (directories == null) { directories = new File[0]; } - log.debug("Found possible {} plugins: {}", directories.length, directories); + log.debug("Found {} possible plugins: {}", directories.length, directories); if (directories.length == 0) { log.info("No plugins"); return; @@ -554,7 +559,7 @@ public class DefaultPluginManager implements PluginManager { List> extensionsWrapper = extensionFinder.find(type); List extensions = new ArrayList(extensionsWrapper.size()); for (ExtensionWrapper extensionWrapper : extensionsWrapper) { - extensions.add(extensionWrapper.getInstance()); + extensions.add(extensionWrapper.getExtension()); } return extensions; @@ -633,7 +638,7 @@ public class DefaultPluginManager implements PluginManager { * Add the possibility to override the ExtensionFinder. */ protected ExtensionFinder createExtensionFinder() { - DefaultExtensionFinder extensionFinder = new DefaultExtensionFinder(this); + DefaultExtensionFinder extensionFinder = new DefaultExtensionFinder(this, extensionFactory); addPluginStateListener(extensionFinder); return extensionFinder; @@ -702,7 +707,21 @@ public class DefaultPluginManager implements PluginManager { return new File(pluginsDir); } - private void initialize() { + /** + * Add the possibility to override the PluginFactory.. + */ + protected PluginFactory createPluginFactory() { + return new DefaultPluginFactory(); + } + + /** + * Add the possibility to override the ExtensionFactory. + */ + protected ExtensionFactory createExtensionFactory() { + return new DefaultExtensionFactory(); + } + + private void initialize() { plugins = new HashMap(); pluginClassLoaders = new HashMap(); pathToIdMap = new HashMap(); @@ -716,6 +735,8 @@ public class DefaultPluginManager implements PluginManager { log.info("PF4J version {} in '{}' mode", getVersion(), getRuntimeMode()); pluginClasspath = createPluginClasspath(); + pluginFactory = createPluginFactory(); + extensionFactory = createExtensionFactory(); pluginDescriptorFinder = createPluginDescriptorFinder(); extensionFinder = createExtensionFinder(); @@ -760,6 +781,7 @@ public class DefaultPluginManager implements PluginManager { // create the plugin wrapper log.debug("Creating wrapper for plugin '{}'", pluginPath); PluginWrapper pluginWrapper = new PluginWrapper(pluginDescriptor, pluginPath, pluginLoader.getPluginClassLoader()); + pluginWrapper.setPluginFactory(pluginFactory); pluginWrapper.setRuntimeMode(getRuntimeMode()); // test for disabled plugin diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionDescriptor.java b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionDescriptor.java new file mode 100644 index 0000000..32a5225 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionDescriptor.java @@ -0,0 +1,39 @@ +/* + * Copyright 2014 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with + * the License. You may obtain a copy of the License in the LICENSE file, or at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package ro.fortsoft.pf4j; + +/** + * @author Decebal Suiu + */ +public class ExtensionDescriptor { + + private int ordinal; + private Class extensionClass; + + public Class getExtensionClass() { + return extensionClass; + } + + public int getOrdinal() { + return ordinal; + } + + void setExtensionClass(Class extensionClass) { + this.extensionClass = extensionClass; + } + + void setOrdinal(int ordinal) { + this.ordinal = ordinal; + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFactory.java b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFactory.java new file mode 100644 index 0000000..fd7c56b --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFactory.java @@ -0,0 +1,22 @@ +/* + * Copyright 2014 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with + * the License. You may obtain a copy of the License in the LICENSE file, or at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package ro.fortsoft.pf4j; + +/** + * Creates an extension instance. + */ +public interface ExtensionFactory { + + public Object create(Class extensionClass); + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionWrapper.java b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionWrapper.java index 2f147c0..d6ac74d 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionWrapper.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionWrapper.java @@ -1,11 +1,11 @@ /* - * Copyright 2012 Decebal Suiu - * + * Copyright 2014 Decebal Suiu + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with * the License. You may obtain a copy of the License in the LICENSE file, or at: - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. @@ -13,29 +13,43 @@ package ro.fortsoft.pf4j; /** + * A wrapper over extension instance. + * * @author Decebal Suiu */ public class ExtensionWrapper implements Comparable> { - private final T instance; - private final int ordinal; - - public ExtensionWrapper(T instance, int ordinal) { - this.instance = instance; - this.ordinal = ordinal; + ExtensionDescriptor descriptor; + ExtensionFactory extensionFactory; + T extension; // cache + + public ExtensionWrapper(ExtensionDescriptor descriptor) { + this.descriptor = descriptor; } - public T getInstance() { - return instance; + public T getExtension() { + if (extension == null) { + extension = (T) extensionFactory.create(descriptor.getExtensionClass()); + } + + return extension; } - public int getOrdinal() { - return ordinal; + public ExtensionDescriptor getDescriptor() { + return descriptor; + } + + public int getOrdinal() { + return descriptor.getOrdinal(); } @Override public int compareTo(ExtensionWrapper o) { - return (ordinal - o.getOrdinal()); + return (getOrdinal() - o.getOrdinal()); } + void setExtensionFactory(ExtensionFactory extensionFactory) { + this.extensionFactory = extensionFactory; + } + } \ No newline at end of file diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginFactory.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginFactory.java new file mode 100644 index 0000000..b9ece11 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginFactory.java @@ -0,0 +1,22 @@ +/* + * Copyright 2014 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with + * the License. You may obtain a copy of the License in the LICENSE file, or at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package ro.fortsoft.pf4j; + +/** + * Creates a plugin instance. + */ +public interface PluginFactory { + + public Plugin create(PluginWrapper pluginWrapper); + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java index f8bf716..3c3b6e1 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java @@ -12,9 +12,6 @@ */ package ro.fortsoft.pf4j; -import java.lang.reflect.Constructor; -import java.lang.reflect.Modifier; - /** * A wrapper over plugin instance. * @@ -25,22 +22,16 @@ public class PluginWrapper { PluginDescriptor descriptor; String pluginPath; PluginClassLoader pluginClassLoader; - Plugin plugin; + PluginFactory pluginFactory; PluginState pluginState; RuntimeMode runtimeMode; + Plugin plugin; // cache public PluginWrapper(PluginDescriptor descriptor, String pluginPath, PluginClassLoader pluginClassLoader) { this.descriptor = descriptor; this.pluginPath = pluginPath; this.pluginClassLoader = pluginClassLoader; - // TODO - try { - plugin = createPluginInstance(); - } catch (Exception e) { - e.printStackTrace(); - } - pluginState = PluginState.CREATED; } @@ -68,7 +59,11 @@ public class PluginWrapper { } public Plugin getPlugin() { - return plugin; + if (plugin == null) { + plugin = pluginFactory.create(this); + } + + return plugin; } public PluginState getPluginState() { @@ -130,23 +125,8 @@ public class PluginWrapper { this.runtimeMode = runtimeMode; } - private Plugin createPluginInstance() throws Exception { - String pluginClassName = descriptor.getPluginClass(); - Class pluginClass = pluginClassLoader.loadClass(pluginClassName); - - // once we have the class, we can do some checks on it to ensure - // that it is a valid implementation of a plugin. - int modifiers = pluginClass.getModifiers(); - if (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers) - || (!Plugin.class.isAssignableFrom(pluginClass))) { - throw new PluginException("The plugin class '" + pluginClassName + "' is not valid."); - } - - // create the plugin instance - Constructor constructor = pluginClass.getConstructor(new Class[] { PluginWrapper.class }); - Plugin plugin = (Plugin) constructor.newInstance(new Object[] { this }); - - return plugin; + void setPluginFactory(PluginFactory pluginFactory) { + this.pluginFactory = pluginFactory; } } -- 2.39.5