From: Decebal Suiu Date: Tue, 16 Oct 2012 11:33:48 +0000 (+0300) Subject: prepare for maven central repository X-Git-Tag: release-0.1~4 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=9bd850bd57dc1df58f8103024df996d164c5ca0b;p=pf4j.git prepare for maven central repository --- diff --git a/README.md b/README.md index ff1870b..ae3a212 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ In your pom.xml you must define the dependencies to PF4J artifacts with: ```xml - org.pf4j + ro.fortsoft.pf4j pf4j ${pf4j.version} @@ -77,13 +77,13 @@ In this case the `classes/META-INF/MANIFEST.MF` looks like: Created-By: Apache Maven Built-By: decebal Build-Jdk: 1.6.0_17 - Plugin-Class: org.pf4j.demo.welcome.WelcomePlugin + Plugin-Class: ro.fortsoft.pf4j.demo.welcome.WelcomePlugin Plugin-Dependencies: x, y, z Plugin-Id: welcome-plugin Plugin-Provider: Decebal Suiu Plugin-Version: 0.0.1 -In above manifest I described a plugin with id `welcome-plugin`, with class `org.pf4j.demo.welcome.WelcomePlugin`, with version `0.0.1` and with dependencies +In above manifest I described a plugin with id `welcome-plugin`, with class `ro.fortsoft.pf4j.demo.welcome.WelcomePlugin`, with version `0.0.1` and with dependencies to plugins `x, y, z`. You can define an extension point in your application using **ExtensionPoint** interface marker. diff --git a/demo/api/pom.xml b/demo/api/pom.xml index 1514476..59c02ff 100644 --- a/demo/api/pom.xml +++ b/demo/api/pom.xml @@ -2,7 +2,7 @@ - org.pf4j.demo + ro.fortsoft.pf4j.demo pom 0.1-SNAPSHOT @@ -34,7 +34,7 @@ - org.pf4j + ro.fortsoft.pf4j pf4j ${project.version} provided diff --git a/demo/api/src/main/java/org/pf4j/demo/api/Greeting.java b/demo/api/src/main/java/org/pf4j/demo/api/Greeting.java deleted file mode 100644 index b21f80c..0000000 --- a/demo/api/src/main/java/org/pf4j/demo/api/Greeting.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j.demo.api; - -import org.pf4j.ExtensionPoint; - -/** - * @author Decebal Suiu - */ -public interface Greeting extends ExtensionPoint { - - public String getGreeting(); - -} diff --git a/demo/api/src/main/java/ro/fortsoft/pf4j/demo/api/Greeting.java b/demo/api/src/main/java/ro/fortsoft/pf4j/demo/api/Greeting.java new file mode 100644 index 0000000..9a700c2 --- /dev/null +++ b/demo/api/src/main/java/ro/fortsoft/pf4j/demo/api/Greeting.java @@ -0,0 +1,24 @@ +/* + * Copyright 2012 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.demo.api; + +import ro.fortsoft.pf4j.ExtensionPoint; + +/** + * @author Decebal Suiu + */ +public interface Greeting extends ExtensionPoint { + + public String getGreeting(); + +} diff --git a/demo/app/pom.xml b/demo/app/pom.xml index d9084b1..38867a9 100644 --- a/demo/app/pom.xml +++ b/demo/app/pom.xml @@ -2,7 +2,7 @@ - org.pf4j.demo + ro.fortsoft.pf4j.demo pom 0.1-SNAPSHOT @@ -22,7 +22,7 @@ - org.pf4j.demo.Boot + ro.fortsoft.pf4j.demo.Boot @@ -75,12 +75,12 @@ - org.pf4j + ro.fortsoft.pf4j pf4j ${project.version} - org.pf4j.demo + ro.fortsoft.pf4j.demo pf4j-demo-api ${project.version} diff --git a/demo/app/src/main/java/org/pf4j/demo/Boot.java b/demo/app/src/main/java/org/pf4j/demo/Boot.java deleted file mode 100644 index b34af53..0000000 --- a/demo/app/src/main/java/org/pf4j/demo/Boot.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j.demo; - -import java.util.List; - -import org.apache.commons.lang.StringUtils; -import org.pf4j.DefaultPluginManager; -import org.pf4j.ExtensionWrapper; -import org.pf4j.PluginManager; -import org.pf4j.demo.api.Greeting; - -/** - * A boot class that start the demo. - * - * @author Decebal Suiu - */ -public class Boot { - - public static void main(String[] args) { - // print logo - printLogo(); - - // load and start (active/resolved) plugins - final PluginManager pluginManager = new DefaultPluginManager(); - pluginManager.loadPlugins(); - pluginManager.startPlugins(); - - List> greetings = pluginManager.getExtensions(Greeting.class); - for (ExtensionWrapper greeting : greetings) { - System.out.println(">>> " + greeting.getInstance().getGreeting()); - } - - pluginManager.stopPlugins(); - /* - Runtime.getRuntime().addShutdownHook(new Thread() { - - @Override - public void run() { - pluginManager.stopPlugins(); - } - - }); - */ - } - - private static void printLogo() { - System.out.println(StringUtils.repeat("#", 40)); - System.out.println(StringUtils.center("PF4J-DEMO", 40)); - System.out.println(StringUtils.repeat("#", 40)); - } - -} diff --git a/demo/app/src/main/java/ro/fortsoft/pf4j/demo/Boot.java b/demo/app/src/main/java/ro/fortsoft/pf4j/demo/Boot.java new file mode 100644 index 0000000..9a361fa --- /dev/null +++ b/demo/app/src/main/java/ro/fortsoft/pf4j/demo/Boot.java @@ -0,0 +1,64 @@ +/* + * Copyright 2012 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.demo; + +import java.util.List; + +import org.apache.commons.lang.StringUtils; + +import ro.fortsoft.pf4j.DefaultPluginManager; +import ro.fortsoft.pf4j.ExtensionWrapper; +import ro.fortsoft.pf4j.PluginManager; +import ro.fortsoft.pf4j.demo.api.Greeting; + +/** + * A boot class that start the demo. + * + * @author Decebal Suiu + */ +public class Boot { + + public static void main(String[] args) { + // print logo + printLogo(); + + // load and start (active/resolved) plugins + final PluginManager pluginManager = new DefaultPluginManager(); + pluginManager.loadPlugins(); + pluginManager.startPlugins(); + + List> greetings = pluginManager.getExtensions(Greeting.class); + for (ExtensionWrapper greeting : greetings) { + System.out.println(">>> " + greeting.getInstance().getGreeting()); + } + + pluginManager.stopPlugins(); + /* + Runtime.getRuntime().addShutdownHook(new Thread() { + + @Override + public void run() { + pluginManager.stopPlugins(); + } + + }); + */ + } + + private static void printLogo() { + System.out.println(StringUtils.repeat("#", 40)); + System.out.println(StringUtils.center("PF4J-DEMO", 40)); + System.out.println(StringUtils.repeat("#", 40)); + } + +} diff --git a/demo/plugin1/plugin.properties b/demo/plugin1/plugin.properties index 9da9bcc..4f95d99 100644 --- a/demo/plugin1/plugin.properties +++ b/demo/plugin1/plugin.properties @@ -1,5 +1,5 @@ plugin.id=welcome-plugin -plugin.class=org.pf4j.demo.welcome.WelcomePlugin +plugin.class=ro.fortsoft.pf4j.demo.welcome.WelcomePlugin plugin.version=0.0.1 plugin.provider=Decebal Suiu plugin.dependencies= diff --git a/demo/plugin1/pom.xml b/demo/plugin1/pom.xml index 10c1163..99d467e 100644 --- a/demo/plugin1/pom.xml +++ b/demo/plugin1/pom.xml @@ -2,7 +2,7 @@ - org.pf4j.demo + ro.fortsoft.pf4j.demo pom 0.1-SNAPSHOT @@ -111,13 +111,13 @@ - org.pf4j + ro.fortsoft.pf4j pf4j ${project.version} provided - org.pf4j.demo + ro.fortsoft.pf4j.demo pf4j-demo-api ${project.version} provided diff --git a/demo/plugin1/src/main/java/org/pf4j/demo/welcome/WelcomePlugin.java b/demo/plugin1/src/main/java/org/pf4j/demo/welcome/WelcomePlugin.java deleted file mode 100644 index 22ebf7d..0000000 --- a/demo/plugin1/src/main/java/org/pf4j/demo/welcome/WelcomePlugin.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j.demo.welcome; - -import org.pf4j.Extension; -import org.pf4j.Plugin; -import org.pf4j.PluginWrapper; -import org.pf4j.demo.api.Greeting; - -/** - * @author Decebal Suiu - */ -public class WelcomePlugin extends Plugin { - - public WelcomePlugin(PluginWrapper wrapper) { - super(wrapper); - } - - public void start() { - System.out.println("WelcomePlugin.start()"); - } - - public void stop() { - System.out.println("WelcomePlugin.stop()"); - } - - @Extension - public static class WelcomeGreeting implements Greeting { - - public String getGreeting() { - return "Welcome"; - } - - } - -} diff --git a/demo/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java b/demo/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java new file mode 100644 index 0000000..d10d3dd --- /dev/null +++ b/demo/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java @@ -0,0 +1,46 @@ +/* + * Copyright 2012 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.demo.welcome; + +import ro.fortsoft.pf4j.Extension; +import ro.fortsoft.pf4j.Plugin; +import ro.fortsoft.pf4j.PluginWrapper; +import ro.fortsoft.pf4j.demo.api.Greeting; + +/** + * @author Decebal Suiu + */ +public class WelcomePlugin extends Plugin { + + public WelcomePlugin(PluginWrapper wrapper) { + super(wrapper); + } + + public void start() { + System.out.println("WelcomePlugin.start()"); + } + + public void stop() { + System.out.println("WelcomePlugin.stop()"); + } + + @Extension + public static class WelcomeGreeting implements Greeting { + + public String getGreeting() { + return "Welcome"; + } + + } + +} diff --git a/demo/plugin2/plugin.properties b/demo/plugin2/plugin.properties index 60b6f33..0de45e6 100644 --- a/demo/plugin2/plugin.properties +++ b/demo/plugin2/plugin.properties @@ -1,5 +1,5 @@ plugin.id=hello-plugin -plugin.class=org.pf4j.demo.hello.HelloPlugin +plugin.class=ro.fortsoft.pf4j.demo.hello.HelloPlugin plugin.version=0.0.1 plugin.provider=Decebal Suiu plugin.dependencies= diff --git a/demo/plugin2/pom.xml b/demo/plugin2/pom.xml index 7f4f617..c1b5cb3 100644 --- a/demo/plugin2/pom.xml +++ b/demo/plugin2/pom.xml @@ -2,7 +2,7 @@ - org.pf4j.demo + ro.fortsoft.pf4j.demo pom 0.1-SNAPSHOT @@ -120,13 +120,13 @@ - org.pf4j + ro.fortsoft.pf4j pf4j ${project.version} provided - org.pf4j.demo + ro.fortsoft.pf4j.demo pf4j-demo-api ${project.version} provided diff --git a/demo/plugin2/src/main/java/org/pf4j/demo/hello/HelloPlugin.java b/demo/plugin2/src/main/java/org/pf4j/demo/hello/HelloPlugin.java deleted file mode 100644 index ad7fd0c..0000000 --- a/demo/plugin2/src/main/java/org/pf4j/demo/hello/HelloPlugin.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j.demo.hello; - -import org.pf4j.Extension; -import org.pf4j.Plugin; -import org.pf4j.PluginWrapper; -import org.pf4j.demo.api.Greeting; - -/** - * A very simple plugin. - * - * @author Decebal Suiu - */ -public class HelloPlugin extends Plugin { - - public HelloPlugin(PluginWrapper wrapper) { - super(wrapper); - } - - public void start() { - System.out.println("HelloPlugin.start()"); - } - - public void stop() { - System.out.println("HelloPlugin.stop()"); - } - - @Extension - public static class HelloGreeting implements Greeting { - - public String getGreeting() { - return "Hello"; - } - - } - -} diff --git a/demo/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java b/demo/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java new file mode 100644 index 0000000..8f12e23 --- /dev/null +++ b/demo/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012 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.demo.hello; + +import ro.fortsoft.pf4j.Extension; +import ro.fortsoft.pf4j.Plugin; +import ro.fortsoft.pf4j.PluginWrapper; +import ro.fortsoft.pf4j.demo.api.Greeting; + +/** + * A very simple plugin. + * + * @author Decebal Suiu + */ +public class HelloPlugin extends Plugin { + + public HelloPlugin(PluginWrapper wrapper) { + super(wrapper); + } + + public void start() { + System.out.println("HelloPlugin.start()"); + } + + public void stop() { + System.out.println("HelloPlugin.stop()"); + } + + @Extension + public static class HelloGreeting implements Greeting { + + public String getGreeting() { + return "Hello"; + } + + } + +} diff --git a/demo/pom.xml b/demo/pom.xml index dbb029a..ec642de 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -2,13 +2,13 @@ - org.pf4j + ro.fortsoft.pf4j pom 0.1-SNAPSHOT 4.0.0 - org.pf4j.demo + ro.fortsoft.pf4j.demo pom 0.1-SNAPSHOT pom diff --git a/pf4j/pom.xml b/pf4j/pom.xml index 920a870..feae21a 100644 --- a/pf4j/pom.xml +++ b/pf4j/pom.xml @@ -2,7 +2,7 @@ - org.pf4j + ro.fortsoft.pf4j pom 0.1-SNAPSHOT diff --git a/pf4j/src/main/java/org/pf4j/DefaultExtensionFinder.java b/pf4j/src/main/java/org/pf4j/DefaultExtensionFinder.java deleted file mode 100644 index e7a6dc9..0000000 --- a/pf4j/src/main/java/org/pf4j/DefaultExtensionFinder.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j; - -import java.lang.reflect.AnnotatedElement; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import net.java.sezpoz.Index; -import net.java.sezpoz.IndexItem; - -/** - * Using Sezpoz(http://sezpoz.java.net/) for extensions discovery. - * - * @author Decebal Suiu - */ -public class DefaultExtensionFinder implements ExtensionFinder { - - private static final Logger LOG = LoggerFactory.getLogger(DefaultExtensionFinder.class); - - private volatile List> indices; - private ClassLoader classLoader; - - public DefaultExtensionFinder(ClassLoader classLoader) { - this.classLoader = classLoader; - } - - @Override - public List> find(Class type) { - LOG.debug("Find extensions for " + type); - List> result = new ArrayList>(); - getIndices(); -// System.out.println("indices = "+ indices); - for (IndexItem item : indices) { - try { - AnnotatedElement element = item.element(); - Class extensionType = (Class) element; - LOG.debug("Checking extension type " + extensionType); - if (type.isAssignableFrom(extensionType)) { - Object instance = item.instance(); - if (instance != null) { - LOG.debug("Added extension " + extensionType); - result.add(new ExtensionWrapper(type.cast(instance), item.annotation().ordinal())); - } - } - } catch (InstantiationException e) { - LOG.error(e.getMessage(), e); - } - } - - return result; - } - - private List> getIndices() { - if (indices == null) { - indices = new ArrayList>(); - Iterator> it = Index.load(Extension.class, Object.class, classLoader).iterator(); - while (it.hasNext()) { - indices.add(it.next()); - } - } - - return indices; - } - -} diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginDescriptorFinder.java b/pf4j/src/main/java/org/pf4j/DefaultPluginDescriptorFinder.java deleted file mode 100644 index 0055e57..0000000 --- a/pf4j/src/main/java/org/pf4j/DefaultPluginDescriptorFinder.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.jar.Attributes; -import java.util.jar.Manifest; - -import org.apache.commons.lang.StringUtils; - -/** - * Read the plugin descriptor from the manifest file. - * - * @author Decebal Suiu - */ -public class DefaultPluginDescriptorFinder implements PluginDescriptorFinder { - - @Override - public PluginDescriptor find(File pluginRepository) throws PluginException { - // TODO it's ok with classes/ ? - File manifestFile = new File(pluginRepository, "classes/META-INF/MANIFEST.MF"); - if (!manifestFile.exists()) { - // not found a 'plugin.xml' file for this plugin - throw new PluginException("Cannot find '" + manifestFile + "' file"); - } - - FileInputStream input = null; - try { - input = new FileInputStream(manifestFile); - } catch (FileNotFoundException e) { - // not happening - } - - Manifest manifest = null; - try { - manifest = new Manifest(input); - } catch (IOException e) { - throw new PluginException(e.getMessage(), e); - } finally { - try { - input.close(); - } catch (IOException e) { - throw new PluginException(e.getMessage(), e); - } - } - - PluginDescriptor pluginDescriptor = new PluginDescriptor(); - - // TODO validate !!! - Attributes attrs = manifest.getMainAttributes(); - String id = attrs.getValue("Plugin-Id"); - if (StringUtils.isEmpty(id)) { - throw new PluginException("Plugin-Id cannot be empty"); - } - pluginDescriptor.setPluginId(id); - - String clazz = attrs.getValue("Plugin-Class"); - if (StringUtils.isEmpty(clazz)) { - throw new PluginException("Plugin-Class cannot be empty"); - } - pluginDescriptor.setPluginClass(clazz); - - String version = attrs.getValue("Plugin-Version"); - if (StringUtils.isEmpty(version)) { - throw new PluginException("Plugin-Version cannot be empty"); - } - pluginDescriptor.setPluginVersion(PluginVersion.createVersion(version)); - - String provider = attrs.getValue("Plugin-Provider"); - pluginDescriptor.setProvider(provider); - String dependencies = attrs.getValue("Plugin-Dependencies"); - pluginDescriptor.setDependencies(dependencies); - - return pluginDescriptor; - } - -} diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java b/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java deleted file mode 100644 index 9cb7ccd..0000000 --- a/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j; - -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.lang.reflect.Constructor; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.pf4j.util.DirectoryFilter; -import org.pf4j.util.UberClassLoader; -import org.pf4j.util.Unzip; -import org.pf4j.util.ZipFilter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * Default implementation of the PluginManager interface. - * - * @author Decebal Suiu - */ -public class DefaultPluginManager implements PluginManager { - - private static final Logger LOG = LoggerFactory.getLogger(DefaultPluginManager.class); - - /** - * The plugins repository. - */ - private File pluginsDirectory; - - private ExtensionFinder extensionFinder; - - private PluginDescriptorFinder pluginDescriptorFinder; - - /** - * A map of plugins this manager is responsible for (the key is the 'pluginId'). - */ - private Map plugins; - - /** - * A map of plugin class loaders (he key is the 'pluginId'). - */ - private Map pluginClassLoaders; - - /** - * A relation between 'pluginPath' and 'pluginId' - */ - private Map pathToIdMap; - - /** - * A list with unresolved plugins (unresolved dependency). - */ - private List unresolvedPlugins; - - /** - * A list with resolved plugins (resolved dependency). - */ - private List resolvedPlugins; - - /** - * A list with disabled plugins. - */ - private List disabledPlugins; - - private UberClassLoader uberClassLoader; - - /** - * Th plugins directory is supplied by System.getProperty("pf4j.pluginsDir", "plugins"). - */ - public DefaultPluginManager() { - this(new File(System.getProperty("pf4j.pluginsDir", "plugins"))); - } - - /** - * Constructs DefaultPluginManager which the given plugins directory. - * - * @param pluginsDirectory - * the directory to search for plugins - */ - public DefaultPluginManager(File pluginsDirectory) { - this.pluginsDirectory = pluginsDirectory; - plugins = new HashMap(); - pluginClassLoaders = new HashMap(); - pathToIdMap = new HashMap(); - unresolvedPlugins = new ArrayList(); - resolvedPlugins = new ArrayList(); - disabledPlugins = new ArrayList(); - pluginDescriptorFinder = new DefaultPluginDescriptorFinder(); - uberClassLoader = new UberClassLoader(); - extensionFinder = new DefaultExtensionFinder(uberClassLoader); - } - - /** - * Retrieves all active plugins. - */ - public List getPlugins() { - return new ArrayList(plugins.values()); - } - - public List getResolvedPlugins() { - return resolvedPlugins; - } - - public Plugin getPlugin(String pluginId) { - return plugins.get(pluginId); - } - - public List getUnresolvedPlugins() { - return unresolvedPlugins; - } - - public List getDisabledPlugins() { - return disabledPlugins; - } - - /** - * Start all active plugins. - */ - public void startPlugins() { - List resolvedPlugins = getResolvedPlugins(); - for (Plugin plugin : resolvedPlugins) { - try { - plugin.start(); - } catch (PluginException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - - /** - * Stop all active plugins. - */ - public void stopPlugins() { - List resolvedPlugins = getResolvedPlugins(); - for (Plugin plugin : resolvedPlugins) { - try { - plugin.stop(); - } catch (PluginException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - - /** - * Load plugins. - */ - public void loadPlugins() { - // check for plugins directory - if (!pluginsDirectory.exists() || !pluginsDirectory.isDirectory()) { - LOG.error("No '" + pluginsDirectory + "' directory"); - return; - } - - // expand all plugin archives - FilenameFilter zipFilter = new ZipFilter(); - String[] zipFiles = pluginsDirectory.list(zipFilter); - for (String zipFile : zipFiles) { - try { - expandPluginArchive(zipFile); - } catch (IOException e) { - LOG.error(e.getMessage(), e); - e.printStackTrace(); - } - } - - // load any plugin from plugins directory - FilenameFilter directoryFilter = new DirectoryFilter(); - String[] directories = pluginsDirectory.list(directoryFilter); - for (String directory : directories) { - try { - loadPlugin(directory); - } catch (Exception e) { - LOG.error(e.getMessage(), e); - e.printStackTrace(); - } - } - - // check for no plugins - if (directories.length == 0) { - LOG.info("No plugins"); - return; - } - - // resolve 'unresolvedPlugins' - resolvePlugins(); - } - - /** - * Get plugin class loader for this path. - */ - public PluginClassLoader getPluginClassLoader(String pluginId) { - return pluginClassLoaders.get(pluginId); - } - - public List> getExtensions(Class type) { - return extensionFinder.find(type); - } - - private void loadPlugin(String fileName) throws Exception { - // test for plugin directory - File pluginDirectory = new File(pluginsDirectory, fileName); - if (!pluginDirectory.isDirectory()) { - return; - } - - // try to load the plugin - String pluginPath = "/".concat(fileName); - - // test for disabled plugin - if (disabledPlugins.contains(pluginPath)) { - return; - } - - // it's a new plugin - if (plugins.get(pathToIdMap.get(pluginPath)) != null) { - return; - } - - // retrieves the plugin descriptor - LOG.debug("Find plugin descriptor '" + pluginPath + "'"); - PluginDescriptor pluginDescriptor = pluginDescriptorFinder.find(pluginDirectory); - LOG.debug("Descriptor " + pluginDescriptor); - String pluginClassName = pluginDescriptor.getPluginClass(); - LOG.debug("Class '" + pluginClassName + "'" + " for plugin '" + pluginPath + "'"); - - // load plugin - LOG.debug("Loading plugin '" + pluginPath + "'"); - PluginWrapper pluginWrapper = new PluginWrapper(pluginDescriptor); - PluginLoader pluginLoader = new PluginLoader(this, pluginWrapper, pluginDirectory); - pluginLoader.load(); - LOG.debug("Loaded plugin '" + pluginPath + "'"); - - // set some variables in plugin wrapper - pluginWrapper.setPluginPath(pluginPath); - pluginWrapper.setPluginClassLoader(pluginLoader.getPluginClassLoader()); - - // create the plugin instance - LOG.debug("Creating instance for plugin '" + pluginPath + "'"); - Plugin plugin = getPluginInstance(pluginWrapper, pluginLoader); - LOG.debug("Created instance '" + plugin + "' for plugin '" + pluginPath + "'"); - - String pluginId = pluginDescriptor.getPluginId(); - - // add plugin to the list with plugins - plugins.put(pluginId, plugin); - unresolvedPlugins.add(plugin); - - // add plugin class loader to the list with class loaders - PluginClassLoader pluginClassLoader = pluginLoader.getPluginClassLoader(); - pluginDescriptor.setPluginClassLoader(pluginClassLoader); - pluginClassLoaders.put(pluginId, pluginClassLoader); - } - - private void expandPluginArchive(String fileName) throws IOException { - File pluginArchiveFile = new File(pluginsDirectory, fileName); - long pluginArchiveDate = pluginArchiveFile.lastModified(); - String pluginName = fileName.substring(0, fileName.length() - 4); - File pluginDirectory = new File(pluginsDirectory, pluginName); - // check if exists directory or the '.zip' file is "newer" than directory - if (!pluginDirectory.exists() || pluginArchiveDate > pluginDirectory.lastModified()) { - LOG.debug("Expand plugin archive '" + pluginArchiveFile + "' in '" + pluginDirectory + "'"); - // create directorie for plugin - pluginDirectory.mkdirs(); - - // expand '.zip' file - Unzip unzip = new Unzip(); - unzip.setSource(pluginArchiveFile); - unzip.setDestination(pluginDirectory); - unzip.extract(); - } - } - - private Plugin getPluginInstance(PluginWrapper pluginWrapper, PluginLoader pluginLoader) - throws Exception { - String pluginClassName = pluginWrapper.getDescriptor().getPluginClass(); - - ClassLoader pluginClassLoader = pluginLoader.getPluginClassLoader(); - 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 compatible."); - } - - // create the plugin instance - Constructor constructor = pluginClass.getConstructor(new Class[] { PluginWrapper.class }); - Plugin plugin = (Plugin) constructor.newInstance(new Object[] { pluginWrapper }); - - return plugin; - } - - private void resolvePlugins() { - resolveDependencies(); - } - - private void resolveDependencies() { - DependencyResolver dependencyResolver = new DependencyResolver(unresolvedPlugins); - resolvedPlugins = dependencyResolver.getSortedDependencies(); - for (Plugin plugin : resolvedPlugins) { - unresolvedPlugins.remove(plugin); - uberClassLoader.addLoader(plugin.getWrapper().getPluginClassLoader()); - } - } - -} diff --git a/pf4j/src/main/java/org/pf4j/DependencyResolver.java b/pf4j/src/main/java/org/pf4j/DependencyResolver.java deleted file mode 100644 index 2f5f601..0000000 --- a/pf4j/src/main/java/org/pf4j/DependencyResolver.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j; - -import java.util.ArrayList; -import java.util.List; - -import org.pf4j.util.DirectedGraph; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * @author Decebal Suiu - */ -class DependencyResolver { - - private static final Logger LOG = LoggerFactory.getLogger(DependencyResolver.class); - - private List plugins; - - public DependencyResolver(List plugins) { - this.plugins = plugins; - } - - /** - * Get the list of plugins in dependency sorted order. - */ - public List getSortedDependencies() { - DirectedGraph graph = new DirectedGraph(); - for (Plugin plugin : plugins) { - PluginDescriptor descriptor = plugin.getWrapper().getDescriptor(); - String pluginId = descriptor.getPluginId(); - List dependencies = descriptor.getDependencies(); - if (!dependencies.isEmpty()) { - for (String dependency : dependencies) { - graph.addEdge(pluginId, dependency); - } - } else { - graph.addVertex(pluginId); - } - } - - LOG.debug("Graph: " + graph); - List pluginsId = graph.reverseTopologicalSort(); - - if (pluginsId == null) { - LOG.error("Cyclic dependences !!!"); - return null; - } - - LOG.debug("Plugins order: " + pluginsId); - List sortedPlugins = new ArrayList(); - for (String pluginId : pluginsId) { - sortedPlugins.add(getPlugin(pluginId)); - } - - return sortedPlugins; - } - - private Plugin getPlugin(String pluginId) { - for (Plugin plugin : plugins) { - if (pluginId.equals(plugin.getWrapper().getDescriptor().getPluginId())) { - return plugin; - } - } - - return null; - } - -} diff --git a/pf4j/src/main/java/org/pf4j/Extension.java b/pf4j/src/main/java/org/pf4j/Extension.java deleted file mode 100644 index 6c499b6..0000000 --- a/pf4j/src/main/java/org/pf4j/Extension.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import net.java.sezpoz.Indexable; - -/** - * @author Decebal Suiu - */ -@Indexable -@Retention(RUNTIME) -@Target(TYPE) -@Documented -public @interface Extension { - - int ordinal() default 0; - -} diff --git a/pf4j/src/main/java/org/pf4j/ExtensionFinder.java b/pf4j/src/main/java/org/pf4j/ExtensionFinder.java deleted file mode 100644 index 5fc466d..0000000 --- a/pf4j/src/main/java/org/pf4j/ExtensionFinder.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j; - -import java.util.List; - -/** - * @author Decebal Suiu - */ -public interface ExtensionFinder { - - public List> find(Class type); - -} diff --git a/pf4j/src/main/java/org/pf4j/ExtensionPoint.java b/pf4j/src/main/java/org/pf4j/ExtensionPoint.java deleted file mode 100644 index 9113c1d..0000000 --- a/pf4j/src/main/java/org/pf4j/ExtensionPoint.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j; - -/** - * @author Decebal Suiu - */ -public interface ExtensionPoint { - -} diff --git a/pf4j/src/main/java/org/pf4j/ExtensionWrapper.java b/pf4j/src/main/java/org/pf4j/ExtensionWrapper.java deleted file mode 100644 index 9ba320e..0000000 --- a/pf4j/src/main/java/org/pf4j/ExtensionWrapper.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j; - -/** - * @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; - } - - public T getInstance() { - return instance; - } - - public int getOrdinal() { - return ordinal; - } - - @Override - public int compareTo(ExtensionWrapper o) { - return (ordinal - o.getOrdinal()); - } - -} \ No newline at end of file diff --git a/pf4j/src/main/java/org/pf4j/Plugin.java b/pf4j/src/main/java/org/pf4j/Plugin.java deleted file mode 100644 index bab55c1..0000000 --- a/pf4j/src/main/java/org/pf4j/Plugin.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class will be extended by all plugins and - * serve as the common class between a plugin and the application. - * - * @author Decebal Suiu - */ -public abstract class Plugin { - - /** - * Makes logging service available for descending classes. - */ - protected final Logger log = LoggerFactory.getLogger(getClass()); - - /** - * Wrapper of the plugin. - */ - PluginWrapper wrapper; - - /** - * Constructor to be used by plugin manager for plugin instantiation. - * Your plugins have to provide constructor with this exact signature to - * be successfully loaded by manager. - */ - public Plugin(final PluginWrapper wrapper) { - if (wrapper == null) { - throw new IllegalArgumentException("Wrapper cannot be null"); - } - - this.wrapper = wrapper; - } - - /** - * Retrieves the wrapper of this plug-in. - */ - public final PluginWrapper getWrapper() { - return wrapper; - } - - /** - * Start method is called by the application when the plugin is loaded. - */ - public void start() throws PluginException { - } - - /** - * Stop method is called by the application when the plugin is unloaded. - */ - public void stop() throws PluginException { - } - -} diff --git a/pf4j/src/main/java/org/pf4j/PluginClassLoader.java b/pf4j/src/main/java/org/pf4j/PluginClassLoader.java deleted file mode 100644 index 884642f..0000000 --- a/pf4j/src/main/java/org/pf4j/PluginClassLoader.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j; - -import java.net.URL; -import java.net.URLClassLoader; -import java.util.List; - -/** - * One instance of this class should be created by plugin manager for every available plug-in. - * - * @author Decebal Suiu - */ -class PluginClassLoader extends URLClassLoader { - - private static final String JAVA_PACKAGE_PREFIX = "java."; - private static final String JAVAX_PACKAGE_PREFIX = "javax."; - private static final String PLUGIN_PACKAGE_PREFIX = "org.pf4j."; - - private PluginManager pluginManager; - private PluginWrapper pluginWrapper; - - public PluginClassLoader(PluginManager pluginManager, PluginWrapper pluginWrapper, ClassLoader parent) { - super(new URL[0], parent); - - this.pluginManager = pluginManager; - this.pluginWrapper = pluginWrapper; - } - - @Override - public void addURL(URL url) { - super.addURL(url); - } - - @Override - public Class loadClass(String className) throws ClassNotFoundException { -// System.out.println(">>>" + className); - - // first check whether it's a system class, delegate to the system loader - if (className.startsWith(JAVA_PACKAGE_PREFIX) || className.startsWith(JAVAX_PACKAGE_PREFIX)) { - return findSystemClass(className); - } - - // second check whether it's already been loaded - Class loadedClass = findLoadedClass(className); - if (loadedClass != null) { - return loadedClass; - } - - // nope, try to load locally - try { - return findClass(className); - } catch (ClassNotFoundException e) { - // try next step - } - - // if the class it's a part of the plugin engine use parent class loader - if (className.startsWith(PLUGIN_PACKAGE_PREFIX)) { - try { - return PluginClassLoader.class.getClassLoader().loadClass(className); - } catch (ClassNotFoundException e) { - // try next step - } - } - - // look in dependencies - List dependencies = pluginWrapper.getDescriptor().getDependencies(); - for (String dependency : dependencies) { - PluginClassLoader classLoader = pluginManager.getPluginClassLoader(dependency); - try { - return classLoader.loadClass(className); - } catch (ClassNotFoundException e) { - // try next dependency - } - } - - // use the standard URLClassLoader (which follows normal parent delegation) - return super.loadClass(className); - } - -} diff --git a/pf4j/src/main/java/org/pf4j/PluginDescriptor.java b/pf4j/src/main/java/org/pf4j/PluginDescriptor.java deleted file mode 100644 index 8346e48..0000000 --- a/pf4j/src/main/java/org/pf4j/PluginDescriptor.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.builder.ToStringBuilder; -import org.apache.commons.lang.builder.ToStringStyle; - -/** - * A plugin descriptor contains information about a plug-in obtained - * from the manifest (META-INF) file. - * - * @author Decebal Suiu - */ -class PluginDescriptor { - - private String pluginId; - private String pluginClass; - private PluginVersion version; - private String provider; - private String pluginPath; - private List dependencies; - private PluginClassLoader pluginClassLoader; - - public PluginDescriptor() { - dependencies = new ArrayList(); - } - - /** - * Returns the unique identifier of this plugin. - */ - public String getPluginId() { - return pluginId; - } - - /** - * Returns the name of the class that implements Plugin interface. - */ - public String getPluginClass() { - return pluginClass; - } - - /** - * Returns the version of this plugin. - */ - public PluginVersion getVersion() { - return version; - } - - /** - * Returns the provider name of this plugin. - */ - public String getProvider() { - return provider; - } - - /** - * Returns the path of this plugin relative to plugins directory. - */ - public String getPluginPath() { - return pluginPath; - } - - /** - * Returns all dependencies declared by this plugin. - * Returns an empty array if this plugin does not declare any require. - */ - public List getDependencies() { - return dependencies; - } - - /** - * Returns the plugin class loader used to load classes and resources - * for this plug-in. The class loader can be used to directly access - * plug-in resources and classes. - */ - public PluginClassLoader getPluginClassLoader() { - return pluginClassLoader; - } - - @Override - public String toString() { - return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) - .append("pluginId", pluginId) - .append("pluginClass", pluginClass) - .append("version", version) - .append("provider", provider) - .append("pluginPath", pluginPath) - .append("dependencies", dependencies) - .toString(); - } - - void setPluginId(String pluginId) { - this.pluginId = pluginId; - } - - void setPluginClass(String pluginClassName) { - this.pluginClass = pluginClassName; - } - - void setPluginVersion(PluginVersion version) { - this.version = version; - } - - void setProvider(String provider) { - this.provider = provider; - } - - void setPluginPath(String pluginPath) { - this.pluginPath = pluginPath; - } - - void setDependencies(String dependencies) { - if (dependencies != null) { - this.dependencies = Arrays.asList(StringUtils.split(dependencies, ',')); - } else { - this.dependencies = Collections.emptyList(); - } - } - - void setPluginClassLoader(PluginClassLoader pluginClassLoader) { - this.pluginClassLoader = pluginClassLoader; - } - -} diff --git a/pf4j/src/main/java/org/pf4j/PluginDescriptorFinder.java b/pf4j/src/main/java/org/pf4j/PluginDescriptorFinder.java deleted file mode 100644 index c59ec65..0000000 --- a/pf4j/src/main/java/org/pf4j/PluginDescriptorFinder.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j; - -import java.io.File; - -/** - * Find a plugin descriptor in a directory (plugin repository). - * You can find in manifest file @see DefaultPluginDescriptorFinder, - * xml file, properties file, java services (with ServiceLoader), etc. - * - * @author Decebal Suiu - */ -public interface PluginDescriptorFinder { - - public PluginDescriptor find(File pluginRepository) throws PluginException; - -} diff --git a/pf4j/src/main/java/org/pf4j/PluginException.java b/pf4j/src/main/java/org/pf4j/PluginException.java deleted file mode 100644 index c341c70..0000000 --- a/pf4j/src/main/java/org/pf4j/PluginException.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j; - -/** - * An exception used to indicate that a plugin problem occurred. - * - * @author Decebal Suiu - */ -class PluginException extends Exception { - - private static final long serialVersionUID = 1L; - - public PluginException() { - super(); - } - - public PluginException(String message) { - super(message); - } - - public PluginException(Throwable cause) { - super(cause); - } - - public PluginException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/pf4j/src/main/java/org/pf4j/PluginLoader.java b/pf4j/src/main/java/org/pf4j/PluginLoader.java deleted file mode 100644 index bd64d59..0000000 --- a/pf4j/src/main/java/org/pf4j/PluginLoader.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j; - -import java.io.File; -import java.io.FilenameFilter; -import java.net.MalformedURLException; -import java.util.Vector; - -import org.pf4j.util.DirectoryFilter; -import org.pf4j.util.JarFilter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * Load all informations needed by a plugin. - * This means add all jar files from 'lib' directory, 'classes' - * to classpath. - * It's a class for only the internal use. - * - * @author Decebal Suiu - */ -class PluginLoader { - - private static final Logger LOG = LoggerFactory.getLogger(PluginLoader.class); - - /* - * The plugin repository. - */ - private File pluginRepository; - - /* - * The directory with '.class' files. - */ - private File classesDirectory; - - /* - * The directory with '.jar' files. - */ - private File libDirectory; - - private PluginClassLoader pluginClassLoader; - - public PluginLoader(PluginManager pluginManager, PluginWrapper pluginWrapper, File pluginRepository) { - this.pluginRepository = pluginRepository; - classesDirectory = new File(pluginRepository, "classes"); - libDirectory = new File(pluginRepository, "lib"); - ClassLoader parent = getClass().getClassLoader(); - pluginClassLoader = new PluginClassLoader(pluginManager, pluginWrapper, parent); - LOG.debug("Created class loader " + pluginClassLoader); - } - - public File getPluginRepository() { - return pluginRepository; - } - - public boolean load() { - return loadClassesAndJars(); - } - - public PluginClassLoader getPluginClassLoader() { - return pluginClassLoader; - } - - private boolean loadClassesAndJars() { - return loadClasses() && loadJars(); - } - - private void getJars(Vector v, File file) { - FilenameFilter jarFilter = new JarFilter(); - FilenameFilter directoryFilter = new DirectoryFilter(); - - if (file.exists() && file.isDirectory() && file.isAbsolute()) { - String[] jars = file.list(jarFilter); - for (int i = 0; (jars != null) && (i < jars.length); ++i) { - v.addElement(jars[i]); - } - - String[] directoryList = file.list(directoryFilter); - for (int i = 0; (directoryList != null) && (i < directoryList.length); ++i) { - File directory = new File(file, directoryList[i]); - getJars(v, directory); - } - } - } - - private boolean loadClasses() { - // make 'classesDirectory' absolute - classesDirectory = classesDirectory.getAbsoluteFile(); - - if (classesDirectory.exists() && classesDirectory.isDirectory()) { - LOG.debug("Found '" + classesDirectory.getPath() + "' directory"); - - try { - pluginClassLoader.addURL(classesDirectory.toURI().toURL()); - LOG.debug("Added '" + classesDirectory + "' to the class loader path"); - } catch (MalformedURLException e) { - e.printStackTrace(); - LOG.error(e.getMessage(), e); - return false; - } - } - - return true; - } - - /** - * Add all *.jar files from '/lib' directory. - */ - private boolean loadJars() { - // make 'jarDirectory' absolute - libDirectory = libDirectory.getAbsoluteFile(); - - Vector jars = new Vector(); - getJars(jars, libDirectory); - for (String jar : jars) { - File jarFile = new File(libDirectory, jar); - try { - pluginClassLoader.addURL(jarFile.toURI().toURL()); - LOG.debug("Added '" + jarFile + "' to the class loader path"); - } catch (MalformedURLException e) { - e.printStackTrace(); - LOG.error(e.getMessage(), e); - return false; - } - } - - return true; - } - -} diff --git a/pf4j/src/main/java/org/pf4j/PluginManager.java b/pf4j/src/main/java/org/pf4j/PluginManager.java deleted file mode 100644 index cf0cff1..0000000 --- a/pf4j/src/main/java/org/pf4j/PluginManager.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j; - -import java.util.List; - -/** - * Provides the functionality for plugin management such as load, - * start and stop the plugins. - * - * @author Decebal Suiu - */ -public interface PluginManager { - - /** - * Retrieves all plugins. - */ - public List getPlugins(); - - /** - * Load plugins. - */ - public void loadPlugins(); - - /** - * Start all active plugins. - */ - public void startPlugins(); - - /** - * Stop all active plugins. - */ - public void stopPlugins(); - - public PluginClassLoader getPluginClassLoader(String pluginId); - - public List> getExtensions(Class type); - -} diff --git a/pf4j/src/main/java/org/pf4j/PluginVersion.java b/pf4j/src/main/java/org/pf4j/PluginVersion.java deleted file mode 100644 index 66267c9..0000000 --- a/pf4j/src/main/java/org/pf4j/PluginVersion.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j; - -import java.util.ArrayList; -import java.util.List; -import java.util.StringTokenizer; - -/** - * Represents the version of a Plugin and allows versions to be compared. - * Version identifiers have five components. - * - * 1. Major version. A non-negative integer. - * 2. Minor version. A non-negative integer. - * 3. Release version. A non-negative integer. - * 4. Build version. A non-negative integer. - * 5. Qualifier. A text string. - * - * This class is immutable. - * - * @author Decebal Suiu - */ -public class PluginVersion implements Comparable { - - private int major; - private int minor; - private int release; - private int build; - private String qualifier; - - private PluginVersion() { - } - - public PluginVersion(int major, int minor, int release) { - this.major = major; - this.minor = minor; - this.release = release; - } - - public PluginVersion(int major, int minor, int release, int build) { - this.major = major; - this.minor = minor; - this.release = release; - this.build = build; - } - - public PluginVersion(int major, int minor, int release, int build, String qualifier) { - this.major = major; - this.minor = minor; - this.release = release; - this.build = build; - this.qualifier = qualifier; - } - - public static PluginVersion createVersion(String version) { - if (version == null) { - return new PluginVersion(); - } - - PluginVersion v = new PluginVersion(); - - StringTokenizer st = new StringTokenizer(version, "."); - List tmp = new ArrayList(); - for (int i = 0; st.hasMoreTokens() && i < 4; i++) { - tmp.add(st.nextToken()); - } - - int n = tmp.size(); - switch (n) { - case 0 : - break; - case 1 : - v.major = Integer.parseInt(tmp.get(0)); - break; - case 2 : - v.major = Integer.parseInt(tmp.get(0)); - v.minor = Integer.parseInt(tmp.get(1)); - break; - case 3 : - v.major = Integer.parseInt(tmp.get(0)); - v.minor = Integer.parseInt(tmp.get(1)); - v.release = Integer.parseInt(tmp.get(2)); - break; - case 4 : - v.major = Integer.parseInt(tmp.get(0)); - v.minor = Integer.parseInt(tmp.get(1)); - v.release = Integer.parseInt(tmp.get(2)); - v.build = Integer.parseInt(tmp.get(3)); - break; - } - - return v; - } - - public int getMajor() { - return this.major; - } - - public int getMinor() { - return this.minor; - } - - public int getRelease() { - return this.release; - } - - public int getBuild() { - return this.build; - } - - public String getQualifier() { - return qualifier; - } - - public String toString() { - StringBuffer sb = new StringBuffer(50); - sb.append(major); - sb.append('.'); - sb.append(minor); - sb.append('.'); - sb.append(release); - sb.append('.'); - sb.append(build); - if (qualifier != null) { - sb.append(qualifier); - } - - return sb.toString(); - } - - public int compareTo(PluginVersion version) { - if (version.major > major) { - return 1; - } else if (version.major < major) { - return -1; - } - - if (version.minor > minor) { - return 1; - } else if (version.minor < minor) { - return -1; - } - - if (version.release > release) { - return 1; - } else if (version.release < release) { - return -1; - } - - if (version.build > build) { - return 1; - } else if (version.build < build) { - return -1; - } - - return 0; - } - - /* - private String extractQualifier(String token) { - StringTokenizer st = new StringTokenizer(token, "-"); - if (st.countTokens() == 2) { - return st. - } - } - */ - - // for test only - public static void main(String[] args) { - PluginVersion v = PluginVersion.createVersion("4.0.0.123"); - System.out.println(v.toString()); -// v = PluginVersion.createVersion("4.0.0.123-alpha"); -// System.out.println(v.toString()); - PluginVersion v1 = PluginVersion.createVersion("4.1.0"); - System.out.println(v1.toString()); - PluginVersion v2 = PluginVersion.createVersion("4.0.32"); - System.out.println(v2.toString()); - System.out.println(v1.compareTo(v2)); - } - -} diff --git a/pf4j/src/main/java/org/pf4j/PluginWrapper.java b/pf4j/src/main/java/org/pf4j/PluginWrapper.java deleted file mode 100644 index 46a0da1..0000000 --- a/pf4j/src/main/java/org/pf4j/PluginWrapper.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j; - -/** - * A wrapper over plugin instance. - * - * @author Decebal Suiu - */ -public class PluginWrapper { - - PluginDescriptor descriptor; - String pluginPath; - PluginClassLoader pluginClassLoader; - - public PluginWrapper(PluginDescriptor descriptor) { - this.descriptor = descriptor; - } - - /** - * Returns the plugin descriptor. - */ - public PluginDescriptor getDescriptor() { - return descriptor; - } - - /** - * Returns the path of this plugin relative to plugins directory. - */ - public String getPluginPath() { - return pluginPath; - } - - /** - * Returns the plugin class loader used to load classes and resources - * for this plug-in. The class loader can be used to directly access - * plug-in resources and classes. - */ - public PluginClassLoader getPluginClassLoader() { - return pluginClassLoader; - } - - void setPluginPath(String pluginPath) { - this.pluginPath = pluginPath; - } - - void setPluginClassLoader(PluginClassLoader pluginClassLoader) { - this.pluginClassLoader = pluginClassLoader; - } - -} diff --git a/pf4j/src/main/java/org/pf4j/util/DirectedGraph.java b/pf4j/src/main/java/org/pf4j/util/DirectedGraph.java deleted file mode 100644 index a5fa829..0000000 --- a/pf4j/src/main/java/org/pf4j/util/DirectedGraph.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j.util; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Stack; - -/** - * @author Decebal Suiu - */ -public class DirectedGraph { - - /** - * The implementation here is basically an adjacency list, but instead - * of an array of lists, a Map is used to map each vertex to its list of - * adjacent vertices. - */ - private Map> neighbors = new HashMap>(); - - /** - * Add a vertex to the graph. Nothing happens if vertex is already in graph. - */ - public void addVertex(V vertex) { - if (neighbors.containsKey(vertex)) { - return; - } - neighbors.put(vertex, new ArrayList()); - } - - /** - * True if graph contains vertex. - */ - public boolean containsVertex(V vertex) { - return neighbors.containsKey(vertex); - } - - /** - * Add an edge to the graph; if either vertex does not exist, it's added. - * This implementation allows the creation of multi-edges and self-loops. - */ - public void addEdge(V from, V to) { - this.addVertex(from); - this.addVertex(to); - neighbors.get(from).add(to); - } - - /** - * Remove an edge from the graph. Nothing happens if no such edge. - * @throws IllegalArgumentException if either vertex doesn't exist. - */ - public void remove(V from, V to) { - if (!(this.containsVertex(from) && this.containsVertex(to))) { - throw new IllegalArgumentException("Nonexistent vertex"); - } - neighbors.get(from).remove(to); - } - - /** - * Report (as a Map) the out-degree of each vertex. - */ - public Map outDegree() { - Map result = new HashMap(); - for (V vertex : neighbors.keySet()) { - result.put(vertex, neighbors.get(vertex).size()); - } - - return result; - } - - /** - * Report (as a Map) the in-degree of each vertex. - */ - public Map inDegree() { - Map result = new HashMap(); - for (V vertex : neighbors.keySet()) { - result.put(vertex, 0); // all in-degrees are 0 - } - for (V from : neighbors.keySet()) { - for (V to : neighbors.get(from)) { - result.put(to, result.get(to) + 1); // increment in-degree - } - } - - return result; - } - - /** - * Report (as a List) the topological sort of the vertices; null for no such sort. - */ - public List topologicalSort() { - Map degree = inDegree(); - - // determine all vertices with zero in-degree - Stack zeroVertices = new Stack(); // stack as good as any here - for (V v : degree.keySet()) { - if (degree.get(v) == 0) { - zeroVertices.push(v); - } - } - - // determine the topological order - List result = new ArrayList(); - while (!zeroVertices.isEmpty()) { - V vertex = zeroVertices.pop(); // choose a vertex with zero in-degree - result.add(vertex); // vertex 'v' is next in topological order - // "remove" vertex 'v' by updating its neighbors - for (V neighbor : neighbors.get(vertex)) { - degree.put(neighbor, degree.get(neighbor) - 1); - // remember any vertices that now have zero in-degree - if (degree.get(neighbor) == 0) { - zeroVertices.push(neighbor); - } - } - } - - // check that we have used the entire graph (if not, there was a cycle) - if (result.size() != neighbors.size()) { - return null; - } - - return result; - } - - /** - * Report (as a List) the reverse topological sort of the vertices; null for no such sort. - */ - public List reverseTopologicalSort() { - List list = topologicalSort(); - if (list == null) { - return null; - } - Collections.reverse(list); - - return list; - } - - /** - * True if graph is a dag (directed acyclic graph). - */ - public boolean isDag () { - return topologicalSort() != null; - } - - /** - * String representation of graph. - */ - @Override - public String toString() { - StringBuffer sb = new StringBuffer(); - for (V vertex : neighbors.keySet()) { - sb.append("\n " + vertex + " -> " + neighbors.get(vertex)); - } - - return sb.toString(); - } - -} diff --git a/pf4j/src/main/java/org/pf4j/util/DirectoryFilter.java b/pf4j/src/main/java/org/pf4j/util/DirectoryFilter.java deleted file mode 100644 index c6a236b..0000000 --- a/pf4j/src/main/java/org/pf4j/util/DirectoryFilter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j.util; - -import java.io.File; -import java.io.FileFilter; -import java.io.FilenameFilter; - -/** - * @author Decebal Suiu - */ -public class DirectoryFilter implements FileFilter, FilenameFilter { - - /** - * Accepts any file ending in .jar. The case of the filename is ignored. - */ - public boolean accept(File file) { - return file.isDirectory(); - } - - public boolean accept(File dir, String name) { - return accept(new File(dir, name)); - } - -} diff --git a/pf4j/src/main/java/org/pf4j/util/ExtensionFilter.java b/pf4j/src/main/java/org/pf4j/util/ExtensionFilter.java deleted file mode 100644 index 1252a4e..0000000 --- a/pf4j/src/main/java/org/pf4j/util/ExtensionFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j.util; - -import java.io.File; -import java.io.FilenameFilter; - -/** - * @author Decebal Suiu - */ -public class ExtensionFilter implements FilenameFilter { - - private String extension; - - public ExtensionFilter(String extension) { - this.extension = extension; - } - - /** - * Accepts any file ending in extension. The case of the filename is ignored. - */ - public boolean accept(File file) { - // perform a case insensitive check. - return file.getName().toUpperCase().endsWith(extension.toUpperCase()); - } - - public boolean accept(File dir, String name) { - return accept(new File(dir, name)); - } - -} diff --git a/pf4j/src/main/java/org/pf4j/util/JarFilter.java b/pf4j/src/main/java/org/pf4j/util/JarFilter.java deleted file mode 100644 index 09c322c..0000000 --- a/pf4j/src/main/java/org/pf4j/util/JarFilter.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j.util; - -/** - * File filter that accepts all files ending with .JAR. - * This filter is case insensitive. - * - * @author Decebal Suiu - */ -public class JarFilter extends ExtensionFilter { - - /** - * The extension that this filter will search for. - */ - private static final String JAR_EXTENSION = ".JAR"; - - public JarFilter() { - super(JAR_EXTENSION); - } - -} diff --git a/pf4j/src/main/java/org/pf4j/util/UberClassLoader.java b/pf4j/src/main/java/org/pf4j/util/UberClassLoader.java deleted file mode 100644 index 4ecfe2c..0000000 --- a/pf4j/src/main/java/org/pf4j/util/UberClassLoader.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j.util; - -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * A class loader that has multiple loaders and uses them for loading classes and resources. - * - * @author Decebal Suiu - */ -public class UberClassLoader extends ClassLoader { - - private Set loaders = new HashSet(); - - public void addLoader(ClassLoader loader) { - loaders.add(loader); - } - - @Override - public Class findClass(String name) throws ClassNotFoundException { - for (ClassLoader loader : loaders) { - try { - return loader.loadClass(name); - } catch (ClassNotFoundException e) { - // try next - } - } - - throw new ClassNotFoundException(name); - } - - @Override - public URL findResource(String name) { - for (ClassLoader loader : loaders) { - URL url = loader.getResource(name); - if (url != null) { - return url; - } - } - - return null; - } - - @Override - protected Enumeration findResources(String name) throws IOException { - List resources = new ArrayList(); - for (ClassLoader loader : loaders) { - resources.addAll(Collections.list(loader.getResources(name))); - } - - return Collections.enumeration(resources); - } - -} diff --git a/pf4j/src/main/java/org/pf4j/util/Unzip.java b/pf4j/src/main/java/org/pf4j/util/Unzip.java deleted file mode 100644 index d5e6a04..0000000 --- a/pf4j/src/main/java/org/pf4j/util/Unzip.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j.util; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class extracts the containt of the plugin archive into a directory. - * It's a class for only the internal use. - * - * @author Decebal Suiu - */ -public class Unzip { - - private static final Logger LOG = LoggerFactory.getLogger(Unzip.class); - - /** - * Holds the destination directory. - * File will be unzipped into the destination directory. - */ - private File destination; - - /** - * Holds path to zip file. - */ - private File source; - - public Unzip() { - } - - public Unzip(File source, File destination) { - this.source = source; - this.destination = destination; - } - - public void setSource(File source) { - this.source = source; - } - - public void setDestination(File destination) { - this.destination = destination; - } - - public void extract() throws IOException { - LOG.debug("Extract content of " + source + " to " + destination); - - // delete destination file if exists - removeDirectory(destination); - - ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(source)); - ZipEntry zipEntry = null; - - while ((zipEntry = zipInputStream.getNextEntry()) != null) { - try { - File file = new File(destination, zipEntry.getName()); - - // create intermediary directories - sometimes zip don't add them - File dir = new File(file.getParent()); - dir.mkdirs(); - - if (zipEntry.isDirectory()) { - file.mkdirs(); - } else { - byte[] buffer = new byte[1024]; - int length = 0; - FileOutputStream fos = new FileOutputStream(file); - - while ((length = zipInputStream.read(buffer)) >= 0) { - fos.write(buffer, 0, length); - } - - fos.close(); - } - } catch (FileNotFoundException e) { - LOG.error("File '" + zipEntry.getName() + "' not found"); - } - } - - zipInputStream.close(); - } - - private boolean removeDirectory(File directory) { - if (!directory.exists()) { - return true; - } - - if (!directory.isDirectory()) { - return false; - } - - File[] files = directory.listFiles(); - for (File file : files) { - if (file.isDirectory()) { - removeDirectory(file); - } else { - file.delete(); - } - } - - return directory.delete(); - } - -} diff --git a/pf4j/src/main/java/org/pf4j/util/ZipFilter.java b/pf4j/src/main/java/org/pf4j/util/ZipFilter.java deleted file mode 100644 index 4884e90..0000000 --- a/pf4j/src/main/java/org/pf4j/util/ZipFilter.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2012 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 org.pf4j.util; - -/** - * File filter that accepts all files ending with .ZIP. - * This filter is case insensitive. - * - * @author Decebal Suiu - */ -public class ZipFilter extends ExtensionFilter { - - /** - * The extension that this filter will search for. - */ - private static final String ZIP_EXTENSION = ".ZIP"; - - public ZipFilter() { - super(ZIP_EXTENSION); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java new file mode 100644 index 0000000..66c896c --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java @@ -0,0 +1,80 @@ +/* + * Copyright 2012 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 java.lang.reflect.AnnotatedElement; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.java.sezpoz.Index; +import net.java.sezpoz.IndexItem; + +/** + * Using Sezpoz(http://sezpoz.java.net/) for extensions discovery. + * + * @author Decebal Suiu + */ +public class DefaultExtensionFinder implements ExtensionFinder { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultExtensionFinder.class); + + private volatile List> indices; + private ClassLoader classLoader; + + public DefaultExtensionFinder(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + @Override + public List> find(Class type) { + LOG.debug("Find extensions for " + type); + List> result = new ArrayList>(); + getIndices(); +// System.out.println("indices = "+ indices); + for (IndexItem item : indices) { + try { + AnnotatedElement element = item.element(); + Class extensionType = (Class) element; + LOG.debug("Checking extension type " + extensionType); + if (type.isAssignableFrom(extensionType)) { + Object instance = item.instance(); + if (instance != null) { + LOG.debug("Added extension " + extensionType); + result.add(new ExtensionWrapper(type.cast(instance), item.annotation().ordinal())); + } + } + } catch (InstantiationException e) { + LOG.error(e.getMessage(), e); + } + } + + return result; + } + + private List> getIndices() { + if (indices == null) { + indices = new ArrayList>(); + Iterator> it = Index.load(Extension.class, Object.class, classLoader).iterator(); + while (it.hasNext()) { + indices.add(it.next()); + } + } + + return indices; + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginDescriptorFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginDescriptorFinder.java new file mode 100644 index 0000000..fa86e2e --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginDescriptorFinder.java @@ -0,0 +1,90 @@ +/* + * Copyright 2012 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 java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import org.apache.commons.lang.StringUtils; + +/** + * Read the plugin descriptor from the manifest file. + * + * @author Decebal Suiu + */ +public class DefaultPluginDescriptorFinder implements PluginDescriptorFinder { + + @Override + public PluginDescriptor find(File pluginRepository) throws PluginException { + // TODO it's ok with classes/ ? + File manifestFile = new File(pluginRepository, "classes/META-INF/MANIFEST.MF"); + if (!manifestFile.exists()) { + // not found a 'plugin.xml' file for this plugin + throw new PluginException("Cannot find '" + manifestFile + "' file"); + } + + FileInputStream input = null; + try { + input = new FileInputStream(manifestFile); + } catch (FileNotFoundException e) { + // not happening + } + + Manifest manifest = null; + try { + manifest = new Manifest(input); + } catch (IOException e) { + throw new PluginException(e.getMessage(), e); + } finally { + try { + input.close(); + } catch (IOException e) { + throw new PluginException(e.getMessage(), e); + } + } + + PluginDescriptor pluginDescriptor = new PluginDescriptor(); + + // TODO validate !!! + Attributes attrs = manifest.getMainAttributes(); + String id = attrs.getValue("Plugin-Id"); + if (StringUtils.isEmpty(id)) { + throw new PluginException("Plugin-Id cannot be empty"); + } + pluginDescriptor.setPluginId(id); + + String clazz = attrs.getValue("Plugin-Class"); + if (StringUtils.isEmpty(clazz)) { + throw new PluginException("Plugin-Class cannot be empty"); + } + pluginDescriptor.setPluginClass(clazz); + + String version = attrs.getValue("Plugin-Version"); + if (StringUtils.isEmpty(version)) { + throw new PluginException("Plugin-Version cannot be empty"); + } + pluginDescriptor.setPluginVersion(PluginVersion.createVersion(version)); + + String provider = attrs.getValue("Plugin-Provider"); + pluginDescriptor.setProvider(provider); + String dependencies = attrs.getValue("Plugin-Dependencies"); + pluginDescriptor.setDependencies(dependencies); + + return pluginDescriptor; + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java new file mode 100644 index 0000000..ea56e67 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java @@ -0,0 +1,328 @@ +/* + * Copyright 2012 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 java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ro.fortsoft.pf4j.util.DirectoryFilter; +import ro.fortsoft.pf4j.util.UberClassLoader; +import ro.fortsoft.pf4j.util.Unzip; +import ro.fortsoft.pf4j.util.ZipFilter; + + +/** + * Default implementation of the PluginManager interface. + * + * @author Decebal Suiu + */ +public class DefaultPluginManager implements PluginManager { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultPluginManager.class); + + /** + * The plugins repository. + */ + private File pluginsDirectory; + + private ExtensionFinder extensionFinder; + + private PluginDescriptorFinder pluginDescriptorFinder; + + /** + * A map of plugins this manager is responsible for (the key is the 'pluginId'). + */ + private Map plugins; + + /** + * A map of plugin class loaders (he key is the 'pluginId'). + */ + private Map pluginClassLoaders; + + /** + * A relation between 'pluginPath' and 'pluginId' + */ + private Map pathToIdMap; + + /** + * A list with unresolved plugins (unresolved dependency). + */ + private List unresolvedPlugins; + + /** + * A list with resolved plugins (resolved dependency). + */ + private List resolvedPlugins; + + /** + * A list with disabled plugins. + */ + private List disabledPlugins; + + private UberClassLoader uberClassLoader; + + /** + * Th plugins directory is supplied by System.getProperty("pf4j.pluginsDir", "plugins"). + */ + public DefaultPluginManager() { + this(new File(System.getProperty("pf4j.pluginsDir", "plugins"))); + } + + /** + * Constructs DefaultPluginManager which the given plugins directory. + * + * @param pluginsDirectory + * the directory to search for plugins + */ + public DefaultPluginManager(File pluginsDirectory) { + this.pluginsDirectory = pluginsDirectory; + plugins = new HashMap(); + pluginClassLoaders = new HashMap(); + pathToIdMap = new HashMap(); + unresolvedPlugins = new ArrayList(); + resolvedPlugins = new ArrayList(); + disabledPlugins = new ArrayList(); + pluginDescriptorFinder = new DefaultPluginDescriptorFinder(); + uberClassLoader = new UberClassLoader(); + extensionFinder = new DefaultExtensionFinder(uberClassLoader); + } + + /** + * Retrieves all active plugins. + */ + public List getPlugins() { + return new ArrayList(plugins.values()); + } + + public List getResolvedPlugins() { + return resolvedPlugins; + } + + public Plugin getPlugin(String pluginId) { + return plugins.get(pluginId); + } + + public List getUnresolvedPlugins() { + return unresolvedPlugins; + } + + public List getDisabledPlugins() { + return disabledPlugins; + } + + /** + * Start all active plugins. + */ + public void startPlugins() { + List resolvedPlugins = getResolvedPlugins(); + for (Plugin plugin : resolvedPlugins) { + try { + plugin.start(); + } catch (PluginException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + /** + * Stop all active plugins. + */ + public void stopPlugins() { + List resolvedPlugins = getResolvedPlugins(); + for (Plugin plugin : resolvedPlugins) { + try { + plugin.stop(); + } catch (PluginException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + /** + * Load plugins. + */ + public void loadPlugins() { + // check for plugins directory + if (!pluginsDirectory.exists() || !pluginsDirectory.isDirectory()) { + LOG.error("No '" + pluginsDirectory + "' directory"); + return; + } + + // expand all plugin archives + FilenameFilter zipFilter = new ZipFilter(); + String[] zipFiles = pluginsDirectory.list(zipFilter); + for (String zipFile : zipFiles) { + try { + expandPluginArchive(zipFile); + } catch (IOException e) { + LOG.error(e.getMessage(), e); + e.printStackTrace(); + } + } + + // load any plugin from plugins directory + FilenameFilter directoryFilter = new DirectoryFilter(); + String[] directories = pluginsDirectory.list(directoryFilter); + for (String directory : directories) { + try { + loadPlugin(directory); + } catch (Exception e) { + LOG.error(e.getMessage(), e); + e.printStackTrace(); + } + } + + // check for no plugins + if (directories.length == 0) { + LOG.info("No plugins"); + return; + } + + // resolve 'unresolvedPlugins' + resolvePlugins(); + } + + /** + * Get plugin class loader for this path. + */ + public PluginClassLoader getPluginClassLoader(String pluginId) { + return pluginClassLoaders.get(pluginId); + } + + public List> getExtensions(Class type) { + return extensionFinder.find(type); + } + + private void loadPlugin(String fileName) throws Exception { + // test for plugin directory + File pluginDirectory = new File(pluginsDirectory, fileName); + if (!pluginDirectory.isDirectory()) { + return; + } + + // try to load the plugin + String pluginPath = "/".concat(fileName); + + // test for disabled plugin + if (disabledPlugins.contains(pluginPath)) { + return; + } + + // it's a new plugin + if (plugins.get(pathToIdMap.get(pluginPath)) != null) { + return; + } + + // retrieves the plugin descriptor + LOG.debug("Find plugin descriptor '" + pluginPath + "'"); + PluginDescriptor pluginDescriptor = pluginDescriptorFinder.find(pluginDirectory); + LOG.debug("Descriptor " + pluginDescriptor); + String pluginClassName = pluginDescriptor.getPluginClass(); + LOG.debug("Class '" + pluginClassName + "'" + " for plugin '" + pluginPath + "'"); + + // load plugin + LOG.debug("Loading plugin '" + pluginPath + "'"); + PluginWrapper pluginWrapper = new PluginWrapper(pluginDescriptor); + PluginLoader pluginLoader = new PluginLoader(this, pluginWrapper, pluginDirectory); + pluginLoader.load(); + LOG.debug("Loaded plugin '" + pluginPath + "'"); + + // set some variables in plugin wrapper + pluginWrapper.setPluginPath(pluginPath); + pluginWrapper.setPluginClassLoader(pluginLoader.getPluginClassLoader()); + + // create the plugin instance + LOG.debug("Creating instance for plugin '" + pluginPath + "'"); + Plugin plugin = getPluginInstance(pluginWrapper, pluginLoader); + LOG.debug("Created instance '" + plugin + "' for plugin '" + pluginPath + "'"); + + String pluginId = pluginDescriptor.getPluginId(); + + // add plugin to the list with plugins + plugins.put(pluginId, plugin); + unresolvedPlugins.add(plugin); + + // add plugin class loader to the list with class loaders + PluginClassLoader pluginClassLoader = pluginLoader.getPluginClassLoader(); + pluginDescriptor.setPluginClassLoader(pluginClassLoader); + pluginClassLoaders.put(pluginId, pluginClassLoader); + } + + private void expandPluginArchive(String fileName) throws IOException { + File pluginArchiveFile = new File(pluginsDirectory, fileName); + long pluginArchiveDate = pluginArchiveFile.lastModified(); + String pluginName = fileName.substring(0, fileName.length() - 4); + File pluginDirectory = new File(pluginsDirectory, pluginName); + // check if exists directory or the '.zip' file is "newer" than directory + if (!pluginDirectory.exists() || pluginArchiveDate > pluginDirectory.lastModified()) { + LOG.debug("Expand plugin archive '" + pluginArchiveFile + "' in '" + pluginDirectory + "'"); + // create directorie for plugin + pluginDirectory.mkdirs(); + + // expand '.zip' file + Unzip unzip = new Unzip(); + unzip.setSource(pluginArchiveFile); + unzip.setDestination(pluginDirectory); + unzip.extract(); + } + } + + private Plugin getPluginInstance(PluginWrapper pluginWrapper, PluginLoader pluginLoader) + throws Exception { + String pluginClassName = pluginWrapper.getDescriptor().getPluginClass(); + + ClassLoader pluginClassLoader = pluginLoader.getPluginClassLoader(); + 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 compatible."); + } + + // create the plugin instance + Constructor constructor = pluginClass.getConstructor(new Class[] { PluginWrapper.class }); + Plugin plugin = (Plugin) constructor.newInstance(new Object[] { pluginWrapper }); + + return plugin; + } + + private void resolvePlugins() { + resolveDependencies(); + } + + private void resolveDependencies() { + DependencyResolver dependencyResolver = new DependencyResolver(unresolvedPlugins); + resolvedPlugins = dependencyResolver.getSortedDependencies(); + for (Plugin plugin : resolvedPlugins) { + unresolvedPlugins.remove(plugin); + uberClassLoader.addLoader(plugin.getWrapper().getPluginClassLoader()); + } + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java new file mode 100644 index 0000000..7485b22 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java @@ -0,0 +1,82 @@ +/* + * Copyright 2012 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 java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ro.fortsoft.pf4j.util.DirectedGraph; + + +/** + * @author Decebal Suiu + */ +class DependencyResolver { + + private static final Logger LOG = LoggerFactory.getLogger(DependencyResolver.class); + + private List plugins; + + public DependencyResolver(List plugins) { + this.plugins = plugins; + } + + /** + * Get the list of plugins in dependency sorted order. + */ + public List getSortedDependencies() { + DirectedGraph graph = new DirectedGraph(); + for (Plugin plugin : plugins) { + PluginDescriptor descriptor = plugin.getWrapper().getDescriptor(); + String pluginId = descriptor.getPluginId(); + List dependencies = descriptor.getDependencies(); + if (!dependencies.isEmpty()) { + for (String dependency : dependencies) { + graph.addEdge(pluginId, dependency); + } + } else { + graph.addVertex(pluginId); + } + } + + LOG.debug("Graph: " + graph); + List pluginsId = graph.reverseTopologicalSort(); + + if (pluginsId == null) { + LOG.error("Cyclic dependences !!!"); + return null; + } + + LOG.debug("Plugins order: " + pluginsId); + List sortedPlugins = new ArrayList(); + for (String pluginId : pluginsId) { + sortedPlugins.add(getPlugin(pluginId)); + } + + return sortedPlugins; + } + + private Plugin getPlugin(String pluginId) { + for (Plugin plugin : plugins) { + if (pluginId.equals(plugin.getWrapper().getDescriptor().getPluginId())) { + return plugin; + } + } + + return null; + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/Extension.java b/pf4j/src/main/java/ro/fortsoft/pf4j/Extension.java new file mode 100644 index 0000000..f1b7cac --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/Extension.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012 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 static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import net.java.sezpoz.Indexable; + +/** + * @author Decebal Suiu + */ +@Indexable +@Retention(RUNTIME) +@Target(TYPE) +@Documented +public @interface Extension { + + int ordinal() default 0; + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFinder.java new file mode 100644 index 0000000..2eeca18 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFinder.java @@ -0,0 +1,24 @@ +/* + * Copyright 2012 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 java.util.List; + +/** + * @author Decebal Suiu + */ +public interface ExtensionFinder { + + public List> find(Class type); + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionPoint.java b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionPoint.java new file mode 100644 index 0000000..8d94847 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionPoint.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012 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 interface ExtensionPoint { + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionWrapper.java b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionWrapper.java new file mode 100644 index 0000000..2f147c0 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionWrapper.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012 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 ExtensionWrapper implements Comparable> { + + private final T instance; + private final int ordinal; + + public ExtensionWrapper(T instance, int ordinal) { + this.instance = instance; + this.ordinal = ordinal; + } + + public T getInstance() { + return instance; + } + + public int getOrdinal() { + return ordinal; + } + + @Override + public int compareTo(ExtensionWrapper o) { + return (ordinal - o.getOrdinal()); + } + +} \ No newline at end of file diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/Plugin.java b/pf4j/src/main/java/ro/fortsoft/pf4j/Plugin.java new file mode 100644 index 0000000..22498d7 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/Plugin.java @@ -0,0 +1,68 @@ +/* + * Copyright 2012 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; + +/** + * This class will be extended by all plugins and + * serve as the common class between a plugin and the application. + * + * @author Decebal Suiu + */ +public abstract class Plugin { + + /** + * Makes logging service available for descending classes. + */ + protected final Logger log = LoggerFactory.getLogger(getClass()); + + /** + * Wrapper of the plugin. + */ + PluginWrapper wrapper; + + /** + * Constructor to be used by plugin manager for plugin instantiation. + * Your plugins have to provide constructor with this exact signature to + * be successfully loaded by manager. + */ + public Plugin(final PluginWrapper wrapper) { + if (wrapper == null) { + throw new IllegalArgumentException("Wrapper cannot be null"); + } + + this.wrapper = wrapper; + } + + /** + * Retrieves the wrapper of this plug-in. + */ + public final PluginWrapper getWrapper() { + return wrapper; + } + + /** + * Start method is called by the application when the plugin is loaded. + */ + public void start() throws PluginException { + } + + /** + * Stop method is called by the application when the plugin is unloaded. + */ + public void stop() throws PluginException { + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClassLoader.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClassLoader.java new file mode 100644 index 0000000..1d80c92 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClassLoader.java @@ -0,0 +1,91 @@ +/* + * Copyright 2012 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 java.net.URL; +import java.net.URLClassLoader; +import java.util.List; + +/** + * One instance of this class should be created by plugin manager for every available plug-in. + * + * @author Decebal Suiu + */ +class PluginClassLoader extends URLClassLoader { + + private static final String JAVA_PACKAGE_PREFIX = "java."; + private static final String JAVAX_PACKAGE_PREFIX = "javax."; + private static final String PLUGIN_PACKAGE_PREFIX = "ro.fortsoft.pf4j."; + + private PluginManager pluginManager; + private PluginWrapper pluginWrapper; + + public PluginClassLoader(PluginManager pluginManager, PluginWrapper pluginWrapper, ClassLoader parent) { + super(new URL[0], parent); + + this.pluginManager = pluginManager; + this.pluginWrapper = pluginWrapper; + } + + @Override + public void addURL(URL url) { + super.addURL(url); + } + + @Override + public Class loadClass(String className) throws ClassNotFoundException { +// System.out.println(">>>" + className); + + // first check whether it's a system class, delegate to the system loader + if (className.startsWith(JAVA_PACKAGE_PREFIX) || className.startsWith(JAVAX_PACKAGE_PREFIX)) { + return findSystemClass(className); + } + + // second check whether it's already been loaded + Class loadedClass = findLoadedClass(className); + if (loadedClass != null) { + return loadedClass; + } + + // nope, try to load locally + try { + return findClass(className); + } catch (ClassNotFoundException e) { + // try next step + } + + // if the class it's a part of the plugin engine use parent class loader + if (className.startsWith(PLUGIN_PACKAGE_PREFIX)) { + try { + return PluginClassLoader.class.getClassLoader().loadClass(className); + } catch (ClassNotFoundException e) { + // try next step + } + } + + // look in dependencies + List dependencies = pluginWrapper.getDescriptor().getDependencies(); + for (String dependency : dependencies) { + PluginClassLoader classLoader = pluginManager.getPluginClassLoader(dependency); + try { + return classLoader.loadClass(className); + } catch (ClassNotFoundException e) { + // try next dependency + } + } + + // use the standard URLClassLoader (which follows normal parent delegation) + return super.loadClass(className); + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptor.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptor.java new file mode 100644 index 0000000..494877e --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptor.java @@ -0,0 +1,140 @@ +/* + * Copyright 2012 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 java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; + +/** + * A plugin descriptor contains information about a plug-in obtained + * from the manifest (META-INF) file. + * + * @author Decebal Suiu + */ +class PluginDescriptor { + + private String pluginId; + private String pluginClass; + private PluginVersion version; + private String provider; + private String pluginPath; + private List dependencies; + private PluginClassLoader pluginClassLoader; + + public PluginDescriptor() { + dependencies = new ArrayList(); + } + + /** + * Returns the unique identifier of this plugin. + */ + public String getPluginId() { + return pluginId; + } + + /** + * Returns the name of the class that implements Plugin interface. + */ + public String getPluginClass() { + return pluginClass; + } + + /** + * Returns the version of this plugin. + */ + public PluginVersion getVersion() { + return version; + } + + /** + * Returns the provider name of this plugin. + */ + public String getProvider() { + return provider; + } + + /** + * Returns the path of this plugin relative to plugins directory. + */ + public String getPluginPath() { + return pluginPath; + } + + /** + * Returns all dependencies declared by this plugin. + * Returns an empty array if this plugin does not declare any require. + */ + public List getDependencies() { + return dependencies; + } + + /** + * Returns the plugin class loader used to load classes and resources + * for this plug-in. The class loader can be used to directly access + * plug-in resources and classes. + */ + public PluginClassLoader getPluginClassLoader() { + return pluginClassLoader; + } + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .append("pluginId", pluginId) + .append("pluginClass", pluginClass) + .append("version", version) + .append("provider", provider) + .append("pluginPath", pluginPath) + .append("dependencies", dependencies) + .toString(); + } + + void setPluginId(String pluginId) { + this.pluginId = pluginId; + } + + void setPluginClass(String pluginClassName) { + this.pluginClass = pluginClassName; + } + + void setPluginVersion(PluginVersion version) { + this.version = version; + } + + void setProvider(String provider) { + this.provider = provider; + } + + void setPluginPath(String pluginPath) { + this.pluginPath = pluginPath; + } + + void setDependencies(String dependencies) { + if (dependencies != null) { + this.dependencies = Arrays.asList(StringUtils.split(dependencies, ',')); + } else { + this.dependencies = Collections.emptyList(); + } + } + + void setPluginClassLoader(PluginClassLoader pluginClassLoader) { + this.pluginClassLoader = pluginClassLoader; + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptorFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptorFinder.java new file mode 100644 index 0000000..44b0846 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptorFinder.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012 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 java.io.File; + +/** + * Find a plugin descriptor in a directory (plugin repository). + * You can find in manifest file @see DefaultPluginDescriptorFinder, + * xml file, properties file, java services (with ServiceLoader), etc. + * + * @author Decebal Suiu + */ +public interface PluginDescriptorFinder { + + public PluginDescriptor find(File pluginRepository) throws PluginException; + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginException.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginException.java new file mode 100644 index 0000000..600e28f --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginException.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012 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; + +/** + * An exception used to indicate that a plugin problem occurred. + * + * @author Decebal Suiu + */ +class PluginException extends Exception { + + private static final long serialVersionUID = 1L; + + public PluginException() { + super(); + } + + public PluginException(String message) { + super(message); + } + + public PluginException(Throwable cause) { + super(cause); + } + + public PluginException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java new file mode 100644 index 0000000..8ae65e7 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java @@ -0,0 +1,143 @@ +/* + * Copyright 2012 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 java.io.File; +import java.io.FilenameFilter; +import java.net.MalformedURLException; +import java.util.Vector; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ro.fortsoft.pf4j.util.DirectoryFilter; +import ro.fortsoft.pf4j.util.JarFilter; + + +/** + * Load all informations needed by a plugin. + * This means add all jar files from 'lib' directory, 'classes' + * to classpath. + * It's a class for only the internal use. + * + * @author Decebal Suiu + */ +class PluginLoader { + + private static final Logger LOG = LoggerFactory.getLogger(PluginLoader.class); + + /* + * The plugin repository. + */ + private File pluginRepository; + + /* + * The directory with '.class' files. + */ + private File classesDirectory; + + /* + * The directory with '.jar' files. + */ + private File libDirectory; + + private PluginClassLoader pluginClassLoader; + + public PluginLoader(PluginManager pluginManager, PluginWrapper pluginWrapper, File pluginRepository) { + this.pluginRepository = pluginRepository; + classesDirectory = new File(pluginRepository, "classes"); + libDirectory = new File(pluginRepository, "lib"); + ClassLoader parent = getClass().getClassLoader(); + pluginClassLoader = new PluginClassLoader(pluginManager, pluginWrapper, parent); + LOG.debug("Created class loader " + pluginClassLoader); + } + + public File getPluginRepository() { + return pluginRepository; + } + + public boolean load() { + return loadClassesAndJars(); + } + + public PluginClassLoader getPluginClassLoader() { + return pluginClassLoader; + } + + private boolean loadClassesAndJars() { + return loadClasses() && loadJars(); + } + + private void getJars(Vector v, File file) { + FilenameFilter jarFilter = new JarFilter(); + FilenameFilter directoryFilter = new DirectoryFilter(); + + if (file.exists() && file.isDirectory() && file.isAbsolute()) { + String[] jars = file.list(jarFilter); + for (int i = 0; (jars != null) && (i < jars.length); ++i) { + v.addElement(jars[i]); + } + + String[] directoryList = file.list(directoryFilter); + for (int i = 0; (directoryList != null) && (i < directoryList.length); ++i) { + File directory = new File(file, directoryList[i]); + getJars(v, directory); + } + } + } + + private boolean loadClasses() { + // make 'classesDirectory' absolute + classesDirectory = classesDirectory.getAbsoluteFile(); + + if (classesDirectory.exists() && classesDirectory.isDirectory()) { + LOG.debug("Found '" + classesDirectory.getPath() + "' directory"); + + try { + pluginClassLoader.addURL(classesDirectory.toURI().toURL()); + LOG.debug("Added '" + classesDirectory + "' to the class loader path"); + } catch (MalformedURLException e) { + e.printStackTrace(); + LOG.error(e.getMessage(), e); + return false; + } + } + + return true; + } + + /** + * Add all *.jar files from '/lib' directory. + */ + private boolean loadJars() { + // make 'jarDirectory' absolute + libDirectory = libDirectory.getAbsoluteFile(); + + Vector jars = new Vector(); + getJars(jars, libDirectory); + for (String jar : jars) { + File jarFile = new File(libDirectory, jar); + try { + pluginClassLoader.addURL(jarFile.toURI().toURL()); + LOG.debug("Added '" + jarFile + "' to the class loader path"); + } catch (MalformedURLException e) { + e.printStackTrace(); + LOG.error(e.getMessage(), e); + return false; + } + } + + return true; + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java new file mode 100644 index 0000000..e191e22 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java @@ -0,0 +1,49 @@ +/* + * Copyright 2012 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 java.util.List; + +/** + * Provides the functionality for plugin management such as load, + * start and stop the plugins. + * + * @author Decebal Suiu + */ +public interface PluginManager { + + /** + * Retrieves all plugins. + */ + public List getPlugins(); + + /** + * Load plugins. + */ + public void loadPlugins(); + + /** + * Start all active plugins. + */ + public void startPlugins(); + + /** + * Stop all active plugins. + */ + public void stopPlugins(); + + public PluginClassLoader getPluginClassLoader(String pluginId); + + public List> getExtensions(Class type); + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginVersion.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginVersion.java new file mode 100644 index 0000000..9fcab40 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginVersion.java @@ -0,0 +1,191 @@ +/* + * Copyright 2012 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 java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +/** + * Represents the version of a Plugin and allows versions to be compared. + * Version identifiers have five components. + * + * 1. Major version. A non-negative integer. + * 2. Minor version. A non-negative integer. + * 3. Release version. A non-negative integer. + * 4. Build version. A non-negative integer. + * 5. Qualifier. A text string. + * + * This class is immutable. + * + * @author Decebal Suiu + */ +public class PluginVersion implements Comparable { + + private int major; + private int minor; + private int release; + private int build; + private String qualifier; + + private PluginVersion() { + } + + public PluginVersion(int major, int minor, int release) { + this.major = major; + this.minor = minor; + this.release = release; + } + + public PluginVersion(int major, int minor, int release, int build) { + this.major = major; + this.minor = minor; + this.release = release; + this.build = build; + } + + public PluginVersion(int major, int minor, int release, int build, String qualifier) { + this.major = major; + this.minor = minor; + this.release = release; + this.build = build; + this.qualifier = qualifier; + } + + public static PluginVersion createVersion(String version) { + if (version == null) { + return new PluginVersion(); + } + + PluginVersion v = new PluginVersion(); + + StringTokenizer st = new StringTokenizer(version, "."); + List tmp = new ArrayList(); + for (int i = 0; st.hasMoreTokens() && i < 4; i++) { + tmp.add(st.nextToken()); + } + + int n = tmp.size(); + switch (n) { + case 0 : + break; + case 1 : + v.major = Integer.parseInt(tmp.get(0)); + break; + case 2 : + v.major = Integer.parseInt(tmp.get(0)); + v.minor = Integer.parseInt(tmp.get(1)); + break; + case 3 : + v.major = Integer.parseInt(tmp.get(0)); + v.minor = Integer.parseInt(tmp.get(1)); + v.release = Integer.parseInt(tmp.get(2)); + break; + case 4 : + v.major = Integer.parseInt(tmp.get(0)); + v.minor = Integer.parseInt(tmp.get(1)); + v.release = Integer.parseInt(tmp.get(2)); + v.build = Integer.parseInt(tmp.get(3)); + break; + } + + return v; + } + + public int getMajor() { + return this.major; + } + + public int getMinor() { + return this.minor; + } + + public int getRelease() { + return this.release; + } + + public int getBuild() { + return this.build; + } + + public String getQualifier() { + return qualifier; + } + + public String toString() { + StringBuffer sb = new StringBuffer(50); + sb.append(major); + sb.append('.'); + sb.append(minor); + sb.append('.'); + sb.append(release); + sb.append('.'); + sb.append(build); + if (qualifier != null) { + sb.append(qualifier); + } + + return sb.toString(); + } + + public int compareTo(PluginVersion version) { + if (version.major > major) { + return 1; + } else if (version.major < major) { + return -1; + } + + if (version.minor > minor) { + return 1; + } else if (version.minor < minor) { + return -1; + } + + if (version.release > release) { + return 1; + } else if (version.release < release) { + return -1; + } + + if (version.build > build) { + return 1; + } else if (version.build < build) { + return -1; + } + + return 0; + } + + /* + private String extractQualifier(String token) { + StringTokenizer st = new StringTokenizer(token, "-"); + if (st.countTokens() == 2) { + return st. + } + } + */ + + // for test only + public static void main(String[] args) { + PluginVersion v = PluginVersion.createVersion("4.0.0.123"); + System.out.println(v.toString()); +// v = PluginVersion.createVersion("4.0.0.123-alpha"); +// System.out.println(v.toString()); + PluginVersion v1 = PluginVersion.createVersion("4.1.0"); + System.out.println(v1.toString()); + PluginVersion v2 = PluginVersion.createVersion("4.0.32"); + System.out.println(v2.toString()); + System.out.println(v1.compareTo(v2)); + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java new file mode 100644 index 0000000..6d935e0 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java @@ -0,0 +1,61 @@ +/* + * Copyright 2012 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; + +/** + * A wrapper over plugin instance. + * + * @author Decebal Suiu + */ +public class PluginWrapper { + + PluginDescriptor descriptor; + String pluginPath; + PluginClassLoader pluginClassLoader; + + public PluginWrapper(PluginDescriptor descriptor) { + this.descriptor = descriptor; + } + + /** + * Returns the plugin descriptor. + */ + public PluginDescriptor getDescriptor() { + return descriptor; + } + + /** + * Returns the path of this plugin relative to plugins directory. + */ + public String getPluginPath() { + return pluginPath; + } + + /** + * Returns the plugin class loader used to load classes and resources + * for this plug-in. The class loader can be used to directly access + * plug-in resources and classes. + */ + public PluginClassLoader getPluginClassLoader() { + return pluginClassLoader; + } + + void setPluginPath(String pluginPath) { + this.pluginPath = pluginPath; + } + + void setPluginClassLoader(PluginClassLoader pluginClassLoader) { + this.pluginClassLoader = pluginClassLoader; + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectedGraph.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectedGraph.java new file mode 100644 index 0000000..5c1f446 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectedGraph.java @@ -0,0 +1,171 @@ +/* + * Copyright 2012 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.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +/** + * @author Decebal Suiu + */ +public class DirectedGraph { + + /** + * The implementation here is basically an adjacency list, but instead + * of an array of lists, a Map is used to map each vertex to its list of + * adjacent vertices. + */ + private Map> neighbors = new HashMap>(); + + /** + * Add a vertex to the graph. Nothing happens if vertex is already in graph. + */ + public void addVertex(V vertex) { + if (neighbors.containsKey(vertex)) { + return; + } + neighbors.put(vertex, new ArrayList()); + } + + /** + * True if graph contains vertex. + */ + public boolean containsVertex(V vertex) { + return neighbors.containsKey(vertex); + } + + /** + * Add an edge to the graph; if either vertex does not exist, it's added. + * This implementation allows the creation of multi-edges and self-loops. + */ + public void addEdge(V from, V to) { + this.addVertex(from); + this.addVertex(to); + neighbors.get(from).add(to); + } + + /** + * Remove an edge from the graph. Nothing happens if no such edge. + * @throws IllegalArgumentException if either vertex doesn't exist. + */ + public void remove(V from, V to) { + if (!(this.containsVertex(from) && this.containsVertex(to))) { + throw new IllegalArgumentException("Nonexistent vertex"); + } + neighbors.get(from).remove(to); + } + + /** + * Report (as a Map) the out-degree of each vertex. + */ + public Map outDegree() { + Map result = new HashMap(); + for (V vertex : neighbors.keySet()) { + result.put(vertex, neighbors.get(vertex).size()); + } + + return result; + } + + /** + * Report (as a Map) the in-degree of each vertex. + */ + public Map inDegree() { + Map result = new HashMap(); + for (V vertex : neighbors.keySet()) { + result.put(vertex, 0); // all in-degrees are 0 + } + for (V from : neighbors.keySet()) { + for (V to : neighbors.get(from)) { + result.put(to, result.get(to) + 1); // increment in-degree + } + } + + return result; + } + + /** + * Report (as a List) the topological sort of the vertices; null for no such sort. + */ + public List topologicalSort() { + Map degree = inDegree(); + + // determine all vertices with zero in-degree + Stack zeroVertices = new Stack(); // stack as good as any here + for (V v : degree.keySet()) { + if (degree.get(v) == 0) { + zeroVertices.push(v); + } + } + + // determine the topological order + List result = new ArrayList(); + while (!zeroVertices.isEmpty()) { + V vertex = zeroVertices.pop(); // choose a vertex with zero in-degree + result.add(vertex); // vertex 'v' is next in topological order + // "remove" vertex 'v' by updating its neighbors + for (V neighbor : neighbors.get(vertex)) { + degree.put(neighbor, degree.get(neighbor) - 1); + // remember any vertices that now have zero in-degree + if (degree.get(neighbor) == 0) { + zeroVertices.push(neighbor); + } + } + } + + // check that we have used the entire graph (if not, there was a cycle) + if (result.size() != neighbors.size()) { + return null; + } + + return result; + } + + /** + * Report (as a List) the reverse topological sort of the vertices; null for no such sort. + */ + public List reverseTopologicalSort() { + List list = topologicalSort(); + if (list == null) { + return null; + } + Collections.reverse(list); + + return list; + } + + /** + * True if graph is a dag (directed acyclic graph). + */ + public boolean isDag () { + return topologicalSort() != null; + } + + /** + * String representation of graph. + */ + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + for (V vertex : neighbors.keySet()) { + sb.append("\n " + vertex + " -> " + neighbors.get(vertex)); + } + + return sb.toString(); + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectoryFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectoryFilter.java new file mode 100644 index 0000000..54e1a95 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectoryFilter.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012 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.util; + +import java.io.File; +import java.io.FileFilter; +import java.io.FilenameFilter; + +/** + * @author Decebal Suiu + */ +public class DirectoryFilter implements FileFilter, FilenameFilter { + + /** + * Accepts any file ending in .jar. The case of the filename is ignored. + */ + public boolean accept(File file) { + return file.isDirectory(); + } + + public boolean accept(File dir, String name) { + return accept(new File(dir, name)); + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/ExtensionFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/ExtensionFilter.java new file mode 100644 index 0000000..22b999f --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/ExtensionFilter.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012 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.util; + +import java.io.File; +import java.io.FilenameFilter; + +/** + * @author Decebal Suiu + */ +public class ExtensionFilter implements FilenameFilter { + + private String extension; + + public ExtensionFilter(String extension) { + this.extension = extension; + } + + /** + * Accepts any file ending in extension. The case of the filename is ignored. + */ + public boolean accept(File file) { + // perform a case insensitive check. + return file.getName().toUpperCase().endsWith(extension.toUpperCase()); + } + + public boolean accept(File dir, String name) { + return accept(new File(dir, name)); + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/JarFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/JarFilter.java new file mode 100644 index 0000000..60c8859 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/JarFilter.java @@ -0,0 +1,32 @@ +/* + * Copyright 2012 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.util; + +/** + * File filter that accepts all files ending with .JAR. + * This filter is case insensitive. + * + * @author Decebal Suiu + */ +public class JarFilter extends ExtensionFilter { + + /** + * The extension that this filter will search for. + */ + private static final String JAR_EXTENSION = ".JAR"; + + public JarFilter() { + super(JAR_EXTENSION); + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/UberClassLoader.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/UberClassLoader.java new file mode 100644 index 0000000..013c2a1 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/UberClassLoader.java @@ -0,0 +1,72 @@ +/* + * Copyright 2012 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.util; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * A class loader that has multiple loaders and uses them for loading classes and resources. + * + * @author Decebal Suiu + */ +public class UberClassLoader extends ClassLoader { + + private Set loaders = new HashSet(); + + public void addLoader(ClassLoader loader) { + loaders.add(loader); + } + + @Override + public Class findClass(String name) throws ClassNotFoundException { + for (ClassLoader loader : loaders) { + try { + return loader.loadClass(name); + } catch (ClassNotFoundException e) { + // try next + } + } + + throw new ClassNotFoundException(name); + } + + @Override + public URL findResource(String name) { + for (ClassLoader loader : loaders) { + URL url = loader.getResource(name); + if (url != null) { + return url; + } + } + + return null; + } + + @Override + protected Enumeration findResources(String name) throws IOException { + List resources = new ArrayList(); + for (ClassLoader loader : loaders) { + resources.addAll(Collections.list(loader.getResources(name))); + } + + return Collections.enumeration(resources); + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/Unzip.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/Unzip.java new file mode 100644 index 0000000..4a2c9a3 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/Unzip.java @@ -0,0 +1,122 @@ +/* + * Copyright 2012 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.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class extracts the containt of the plugin archive into a directory. + * It's a class for only the internal use. + * + * @author Decebal Suiu + */ +public class Unzip { + + private static final Logger LOG = LoggerFactory.getLogger(Unzip.class); + + /** + * Holds the destination directory. + * File will be unzipped into the destination directory. + */ + private File destination; + + /** + * Holds path to zip file. + */ + private File source; + + public Unzip() { + } + + public Unzip(File source, File destination) { + this.source = source; + this.destination = destination; + } + + public void setSource(File source) { + this.source = source; + } + + public void setDestination(File destination) { + this.destination = destination; + } + + public void extract() throws IOException { + LOG.debug("Extract content of " + source + " to " + destination); + + // delete destination file if exists + removeDirectory(destination); + + ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(source)); + ZipEntry zipEntry = null; + + while ((zipEntry = zipInputStream.getNextEntry()) != null) { + try { + File file = new File(destination, zipEntry.getName()); + + // create intermediary directories - sometimes zip don't add them + File dir = new File(file.getParent()); + dir.mkdirs(); + + if (zipEntry.isDirectory()) { + file.mkdirs(); + } else { + byte[] buffer = new byte[1024]; + int length = 0; + FileOutputStream fos = new FileOutputStream(file); + + while ((length = zipInputStream.read(buffer)) >= 0) { + fos.write(buffer, 0, length); + } + + fos.close(); + } + } catch (FileNotFoundException e) { + LOG.error("File '" + zipEntry.getName() + "' not found"); + } + } + + zipInputStream.close(); + } + + private boolean removeDirectory(File directory) { + if (!directory.exists()) { + return true; + } + + if (!directory.isDirectory()) { + return false; + } + + File[] files = directory.listFiles(); + for (File file : files) { + if (file.isDirectory()) { + removeDirectory(file); + } else { + file.delete(); + } + } + + return directory.delete(); + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/ZipFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/ZipFilter.java new file mode 100644 index 0000000..4b10de3 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/ZipFilter.java @@ -0,0 +1,32 @@ +/* + * Copyright 2012 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.util; + +/** + * File filter that accepts all files ending with .ZIP. + * This filter is case insensitive. + * + * @author Decebal Suiu + */ +public class ZipFilter extends ExtensionFilter { + + /** + * The extension that this filter will search for. + */ + private static final String ZIP_EXTENSION = ".ZIP"; + + public ZipFilter() { + super(ZIP_EXTENSION); + } + +} diff --git a/pom.xml b/pom.xml index a8d45f9..aa9c67d 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 - org.pf4j + ro.fortsoft.pf4j pom 0.1-SNAPSHOT pom