FlexiCore Boot Hello World

A refresher on FlexiCore Boot (version 5.0)

With FlexiCore Boot you can add FlexiCore capabilities to an existing Spring Boot application.
With FlexiCore Boot you can add different features of FlexiCore when you need them, for example:
  • Start with plugins support only , use FlexiCore unique ability to use inter-dependent plugins.
  • Add plugins’ REST API support, your plugins can now add REST endpoints to your application and other plugins existing ones.
  • Define domain model entities in plugins. Extend your domain model using plugins. Or define the entire domain model through plugins.
  • Add multi-tenancy access control support to an existing Spring Boot application domain model.
  • Add additional capabilities using existing plugins from the FlexiCore marketplace.
  • Note: the latest guide with code for FlexiCore boot demo is available on GitHub
    • It starts with a Spring Boot application having no FlexiCore dependencies
    • In stages, based on branches, additional steps are taken in developing plugins for the first version of the application  

What you’ll need

Development requirements

A Java™ Development Kit (JDK) installed. We recommend AdoptOpenJDK version 11 or 8.

Apache Maven version 3.1 or later installed.

Optional

An Integrated Developer Environment (IDE)

Popular choices include IntelliJ IDEA, Spring Tools, Visual Studio Code, or Eclipse, and many others.

Testing and running requirements

Java installed (version 8 or better)

What you'll build

You will build a Spring Boot Application and expand it with two interdependent plugins.

Step 1: Create a minimal Spring Boot Application

Project Structure

Step 2: Update POM.XML

<?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>
 

Step 3: Update the application code

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.

Step 4: Build and run the 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.

Step 5: Add FlexiCore-boot plugins support.

Add the required FlexiCore Boot Dependency in your pom.xml
 
        <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>
         
Add the required annotation to your application code

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 

Step 5.1 : Create a simple hello-world plug-in

 

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.

Step 5.2: set Spring application.properties file

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

 

Step 5.3: set Spring logback-spring.xml file

<?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 

Step 6: Create an injected (auto-wired) plugin

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>
 

Step 6.1: Update the main class file

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);
    }
}
 

Step 6.2: build the injected plugin

./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 

Step 6.3: update and build the HelloWorld plugin

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@&gt;=${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.

Step 6.3.1: update the HelloWorld class

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 
Thank you for joining the beta, you will be notified by our team soon