mirror of https://github.com/pf4j/pf4j.git
Decebal Suiu
11 years ago
13 changed files with 1022 additions and 849 deletions
@ -1,145 +1,67 @@
|
||||
/* |
||||
* Copyright 2012 Decebal Suiu |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
*/ |
||||
package ro.fortsoft.pf4j.demo; |
||||
|
||||
import java.io.File; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.NoSuchElementException; |
||||
|
||||
import org.apache.commons.lang.StringUtils; |
||||
|
||||
import ro.fortsoft.pf4j.DefaultPluginManager; |
||||
import ro.fortsoft.pf4j.PluginClasspath; |
||||
import ro.fortsoft.pf4j.PluginDescriptorFinder; |
||||
import ro.fortsoft.pf4j.PluginManager; |
||||
import ro.fortsoft.pf4j.PropertiesPluginDescriptorFinder; |
||||
import ro.fortsoft.pf4j.demo.api.Greeting; |
||||
|
||||
/** |
||||
* A boot class that start the demo. |
||||
* |
||||
* @author Decebal Suiu |
||||
*/ |
||||
public class Boot { |
||||
|
||||
public static void main(String[] args) { |
||||
// print logo
|
||||
printLogo(); |
||||
|
||||
// create the plugin manager
|
||||
final PluginManager pluginManager = createPluginManager(); |
||||
|
||||
// load and start (active/resolved) the plugins
|
||||
pluginManager.loadPlugins(); |
||||
pluginManager.startPlugins(); |
||||
|
||||
// retrieves the extensions for Greeting extension point
|
||||
List<Greeting> greetings = pluginManager.getExtensions(Greeting.class); |
||||
for (Greeting greeting : greetings) { |
||||
System.out.println(">>> " + greeting.getGreeting()); |
||||
} |
||||
|
||||
// stop the plugins
|
||||
pluginManager.stopPlugins(); |
||||
/* |
||||
Runtime.getRuntime().addShutdownHook(new Thread() { |
||||
|
||||
@Override |
||||
public void run() { |
||||
pluginManager.stopPlugins(); |
||||
} |
||||
|
||||
}); |
||||
*/ |
||||
} |
||||
|
||||
private static PluginManager createPluginManager() { |
||||
// retrieves the pf4j runtime mode
|
||||
String modeAsString = System.getProperty("pf4j.mode", RuntimeMode.PROD.toString()); |
||||
RuntimeMode mode = RuntimeMode.byName(modeAsString); |
||||
|
||||
System.out.println("PF4J runtime mode: '" + mode + "'"); |
||||
|
||||
// create the plugin manager depending on runtime mode
|
||||
PluginManager pluginManager = null; |
||||
if (mode == RuntimeMode.PROD) { |
||||
pluginManager = new DefaultPluginManager(); |
||||
} else if (mode == RuntimeMode.DEV) { |
||||
// run from eclipse IDE (for example)
|
||||
pluginManager = new DefaultPluginManager(new File("../plugins")) { |
||||
|
||||
@Override |
||||
protected PluginClasspath createPluginClasspath() { |
||||
PluginClasspath pluginClasspath = super.createPluginClasspath(); |
||||
// modify plugin classes
|
||||
List<String> pluginClasses = pluginClasspath.getClassesDirectories(); |
||||
pluginClasses.clear(); |
||||
pluginClasses.add("target/classes"); |
||||
|
||||
return pluginClasspath; |
||||
} |
||||
|
||||
@Override |
||||
protected PluginDescriptorFinder createPluginDescriptorFinder() { |
||||
return new PropertiesPluginDescriptorFinder(); |
||||
} |
||||
|
||||
}; |
||||
} |
||||
|
||||
return pluginManager; |
||||
} |
||||
|
||||
private static void printLogo() { |
||||
System.out.println(StringUtils.repeat("#", 40)); |
||||
System.out.println(StringUtils.center("PF4J-DEMO", 40)); |
||||
System.out.println(StringUtils.repeat("#", 40)); |
||||
} |
||||
|
||||
public enum RuntimeMode { |
||||
|
||||
DEV("dev"), // development
|
||||
PROD("prod"); // production
|
||||
|
||||
private final String name; |
||||
|
||||
private static final Map<String, RuntimeMode> map = new HashMap<String, RuntimeMode>(); |
||||
|
||||
static { |
||||
for (RuntimeMode mode : RuntimeMode.values()) { |
||||
map.put(mode.name, mode); |
||||
} |
||||
} |
||||
|
||||
private RuntimeMode(final String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return name; |
||||
} |
||||
|
||||
public static RuntimeMode byName(String name) { |
||||
if (map.containsKey(name)) { |
||||
return map.get(name); |
||||
} |
||||
|
||||
throw new NoSuchElementException("Cannot found PF4J runtime mode with name '" + name + "'"); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
/* |
||||
* Copyright 2012 Decebal Suiu |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
*/ |
||||
package ro.fortsoft.pf4j.demo; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.apache.commons.lang.StringUtils; |
||||
|
||||
import ro.fortsoft.pf4j.DefaultPluginManager; |
||||
import ro.fortsoft.pf4j.PluginManager; |
||||
import ro.fortsoft.pf4j.demo.api.Greeting; |
||||
|
||||
/** |
||||
* A boot class that start the demo. |
||||
* |
||||
* @author Decebal Suiu |
||||
*/ |
||||
public class Boot { |
||||
|
||||
public static void main(String[] args) { |
||||
// print logo
|
||||
printLogo(); |
||||
|
||||
// create the plugin manager
|
||||
final PluginManager pluginManager = new DefaultPluginManager(); |
||||
|
||||
// load and start (active/resolved) the plugins
|
||||
pluginManager.loadPlugins(); |
||||
pluginManager.startPlugins(); |
||||
|
||||
// retrieves the extensions for Greeting extension point
|
||||
List<Greeting> greetings = pluginManager.getExtensions(Greeting.class); |
||||
for (Greeting greeting : greetings) { |
||||
System.out.println(">>> " + greeting.getGreeting()); |
||||
} |
||||
|
||||
// stop the plugins
|
||||
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)); |
||||
} |
||||
|
||||
} |
||||
|
@ -1,131 +1,123 @@
|
||||
<?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>ro.fortsoft.pf4j.demo</groupId> |
||||
<artifactId>pf4j-demo-plugins</artifactId> |
||||
<version>0.6-SNAPSHOT</version> |
||||
</parent> |
||||
|
||||
<modelVersion>4.0.0</modelVersion> |
||||
<artifactId>pf4j-demo-plugin1</artifactId> |
||||
<version>0.6-SNAPSHOT</version> |
||||
<packaging>jar</packaging> |
||||
<name>Demo Plugin #1</name> |
||||
|
||||
<properties> |
||||
<plugin.id>welcome-plugin</plugin.id> |
||||
<plugin.class>ro.fortsoft.pf4j.demo.welcome.WelcomePlugin</plugin.class> |
||||
<plugin.version>0.0.1</plugin.version> |
||||
<plugin.provider>Decebal Suiu</plugin.provider> |
||||
<plugin.dependencies /> |
||||
</properties> |
||||
|
||||
<build> |
||||
<plugins> |
||||
<!-- DOESN'T WORK WITH MAVEN 3 (I defined the plugin metadata in properties section) |
||||
<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> |
||||
<version>2.4</version> |
||||
<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>ro.fortsoft.pf4j</groupId> |
||||
<artifactId>pf4j</artifactId> |
||||
<version>${project.version}</version> |
||||
<scope>provided</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>ro.fortsoft.pf4j.demo</groupId> |
||||
<artifactId>pf4j-demo-api</artifactId> |
||||
<version>${project.version}</version> |
||||
<scope>provided</scope> |
||||
</dependency> |
||||
</dependencies> |
||||
|
||||
</project> |
||||
<?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>ro.fortsoft.pf4j.demo</groupId> |
||||
<artifactId>pf4j-demo-plugins</artifactId> |
||||
<version>0.6-SNAPSHOT</version> |
||||
</parent> |
||||
|
||||
<modelVersion>4.0.0</modelVersion> |
||||
<artifactId>pf4j-demo-plugin1</artifactId> |
||||
<version>0.6-SNAPSHOT</version> |
||||
<packaging>jar</packaging> |
||||
<name>Demo Plugin #1</name> |
||||
|
||||
<properties> |
||||
<plugin.id>welcome-plugin</plugin.id> |
||||
<plugin.class>ro.fortsoft.pf4j.demo.welcome.WelcomePlugin</plugin.class> |
||||
<plugin.version>0.0.1</plugin.version> |
||||
<plugin.provider>Decebal Suiu</plugin.provider> |
||||
<plugin.dependencies /> |
||||
</properties> |
||||
|
||||
<build> |
||||
<plugins> |
||||
<!-- DOESN'T WORK WITH MAVEN 3 (I defined the plugin metadata in properties section) |
||||
<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> |
||||
<version>2.4</version> |
||||
<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>commons-lang</groupId> |
||||
<artifactId>commons-lang</artifactId> |
||||
<version>2.6</version> |
||||
</dependency> |
||||
</dependencies> |
||||
|
||||
</project> |
||||
|
@ -1,131 +1,115 @@
|
||||
<?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>ro.fortsoft.pf4j.demo</groupId> |
||||
<artifactId>pf4j-demo-plugins</artifactId> |
||||
<version>0.6-SNAPSHOT</version> |
||||
</parent> |
||||
|
||||
<modelVersion>4.0.0</modelVersion> |
||||
<artifactId>pf4j-demo-plugin2</artifactId> |
||||
<version>0.6-SNAPSHOT</version> |
||||
<packaging>jar</packaging> |
||||
<name>Demo Plugin #2</name> |
||||
|
||||
<properties> |
||||
<plugin.id>hello-plugin</plugin.id> |
||||
<plugin.class>ro.fortsoft.pf4j.demo.hello.HelloPlugin</plugin.class> |
||||
<plugin.version>0.0.1</plugin.version> |
||||
<plugin.provider>Decebal Suiu</plugin.provider> |
||||
<plugin.dependencies /> |
||||
</properties> |
||||
|
||||
<build> |
||||
<plugins> |
||||
<!-- DOESN'T WORK WITH MAVEN 3 (I defined the plugin metadata in properties section) |
||||
<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> |
||||
<version>2.4</version> |
||||
<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>ro.fortsoft.pf4j</groupId> |
||||
<artifactId>pf4j</artifactId> |
||||
<version>${project.version}</version> |
||||
<scope>provided</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>ro.fortsoft.pf4j.demo</groupId> |
||||
<artifactId>pf4j-demo-api</artifactId> |
||||
<version>${project.version}</version> |
||||
<scope>provided</scope> |
||||
</dependency> |
||||
</dependencies> |
||||
|
||||
</project> |
||||
<?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>ro.fortsoft.pf4j.demo</groupId> |
||||
<artifactId>pf4j-demo-plugins</artifactId> |
||||
<version>0.6-SNAPSHOT</version> |
||||
</parent> |
||||
|
||||
<modelVersion>4.0.0</modelVersion> |
||||
<artifactId>pf4j-demo-plugin2</artifactId> |
||||
<version>0.6-SNAPSHOT</version> |
||||
<packaging>jar</packaging> |
||||
<name>Demo Plugin #2</name> |
||||
|
||||
<properties> |
||||
<plugin.id>hello-plugin</plugin.id> |
||||
<plugin.class>ro.fortsoft.pf4j.demo.hello.HelloPlugin</plugin.class> |
||||
<plugin.version>0.0.1</plugin.version> |
||||
<plugin.provider>Decebal Suiu</plugin.provider> |
||||
<plugin.dependencies /> |
||||
</properties> |
||||
|
||||
<build> |
||||
<plugins> |
||||
<!-- DOESN'T WORK WITH MAVEN 3 (I defined the plugin metadata in properties section) |
||||
<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> |
||||
<version>2.4</version> |
||||
<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> |
||||
|
||||
</project> |
||||
|
@ -1,37 +1,106 @@
|
||||
<?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>ro.fortsoft.pf4j.demo</groupId> |
||||
<artifactId>pf4j-demo-parent</artifactId> |
||||
<version>0.6-SNAPSHOT</version> |
||||
</parent> |
||||
|
||||
<modelVersion>4.0.0</modelVersion> |
||||
<groupId>ro.fortsoft.pf4j.demo</groupId> |
||||
<artifactId>pf4j-demo-plugins</artifactId> |
||||
<version>0.6-SNAPSHOT</version> |
||||
<packaging>pom</packaging> |
||||
<name>Demo Plugins Parent</name> |
||||
|
||||
<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> |
||||
</build> |
||||
|
||||
<modules> |
||||
<module>plugin1</module> |
||||
<module>plugin2</module> |
||||
</modules> |
||||
|
||||
</project> |
||||
<?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>ro.fortsoft.pf4j.demo</groupId> |
||||
<artifactId>pf4j-demo-parent</artifactId> |
||||
<version>0.6-SNAPSHOT</version> |
||||
</parent> |
||||
|
||||
<modelVersion>4.0.0</modelVersion> |
||||
<groupId>ro.fortsoft.pf4j.demo</groupId> |
||||
<artifactId>pf4j-demo-plugins</artifactId> |
||||
<version>0.6-SNAPSHOT</version> |
||||
<packaging>pom</packaging> |
||||
<name>Demo Plugins Parent</name> |
||||
|
||||
<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> |
||||
<artifactId>maven-dependency-plugin</artifactId> |
||||
<executions> |
||||
<execution> |
||||
<phase>process-sources</phase> |
||||
<goals> |
||||
<goal>copy-dependencies</goal> |
||||
</goals> |
||||
<configuration> |
||||
<outputDirectory>${project.build.directory}/lib</outputDirectory> |
||||
<excludeScope>provided</excludeScope> |
||||
</configuration> |
||||
</execution> |
||||
</executions> |
||||
</plugin> |
||||
</plugins> |
||||
|
||||
<pluginManagement> |
||||
<plugins> |
||||
<plugin> |
||||
<groupId>org.eclipse.m2e</groupId> |
||||
<artifactId>lifecycle-mapping</artifactId> |
||||
<version>1.0.0</version> |
||||
<configuration> |
||||
<lifecycleMappingMetadata> |
||||
<pluginExecutions> |
||||
<pluginExecution> |
||||
<pluginExecutionFilter> |
||||
<groupId>org.apache.maven.plugins</groupId> |
||||
<artifactId>maven-dependency-plugin</artifactId> |
||||
<versionRange>[2.0,)</versionRange> |
||||
<goals> |
||||
<goal>copy-dependencies</goal> |
||||
</goals> |
||||
</pluginExecutionFilter> |
||||
<action> |
||||
<!-- |
||||
<execute/> |
||||
--> |
||||
<execute> |
||||
<runOnIncremental>true</runOnIncremental> |
||||
<runOnConfiguration>true</runOnConfiguration> |
||||
</execute> |
||||
</action> |
||||
</pluginExecution> |
||||
</pluginExecutions> |
||||
</lifecycleMappingMetadata> |
||||
</configuration> |
||||
</plugin> |
||||
</plugins> |
||||
</pluginManagement> |
||||
</build> |
||||
|
||||
<modules> |
||||
<module>plugin1</module> |
||||
<module>plugin2</module> |
||||
</modules> |
||||
|
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>ro.fortsoft.pf4j</groupId> |
||||
<artifactId>pf4j</artifactId> |
||||
<version>${project.version}</version> |
||||
<scope>provided</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>ro.fortsoft.pf4j.demo</groupId> |
||||
<artifactId>pf4j-demo-api</artifactId> |
||||
<version>${project.version}</version> |
||||
<scope>provided</scope> |
||||
</dependency> |
||||
</dependencies> |
||||
|
||||
</project> |
||||
|
@ -1,392 +1,460 @@
|
||||
/* |
||||
* Copyright 2012 Decebal Suiu |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
*/ |
||||
package ro.fortsoft.pf4j; |
||||
|
||||
import java.io.File; |
||||
import java.io.FileFilter; |
||||
import java.io.IOException; |
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import ro.fortsoft.pf4j.util.AndFileFilter; |
||||
import ro.fortsoft.pf4j.util.CompoundClassLoader; |
||||
import ro.fortsoft.pf4j.util.DirectoryFileFilter; |
||||
import ro.fortsoft.pf4j.util.FileUtils; |
||||
import ro.fortsoft.pf4j.util.HiddenFilter; |
||||
import ro.fortsoft.pf4j.util.NotFileFilter; |
||||
import ro.fortsoft.pf4j.util.Unzip; |
||||
import ro.fortsoft.pf4j.util.ZipFileFilter; |
||||
|
||||
/** |
||||
* 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 final ExtensionFinder extensionFinder; |
||||
|
||||
private final PluginDescriptorFinder pluginDescriptorFinder; |
||||
|
||||
private final PluginClasspath pluginClasspath; |
||||
|
||||
/** |
||||
* A map of plugins this manager is responsible for (the key is the 'pluginId'). |
||||
*/ |
||||
private Map<String, PluginWrapper> 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<PluginWrapper> unresolvedPlugins; |
||||
|
||||
/** |
||||
* A list with resolved plugins (resolved dependency). |
||||
*/ |
||||
private List<PluginWrapper> resolvedPlugins; |
||||
|
||||
/** |
||||
* A list with started plugins. |
||||
*/ |
||||
private List<PluginWrapper> startedPlugins; |
||||
|
||||
private List<String> enabledPlugins; |
||||
private List<String> disabledPlugins; |
||||
|
||||
/** |
||||
* A compound class loader of resolved plugins. |
||||
*/ |
||||
protected CompoundClassLoader compoundClassLoader; |
||||
|
||||
/** |
||||
* 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, PluginWrapper>(); |
||||
pluginClassLoaders = new HashMap<String, PluginClassLoader>(); |
||||
pathToIdMap = new HashMap<String, String>(); |
||||
unresolvedPlugins = new ArrayList<PluginWrapper>(); |
||||
resolvedPlugins = new ArrayList<PluginWrapper>(); |
||||
startedPlugins = new ArrayList<PluginWrapper>(); |
||||
disabledPlugins = new ArrayList<String>(); |
||||
compoundClassLoader = new CompoundClassLoader(); |
||||
|
||||
pluginClasspath = createPluginClasspath(); |
||||
pluginDescriptorFinder = createPluginDescriptorFinder(); |
||||
extensionFinder = createExtensionFinder(); |
||||
|
||||
try { |
||||
// create a list with plugin identifiers that should be only accepted by this manager (whitelist from plugins/enabled.txt file)
|
||||
enabledPlugins = FileUtils.readLines(new File(pluginsDirectory, "enabled.txt"), true); |
||||
log.info("Enabled plugins: {}", enabledPlugins); |
||||
|
||||
// create a list with plugin identifiers that should not be accepted by this manager (blacklist from plugins/disabled.txt file)
|
||||
disabledPlugins = FileUtils.readLines(new File(pluginsDirectory, "disabled.txt"), true); |
||||
log.info("Disabled plugins: {}", disabledPlugins); |
||||
} catch (IOException e) { |
||||
log.error(e.getMessage(), e); |
||||
} |
||||
|
||||
System.setProperty("pf4j.pluginsDir", pluginsDirectory.getAbsolutePath()); |
||||
} |
||||
|
||||
@Override |
||||
public List<PluginWrapper> getPlugins() { |
||||
return new ArrayList<PluginWrapper>(plugins.values()); |
||||
} |
||||
|
||||
@Override |
||||
public List<PluginWrapper> getResolvedPlugins() { |
||||
return resolvedPlugins; |
||||
} |
||||
|
||||
public PluginWrapper getPlugin(String pluginId) { |
||||
return plugins.get(pluginId); |
||||
} |
||||
|
||||
@Override |
||||
public List<PluginWrapper> getUnresolvedPlugins() { |
||||
return unresolvedPlugins; |
||||
} |
||||
|
||||
@Override |
||||
public List<PluginWrapper> getStartedPlugins() { |
||||
return startedPlugins; |
||||
} |
||||
|
||||
/** |
||||
* Start all active plugins. |
||||
*/ |
||||
@Override |
||||
public void startPlugins() { |
||||
for (PluginWrapper pluginWrapper : resolvedPlugins) { |
||||
try { |
||||
log.info("Start plugin '{}'", pluginWrapper.getDescriptor().getPluginId()); |
||||
pluginWrapper.getPlugin().start(); |
||||
pluginWrapper.setPluginState(PluginState.STARTED); |
||||
startedPlugins.add(pluginWrapper); |
||||
} catch (PluginException e) { |
||||
log.error(e.getMessage(), e); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Stop all active plugins. |
||||
*/ |
||||
@Override |
||||
public void stopPlugins() { |
||||
// stop started plugins in reverse order
|
||||
Collections.reverse(startedPlugins); |
||||
for (PluginWrapper pluginWrapper : startedPlugins) { |
||||
try { |
||||
log.info("Stop plugin '{}'", pluginWrapper.getDescriptor().getPluginId()); |
||||
pluginWrapper.getPlugin().stop(); |
||||
pluginWrapper.setPluginState(PluginState.STOPPED); |
||||
} catch (PluginException e) { |
||||
log.error(e.getMessage(), e); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Load plugins. |
||||
*/ |
||||
@Override |
||||
public void loadPlugins() { |
||||
log.debug("Lookup plugins in '{}'", pluginsDirectory.getAbsolutePath()); |
||||
// check for plugins directory
|
||||
if (!pluginsDirectory.exists() || !pluginsDirectory.isDirectory()) { |
||||
log.error("No '{}' directory", pluginsDirectory.getAbsolutePath()); |
||||
return; |
||||
} |
||||
|
||||
// expand all plugin archives
|
||||
FileFilter zipFilter = new ZipFileFilter(); |
||||
File[] zipFiles = pluginsDirectory.listFiles(zipFilter); |
||||
for (File zipFile : zipFiles) { |
||||
try { |
||||
expandPluginArchive(zipFile); |
||||
} catch (IOException e) { |
||||
log.error(e.getMessage(), e); |
||||
} |
||||
} |
||||
|
||||
// check for no plugins
|
||||
List<FileFilter> filterList = new ArrayList<FileFilter>(); |
||||
filterList.add(new DirectoryFileFilter()); |
||||
filterList.add(new NotFileFilter(createHiddenPluginFilter())); |
||||
FileFilter pluginsFilter = new AndFileFilter(filterList); |
||||
File[] directories = pluginsDirectory.listFiles(pluginsFilter); |
||||
if (directories.length == 0) { |
||||
log.info("No plugins"); |
||||
return; |
||||
} |
||||
|
||||
// load any plugin from plugins directory
|
||||
for (File directory : directories) { |
||||
try { |
||||
loadPlugin(directory); |
||||
} catch (PluginException e) { |
||||
log.error(e.getMessage(), e); |
||||
} |
||||
} |
||||
|
||||
// resolve 'unresolvedPlugins'
|
||||
try { |
||||
resolvePlugins(); |
||||
} catch (PluginException e) { |
||||
log.error(e.getMessage(), e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Get plugin class loader for this path. |
||||
*/ |
||||
@Override |
||||
public PluginClassLoader getPluginClassLoader(String pluginId) { |
||||
return pluginClassLoaders.get(pluginId); |
||||
} |
||||
|
||||
@Override |
||||
public <T> List<T> getExtensions(Class<T> type) { |
||||
List<ExtensionWrapper<T>> extensionsWrapper = extensionFinder.find(type); |
||||
List<T> extensions = new ArrayList<T>(extensionsWrapper.size()); |
||||
for (ExtensionWrapper<T> extensionWrapper : extensionsWrapper) { |
||||
extensions.add(extensionWrapper.getInstance()); |
||||
} |
||||
|
||||
return extensions; |
||||
} |
||||
|
||||
/** |
||||
* Retrieves the {@link PluginWrapper} that loaded the given class 'clazz'. |
||||
*/ |
||||
public PluginWrapper whichPlugin(Class<?> clazz) { |
||||
ClassLoader classLoader = clazz.getClassLoader(); |
||||
for (PluginWrapper plugin : resolvedPlugins) { |
||||
if (plugin.getPluginClassLoader() == classLoader) { |
||||
return plugin; |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* Add the possibility to override the PluginDescriptorFinder. |
||||
*/ |
||||
protected PluginDescriptorFinder createPluginDescriptorFinder() { |
||||
return new DefaultPluginDescriptorFinder(pluginClasspath); |
||||
} |
||||
|
||||
/** |
||||
* Add the possibility to override the ExtensionFinder. |
||||
*/ |
||||
protected ExtensionFinder createExtensionFinder() { |
||||
return new DefaultExtensionFinder(compoundClassLoader); |
||||
} |
||||
|
||||
/** |
||||
* Add the possibility to override the PluginClassPath. |
||||
*/ |
||||
protected PluginClasspath createPluginClasspath() { |
||||
return new PluginClasspath(); |
||||
} |
||||
|
||||
protected boolean isPluginDisabled(String pluginId) { |
||||
if (enabledPlugins.isEmpty()) { |
||||
return disabledPlugins.contains(pluginId); |
||||
} |
||||
|
||||
return !enabledPlugins.contains(pluginId); |
||||
} |
||||
|
||||
protected FileFilter createHiddenPluginFilter() { |
||||
return new HiddenFilter(); |
||||
} |
||||
|
||||
private void loadPlugin(File pluginDirectory) throws PluginException { |
||||
// try to load the plugin
|
||||
String pluginName = pluginDirectory.getName(); |
||||
String pluginPath = "/".concat(pluginName); |
||||
|
||||
// test for plugin duplication
|
||||
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 '{}' for plugin '{}'", pluginClassName, pluginPath); |
||||
|
||||
// test for disabled plugin
|
||||
if (isPluginDisabled(pluginDescriptor.getPluginId())) { |
||||
log.info("Plugin '{}' is disabled", pluginPath); |
||||
return; |
||||
} |
||||
|
||||
// load plugin
|
||||
log.debug("Loading plugin '{}'", pluginPath); |
||||
PluginLoader pluginLoader = new PluginLoader(this, pluginDescriptor, pluginDirectory, pluginClasspath); |
||||
pluginLoader.load(); |
||||
log.debug("Loaded plugin '{}'", pluginPath); |
||||
|
||||
// create the plugin wrapper
|
||||
log.debug("Creating wrapper for plugin '{}'", pluginPath); |
||||
PluginWrapper pluginWrapper = new PluginWrapper(pluginDescriptor, pluginPath, pluginLoader.getPluginClassLoader()); |
||||
log.debug("Created wrapper '{}' for plugin '{}'", pluginWrapper, pluginPath); |
||||
|
||||
String pluginId = pluginDescriptor.getPluginId(); |
||||
|
||||
// add plugin to the list with plugins
|
||||
plugins.put(pluginId, pluginWrapper); |
||||
unresolvedPlugins.add(pluginWrapper); |
||||
|
||||
// add plugin class loader to the list with class loaders
|
||||
PluginClassLoader pluginClassLoader = pluginLoader.getPluginClassLoader(); |
||||
pluginClassLoaders.put(pluginId, pluginClassLoader); |
||||
} |
||||
|
||||
private void expandPluginArchive(File pluginArchiveFile) throws IOException { |
||||
String fileName = pluginArchiveFile.getName(); |
||||
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 '{}' in '{}'", pluginArchiveFile, pluginDirectory); |
||||
// create directory for plugin
|
||||
pluginDirectory.mkdirs(); |
||||
|
||||
// expand '.zip' file
|
||||
Unzip unzip = new Unzip(); |
||||
unzip.setSource(pluginArchiveFile); |
||||
unzip.setDestination(pluginDirectory); |
||||
unzip.extract(); |
||||
} |
||||
} |
||||
|
||||
private void resolvePlugins() throws PluginException { |
||||
resolveDependencies(); |
||||
} |
||||
|
||||
private void resolveDependencies() throws PluginException { |
||||
DependencyResolver dependencyResolver = new DependencyResolver(unresolvedPlugins); |
||||
resolvedPlugins = dependencyResolver.getSortedPlugins(); |
||||
for (PluginWrapper pluginWrapper : resolvedPlugins) { |
||||
unresolvedPlugins.remove(pluginWrapper); |
||||
compoundClassLoader.addLoader(pluginWrapper.getPluginClassLoader()); |
||||
log.info("Plugin '{}' resolved", pluginWrapper.getDescriptor().getPluginId()); |
||||
} |
||||
} |
||||
|
||||
} |
||||
/* |
||||
* Copyright 2012 Decebal Suiu |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
*/ |
||||
package ro.fortsoft.pf4j; |
||||
|
||||
import java.io.File; |
||||
import java.io.FileFilter; |
||||
import java.io.IOException; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import ro.fortsoft.pf4j.util.AndFileFilter; |
||||
import ro.fortsoft.pf4j.util.CompoundClassLoader; |
||||
import ro.fortsoft.pf4j.util.DirectoryFileFilter; |
||||
import ro.fortsoft.pf4j.util.FileUtils; |
||||
import ro.fortsoft.pf4j.util.HiddenFilter; |
||||
import ro.fortsoft.pf4j.util.NotFileFilter; |
||||
import ro.fortsoft.pf4j.util.Unzip; |
||||
import ro.fortsoft.pf4j.util.ZipFileFilter; |
||||
|
||||
/** |
||||
* Default implementation of the PluginManager interface. |
||||
* |
||||
* @author Decebal Suiu |
||||
*/ |
||||
public class DefaultPluginManager implements PluginManager { |
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(DefaultPluginManager.class); |
||||
|
||||
public static final String DEFAULT_PLUGINS_DIRECTORY = "plugins"; |
||||
public static final String DEVELOPMENT_PLUGINS_DIRECTORY = "../plugins"; |
||||
|
||||
/** |
||||
* The plugins repository. |
||||
*/ |
||||
private File pluginsDirectory; |
||||
|
||||
private ExtensionFinder extensionFinder; |
||||
|
||||
private PluginDescriptorFinder pluginDescriptorFinder; |
||||
|
||||
private PluginClasspath pluginClasspath; |
||||
|
||||
/** |
||||
* A map of plugins this manager is responsible for (the key is the 'pluginId'). |
||||
*/ |
||||
private Map<String, PluginWrapper> 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<PluginWrapper> unresolvedPlugins; |
||||
|
||||
/** |
||||
* A list with resolved plugins (resolved dependency). |
||||
*/ |
||||
private List<PluginWrapper> resolvedPlugins; |
||||
|
||||
/** |
||||
* A list with started plugins. |
||||
*/ |
||||
private List<PluginWrapper> startedPlugins; |
||||
|
||||
private List<String> enabledPlugins; |
||||
private List<String> disabledPlugins; |
||||
|
||||
/** |
||||
* A compound class loader of resolved plugins. |
||||
*/ |
||||
protected CompoundClassLoader compoundClassLoader; |
||||
|
||||
/** |
||||
* Cache value for the runtime mode. No need to re-read it because it wont change at |
||||
* runtime. |
||||
*/ |
||||
private RuntimeMode runtimeMode; |
||||
|
||||
/** |
||||
* The plugins directory is supplied by System.getProperty("pf4j.pluginsDir", "plugins"). |
||||
*/ |
||||
public DefaultPluginManager() { |
||||
this.pluginsDirectory = createPluginsDirectory(); |
||||
|
||||
initialize(); |
||||
} |
||||
|
||||
/** |
||||
* Constructs DefaultPluginManager which the given plugins directory. |
||||
* |
||||
* @param pluginsDirectory |
||||
* the directory to search for plugins |
||||
*/ |
||||
public DefaultPluginManager(File pluginsDirectory) { |
||||
this.pluginsDirectory = pluginsDirectory; |
||||
|
||||
initialize(); |
||||
} |
||||
|
||||
@Override |
||||
public List<PluginWrapper> getPlugins() { |
||||
return new ArrayList<PluginWrapper>(plugins.values()); |
||||
} |
||||
|
||||
@Override |
||||
public List<PluginWrapper> getResolvedPlugins() { |
||||
return resolvedPlugins; |
||||
} |
||||
|
||||
public PluginWrapper getPlugin(String pluginId) { |
||||
return plugins.get(pluginId); |
||||
} |
||||
|
||||
@Override |
||||
public List<PluginWrapper> getUnresolvedPlugins() { |
||||
return unresolvedPlugins; |
||||
} |
||||
|
||||
@Override |
||||
public List<PluginWrapper> getStartedPlugins() { |
||||
return startedPlugins; |
||||
} |
||||
|
||||
/** |
||||
* Start all active plugins. |
||||
*/ |
||||
@Override |
||||
public void startPlugins() { |
||||
for (PluginWrapper pluginWrapper : resolvedPlugins) { |
||||
try { |
||||
log.info("Start plugin '{}'", pluginWrapper.getDescriptor().getPluginId()); |
||||
pluginWrapper.getPlugin().start(); |
||||
pluginWrapper.setPluginState(PluginState.STARTED); |
||||
startedPlugins.add(pluginWrapper); |
||||
} catch (PluginException e) { |
||||
log.error(e.getMessage(), e); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Stop all active plugins. |
||||
*/ |
||||
@Override |
||||
public void stopPlugins() { |
||||
// stop started plugins in reverse order
|
||||
Collections.reverse(startedPlugins); |
||||
for (PluginWrapper pluginWrapper : startedPlugins) { |
||||
try { |
||||
log.info("Stop plugin '{}'", pluginWrapper.getDescriptor().getPluginId()); |
||||
pluginWrapper.getPlugin().stop(); |
||||
pluginWrapper.setPluginState(PluginState.STOPPED); |
||||
} catch (PluginException e) { |
||||
log.error(e.getMessage(), e); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Load plugins. |
||||
*/ |
||||
@Override |
||||
public void loadPlugins() { |
||||
log.debug("Lookup plugins in '{}'", pluginsDirectory.getAbsolutePath()); |
||||
// check for plugins directory
|
||||
if (!pluginsDirectory.exists() || !pluginsDirectory.isDirectory()) { |
||||
log.error("No '{}' directory", pluginsDirectory.getAbsolutePath()); |
||||
return; |
||||
} |
||||
|
||||
// expand all plugin archives
|
||||
FileFilter zipFilter = new ZipFileFilter(); |
||||
File[] zipFiles = pluginsDirectory.listFiles(zipFilter); |
||||
for (File zipFile : zipFiles) { |
||||
try { |
||||
expandPluginArchive(zipFile); |
||||
} catch (IOException e) { |
||||
log.error(e.getMessage(), e); |
||||
} |
||||
} |
||||
|
||||
// check for no plugins
|
||||
List<FileFilter> filterList = new ArrayList<FileFilter>(); |
||||
filterList.add(new DirectoryFileFilter()); |
||||
filterList.add(new NotFileFilter(createHiddenPluginFilter())); |
||||
FileFilter pluginsFilter = new AndFileFilter(filterList); |
||||
File[] directories = pluginsDirectory.listFiles(pluginsFilter); |
||||
log.debug("Possible plugins: {}", Arrays.asList(directories)); |
||||
if (directories.length == 0) { |
||||
log.info("No plugins"); |
||||
return; |
||||
} |
||||
|
||||
// load any plugin from plugins directory
|
||||
for (File directory : directories) { |
||||
try { |
||||
loadPlugin(directory); |
||||
} catch (PluginException e) { |
||||
log.error(e.getMessage(), e); |
||||
} |
||||
} |
||||
|
||||
// resolve 'unresolvedPlugins'
|
||||
try { |
||||
resolvePlugins(); |
||||
} catch (PluginException e) { |
||||
log.error(e.getMessage(), e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Get plugin class loader for this path. |
||||
*/ |
||||
@Override |
||||
public PluginClassLoader getPluginClassLoader(String pluginId) { |
||||
return pluginClassLoaders.get(pluginId); |
||||
} |
||||
|
||||
@Override |
||||
public <T> List<T> getExtensions(Class<T> type) { |
||||
List<ExtensionWrapper<T>> extensionsWrapper = extensionFinder.find(type); |
||||
List<T> extensions = new ArrayList<T>(extensionsWrapper.size()); |
||||
for (ExtensionWrapper<T> extensionWrapper : extensionsWrapper) { |
||||
extensions.add(extensionWrapper.getInstance()); |
||||
} |
||||
|
||||
return extensions; |
||||
} |
||||
|
||||
@Override |
||||
public RuntimeMode getRuntimeMode() { |
||||
if (runtimeMode == null) { |
||||
// retrieves the runtime mode from system
|
||||
String modeAsString = System.getProperty("pf4j.mode", RuntimeMode.DEPLOYMENT.toString()); |
||||
runtimeMode = RuntimeMode.byName(modeAsString); |
||||
|
||||
log.info("PF4J runtime mode: '" + runtimeMode + "'"); |
||||
|
||||
} |
||||
|
||||
return runtimeMode; |
||||
} |
||||
|
||||
/** |
||||
* Retrieves the {@link PluginWrapper} that loaded the given class 'clazz'. |
||||
*/ |
||||
public PluginWrapper whichPlugin(Class<?> clazz) { |
||||
ClassLoader classLoader = clazz.getClassLoader(); |
||||
for (PluginWrapper plugin : resolvedPlugins) { |
||||
if (plugin.getPluginClassLoader() == classLoader) { |
||||
return plugin; |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* Add the possibility to override the PluginDescriptorFinder. |
||||
* By default if getRuntimeMode() returns RuntimeMode.DEVELOPMENT than a |
||||
* PropertiesPluginDescriptorFinder is returned else this method returns |
||||
* DefaultPluginDescriptorFinder. |
||||
*/ |
||||
protected PluginDescriptorFinder createPluginDescriptorFinder() { |
||||
if (RuntimeMode.DEVELOPMENT.equals(getRuntimeMode())) { |
||||
return new PropertiesPluginDescriptorFinder(); |
||||
} |
||||
|
||||
return new DefaultPluginDescriptorFinder(pluginClasspath); |
||||
} |
||||
|
||||
/** |
||||
* Add the possibility to override the ExtensionFinder. |
||||
*/ |
||||
protected ExtensionFinder createExtensionFinder() { |
||||
return new DefaultExtensionFinder(compoundClassLoader); |
||||
} |
||||
|
||||
/** |
||||
* Add the possibility to override the PluginClassPath. |
||||
* By default if getRuntimeMode() returns RuntimeMode.DEVELOPMENT than a |
||||
* DevelopmentPluginClasspath is returned else this method returns |
||||
* PluginClasspath. |
||||
*/ |
||||
protected PluginClasspath createPluginClasspath() { |
||||
if (RuntimeMode.DEVELOPMENT.equals(getRuntimeMode())) { |
||||
return new DevelopmentPluginClasspath(); |
||||
} |
||||
|
||||
return new PluginClasspath(); |
||||
} |
||||
|
||||
protected boolean isPluginDisabled(String pluginId) { |
||||
if (enabledPlugins.isEmpty()) { |
||||
return disabledPlugins.contains(pluginId); |
||||
} |
||||
|
||||
return !enabledPlugins.contains(pluginId); |
||||
} |
||||
|
||||
protected FileFilter createHiddenPluginFilter() { |
||||
return new HiddenFilter(); |
||||
} |
||||
|
||||
/** |
||||
* Add the possibility to override the plugins directory. |
||||
* If a "pf4j.pluginsDir" system property is defined than this method returns |
||||
* that directory. |
||||
* If getRuntimeMode() returns RuntimeMode.DEVELOPMENT than a |
||||
* DEVELOPMENT_PLUGINS_DIRECTORY ("../plugins") is returned else this method returns |
||||
* DEFAULT_PLUGINS_DIRECTORY ("plugins"). |
||||
* @return |
||||
*/ |
||||
protected File createPluginsDirectory() { |
||||
String pluginsDir = System.getProperty("pf4j.pluginsDir"); |
||||
if (pluginsDir == null) { |
||||
if (RuntimeMode.DEVELOPMENT.equals(getRuntimeMode())) { |
||||
pluginsDir = DEVELOPMENT_PLUGINS_DIRECTORY; |
||||
} else { |
||||
pluginsDir = DEFAULT_PLUGINS_DIRECTORY; |
||||
} |
||||
} |
||||
|
||||
return new File(pluginsDir); |
||||
} |
||||
|
||||
private void initialize() { |
||||
plugins = new HashMap<String, PluginWrapper>(); |
||||
pluginClassLoaders = new HashMap<String, PluginClassLoader>(); |
||||
pathToIdMap = new HashMap<String, String>(); |
||||
unresolvedPlugins = new ArrayList<PluginWrapper>(); |
||||
resolvedPlugins = new ArrayList<PluginWrapper>(); |
||||
startedPlugins = new ArrayList<PluginWrapper>(); |
||||
disabledPlugins = new ArrayList<String>(); |
||||
compoundClassLoader = new CompoundClassLoader(); |
||||
|
||||
pluginClasspath = createPluginClasspath(); |
||||
pluginDescriptorFinder = createPluginDescriptorFinder(); |
||||
extensionFinder = createExtensionFinder(); |
||||
|
||||
try { |
||||
// create a list with plugin identifiers that should be only accepted by this manager (whitelist from plugins/enabled.txt file)
|
||||
enabledPlugins = FileUtils.readLines(new File(pluginsDirectory, "enabled.txt"), true); |
||||
log.info("Enabled plugins: {}", enabledPlugins); |
||||
|
||||
// create a list with plugin identifiers that should not be accepted by this manager (blacklist from plugins/disabled.txt file)
|
||||
disabledPlugins = FileUtils.readLines(new File(pluginsDirectory, "disabled.txt"), true); |
||||
log.info("Disabled plugins: {}", disabledPlugins); |
||||
} catch (IOException e) { |
||||
log.error(e.getMessage(), e); |
||||
} |
||||
|
||||
System.setProperty("pf4j.pluginsDir", pluginsDirectory.getAbsolutePath()); |
||||
} |
||||
|
||||
private void loadPlugin(File pluginDirectory) throws PluginException { |
||||
// try to load the plugin
|
||||
String pluginName = pluginDirectory.getName(); |
||||
String pluginPath = "/".concat(pluginName); |
||||
|
||||
// test for plugin duplication
|
||||
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 '{}' for plugin '{}'", pluginClassName, pluginPath); |
||||
|
||||
// test for disabled plugin
|
||||
if (isPluginDisabled(pluginDescriptor.getPluginId())) { |
||||
log.info("Plugin '{}' is disabled", pluginPath); |
||||
return; |
||||
} |
||||
|
||||
// load plugin
|
||||
log.debug("Loading plugin '{}'", pluginPath); |
||||
PluginLoader pluginLoader = new PluginLoader(this, pluginDescriptor, pluginDirectory, pluginClasspath); |
||||
pluginLoader.load(); |
||||
log.debug("Loaded plugin '{}'", pluginPath); |
||||
|
||||
// create the plugin wrapper
|
||||
log.debug("Creating wrapper for plugin '{}'", pluginPath); |
||||
PluginWrapper pluginWrapper = new PluginWrapper(pluginDescriptor, pluginPath, pluginLoader.getPluginClassLoader()); |
||||
pluginWrapper.setRuntimeMode(getRuntimeMode()); |
||||
log.debug("Created wrapper '{}' for plugin '{}'", pluginWrapper, pluginPath); |
||||
|
||||
String pluginId = pluginDescriptor.getPluginId(); |
||||
|
||||
// add plugin to the list with plugins
|
||||
plugins.put(pluginId, pluginWrapper); |
||||
unresolvedPlugins.add(pluginWrapper); |
||||
|
||||
// add plugin class loader to the list with class loaders
|
||||
PluginClassLoader pluginClassLoader = pluginLoader.getPluginClassLoader(); |
||||
pluginClassLoaders.put(pluginId, pluginClassLoader); |
||||
} |
||||
|
||||
private void expandPluginArchive(File pluginArchiveFile) throws IOException { |
||||
String fileName = pluginArchiveFile.getName(); |
||||
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 '{}' in '{}'", pluginArchiveFile, pluginDirectory); |
||||
// create directory for plugin
|
||||
pluginDirectory.mkdirs(); |
||||
|
||||
// expand '.zip' file
|
||||
Unzip unzip = new Unzip(); |
||||
unzip.setSource(pluginArchiveFile); |
||||
unzip.setDestination(pluginDirectory); |
||||
unzip.extract(); |
||||
} |
||||
} |
||||
|
||||
private void resolvePlugins() throws PluginException { |
||||
resolveDependencies(); |
||||
} |
||||
|
||||
private void resolveDependencies() throws PluginException { |
||||
DependencyResolver dependencyResolver = new DependencyResolver(unresolvedPlugins); |
||||
resolvedPlugins = dependencyResolver.getSortedPlugins(); |
||||
for (PluginWrapper pluginWrapper : resolvedPlugins) { |
||||
unresolvedPlugins.remove(pluginWrapper); |
||||
compoundClassLoader.addLoader(pluginWrapper.getPluginClassLoader()); |
||||
log.info("Plugin '{}' resolved", pluginWrapper.getDescriptor().getPluginId()); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
@ -0,0 +1,36 @@
|
||||
/* |
||||
* Copyright 2013 Decebal Suiu |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
*/ |
||||
package ro.fortsoft.pf4j; |
||||
|
||||
/** |
||||
* Overwrite classes directories to "target/classes" and lib directories to "target/lib". |
||||
* |
||||
* @author Decebal Suiu |
||||
*/ |
||||
public class DevelopmentPluginClasspath extends PluginClasspath { |
||||
|
||||
private static final String DEVELOPMENT_CLASSES_DIRECTORY = "target/classes"; |
||||
private static final String DEVELOPMENT_LIB_DIRECTORY = "target/lib"; |
||||
|
||||
public DevelopmentPluginClasspath() { |
||||
super(); |
||||
} |
||||
|
||||
@Override |
||||
protected void addResources() { |
||||
classesDirectories.add(DEVELOPMENT_CLASSES_DIRECTORY); |
||||
libDirectories.add(DEVELOPMENT_LIB_DIRECTORY); |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,55 @@
|
||||
/* |
||||
* Copyright 2013 Decebal Suiu |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with |
||||
* the License. You may obtain a copy of the License in the LICENSE file, or at: |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
*/ |
||||
package ro.fortsoft.pf4j; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.NoSuchElementException; |
||||
|
||||
/** |
||||
* @author Decebal Suiu |
||||
*/ |
||||
public enum RuntimeMode { |
||||
|
||||
DEVELOPMENT("development"), // development
|
||||
DEPLOYMENT("deployment"); // deployment
|
||||
|
||||
private final String name; |
||||
|
||||
private static final Map<String, RuntimeMode> map = new HashMap<String, RuntimeMode>(); |
||||
|
||||
static { |
||||
for (RuntimeMode mode : RuntimeMode.values()) { |
||||
map.put(mode.name, mode); |
||||
} |
||||
} |
||||
|
||||
private RuntimeMode(final String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return name; |
||||
} |
||||
|
||||
public static RuntimeMode byName(String name) { |
||||
if (map.containsKey(name)) { |
||||
return map.get(name); |
||||
} |
||||
|
||||
throw new NoSuchElementException("Cannot found PF4J runtime mode with name '" + name + |
||||
"'. Must be 'development' or 'deployment'."); |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue