]> source.dussan.org Git - pf4j.git/commitdiff
Resolve #166
authorDecebal Suiu <decebal.suiu@gmail.com>
Tue, 23 Jan 2018 20:05:05 +0000 (22:05 +0200)
committerDecebal Suiu <decebal.suiu@gmail.com>
Tue, 23 Jan 2018 20:05:05 +0000 (22:05 +0200)
README.md

index eef5f5a78f35e1cbc153f8acc3fb710385453e97..822f92ca290939785366883d67dbf1c7e1bfe137 100644 (file)
--- a/README.md
+++ b/README.md
@@ -39,99 +39,11 @@ Components
 Any java interface or abstract class can be marked as an extension point (implements `ExtensionPoint` interface).
 - **Extension** is an implementation of an extension point. It's a java annotation on a class.
 
-Artifacts
--------------------
-- PF4J `pf4j` (jar)
-- PF4J Demo `pf4j-demo` (executable jar)
-
-Using Maven
--------------------
-In your pom.xml you must define the dependencies to PF4J artifacts with:
-
-```xml
-<dependency>
-    <groupId>org.pf4j</groupId>
-    <artifactId>pf4j</artifactId>
-    <version>${pf4j.version}</version>
-</dependency>    
-```
-
-where ${pf4j.version} is the last pf4j version.
-
-You may want to check for the latest released version using [Maven Search](http://search.maven.org/#search%7Cga%7C1%7Cpf4j)
-
-Also you can use the latest SNAPSHOT via the Sonatype Maven Repository. For this, you must add above lines in your `pom.xml`:
-
-```xml
-<repositories>
-    <repository>
-        <id>sonatype-nexus-snapshots</id>
-        <url>https://oss.sonatype.org/content/repositories/snapshots</url>
-        <releases>
-            <enabled>false</enabled>
-        </releases>
-        <snapshots>
-            <enabled>true</enabled>
-        </snapshots>
-    </repository>
-</repositories>
-```
-
 How to use
 -------------------
-It's very simple to add pf4j in your application:
-
-```java
-public static void main(String[] args) {
-    ...
-
-    PluginManager pluginManager = new DefaultPluginManager();
-    pluginManager.loadPlugins();
-    pluginManager.startPlugins();
-
-    ...
-}
-```
-
-In above code, I created a **DefaultPluginManager** (it's the default implementation for
-**PluginManager** interface) that loads and starts all active(resolved) plugins.  
-Each available plugin is loaded using a different java class loader, **PluginClassLoader**.   
-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.   
-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
-then the location is returned by `System.getProperty("pf4j.pluginsDir", "plugins")`.
-
-The structure of plugins folder is:
-* plugin1.zip (or plugin1 folder)
-* plugin2.zip (or plugin2 folder)
+It's very simple to add pf4j in your application.
 
-In plugins folder you can put a plugin as folder or archive file (zip).
-A plugin folder has this structure by default:
-* `classes` folder
-* `lib` folder (optional - if the plugin used third party libraries)
-
-The plugin manager searches plugins metadata using a **PluginDescriptorFinder**.   
-**DefaultPluginDescriptorFinder** is a "link" to **ManifestPluginDescriptorFinder** that lookups plugins descriptors in MANIFEST.MF file.
-In this case the `classes/META-INF/MANIFEST.MF` file looks like:
-
-```
-Manifest-Version: 1.0
-Archiver-Version: Plexus Archiver
-Created-By: Apache Maven
-Built-By: decebal
-Build-Jdk: 1.6.0_17
-Plugin-Class: org.pf4j.demo.welcome.WelcomePlugin
-Plugin-Dependencies: x, y, z
-Plugin-Id: welcome-plugin
-Plugin-Provider: Decebal Suiu
-Plugin-Version: 0.0.1
-```
-
-In above manifest I described a plugin with id `welcome-plugin`, with class `org.pf4j.demo.welcome.WelcomePlugin`, with version `0.0.1` and with dependencies
-to plugins `x, y, z`.
-
-**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)
-
-You can define an extension point in your application using **ExtensionPoint** interface marker.
+Define an extension point in your application using **ExtensionPoint** interface marker:
 
 ```java
 public interface Greeting extends ExtensionPoint {
@@ -141,10 +53,8 @@ public interface Greeting extends ExtensionPoint {
 }
 ```
 
