FlexiCore Boot Hello World
A refresher on FlexiCore Boot (version 5.0)
What you’ll need
A Java™ Development Kit (JDK) installed. We recommend AdoptOpenJDK version 11 or 8.
Apache Maven version 3.1 or later installed.
An Integrated Developer Environment (IDE)
Popular choices include IntelliJ IDEA, Spring Tools, Visual Studio Code, or Eclipse, and many others.
Java installed (version 8 or better)
What you'll build
You will build a Spring Boot Application and expand it with two interdependent plugins.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wizzdi</groupId>
<artifactId>flexicore-boot-test</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<spring-boot.version>2.3.0.RELEASE</spring-boot.version>
<swagger.version>2.1.2</swagger.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-loader</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/filtered</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>.</directory>
<includes>
<include>pom.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<forceJavacCompilerUse>true</forceJavacCompilerUse>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
</executions>
<configuration>
<mainClass>com.wizzdi.example.init.App</mainClass>
<layout>ZIP</layout>
</configuration>
</plugin>
</plugins>
</build>
</project>
package com.wizzdi.example.init;
import com.wizzdi.flexicore.boot.base.annotations.plugins.EnableFlexiCorePlugins;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.ApplicationPidFileWriter;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackages = {"com.wizzdi.example"})
public class App {
private static final Logger logger = LoggerFactory.getLogger(App.class);
public static void main(String[] args) {
logger.info("Started app");
SpringApplication app = new SpringApplication(App.class);
app.addListeners(new ApplicationPidFileWriter());
ConfigurableApplicationContext context=app.run(args);
logger.info("quitting , total bean count: "+context.getBeanDefinitionCount());
}
}
The created application is a standard Spring Boot application.
You can skip the previous part if you want to add plugins support to an existing Spring Boot application.
You can now create the Spring Boot application by running:
mvn package
from the command line when your current folder is where the pom.xml resides.
Alternatively, you can package from the IDE.
from the command line, change the current directory to the target folder and run
java -jar flexicore-boot-test-1.0.0-exec.jar
the above is a standard hello world Spring Boot application.
We will now add FlexiCore plugins support using a single annotation.
<dependency>
<groupId>com.wizzdi</groupId>
<artifactId>flexicore-boot</artifactId>
<version>1.0.2</version>
<exclusions>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
The only annotation required for supporting inter-dependent plugins is:
@EnableFlexiCorePlugins
@SpringBootApplication
@ComponentScan(basePackages = {"com.wizzdi.example"})
@EnableFlexiCorePlugins //this is the only annotation required for adding the support for flexicore inter-injected plugins
public class App {
You will need to re-compile the Spring Boot application after including the required dependency on FlexiCore Boot and the required annotation.
Once the application is compiled with the above annotation, additional features can be added using
We will build a simple plugin that will do nothing but adding some information to our log file.
Create a new Maven-based project.
Change the pom.xml file to the content below.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>hello-world-flexicore-boot-minimal</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<version.compiler.plugin>3.3</version.compiler.plugin>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<pf4j-spring.version>0.6.0</pf4j-spring.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.wizzdi</groupId>
<artifactId>flexicore-boot</artifactId>
<version>1.0.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.pf4j</groupId>
<artifactId>pf4j-spring</artifactId>
<version>${pf4j-spring.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>false</minimizeJar>
<createDependencyReducedPom>true</createDependencyReducedPom>
<dependencyReducedPomLocation>${java.io.tmpdir}/dependency-reduced-pom.xml
</dependencyReducedPomLocation>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/cxf/bus-extensions.txt</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Plugin-Id>${artifactId}</Plugin-Id>
<Plugin-Version>${version}</Plugin-Version>
<!--suppress UnresolvedMavenProperty -->
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Update the HelloWorld class file.
package org.example.service;
import com.wizzdi.flexicore.boot.base.events.PluginsLoadedEvent;
import com.wizzdi.flexicore.boot.base.interfaces.Plugin;
import org.pf4j.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
@Extension
public class HelloWorld implements Plugin {
private static final Logger logger = LoggerFactory.getLogger(HelloWorld.class);
@EventListener
public void pluginLoaded(PluginsLoadedEvent event) {
logger.info("\n***************** plugin loaded event called *******plugins started: , " +
"\n number of plugins loaded: "+event.getStartedPlugins().size());
}
}
build the plugin by
mvn package
copy the created jar file into the plugins folder under the folder where the main spring application is located.
Before we run our application we need to make sure that the application.properties file inside the config folder is properly defined.
In case you are adding plugins support to an existing application with an existing application.properties file, add one line to your properties file
flexicore.plugins=plugins
#define the location for FlexiCore boot plugins
flexicore.plugins=plugins #defines where plugins are located.
spring.main.web-application-type=none # this is standard Spring configuration
logging.config=config/logback-spring.xml
#define the location of log defintions file
Save the file in the config folder and name it :
application.properties
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOGS" value="logs" />
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
%black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{1.}): %msg%n%throwable
</Pattern>
</layout>
</appender>
<appender name="wizzdi-example" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOGS}/flexicore.log</file>
<encoder
class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- rollover daily and when the file reaches 10 MegaBytes -->
<fileNamePattern>${LOGS}/flexicore-%d{yyyy-MM-dd}.%i.log.gz
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
<logger name="com.wizzdi" level="info" additivity="false">
<appender-ref ref="wizzdi-example" />
</logger>
<logger name="org.example" level="info" additivity="false">
<appender-ref ref="wizzdi-example" />
</logger>
</configuration>
Save the file in the config folder name it :
logback-spring.xml
Note: The structure of both files allows moving this file structure to any location,.
Now run the Spring boot application:
Form the command line:
java -jar flexicore-boot-test-1.0.0-exec.jar
You can verify that the plugin was correctly run and deployed by looking into the log folder
You should see a line similar to this:
2021-02-20 12:05:36,202 INFO org.example.service.HelloWorld [main]
***************** plugin loaded event called *******plugins started: ,
number of plugins loaded: 1
Note: FlexiCore plugin system for Spring is unique as it allows plugins to provide services to other plugins, this is possible to any depth.
When building a dependent plugin, the injected plugin maven coordinates are needed in the pom.xml file. The injection is done in runtime by FlexiCore, thus the scope entry in the pom.xml is set to ‘provided’. So, the created dependent plugin jar doesn’t include any of the classes defined in the injected plugin.
Create a new Maven project and replace the pom file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wizzdi.examples</groupId>
<artifactId>provider</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<version.compiler.plugin>3.3</version.compiler.plugin>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<pf4j-spring.version>0.6.0</pf4j-spring.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.wizzdi</groupId>
<artifactId>flexicore-boot</artifactId>
<version>1.0.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.pf4j</groupId>
<artifactId>pf4j-spring</artifactId>
<version>${pf4j-spring.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>false</minimizeJar>
<createDependencyReducedPom>true</createDependencyReducedPom>
<dependencyReducedPomLocation>${java.io.tmpdir}/dependency-reduced-pom.xml
</dependencyReducedPomLocation>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/cxf/bus-extensions.txt</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Plugin-Id>${artifactId}</Plugin-Id>
<Plugin-Version>${version}</Plugin-Version>
<!--suppress UnresolvedMavenProperty -->
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
package com.wizzdi.examples.provider;
import com.wizzdi.flexicore.boot.base.events.PluginsLoadedEvent;
import com.wizzdi.flexicore.boot.base.interfaces.Plugin;
import org.pf4j.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
@Extension
/**
* provider class , note the version of the plugin.
*/
public class ServiceProvider implements Plugin {
static final Logger logger= LoggerFactory.getLogger(ServiceProvider.class);
@EventListener
public void pluginLoaded(PluginsLoadedEvent event) {
logger.info("Have loaded plugin "+Class.class.getName());
}
public void doSomething(String message) {
logger.info ("doSomething called with a message: "+message);
}
}
./mvn install
or install from the IDE
copy the provider-1.0.0.jar file into the plugins folder hosting the previous plugin.
if you now run the application you will find that there are two plugins detected:
2021-02-24 12:36:21,770 INFO org.example.service.HelloWorld [main]
***************** plugin loaded event called *******plugins started: ,
number of plugins loaded: 2
We will now show how the HelloWorld plugin makes use of the new provider plugin.
Add the maven coordinates of the injected plugins to pom.xml and update few other sections:
<properties>
<maven.compiler.source>11</maven.compiler.source>
<version.compiler.plugin>3.3</version.compiler.plugin>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<pf4j-spring.version>0.6.0</pf4j-spring.version>
<provider-service.version>1.0.0</provider-service.version>
</properties>
...
<dependency>
<groupId>com.wizzdi.examples</groupId>
<artifactId>provider</artifactId>
<version>${provider-service.version}</version>
<scope>provided</scope>
</dependency>
....
<Plugin-Dependencies>provider@>=${provider-service.version}
</Plugin-Dependencies>
Note: The last section is essential, plugins using injected plugins should declare the plugin-dependencies section as a comma-separated list of plugins artifact-id and version. This should be placed inside the manifestEntries section of the maven shade-plugin section of the full pom.xml of the HelloWorld plugin above.
package org.example.service;
//added
import com.wizzdi.examples.provider.ServiceProvider;
import com.wizzdi.flexicore.boot.base.events.PluginsLoadedEvent;
import com.wizzdi.flexicore.boot.base.interfaces.Plugin;
import org.pf4j.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
@Extension
public class HelloWorld implements Plugin {
//added
@Autowired
ServiceProvider serviceProvider;
private static final Logger logger = LoggerFactory.getLogger(HelloWorld.class);
@EventListener
public void pluginLoaded(PluginsLoadedEvent event) {
logger.info("\n***************** plugin loaded event called *******plugins started: , " +
"\n number of plugins loaded: "+event.getStartedPlugins().size());
//added
serviceProvider.doSomething(" calling from HelloWorld");
}
}
Note the added lines
Now the provider plugin bean is injected to the HelloWorld plugin and its public methods, entities and classes are available from within the HelloWorld plugin.
Obviously, the injected plugin has no ‘knowledge’ of other plugins using its services.
Build the HelloWorld plugin and copy it into the plugins folder.
now run the Spring Boot application again:
java -jar flexicore-boot-test-1.0.0-exec.jar
Verify by looking at the log file:
2021-02-24 13:15:34,817 INFO com.wizzdi.examples.provider.ServiceProvider [main] doSomething called with a message: calling from HelloWorld
Success Stories