├── .gitignore ├── LICENSE ├── README.md ├── _config.yml ├── shared-interfaces ├── README.md ├── pom.xml ├── src │ └── main │ │ └── java │ │ └── com │ │ └── curisprofound │ │ └── plugins │ │ └── PluginInterface.java └── target │ ├── classes │ └── com │ │ └── curisprofound │ │ └── plugins │ │ └── PluginInterface.class │ ├── maven-archiver │ └── pom.properties │ ├── maven-status │ └── maven-compiler-plugin │ │ ├── compile │ │ └── default-compile │ │ │ ├── createdFiles.lst │ │ │ └── inputFiles.lst │ │ └── testCompile │ │ └── default-testCompile │ │ └── inputFiles.lst │ └── shared-plugin-interfaces-1.1.0.jar ├── simple-plugin-container ├── README.md ├── pom.xml ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── curisprofound │ │ │ │ └── plugins │ │ │ │ └── simple │ │ │ │ └── Container.java │ │ └── resources │ │ │ └── assembly.xml │ └── test │ │ ├── lib │ │ ├── java-semver-0.9.0.jar │ │ ├── pf4j-2.3.0.jar │ │ ├── shared-plugin-interface-1.0-SNAPSHOT.jar │ │ ├── slf4j-api-1.7.5.jar │ │ └── slf4j-simple-1.7.5.jar │ │ ├── plugins │ │ ├── test-plugin-simple-1.0-SNAPSHOT.zip │ │ └── test-plugin-simple-1.0-SNAPSHOT │ │ │ ├── classes │ │ │ ├── META-INF │ │ │ │ ├── MANIFEST.MF │ │ │ │ ├── extensions.idx │ │ │ │ └── maven │ │ │ │ │ └── com.curisprofound │ │ │ │ │ └── test-plugin-simple │ │ │ │ │ ├── pom.properties │ │ │ │ │ └── pom.xml │ │ │ ├── assembly.xml │ │ │ ├── com │ │ │ │ └── curisprofound │ │ │ │ │ └── plugins │ │ │ │ │ └── simple │ │ │ │ │ ├── HelloPlugin$HelloGreeting.class │ │ │ │ │ └── HelloPlugin.class │ │ │ └── plugin.properties │ │ │ ├── lib │ │ │ ├── java-semver-0.9.0.jar │ │ │ └── pf4j-2.3.0.jar │ │ │ └── plugin.properties │ │ ├── simple-plugin-container-1.0-SNAPSHOT.jar │ │ └── simple-plugin-container-1.0-SNAPSHOT.zip └── target │ ├── classes │ ├── assembly.xml │ └── com │ │ └── curisprofound │ │ └── plugins │ │ └── simple │ │ └── Container.class │ ├── lib │ ├── java-semver-0.9.0.jar │ ├── pf4j-2.3.0.jar │ ├── shared-plugin-interfaces-1.1.0.jar │ ├── slf4j-api-1.7.5.jar │ └── slf4j-simple-1.7.5.jar │ ├── maven-archiver │ └── pom.properties │ ├── maven-status │ └── maven-compiler-plugin │ │ ├── compile │ │ └── default-compile │ │ │ ├── createdFiles.lst │ │ │ └── inputFiles.lst │ │ └── testCompile │ │ └── default-testCompile │ │ └── inputFiles.lst │ ├── plugins │ ├── test-plugin-simple-1.0-SNAPSHOT.zip │ └── test-plugin-simple-1.0-SNAPSHOT │ │ ├── classes │ │ ├── META-INF │ │ │ └── extensions.idx │ │ ├── assembly.xml │ │ ├── com │ │ │ └── curisprofound │ │ │ │ └── plugins │ │ │ │ └── simple │ │ │ │ ├── SimplePlugin$SimpleIdentityPlugin.class │ │ │ │ └── SimplePlugin.class │ │ └── plugin.properties │ │ ├── lib │ │ ├── java-semver-0.9.0.jar │ │ └── pf4j-2.3.0.jar │ │ └── plugin.properties │ ├── simple-plugin-container-1.0-SNAPSHOT.jar │ ├── simple-plugin-container-1.0-SNAPSHOT.zip │ └── simple-plugin-container-1.0-SNAPSHOT │ ├── lib │ ├── java-semver-0.9.0.jar │ ├── pf4j-2.3.0.jar │ ├── shared-plugin-interfaces-1.1.0.jar │ ├── slf4j-api-1.7.5.jar │ └── slf4j-simple-1.7.5.jar │ ├── plugins │ ├── test-plugin-simple-1.0-SNAPSHOT.zip │ └── test-plugin-simple-1.0-SNAPSHOT │ │ ├── classes │ │ ├── META-INF │ │ │ └── extensions.idx │ │ ├── assembly.xml │ │ ├── com │ │ │ └── curisprofound │ │ │ │ └── plugins │ │ │ │ └── simple │ │ │ │ ├── SimplePlugin$SimpleIdentityPlugin.class │ │ │ │ └── SimplePlugin.class │ │ └── plugin.properties │ │ ├── lib │ │ ├── java-semver-0.9.0.jar │ │ └── pf4j-2.3.0.jar │ │ └── plugin.properties │ └── simple-plugin-container-1.0-SNAPSHOT.jar ├── simple-test-plugin ├── README.md ├── pom.xml ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── curisprofound │ │ │ └── plugins │ │ │ └── simple │ │ │ └── SimplePlugin.java │ │ └── resources │ │ ├── assembly.xml │ │ └── plugin.properties └── target │ ├── classes │ ├── META-INF │ │ └── extensions.idx │ ├── assembly.xml │ ├── com │ │ └── curisprofound │ │ │ └── plugins │ │ │ └── simple │ │ │ ├── SimplePlugin$SimpleIdentityPlugin.class │ │ │ └── SimplePlugin.class │ └── plugin.properties │ ├── maven-archiver │ └── pom.properties │ ├── maven-status │ └── maven-compiler-plugin │ │ ├── compile │ │ └── default-compile │ │ │ ├── createdFiles.lst │ │ │ └── inputFiles.lst │ │ └── testCompile │ │ └── default-testCompile │ │ └── inputFiles.lst │ ├── test-plugin-simple-1.0-SNAPSHOT.jar │ ├── test-plugin-simple-1.0-SNAPSHOT.zip │ └── test-plugin-simple-1.0-SNAPSHOT │ ├── classes │ ├── META-INF │ │ └── extensions.idx │ ├── assembly.xml │ ├── com │ │ └── curisprofound │ │ │ └── plugins │ │ │ └── simple │ │ │ ├── SimplePlugin$SimpleIdentityPlugin.class │ │ │ └── SimplePlugin.class │ └── plugin.properties │ ├── lib │ ├── java-semver-0.9.0.jar │ └── pf4j-2.3.0.jar │ └── plugin.properties ├── spring-plugin-container ├── .gitignore ├── README.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── curisprofound │ │ │ └── springplugincontainer │ │ │ ├── AppConfig.java │ │ │ ├── PluginConfig.java │ │ │ └── SpringPluginContainerApplication.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── curisprofound │ └── springplugincontainer │ └── SpringPluginContainerApplicationTests.java └── spring-test-plugin ├── .gitignore ├── README.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── curisprofound │ │ └── springtestplugin │ │ ├── ApplicationConfiguration.java │ │ ├── GreetProvider.java │ │ ├── PluginController.java │ │ ├── SpringSamplePlugin.java │ │ └── SpringTestPluginApplication.java └── resources │ ├── application.properties │ ├── assembly.xml │ └── plugin.properties └── test └── java └── com └── curisprofound └── springtestplugin └── SpringTestPluginApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | /plugins/ 19 | .mvn 20 | 21 | ### NetBeans ### 22 | /nbproject/private/ 23 | /build/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | 29 | /docs/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 ralemy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Extending applications with PF4J plugins 2 | 3 | This repository provides a tutorial and an example on how to use PF4J to 4 | develop an application that supports extension through the development of third-party plugins. 5 | 6 | # User Stories: 7 | 8 | Initially, we focus on the minimum amount of code to create a java applicaiton with plugin support. 9 | 10 | |Story 01 |Should be able to add functionality via plugin| 11 | |---------|-----------------------| 12 | |As an | Application Developer| 13 | |I need to| support plugins in my applications| 14 | |So that| they can be extended by third-party collaborators| 15 | 16 | Then, we extend the functionality to include container and plugins built with 17 | Spring boot 18 | 19 | |Story 02 |Should be able to use Spring boot in container and plugins| 20 | |---------|-----------------------| 21 | |As an | Spring boot Developer| 22 | |I need to| support plugins written based on Spring| 23 | |So that| I can use Spring functionality such as dependency injection in my plugins| 24 | 25 | Next, we add a new Restful endpoint using an annotated class 26 | 27 | |Story 03 |Should be able to add to Restful endpoints from plugin | 28 | |---------|-----------------------| 29 | |As an | Spring boot Developer| 30 | |I need to| be able to add new endpoints via the plugin| 31 | |So that| provide access to the extended functionality via RESTful interfaces| 32 | 33 | Finally, we add a reactive Restful endpoint using a Router Function 34 | 35 | |Story 04 |Should be able to add reactive endpoints from plugin | 36 | |---------|-----------------------| 37 | |As an | Spring boot Developer| 38 | |I need to| be able to add reactive endpoints via the plugin| 39 | |So that| provide the benefits of reactive programing with my plugins| 40 | 41 | 42 | ## Contents 43 | 44 | [Overview](#overview) ....................... Explains the reasons behind creation of this repository 45 | 46 | [Shared Interface](./shared-interfaces)............. Allows plugins to interact with the container and eachother 47 | 48 | [Baremetal Plugin](./simple-test-plugin)............. Minimal java code that creates a functioning plugin 49 | 50 | [Baremetal Container](./simple-plugin-container)....... Minimal java code that creates standalone plugin container 51 | 52 | [Spring boot Plugin](./spring-test-plugin)............ A plugin that uses Spring boot for DI and adds endpoints created with annotated classes or router functions. 53 | 54 | [Spring boot Container](./spring-plugin-container)...... A Spring boot app with plugin support and reactive Restful service 55 | 56 | # Overview 57 | 58 | Plugins are pieces of software, developed by a third-party, that extend the functionality of a given application. There are many benefits to an application that supports plugins, such as flexibility to accommodate new requirements, extensibility to integrate in new environments, and suitability in being able to create tailored solutions by including only the functionality required for a specific use case. 59 | 60 | There are multiple ways of adding plugin support to a Java application. The powerful, age-old and very complex OSGI network to the use of simple and limited-functionality mechanisms inherent in Java 8. One good mid-way compromise which is neither too simplistic nor too complicated is Pf4J, although by no means this is to claim that it is the only or even the best solution for the problem. 61 | 62 | [PF4J][] is a plugin framework for Java, that allows third party developers to extend the functionality of an application by adding a zip file to the plugins folder that will be loaded at 63 | runtime. Extensions can also be added to the application jar file if needed. the [PF4J][] Github repository has an example of how to develop such an application, along with comprehensive documentation on the framework. While these should certainly be studied, in our experience understanding the examples was not easy for the average developers and we decided to contribure by creating this tutorial. 64 | 65 | This project demonstrates the same concepts in a more practical way. Three separate maven projects are created, and a scenario is presented on how the solution will be created and deployed. 66 | 67 | ## Actors 68 | 69 | In the plugin management system, two main actors are at play. the **application owner**, who creates the application which accepts the plugin, and published the interfaces that the second actor, the **plugin developer**, implements to develop extended functionality. 70 | 71 | ## Artifacts 72 | 73 | The solution involves three components. the first component is in the form of a java interface, which is developed by the application owner and consists of a series of mehtods that plugin developer implements to extend the application. This component should be typically published in maven repository or some similar place and is added to the other two components as a dependency. 74 | 75 | the second component is the container, which is also developed by the application owner. Once started, the plugin container scans the plugins folder and reads and unzips the plugins in that folder, loading the classes and starting and stopping the plugins. 76 | 77 | 78 | the third component is the plugin itself. it is developed by the plugin developer and implements some of the shared interfaces defined in the first component and together with its own dependencies resides as a zip file in the plugins directory where the container will be looking for it. 79 | 80 | 81 | The following table summarizes the above concepts 82 | 83 | |Artifact| Actor| Description| 84 | |--------|------|------------| 85 | |Container| Application Owner| the main application, scans a directory that contains plugins as zip files, decompresses and imports them into the application| 86 | |Shared Interfaces|Application Owner| the collection of interfaces that plugins implement. published by the application owner and included as a dependency by both the plugin-container and the plugin| 87 | |Plugin| Plugin Developer| the implementation of one or more interfaces defined in Shared-Plugin-Interfaces. deployed as a zip file of classes and their dependencies inside the plugins directory of the Plugin-Container| 88 | 89 | [Back to Contents](#contents) 90 | 91 | [PF4J]: https://github.com/pf4j/pf4j 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /shared-interfaces/README.md: -------------------------------------------------------------------------------- 1 | # The Shared Interfaces component 2 | 3 | This component will only contain the interfaces that the plugins are supposed to implement. What is important to keep in mind is that it should try not to force dependencies on plugins. For example, if it is defining an interface that will 4 | be implemented by Spring-based and non-Spring-based plugins, it should not use Spring specific classes and types as parameters or return values. 5 | 6 | It is noteworthy that more than one shared interface component can exist, and while the container will reference all of them, the plugins need only reference 7 | the ones they are implementing. 8 | 9 | For the purposes of our tutorial, we have a very small pom file. 10 | 11 | 12 | ```xml 13 | 14 | 17 | 4.0.0 18 | 19 | com.curis-profound 20 | shared-plugin-interfaces 21 | 1.1.0 22 | 23 | 24 | 25 | 26 | 27 | org.pf4j 28 | pf4j 29 | 2.3.0 30 | 31 | 32 | 33 | 34 | ``` 35 | 36 | the ```org.pf4j``` dependency is the only one necessay, and will provide the superclass for interface to define: 37 | 38 | ```java 39 | package com.curisprofound.plugins; 40 | 41 | import org.pf4j.ExtensionPoint; 42 | import java.util.List; 43 | 44 | public interface PluginInterface extends ExtensionPoint { 45 | String identify(); 46 | List reactiveRoutes(); 47 | List mvcControllers(); 48 | } 49 | ``` 50 | 51 | Three methods are defined. the first one is used by plugins to introduce themselves to the application with a human-readable phrase. The second one is for Spring-based plugins to add new reactive endpoints using ```RouterFunction``` objects, and the third endpoint is again for Spring-based plugins to add Restful endpoints using annotated classes. 52 | 53 | 54 | and we use a simple command to generate the jar file: 55 | 56 | ```bash 57 | mvn clean package 58 | ``` 59 | 60 | The jar is then published to a repository and can be included in dependent projects in a standard way 61 | 62 | ```xml 63 | 64 | 65 | com.curisprofound 66 | shared-plugin-interfaces 67 | 1.1.0 68 | 69 | 70 | ``` 71 | 72 | If you have the jar locally, you can add it to the local maven repo so the other projects could include it as if it was received from upstream. 73 | 74 | ```bash 75 | mvn install:install-file \ 76 | -Dfile=target/shared-plugin-interfaces-1.1.0.jar \ 77 | -DgroupId=com.curisprofound \ 78 | -DartifactId=shared-plugin-interfaces \ 79 | -Dversion=1.1.0 \ 80 | -Dpackaging=jar 81 | ``` 82 | 83 | [Back to Contents](../#contents) 84 | 85 | [PF4J]: https://github.com/pf4j/pf4j 86 | 87 | -------------------------------------------------------------------------------- /shared-interfaces/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.curis-profound 8 | shared-plugin-interfaces 9 | 1.1.0 10 | 11 | 12 | 13 | org.pf4j 14 | pf4j 15 | 2.3.0 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /shared-interfaces/src/main/java/com/curisprofound/plugins/PluginInterface.java: -------------------------------------------------------------------------------- 1 | package com.curisprofound.plugins; 2 | 3 | import org.pf4j.ExtensionPoint; 4 | 5 | import java.util.List; 6 | 7 | public interface PluginInterface extends ExtensionPoint { 8 | String identify(); 9 | List reactiveRoutes(); 10 | List mvcControllers(); 11 | } 12 | -------------------------------------------------------------------------------- /shared-interfaces/target/classes/com/curisprofound/plugins/PluginInterface.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/shared-interfaces/target/classes/com/curisprofound/plugins/PluginInterface.class -------------------------------------------------------------------------------- /shared-interfaces/target/maven-archiver/pom.properties: -------------------------------------------------------------------------------- 1 | #Created by Apache Maven 3.5.3 2 | version=1.1.0 3 | groupId=com.curis-profound 4 | artifactId=shared-plugin-interfaces 5 | -------------------------------------------------------------------------------- /shared-interfaces/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst: -------------------------------------------------------------------------------- 1 | com/curisprofound/plugins/PluginInterface.class 2 | -------------------------------------------------------------------------------- /shared-interfaces/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst: -------------------------------------------------------------------------------- 1 | /Users/rezaalemy/Projects/Spring/pf4j-sample/sharedplugininterfaces/src/main/java/com/curisprofound/plugins/PluginInterface.java 2 | -------------------------------------------------------------------------------- /shared-interfaces/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/shared-interfaces/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst -------------------------------------------------------------------------------- /shared-interfaces/target/shared-plugin-interfaces-1.1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/shared-interfaces/target/shared-plugin-interfaces-1.1.0.jar -------------------------------------------------------------------------------- /simple-plugin-container/README.md: -------------------------------------------------------------------------------- 1 | # The plugin-container application 2 | 3 | For the application to support the plugins, we need the pom file to depend at least on PF4J and the shared plugin interfaces. if you are planning to use the slf4j logger, include the slf4j-simple package 4 | as well. 5 | 6 | 7 | ```xml 8 | 9 | 12 | 4.0.0 13 | 14 | com.curisprofound 15 | simple-plugin-container 16 | 1.0-SNAPSHOT 17 | 18 | 19 | 20 | 21 | org.pf4j 22 | pf4j 23 | 2.3.0 24 | 25 | 26 | 27 | 28 | org.slf4j 29 | slf4j-simple 30 | 1.7.5 31 | 32 | 33 | 34 | com.curisprofound 35 | shared-plugin-interfaces 36 | 1.0-SNAPSHOT 37 | 38 | 39 | 40 | 41 | ``` 42 | in the build section, the following plugins are included: 43 | 44 | * To compile the classes (using class level 8 to be able to use lambda and other extensions): 45 | 46 | ```xml 47 | 48 | org.apache.maven.plugins 49 | maven-compiler-plugin 50 | 51 | 8 52 | 8 53 | 54 | 55 | ``` 56 | * to create an executable jar which looks in lib/ for its dependencies 57 | 58 | ```xml 59 | 60 | org.apache.maven.plugins 61 | maven-jar-plugin 62 | 2.3.1 63 | 64 | 65 | 66 | true 67 | lib/ 68 | com.curisprofound.plugins.simple.Container 69 | 70 | 71 | 72 | 73 | 74 | ``` 75 | 76 | * to create a zip file that would layout the application correctly, we use the maven assembly plugin: 77 | 78 | ```xml 79 | 80 | maven-assembly-plugin 81 | 2.3 82 | 83 | 84 | 85 | src/main/resources/assembly.xml 86 | 87 | 88 | false 89 | 90 | 91 | 92 | make-assembly 93 | package 94 | 95 | attached 96 | 97 | 98 | 99 | 100 | ``` 101 | 102 | The assembly plugin will look in ```src/main/resources/assembly.xml``` to see how to create the zip file for the main application. 103 | 104 | here, we create a lib/ subdirectory and put all the dependencies in it: 105 | 106 | ```xml 107 | 108 | 109 | false 110 | lib 111 | 112 | *:jar:* 113 | 114 | 115 | 116 | ``` 117 | we put the executable jar in the root of the unzipped directory and create an empty plugins directory where third party plugins will reside 118 | 119 | ```xml 120 | 121 | 122 | ${project.build.directory} 123 | 124 | 125 | *.jar 126 | 127 | 128 | *-javadoc.jar 129 | *-sources.jar 130 | 131 | 132 | 133 | . 134 | plugins 135 | 136 | */** 137 | 138 | 139 | 140 | ``` 141 | 142 | So, our plugin container code requires to create a new PluginManager object, 143 | tell it to load and start plugins and then use it to access the plugins. here 144 | is the minimal code for that: 145 | 146 | ```java 147 | package com.curisprofound.plugins.simple; 148 | 149 | 150 | public class Container { 151 | 152 | private static final Logger log = LoggerFactory.getLogger(Container.class); 153 | 154 | public static void main(String[] args){ 155 | 156 | final PluginManager pm = new DefaultPluginManager(); 157 | pm.loadPlugins(); 158 | pm.startPlugins(); 159 | 160 | final List plugins = pm.getExtensions(PluginInterface.class); 161 | 162 | log.info(MessageFormat.format(" {0} Plugin(s) found ", String.valueOf(plugins.size()))); 163 | 164 | plugins.forEach(g -> 165 | log.info(MessageFormat.format(" {0}: {1}", 166 | g.getClass().getCanonicalName(), 167 | g.identify()))); 168 | 169 | pm.stopPlugins(); 170 | 171 | } 172 | } 173 | 174 | ``` 175 | 176 | build the project with 177 | ```bash 178 | mvn clean package 179 | ``` 180 | 181 | this will create, among other things, a zip file in the target directory. 182 | 183 | ```bash 184 | unzip target/simple-plugin-container-1.0-SNAPSHOT.zip 185 | cd target/simple-plugin-container-1.0-SNAPSHOT 186 | java -jar simple-plugin-container-1.0-SNAPSHOT.jar 187 | ``` 188 | The output is as expected for an empty plugins directory 189 | 190 | ```bash 191 | [main] INFO org.pf4j.DefaultPluginStatusProvider - Enabled plugins: [] 192 | [main] INFO org.pf4j.DefaultPluginStatusProvider - Disabled plugins: [] 193 | [main] INFO org.pf4j.DefaultPluginManager - PF4J version 2.3.0 in 'deployment' mode 194 | [main] INFO org.pf4j.AbstractPluginManager - No plugins 195 | [main] INFO com.curisprofound.plugins.simple.Container - 0 Plugin(s) found 196 | ``` 197 | 198 | Once a plugin is created, copy it to the plugin directory. for example, the ```simple-test-plugin``` project will create a ```target/test-plugin-simple-1.0-SNAPSHOT.zip``` when built with ```mvn clean package```. Copy this file to the plugins directory and run the container again to see the plugin discovered and working 199 | 200 | ```bash 201 | $ cd target/simple-plugin-container-1.0-SNAPSHOT 202 | $ cp ../../../simple-test-plugin/target/test-plugin-simple-1.0-SNAPSHOT.zip ./plugins/ 203 | $ java -jar simple-plugin-container-1.0-SNAPSHOT.jar 204 | [main] INFO org.pf4j.DefaultPluginStatusProvider - Enabled plugins: [] 205 | [main] INFO org.pf4j.DefaultPluginStatusProvider - Disabled plugins: [] 206 | [main] INFO org.pf4j.DefaultPluginManager - PF4J version 2.3.0 in 'deployment' mode 207 | [main] INFO org.pf4j.util.FileUtils - Expanded plugin zip 'test-plugin-simple-1.0-SNAPSHOT.zip' in 'test-plugin-simple-1.0-SNAPSHOT' 208 | [main] INFO org.pf4j.AbstractPluginManager - Plugin 'simple-identity-plugin@0.0.1' resolved 209 | [main] INFO org.pf4j.AbstractPluginManager - Start plugin 'simple-identity-plugin@0.0.1' 210 | [main] INFO com.curisprofound.plugins.simple.SimplePlugin - Simple Plugin Started 211 | [main] INFO com.curisprofound.plugins.simple.Container - 1 Plugin(s) found 212 | [main] INFO com.curisprofound.plugins.simple.Container - com.curisprofound.plugins.simple.SimplePlugin.SimpleIdentityPlugin: A simple plugin with no dependency on Spring 213 | [main] INFO org.pf4j.AbstractPluginManager - Stop plugin 'simple-identity-plugin@0.0.1' 214 | [main] INFO com.curisprofound.plugins.simple.SimplePlugin - Simple Plugin Stopped 215 | 216 | ``` 217 | 218 | [Back to Contents](../#contents) 219 | 220 | 221 | [PF4J]: https://github.com/pf4j/pf4j 222 | 223 | -------------------------------------------------------------------------------- /simple-plugin-container/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.curisprofound 8 | simple-plugin-container 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | org.pf4j 15 | pf4j 16 | 2.3.0 17 | 18 | 19 | 20 | 21 | org.slf4j 22 | slf4j-simple 23 | 1.7.5 24 | 25 | 26 | 27 | com.curisprofound 28 | shared-plugin-interfaces 29 | 1.1.0 30 | 31 | 32 | 33 | 34 | 35 | 36 | maven-assembly-plugin 37 | 2.3 38 | 39 | 40 | 41 | src/main/resources/assembly.xml 42 | 43 | 44 | false 45 | 46 | 47 | 48 | make-assembly 49 | package 50 | 51 | attached 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.apache.maven.plugins 59 | maven-jar-plugin 60 | 2.3.1 61 | 62 | 63 | 64 | true 65 | lib/ 66 | com.curisprofound.plugins.simple.Container 67 | 68 | 69 | 70 | 71 | 72 | 73 | maven-deploy-plugin 74 | 75 | true 76 | 77 | 78 | 79 | org.apache.maven.plugins 80 | maven-compiler-plugin 81 | 82 | 8 83 | 8 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /simple-plugin-container/src/main/java/com/curisprofound/plugins/simple/Container.java: -------------------------------------------------------------------------------- 1 | package com.curisprofound.plugins.simple; 2 | 3 | import com.curisprofound.plugins.PluginInterface; 4 | import org.pf4j.DefaultPluginManager; 5 | import org.pf4j.PluginManager; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.text.MessageFormat; 10 | import java.util.List; 11 | 12 | public class Container { 13 | 14 | private static final Logger log = LoggerFactory.getLogger(Container.class); 15 | 16 | public static void main(String[] args){ 17 | final PluginManager pm = new DefaultPluginManager(); 18 | 19 | pm.loadPlugins(); 20 | 21 | pm.startPlugins(); 22 | 23 | final List plugins = pm.getExtensions(PluginInterface.class); 24 | 25 | log.info(MessageFormat.format(" {0} Plugin(s) found ", String.valueOf(plugins.size()))); 26 | 27 | plugins.forEach(g -> 28 | log.info(MessageFormat.format(" {0}: {1}", 29 | g.getClass().getCanonicalName(), 30 | g.identify()))); 31 | 32 | pm.stopPlugins(); 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /simple-plugin-container/src/main/resources/assembly.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | app 9 | 10 | dir 11 | zip 12 | 13 | false 14 | 15 | 16 | false 17 | lib 18 | 19 | *:jar:* 20 | 21 | 22 | 23 | 24 | 25 | ${project.build.directory} 26 | 27 | 28 | *.jar 29 | 30 | 31 | *-javadoc.jar 32 | *-sources.jar 33 | 34 | 35 | 36 | . 37 | plugins 38 | 39 | */** 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /simple-plugin-container/src/test/lib/java-semver-0.9.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/src/test/lib/java-semver-0.9.0.jar -------------------------------------------------------------------------------- /simple-plugin-container/src/test/lib/pf4j-2.3.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/src/test/lib/pf4j-2.3.0.jar -------------------------------------------------------------------------------- /simple-plugin-container/src/test/lib/shared-plugin-interface-1.0-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/src/test/lib/shared-plugin-interface-1.0-SNAPSHOT.jar -------------------------------------------------------------------------------- /simple-plugin-container/src/test/lib/slf4j-api-1.7.5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/src/test/lib/slf4j-api-1.7.5.jar -------------------------------------------------------------------------------- /simple-plugin-container/src/test/lib/slf4j-simple-1.7.5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/src/test/lib/slf4j-simple-1.7.5.jar -------------------------------------------------------------------------------- /simple-plugin-container/src/test/plugins/test-plugin-simple-1.0-SNAPSHOT.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/src/test/plugins/test-plugin-simple-1.0-SNAPSHOT.zip -------------------------------------------------------------------------------- /simple-plugin-container/src/test/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Plugin-Dependencies: 3 | Plugin-Id: simple-hello-plugin 4 | Archiver-Version: Plexus Archiver 5 | Built-By: rezaalemy 6 | Plugin-Provider: Curis Profound 7 | Plugin-Version: 0.0.1 8 | Plugin-Class: com.curisprofound.plugins.simple.HelloPlugin 9 | Created-By: Apache Maven 3.5.3 10 | Build-Jdk: 1.8.0_161 11 | 12 | -------------------------------------------------------------------------------- /simple-plugin-container/src/test/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/META-INF/extensions.idx: -------------------------------------------------------------------------------- 1 | # Generated by PF4J 2 | com.curisprofound.plugins.simple.HelloPlugin$HelloGreeting 3 | -------------------------------------------------------------------------------- /simple-plugin-container/src/test/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/META-INF/maven/com.curisprofound/test-plugin-simple/pom.properties: -------------------------------------------------------------------------------- 1 | #Generated by Maven 2 | #Wed Jul 25 15:02:13 PDT 2018 3 | version=1.0-SNAPSHOT 4 | groupId=com.curisprofound 5 | artifactId=test-plugin-simple 6 | -------------------------------------------------------------------------------- /simple-plugin-container/src/test/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/META-INF/maven/com.curisprofound/test-plugin-simple/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.curisprofound 8 | test-plugin-simple 9 | 1.0-SNAPSHOT 10 | Simple Test Plugin 11 | jar 12 | 13 | 14 | simple-hello-plugin 15 | com.curisprofound.plugins.simple.HelloPlugin 16 | 0.0.1 17 | Curis Profound 18 | 19 | 20 | 21 | 22 | 23 | 24 | org.pf4j 25 | pf4j 26 | 2.3.0 27 | 28 | 29 | com.curisprofound 30 | shared-plugin-interface 31 | 1.0-SNAPSHOT 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.apache.maven.plugins 39 | maven-antrun-plugin 40 | 1.6 41 | 42 | 43 | unzip jar file 44 | package 45 | 46 | 47 | 48 | 49 | 50 | 51 | run 52 | 53 | 54 | 55 | 56 | 57 | 58 | maven-assembly-plugin 59 | 2.3 60 | 61 | 62 | 63 | src/main/resources/assembly.xml 64 | 65 | 66 | false 67 | 68 | 69 | 70 | make-assembly 71 | package 72 | 73 | attached 74 | 75 | 76 | 77 | 78 | 79 | 80 | org.apache.maven.plugins 81 | maven-jar-plugin 82 | 2.4 83 | 84 | 85 | 86 | ${plugin.id} 87 | ${plugin.class} 88 | ${plugin.version} 89 | ${plugin.provider} 90 | ${plugin.dependencies} 91 | 92 | 93 | 94 | 95 | 96 | 97 | maven-deploy-plugin 98 | 99 | true 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /simple-plugin-container/src/test/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/assembly.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | plugin 9 | 10 | zip 11 | 12 | false 13 | 14 | 15 | false 16 | runtime 17 | lib 18 | 19 | *:jar:* 20 | 21 | 22 | org.slf4j:* 23 | com.curisprofound:shared-plugin-interface 24 | 25 | 26 | 27 | 28 | 29 | target/plugin-classes 30 | classes 31 | 32 | 33 | src/main/resources 34 | 35 | 36 | plugin.properties 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /simple-plugin-container/src/test/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/com/curisprofound/plugins/simple/HelloPlugin$HelloGreeting.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/src/test/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/com/curisprofound/plugins/simple/HelloPlugin$HelloGreeting.class -------------------------------------------------------------------------------- /simple-plugin-container/src/test/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/com/curisprofound/plugins/simple/HelloPlugin.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/src/test/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/com/curisprofound/plugins/simple/HelloPlugin.class -------------------------------------------------------------------------------- /simple-plugin-container/src/test/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/plugin.properties: -------------------------------------------------------------------------------- 1 | plugin.id=simple-hello-plugin 2 | plugin.class=com.curisprofound.plugins.simple.HelloPlugin 3 | plugin.version=0.0.1 4 | plugin.provider=Curis Profound 5 | plugin.dependencies= 6 | 7 | -------------------------------------------------------------------------------- /simple-plugin-container/src/test/plugins/test-plugin-simple-1.0-SNAPSHOT/lib/java-semver-0.9.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/src/test/plugins/test-plugin-simple-1.0-SNAPSHOT/lib/java-semver-0.9.0.jar -------------------------------------------------------------------------------- /simple-plugin-container/src/test/plugins/test-plugin-simple-1.0-SNAPSHOT/lib/pf4j-2.3.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/src/test/plugins/test-plugin-simple-1.0-SNAPSHOT/lib/pf4j-2.3.0.jar -------------------------------------------------------------------------------- /simple-plugin-container/src/test/plugins/test-plugin-simple-1.0-SNAPSHOT/plugin.properties: -------------------------------------------------------------------------------- 1 | plugin.id=simple-hello-plugin 2 | plugin.class=com.curisprofound.plugins.simple.HelloPlugin 3 | plugin.version=0.0.1 4 | plugin.provider=Curis Profound 5 | plugin.dependencies= 6 | 7 | -------------------------------------------------------------------------------- /simple-plugin-container/src/test/simple-plugin-container-1.0-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/src/test/simple-plugin-container-1.0-SNAPSHOT.jar -------------------------------------------------------------------------------- /simple-plugin-container/src/test/simple-plugin-container-1.0-SNAPSHOT.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/src/test/simple-plugin-container-1.0-SNAPSHOT.zip -------------------------------------------------------------------------------- /simple-plugin-container/target/classes/assembly.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | app 9 | 10 | dir 11 | zip 12 | 13 | false 14 | 15 | 16 | false 17 | lib 18 | 19 | *:jar:* 20 | 21 | 22 | 23 | 24 | 25 | ${project.build.directory} 26 | 27 | 28 | *.jar 29 | 30 | 31 | *-javadoc.jar 32 | *-sources.jar 33 | 34 | 35 | 36 | . 37 | plugins 38 | 39 | */** 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /simple-plugin-container/target/classes/com/curisprofound/plugins/simple/Container.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/classes/com/curisprofound/plugins/simple/Container.class -------------------------------------------------------------------------------- /simple-plugin-container/target/lib/java-semver-0.9.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/lib/java-semver-0.9.0.jar -------------------------------------------------------------------------------- /simple-plugin-container/target/lib/pf4j-2.3.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/lib/pf4j-2.3.0.jar -------------------------------------------------------------------------------- /simple-plugin-container/target/lib/shared-plugin-interfaces-1.1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/lib/shared-plugin-interfaces-1.1.0.jar -------------------------------------------------------------------------------- /simple-plugin-container/target/lib/slf4j-api-1.7.5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/lib/slf4j-api-1.7.5.jar -------------------------------------------------------------------------------- /simple-plugin-container/target/lib/slf4j-simple-1.7.5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/lib/slf4j-simple-1.7.5.jar -------------------------------------------------------------------------------- /simple-plugin-container/target/maven-archiver/pom.properties: -------------------------------------------------------------------------------- 1 | #Generated by Maven 2 | #Sun Jul 29 01:20:49 PDT 2018 3 | version=1.0-SNAPSHOT 4 | groupId=com.curisprofound 5 | artifactId=simple-plugin-container 6 | -------------------------------------------------------------------------------- /simple-plugin-container/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst: -------------------------------------------------------------------------------- 1 | com/curisprofound/plugins/simple/Container.class 2 | -------------------------------------------------------------------------------- /simple-plugin-container/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst: -------------------------------------------------------------------------------- 1 | /Users/rezaalemy/Projects/Spring/pf4j-sample/simple-plugin-container/src/main/java/com/curisprofound/plugins/simple/Container.java 2 | -------------------------------------------------------------------------------- /simple-plugin-container/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst -------------------------------------------------------------------------------- /simple-plugin-container/target/plugins/test-plugin-simple-1.0-SNAPSHOT.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/plugins/test-plugin-simple-1.0-SNAPSHOT.zip -------------------------------------------------------------------------------- /simple-plugin-container/target/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/META-INF/extensions.idx: -------------------------------------------------------------------------------- 1 | # Generated by PF4J 2 | com.curisprofound.plugins.simple.SimplePlugin$SimpleIdentityPlugin 3 | -------------------------------------------------------------------------------- /simple-plugin-container/target/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/assembly.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | plugin 9 | 10 | zip 11 | 12 | false 13 | 14 | 15 | false 16 | runtime 17 | lib 18 | 19 | *:jar:* 20 | 21 | 22 | org.slf4j:* 23 | com.curisprofound:* 24 | 25 | 26 | 27 | 28 | 29 | target/classes 30 | classes 31 | 32 | 33 | src/main/resources 34 | 35 | 36 | plugin.properties 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /simple-plugin-container/target/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/com/curisprofound/plugins/simple/SimplePlugin$SimpleIdentityPlugin.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/com/curisprofound/plugins/simple/SimplePlugin$SimpleIdentityPlugin.class -------------------------------------------------------------------------------- /simple-plugin-container/target/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/com/curisprofound/plugins/simple/SimplePlugin.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/com/curisprofound/plugins/simple/SimplePlugin.class -------------------------------------------------------------------------------- /simple-plugin-container/target/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/plugin.properties: -------------------------------------------------------------------------------- 1 | plugin.id=simple-identity-plugin 2 | plugin.class=com.curisprofound.plugins.simple.SimplePlugin 3 | plugin.version=0.0.1 4 | plugin.provider=Curis Profound 5 | plugin.dependencies= 6 | 7 | -------------------------------------------------------------------------------- /simple-plugin-container/target/plugins/test-plugin-simple-1.0-SNAPSHOT/lib/java-semver-0.9.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/plugins/test-plugin-simple-1.0-SNAPSHOT/lib/java-semver-0.9.0.jar -------------------------------------------------------------------------------- /simple-plugin-container/target/plugins/test-plugin-simple-1.0-SNAPSHOT/lib/pf4j-2.3.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/plugins/test-plugin-simple-1.0-SNAPSHOT/lib/pf4j-2.3.0.jar -------------------------------------------------------------------------------- /simple-plugin-container/target/plugins/test-plugin-simple-1.0-SNAPSHOT/plugin.properties: -------------------------------------------------------------------------------- 1 | plugin.id=simple-identity-plugin 2 | plugin.class=com.curisprofound.plugins.simple.SimplePlugin 3 | plugin.version=0.0.1 4 | plugin.provider=Curis Profound 5 | plugin.dependencies= 6 | 7 | -------------------------------------------------------------------------------- /simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT.jar -------------------------------------------------------------------------------- /simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT.zip -------------------------------------------------------------------------------- /simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/lib/java-semver-0.9.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/lib/java-semver-0.9.0.jar -------------------------------------------------------------------------------- /simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/lib/pf4j-2.3.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/lib/pf4j-2.3.0.jar -------------------------------------------------------------------------------- /simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/lib/shared-plugin-interfaces-1.1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/lib/shared-plugin-interfaces-1.1.0.jar -------------------------------------------------------------------------------- /simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/lib/slf4j-api-1.7.5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/lib/slf4j-api-1.7.5.jar -------------------------------------------------------------------------------- /simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/lib/slf4j-simple-1.7.5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/lib/slf4j-simple-1.7.5.jar -------------------------------------------------------------------------------- /simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/plugins/test-plugin-simple-1.0-SNAPSHOT.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/plugins/test-plugin-simple-1.0-SNAPSHOT.zip -------------------------------------------------------------------------------- /simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/META-INF/extensions.idx: -------------------------------------------------------------------------------- 1 | # Generated by PF4J 2 | com.curisprofound.plugins.simple.SimplePlugin$SimpleIdentityPlugin 3 | -------------------------------------------------------------------------------- /simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/assembly.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | plugin 9 | 10 | zip 11 | 12 | false 13 | 14 | 15 | false 16 | runtime 17 | lib 18 | 19 | *:jar:* 20 | 21 | 22 | org.slf4j:* 23 | com.curisprofound:* 24 | 25 | 26 | 27 | 28 | 29 | target/classes 30 | classes 31 | 32 | 33 | src/main/resources 34 | 35 | 36 | plugin.properties 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/com/curisprofound/plugins/simple/SimplePlugin$SimpleIdentityPlugin.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/com/curisprofound/plugins/simple/SimplePlugin$SimpleIdentityPlugin.class -------------------------------------------------------------------------------- /simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/com/curisprofound/plugins/simple/SimplePlugin.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/com/curisprofound/plugins/simple/SimplePlugin.class -------------------------------------------------------------------------------- /simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/plugins/test-plugin-simple-1.0-SNAPSHOT/classes/plugin.properties: -------------------------------------------------------------------------------- 1 | plugin.id=simple-identity-plugin 2 | plugin.class=com.curisprofound.plugins.simple.SimplePlugin 3 | plugin.version=0.0.1 4 | plugin.provider=Curis Profound 5 | plugin.dependencies= 6 | 7 | -------------------------------------------------------------------------------- /simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/plugins/test-plugin-simple-1.0-SNAPSHOT/lib/java-semver-0.9.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/plugins/test-plugin-simple-1.0-SNAPSHOT/lib/java-semver-0.9.0.jar -------------------------------------------------------------------------------- /simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/plugins/test-plugin-simple-1.0-SNAPSHOT/lib/pf4j-2.3.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/plugins/test-plugin-simple-1.0-SNAPSHOT/lib/pf4j-2.3.0.jar -------------------------------------------------------------------------------- /simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/plugins/test-plugin-simple-1.0-SNAPSHOT/plugin.properties: -------------------------------------------------------------------------------- 1 | plugin.id=simple-identity-plugin 2 | plugin.class=com.curisprofound.plugins.simple.SimplePlugin 3 | plugin.version=0.0.1 4 | plugin.provider=Curis Profound 5 | plugin.dependencies= 6 | 7 | -------------------------------------------------------------------------------- /simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/simple-plugin-container-1.0-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-plugin-container/target/simple-plugin-container-1.0-SNAPSHOT/simple-plugin-container-1.0-SNAPSHOT.jar -------------------------------------------------------------------------------- /simple-test-plugin/README.md: -------------------------------------------------------------------------------- 1 | ## Simple Plugin 2 | 3 | A plugin is packaged as a zip or jar file and is put in a directory for the container to scan, decompress and use. the POM file for the plugin shall contain dependenceis required to compile it as well as the information that needs to go into the manifest. 4 | 5 | Minimum dependencies include the PF4J and the shared interfaces. 6 | 7 | ```xml 8 | 9 | 10 | org.pf4j 11 | pf4j 12 | 2.3.0 13 | 14 | 15 | com.curisprofound 16 | shared-plugin-interfaces 17 | 1.1.0 18 | 19 | 20 | ``` 21 | 22 | We also need to configure maven-jar-plugin to add plugin information to manifest: 23 | 24 | 25 | ```xml 26 | .... 27 | 28 | simple-hello-plugin 29 | com.curisprofound.plugins.simple.HelloPlugin 30 | 0.0.1 31 | Curis Profound 32 | 33 | 34 | 35 | .... 36 | .... 37 | 38 | 39 | org.apache.maven.plugins 40 | maven-jar-plugin 41 | 2.4 42 | 43 | 44 | 45 | ${plugin.id} 46 | ${plugin.class} 47 | ${plugin.version} 48 | ${plugin.provider} 49 | ${plugin.dependencies} 50 | 51 | 52 | 53 | 54 | 55 | ``` 56 | 57 | To create the zip file, maven-assembly-plugin is used. this plugin is configured 58 | to read ```src/main/resources/assembly.xml``` file for instructions on how 59 | to create the zip file. 60 | 61 | The most important point on creating the assembly file is to ensure that it does not include dependencies that are shared between the plugin and the container or other plugins. for example, the ```slf4j``` or the shared interfaces should not be included in the plugin zip file as they will be included in the container dependencies. 62 | 63 | * we exclude the dependencies that are already declared in the container 64 | 65 | ```xml 66 | 67 | 68 | false 69 | runtime 70 | lib 71 | 72 | *:jar:* 73 | 74 | 75 | org.slf4j:* 76 | com.curisprofound:shared-plugin-interface 77 | 78 | 79 | 80 | ``` 81 | 82 | * we include the classes, not the jar file, because the unzipped directory is not in the classpath 83 | 84 | ```xml 85 | 86 | target/classes 87 | classes 88 | 89 | ``` 90 | 91 | * we include a plugin.properties file in the root of the zip file 92 | 93 | ```xml 94 | 95 | src/main/resources 96 | 97 | 98 | plugin.properties 99 | 100 | 101 | ``` 102 | 103 | this just includes the properties we added to the manifest above 104 | 105 | ``` 106 | plugin.id=simple-hello-plugin 107 | plugin.class=com.curisprofound.plugins.simple.HelloPlugin 108 | plugin.version=0.0.1 109 | plugin.provider=Curis Profound 110 | plugin.dependencies= 111 | ``` 112 | 113 | ## The plugin code 114 | 115 | the actual plugin now can implement extension points and override start and stop hooks: 116 | 117 | ```java 118 | package com.curisprofound.plugins.simple; 119 | 120 | public class SimplePlugin extends Plugin { 121 | 122 | private static Logger log = LoggerFactory.getLogger(SimplePlugin.class); 123 | 124 | public SimplePlugin(PluginWrapper wrapper) { 125 | super(wrapper); 126 | } 127 | 128 | @Override 129 | public void start(){ 130 | log.info("Simple Plugin Started"); 131 | } 132 | 133 | @Override 134 | public void stop(){ 135 | log.info("Simple Plugin Stopped"); 136 | } 137 | 138 | @Extension 139 | public static class SimpleIdentityPlugin implements PluginInterface { 140 | 141 | public String identify() { 142 | return "A simple plugin with no dependency on Spring"; 143 | } 144 | 145 | @Override 146 | public List reactiveRoutes() { 147 | return new ArrayList<>(); 148 | } 149 | 150 | @Override 151 | public List mvcControllers() { 152 | return new ArrayList<>(); 153 | } 154 | } 155 | } 156 | ``` 157 | Notice that the plugin returns empty lists (not null) for methods that it won't implement. this is a good idea to return the empty version of the object instead of null and avoid ```NullPointerException``` messsages. 158 | 159 | 160 | building the project with ```mvn clean package``` will create a zip file in the 161 | ```target``` directory. in the future steps we will move this zip file into the plugins directory and run it through the container. 162 | 163 | [Back to Contents](../#contents) 164 | 165 | [PF4J]: https://github.com/pf4j/pf4j 166 | 167 | -------------------------------------------------------------------------------- /simple-test-plugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.curisprofound 8 | test-plugin-simple 9 | 1.0-SNAPSHOT 10 | Simple Test Plugin 11 | jar 12 | 13 | 14 | simple-hello-plugin 15 | com.curisprofound.plugins.simple.SimplePlugin 16 | 0.0.1 17 | Curis Profound 18 | 19 | 20 | 21 | 22 | 23 | 24 | org.pf4j 25 | pf4j 26 | 2.3.0 27 | 28 | 29 | com.curisprofound 30 | shared-plugin-interfaces 31 | 1.1.0 32 | 33 | 34 | 35 | 36 | 37 | 38 | maven-assembly-plugin 39 | 2.3 40 | 41 | 42 | 43 | src/main/resources/assembly.xml 44 | 45 | 46 | false 47 | 48 | 49 | 50 | make-assembly 51 | package 52 | 53 | attached 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.apache.maven.plugins 61 | maven-jar-plugin 62 | 2.4 63 | 64 | 65 | 66 | ${plugin.id} 67 | ${plugin.class} 68 | ${plugin.version} 69 | ${plugin.provider} 70 | ${plugin.dependencies} 71 | 72 | 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-compiler-plugin 78 | 79 | 8 80 | 8 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /simple-test-plugin/src/main/java/com/curisprofound/plugins/simple/SimplePlugin.java: -------------------------------------------------------------------------------- 1 | package com.curisprofound.plugins.simple; 2 | 3 | import com.curisprofound.plugins.PluginInterface; 4 | import org.pf4j.Extension; 5 | import org.pf4j.Plugin; 6 | import org.pf4j.PluginWrapper; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public class SimplePlugin extends Plugin { 14 | 15 | private static Logger log = LoggerFactory.getLogger(com.curisprofound.plugins.simple.SimplePlugin.class); 16 | 17 | public SimplePlugin(PluginWrapper wrapper) { 18 | super(wrapper); 19 | } 20 | 21 | @Override 22 | public void start(){ 23 | log.info("Simple Plugin Started"); 24 | } 25 | @Override 26 | public void stop(){ 27 | log.info("Simple Plugin Stopped"); 28 | } 29 | 30 | @Extension 31 | public static class SimpleIdentityPlugin implements PluginInterface { 32 | 33 | public String identify() { 34 | return "A simple plugin with no dependency on Spring"; 35 | } 36 | 37 | @Override 38 | public List reactiveRoutes() { 39 | return new ArrayList<>(); 40 | } 41 | 42 | @Override 43 | public List mvcControllers() { 44 | return new ArrayList<>(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /simple-test-plugin/src/main/resources/assembly.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | plugin 9 | 10 | zip 11 | 12 | false 13 | 14 | 15 | false 16 | runtime 17 | lib 18 | 19 | *:jar:* 20 | 21 | 22 | org.slf4j:* 23 | com.curisprofound:* 24 | 25 | 26 | 27 | 28 | 29 | target/classes 30 | classes 31 | 32 | 33 | src/main/resources 34 | 35 | 36 | plugin.properties 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /simple-test-plugin/src/main/resources/plugin.properties: -------------------------------------------------------------------------------- 1 | plugin.id=simple-identity-plugin 2 | plugin.class=com.curisprofound.plugins.simple.SimplePlugin 3 | plugin.version=0.0.1 4 | plugin.provider=Curis Profound 5 | plugin.dependencies= 6 | 7 | -------------------------------------------------------------------------------- /simple-test-plugin/target/classes/META-INF/extensions.idx: -------------------------------------------------------------------------------- 1 | # Generated by PF4J 2 | com.curisprofound.plugins.simple.SimplePlugin$SimpleIdentityPlugin 3 | -------------------------------------------------------------------------------- /simple-test-plugin/target/classes/assembly.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | plugin 9 | 10 | zip 11 | 12 | false 13 | 14 | 15 | false 16 | runtime 17 | lib 18 | 19 | *:jar:* 20 | 21 | 22 | org.slf4j:* 23 | com.curisprofound:* 24 | 25 | 26 | 27 | 28 | 29 | target/classes 30 | classes 31 | 32 | 33 | src/main/resources 34 | 35 | 36 | plugin.properties 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /simple-test-plugin/target/classes/com/curisprofound/plugins/simple/SimplePlugin$SimpleIdentityPlugin.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-test-plugin/target/classes/com/curisprofound/plugins/simple/SimplePlugin$SimpleIdentityPlugin.class -------------------------------------------------------------------------------- /simple-test-plugin/target/classes/com/curisprofound/plugins/simple/SimplePlugin.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-test-plugin/target/classes/com/curisprofound/plugins/simple/SimplePlugin.class -------------------------------------------------------------------------------- /simple-test-plugin/target/classes/plugin.properties: -------------------------------------------------------------------------------- 1 | plugin.id=simple-identity-plugin 2 | plugin.class=com.curisprofound.plugins.simple.SimplePlugin 3 | plugin.version=0.0.1 4 | plugin.provider=Curis Profound 5 | plugin.dependencies= 6 | 7 | -------------------------------------------------------------------------------- /simple-test-plugin/target/maven-archiver/pom.properties: -------------------------------------------------------------------------------- 1 | #Generated by Maven 2 | #Sun Jul 29 00:52:20 PDT 2018 3 | version=1.0-SNAPSHOT 4 | groupId=com.curisprofound 5 | artifactId=test-plugin-simple 6 | -------------------------------------------------------------------------------- /simple-test-plugin/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst: -------------------------------------------------------------------------------- 1 | com/curisprofound/plugins/simple/SimplePlugin$SimpleIdentityPlugin.class 2 | META-INF/extensions.idx 3 | com/curisprofound/plugins/simple/SimplePlugin.class 4 | -------------------------------------------------------------------------------- /simple-test-plugin/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst: -------------------------------------------------------------------------------- 1 | /Users/rezaalemy/Projects/Spring/pf4j-sample/simple-test-plugin/src/main/java/com/curisprofound/plugins/simple/SimplePlugin.java 2 | -------------------------------------------------------------------------------- /simple-test-plugin/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-test-plugin/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst -------------------------------------------------------------------------------- /simple-test-plugin/target/test-plugin-simple-1.0-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-test-plugin/target/test-plugin-simple-1.0-SNAPSHOT.jar -------------------------------------------------------------------------------- /simple-test-plugin/target/test-plugin-simple-1.0-SNAPSHOT.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-test-plugin/target/test-plugin-simple-1.0-SNAPSHOT.zip -------------------------------------------------------------------------------- /simple-test-plugin/target/test-plugin-simple-1.0-SNAPSHOT/classes/META-INF/extensions.idx: -------------------------------------------------------------------------------- 1 | # Generated by PF4J 2 | com.curisprofound.plugins.simple.SimplePlugin$SimpleIdentityPlugin 3 | -------------------------------------------------------------------------------- /simple-test-plugin/target/test-plugin-simple-1.0-SNAPSHOT/classes/assembly.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | plugin 9 | 10 | zip 11 | 12 | false 13 | 14 | 15 | false 16 | runtime 17 | lib 18 | 19 | *:jar:* 20 | 21 | 22 | org.slf4j:* 23 | com.curisprofound:* 24 | 25 | 26 | 27 | 28 | 29 | target/classes 30 | classes 31 | 32 | 33 | src/main/resources 34 | 35 | 36 | plugin.properties 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /simple-test-plugin/target/test-plugin-simple-1.0-SNAPSHOT/classes/com/curisprofound/plugins/simple/SimplePlugin$SimpleIdentityPlugin.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-test-plugin/target/test-plugin-simple-1.0-SNAPSHOT/classes/com/curisprofound/plugins/simple/SimplePlugin$SimpleIdentityPlugin.class -------------------------------------------------------------------------------- /simple-test-plugin/target/test-plugin-simple-1.0-SNAPSHOT/classes/com/curisprofound/plugins/simple/SimplePlugin.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-test-plugin/target/test-plugin-simple-1.0-SNAPSHOT/classes/com/curisprofound/plugins/simple/SimplePlugin.class -------------------------------------------------------------------------------- /simple-test-plugin/target/test-plugin-simple-1.0-SNAPSHOT/classes/plugin.properties: -------------------------------------------------------------------------------- 1 | plugin.id=simple-identity-plugin 2 | plugin.class=com.curisprofound.plugins.simple.SimplePlugin 3 | plugin.version=0.0.1 4 | plugin.provider=Curis Profound 5 | plugin.dependencies= 6 | 7 | -------------------------------------------------------------------------------- /simple-test-plugin/target/test-plugin-simple-1.0-SNAPSHOT/lib/java-semver-0.9.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-test-plugin/target/test-plugin-simple-1.0-SNAPSHOT/lib/java-semver-0.9.0.jar -------------------------------------------------------------------------------- /simple-test-plugin/target/test-plugin-simple-1.0-SNAPSHOT/lib/pf4j-2.3.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/simple-test-plugin/target/test-plugin-simple-1.0-SNAPSHOT/lib/pf4j-2.3.0.jar -------------------------------------------------------------------------------- /simple-test-plugin/target/test-plugin-simple-1.0-SNAPSHOT/plugin.properties: -------------------------------------------------------------------------------- 1 | plugin.id=simple-identity-plugin 2 | plugin.class=com.curisprofound.plugins.simple.SimplePlugin 3 | plugin.version=0.0.1 4 | plugin.provider=Curis Profound 5 | plugin.dependencies= 6 | 7 | -------------------------------------------------------------------------------- /spring-plugin-container/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | /plugins/ 19 | .mvn 20 | 21 | ### NetBeans ### 22 | /nbproject/private/ 23 | /build/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ -------------------------------------------------------------------------------- /spring-plugin-container/README.md: -------------------------------------------------------------------------------- 1 | # Spring boot app as plugin container 2 | 3 | Using [PF4J-Spring]() package, it is rather straight-forward to add plugin support to an existing application. once more, the pom file needs to declare 4 | dependencies on ```pf4j-spring``` and the shared interfaces 5 | 6 | ```xml 7 | 8 | org.pf4j 9 | pf4j-spring 10 | 0.4.0 11 | 12 | 13 | 14 | com.curisprofound 15 | shared-plugin-interfaces 16 | 1.1.0 17 | 18 | ``` 19 | 20 | the build section does not need to change, except if you decide to have an external directory added to the classpath, which comes in handy when resolving dependencies 21 | 22 | ```xml 23 | 24 | org.springframework.boot 25 | spring-boot-maven-plugin 26 | 27 | 28 | ./lib/ 29 | 30 | 31 | 32 | ``` 33 | 34 | The application needs to add a Singleton for ```PluginManager``` to the dependency injection platform. this can be done alongside other configuration 35 | 36 | ```java 37 | @Configuration 38 | public class AppConfig { 39 | 40 | @Bean 41 | public SpringPluginManager pluginManager() { 42 | return new SpringPluginManager(); 43 | } 44 | 45 | @Bean 46 | public RouterFunction route() { 47 | String message = "Reactive endpoint on contaainer"; 48 | return RouterFunctions.route( 49 | GET("/hello") 50 | .and(accept(MediaType.TEXT_PLAIN)), 51 | req -> ServerResponse 52 | .ok() 53 | .body(Mono.just(message), String.class)); 54 | } 55 | } 56 | ``` 57 | 58 | The above config also adds a ```/hello``` endpoint to show a use case of the container having its own endpoints. 59 | 60 | # Configuring the Plugin 61 | 62 | The plugin configuration is done through a class extending ```BeanFactoryAware``` and annotated with ```@Configuration```. 63 | 64 | ```java 65 | @Configuration 66 | public class PluginConfig implements BeanFactoryAware { 67 | 68 | 69 | private final SpringPluginManager pluginManager; 70 | private final ApplicationContext applicationContext; 71 | private final ObjectMapper objectMapper; 72 | private BeanFactory beanFactory; 73 | 74 | @Autowired 75 | public PluginConfig(SpringPluginManager pm, ApplicationContext applicationContext) { 76 | this.pluginManager = pm; 77 | this.applicationContext = applicationContext; 78 | this.objectMapper = new ObjectMapper(); 79 | } 80 | 81 | @Override 82 | public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 83 | this.beanFactory = beanFactory; 84 | } 85 | 86 | @Bean 87 | @DependsOn("pluginManager") 88 | public RouterFunction pluginEndpoints(PluginManager pm) { 89 | registerMvcEndpoints(pm); 90 | return getReactiveRoutes(pm); 91 | } 92 | ... 93 | ... 94 | @PreDestroy 95 | public void cleanup() { 96 | pluginManager.stopPlugins(); 97 | } 98 | ``` 99 | 100 | The PluginManager and the ApplicationContext are auto-wired to this class, and a BeanFactory is obtained by the ```setBeanFactory``` method getting called at the initialization of the class. It is important to have a ```@PreDestroy``` annotated method to stop the plugins on plugin manager. 101 | 102 | This class returns a Bean that registers the MVC and reactive endpoints the plugin provides. registering the MVC endpoints is done in two steps, first we add each endpoint as a singleton using the bean factory, then we call the ```afterPropertiesSet()``` method on ```RequestMappingHandlerMapping``` classes to map them to the correct endpoints: 103 | 104 | ```java 105 | private void registerMvcEndpoints(PluginManager pm) { 106 | pm.getExtensions(PluginInterface.class).stream() 107 | .flatMap(g -> g.mvcControllers().stream()) 108 | .forEach(r -> ((ConfigurableBeanFactory) beanFactory) 109 | .registerSingleton(r.getClass().getName(), r)); 110 | applicationContext 111 | .getBeansOfType(RequestMappingHandlerMapping.class) 112 | .forEach((k, v) -> v.afterPropertiesSet()); 113 | } 114 | ``` 115 | 116 | We use another approach for the reactive endpoints. here we provide a base, which allows the user to see which plugins are in the system: 117 | 118 | ```java 119 | private RouterFunction baseRoot(PluginManager pm) { 120 | return route(GET("/plugins"), 121 | req -> ServerResponse.ok().body(Mono.just(pluginNamesMono(pm)), String.class)); 122 | } 123 | 124 | private String pluginNamesMono(PluginManager pm) { 125 | try { 126 | List identityList = pm 127 | .getExtensions(PluginInterface.class).stream() 128 | .map(g-> g.getClass().getName() + ": " + g.identify()) 129 | .collect(Collectors.toList()); 130 | return objectMapper.writeValueAsString(identityList); 131 | } catch (JsonProcessingException e) { 132 | return "[]"; 133 | } 134 | } 135 | 136 | ``` 137 | 138 | to this base we add all if any router functions returned by plugins: 139 | 140 | ```java 141 | private RouterFunction getReactiveRoutes(PluginManager pm) { 142 | RouterFunction base = baseRoot(pm); 143 | RouterFunction routes = pm 144 | .getExtensions(PluginInterface.class).stream() 145 | .flatMap(g -> g.reactiveRoutes().stream()) 146 | .map(r-> (RouterFunction)r) 147 | .reduce((o,r )-> (RouterFunction) o.andOther(r)) 148 | .orElse(null); 149 | return routes == null ? base : base.andOther(routes); 150 | } 151 | ``` 152 | 153 | Once the application starts, it looks under the current directory for a directory called plugins to scan for the plugins. Consult the [PF4J Documentation][] on how to specify a location using environment variables. 154 | 155 | Once the zip files of the simple and spring plugin samples are in the plugins directory, here is the output the application confirms the correct discovery and installation of plugins and endpoints: 156 | 157 | ```bash 158 | $ java -jar target/spring-plugin-container-0.0.1-SNAPSHOT.jar 159 | .... 160 | .... 161 | Enabled plugins: [] 162 | Disabled plugins: [] 163 | PF4J version 2.0.0 in 'deployment' mode 164 | Plugin 'spring-sample-plugin@0.0.1' resolved 165 | Plugin 'simple-identity-plugin@0.0.1' resolved 166 | Start plugin 'spring-sample-plugin@0.0.1' 167 | Spring Sample plugin.start() 168 | Start plugin 'simple-identity-plugin@0.0.1' 169 | Simple Plugin Started 170 | Mapped "{[/plugin-mvc-controller],methods=[GET]}" onto public org.springframework.http.ResponseEntity com.curisprofound.springtestplugin.PluginController.greetMVC() 171 | Mapped (GET && /plugins) -> com.curisprofound.springplugincontainer.PluginConfig$$Lambda$238/90205195@1c72da34 172 | (GET && /plugin-end-point) -> com.curisprofound.springtestplugin.SpringSamplePlugin$SpringPlugin$2$$Lambda$243/280265505@6b0c2d26 173 | Mapped ((GET && /hello) && Accept: [text/plain]) -> com.curisprofound.springplugincontainer.AppConfig$$Lambda$244/1161667116@6e38921c 174 | Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.reactive.resource.ResourceWebHandler] 175 | Mapped URL path [/**] onto handler of type [class org.springframework.web.reactive.resource.ResourceWebHandler] 176 | Registering beans for JMX exposure on startup 177 | Started HttpServer on /0:0:0:0:0:0:0:0:8080 178 | Netty started on port(s): 8080 179 | Started SpringPluginContainerApplication in 2.71 seconds (JVM running for 3.192) 180 | Number of plugins found: 2 181 | com.curisprofound.plugins.simple.SimplePlugin$SimpleIdentityPlugin:A simple plugin with no dependency on Spring 182 | com.curisprofound.springtestplugin.SpringSamplePlugin$SpringPlugin:A plugin using Spring framework 183 | ``` 184 | 185 | the /plugins will return 186 | 187 | ```json 188 | [ 189 | "com.curisprofound.plugins.simple.SimplePlugin$SimpleIdentityPlugin: A simple plugin with no dependency on Spring", 190 | "com.curisprofound.springtestplugin.SpringSamplePlugin$SpringPlugin: A plugin using Spring framework" 191 | ] 192 | ``` 193 | 194 | here is the return value for the rest of the endpoints 195 | 196 | |Endpoint| Returns| 197 | |--------|--------| 198 | |/hello| Reactive endpoint on contaainer| 199 | |/plugin-mvc-controller| An endpoint defined by annotation in plugin| 200 | |/plugin-end-point| reactive router endpoint| 201 | 202 | 203 | [Back to Contents](../#contents) 204 | 205 | [PF4J Documentation]:https://pf4j.org/ 206 | -------------------------------------------------------------------------------- /spring-plugin-container/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /spring-plugin-container/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /spring-plugin-container/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.curisprofound 7 | spring-plugin-container 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | spring-plugin-container 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.3.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-webflux 31 | 32 | 33 | 34 | 35 | org.pf4j 36 | pf4j-spring 37 | 0.4.0 38 | 39 | 40 | 41 | com.curisprofound 42 | shared-plugin-interfaces 43 | 1.1.0 44 | 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-starter-test 50 | test 51 | 52 | 53 | io.projectreactor 54 | reactor-test 55 | test 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-maven-plugin 64 | 65 | 66 | ./lib/ 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /spring-plugin-container/src/main/java/com/curisprofound/springplugincontainer/AppConfig.java: -------------------------------------------------------------------------------- 1 | package com.curisprofound.springplugincontainer; 2 | 3 | import org.pf4j.spring.SpringPluginManager; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.http.MediaType; 7 | import org.springframework.web.reactive.function.server.RouterFunction; 8 | import org.springframework.web.reactive.function.server.RouterFunctions; 9 | import org.springframework.web.reactive.function.server.ServerResponse; 10 | import reactor.core.publisher.Mono; 11 | 12 | import static org.springframework.web.reactive.function.server.RequestPredicates.GET; 13 | import static org.springframework.web.reactive.function.server.RequestPredicates.accept; 14 | 15 | @Configuration 16 | public class AppConfig { 17 | @Bean 18 | public SpringPluginManager pluginManager() { 19 | return new SpringPluginManager(); 20 | } 21 | 22 | @Bean 23 | public RouterFunction route() { 24 | return RouterFunctions.route(GET("/hello") 25 | .and(accept(MediaType.TEXT_PLAIN)), 26 | req -> ServerResponse.ok().body(Mono.just("Reactive endpoint on contaainer"), String.class)); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /spring-plugin-container/src/main/java/com/curisprofound/springplugincontainer/PluginConfig.java: -------------------------------------------------------------------------------- 1 | package com.curisprofound.springplugincontainer; 2 | 3 | import com.curisprofound.plugins.PluginInterface; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import org.pf4j.PluginManager; 7 | import org.pf4j.spring.SpringPluginManager; 8 | import org.springframework.beans.BeansException; 9 | import org.springframework.beans.factory.BeanFactory; 10 | import org.springframework.beans.factory.BeanFactoryAware; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.beans.factory.config.ConfigurableBeanFactory; 13 | import org.springframework.context.ApplicationContext; 14 | import org.springframework.context.annotation.Bean; 15 | import org.springframework.context.annotation.Configuration; 16 | import org.springframework.context.annotation.DependsOn; 17 | import org.springframework.web.reactive.function.server.RouterFunction; 18 | import org.springframework.web.reactive.function.server.ServerResponse; 19 | import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping; 20 | import reactor.core.publisher.Mono; 21 | 22 | import javax.annotation.PreDestroy; 23 | import java.util.List; 24 | import java.util.stream.Collectors; 25 | 26 | import static org.springframework.web.reactive.function.server.RequestPredicates.GET; 27 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 28 | 29 | @Configuration 30 | public class PluginConfig implements BeanFactoryAware { 31 | 32 | 33 | private final SpringPluginManager pluginManager; 34 | private final ApplicationContext applicationContext; 35 | private final ObjectMapper objectMapper; 36 | private BeanFactory beanFactory; 37 | 38 | @Autowired 39 | public PluginConfig(SpringPluginManager pm, ApplicationContext applicationContext) { 40 | this.pluginManager = pm; 41 | this.applicationContext = applicationContext; 42 | this.objectMapper = new ObjectMapper(); 43 | } 44 | 45 | @Override 46 | public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 47 | this.beanFactory = beanFactory; 48 | } 49 | 50 | @Bean 51 | @DependsOn("pluginManager") 52 | public RouterFunction pluginEndpoints(PluginManager pm) { 53 | registerMvcEndpoints(pm); 54 | return getReactiveRoutes(pm); 55 | } 56 | 57 | private RouterFunction getReactiveRoutes(PluginManager pm) { 58 | RouterFunction base = baseRoot(pm); 59 | RouterFunction routes = pm.getExtensions(PluginInterface.class).stream() 60 | .flatMap(g -> g.reactiveRoutes().stream()) 61 | .map(r-> (RouterFunction)r) 62 | .reduce((o,r )-> (RouterFunction) o.andOther(r)) 63 | .orElse(null); 64 | return routes == null ? base : base.andOther(routes); 65 | } 66 | 67 | private RouterFunction baseRoot(PluginManager pm) { 68 | return route(GET("/plugins"), 69 | req -> ServerResponse.ok().body(Mono.just(pluginNamesMono(pm)), String.class)); 70 | } 71 | 72 | private String pluginNamesMono(PluginManager pm) { 73 | List identityList = pm.getExtensions(PluginInterface.class).stream() 74 | .map(g-> g.getClass().getName() + ": " + g.identify()) 75 | .collect(Collectors.toList()); 76 | try { 77 | return objectMapper.writeValueAsString(identityList); 78 | } catch (JsonProcessingException e) { 79 | return "[]"; 80 | } 81 | } 82 | 83 | 84 | private void registerMvcEndpoints(PluginManager pm) { 85 | pm.getExtensions(PluginInterface.class).stream() 86 | .flatMap(g -> g.mvcControllers().stream()) 87 | .forEach(r -> ((ConfigurableBeanFactory) beanFactory) 88 | .registerSingleton(r.getClass().getName(), r)); 89 | applicationContext 90 | .getBeansOfType(RequestMappingHandlerMapping.class) 91 | .forEach((k, v) -> v.afterPropertiesSet()); 92 | } 93 | 94 | 95 | @PreDestroy 96 | public void cleanup() { 97 | pluginManager.stopPlugins(); 98 | } 99 | 100 | } -------------------------------------------------------------------------------- /spring-plugin-container/src/main/java/com/curisprofound/springplugincontainer/SpringPluginContainerApplication.java: -------------------------------------------------------------------------------- 1 | package com.curisprofound.springplugincontainer; 2 | 3 | import com.curisprofound.plugins.PluginInterface; 4 | import org.pf4j.spring.SpringPluginManager; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.ApplicationArguments; 9 | import org.springframework.boot.ApplicationRunner; 10 | import org.springframework.boot.SpringApplication; 11 | import org.springframework.boot.autoconfigure.SpringBootApplication; 12 | import org.springframework.context.annotation.Bean; 13 | 14 | import java.util.List; 15 | 16 | @SpringBootApplication 17 | public class SpringPluginContainerApplication { 18 | 19 | private static Logger log = LoggerFactory.getLogger(SpringPluginContainerApplication.class); 20 | 21 | public static void main(String[] args) { 22 | SpringApplication.run(SpringPluginContainerApplication.class, args); 23 | } 24 | 25 | @Bean 26 | public ApplicationRunner run() { 27 | return new ApplicationRunner() { 28 | 29 | @Autowired 30 | private SpringPluginManager springPluginManager; 31 | 32 | @Override 33 | public void run(ApplicationArguments args) throws Exception { 34 | List plugins = springPluginManager.getExtensions(PluginInterface.class); 35 | log.info(String.format("Number of plugins found: %d", plugins.size())); 36 | plugins.forEach(c -> log.info(c.getClass().getName() + ":" + c.identify())); 37 | } 38 | }; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /spring-plugin-container/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/spring-plugin-container/src/main/resources/application.properties -------------------------------------------------------------------------------- /spring-plugin-container/src/test/java/com/curisprofound/springplugincontainer/SpringPluginContainerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.curisprofound.springplugincontainer; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class SpringPluginContainerApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /spring-test-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /spring-test-plugin/README.md: -------------------------------------------------------------------------------- 1 | # Spring-enabled plugins 2 | 3 | Developing plugins as Spring boot applications has a number of advantages, including the utility of 4 | Spring functionality, Dependency injection and auto-wiring, reactive libraries, etc. 5 | 6 | Plugins should also be able to define new endpoints in the container, whether the old-style annotated 7 | controllers or the new ```RouterFunction``` style for reactive endpoints. 8 | 9 | ## Pom file 10 | 11 | The pom file needs to include the shared interface and the [pf4j-spring](https://github.com/pf4j/pf4j-spring) package as dependencies. 12 | The rest of the dependencies would be the same that the Spring boot application would have required 13 | in standalone mode. 14 | 15 | The build section needs to be customized to create the correct plugin structure. 16 | 17 | * The ant-run plugin opens the jar and puts the classes in a separate directory 18 | 19 | ```xml 20 | 21 | org.apache.maven.plugins 22 | maven-antrun-plugin 23 | 1.6 24 | 25 | 26 | unzip jar file 27 | package 28 | 29 | 30 | 32 | 33 | 34 | 35 | run 36 | 37 | 38 | 39 | 40 | ``` 41 | 42 | * The jar plugin sets the parameters for Manifest file 43 | 44 | ```xml 45 | 46 | org.apache.maven.plugins 47 | maven-jar-plugin 48 | 2.4 49 | 50 | 51 | 52 | ${plugin.id} 53 | ${plugin.class} 54 | ${plugin.version} 55 | ${plugin.provider} 56 | ${plugin.dependencies} 57 | 58 | 59 | 60 | 61 | ``` 62 | 63 | * The maven-assemply-plugin uses the ```src/main/resources/assembly.xml``` to create the zip file 64 | 65 | The ```assembly.xml``` file needs to exclude any packages used by the container, 66 | and put the ```plugin.properties``` file in the root directory. 67 | 68 | ```xml 69 | 70 | plugin 71 | 72 | zip 73 | 74 | false 75 | 76 | 77 | false 78 | runtime 79 | lib 80 | 81 | org.pf4j:* 82 | 83 | 84 | org.springframework:* 85 | org.slf4j:* 86 | com.curisprofound:shared-plugin-interfaces 87 | 88 | 89 | 90 | 91 | 92 | target/plugin-classes 93 | classes 94 | 95 | 96 | src/main/resources 97 | 98 | 99 | plugin.properties 100 | 101 | 102 | 103 | 104 | ``` 105 | 106 | # Configuration 107 | 108 | Using the ```@Configuration``` annotation, a class would be marked as configuration and can provide beans for later auto-wiring 109 | 110 | ```java 111 | //in GreetProvider.java 112 | 113 | public class GreetProvider { 114 | public String provide(){ 115 | return "A plugin using Spring framework"; 116 | } 117 | } 118 | 119 | //in ApplicationConfiguration.java 120 | @Configuration 121 | public class ApplicationConfiguration { 122 | @Bean 123 | public GreetProvider greetProvider(){ 124 | return new GreetProvider(); 125 | } 126 | } 127 | ``` 128 | 129 | Restful endpoints can be created in annotated classes 130 | 131 | ```java 132 | @Controller 133 | @RequestMapping("/plugin-mvc-controller") 134 | public class PluginController { 135 | @GetMapping 136 | public ResponseEntity greetMVC(){ 137 | String message = "An endpoint defined by annotation in plugin"; 138 | return ResponseEntity.ok().body(message); 139 | } 140 | } 141 | ``` 142 | 143 | The plugin is implemented as an extension to the ```SpringPlugin```, and has 144 | methods to start, stop, and most importantly, create application context which 145 | will be used to register the annotated configuration class: 146 | 147 | ```java 148 | public class SpringSamplePlugin extends SpringPlugin { 149 | 150 | private static final Logger log = LoggerFactory.getLogger(SpringSamplePlugin.class); 151 | 152 | public SpringSamplePlugin(PluginWrapper wrapper) { 153 | super(wrapper); 154 | } 155 | 156 | @Override 157 | public void start() { 158 | log.info("Spring Sample plugin.start()"); 159 | } 160 | 161 | @Override 162 | public void stop() { 163 | log.info("Spring Sample plugin.stop()"); 164 | super.stop(); // to close applicationContext 165 | } 166 | 167 | @Override 168 | protected ApplicationContext createApplicationContext() { 169 | AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); 170 | applicationContext.setClassLoader(getWrapper().getPluginClassLoader()); 171 | applicationContext.register(ApplicationConfiguration.class); 172 | applicationContext.refresh(); 173 | return applicationContext; 174 | } 175 | ... 176 | ... 177 | } 178 | ``` 179 | 180 | The interfaces to implement are included inside the plugin as static classes implementing the interface and annotated with ```@Extension```: 181 | 182 | ```java 183 | 184 | @Extension(ordinal = 1) 185 | public static class SpringPlugin implements PluginInterface { 186 | 187 | @Autowired 188 | private GreetProvider greetProvider; 189 | 190 | 191 | 192 | @Override 193 | public String identify() { 194 | return greetProvider.provide(); 195 | } 196 | 197 | @Override 198 | public List mvcControllers() { 199 | return new ArrayList() {{ 200 | add(new PluginController()); 201 | }}; 202 | } 203 | 204 | @Override 205 | public List> reactiveRoutes() { 206 | return new ArrayList>() {{ 207 | add(route(GET("/plugin-end-point"), 208 | req -> ServerResponse 209 | .ok() 210 | .body(Mono.just("reactive router endpoint"), 211 | String.class))); 212 | }}; 213 | } 214 | } 215 | ``` 216 | 217 | Here the ```mvcControllers()``` returns a List of annotated classes to be used as restcontrollers and the ```reactiveRoutes()``` returns a list of ```RouterFunction``` objects to create the reactive endpoints. needless to say, 218 | a plugin can return an empty list if it doesn't have any endpoints to add. 219 | 220 | [Back to Contents](../#contents) 221 | -------------------------------------------------------------------------------- /spring-test-plugin/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /spring-test-plugin/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /spring-test-plugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.curisprofound 7 | spring-test-plugin 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | spring-test-plugin 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.3.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | spring-hello-plugin 26 | com.curisprofound.springtestplugin.SpringSamplePlugin 27 | 0.0.1 28 | Curis Profound 29 | 30 | 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-webflux 36 | 37 | 38 | 39 | com.curisprofound 40 | shared-plugin-interfaces 41 | 1.1.0 42 | 43 | 44 | 45 | 46 | org.pf4j 47 | pf4j-spring 48 | 0.4.0 49 | 50 | 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-test 55 | test 56 | 57 | 58 | io.projectreactor 59 | reactor-test 60 | test 61 | 62 | 63 | 64 | 65 | 66 | 67 | org.apache.maven.plugins 68 | maven-antrun-plugin 69 | 1.6 70 | 71 | 72 | unzip jar file 73 | package 74 | 75 | 76 | 78 | 79 | 80 | 81 | run 82 | 83 | 84 | 85 | 86 | 87 | 88 | maven-assembly-plugin 89 | 2.3 90 | 91 | 92 | 93 | src/main/resources/assembly.xml 94 | 95 | 96 | false 97 | 98 | 99 | 100 | make-assembly 101 | package 102 | 103 | attached 104 | 105 | 106 | 107 | 108 | 109 | 110 | org.apache.maven.plugins 111 | maven-jar-plugin 112 | 2.4 113 | 114 | 115 | 116 | ${plugin.id} 117 | ${plugin.class} 118 | ${plugin.version} 119 | ${plugin.provider} 120 | ${plugin.dependencies} 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /spring-test-plugin/src/main/java/com/curisprofound/springtestplugin/ApplicationConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.curisprofound.springtestplugin; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | @Configuration 7 | public class ApplicationConfiguration { 8 | @Bean 9 | public GreetProvider greetProvider(){ 10 | return new GreetProvider(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spring-test-plugin/src/main/java/com/curisprofound/springtestplugin/GreetProvider.java: -------------------------------------------------------------------------------- 1 | package com.curisprofound.springtestplugin; 2 | 3 | public class GreetProvider { 4 | public String provide(){ 5 | return "A plugin using Spring framework"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /spring-test-plugin/src/main/java/com/curisprofound/springtestplugin/PluginController.java: -------------------------------------------------------------------------------- 1 | package com.curisprofound.springtestplugin; 2 | 3 | import org.springframework.http.ResponseEntity; 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | 8 | @Controller 9 | @RequestMapping("/plugin-mvc-controller") 10 | public class PluginController { 11 | @GetMapping 12 | public ResponseEntity greetMVC(){ 13 | return ResponseEntity.ok().body("An endpoint defined by annotation in plugin"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /spring-test-plugin/src/main/java/com/curisprofound/springtestplugin/SpringSamplePlugin.java: -------------------------------------------------------------------------------- 1 | package com.curisprofound.springtestplugin; 2 | 3 | import com.curisprofound.plugins.PluginInterface; 4 | import org.pf4j.Extension; 5 | import org.pf4j.PluginWrapper; 6 | import org.pf4j.spring.SpringPlugin; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.context.ApplicationContext; 11 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 12 | import org.springframework.web.reactive.function.server.RouterFunction; 13 | import org.springframework.web.reactive.function.server.ServerResponse; 14 | import reactor.core.publisher.Mono; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | import static org.springframework.web.reactive.function.server.RequestPredicates.GET; 20 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 21 | 22 | public class SpringSamplePlugin extends SpringPlugin { 23 | 24 | private static final Logger log = LoggerFactory.getLogger(SpringSamplePlugin.class); 25 | 26 | public SpringSamplePlugin(PluginWrapper wrapper) { 27 | super(wrapper); 28 | } 29 | 30 | @Override 31 | public void start() { 32 | log.info("Spring Sample plugin.start()"); 33 | } 34 | 35 | @Override 36 | public void stop() { 37 | log.info("Spring Sample plugin.stop()"); 38 | super.stop(); // to close applicationContext 39 | } 40 | 41 | @Override 42 | protected ApplicationContext createApplicationContext() { 43 | AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); 44 | applicationContext.setClassLoader(getWrapper().getPluginClassLoader()); 45 | applicationContext.register(ApplicationConfiguration.class); 46 | applicationContext.refresh(); 47 | return applicationContext; 48 | } 49 | 50 | 51 | @Extension(ordinal = 1) 52 | public static class SpringPlugin implements PluginInterface { 53 | 54 | @Autowired 55 | private GreetProvider greetProvider; 56 | 57 | 58 | 59 | @Override 60 | public String identify() { 61 | return greetProvider.provide(); 62 | } 63 | 64 | @Override 65 | public List mvcControllers() { 66 | return new ArrayList() {{ 67 | add(new PluginController()); 68 | }}; 69 | } 70 | 71 | @Override 72 | public List> reactiveRoutes() { 73 | return new ArrayList>() {{ 74 | add(route(GET("/plugin-end-point"), 75 | req -> ServerResponse.ok().body(Mono.just("reactive router endpoint"), String.class))); 76 | }}; 77 | } 78 | 79 | 80 | } 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /spring-test-plugin/src/main/java/com/curisprofound/springtestplugin/SpringTestPluginApplication.java: -------------------------------------------------------------------------------- 1 | package com.curisprofound.springtestplugin; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringTestPluginApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringTestPluginApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spring-test-plugin/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ralemy/pf4j-spring-tutorial/65c97de962ec8572f632b212dde369e3d873df13/spring-test-plugin/src/main/resources/application.properties -------------------------------------------------------------------------------- /spring-test-plugin/src/main/resources/assembly.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | plugin 9 | 10 | zip 11 | 12 | false 13 | 14 | 15 | false 16 | runtime 17 | lib 18 | 19 | org.pf4j:* 20 | 21 | 22 | org.springframework:* 23 | org.slf4j:* 24 | com.curisprofound:shared-plugin-interfaces 25 | 26 | 27 | 28 | 29 | 30 | target/plugin-classes 31 | classes 32 | 33 | 34 | src/main/resources 35 | 36 | 37 | plugin.properties 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /spring-test-plugin/src/main/resources/plugin.properties: -------------------------------------------------------------------------------- 1 | plugin.id=spring-sample-plugin 2 | plugin.class=com.curisprofound.springtestplugin.SpringSamplePlugin 3 | plugin.version=0.0.1 4 | plugin.provider=Curis Profound 5 | plugin.dependencies= 6 | -------------------------------------------------------------------------------- /spring-test-plugin/src/test/java/com/curisprofound/springtestplugin/SpringTestPluginApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.curisprofound.springtestplugin; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class SpringTestPluginApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | --------------------------------------------------------------------------------