```xml
<dependency>
- <groupId>org.pf4j</groupId>
+ <groupId>ro.fortsoft.pf4j</groupId>
<artifactId>pf4j</artifactId>
<version>${pf4j.version}</version>
</dependency>
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.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
- <groupId>org.pf4j.demo</groupId>
+ <groupId>ro.fortsoft.pf4j.demo</groupId>
<artifactId>pom</artifactId>
<version>0.1-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
- <groupId>org.pf4j</groupId>
+ <groupId>ro.fortsoft.pf4j</groupId>
<artifactId>pf4j</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
+++ /dev/null
-/*
- * 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();
-
-}
--- /dev/null
+/*
+ * 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();
+
+}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
- <groupId>org.pf4j.demo</groupId>
+ <groupId>ro.fortsoft.pf4j.demo</groupId>
<artifactId>pom</artifactId>
<version>0.1-SNAPSHOT</version>
</parent>
</licenses>
<properties>
- <main.class>org.pf4j.demo.Boot</main.class>
+ <main.class>ro.fortsoft.pf4j.demo.Boot</main.class>
</properties>
<build>
<dependencies>
<dependency>
- <groupId>org.pf4j</groupId>
+ <groupId>ro.fortsoft.pf4j</groupId>
<artifactId>pf4j</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.pf4j.demo</groupId>
+ <groupId>ro.fortsoft.pf4j.demo</groupId>
<artifactId>pf4j-demo-api</artifactId>
<version>${project.version}</version>
</dependency>
+++ /dev/null
-/*
- * 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<ExtensionWrapper<Greeting>> greetings = pluginManager.getExtensions(Greeting.class);
- for (ExtensionWrapper<Greeting> 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));
- }
-
-}
--- /dev/null
+/*
+ * 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<ExtensionWrapper<Greeting>> greetings = pluginManager.getExtensions(Greeting.class);
+ for (ExtensionWrapper<Greeting> 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));
+ }
+
+}
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=
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
- <groupId>org.pf4j.demo</groupId>
+ <groupId>ro.fortsoft.pf4j.demo</groupId>
<artifactId>pom</artifactId>
<version>0.1-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
- <groupId>org.pf4j</groupId>
+ <groupId>ro.fortsoft.pf4j</groupId>
<artifactId>pf4j</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.pf4j.demo</groupId>
+ <groupId>ro.fortsoft.pf4j.demo</groupId>
<artifactId>pf4j-demo-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
+++ /dev/null
-/*
- * 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";
- }
-
- }
-
-}
--- /dev/null
+/*
+ * 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";
+ }
+
+ }
+
+}
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=
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
- <groupId>org.pf4j.demo</groupId>
+ <groupId>ro.fortsoft.pf4j.demo</groupId>
<artifactId>pom</artifactId>
<version>0.1-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
- <groupId>org.pf4j</groupId>
+ <groupId>ro.fortsoft.pf4j</groupId>
<artifactId>pf4j</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.pf4j.demo</groupId>
+ <groupId>ro.fortsoft.pf4j.demo</groupId>
<artifactId>pf4j-demo-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
+++ /dev/null
-/*
- * 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";
- }
-
- }
-
-}
--- /dev/null
+/*
+ * 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";
+ }
+
+ }
+
+}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
- <groupId>org.pf4j</groupId>
+ <groupId>ro.fortsoft.pf4j</groupId>
<artifactId>pom</artifactId>
<version>0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
- <groupId>org.pf4j.demo</groupId>
+ <groupId>ro.fortsoft.pf4j.demo</groupId>
<artifactId>pom</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
- <groupId>org.pf4j</groupId>
+ <groupId>ro.fortsoft.pf4j</groupId>
<artifactId>pom</artifactId>
<version>0.1-SNAPSHOT</version>
</parent>
+++ /dev/null
-/*
- * 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<IndexItem<Extension, Object>> indices;
- private ClassLoader classLoader;
-
- public DefaultExtensionFinder(ClassLoader classLoader) {
- this.classLoader = classLoader;
- }
-
- @Override
- public <T> List<ExtensionWrapper<T>> find(Class<T> type) {
- LOG.debug("Find extensions for " + type);
- List<ExtensionWrapper<T>> result = new ArrayList<ExtensionWrapper<T>>();
- getIndices();
-// System.out.println("indices = "+ indices);
- for (IndexItem<Extension, Object> 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<T>(type.cast(instance), item.annotation().ordinal()));
- }
- }
- } catch (InstantiationException e) {
- LOG.error(e.getMessage(), e);
- }
- }
-
- return result;
- }
-
- private List<IndexItem<Extension, Object>> getIndices() {
- if (indices == null) {
- indices = new ArrayList<IndexItem<Extension, Object>>();
- Iterator<IndexItem<Extension, Object>> it = Index.load(Extension.class, Object.class, classLoader).iterator();
- while (it.hasNext()) {
- indices.add(it.next());
- }
- }
-
- return indices;
- }
-
-}
+++ /dev/null
-/*
- * 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;
- }
-
-}
+++ /dev/null
-/*
- * 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<String, Plugin> plugins;
-
- /**
- * A map of plugin class loaders (he key is the 'pluginId').
- */
- private Map<String, PluginClassLoader> pluginClassLoaders;
-
- /**
- * A relation between 'pluginPath' and 'pluginId'
- */
- private Map<String, String> pathToIdMap;
-
- /**
- * A list with unresolved plugins (unresolved dependency).
- */
- private List<Plugin> unresolvedPlugins;
-
- /**
- * A list with resolved plugins (resolved dependency).
- */
- private List<Plugin> resolvedPlugins;
-
- /**
- * A list with disabled plugins.
- */
- private List<Plugin> 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<String, Plugin>();
- pluginClassLoaders = new HashMap<String, PluginClassLoader>();
- pathToIdMap = new HashMap<String, String>();
- unresolvedPlugins = new ArrayList<Plugin>();
- resolvedPlugins = new ArrayList<Plugin>();
- disabledPlugins = new ArrayList<Plugin>();
- pluginDescriptorFinder = new DefaultPluginDescriptorFinder();
- uberClassLoader = new UberClassLoader();
- extensionFinder = new DefaultExtensionFinder(uberClassLoader);
- }
-
- /**
- * Retrieves all active plugins.
- */
- public List<Plugin> getPlugins() {
- return new ArrayList<Plugin>(plugins.values());
- }
-
- public List<Plugin> getResolvedPlugins() {
- return resolvedPlugins;
- }
-
- public Plugin getPlugin(String pluginId) {
- return plugins.get(pluginId);
- }
-
- public List<Plugin> getUnresolvedPlugins() {
- return unresolvedPlugins;
- }
-
- public List<Plugin> getDisabledPlugins() {
- return disabledPlugins;
- }
-
- /**
- * Start all active plugins.
- */
- public void startPlugins() {
- List<Plugin> 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<Plugin> 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 <T> List<ExtensionWrapper<T>> getExtensions(Class<T> 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());
- }
- }
-
-}
+++ /dev/null
-/*
- * 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<Plugin> plugins;
-
- public DependencyResolver(List<Plugin> plugins) {
- this.plugins = plugins;
- }
-
- /**
- * Get the list of plugins in dependency sorted order.
- */
- public List<Plugin> getSortedDependencies() {
- DirectedGraph<String> graph = new DirectedGraph<String>();
- for (Plugin plugin : plugins) {
- PluginDescriptor descriptor = plugin.getWrapper().getDescriptor();
- String pluginId = descriptor.getPluginId();
- List<String> dependencies = descriptor.getDependencies();
- if (!dependencies.isEmpty()) {
- for (String dependency : dependencies) {
- graph.addEdge(pluginId, dependency);
- }
- } else {
- graph.addVertex(pluginId);
- }
- }
-
- LOG.debug("Graph: " + graph);
- List<String> pluginsId = graph.reverseTopologicalSort();
-
- if (pluginsId == null) {
- LOG.error("Cyclic dependences !!!");
- return null;
- }
-
- LOG.debug("Plugins order: " + pluginsId);
- List<Plugin> sortedPlugins = new ArrayList<Plugin>();
- 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;
- }
-
-}
+++ /dev/null
-/*
- * 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;
-
-}
+++ /dev/null
-/*
- * 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 <T> List<ExtensionWrapper<T>> find(Class<T> type);
-
-}
+++ /dev/null
-/*
- * 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 {
-
-}
+++ /dev/null
-/*
- * 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<T> implements Comparable<ExtensionWrapper<T>> {
-
- 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<T> o) {
- return (ordinal - o.getOrdinal());
- }
-
-}
\ No newline at end of file
+++ /dev/null
-/*
- * 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 {
- }
-
-}
+++ /dev/null
-/*
- * 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<String> 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);
- }
-
-}
+++ /dev/null
-/*
- * 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<String> dependencies;
- private PluginClassLoader pluginClassLoader;
-
- public PluginDescriptor() {
- dependencies = new ArrayList<String>();
- }
-
- /**
- * 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<String> 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;
- }
-
-}
+++ /dev/null
-/*
- * 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;
-
-}
+++ /dev/null
-/*
- * 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);
- }
-
-}
+++ /dev/null
-/*
- * 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<String> 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<String> jars = new Vector<String>();
- 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;
- }
-
-}
+++ /dev/null
-/*
- * 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<Plugin> 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 <T> List<ExtensionWrapper<T>> getExtensions(Class<T> type);
-
-}
+++ /dev/null
-/*
- * 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<PluginVersion> {
-
- 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<String> tmp = new ArrayList<String>();
- 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));
- }
-
-}
+++ /dev/null
-/*
- * 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;
- }
-
-}
+++ /dev/null
-/*\r
- * Copyright 2012 Decebal Suiu\r
- * \r
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with\r
- * the License. You may obtain a copy of the License in the LICENSE file, or at:\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on\r
- * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the\r
- * specific language governing permissions and limitations under the License.\r
- */\r
-package org.pf4j.util;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Stack;\r
-\r
-/**\r
- * @author Decebal Suiu\r
- */\r
-public class DirectedGraph<V> {\r
-\r
- /**\r
- * The implementation here is basically an adjacency list, but instead\r
- * of an array of lists, a Map is used to map each vertex to its list of\r
- * adjacent vertices.\r
- */\r
- private Map<V, List<V>> neighbors = new HashMap<V, List<V>>();\r
-\r
- /**\r
- * Add a vertex to the graph. Nothing happens if vertex is already in graph.\r
- */\r
- public void addVertex(V vertex) {\r
- if (neighbors.containsKey(vertex)) {\r
- return;\r
- }\r
- neighbors.put(vertex, new ArrayList<V>());\r
- }\r
-\r
- /**\r
- * True if graph contains vertex.\r
- */\r
- public boolean containsVertex(V vertex) {\r
- return neighbors.containsKey(vertex);\r
- }\r
-\r
- /**\r
- * Add an edge to the graph; if either vertex does not exist, it's added.\r
- * This implementation allows the creation of multi-edges and self-loops.\r
- */\r
- public void addEdge(V from, V to) {\r
- this.addVertex(from);\r
- this.addVertex(to);\r
- neighbors.get(from).add(to);\r
- }\r
-\r
- /**\r
- * Remove an edge from the graph. Nothing happens if no such edge.\r
- * @throws IllegalArgumentException if either vertex doesn't exist.\r
- */\r
- public void remove(V from, V to) {\r
- if (!(this.containsVertex(from) && this.containsVertex(to))) {\r
- throw new IllegalArgumentException("Nonexistent vertex");\r
- }\r
- neighbors.get(from).remove(to);\r
- }\r
-\r
- /**\r
- * Report (as a Map) the out-degree of each vertex.\r
- */\r
- public Map<V, Integer> outDegree() {\r
- Map<V, Integer> result = new HashMap<V, Integer>();\r
- for (V vertex : neighbors.keySet()) {\r
- result.put(vertex, neighbors.get(vertex).size());\r
- }\r
-\r
- return result;\r
- }\r
-\r
- /**\r
- * Report (as a Map) the in-degree of each vertex.\r
- */\r
- public Map<V,Integer> inDegree() {\r
- Map<V, Integer> result = new HashMap<V, Integer>();\r
- for (V vertex : neighbors.keySet()) {\r
- result.put(vertex, 0); // all in-degrees are 0\r
- }\r
- for (V from : neighbors.keySet()) {\r
- for (V to : neighbors.get(from)) {\r
- result.put(to, result.get(to) + 1); // increment in-degree\r
- }\r
- }\r
-\r
- return result;\r
- }\r
-\r
- /**\r
- * Report (as a List) the topological sort of the vertices; null for no such sort.\r
- */\r
- public List<V> topologicalSort() {\r
- Map<V, Integer> degree = inDegree();\r
-\r
- // determine all vertices with zero in-degree\r
- Stack<V> zeroVertices = new Stack<V>(); // stack as good as any here\r
- for (V v : degree.keySet()) {\r
- if (degree.get(v) == 0) {\r
- zeroVertices.push(v);\r
- }\r
- }\r
-\r
- // determine the topological order\r
- List<V> result = new ArrayList<V>();\r
- while (!zeroVertices.isEmpty()) {\r
- V vertex = zeroVertices.pop(); // choose a vertex with zero in-degree\r
- result.add(vertex); // vertex 'v' is next in topological order\r
- // "remove" vertex 'v' by updating its neighbors\r
- for (V neighbor : neighbors.get(vertex)) {\r
- degree.put(neighbor, degree.get(neighbor) - 1);\r
- // remember any vertices that now have zero in-degree\r
- if (degree.get(neighbor) == 0) {\r
- zeroVertices.push(neighbor);\r
- }\r
- }\r
- }\r
-\r
- // check that we have used the entire graph (if not, there was a cycle)\r
- if (result.size() != neighbors.size()) {\r
- return null;\r
- }\r
-\r
- return result;\r
- }\r
-\r
- /**\r
- * Report (as a List) the reverse topological sort of the vertices; null for no such sort.\r
- */\r
- public List<V> reverseTopologicalSort() {\r
- List<V> list = topologicalSort();\r
- if (list == null) {\r
- return null;\r
- }\r
- Collections.reverse(list);\r
-\r
- return list;\r
- }\r
-\r
- /**\r
- * True if graph is a dag (directed acyclic graph).\r
- */\r
- public boolean isDag () {\r
- return topologicalSort() != null;\r
- }\r
-\r
- /**\r
- * String representation of graph.\r
- */\r
- @Override\r
- public String toString() {\r
- StringBuffer sb = new StringBuffer();\r
- for (V vertex : neighbors.keySet()) {\r
- sb.append("\n " + vertex + " -> " + neighbors.get(vertex));\r
- }\r
-\r
- return sb.toString();\r
- }\r
-\r
-}\r
+++ /dev/null
-/*
- * 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));
- }
-
-}
+++ /dev/null
-/*
- * 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));
- }
-
-}
+++ /dev/null
-/*
- * 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);
- }
-
-}
+++ /dev/null
-/*
- * 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<ClassLoader> loaders = new HashSet<ClassLoader>();
-
- 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<URL> findResources(String name) throws IOException {
- List<URL> resources = new ArrayList<URL>();
- for (ClassLoader loader : loaders) {
- resources.addAll(Collections.list(loader.getResources(name)));
- }
-
- return Collections.enumeration(resources);
- }
-
-}
+++ /dev/null
-/*
- * 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();
- }
-
-}
+++ /dev/null
-/*
- * 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);
- }
-
-}
--- /dev/null
+/*
+ * 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<IndexItem<Extension, Object>> indices;
+ private ClassLoader classLoader;
+
+ public DefaultExtensionFinder(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ @Override
+ public <T> List<ExtensionWrapper<T>> find(Class<T> type) {
+ LOG.debug("Find extensions for " + type);
+ List<ExtensionWrapper<T>> result = new ArrayList<ExtensionWrapper<T>>();
+ getIndices();
+// System.out.println("indices = "+ indices);
+ for (IndexItem<Extension, Object> 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<T>(type.cast(instance), item.annotation().ordinal()));
+ }
+ }
+ } catch (InstantiationException e) {
+ LOG.error(e.getMessage(), e);
+ }
+ }
+
+ return result;
+ }
+
+ private List<IndexItem<Extension, Object>> getIndices() {
+ if (indices == null) {
+ indices = new ArrayList<IndexItem<Extension, Object>>();
+ Iterator<IndexItem<Extension, Object>> it = Index.load(Extension.class, Object.class, classLoader).iterator();
+ while (it.hasNext()) {
+ indices.add(it.next());
+ }
+ }
+
+ return indices;
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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<String, Plugin> plugins;
+
+ /**
+ * A map of plugin class loaders (he key is the 'pluginId').
+ */
+ private Map<String, PluginClassLoader> pluginClassLoaders;
+
+ /**
+ * A relation between 'pluginPath' and 'pluginId'
+ */
+ private Map<String, String> pathToIdMap;
+
+ /**
+ * A list with unresolved plugins (unresolved dependency).
+ */
+ private List<Plugin> unresolvedPlugins;
+
+ /**
+ * A list with resolved plugins (resolved dependency).
+ */
+ private List<Plugin> resolvedPlugins;
+
+ /**
+ * A list with disabled plugins.
+ */
+ private List<Plugin> 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<String, Plugin>();
+ pluginClassLoaders = new HashMap<String, PluginClassLoader>();
+ pathToIdMap = new HashMap<String, String>();
+ unresolvedPlugins = new ArrayList<Plugin>();
+ resolvedPlugins = new ArrayList<Plugin>();
+ disabledPlugins = new ArrayList<Plugin>();
+ pluginDescriptorFinder = new DefaultPluginDescriptorFinder();
+ uberClassLoader = new UberClassLoader();
+ extensionFinder = new DefaultExtensionFinder(uberClassLoader);
+ }
+
+ /**
+ * Retrieves all active plugins.
+ */
+ public List<Plugin> getPlugins() {
+ return new ArrayList<Plugin>(plugins.values());
+ }
+
+ public List<Plugin> getResolvedPlugins() {
+ return resolvedPlugins;
+ }
+
+ public Plugin getPlugin(String pluginId) {
+ return plugins.get(pluginId);
+ }
+
+ public List<Plugin> getUnresolvedPlugins() {
+ return unresolvedPlugins;
+ }
+
+ public List<Plugin> getDisabledPlugins() {
+ return disabledPlugins;
+ }
+
+ /**
+ * Start all active plugins.
+ */
+ public void startPlugins() {
+ List<Plugin> 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<Plugin> 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 <T> List<ExtensionWrapper<T>> getExtensions(Class<T> 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());
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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<Plugin> plugins;
+
+ public DependencyResolver(List<Plugin> plugins) {
+ this.plugins = plugins;
+ }
+
+ /**
+ * Get the list of plugins in dependency sorted order.
+ */
+ public List<Plugin> getSortedDependencies() {
+ DirectedGraph<String> graph = new DirectedGraph<String>();
+ for (Plugin plugin : plugins) {
+ PluginDescriptor descriptor = plugin.getWrapper().getDescriptor();
+ String pluginId = descriptor.getPluginId();
+ List<String> dependencies = descriptor.getDependencies();
+ if (!dependencies.isEmpty()) {
+ for (String dependency : dependencies) {
+ graph.addEdge(pluginId, dependency);
+ }
+ } else {
+ graph.addVertex(pluginId);
+ }
+ }
+
+ LOG.debug("Graph: " + graph);
+ List<String> pluginsId = graph.reverseTopologicalSort();
+
+ if (pluginsId == null) {
+ LOG.error("Cyclic dependences !!!");
+ return null;
+ }
+
+ LOG.debug("Plugins order: " + pluginsId);
+ List<Plugin> sortedPlugins = new ArrayList<Plugin>();
+ 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+
+}
--- /dev/null
+/*
+ * 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 <T> List<ExtensionWrapper<T>> find(Class<T> type);
+
+}
--- /dev/null
+/*
+ * 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 {
+
+}
--- /dev/null
+/*
+ * 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<T> implements Comparable<ExtensionWrapper<T>> {
+
+ 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<T> o) {
+ return (ordinal - o.getOrdinal());
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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 {
+ }
+
+}
--- /dev/null
+/*
+ * 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<String> 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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<String> dependencies;
+ private PluginClassLoader pluginClassLoader;
+
+ public PluginDescriptor() {
+ dependencies = new ArrayList<String>();
+ }
+
+ /**
+ * 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<String> 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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<String> 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<String> jars = new Vector<String>();
+ 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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<Plugin> 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 <T> List<ExtensionWrapper<T>> getExtensions(Class<T> type);
+
+}
--- /dev/null
+/*
+ * 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<PluginVersion> {
+
+ 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<String> tmp = new ArrayList<String>();
+ 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));
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*\r
+ * Copyright 2012 Decebal Suiu\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with\r
+ * the License. You may obtain a copy of the License in the LICENSE file, or at:\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on\r
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the\r
+ * specific language governing permissions and limitations under the License.\r
+ */\r
+package ro.fortsoft.pf4j.util;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Stack;\r
+\r
+/**\r
+ * @author Decebal Suiu\r
+ */\r
+public class DirectedGraph<V> {\r
+\r
+ /**\r
+ * The implementation here is basically an adjacency list, but instead\r
+ * of an array of lists, a Map is used to map each vertex to its list of\r
+ * adjacent vertices.\r
+ */\r
+ private Map<V, List<V>> neighbors = new HashMap<V, List<V>>();\r
+\r
+ /**\r
+ * Add a vertex to the graph. Nothing happens if vertex is already in graph.\r
+ */\r
+ public void addVertex(V vertex) {\r
+ if (neighbors.containsKey(vertex)) {\r
+ return;\r
+ }\r
+ neighbors.put(vertex, new ArrayList<V>());\r
+ }\r
+\r
+ /**\r
+ * True if graph contains vertex.\r
+ */\r
+ public boolean containsVertex(V vertex) {\r
+ return neighbors.containsKey(vertex);\r
+ }\r
+\r
+ /**\r
+ * Add an edge to the graph; if either vertex does not exist, it's added.\r
+ * This implementation allows the creation of multi-edges and self-loops.\r
+ */\r
+ public void addEdge(V from, V to) {\r
+ this.addVertex(from);\r
+ this.addVertex(to);\r
+ neighbors.get(from).add(to);\r
+ }\r
+\r
+ /**\r
+ * Remove an edge from the graph. Nothing happens if no such edge.\r
+ * @throws IllegalArgumentException if either vertex doesn't exist.\r
+ */\r
+ public void remove(V from, V to) {\r
+ if (!(this.containsVertex(from) && this.containsVertex(to))) {\r
+ throw new IllegalArgumentException("Nonexistent vertex");\r
+ }\r
+ neighbors.get(from).remove(to);\r
+ }\r
+\r
+ /**\r
+ * Report (as a Map) the out-degree of each vertex.\r
+ */\r
+ public Map<V, Integer> outDegree() {\r
+ Map<V, Integer> result = new HashMap<V, Integer>();\r
+ for (V vertex : neighbors.keySet()) {\r
+ result.put(vertex, neighbors.get(vertex).size());\r
+ }\r
+\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Report (as a Map) the in-degree of each vertex.\r
+ */\r
+ public Map<V,Integer> inDegree() {\r
+ Map<V, Integer> result = new HashMap<V, Integer>();\r
+ for (V vertex : neighbors.keySet()) {\r
+ result.put(vertex, 0); // all in-degrees are 0\r
+ }\r
+ for (V from : neighbors.keySet()) {\r
+ for (V to : neighbors.get(from)) {\r
+ result.put(to, result.get(to) + 1); // increment in-degree\r
+ }\r
+ }\r
+\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Report (as a List) the topological sort of the vertices; null for no such sort.\r
+ */\r
+ public List<V> topologicalSort() {\r
+ Map<V, Integer> degree = inDegree();\r
+\r
+ // determine all vertices with zero in-degree\r
+ Stack<V> zeroVertices = new Stack<V>(); // stack as good as any here\r
+ for (V v : degree.keySet()) {\r
+ if (degree.get(v) == 0) {\r
+ zeroVertices.push(v);\r
+ }\r
+ }\r
+\r
+ // determine the topological order\r
+ List<V> result = new ArrayList<V>();\r
+ while (!zeroVertices.isEmpty()) {\r
+ V vertex = zeroVertices.pop(); // choose a vertex with zero in-degree\r
+ result.add(vertex); // vertex 'v' is next in topological order\r
+ // "remove" vertex 'v' by updating its neighbors\r
+ for (V neighbor : neighbors.get(vertex)) {\r
+ degree.put(neighbor, degree.get(neighbor) - 1);\r
+ // remember any vertices that now have zero in-degree\r
+ if (degree.get(neighbor) == 0) {\r
+ zeroVertices.push(neighbor);\r
+ }\r
+ }\r
+ }\r
+\r
+ // check that we have used the entire graph (if not, there was a cycle)\r
+ if (result.size() != neighbors.size()) {\r
+ return null;\r
+ }\r
+\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Report (as a List) the reverse topological sort of the vertices; null for no such sort.\r
+ */\r
+ public List<V> reverseTopologicalSort() {\r
+ List<V> list = topologicalSort();\r
+ if (list == null) {\r
+ return null;\r
+ }\r
+ Collections.reverse(list);\r
+\r
+ return list;\r
+ }\r
+\r
+ /**\r
+ * True if graph is a dag (directed acyclic graph).\r
+ */\r
+ public boolean isDag () {\r
+ return topologicalSort() != null;\r
+ }\r
+\r
+ /**\r
+ * String representation of graph.\r
+ */\r
+ @Override\r
+ public String toString() {\r
+ StringBuffer sb = new StringBuffer();\r
+ for (V vertex : neighbors.keySet()) {\r
+ sb.append("\n " + vertex + " -> " + neighbors.get(vertex));\r
+ }\r
+\r
+ return sb.toString();\r
+ }\r
+\r
+}\r
--- /dev/null
+/*
+ * 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));
+ }
+
+}
--- /dev/null
+/*
+ * 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));
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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<ClassLoader> loaders = new HashSet<ClassLoader>();
+
+ 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<URL> findResources(String name) throws IOException {
+ List<URL> resources = new ArrayList<URL>();
+ for (ClassLoader loader : loaders) {
+ resources.addAll(Collections.list(loader.getResources(name)));
+ }
+
+ return Collections.enumeration(resources);
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
- <groupId>org.pf4j</groupId>
+ <groupId>ro.fortsoft.pf4j</groupId>
<artifactId>pom</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>pom</packaging>