Browse Source

Merge branch 'master' of https://github.com/decebals/pf4j

pull/3/head
Decebal Suiu 11 years ago
parent
commit
4662ecf8f8
  1. 12
      README.md
  2. 4
      demo/api/pom.xml
  3. 4
      demo/app/pom.xml
  4. 4
      demo/plugins/plugin1/pom.xml
  5. 4
      demo/plugins/plugin2/pom.xml
  6. 2
      demo/plugins/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java
  7. 4
      demo/plugins/pom.xml
  8. 4
      demo/pom.xml
  9. 22
      pf4j/pom.xml
  10. 124
      pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java
  11. 2
      pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java
  12. 3
      pf4j/src/main/java/ro/fortsoft/pf4j/Extension.java
  13. 135
      pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionsIndexer.java
  14. 80
      pf4j/src/main/java/ro/fortsoft/pf4j/SezpozExtensionFinder.java
  15. 1
      pf4j/src/main/resources/META-INF/services/javax.annotation.processing.Processor
  16. 2
      pom.xml

12
README.md

@ -8,7 +8,7 @@ Current build status: [![Build Status](https://buildhive.cloudbees.com/job/dece
Features/Benefits
-------------------
With PF4J you can easily transform a monolithic java application in a modular application.
PF4J is an open source (Apache license) lightweight (around 35KB) plugin framework for java, with minimal dependencies and very extensible (see PluginDescriptorFinder and ExtensionFinder).
PF4J is an open source (Apache license) lightweight (around 50KB) plugin framework for java, with minimal dependencies (only slf4j-api) and very extensible (see PluginDescriptorFinder and ExtensionFinder).
No XML, only Java.
@ -101,8 +101,8 @@ You can define an extension point in your application using **ExtensionPoint** i
}
Another important internal component is **ExtensionFinder** that describes how plugin manager discovers extensions for extensions points.
**DefaultExtensionFinder** is a "link" to **SezpozExtensionFinder** that looks up extensions using **Extension** annotation.
Another important internal component is **ExtensionFinder** that describes how the plugin manager discovers extensions for the extensions points.
**DefaultExtensionFinder** looks up extensions using **Extension** annotation. You can control extension instance creation overriding `createExtensionFactory` method from DefaultExtensionFinder.
public class WelcomePlugin extends Plugin {
@ -135,7 +135,7 @@ The output is:
>>> Welcome
>>> Hello
You can inject your custom component (for example PluginDescriptorFinder, ExtensionFinder, PluginClasspath, ...) in DefaultPluginManager just override createXXX methods (factory method pattern).
You can inject your custom component (for example PluginDescriptorFinder, ExtensionFinder, PluginClasspath, ...) in DefaultPluginManager just override `create...` methods (factory method pattern).
Example:
@ -154,7 +154,7 @@ and in plugin respository you must have a plugin.properties file with the below
For more information please see the demo sources.
Development runtime mode
Development mode
--------------------------
PF4J can run in two modes: **DEVELOPMENT** and **DEPLOYMENT**.
The DEPLOYMENT(default) mode is the standard workflow for plugins creation: create a new maven module for each plugin, codding the plugin (declares new extension points and/or
@ -169,7 +169,7 @@ You can retrieve the current runtime mode using `PluginManager.getRuntimeMode()`
The DefaultPluginManager determines automatically the correct runtime mode and for DEVELOPMENT mode overrides some components(pluginsDirectory is __"../plugins"__, __PropertiesPluginDescriptorFinder__ as PluginDescriptorFinder, __DevelopmentPluginClasspath__ as PluginClassPath).
Another advantage of DEVELOPMENT runtime mode is that you can execute some code lines only in this mode (for example more debug messages).
If you use maven as build manger, after each dependency modification in you plugin (maven module) you must run Maven>Update Project...
If you use maven as build manger, after each dependency modification in your plugin (maven module) you must run Maven>Update Project...
For more details see the demo application.

4
demo/api/pom.xml

@ -4,12 +4,12 @@
<parent>
<groupId>ro.fortsoft.pf4j.demo</groupId>
<artifactId>pf4j-demo-parent</artifactId>
<version>0.6-SNAPSHOT</version>
<version>0.7-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pf4j-demo-api</artifactId>
<version>0.6-SNAPSHOT</version>
<version>0.7-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Demo Api</name>

4
demo/app/pom.xml

@ -4,12 +4,12 @@
<parent>
<groupId>ro.fortsoft.pf4j.demo</groupId>
<artifactId>pf4j-demo-parent</artifactId>
<version>0.6-SNAPSHOT</version>
<version>0.7-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pf4j-demo-app</artifactId>
<version>0.6-SNAPSHOT</version>
<version>0.7-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Demo App</name>

4
demo/plugins/plugin1/pom.xml

@ -4,12 +4,12 @@
<parent>
<groupId>ro.fortsoft.pf4j.demo</groupId>
<artifactId>pf4j-demo-plugins</artifactId>
<version>0.6-SNAPSHOT</version>
<version>0.7-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pf4j-demo-plugin1</artifactId>
<version>0.6-SNAPSHOT</version>
<version>0.7-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Demo Plugin #1</name>

4
demo/plugins/plugin2/pom.xml

@ -4,12 +4,12 @@
<parent>
<groupId>ro.fortsoft.pf4j.demo</groupId>
<artifactId>pf4j-demo-plugins</artifactId>
<version>0.6-SNAPSHOT</version>
<version>0.7-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pf4j-demo-plugin2</artifactId>
<version>0.6-SNAPSHOT</version>
<version>0.7-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Demo Plugin #2</name>

2
demo/plugins/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java

@ -38,7 +38,7 @@ public class HelloPlugin extends Plugin {
System.out.println("HelloPlugin.stop()");
}
@Extension
@Extension(ordinal=1)
public static class HelloGreeting implements Greeting {
@Override

4
demo/plugins/pom.xml

@ -4,13 +4,13 @@
<parent>
<groupId>ro.fortsoft.pf4j.demo</groupId>
<artifactId>pf4j-demo-parent</artifactId>
<version>0.6-SNAPSHOT</version>
<version>0.7-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>ro.fortsoft.pf4j.demo</groupId>
<artifactId>pf4j-demo-plugins</artifactId>
<version>0.6-SNAPSHOT</version>
<version>0.7-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Demo Plugins Parent</name>

4
demo/pom.xml

@ -4,13 +4,13 @@
<parent>
<groupId>ro.fortsoft.pf4j</groupId>
<artifactId>pf4j-parent</artifactId>
<version>0.6-SNAPSHOT</version>
<version>0.7-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>ro.fortsoft.pf4j.demo</groupId>
<artifactId>pf4j-demo-parent</artifactId>
<version>0.6-SNAPSHOT</version>
<version>0.7-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Demo Parent</name>

22
pf4j/pom.xml

@ -4,23 +4,29 @@
<parent>
<groupId>ro.fortsoft.pf4j</groupId>
<artifactId>pf4j-parent</artifactId>
<version>0.6-SNAPSHOT</version>
<version>0.7-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pf4j</artifactId>
<version>0.6-SNAPSHOT</version>
<version>0.7-SNAPSHOT</version>
<packaging>jar</packaging>
<name>PF4J</name>
<description>Plugin Framework for Java</description>
<dependencies>
<dependency>
<groupId>net.java.sezpoz</groupId>
<artifactId>sezpoz</artifactId>
<version>1.9</version>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgument>-proc:none</compilerArgument>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>

124
pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java

@ -12,16 +12,134 @@
*/
package ro.fortsoft.pf4j;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The default implementation for ExtensionFinder.
* Now, this class it's a "link" to {@link ro.fortsoft.pf4j.SezpozExtensionFinder}.
* All extensions declared in a plugin are indexed in a file "META-INF/extensions.idx".
* This class lookup extensions in all extensions index files "META-INF/extensions.idx".
*
* @author Decebal Suiu
*/
public class DefaultExtensionFinder extends SezpozExtensionFinder {
public class DefaultExtensionFinder implements ExtensionFinder {
private static final Logger log = LoggerFactory.getLogger(DefaultExtensionFinder.class);
private ClassLoader classLoader;
private ExtensionFactory extensionFactory;
private volatile Set<String> entries;
public DefaultExtensionFinder(ClassLoader classLoader) {
super(classLoader);
this.classLoader = classLoader;
this.extensionFactory = createExtensionFactory();
}
@Override
public <T> List<ExtensionWrapper<T>> find(Class<T> type) {
log.debug("Find extensions for extension point {}", type.getName());
List<ExtensionWrapper<T>> result = new ArrayList<ExtensionWrapper<T>>();
if (entries == null) {
entries = readIndexFiles();
}
for (String entry : entries) {
try {
Class<?> extensionType = classLoader.loadClass(entry);
log.debug("Checking extension type {}", extensionType.getName());
if (type.isAssignableFrom(extensionType)) {
Object instance = extensionFactory.create(extensionType);
if (instance != null) {
Extension extension = extensionType.getAnnotation(Extension.class);
log.debug("Added extension {} with ordinal {}", extensionType.getName(), extension.ordinal());
result.add(new ExtensionWrapper<T>(type.cast(instance), extension.ordinal()));
}
} else {
log.warn("{} is not an extension for extension point {}", extensionType.getName(), type.getName());
}
} catch (ClassNotFoundException e) {
log.error(e.getMessage(), e);
}
}
if (entries.isEmpty()) {
log.debug("No extensions found for extension point {}", type.getName());
} else {
log.debug("Found {} extensions for extension point {}", entries.size(), type.getName());
}
// sort by "ordinal" property
Collections.sort(result);
return result;
}
/**
* Add the possibility to override the ExtensionFactory.
* The default implementation uses Class.newInstance() method.
*/
protected ExtensionFactory createExtensionFactory() {
return new ExtensionFactory() {
@Override
public Object create(Class<?> extensionType) {
log.debug("Create instance for extension {}", extensionType.getName());
try {
return extensionType.newInstance();
} catch (InstantiationException e) {
log.error(e.getMessage(), e);
} catch (IllegalAccessException e) {
log.error(e.getMessage(), e);
}
return null;
}
};
}
private Set<String> readIndexFiles() {
log.debug("Reading extensions index files");
Set<String> entries = new HashSet<String>();
try {
Enumeration<URL> indexFiles = classLoader.getResources(ExtensionsIndexer.EXTENSIONS_RESOURCE);
while (indexFiles.hasMoreElements()) {
Reader reader = new InputStreamReader(indexFiles.nextElement().openStream(), "UTF-8");
ExtensionsIndexer.readIndex(reader, entries);
}
} catch (IOException e) {
log.error(e.getMessage(), e);
}
if (entries.isEmpty()) {
log.debug("No extensions found");
} else {
log.debug("Found possible {} extensions", entries.size());
}
return entries;
}
/**
* Creates an extension instance.
*/
public static interface ExtensionFactory {
public Object create(Class<?> extensionType);
}
}

2
pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java

@ -210,7 +210,7 @@ public class DefaultPluginManager implements PluginManager {
filterList.add(new NotFileFilter(createHiddenPluginFilter()));
FileFilter pluginsFilter = new AndFileFilter(filterList);
File[] directories = pluginsDirectory.listFiles(pluginsFilter);
log.debug("Possible plugins: {}", Arrays.asList(directories));
log.debug("Found possible {} plugins: {}", directories.length, Arrays.asList(directories));
if (directories.length == 0) {
log.info("No plugins");
return;

3
pf4j/src/main/java/ro/fortsoft/pf4j/Extension.java

@ -19,12 +19,9 @@ 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

135
pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionsIndexer.java

@ -0,0 +1,135 @@
/*
* 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.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
/**
* @author Decebal Suiu
*/
public class ExtensionsIndexer extends AbstractProcessor {
public static final String EXTENSIONS_RESOURCE = "META-INF/extensions.idx";
private List<TypeElement> extensions = new ArrayList<TypeElement>();
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> annotationTypes = new HashSet<String>();
annotationTypes.add(Extension.class.getName());
return annotationTypes;
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
return false;
}
for (Element element : roundEnv.getElementsAnnotatedWith(Extension.class)) {
if (!(element instanceof TypeElement)) {
continue;
}
TypeElement typeElement = (TypeElement) element;
String message = "Extension found in " + processingEnv.getElementUtils().getBinaryName(typeElement).toString();
processingEnv.getMessager().printMessage(Kind.NOTE, message);
extensions.add(typeElement);
}
/*
if (!roundEnv.processingOver()) {
return false;
}
*/
write();
return false;
// return true; // no further processing of this annotation type
}
private void write() {
Set<String> entries = new HashSet<String>();
for (TypeElement typeElement : extensions) {
entries.add(processingEnv.getElementUtils().getBinaryName(typeElement).toString());
}
read(entries); // read old entries
write(entries); // write entries
}
private void write(Set<String> entries) {
try {
FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", EXTENSIONS_RESOURCE);
Writer writer = file.openWriter();
for (String entry : entries) {
writer.write(entry);
writer.write("\n");
}
writer.close();
} catch (FileNotFoundException e) {
// it's the first time, create the file
} catch (IOException e) {
processingEnv.getMessager().printMessage(Kind.ERROR, e.toString());
}
}
private void read(Set<String> entries) {
try {
FileObject file = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", EXTENSIONS_RESOURCE);
readIndex(file.openReader(true), entries);
} catch (FileNotFoundException e) {
} catch (IOException e) {
// thrown by Eclipse JDT when not found
} catch (UnsupportedOperationException e) {
// java6 does not support reading old index files
}
}
public static void readIndex(Reader reader, Set<String> entries) throws IOException {
BufferedReader bufferedReader = new BufferedReader(reader);
String line;
while ((line = bufferedReader.readLine()) != null) {
entries.add(line);
}
reader.close();
}
}

80
pf4j/src/main/java/ro/fortsoft/pf4j/SezpozExtensionFinder.java

@ -1,80 +0,0 @@
/*
* Copyright 2012 Decebal Suiu
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with
* the License. You may obtain a copy of the License in the LICENSE file, or at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package ro.fortsoft.pf4j;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.java.sezpoz.Index;
import net.java.sezpoz.IndexItem;
/**
* Using Sezpoz(http://sezpoz.java.net/) for extensions discovery.
*
* @author Decebal Suiu
*/
public class SezpozExtensionFinder implements ExtensionFinder {
private static final Logger log = LoggerFactory.getLogger(SezpozExtensionFinder.class);
private volatile List<IndexItem<Extension, Object>> indices;
private ClassLoader classLoader;
public SezpozExtensionFinder(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;
}
}

1
pf4j/src/main/resources/META-INF/services/javax.annotation.processing.Processor

@ -0,0 +1 @@
ro.fortsoft.pf4j.ExtensionsIndexer

2
pom.xml

@ -10,7 +10,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>ro.fortsoft.pf4j</groupId>
<artifactId>pf4j-parent</artifactId>
<version>0.6-SNAPSHOT</version>
<version>0.7-SNAPSHOT</version>
<packaging>pom</packaging>
<name>PF4J Parent</name>
<description>Plugin Framework for Java</description>

Loading…
Cancel
Save