-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.   
-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.
-
+Create a plugin that contribute with an extension:
 ```java
 public class WelcomePlugin extends Plugin {
 
@@ -164,403 +74,65 @@ public class WelcomePlugin extends Plugin {
 }
 ```
 
-In above code I supply an extension for the `Greeting` extension point.
+In above code I created a plugin that comes with one extension for the `Greeting` extension point.
 
-You can retrieve all extensions for an extension point with:
+You can distribute you plugin as a jar file (the simple solution). In this case add the plugin's metadata in `MANIFEST.MF` file of jar:
 
-```java
-List<Greeting> greetings = pluginManager.getExtensions(Greeting.class);
-for (Greeting greeting : greetings) {
-    System.out.println(">>> " + greeting.getGreeting());
-}
-```
-
-The output is:
-
-```
->>> Welcome
->>> Hello
-```
-
-You can inject your custom component (for example PluginDescriptorFinder, ExtensionFinder, PluginClasspath, ...) in DefaultPluginManager just override `create...` methods (factory method pattern).
-
-Example:
-
-```java
-protected PluginDescriptorFinder createPluginDescriptorFinder() {
-    return new PropertiesPluginDescriptorFinder();
-}
-```
-
-and in plugin repository you must have a plugin.properties file with the below content:
-
-```
-plugin.class=org.pf4j.demo.welcome.WelcomePlugin
-plugin.dependencies=x, y, z
-plugin.id=welcome-plugin
-plugin.provider=Decebal Suiu
-plugin.version=0.0.1
-```
-
-You can control extension instance creation overriding `createExtensionFactory` method from DefaultExtensionFinder.
-Also, you can control plugin instance creation overriding `createPluginFactory` method from DefaultExtensionFinder.
-
-For more information please see the demo sources.
-
-**NOTE:** If your application didn't find extensions then make sure that you have a file with name `extensions.idx`
-generated by PF4J in the plugin jar.
-It's most likely that they are some problems with the annotation processing mechanism from Java.
-O possible solution to resolve your problem is to add a configuration to your maven build.
-The `maven-compiler-plugin` can be configured to do this like so:
 ```
-<plugin>
-    <groupId>org.apache.maven.plugins</groupId>
-    <artifactId>maven-compiler-plugin</artifactId>
-    <version>2.5.1</version>
-    <configuration>
-        <annotationProcessors>
-            <annotationProcessor>org.pf4j.processor.ExtensionAnnotationProcessor</annotationProcessor>
-        </annotationProcessors>
-    </configuration>
-</plugin>
+Manifest-Version: 1.0
+Archiver-Version: Plexus Archiver
+Created-By: Apache Maven
+Built-By: decebal
+Build-Jdk: 1.6.0_17
+Plugin-Class: org.pf4j.demo.welcome.WelcomePlugin
+Plugin-Dependencies: x, y, z
+Plugin-Id: welcome-plugin
+Plugin-Provider: Decebal Suiu
+Plugin-Version: 0.0.1
 ```
 
