I am in a project that requires the use of jBPM and the technical approach is to have the different BPM processes deployed in containers. This forces me to generate many jBPM applications and I have decided to create an archetype with everything I require, and thus avoid repetitive tasks.

The first thing is to create a basic project with maven, as for example with:

mvn archetype:generate -DgroupId=es.feitam.archetype -DartifactId=feitam-archetype-processjbpm -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

Once created, we move to the folder of the created project, in this case "feitam-archetype-processjbpm", and we execute the following command to prepare in the target of this project, the real project of the new archetype:

mvn archetype:create-from-project

First we modify the pom.xml of the archetype project (not the original one) generated with the previous command located in the target/generated-sources/archetype folder, changing the name of the artifactId that the previous process has added the suffix "-archetype" and not I want the name of the new archetype with that suffix. I change it from the value:

<?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>es.feitam.archetype</groupId>
  <artifactId>feitam-archetype-processjbpm-archetype</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>maven-archetype</packaging>

  <name>feitam-archetype-processjbpm-archetype</name>

  <build>
    <extensions>
      <extension>
        <groupId>org.apache.maven.archetype</groupId>
        <artifactId>archetype-packaging</artifactId>
        <version>3.0.1</version>
      </extension>
    </extensions>

    <pluginManagement>
      <plugins>
        <plugin>
          <artifactId>maven-archetype-plugin</artifactId>
          <version>3.0.1</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>

  <url>http://maven.apache.org</url>
</project>

The pom.xml remains, after modifying the "artifactId" and "name" tags, as I specify 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>es.feitam.archetype</groupId>
  <artifactId>feitam-archetype-processjbpm</artifactId>
  <version>1.0.0</version>
  <packaging>maven-archetype</packaging>

  <name>A feitam.es archetype jBPM Maven project</name>

  <build>
    <extensions>
      <extension>
        <groupId>org.apache.maven.archetype</groupId>
        <artifactId>archetype-packaging</artifactId>
        <version>3.0.1</version>
      </extension>
    </extensions>

    <pluginManagement>
      <plugins>
        <plugin>
          <artifactId>maven-archetype-plugin</artifactId>
          <version>3.0.1</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>

  <url>http://maven.apache.org</url>
</project>

Once this is done, we locate ourselves in the folder target/generated-sources/archetype/src/main/resources/archetype-resources, edit the existing pom.xml file and modify it as I want it to be in the archetype, using the variables $ {groupId} and $ {artifactId} so that the pom.xml generated when using archetype has the values ​​specified in its generate of maven:

<?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>${groupId}</groupId>
  <artifactId>${artifactId}</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  

  <name>jBPM :: feitam.es Maven Project</name>
  <description>A feitam.es jBPM Maven project</description>
  <url>https://feitam.es</url>
  
  <properties>
    <jbpm.version>6.0.0.Final</jbpm.version>
  </properties>
  
  <repositories>
    <repository>
      <id>jboss-public-repository-group</id>
      <name>JBoss Public Repository Group</name>
      <url>http://repository.jboss.org/nexus/content/groups/public/</url>
      <releases>
        <enabled>true</enabled>
        <updatePolicy>never</updatePolicy>
      </releases>
      <snapshots>
        <enabled>true</enabled>
        <updatePolicy>daily</updatePolicy>
      </snapshots>
    </repository>
  </repositories>
  
  
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.jbpm</groupId>
      <artifactId>jbpm-test</artifactId>
      <version>${jbpm.version}</version>
    </dependency>
  </dependencies>
  

  <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                         <goals>
                            <goal>java</goal>
                        </goals>
                        <configuration>
                            <executable>maven</executable>
                            <mainClass>${groupId}.AppDemoStart</mainClass>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
  </build>

  <!--
  mvn archetype:generate -DgroupId=${groupId} -DartifactId=${artifactId} -DarchetypeArtifactId=feitam-archetype-processjbpm -DinteractiveMode=false
  
  mvn eclipse:eclipse -Dwtpversion=2.0
  
  mvn clean compile package install
  
  mvn exec:java -Dexec.mainClass="${groupId}.AppDemoStart"
  
  -->
  
</project>


Now we generate in the directory target/generated-sources/archetype/src/main/resources/archetype-resources/src the directories and files that we want to have in the initial project to be generated with the new archetype.

