Saturday, February 27. 2021
Adding custom layers to bootable JAR
Two posts ago the bootable JAR was tested inside the blog. That time the same application that was previously used inside Spring Boot was modified to be packaged using this new technology. With this packaging technique the final executable JAR file contains the customized wildfly bundle (only the needed layers are included) and the application that runs inside it. Today's entry complements the previous one in order to manage your own layers. As I commented in the first post the final configuration can be modified with CLI commands, but adding your own layers is another way of customizing it, and layers can be used for configuring the standalone.xml file but also adding your own modules. The project wildfly-datasources-galleon-pack includes several layers to add the most common jdbc datasources (postgresql, mysql and oracle) inside the OpenShift Container Platform (OCP). Although it is not exactly done for bootable JAR, it is almost the same and the project is a perfect example for managing your own layers in your application.
A simple application has been developed for the entry. The app implements a jax-rs endpoint to perform CRUD operations over a hypothetical user entity. A mariadb server 10.4 will be installed in my laptop to be used as the backend database. The application project is divided in two sub-projects: mariadb-layer, which adds the custom layers for mariadb (module, driver and datasource/pool); and bootable-mariadb-war, the application itself that uses the bootable jar and includes the layer previously defined in the former project.
Let's compare how layers work instead of the common CLI commands.
The first thing to add is the jdbc client library for mariadb (the JAR file itself). A module is recommended to be used. In a common CLI configuration the jar should be downloaded and configured as a new module.
module add --name=org.mariadb --resources=/path/to/mariadb-java-client-2.7.2.jar --dependencies=javax.api,javax.transaction.api
But using layers, the library is defined as a maven dependency for the project and the file src/main/resources/modules/org/mariadb/main/module.xml is placed pointing to that dependency.
<?xml version="1.0" encoding="UTF-8"?> <module name="org.mariadb" xmlns="urn:jboss:module:1.8"> <resources> <artifact name="${org.mariadb.jdbc:mariadb-java-client}"/> </resources> <dependencies> <module name="javax.api"/> <module name="javax.transaction.api"/> </dependencies> </module>
Once the module is installed a driver is configured via CLI.
/subsystem=datasources/jdbc-driver=mariadb:add(driver-name=mariadb, driver-module-name=org.mariadb, driver-xa-datasource-class-name=org.mariadb.jdbc.MySQLDataSource)
But a layer can also be defined setting the same information in a layer-spec file inside the project (src/main/resources/layers/standalone/mariadb-driver/layer-spec.xml).
<?xml version="1.0" encoding="UTF-8"?> <layer-spec xmlns="urn:jboss:galleon:layer-spec:1.0" name="mariadb-driver"> <feature spec="subsystem.datasources"> <feature spec="subsystem.datasources.jdbc-driver"> <param name="driver-name" value="mariadb"/> <param name="jdbc-driver" value="mariadb"/> <param name="driver-xa-datasource-class-name" value="org.mariadb.jdbc.MySQLDataSource"/> <param name="driver-module-name" value="org.mariadb"/> </feature> </feature> <packages> <package name="org.mariadb"/> </packages> </layer-spec>
This file defines a new layer called mariadb-driver (that can be included like any other layer to the final wildfly JAR) which adds the exact same diver configuration than the previous CLI command. The layer also references the module previously created, that way it will also be included in the bootable JAR.
The last step is defining the datasource/pool.
data-source add --name=MariaDBDS --jndi-name=java:jboss/MariaDBDS --driver-name=mariadb --connection-url=jdbc:mariadb://localhost:3306/test --user-name=test --password=password --validate-on-match=true --background-validation=false --valid-connection-checker-class-name=org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker --exception-sorter-class-name=org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter
But again a layer can do exactly the same configuration. A new file in the project src/main/resources/layers/standalone/mariadb-datasource/layer-spec.xml is needed.
<?xml version="1.0" encoding="UTF-8"?> <layer-spec xmlns="urn:jboss:galleon:layer-spec:1.0" name="mariadb-datasource"> <dependencies> <layer name="mariadb-driver"/> </dependencies> <feature spec="subsystem.datasources.data-source"> <param name="data-source" value="MariaDBDS"/> <param name="jndi-name" value="java:jboss/MariaDBDS"/> <param name="driver-name" value="mariadb"/> <param name="connection-url" value="jdbc:mariadb://localhost:3306/test"/> <param name="user-name" value="test"/> <param name="password" value="password"/> <param name="background-validation" value="false"/> <param name="validate-on-match" value="true"/> <param name="valid-connection-checker-class-name" value="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker"/> <param name="exception-sorter-class-name" value="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter"/> </feature> </layer-spec>
Note that this layer mariadb-datasource depends on the previous layer mariadb-driver. Therefore if you include this one, the previous one is also added and, in turn, the client library/module.
Finally the war application project needs to reference the layer one. The wildfly-jar-maven-plugin should include the resulting maven artifact of the previous project es.rickyepoderi.bootablejar:mariadb-layer:0.0.1-SNAPSHOT as a feature-pack. The provided layer mariadb-datasource is incorporated over the base jaxrs-server.
<plugin> <groupId>org.wildfly.plugins</groupId> <artifactId>wildfly-jar-maven-plugin</artifactId> <version>2.0.2.Final</version> <configuration> <feature-packs> <feature-pack> <location>wildfly@maven(org.jboss.universe:community-universe)#22.0.1.Final</location> </feature-pack> <feature-pack> <groupId>es.rickyepoderi.bootablejar</groupId> <artifactId>mariadb-layer</artifactId> <version>0.0.1-SNAPSHOT</version> </feature-pack> </feature-packs> <layers> <layer>jaxrs-server</layer> <layer>mariadb-datasource</layer> </layers> </configuration> <executions> <execution> <goals> <goal>package</goal> </goals> </execution> </executions> </plugin>
This configuration adds the mariadb datasource, driver and module to the base jaxrs-server and constructs a bootable JAR able to connect to the local database. The application is finally complete.
Here it is the video that shows how the bootable JAR is created and started. Accessing to the swagger index page a new user is created and retrieved from the application. The local database is correctly working with the application.
And that is all for today. The goal was showing that the bootable JAR can also be extended with custom galleon layers. They can be defined in other maven projects and can include configuration and modules (jars) to the wildfly bundle. Layers can be combined with CLI commands in order to customize and extend the bootable JAR technology. In this entry a sub-project was created to add all the needed components to connect to a mariadb server (module/jar and configuration for the driver and the datasource). The application war sub-project adds the previous layer to a jax-rs base server to get a complete and functional application with access to the database. The final maven project for this entry can be downloaded from here.
Layered regards!
Comments