-### Kotlin
-
-PF4J can be used in Kotlin project as well. One has to use the Kotlin annotation processing tool 
-[**kapt**](https://kotlinlang.org/docs/reference/kapt.html) for the plugin project written in Kotlin.
-The demo_gradle project contains one plugin project _plugin3_ written in Kotlin for demonstration.
-
-
-Plugin assembly
-------------------------------
-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.  
-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.  
-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.  
-
-Plugin lifecycle
---------------------------
-Each plugin passes through a pre-defined set of states. [PluginState](https://github.com/decebals/pf4j/blob/master/pf4j/src/main/java/org/pf4j/PluginState.java) defines all possible states.   
-The primary plugin states are:
-* CREATED
-* DISABLED
-* STARTED
-* STOPPED
-
-The DefaultPluginManager contains the following logic:
-* all plugins are resolved & loaded
-* *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)`
-* only *STARTED* plugins may contribute extensions. Any other state should not be considered ready to contribute an extension to the running system.
-
-The differences between a DISABLED plugin and a STARTED plugin are:
-* a STARTED plugin has executed Plugin.start(), a DISABLED plugin has not
-* a STARTED plugin may contribute extension instances, a DISABLED plugin may not
-
-DISABLED plugins still have valid class loaders and their classes can be manually
-loaded and explored, but the resource loading - which is important for inspection -
-has been handicapped by the DISABLED check.
-
-As integrators of pf4j evolve their extension APIs it will become
-a requirement to specify a minimum system version for loading plugins.
-Loading & starting a newer plugin on an older system could result in
-runtime failures due to method signature changes or other class
-differences.  
-
-For this reason was added a manifest attribute (in PluginDescriptor) to specify a 'requires' version
-which is a minimum system version on x.y.z format, or a 
-[SemVer Expression](https://github.com/zafarkhaja/jsemver#semver-expressions-api-ranges). 
-Also DefaultPluginManager contains a method to
-specify the system version of the plugin manager and the logic to disable
-plugins on load if the system version is too old (if you want total control, 
-please override `isPluginValid()`). This works for both `loadPlugins()` and `loadPlugin()`.  
-
-__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.  
-
-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).
-
-Custom PluginManager
---------------------------
-To create a custom plugin manager you could:
-* implements `PluginManager` interface (create a plugin manager from scratch)
-* modifies some aspects/behaviors of built-in implementations (`DefaultPluginManager`, `JarPluginManager`)
-* extends `AbstractPluginManager` class
-
-`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,
-on which your project depends and, of course, the classes of current project.
-`AbstractPluginManager` adds some glue that help you to create quickly a plugin manager. All you need to do is to implement some factory methods.
-PF4J uses in many places the factory method pattern to implement the dependency injection (DI) concept in a manually mode.
-See below the abstract methods for `AbstractPluginManager`:
-
-```java
-public abstract class AbstractPluginManager implements PluginManager {
-
-    protected abstract PluginRepository createPluginRepository();
-    protected abstract PluginFactory createPluginFactory();
-    protected abstract ExtensionFactory createExtensionFactory();
-    protected abstract PluginDescriptorFinder createPluginDescriptorFinder();
-    protected abstract ExtensionFinder createExtensionFinder();
-    protected abstract PluginStatusProvider createPluginStatusProvider();
-    protected abstract PluginLoader createPluginLoader();
-
-    // other non abstract methods
-
-}
-```
+In above manifest I described a plugin with id `welcome-plugin`, with class `org.pf4j.demo.welcome.WelcomePlugin`, with version `0.0.1` and with dependencies
+to plugins `x, y, z`.
 
-`DefaultPluginManager` contributes with "default" components (`DefaultExtensionFactory`, `DefaultPluginFactory`, `DefaultPluginLoader`, ...) to `AbstractPluginManager`.  
-Most of the times it's enough to extends `DefaultPluginManager` and to supply your custom components.
-Starting with version 2.0 it's possible to coexist multiple plugins types (jar, zip, directory) in the same `PluginManager`.
-For example, `DefaultPluginManager` works out of the box with zip and jar plugins. The idea is that `DefaultPluginManager` uses a compound version for:
-- `PluginDescriptorFinder` (`CompoundPluginDescriptorFinder`)
-- `PluginLoader` (`CompoundPluginLoader`)
-- `PluginRepository` (`CompoundPluginRepository`)
+Now you can play with plugins and extensions in your code:
 
 ```java
-public class DefaultPluginManager extends AbstractPluginManager {
-   
+public static void main(String[] args) {
     ...
-    
-    @Override
-    protected PluginDescriptorFinder createPluginDescriptorFinder() {
-        return new CompoundPluginDescriptorFinder()
-            .add(new PropertiesPluginDescriptorFinder())
-            .add(new ManifestPluginDescriptorFinder());
-    }
-    
-    @Override
-    protected PluginRepository createPluginRepository() {
-        return new CompoundPluginRepository()
-            .add(new DefaultPluginRepository(getPluginsRoot(), isDevelopment()))
-            .add(new JarPluginRepository(getPluginsRoot()));
-    }
-    
-    @Override
-    protected PluginLoader createPluginLoader() {
-        return new CompoundPluginLoader()
-            .add(new DefaultPluginLoader(this, pluginClasspath))
-            .add(new JarPluginLoader(this));
-    }
-
-}
-```
-
-So, it's very easy to add new strategies for plugin descriptor finder, plugin loader and plugin repository.
-
-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
-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.  
-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.   
-
-Lets describe how DEVELOPMENT runtime mode works.
-
-First, you can change the runtime mode using the "pf4j.mode" system property or overriding `DefaultPluginManager.getRuntimeMode()`.  
-For example I run the pf4j demo in eclipse in DEVELOPMENT mode adding only `"-Dpf4j.mode=development"` to the pf4j demo launcher.  
-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/org/pf4j/demo/welcome/WelcomePlugin.java)).   
-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).
-
-**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__
-Check the __“Enable Project Specific Settings”__ and make sure __“Enable annotation processing”__ is checked.  
-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.
-
-Enable/Disable plugins
--------------------
-In theory, it's a relation **1:N** between an extension point and the extensions for this extension point.   
-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.  
-In this situation you wish a possibility to disable all but one extension.   
-For example I have an extension point for sending mail (EmailSender interface) with two extensions: one based on Sendgrid and another
-based on Amazon Simple Email Service.   
-The first extension is located in Plugin1 and the second extension is located in Plugin2.   
-I want to go only with one extension ( **1:1** relation between extension point and extensions) and to achieve this I have two options:  
-1) uninstall Plugin1 or Plugin2 (remove folder pluginX.zip and pluginX from plugins folder)  
-2) disable Plugin1 or Plugin2  
-
-For option two you must create a simple file **enabled.txt** or **disabled.txt** in your plugins folder.   
-The content for **enabled.txt** is similar with:
-
-```
-########################################
-# - load only these plugins
-# - add one plugin id on each line
-# - put this file in plugins folder
-########################################
-welcome-plugin
-```
 