The following directories are created:

  • In src / main the folder 'resources' must be created
  • In src / main, the 'META-INF' folder must be found in the 'resources' folder above


The files are created using the variable $ {package} where it is required to be replaced by the value of package (when generating the final project) that will be specified when the new archetype is used:

  • In src/main/java we delete the App.java file
  • In src/main/java we create the AppDemoStart.java file with the following content:
#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
package ${package};

import java.util.HashMap;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.jbpm.test.JBPMHelper;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.manager.RuntimeEngine;
import org.kie.api.runtime.manager.RuntimeEnvironmentBuilder;
import org.kie.api.runtime.manager.RuntimeManager;
import org.kie.api.runtime.manager.RuntimeManagerFactory;
import org.kie.api.task.TaskService;

/**
 * It's working!
 *
 */
public class AppDemoStart 
{
    public static void main( String[] args )
    {
    	  System.out.println( "Start" );
        System.out.println( "It's working!" );
        KieServices ks = KieServices.Factory.get();
    		KieContainer kContainer = ks.getKieClasspathContainer();
    		KieBase kbase = kContainer.getKieBase("kbase");
    		
    		RuntimeManager manager = createRuntimeManager(kbase);
    		RuntimeEngine engine = manager.getRuntimeEngine(null);
    		KieSession ksession = engine.getKieSession();
    		TaskService taskService = engine.getTaskService();
    		
        HashMap<String, Object> params = new HashMap<String, Object>();
        params.put("name", "John");
        
        ksession.startProcess("${package}.bpmn.itsworking", params);
        ksession.dispose();
        System.out.println( "End" );
    }
    
  	private static RuntimeManager createRuntimeManager(KieBase kbase) {
  		JBPMHelper.startH2Server();
  		JBPMHelper.setupDataSource();
  		EntityManagerFactory emf = Persistence.createEntityManagerFactory("org.jbpm.persistence.jpa");
  		RuntimeEnvironmentBuilder builder = RuntimeEnvironmentBuilder.Factory.get()
  			.newDefaultBuilder().entityManagerFactory(emf)
  			.knowledgeBase(kbase);
  		return RuntimeManagerFactory.Factory.get()
  			.newSingletonRuntimeManager(builder.get(), "${package}:example:1.0.0");
  	}
}

  • In src/main/resources we create the file itsworking.bpmn with the following content:
<?xml version="1.0" encoding="UTF-8"?> 
<definitions id="Definition"
             targetNamespace="http://www.jboss.org/drools"
             typeLanguage="http://www.java.com/javaTypes"
             expressionLanguage="http://www.mvel.org/2.0"
             xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"
             xmlns:g="http://www.jboss.org/drools/flow/gpd"
             xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
             xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
             xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
             xmlns:tns="http://www.jboss.org/drools">

  <process processType="Private" isExecutable="true" id="${package}.itsworking" name="Check Process" >

    <!-- nodes -->
    <startEvent id="_1"  isInterrupting="true"/>
    <scriptTask id="_jbpm-unique-4" name="Itsworking" scriptFormat="http://www.java.com/java" >
      <script>System.out.println("=============================");
System.out.println("=============================");
System.out.println("=============================");
System.out.println("=============================");
System.out.println("It's working! Hi, " + kcontext.getVariable("name") + "!");
System.out.println("Today is  " + new java.util.Date().toString());
System.out.println("=============================");
System.out.println("=============================");
System.out.println("=============================");
System.out.println("=============================");</script>
    </scriptTask>
    <endEvent id="_jbpm-unique-5" name="End" >
        <terminateEventDefinition />
    </endEvent>

    <!-- connections -->
    <sequenceFlow id="_1-_jbpm-unique-4" sourceRef="_1" targetRef="_jbpm-unique-4" />
    <sequenceFlow id="_jbpm-unique-4-_jbpm-unique-5" sourceRef="_jbpm-unique-4" targetRef="_jbpm-unique-5" />

  </process>

  <bpmndi:BPMNDiagram>
    <bpmndi:BPMNPlane bpmnElement="com.demo.bpmn.itsworking" >
      <bpmndi:BPMNShape bpmnElement="_1" >
        <dc:Bounds x="39" y="39" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_jbpm-unique-4" >
        <dc:Bounds x="184" y="38" width="80" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_jbpm-unique-5" >
        <dc:Bounds x="331" y="38" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_1-_jbpm-unique-4" >
        <di:waypoint x="63" y="63" />
        <di:waypoint x="224" y="62" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_jbpm-unique-4-_jbpm-unique-5" >
        <di:waypoint x="224" y="62" />
        <di:waypoint x="355" y="62" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>

