diff options
author | Decebal Suiu <decebal.suiu@gmail.com> | 2013-10-10 15:11:23 +0300 |
---|---|---|
committer | Decebal Suiu <decebal.suiu@gmail.com> | 2013-10-10 15:11:23 +0300 |
commit | 78d959eb50b4606cc65e2f08cbf24d6441bc43bc (patch) | |
tree | f76e21bba6f7dfb22d7cd49bb67136240ef35ad7 | |
parent | 198949b8b08b79cb4e3ae69aa9fcb1969bb7acf9 (diff) | |
download | pf4j-78d959eb50b4606cc65e2f08cbf24d6441bc43bc.tar.gz pf4j-78d959eb50b4606cc65e2f08cbf24d6441bc43bc.zip |
remove sezpoz dependency
9 files changed, 244 insertions, 99 deletions
@@ -8,7 +8,7 @@ Current build status: [ 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. @@ -102,7 +102,7 @@ You can define an extension point in your application using **ExtensionPoint** i } Another important internal component is **ExtensionFinder** that describes how the plugin manager discovers extensions for the extensions points. -**DefaultExtensionFinder** is a "link" to **SezpozExtensionFinder** that looks up extensions using **Extension** annotation. +**DefaultExtensionFinder** looks up extensions using **Extension** annotation. public class WelcomePlugin extends Plugin { @@ -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. diff --git a/demo/plugins/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java b/demo/plugins/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java index d8963fc..7072e3d 100644 --- a/demo/plugins/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java +++ b/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 diff --git a/pf4j/pom.xml b/pf4j/pom.xml index 0859a55..52e120b 100644 --- a/pf4j/pom.xml +++ b/pf4j/pom.xml @@ -13,14 +13,20 @@ <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> diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java index 66c1936..5ab989b 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java @@ -12,16 +12,102 @@ */ 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}. + * The default implementation for ExtensionFinder. + * 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 volatile Set<String> entries; + public DefaultExtensionFinder(ClassLoader classLoader) { - super(classLoader); + this.classLoader = classLoader; + } + + @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 = extensionType.newInstance(); + if (instance != null) { + Extension extension = extensionType.getAnnotation(Extension.class); + log.debug("Added extension {} with ordinal {}", extensionType, extension.ordinal()); + result.add(new ExtensionWrapper<T>(type.cast(instance), extension.ordinal())); + } + } else { + log.warn("{} is not an extension for extension point {}", extensionType, type.getName()); + } + } catch (ClassNotFoundException e) { + log.error(e.getMessage(), e); + } catch (InstantiationException e) { + log.error(e.getMessage(), e); + } catch (IllegalAccessException 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; } + 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; + } + } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java index 30658cd..fcb839b 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java +++ b/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;
diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/Extension.java b/pf4j/src/main/java/ro/fortsoft/pf4j/Extension.java index f1b7cac..f2c01bd 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/Extension.java +++ b/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 diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionsIndexer.java b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionsIndexer.java new file mode 100644 index 0000000..c4a8f40 --- /dev/null +++ b/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(); + } + +} +
\ No newline at end of file diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/SezpozExtensionFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/SezpozExtensionFinder.java deleted file mode 100644 index 3f5014c..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/SezpozExtensionFinder.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with - * the License. You may obtain a copy of the License in the LICENSE file, or at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ -package 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; - } - -} diff --git a/pf4j/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/pf4j/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 0000000..a944d7c --- /dev/null +++ b/pf4j/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +ro.fortsoft.pf4j.ExtensionsIndexer |