You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

преди 6 години
преди 11 години
преди 7 години
преди 9 години
преди 9 години
преди 11 години
преди 10 години
преди 9 години
преди 10 години
преди 11 години
преди 11 години
преди 10 години
преди 7 години
преди 10 години
преди 10 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 7 години
преди 11 години
преди 7 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 8 години
преди 11 години
преди 7 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 9 години
преди 7 години
преди 6 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 7 години
преди 11 години
преди 7 години
преди 11 години
преди 11 години
преди 11 години
преди 7 години
преди 11 години
преди 11 години
преди 10 години
преди 10 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 7 години
преди 8 години
преди 11 години
преди 7 години
преди 11 години
преди 11 години
преди 7 години
преди 8 години
преди 7 години
преди 8 години
преди 10 години
преди 10 години
преди 6 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 7 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 7 години
преди 10 години
преди 10 години
преди 10 години
преди 7 години
преди 6 години
преди 9 години
преди 10 години
преди 7 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 6 години
преди 11 години
преди 7 години
преди 7 години
преди 7 години
преди 7 години
преди 7 години
преди 7 години
преди 7 години
преди 8 години
преди 7 години
преди 7 години
преди 7 години
преди 8 години
преди 6 години
преди 7 години
преди 7 години
преди 11 години
преди 11 години
преди 11 години
преди 7 години
преди 11 години
преди 10 години
преди 7 години
преди 9 години
преди 10 години
преди 7 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 7 години
преди 11 години
преди 7 години
преди 11 години
преди 7 години
преди 11 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. <img src="pf4j-logo.svg" width="250"/>
  2. Plugin Framework for Java (PF4J)
  3. =====================
  4. [![Join the chat at https://gitter.im/decebals/pf4j](https://badges.gitter.im/decebals/pf4j.svg)](https://gitter.im/decebals/pf4j?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
  5. [![Travis CI Build Status](https://travis-ci.org/decebals/pf4j.png)](https://travis-ci.org/decebals/pf4j)
  6. [![Coverage Status](https://coveralls.io/repos/decebals/pf4j/badge.svg?branch=master&service=github)](https://coveralls.io/github/decebals/pf4j?branch=master)
  7. [![Maven Central](http://img.shields.io/maven-central/v/ro.fortsoft.pf4j/pf4j.svg)](http://search.maven.org/#search|ga|1|pf4j)
  8. A plugin is a way for a third party to extend the functionality of an application. A plugin implements extension points
  9. declared by application or other plugins. Also a plugin can define extension points.
  10. **NOTE:** Starting with version 0.9 you can define an extension directly in the application jar (you're not obligated to put the extension in a plugin - you can see this extension as a default/system extension). See [WhazzupGreeting](https://github.com/decebals/pf4j/blob/master/demo/app/src/main/java/ro/fortsoft/pf4j/demo/WhazzupGreeting.java) for a real example.
  11. Features/Benefits
  12. -------------------
  13. With PF4J you can easily transform a monolithic java application in a modular application.
  14. PF4J is an open source (Apache license) lightweight (around __50 KB__) plugin framework for java, with minimal dependencies (only slf4j-api) and very extensible (see PluginDescriptorFinder and ExtensionFinder).
  15. Practically PF4J is a microframework and the aim is to keep the core simple but extensible. I try to create a little ecosystem (extensions) based on this core with the help of the comunity.
  16. For now are available these extensions:
  17. - [pf4j-update](https://github.com/decebals/pf4j-update) (update mechanism for PF4J)
  18. - [pf4j-spring](https://github.com/decebals/pf4j-spring) (PF4J - Spring Framework integration)
  19. - [pf4j-web](https://github.com/rmrodrigues/pf4j-web) (PF4J in web applications)
  20. - [wicket-plugin](https://github.com/decebals/wicket-plugin) (Wicket Plugin Framework based on PF4J)
  21. No XML, only Java.
  22. You can mark any interface or abstract class as an extension point (with marker interface ExtensionPoint) and you specified that an class is an extension with @Extension annotation.
  23. Also, PF4J can be used in web applications. For my web applications when I want modularity I use [Wicket Plugin](https://github.com/decebals/wicket-plugin).
  24. Components
  25. -------------------
  26. - **Plugin** is the base class for all plugins types. Each plugin is loaded into a separate class loader to avoid conflicts.
  27. - **PluginManager** is used for all aspects of plugins management (loading, starting, stopping). You can use a built-in implementation as `DefaultPluginManager`, `JarPluginManager` or you can implement a custom plugin manager starting from `AbstractPluginManager` (implement only factory methods).
  28. - **PluginLoader** loads all information (classes) needed by a plugin.
  29. - **ExtensionPoint** is a point in the application where custom code can be invoked. It's a java interface marker.
  30. Any java interface or abstract class can be marked as an extension point (implements `ExtensionPoint` interface).
  31. - **Extension** is an implementation of an extension point. It's a java annotation on a class.
  32. Artifacts
  33. -------------------
  34. - PF4J `pf4j` (jar)
  35. - PF4J Demo `pf4j-demo` (executable jar)
  36. Using Maven
  37. -------------------
  38. In your pom.xml you must define the dependencies to PF4J artifacts with:
  39. ```xml
  40. <dependency>
  41. <groupId>ro.fortsoft.pf4j</groupId>
  42. <artifactId>pf4j</artifactId>
  43. <version>${pf4j.version}</version>
  44. </dependency>
  45. ```
  46. where ${pf4j.version} is the last pf4j version.
  47. You may want to check for the latest released version using [Maven Search](http://search.maven.org/#search%7Cga%7C1%7Cpf4j)
  48. Also you can use the latest SNAPSHOT via the Sonatype Maven Repository. For this, you must add above lines in your `pom.xml`:
  49. ```xml
  50. <repositories>
  51. <repository>
  52. <id>sonatype-nexus-snapshots</id>
  53. <url>https://oss.sonatype.org/content/repositories/snapshots</url>
  54. <releases>
  55. <enabled>false</enabled>
  56. </releases>
  57. <snapshots>
  58. <enabled>true</enabled>
  59. </snapshots>
  60. </repository>
  61. </repositories>
  62. ```
  63. How to use
  64. -------------------
  65. It's very simple to add pf4j in your application:
  66. ```java
  67. public static void main(String[] args) {
  68. ...
  69. PluginManager pluginManager = new DefaultPluginManager();
  70. pluginManager.loadPlugins();
  71. pluginManager.startPlugins();
  72. ...
  73. }
  74. ```
  75. In above code, I created a **DefaultPluginManager** (it's the default implementation for
  76. **PluginManager** interface) that loads and starts all active(resolved) plugins.
  77. Each available plugin is loaded using a different java class loader, **PluginClassLoader**.
  78. The **PluginClassLoader** contains only classes found in **PluginClasspath** (default _classes_ and _lib_ folders) of plugin and runtime classes and libraries of the required/dependent plugins. This class loader is a _Parent Last ClassLoader_ - it loads the classes from the plugin's jars before delegating to the parent class loader.
  79. The plugins are stored in a folder. You can specify the plugins folder in the constructor of DefaultPluginManager. If the plugins folder is not specified
  80. then the location is returned by `System.getProperty("pf4j.pluginsDir", "plugins")`.
  81. The structure of plugins folder is:
  82. * plugin1.zip (or plugin1 folder)
  83. * plugin2.zip (or plugin2 folder)
  84. In plugins folder you can put a plugin as folder or archive file (zip).
  85. A plugin folder has this structure by default:
  86. * `classes` folder
  87. * `lib` folder (optional - if the plugin used third party libraries)
  88. The plugin manager searches plugins metadata using a **PluginDescriptorFinder**.
  89. **DefaultPluginDescriptorFinder** is a "link" to **ManifestPluginDescriptorFinder** that lookups plugins descriptors in MANIFEST.MF file.
  90. In this case the `classes/META-INF/MANIFEST.MF` file looks like:
  91. ```
  92. Manifest-Version: 1.0
  93. Archiver-Version: Plexus Archiver
  94. Created-By: Apache Maven
  95. Built-By: decebal
  96. Build-Jdk: 1.6.0_17
  97. Plugin-Class: ro.fortsoft.pf4j.demo.welcome.WelcomePlugin
  98. Plugin-Dependencies: x, y, z
  99. Plugin-Id: welcome-plugin
  100. Plugin-Provider: Decebal Suiu
  101. Plugin-Version: 0.0.1
  102. ```
  103. In above manifest I described a plugin with id `welcome-plugin`, with class `ro.fortsoft.pf4j.demo.welcome.WelcomePlugin`, with version `0.0.1` and with dependencies
  104. to plugins `x, y, z`.
  105. **NOTE:** The plugin version must be compliant with [Semantic Versioning](http://semver.org) (PF4J uses `jsemver` as implementation for SemVer because it comes with support for comparing versions)
  106. You can define an extension point in your application using **ExtensionPoint** interface marker.
  107. ```java
  108. public interface Greeting extends ExtensionPoint {
  109. String getGreeting();
  110. }
  111. ```
  112. Another important internal component is **ExtensionFinder** that describes how the plugin manager discovers extensions for the extensions points.
  113. **DefaultExtensionFinder** looks up extensions using **Extension** annotation.
  114. DefaultExtensionFinder looks up extensions in all extensions index files `META-INF/extensions.idx`. PF4J uses Java Annotation Processing to process at compile time all classes annotated with @Extension and to produce the extensions index file.
  115. ```java
  116. public class WelcomePlugin extends Plugin {
  117. public WelcomePlugin(PluginWrapper wrapper) {
  118. super(wrapper);
  119. }
  120. @Extension
  121. public static class WelcomeGreeting implements Greeting {
  122. public String getGreeting() {
  123. return "Welcome";
  124. }
  125. }
  126. }
  127. ```
  128. In above code I supply an extension for the `Greeting` extension point.
  129. You can retrieve all extensions for an extension point with:
  130. ```java
  131. List<Greeting> greetings = pluginManager.getExtensions(Greeting.class);
  132. for (Greeting greeting : greetings) {
  133. System.out.println(">>> " + greeting.getGreeting());
  134. }
  135. ```
  136. The output is:
  137. ```
  138. >>> Welcome
  139. >>> Hello
  140. ```
  141. You can inject your custom component (for example PluginDescriptorFinder, ExtensionFinder, PluginClasspath, ...) in DefaultPluginManager just override `create...` methods (factory method pattern).
  142. Example:
  143. ```java
  144. protected PluginDescriptorFinder createPluginDescriptorFinder() {
  145. return new PropertiesPluginDescriptorFinder();
  146. }
  147. ```
  148. and in plugin repository you must have a plugin.properties file with the below content:
  149. ```
  150. plugin.class=ro.fortsoft.pf4j.demo.welcome.WelcomePlugin
  151. plugin.dependencies=x, y, z
  152. plugin.id=welcome-plugin
  153. plugin.provider=Decebal Suiu
  154. plugin.version=0.0.1
  155. ```
  156. You can control extension instance creation overriding `createExtensionFactory` method from DefaultExtensionFinder.
  157. Also, you can control plugin instance creation overriding `createPluginFactory` method from DefaultExtensionFinder.
  158. For more information please see the demo sources.
  159. **NOTE:** If your application didn't find extensions then make sure that you have a file with name `extensions.idx`
  160. generated by PF4J in the plugin jar.
  161. It's most likely that they are some problems with the annotation processing mechanism from Java.
  162. O possible solution to resolve your problem is to add a configuration to your maven build.
  163. The `maven-compiler-plugin` can be configured to do this like so:
  164. ```
  165. <plugin>
  166. <groupId>org.apache.maven.plugins</groupId>
  167. <artifactId>maven-compiler-plugin</artifactId>
  168. <version>2.5.1</version>
  169. <configuration>
  170. <annotationProcessors>
  171. <annotationProcessor>ro.fortsoft.pf4j.processor.ExtensionAnnotationProcessor</annotationProcessor>
  172. </annotationProcessors>
  173. </configuration>
  174. </plugin>
  175. ```
  176. ### Kotlin
  177. PF4J can be used in Kotlin project as well. One has to use the Kotlin annotation processing tool
  178. [**kapt**](https://kotlinlang.org/docs/reference/kapt.html) for the plugin project written in Kotlin.
  179. The demo_gradle project contains one plugin project _plugin3_ written in Kotlin for demonstration.
  180. Plugin assembly
  181. ------------------------------
  182. After you developed a plugin the next step is to deploy it in your application. For this task, one option is to create a zip file with a structure described in section [How to use](https://github.com/decebals/pf4j/blob/master/README.md#how-to-use) from the beginning of the document.
  183. If you use `apache maven` as build manger then your pom.xml file must looks like [this](https://github.com/decebals/pf4j/blob/master/demo/plugins/plugin1/pom.xml). This file it's very simple and it's self explanatory.
  184. If you use `apache ant` then your build.xml file must looks like [this](https://github.com/gitblit/gitblit-powertools-plugin/blob/master/build.xml). In this case please look at the "build" target.
  185. Plugin lifecycle
  186. --------------------------
  187. Each plugin passes through a pre-defined set of states. [PluginState](https://github.com/decebals/pf4j/blob/master/pf4j/src/main/java/ro/fortsoft/pf4j/PluginState.java) defines all possible states.
  188. The primary plugin states are:
  189. * CREATED
  190. * DISABLED
  191. * STARTED
  192. * STOPPED
  193. The DefaultPluginManager contains the following logic:
  194. * all plugins are resolved & loaded
  195. * *DISABLED* plugins are NOT automatically *STARTED* by pf4j in `startPlugins()` BUT you may manually start (and therefore enable) a *DISABLED* plugin by calling `startPlugin(pluginId)` instead of `enablePlugin(pluginId)` + `startPlugin(pluginId)`
  196. * only *STARTED* plugins may contribute extensions. Any other state should not be considered ready to contribute an extension to the running system.
  197. The differences between a DISABLED plugin and a STARTED plugin are:
  198. * a STARTED plugin has executed Plugin.start(), a DISABLED plugin has not
  199. * a STARTED plugin may contribute extension instances, a DISABLED plugin may not
  200. DISABLED plugins still have valid class loaders and their classes can be manually
  201. loaded and explored, but the resource loading - which is important for inspection -
  202. has been handicapped by the DISABLED check.
  203. As integrators of pf4j evolve their extension APIs it will become
  204. a requirement to specify a minimum system version for loading plugins.
  205. Loading & starting a newer plugin on an older system could result in
  206. runtime failures due to method signature changes or other class
  207. differences.
  208. For this reason was added a manifest attribute (in PluginDescriptor) to specify a 'requires' version
  209. which is a minimum system version on x.y.z format, or a
  210. [SemVer Expression](https://github.com/zafarkhaja/jsemver#semver-expressions-api-ranges).
  211. Also DefaultPluginManager contains a method to
  212. specify the system version of the plugin manager and the logic to disable
  213. plugins on load if the system version is too old (if you want total control,
  214. please override `isPluginValid()`). This works for both `loadPlugins()` and `loadPlugin()`.
  215. __PluginStateListener__ defines the interface for an object that listens to plugin state changes. You can use `addPluginStateListener()` and `removePluginStateListener()` from PluginManager if you want to add or remove a plugin state listener.
  216. Your application, as a PF4J consumer, has full control over each plugin (state). So, you can load, unload, enable, disable, start, stop and delete a certain plugin using PluginManager (programmatically).
  217. Custom PluginManager
  218. --------------------------
  219. To create a custom plugin manager you could:
  220. * implements `PluginManager` interface (create a plugin manager from scratch)
  221. * modifies some aspects/behaviors of built-in implementations (`DefaultPluginManager`, `JarPluginManager`)
  222. * extends `AbstractPluginManager` class
  223. `JarPluginManager` is a `PluginManager` that loads plugin from a jar file. Actually, a plugin is a fat jar, a jar which contains classes from all the libraries,
  224. on which your project depends and, of course, the classes of current project.
  225. `AbstractPluginManager` adds some glue that help you to create quickly a plugin manager. All you need to do is to implement some factory methods.
  226. PF4J uses in many places the factory method pattern to implement the dependency injection (DI) concept in a manually mode.
  227. See below the abstract methods for `AbstractPluginManager`:
  228. ```java
  229. public abstract class AbstractPluginManager implements PluginManager {
  230. protected abstract PluginRepository createPluginRepository();
  231. protected abstract PluginFactory createPluginFactory();
  232. protected abstract ExtensionFactory createExtensionFactory();
  233. protected abstract PluginDescriptorFinder createPluginDescriptorFinder();
  234. protected abstract ExtensionFinder createExtensionFinder();
  235. protected abstract PluginStatusProvider createPluginStatusProvider();
  236. protected abstract PluginLoader createPluginLoader();
  237. // other non abstract methods
  238. }
  239. ```
  240. `DefaultPluginManager` contributes with "default" components (`DefaultExtensionFactory`, `DefaultPluginFactory`, `DefaultPluginLoader`, ...) to `AbstractPluginManager`.
  241. Most of the times it's enough to extends `DefaultPluginManager` and to supply your custom components. As example, I will show you the implementation for `JarPluginManager`:
  242. ```java
  243. public class JarPluginManager extends DefaultPluginManager {
  244. @Override
  245. protected PluginRepository createPluginRepository() {
  246. return new JarPluginRepository(getPluginsRoot(), isDevelopment());
  247. }
  248. @Override
  249. protected PluginDescriptorFinder createPluginDescriptorFinder() {
  250. return isDevelopment() ? new PropertiesPluginDescriptorFinder() : new JarPluginDescriptorFinder();
  251. }
  252. @Override
  253. protected PluginLoader createPluginLoader() {
  254. return new JarPluginLoader(this, pluginClasspath);
  255. }
  256. }
  257. ```
  258. Development mode
  259. --------------------------
  260. PF4J can run in two modes: **DEVELOPMENT** and **DEPLOYMENT**.
  261. 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
  262. add new extensions), pack the plugin in a zip file, deploy the zip file to plugins folder. These operations are time consuming and from this reason I introduced the DEVELOPMENT runtime mode.
  263. The main advantage of DEVELOPMENT runtime mode for a plugin developer is that he/she is not enforced to pack and deploy the plugins. In DEVELOPMENT mode you can developing plugins in a simple and fast mode.
  264. Lets describe how DEVELOPMENT runtime mode works.
  265. First, you can change the runtime mode using the "pf4j.mode" system property or overriding `DefaultPluginManager.getRuntimeMode()`.
  266. For example I run the pf4j demo in eclipse in DEVELOPMENT mode adding only `"-Dpf4j.mode=development"` to the pf4j demo launcher.
  267. You can retrieve the current runtime mode using `PluginManager.getRuntimeMode()` or in your Plugin implementation with `getWrapper().getRuntimeMode()`(see [WelcomePlugin](https://github.com/decebals/pf4j/blob/master/demo/plugins/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java)).
  268. The DefaultPluginManager determines automatically the correct runtime mode and for DEVELOPMENT mode overrides some components(pluginsDirectory is __"../plugins"__, __PropertiesPluginDescriptorFinder__ as PluginDescriptorFinder, __DevelopmentPluginClasspath__ as PluginClassPath).
  269. Another advantage of DEVELOPMENT runtime mode is that you can execute some code lines only in this mode (for example more debug messages).
  270. **NOTE:** If you use Eclipse then make sure annotation processing is enabled at least for any projects registering objects using annotations. In the properties for your new project go to __Java Compiler > Annotation Processing__
  271. Check the __“Enable Project Specific Settings”__ and make sure __“Enable annotation processing”__ is checked.
  272. If you use Maven as build manger, after each dependency modification in your plugin (Maven module) you must run __Maven > Update Project...__
  273. For more details see the demo application.
  274. Enable/Disable plugins
  275. -------------------
  276. In theory, it's a relation **1:N** between an extension point and the extensions for this extension point.
  277. This works well, except for when you develop multiple plugins for this extension point as different options for your clients to decide on which one to use.
  278. In this situation you wish a possibility to disable all but one extension.
  279. For example I have an extension point for sending mail (EmailSender interface) with two extensions: one based on Sendgrid and another
  280. based on Amazon Simple Email Service.
  281. The first extension is located in Plugin1 and the second extension is located in Plugin2.
  282. I want to go only with one extension ( **1:1** relation between extension point and extensions) and to achieve this I have two options:
  283. 1) uninstall Plugin1 or Plugin2 (remove folder pluginX.zip and pluginX from plugins folder)
  284. 2) disable Plugin1 or Plugin2
  285. For option two you must create a simple file **enabled.txt** or **disabled.txt** in your plugins folder.
  286. The content for **enabled.txt** is similar with:
  287. ```
  288. ########################################
  289. # - load only these plugins
  290. # - add one plugin id on each line
  291. # - put this file in plugins folder
  292. ########################################
  293. welcome-plugin
  294. ```
  295. The content for **disabled.txt** is similar with:
  296. ```
  297. ########################################
  298. # - load all plugins except these
  299. # - add one plugin id on each line
  300. # - put this file in plugins folder
  301. ########################################
  302. welcome-plugin
  303. ```
  304. All comment lines (line that start with # character) are ignored.
  305. If a file with enabled.txt exists then disabled.txt is ignored. See enabled.txt and disabled.txt from the demo folder.
  306. Default/System extension
  307. -------------------
  308. Starting with version 0.9 you can define an extension directly in the application jar (you're not obligated
  309. to put the extension in a plugin - you can see this extension as a default/system extension).
  310. See [WhazzupGreeting](https://github.com/decebals/pf4j/blob/master/demo/app/src/main/java/ro/fortsoft/pf4j/demo/WhazzupGreeting.java)
  311. for a real example.
  312. This is great for starting application phase. In this scenario you have a minimalist plugin framework with one class loader
  313. (the application class loader), similar with Java [ServiceLoader](https://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html)
  314. but with the following benefits:
  315. - no need to write provider-configuration files in the resource directory `META-INF/services`, you using the elegant
  316. `@Extension` annotation from PF4J
  317. - anytime you can switch to the multiple class loader mechanism without to change one code line in your application
  318. Of course the code present in the `Boot` class from the demo application it is functional but you can use a more minimalist code
  319. skipping `pluginManager.loadPlugins()` and `pluginManager.startPlugins()`.
  320. ```java
  321. public static void main(String[] args) {
  322. PluginManager pluginManager = new DefaultPluginManager();
  323. pluginManager.loadPlugins();
  324. pluginManager.startPlugins();
  325. List<Greeting> greetings = pluginManager.getExtensions(Greeting.class);
  326. for (Greeting greeting : greetings) {
  327. System.out.println(">>> " + greeting.getGreeting());
  328. }
  329. }
  330. ```
  331. The above code can be written:
  332. ```java
  333. public static void main(String[] args) {
  334. PluginManager pluginManager = new DefaultPluginManager();
  335. List<Greeting> greetings = pluginManager.getExtensions(Greeting.class);
  336. for (Greeting greeting : greetings) {
  337. System.out.println(">>> " + greeting.getGreeting());
  338. }
  339. }
  340. ```
  341. ServiceLoader interoperability
  342. -------------------
  343. Starting with version 0.12 PF4J comes with a better support for `ServiceLoader`.
  344. PF4J can read `META-INF/services` (Java Service Provider mechanism) as extensions, so,
  345. if you have a modular application based on `java.util.ServiceLoader` class you can replace entirely the `ServiceLoader.load()`
  346. calls from your application with `PluginManager.getExtensions()` and migrate smooth from ServiceLoader to PF4J.
  347. Also you have the possibility to change the `ExtensionStorage` used in `ExtensionAnnotationProcessor`.
  348. By default we use the format with `META-INF/extensions.idx`
  349. ```
  350. ro.fortsoft.pf4j.demo.HowdyGreeting
  351. ro.fortsoft.pf4j.demo.WhazzupGreeting
  352. ```
  353. but you can use a more standard location and format, `META-INF/services/<extension-point>`, used by Java Service Provider
  354. (see `java.util.ServiceLoader`) via `ServiceProviderExtensionStorage` implementation.
  355. In this case the format of `META-INF/services/ro.fortsoft.pf4j.demo.api.Greeting` is
  356. ```
  357. # Generated by PF4J
  358. ro.fortsoft.pf4j.demo.HowdyGreeting
  359. ro.fortsoft.pf4j.demo.WhazzupGreeting # pf4j extension
  360. ```
  361. where the `ro.fortsoft.pf4j.demo.HowdyGreeting` entry is legacy (it's not generated by PF4J) but it's seen as
  362. an extension of `Greeting` by PF4J (at runtime).
  363. You can plug your custom `ExtensionStorage` implementation in `ExtensionAnnotationProcessor` in two possible modes:
  364. - set the annotation procesor option with key `pf4j.storageClassName`
  365. - set the system property with key `pf4j.storageClassName`
  366. For example if I want to use `ServiceProviderExtensionStorage` then the value for the `pf4j.storageClassName` key must be
  367. `ro.fortsoft.pf4j.processor.ServiceProviderExtensionStorage`
  368. **NOTE:** `ServiceLoaderExtensionFinder`, the class that lookups for extensions stored in `META-INF/services` folder, is
  369. not added/enabled by default. To do this please override `createExtensionFinder` from `DefaultPluginManager`:
  370. ```java
  371. protected ExtensionFinder createExtensionFinder() {
  372. DefaultExtensionFinder extensionFinder = super.createExtensionFinder();
  373. extensionFinder.addServiceProviderExtensionFinder();
  374. return extensionFinder;
  375. }
  376. ```
  377. Troubleshooting
  378. -------------------
  379. Below are listed some problems that may occur when attempting to use PF4J, and suggestions for solving them.
  380. - **No Extensions Found**
  381. See if you have a file `extensions.idx` in each plugin.
  382. If file `extensions.idx` doesn't exist then probably there is something wrong with the annotation processing step (enable annotation processing in your IDE or in your Maven script).
  383. If file `extensions.idx` exists and it's not empty then sure you have a class loader issue (you have the same extension point in two different class loader), in this situation you must remove some libraries (probably the API jar) from plugin.
  384. If the problem persist or you want to find more info related to the extensions discovery process (e.g what interfaces/classes are loaded by each plugin, what classes are not recognized as extensions for an extension point) then you must put on `TRACE` level the logger for `PluginClassLoader` and `AbstractExtensionFinder` (see the [log4j.properties](https://github.com/decebals/pf4j/blob/master/demo/app/src/main/resources/log4j.properties) file for demo).
  385. Are some resources on the internet related to this subject: [#82](https://github.com/decebals/pf4j/issues/82), [#64](https://github.com/decebals/pf4j/issues/64) and [No extensions found] (https://groups.google.com/forum/#!topic/pf4j/tEQXY_WpD3A).
  386. Demo
  387. -------------------
  388. I have a tiny demo application. The demo application is in demo folder.
  389. In demo/api folder I declared an extension point ( _Greeting_).
  390. In demo/plugins I implemented two plugins: plugin1, plugin2 (each plugin adds an extension for _Greeting_).
  391. To run the demo application use:
  392. ```
  393. ./run-demo.sh (for Linux/Unix)
  394. ./run-demo.bat (for Windows)
  395. ```
  396. How to build
  397. -------------------
  398. Requirements:
  399. - [Git](http://git-scm.com/)
  400. - JDK 7 (test with `java -version`)
  401. - [Apache Maven 3](http://maven.apache.org/) (test with `mvn -version`)
  402. Steps:
  403. - create a local clone of this repository (with `git clone https://github.com/decebals/pf4j.git`)
  404. - go to project's folder (with `cd pf4j`)
  405. - build the artifacts (with `mvn clean package` or `mvn clean install`)
  406. After above steps a folder _pf4j/target_ is created and all goodies are in that folder.
  407. Mailing list
  408. --------------
  409. Much of the conversation between developers and users is managed through [mailing list] (http://groups.google.com/group/pf4j).
  410. Versioning
  411. ------------
  412. PF4J will be maintained under the Semantic Versioning guidelines as much as possible.
  413. Releases will be numbered with the follow format:
  414. `<major>.<minor>.<patch>`
  415. And constructed with the following guidelines:
  416. * Breaking backward compatibility bumps the major
  417. * New additions without breaking backward compatibility bumps the minor
  418. * Bug fixes and misc changes bump the patch
  419. For more information on SemVer, please visit http://semver.org/.
  420. License
  421. --------------
  422. Copyright 2012 Decebal Suiu
  423. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with
  424. the License. You may obtain a copy of the License in the LICENSE file, or at:
  425. http://www.apache.org/licenses/LICENSE-2.0
  426. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  427. an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  428. specific language governing permissions and limitations under the License.