mirror of https://github.com/pf4j/pf4j.git
Decebal Suiu
12 years ago
commit
2aeb77b8c8
44 changed files with 3153 additions and 0 deletions
@ -0,0 +1,151 @@ |
|||||||
|
Plugin Framework for Java (PF4J) |
||||||
|
===================== |
||||||
|
|
||||||
|
A plugin is a way for a third party to extend the functionality of an application. A plugin implements extensions points |
||||||
|
declared by application or another plugins. Also a plugin can defines extension points. |
||||||
|
|
||||||
|
Components |
||||||
|
------------------- |
||||||
|
- **Plugin** is the base class for all plugins types. Each plugin is loaded into a separate class loader to avoid conflicts. |
||||||
|
- **PluginManager** is used for all aspects of plugins management (loading, starting, stopping). |
||||||
|
- **ExtensionPoint** is a point in the application where custom code can be invoked. |
||||||
|
- **Extension** is an implementation of extension point. |
||||||
|
|
||||||
|
Artifacts |
||||||
|
------------------- |
||||||
|
- PF4J `pf4j` (jar) |
||||||
|
- PF4J Demo `pf4j-demo` (executable jar) |
||||||
|
|
||||||
|
Using Maven |
||||||
|
------------------- |
||||||
|
|
||||||
|
First you must install the pf4j artifacts in your local maven repository with: |
||||||
|
|
||||||
|
mvn clean install |
||||||
|
|
||||||
|
I will upload these artifacts in maven central repository as soon as possible. |
||||||
|
|
||||||
|
In your pom.xml you must define the dependencies to PF4J artifacts with: |
||||||
|
|
||||||
|
```xml |
||||||
|
<dependency> |
||||||
|
<groupId>org.pf4j</groupId> |
||||||
|
<artifactId>pf4j</artifactId> |
||||||
|
<version>${pf4j.version}</version> |
||||||
|
</dependency> |
||||||
|
``` |
||||||
|
|
||||||
|
where ${pf4j.version} is the last pf4j version. |
||||||
|
|
||||||
|
How to use |
||||||
|
------------------- |
||||||
|
It's very simple to add pf4j in your application: |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
... |
||||||
|
|
||||||
|
PluginManager pluginManager = new DefaultPluginManager(); |
||||||
|
pluginManager.loadPlugins(); |
||||||
|
pluginManager.startPlugins(); |
||||||
|
|
||||||
|
... |
||||||
|
} |
||||||
|
|
||||||
|
In above code, I created a DefaultPluginManager (it's the default implementation for |
||||||
|
PluginManager interface) that load and start all active(resolved) plugins. |
||||||
|
The plugins are stored in a folder. You can specify the plugins folder in constructor of DefaultPluginManager |
||||||
|
or using the constructor without parameters (in this case plugins folder is returned by System.getProperty("pf4j.pluginsDir", "plugins")). |
||||||
|
|
||||||
|
The structure of plugins folder is: |
||||||
|
- plugin1.zip (or plugin1 folder) |
||||||
|
- plugin2.zip (or plugin2 folder) |
||||||
|
... |
||||||
|
- pluginN.zip (or pluginN folder) |
||||||
|
|
||||||
|
In plugins folder you can put a plugin as folder or archive file (zip). |
||||||
|
A plugin folder has this structure: |
||||||
|
- `classes` folder |
||||||
|
- `lib` folder (optional - if the plugin used third party libraries) |
||||||
|
|
||||||
|
The plugin manager discovers plugins metadata using a PluginDescriptorFinder. DefaultPluginDescriptorFinder lookup plugin descriptor in MANIFEST.MF file. |
||||||
|
In this case the classes/META-INF/MANIFEST.MF looks like: |
||||||
|
|
||||||
|
Manifest-Version: 1.0 |
||||||
|
Archiver-Version: Plexus Archiver |
||||||
|
Created-By: Apache Maven |
||||||
|
Built-By: decebal |
||||||
|
Build-Jdk: 1.6.0_17 |
||||||
|
Plugin-Class: org.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 |
||||||
|
to plugins `x, y, z`. |
||||||
|
|
||||||
|
You can define an extension point in your application using ExtensionPoint interface marker. |
||||||
|
|
||||||
|
public interface Greeting extends ExtensionPoint { |
||||||
|
|
||||||
|
public String getGreeting(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
Another important internal component is ExtensionFinder that describes how plugin manager discovers extensions for extensions points. DefaultExtensionFinder look up extensions using Extension annotation. |
||||||
|
|
||||||
|
public class WelcomePlugin extends Plugin { |
||||||
|
|
||||||
|
public WelcomePlugin(PluginWrapper wrapper) { |
||||||
|
super(wrapper); |
||||||
|
} |
||||||
|
|
||||||
|
@Extension |
||||||
|
public static class WelcomeGreeting implements Greeting { |
||||||
|
|
||||||
|
public String getGreeting() { |
||||||
|
return "Welcome"; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
In above code I supply an extension for the Greeting extension point. |
||||||
|
|
||||||
|
You can retrieves all extensions for an extension point with: |
||||||
|
|
||||||
|
List<ExtensionWrapper<Greeting>> greetings = pluginManager.getExtensions(Greeting.class); |
||||||
|
for (ExtensionWrapper<Greeting> greeting : greetings) { |
||||||
|
System.out.println(">>> " + greeting.getInstance().getGreeting()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
For more information please see the demo sources. |
||||||
|
|
||||||
|
Demo |
||||||
|
------------------- |
||||||
|
|
||||||
|
I have a tiny demo application. In this demo I have implemented two widgets types: |
||||||
|
a chart widget (using open flash chart) and a text widget (display a Lorem Ipsum). |
||||||
|
You can drag and drop widgets, perform some actions on each widget, add or remove new |
||||||
|
widgets, change widget settings, collapse widgets. |
||||||
|
|
||||||
|
The demo application is in demo folder. |
||||||
|
To run the demo application use: |
||||||
|
|
||||||
|
./run-demo.sh |
||||||
|
|
||||||
|
License |
||||||
|
-------------- |
||||||
|
|
||||||
|
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. |
@ -0,0 +1,44 @@ |
|||||||
|
<?xml version="1.0"?> |
||||||
|
<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> |
||||||
|
<artifactId>pom</artifactId> |
||||||
|
<version>0.1-SNAPSHOT</version> |
||||||
|
</parent> |
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<artifactId>pf4j-demo-api</artifactId> |
||||||
|
<version>0.1-SNAPSHOT</version> |
||||||
|
<packaging>jar</packaging> |
||||||
|
<name>Demo Api</name> |
||||||
|
|
||||||
|
<licenses> |
||||||
|
<license> |
||||||
|
<name>The Apache Software License, Version 2.0</name> |
||||||
|
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> |
||||||
|
<distribution>repo</distribution> |
||||||
|
</license> |
||||||
|
</licenses> |
||||||
|
|
||||||
|
<build> |
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<artifactId>maven-deploy-plugin</artifactId> |
||||||
|
<configuration> |
||||||
|
<skip>true</skip> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>org.pf4j</groupId> |
||||||
|
<artifactId>pf4j</artifactId> |
||||||
|
<version>${project.version}</version> |
||||||
|
<scope>provided</scope> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,24 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package org.pf4j.demo.api; |
||||||
|
|
||||||
|
import org.pf4j.ExtensionPoint; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Decebal Suiu |
||||||
|
*/ |
||||||
|
public interface Greeting extends ExtensionPoint { |
||||||
|
|
||||||
|
public String getGreeting(); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,89 @@ |
|||||||
|
<?xml version="1.0"?> |
||||||
|
<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> |
||||||
|
<artifactId>pom</artifactId> |
||||||
|
<version>0.1-SNAPSHOT</version> |
||||||
|
</parent> |
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<artifactId>pf4j-demo-app</artifactId> |
||||||
|
<version>0.1-SNAPSHOT</version> |
||||||
|
<packaging>jar</packaging> |
||||||
|
<name>Demo App</name> |
||||||
|
|
||||||
|
<licenses> |
||||||
|
<license> |
||||||
|
<name>The Apache Software License, Version 2.0</name> |
||||||
|
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> |
||||||
|
<distribution>repo</distribution> |
||||||
|
</license> |
||||||
|
</licenses> |
||||||
|
|
||||||
|
<properties> |
||||||
|
<main.class>org.pf4j.demo.Boot</main.class> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<build> |
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<artifactId>maven-assembly-plugin</artifactId> |
||||||
|
<version>2.3</version> |
||||||
|
<configuration> |
||||||
|
<descriptors> |
||||||
|
<descriptor> |
||||||
|
src/main/assembly/assembly.xml |
||||||
|
</descriptor> |
||||||
|
</descriptors> |
||||||
|
<appendAssemblyId>false</appendAssemblyId> |
||||||
|
</configuration> |
||||||
|
<executions> |
||||||
|
<execution> |
||||||
|
<id>make-assembly</id> |
||||||
|
<phase>package</phase> |
||||||
|
<goals> |
||||||
|
<goal>attached</goal> |
||||||
|
</goals> |
||||||
|
</execution> |
||||||
|
</executions> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-jar-plugin</artifactId> |
||||||
|
<version>2.3.1</version> |
||||||
|
<configuration> |
||||||
|
<archive> |
||||||
|
<manifest> |
||||||
|
<addClasspath>true</addClasspath> |
||||||
|
<classpathPrefix>lib/</classpathPrefix> |
||||||
|
<mainClass>${main.class}</mainClass> |
||||||
|
</manifest> |
||||||
|
</archive> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<artifactId>maven-deploy-plugin</artifactId> |
||||||
|
<configuration> |
||||||
|
<skip>true</skip> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>org.pf4j</groupId> |
||||||
|
<artifactId>pf4j</artifactId> |
||||||
|
<version>${project.version}</version> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.pf4j.demo</groupId> |
||||||
|
<artifactId>pf4j-demo-api</artifactId> |
||||||
|
<version>${project.version}</version> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,32 @@ |
|||||||
|
<!-- |
||||||
|
Describes the dist |
||||||
|
|
||||||
|
@author Decebal Suiu |
||||||
|
@version 1.0 |
||||||
|
--> |
||||||
|
<assembly> |
||||||
|
<id>plugin</id> |
||||||
|
<formats> |
||||||
|
<format>dir</format> |
||||||
|
</formats> |
||||||
|
<includeBaseDirectory>false</includeBaseDirectory> |
||||||
|
<dependencySets> |
||||||
|
<dependencySet> |
||||||
|
<useProjectArtifact>false</useProjectArtifact> |
||||||
|
<outputDirectory>lib</outputDirectory> |
||||||
|
<includes> |
||||||
|
<include>*:jar:*</include> |
||||||
|
</includes> |
||||||
|
</dependencySet> |
||||||
|
</dependencySets> |
||||||
|
<fileSets> |
||||||
|
<fileSet> |
||||||
|
<directory>${project.build.directory}</directory> |
||||||
|
<outputDirectory></outputDirectory> |
||||||
|
<includes> |
||||||
|
<include>*.jar</include> |
||||||
|
</includes> |
||||||
|
</fileSet> |
||||||
|
</fileSets> |
||||||
|
</assembly> |
||||||
|
|
@ -0,0 +1,63 @@ |
|||||||
|
/* |
||||||
|
* 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)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
log4j.rootLogger=DEBUG,Console |
||||||
|
|
||||||
|
log4j.appender.Console=org.apache.log4j.ConsoleAppender |
||||||
|
log4j.appender.Console.layout=org.apache.log4j.PatternLayout |
||||||
|
log4j.appender.Console.layout.conversionPattern=%-5p - %-26.26c{1} - %m\n |
||||||
|
|
||||||
|
log4j.logger.org.apache.wicket=INFO |
||||||
|
log4j.logger.org.apache.wicket.protocol.http.HttpSessionStore=INFO |
||||||
|
log4j.logger.org.apache.wicket.version=INFO |
||||||
|
log4j.logger.org.apache.wicket.RequestCycle=INFO |
@ -0,0 +1,8 @@ |
|||||||
|
#handlers = java.util.logging.ConsoleHandler |
||||||
|
handlers = org.slf4j.bridge.SLF4JBridgeHandler |
||||||
|
|
||||||
|
# Set the default logging level for the root logger |
||||||
|
.level = ALL |
||||||
|
|
||||||
|
# Set the default formatter for new ConsoleHandler instances |
||||||
|
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter |
@ -0,0 +1,5 @@ |
|||||||
|
plugin.id=welcome-plugin |
||||||
|
plugin.class=org.pf4j.demo.welcome.WelcomePlugin |
||||||
|
plugin.version=0.0.1 |
||||||
|
plugin.provider=Decebal Suiu |
||||||
|
plugin.dependencies= |
@ -0,0 +1,127 @@ |
|||||||
|
<?xml version="1.0"?> |
||||||
|
<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> |
||||||
|
<artifactId>pom</artifactId> |
||||||
|
<version>0.1-SNAPSHOT</version> |
||||||
|
</parent> |
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<artifactId>pf4j-demo-plugin1</artifactId> |
||||||
|
<version>0.1-SNAPSHOT</version> |
||||||
|
<packaging>jar</packaging> |
||||||
|
<name>Demo Plugin #1</name> |
||||||
|
|
||||||
|
<licenses> |
||||||
|
<license> |
||||||
|
<name>The Apache Software License, Version 2.0</name> |
||||||
|
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> |
||||||
|
<distribution>repo</distribution> |
||||||
|
</license> |
||||||
|
</licenses> |
||||||
|
|
||||||
|
<build> |
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<groupId>org.codehaus.mojo</groupId> |
||||||
|
<artifactId>properties-maven-plugin</artifactId> |
||||||
|
<version>1.0-alpha-2</version> |
||||||
|
<executions> |
||||||
|
<execution> |
||||||
|
<phase>initialize</phase> |
||||||
|
<goals> |
||||||
|
<goal>read-project-properties</goal> |
||||||
|
</goals> |
||||||
|
<configuration> |
||||||
|
<files> |
||||||
|
<file>plugin.properties</file> |
||||||
|
</files> |
||||||
|
</configuration> |
||||||
|
</execution> |
||||||
|
</executions> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-antrun-plugin</artifactId> |
||||||
|
<version>1.6</version> |
||||||
|
<executions> |
||||||
|
<execution> |
||||||
|
<id>unzip jar file</id> |
||||||
|
<phase>package</phase> |
||||||
|
<configuration> |
||||||
|
<target> |
||||||
|
<unzip src="target/${artifactId}-${version}.${packaging}" dest="target/plugin-classes" /> |
||||||
|
</target> |
||||||
|
</configuration> |
||||||
|
<goals> |
||||||
|
<goal>run</goal> |
||||||
|
</goals> |
||||||
|
</execution> |
||||||
|
</executions> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<artifactId>maven-assembly-plugin</artifactId> |
||||||
|
<version>2.3</version> |
||||||
|
<configuration> |
||||||
|
<descriptors> |
||||||
|
<descriptor> |
||||||
|
src/main/assembly/assembly.xml |
||||||
|
</descriptor> |
||||||
|
</descriptors> |
||||||
|
<appendAssemblyId>false</appendAssemblyId> |
||||||
|
</configuration> |
||||||
|
<executions> |
||||||
|
<execution> |
||||||
|
<id>make-assembly</id> |
||||||
|
<phase>package</phase> |
||||||
|
<goals> |
||||||
|
<goal>attached</goal> |
||||||
|
</goals> |
||||||
|
</execution> |
||||||
|
</executions> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-jar-plugin</artifactId> |
||||||
|
<configuration> |
||||||
|
<archive> |
||||||
|
<manifestEntries> |
||||||
|
<Plugin-Id>${plugin.id}</Plugin-Id> |
||||||
|
<Plugin-Class>${plugin.class}</Plugin-Class> |
||||||
|
<Plugin-Version>${plugin.version}</Plugin-Version> |
||||||
|
<Plugin-Provider>${plugin.provider}</Plugin-Provider> |
||||||
|
<Plugin-Dependencies>${plugin.dependencies}</Plugin-Dependencies> |
||||||
|
</manifestEntries> |
||||||
|
</archive> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<artifactId>maven-deploy-plugin</artifactId> |
||||||
|
<configuration> |
||||||
|
<skip>true</skip> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>org.pf4j</groupId> |
||||||
|
<artifactId>pf4j</artifactId> |
||||||
|
<version>${project.version}</version> |
||||||
|
<scope>provided</scope> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.pf4j.demo</groupId> |
||||||
|
<artifactId>pf4j-demo-api</artifactId> |
||||||
|
<version>${project.version}</version> |
||||||
|
<scope>provided</scope> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,37 @@ |
|||||||
|
<!-- |
||||||
|
Describes the plugin archive |
||||||
|
|
||||||
|
@author Decebal Suiu |
||||||
|
@version 1.0 |
||||||
|
--> |
||||||
|
<assembly> |
||||||
|
<id>plugin</id> |
||||||
|
<formats> |
||||||
|
<format>zip</format> |
||||||
|
</formats> |
||||||
|
<includeBaseDirectory>false</includeBaseDirectory> |
||||||
|
<dependencySets> |
||||||
|
<dependencySet> |
||||||
|
<useProjectArtifact>false</useProjectArtifact> |
||||||
|
<scope>runtime</scope> |
||||||
|
<outputDirectory>lib</outputDirectory> |
||||||
|
<includes> |
||||||
|
<include>*:jar:*</include> |
||||||
|
</includes> |
||||||
|
</dependencySet> |
||||||
|
</dependencySets> |
||||||
|
<!-- |
||||||
|
<fileSets> |
||||||
|
<fileSet> |
||||||
|
<directory>target/classes</directory> |
||||||
|
<outputDirectory>classes</outputDirectory> |
||||||
|
</fileSet> |
||||||
|
</fileSets> |
||||||
|
--> |
||||||
|
<fileSets> |
||||||
|
<fileSet> |
||||||
|
<directory>target/plugin-classes</directory> |
||||||
|
<outputDirectory>classes</outputDirectory> |
||||||
|
</fileSet> |
||||||
|
</fileSets> |
||||||
|
</assembly> |
@ -0,0 +1,46 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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"; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
plugin.id=hello-plugin |
||||||
|
plugin.class=org.pf4j.demo.hello.HelloPlugin |
||||||
|
plugin.version=0.0.1 |
||||||
|
plugin.provider=Decebal Suiu |
||||||
|
plugin.dependencies= |
@ -0,0 +1,136 @@ |
|||||||
|
<?xml version="1.0"?> |
||||||
|
<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> |
||||||
|
<artifactId>pom</artifactId> |
||||||
|
<version>0.1-SNAPSHOT</version> |
||||||
|
</parent> |
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<artifactId>pf4j-demo-plugin2</artifactId> |
||||||
|
<version>0.1-SNAPSHOT</version> |
||||||
|
<packaging>jar</packaging> |
||||||
|
<name>Demo Plugin #2</name> |
||||||
|
|
||||||
|
<licenses> |
||||||
|
<license> |
||||||
|
<name>The Apache Software License, Version 2.0</name> |
||||||
|
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> |
||||||
|
<distribution>repo</distribution> |
||||||
|
</license> |
||||||
|
</licenses> |
||||||
|
|
||||||
|
<build> |
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<groupId>org.codehaus.mojo</groupId> |
||||||
|
<artifactId>properties-maven-plugin</artifactId> |
||||||
|
<version>1.0-alpha-2</version> |
||||||
|
<executions> |
||||||
|
<execution> |
||||||
|
<phase>initialize</phase> |
||||||
|
<goals> |
||||||
|
<goal>read-project-properties</goal> |
||||||
|
</goals> |
||||||
|
<configuration> |
||||||
|
<files> |
||||||
|
<file>plugin.properties</file> |
||||||
|
</files> |
||||||
|
</configuration> |
||||||
|
</execution> |
||||||
|
</executions> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-antrun-plugin</artifactId> |
||||||
|
<version>1.6</version> |
||||||
|
<executions> |
||||||
|
<execution> |
||||||
|
<id>unzip jar file</id> |
||||||
|
<phase>package</phase> |
||||||
|
<configuration> |
||||||
|
<target> |
||||||
|
<unzip src="target/${artifactId}-${version}.${packaging}" dest="target/plugin-classes" /> |
||||||
|
</target> |
||||||
|
</configuration> |
||||||
|
<goals> |
||||||
|
<goal>run</goal> |
||||||
|
</goals> |
||||||
|
</execution> |
||||||
|
</executions> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<artifactId>maven-assembly-plugin</artifactId> |
||||||
|
<version>2.3</version> |
||||||
|
<configuration> |
||||||
|
<appendAssemblyId>false</appendAssemblyId> |
||||||
|
<descriptors> |
||||||
|
<descriptor> |
||||||
|
src/main/assembly/assembly.xml |
||||||
|
</descriptor> |
||||||
|
</descriptors> |
||||||
|
<archive> |
||||||
|
<manifestEntries> |
||||||
|
<Plugin-Id>${plugin.id}</Plugin-Id> |
||||||
|
<Plugin-Class>${plugin.class}</Plugin-Class> |
||||||
|
<Plugin-Version>${plugin.version}</Plugin-Version> |
||||||
|
<Plugin-Provider>${plugin.provider}</Plugin-Provider> |
||||||
|
<Plugin-Dependencies>${plugin.dependencies}</Plugin-Dependencies> |
||||||
|
</manifestEntries> |
||||||
|
</archive> |
||||||
|
</configuration> |
||||||
|
<executions> |
||||||
|
<execution> |
||||||
|
<id>make-assembly</id> |
||||||
|
<phase>package</phase> |
||||||
|
<goals> |
||||||
|
<goal>single</goal> |
||||||
|
</goals> |
||||||
|
</execution> |
||||||
|
</executions> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-jar-plugin</artifactId> |
||||||
|
<configuration> |
||||||
|
<archive> |
||||||
|
<manifestEntries> |
||||||
|
<Plugin-Id>${plugin.id}</Plugin-Id> |
||||||
|
<Plugin-Class>${plugin.class}</Plugin-Class> |
||||||
|
<Plugin-Version>${plugin.version}</Plugin-Version> |
||||||
|
<Plugin-Provider>${plugin.provider}</Plugin-Provider> |
||||||
|
<Plugin-Dependencies>${plugin.dependencies}</Plugin-Dependencies> |
||||||
|
</manifestEntries> |
||||||
|
</archive> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<artifactId>maven-deploy-plugin</artifactId> |
||||||
|
<configuration> |
||||||
|
<skip>true</skip> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>org.pf4j</groupId> |
||||||
|
<artifactId>pf4j</artifactId> |
||||||
|
<version>${project.version}</version> |
||||||
|
<scope>provided</scope> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.pf4j.demo</groupId> |
||||||
|
<artifactId>pf4j-demo-api</artifactId> |
||||||
|
<version>${project.version}</version> |
||||||
|
<scope>provided</scope> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,37 @@ |
|||||||
|
<!-- |
||||||
|
Describes the plugin archive |
||||||
|
|
||||||
|
@author Decebal Suiu |
||||||
|
@version 1.0 |
||||||
|
--> |
||||||
|
<assembly> |
||||||
|
<id>plugin</id> |
||||||
|
<formats> |
||||||
|
<format>zip</format> |
||||||
|
</formats> |
||||||
|
<includeBaseDirectory>false</includeBaseDirectory> |
||||||
|
<dependencySets> |
||||||
|
<dependencySet> |
||||||
|
<useProjectArtifact>false</useProjectArtifact> |
||||||
|
<scope>runtime</scope> |
||||||
|
<outputDirectory>lib</outputDirectory> |
||||||
|
<includes> |
||||||
|
<include>*:jar:*</include> |
||||||
|
</includes> |
||||||
|
</dependencySet> |
||||||
|
</dependencySets> |
||||||
|
<!-- |
||||||
|
<fileSets> |
||||||
|
<fileSet> |
||||||
|
<directory>target/classes</directory> |
||||||
|
<outputDirectory>classes</outputDirectory> |
||||||
|
</fileSet> |
||||||
|
</fileSets> |
||||||
|
--> |
||||||
|
<fileSets> |
||||||
|
<fileSet> |
||||||
|
<directory>target/plugin-classes</directory> |
||||||
|
<outputDirectory>classes</outputDirectory> |
||||||
|
</fileSet> |
||||||
|
</fileSets> |
||||||
|
</assembly> |
@ -0,0 +1,48 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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"; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
<?xml version="1.0"?> |
||||||
|
<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> |
||||||
|
<artifactId>pom</artifactId> |
||||||
|
<version>0.1-SNAPSHOT</version> |
||||||
|
</parent> |
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<groupId>org.pf4j.demo</groupId> |
||||||
|
<artifactId>pom</artifactId> |
||||||
|
<version>0.1-SNAPSHOT</version> |
||||||
|
<packaging>pom</packaging> |
||||||
|
<name>PF4J Demo</name> |
||||||
|
|
||||||
|
<licenses> |
||||||
|
<license> |
||||||
|
<name>The Apache Software License, Version 2.0</name> |
||||||
|
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> |
||||||
|
<distribution>repo</distribution> |
||||||
|
</license> |
||||||
|
</licenses> |
||||||
|
|
||||||
|
<build> |
||||||
|
<resources> |
||||||
|
<resource> |
||||||
|
<filtering>false</filtering> |
||||||
|
<directory>src/main/java</directory> |
||||||
|
<excludes> |
||||||
|
<exclude>**/*.java</exclude> |
||||||
|
</excludes> |
||||||
|
</resource> |
||||||
|
<resource> |
||||||
|
<directory>src/main/resources</directory> |
||||||
|
</resource> |
||||||
|
</resources> |
||||||
|
|
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-compiler-plugin</artifactId> |
||||||
|
<version>2.3.2</version> |
||||||
|
<configuration> |
||||||
|
<source>1.6</source> |
||||||
|
<target>1.6</target> |
||||||
|
<optimize>true</optimize> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
|
||||||
|
<modules> |
||||||
|
<module>app</module> |
||||||
|
<module>api</module> |
||||||
|
<module>plugin1</module> |
||||||
|
<module>plugin2</module> |
||||||
|
</modules> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,122 @@ |
|||||||
|
<?xml version="1.0"?> |
||||||
|
<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> |
||||||
|
<artifactId>pom</artifactId> |
||||||
|
<version>0.1-SNAPSHOT</version> |
||||||
|
</parent> |
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<artifactId>pf4j</artifactId> |
||||||
|
<version>0.1-SNAPSHOT</version> |
||||||
|
<packaging>jar</packaging> |
||||||
|
<name>PF4J Library</name> |
||||||
|
<description>Plugin Framework for Java</description> |
||||||
|
|
||||||
|
<licenses> |
||||||
|
<license> |
||||||
|
<name>The Apache Software License, Version 2.0</name> |
||||||
|
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> |
||||||
|
<distribution>repo</distribution> |
||||||
|
</license> |
||||||
|
</licenses> |
||||||
|
|
||||||
|
<scm> |
||||||
|
<connection>scm:git:https://github.com/decebals/pf4j.git</connection> |
||||||
|
<developerConnection>scm:git:https://github.com/decebals/pf4j.git</developerConnection> |
||||||
|
<url>git@github.com/decebals/pf4j.git</url> |
||||||
|
</scm> |
||||||
|
|
||||||
|
<properties> |
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||||
|
<slf4j.version>1.6.4</slf4j.version> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<build> |
||||||
|
<resources> |
||||||
|
<resource> |
||||||
|
<filtering>false</filtering> |
||||||
|
<directory>src/main/java</directory> |
||||||
|
<excludes> |
||||||
|
<exclude>**/*.java</exclude> |
||||||
|
</excludes> |
||||||
|
</resource> |
||||||
|
<resource> |
||||||
|
<directory>src/main/resources</directory> |
||||||
|
</resource> |
||||||
|
</resources> |
||||||
|
|
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-compiler-plugin</artifactId> |
||||||
|
<version>2.3.2</version> |
||||||
|
<configuration> |
||||||
|
<source>1.6</source> |
||||||
|
<target>1.6</target> |
||||||
|
<optimize>true</optimize> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-war-plugin</artifactId> |
||||||
|
<version>2.1.1</version> |
||||||
|
<configuration> |
||||||
|
<warName>root</warName> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-release-plugin</artifactId> |
||||||
|
<version>2.3</version> |
||||||
|
<configuration> |
||||||
|
<goals>deploy</goals> |
||||||
|
<autoVersionSubmodules>true</autoVersionSubmodules> |
||||||
|
<tagNameFormat>release-@{project.version}</tagNameFormat> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>commons-lang</groupId> |
||||||
|
<artifactId>commons-lang</artifactId> |
||||||
|
<version>2.4</version> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>net.java.sezpoz</groupId> |
||||||
|
<artifactId>sezpoz</artifactId> |
||||||
|
<version>1.9</version> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<!-- Logs --> |
||||||
|
<dependency> |
||||||
|
<groupId>log4j</groupId> |
||||||
|
<artifactId>log4j</artifactId> |
||||||
|
<version>1.2.16</version> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>org.slf4j</groupId> |
||||||
|
<artifactId>slf4j-api</artifactId> |
||||||
|
<version>${slf4j.version}</version> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>org.slf4j</groupId> |
||||||
|
<artifactId>jul-to-slf4j</artifactId> |
||||||
|
<version>${slf4j.version}</version> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>org.slf4j</groupId> |
||||||
|
<artifactId>slf4j-log4j12</artifactId> |
||||||
|
<version>${slf4j.version}</version> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
</project> |
@ -0,0 +1,80 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,90 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,327 @@ |
|||||||
|
/* |
||||||
|
* 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()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,81 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package org.pf4j; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Decebal Suiu |
||||||
|
*/ |
||||||
|
public interface ExtensionFinder { |
||||||
|
|
||||||
|
public <T> List<ExtensionWrapper<T>> find(Class<T> type); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package org.pf4j; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Decebal Suiu |
||||||
|
*/ |
||||||
|
public interface ExtensionPoint { |
||||||
|
|
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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 { |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,140 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,142 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,191 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,171 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package org.pf4j.util; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Stack; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Decebal Suiu |
||||||
|
*/ |
||||||
|
public class DirectedGraph<V> { |
||||||
|
|
||||||
|
/** |
||||||
|
* The implementation here is basically an adjacency list, but instead |
||||||
|
* of an array of lists, a Map is used to map each vertex to its list of |
||||||
|
* adjacent vertices. |
||||||
|
*/ |
||||||
|
private Map<V, List<V>> neighbors = new HashMap<V, List<V>>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Add a vertex to the graph. Nothing happens if vertex is already in graph. |
||||||
|
*/ |
||||||
|
public void addVertex(V vertex) { |
||||||
|
if (neighbors.containsKey(vertex)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
neighbors.put(vertex, new ArrayList<V>()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* True if graph contains vertex. |
||||||
|
*/ |
||||||
|
public boolean containsVertex(V vertex) { |
||||||
|
return neighbors.containsKey(vertex); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add an edge to the graph; if either vertex does not exist, it's added. |
||||||
|
* This implementation allows the creation of multi-edges and self-loops. |
||||||
|
*/ |
||||||
|
public void addEdge(V from, V to) { |
||||||
|
this.addVertex(from); |
||||||
|
this.addVertex(to); |
||||||
|
neighbors.get(from).add(to); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Remove an edge from the graph. Nothing happens if no such edge. |
||||||
|
* @throws IllegalArgumentException if either vertex doesn't exist. |
||||||
|
*/ |
||||||
|
public void remove(V from, V to) { |
||||||
|
if (!(this.containsVertex(from) && this.containsVertex(to))) { |
||||||
|
throw new IllegalArgumentException("Nonexistent vertex"); |
||||||
|
} |
||||||
|
neighbors.get(from).remove(to); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Report (as a Map) the out-degree of each vertex. |
||||||
|
*/ |
||||||
|
public Map<V, Integer> outDegree() { |
||||||
|
Map<V, Integer> result = new HashMap<V, Integer>(); |
||||||
|
for (V vertex : neighbors.keySet()) { |
||||||
|
result.put(vertex, neighbors.get(vertex).size()); |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Report (as a Map) the in-degree of each vertex. |
||||||
|
*/ |
||||||
|
public Map<V,Integer> inDegree() { |
||||||
|
Map<V, Integer> result = new HashMap<V, Integer>(); |
||||||
|
for (V vertex : neighbors.keySet()) { |
||||||
|
result.put(vertex, 0); // all in-degrees are 0
|
||||||
|
} |
||||||
|
for (V from : neighbors.keySet()) { |
||||||
|
for (V to : neighbors.get(from)) { |
||||||
|
result.put(to, result.get(to) + 1); // increment in-degree
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Report (as a List) the topological sort of the vertices; null for no such sort. |
||||||
|
*/ |
||||||
|
public List<V> topologicalSort() { |
||||||
|
Map<V, Integer> degree = inDegree(); |
||||||
|
|
||||||
|
// determine all vertices with zero in-degree
|
||||||
|
Stack<V> zeroVertices = new Stack<V>(); // stack as good as any here
|
||||||
|
for (V v : degree.keySet()) { |
||||||
|
if (degree.get(v) == 0) { |
||||||
|
zeroVertices.push(v); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// determine the topological order
|
||||||
|
List<V> result = new ArrayList<V>(); |
||||||
|
while (!zeroVertices.isEmpty()) { |
||||||
|
V vertex = zeroVertices.pop(); // choose a vertex with zero in-degree
|
||||||
|
result.add(vertex); // vertex 'v' is next in topological order
|
||||||
|
// "remove" vertex 'v' by updating its neighbors
|
||||||
|
for (V neighbor : neighbors.get(vertex)) { |
||||||
|
degree.put(neighbor, degree.get(neighbor) - 1); |
||||||
|
// remember any vertices that now have zero in-degree
|
||||||
|
if (degree.get(neighbor) == 0) { |
||||||
|
zeroVertices.push(neighbor); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// check that we have used the entire graph (if not, there was a cycle)
|
||||||
|
if (result.size() != neighbors.size()) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Report (as a List) the reverse topological sort of the vertices; null for no such sort. |
||||||
|
*/ |
||||||
|
public List<V> reverseTopologicalSort() { |
||||||
|
List<V> list = topologicalSort(); |
||||||
|
if (list == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
Collections.reverse(list); |
||||||
|
|
||||||
|
return list; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* True if graph is a dag (directed acyclic graph). |
||||||
|
*/ |
||||||
|
public boolean isDag () { |
||||||
|
return topologicalSort() != null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* String representation of graph. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
StringBuffer sb = new StringBuffer(); |
||||||
|
for (V vertex : neighbors.keySet()) { |
||||||
|
sb.append("\n " + vertex + " -> " + neighbors.get(vertex)); |
||||||
|
} |
||||||
|
|
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,72 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,122 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||||
|
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
*/ |
||||||
|
package 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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,72 @@ |
|||||||
|
<?xml version="1.0"?> |
||||||
|
<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> |
||||||
|
<artifactId>pom</artifactId> |
||||||
|
<version>0.1-SNAPSHOT</version> |
||||||
|
<packaging>pom</packaging> |
||||||
|
<name>PF4J</name> |
||||||
|
<description>Plugin Framework for Java</description> |
||||||
|
|
||||||
|
<licenses> |
||||||
|
<license> |
||||||
|
<name>The Apache Software License, Version 2.0</name> |
||||||
|
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> |
||||||
|
<distribution>repo</distribution> |
||||||
|
</license> |
||||||
|
</licenses> |
||||||
|
|
||||||
|
<scm> |
||||||
|
<connection>scm:git:https://github.com/decebals/pf4j.git</connection> |
||||||
|
<developerConnection>scm:git:https://github.com/decebals/pf4j.git</developerConnection> |
||||||
|
<url>git@github.com/decebals/pf4j.git</url> |
||||||
|
</scm> |
||||||
|
|
||||||
|
<properties> |
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||||
|
<slf4j.version>1.6.4</slf4j.version> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<build> |
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-compiler-plugin</artifactId> |
||||||
|
<version>2.3.2</version> |
||||||
|
<configuration> |
||||||
|
<source>1.6</source> |
||||||
|
<target>1.6</target> |
||||||
|
<optimize>true</optimize> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-release-plugin</artifactId> |
||||||
|
<version>2.3.2</version> |
||||||
|
<configuration> |
||||||
|
<goals>deploy</goals> |
||||||
|
<autoVersionSubmodules>true</autoVersionSubmodules> |
||||||
|
<tagNameFormat>release-@{project.version}</tagNameFormat> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<artifactId>maven-resources-plugin</artifactId> |
||||||
|
<version>2.4.3</version> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<artifactId>maven-jar-plugin</artifactId> |
||||||
|
<version>2.3.1</version> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
|
||||||
|
<modules> |
||||||
|
<module>pf4j</module> |
||||||
|
<module>demo</module> |
||||||
|
</modules> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,24 @@ |
|||||||
|
#!/bin/sh |
||||||
|
|
||||||
|
# |
||||||
|
# This script creates and run the pf4j demo. |
||||||
|
# |
||||||
|
|
||||||
|
# create artifacts using maven |
||||||
|
mvn clean package |
||||||
|
|
||||||
|
# create demo-dist folder |
||||||
|
rm -fr demo-dist |
||||||
|
mkdir demo-dist |
||||||
|
mkdir demo-dist/plugins |
||||||
|
|
||||||
|
# copy artifacts to demo-dist folder |
||||||
|
cp -r demo/app/target/pf4j-demo-*/* demo-dist/ |
||||||
|
cp demo/plugin1/target/pf4j-demo-plugin1-*.zip demo-dist/plugins/ |
||||||
|
cp demo/plugin2/target/pf4j-demo-plugin2-*.zip demo-dist/plugins/ |
||||||
|
|
||||||
|
# run demo |
||||||
|
cd demo-dist |
||||||
|
java -jar pf4j-demo-app-*.jar |
||||||
|
cd - |
||||||
|
|
Loading…
Reference in new issue