-The content for **disabled.txt** is similar with:
-
-```
-########################################
-# - load all plugins except these
-# - add one plugin id on each line
-# - put this file in plugins folder
-########################################
-welcome-plugin
-```
-
-All comment lines (line that start with # character) are ignored.   
-If a file with enabled.txt exists then disabled.txt is ignored. See enabled.txt and disabled.txt from the demo folder.
-
-Default/System extension
--------------------
-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/org/pf4j/demo/WhazzupGreeting.java)
-for a real example.
-
-This is great for starting application phase. In this scenario you have a minimalist plugin framework with one class loader
-(the application class loader), similar with Java [ServiceLoader](https://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html)
-but with the following benefits:
-- no need to write provider-configuration files in the resource directory `META-INF/services`, you using the elegant
- `@Extension` annotation from PF4J
-- anytime you can switch to the multiple class loader mechanism without to change one code line in your application  
-
-Of course the code present in the `Boot` class from the demo application it is functional but you can use a more minimalist code
-skipping `pluginManager.loadPlugins()` and `pluginManager.startPlugins()`.
-
-```java
-public static void main(String[] args) {
+    // create the plugin manager
     PluginManager pluginManager = new DefaultPluginManager();
+    
+    // start and load all plugins of application
     pluginManager.loadPlugins();
     pluginManager.startPlugins();
-    List<Greeting> greetings = pluginManager.getExtensions(Greeting.class);
-    for (Greeting greeting : greetings) {
-        System.out.println(">>> " + greeting.getGreeting());
-    }
-}
-```
-
-The above code can be written:
 
-```java
-public static void main(String[] args) {
-    PluginManager pluginManager = new DefaultPluginManager();
+    // retrieve all extensions for "Greeting" extension point
     List<Greeting> greetings = pluginManager.getExtensions(Greeting.class);
     for (Greeting greeting : greetings) {
         System.out.println(">>> " + greeting.getGreeting());
     }
+    
+    // stop and unload all plugins
+    pluginManager.stopPlugins();
+    pluginManager.unloadPlugins();
+    
+    ...
 }
 ```
 
-ServiceLoader interoperability
--------------------
-Starting with version 0.12 PF4J comes with a better support for `ServiceLoader`.
-PF4J can read `META-INF/services` (Java Service Provider mechanism) as extensions, so,
-if you have a modular application based on `java.util.ServiceLoader` class you can replace entirely the `ServiceLoader.load()`
-calls from your application with `PluginManager.getExtensions()` and migrate smooth from ServiceLoader to PF4J.
-
-Also you have the possibility to change the `ExtensionStorage` used in `ExtensionAnnotationProcessor`.
-By default we use the format with `META-INF/extensions.idx`
-```
-org.pf4j.demo.HowdyGreeting
-org.pf4j.demo.WhazzupGreeting
-```
+The output is:
 
-but you can use a more standard location and format, `META-INF/services/<extension-point>`, used by Java Service Provider
-(see `java.util.ServiceLoader`) via `ServiceProviderExtensionStorage` implementation.
-In this case the format of `META-INF/services/org.pf4j.demo.api.Greeting` is
 ```
-# Generated by PF4J
-org.pf4j.demo.HowdyGreeting
-org.pf4j.demo.WhazzupGreeting # pf4j extension
-```
-
-where the `org.pf4j.demo.HowdyGreeting` entry is legacy (it's not generated by PF4J) but it's seen as
-an extension of `Greeting` by PF4J (at runtime).
-
-You can plug your custom `ExtensionStorage` implementation in `ExtensionAnnotationProcessor` in two possible modes:
-- set the annotation procesor option with key `pf4j.storageClassName`
-- set the system property with key `pf4j.storageClassName`
-
-For example if I want to use `ServiceProviderExtensionStorage` then the value for the `pf4j.storageClassName` key must be
-`org.pf4j.processor.ServiceProviderExtensionStorage`
-
-**NOTE:** `ServiceLoaderExtensionFinder`, the class that lookups for extensions stored in `META-INF/services` folder, is
-not added/enabled by default. To do this please override `createExtensionFinder` from `DefaultPluginManager`:
-```java
-protected ExtensionFinder createExtensionFinder() {
-    DefaultExtensionFinder extensionFinder = super.createExtensionFinder();
-    extensionFinder.addServiceProviderExtensionFinder();
-
-    return extensionFinder;
-}
+>>> Welcome
 ```
 
-Troubleshooting
--------------------
-Below are listed some problems that may occur when attempting to use PF4J, and suggestions for solving them.
-
-- **No Extensions Found**
-
-See if you have a file `extensions.idx` in each plugin.  
-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).   
-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.   
+PF4J is very customizable and comes with a lot of goodies. Please read the documentation to discover yourself the power of this library.
 
-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).   
-
-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).
+Documentation
+---------------
+Documentation is available on [pf4j.ro](http://www.pf4j.org)
 
 Demo
--------------------
-I have a tiny demo application. The demo application is in demo folder.
-In demo/api folder I declared an extension point ( _Greeting_).  
-In demo/plugins I implemented two plugins: plugin1, plugin2 (each plugin adds an extension for _Greeting_).  
-
-To run the demo application use:  
-
-```
-./run-demo.sh (for Linux/Unix)
-./run-demo.bat (for Windows)
-```
-
-How to build
--------------------
-Requirements:
-- [Git](http://git-scm.com/)
-- JDK 7 (test with `java -version`)
-- [Apache Maven 3](http://maven.apache.org/) (test with `mvn -version`)
-
-Steps:
-- create a local clone of this repository (with `git clone https://github.com/decebals/pf4j.git`)
-- go to project's folder (with `cd pf4j`)
-- build the artifacts (with `mvn clean package` or `mvn clean install`)
-
-After above steps a folder _pf4j/target_ is created and all goodies are in that folder.
-
-Mailing list
---------------
-Much of the conversation between developers and users is managed through [mailing list] (http://groups.google.com/group/pf4j).
-
-Versioning
-------------
-PF4J will be maintained under the Semantic Versioning guidelines as much as possible.
-
-Releases will be numbered with the follow format:
-
-`<major>.<minor>.<patch>`
-
-And constructed with the following guidelines:
-
-* Breaking backward compatibility bumps the major
-* New additions without breaking backward compatibility bumps the minor
-* Bug fixes and misc changes bump the patch
-
-For more information on SemVer, please visit http://semver.org/.
-
-License
---------------
-Copyright 2012 Decebal Suiu
-
-Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with
-the License. You may obtain a copy of the License in the LICENSE file, or at:
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
-an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
-specific language governing permissions and limitations under the License.
+---------------
+Demo applications are available in [demo](https://github.com/decebals/pf4j/tree/master/demo) folder