</definitions>


</definitions>

This BPM process is a small process that only renders a message "It's working!" On the console, plus a greeting to a name sent as a parameter from the AppDemoStart class, in addition to returning the current date and time. An example of expected result:

It's working! Hi, John!
Today is  Fri Oct 09 21:41:56 CEST 2020

This process in the jBPM modeler of the Eclipse IDE plugin looks like this:

  • In src/main/resources/META-INF create the kmodule.xml file with this content:
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">

  <kbase name="kbase" packages="*"/>

</kmodule>


With this we will have in the new jBPM process projects a definition of a simple BPM process specified in the itsworking.bpmn file, which is the file used in the jBPM plugin modeler for Eclipse, we have the resources specified in the descriptor kmodule.xml of the KIE module required for jBPM projects, and we also leave a java class implemented to execute the BPM process defined in itsworking.bpmn.

But the most important thing remains, which is to modify the archetype-metadata.xml located in target/generated-sources/archetype/src/main/resources/META-INF/maven. In this file we must specify the files and directories that we have created with tags such as directory and fileSet (see official documentation at https://maven.apache.org/archetype/maven-archetype-plugin/specification/archetype-metadata. html):

<?xml version="1.0" encoding="UTF-8"?>
<archetype-descriptor xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd" name="feitam-archetype-processjbpm"
    xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <fileSets>
    <fileSet filtered="true" packaged="true" encoding="UTF-8">
      <directory>src/main/java</directory>
      <includes>
        <include>**/*.java</include>
      </includes>
    </fileSet>
    <fileSet filtered="true" packaged="true" encoding="UTF-8">
      <directory>src/main/resources</directory>
      <includes>
        <include>**/*.bpmn</include>
      </includes>
    </fileSet>
    <fileSet filtered="false" encoding="UTF-8">
      <directory>src/main/resources/META-INF</directory>
      <includes>
        <include>**/*</include>
      </includes>
    </fileSet>
    <fileSet filtered="true" packaged="true" encoding="UTF-8">
      <directory>src/test/java</directory>
      <includes>
        <include>**/*.java</include>
      </includes>
    </fileSet>
  </fileSets>
</archetype-descriptor>


Very important: let's look at 'filtered = "false"' in the case of src/main_resources/META-INF, which causes that it will not be processed as a Velocity template and will be generated in the final project as defined. In the case of 'filtered = "true"' it is processed as a Velocity template and will apply the structure of the variable "package". We check this at the end to clarify it.

The directory tree is now as shown in the following image:

We see that the changes we have made in the target/generated-sources/archetype path are not reflected in its target, so now we go to target/generated-sources/archetype and execute:

mvn clean install

And now we repeat the 'tree' and we see that the target of the archetype project (not of the original base project) already appears with the directories and files created:

And the previous command when adding "install" to installed (and added9 to the file $HOME/.m2/repository/archetype-catalog.xml of the user who has executed it, being therefore one of the archetypes available locally on that machine for this user:

Now it is time to create a project with the new generated archetype, we do it by referencing from maven to the user's local archetypes catalog with the command:

mvn archetype:generate -DarchetypeCatalog=local


This command shows us the list of archetypes in our local catalog, which only has one in this case, the one we have created, to select which archetype to use in the new project:

We specify the value "1" corresponding to the new archetype created, and the values ​​of groupId, artefactId, version and package are detailed, plus a confirmation, creating the new project:

Once the project is created we execute the following to compile and package:

mvn clean compile package install

And once compiled we check that it runs without problems by executing:

mvn exec:java -Dexec.mainClass="es.feitam.product.akygu.backend.jbpm.process.AppDemoStart"

And it works correctly showing the expected result:

We already have our new archetype, which we can version according to change needs.


One last thing, let's check that in src/resources/META-INF the structure of the specified "package" has not been applied by having the fileSet of that directory specified with 'filtered = "false"', and yet in the directories src/mainjava and src/main/resources have applied the structure of the "package" to its content:


I hope that we can no longer say that we do not assemble a custom archetype for our complex projects. It is laborious, but easy to understand every aspect.