My goal is to create Jakarta EE web application which uses embedded web server and embedded database, so it will be possible to run this webapp with single command.
Currently I have an app which uses embedded H2 database, it works fine when deployed to separate Glassfish instance, but fails with embedded one.
I have tried both cargo-maven3-plugin
and wildfly-maven-plugin
.
This is web app I'm trying to launch in embedded Jakarta EE server:
./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>example</groupId>
<artifactId>jakartaee-hello-world</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>jakartaee-hello-world</name>
<description>
</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.report.sourceEncoding>UTF-8</project.report.sourceEncoding>
<maven.compiler.release>17</maven.compiler.release>
<jakartaee-api.version>10.0.0</jakartaee-api.version>
<payara.version>6.2023.5</payara.version>
<compiler-plugin.version>3.11.0</compiler-plugin.version>
<war-plugin.version>3.3.2</war-plugin.version>
<cargo.version>1.10.7</cargo.version>
<wildfly.version>27.0.1.Final</wildfly.version>
<wildfly-plugin.version>4.1.0.Final</wildfly-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>${jakartaee-api.version}</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate.orm/hibernate-core -->
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.2.6.Final</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.1.214</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
<build>
<finalName>myapp</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>${war-plugin.version}</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<!-- Execute 'mvn clean package cargo:run' to run the application. -->
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven3-plugin</artifactId>
<version>${cargo.version}</version>
<configuration>
<container>
<containerId>payara</containerId>
<artifactInstaller>
<groupId>fish.payara.distributions</groupId>
<artifactId>payara</artifactId>
<version>${payara.version}</version>
</artifactInstaller>
</container>
</configuration>
</plugin>
<!-- Execute 'mvn clean package wildfly:dev' to run the application. -->
<!-- It doesn't work for some reason in this Jakarta MVC project -->
<plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
<version>${wildfly-plugin.version}</version>
<configuration>
<version>${wildfly.version}</version>
<server-config>glassfish-resources.xml</server-config>
</configuration>
</plugin>
</plugins>
</build>
</project>
src\main\webapp\WEB-INF\glassfish-resources.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
<jdbc-connection-pool
datasource-classname="org.apache.commons.dbcp2.BasicDataSource"
name="embeddedConnPool"
res-type="javax.sql.DataSource">
<property name="URL" value="jdbc:h2:mem:dataSource;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false" />
<property name="User" value="root" />
<property name="Password" value="qwerty" />
</jdbc-connection-pool>
<jdbc-resource jndi-name="java:app/jdbc/embeddedDS" pool-name="embeddedConnPool" enabled="true" />
</resources>
src\main\resources\META-INF\persistence.xml
:
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
version="3.0">
<persistence-unit name="only-unit"
transaction-type="JTA" >
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>java:app/jdbc/embeddedDS</jta-data-source>
<properties>
<property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.SunOneJtaPlatform" />
</properties>
</persistence-unit>
</persistence>
src\main\java\myapp\rest\DemoApplication.java
:
package myapp.rest;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;
@ApplicationPath("/rest/*")
public class DemoApplication extends Application {
}
src\main\java\myapp\rest\PersonResource.java
:
package myapp.rest;
import java.util.List;
import jakarta.inject.Inject;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import myapp.dao.PersonDao;
import myapp.model.Person;
@Path("person")
public class PersonResource {
@Inject
PersonDao personDao;
@GET
@Path("all")
@Produces({ MediaType.APPLICATION_JSON })
public List<Person> findAll() {
var list = personDao.findAll();
return list;
}
@POST
@Path("new")
@Produces({ MediaType.APPLICATION_JSON })
@Consumes(MediaType.APPLICATION_JSON)
public Person create(Person person) {
person.setId(null);
System.out.println(person);
personDao.create(person);
return person;
}
}
src\main\java\myapp\dao\PersonDao.java
:
package myapp.dao;
import java.util.List;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import myapp.model.Person;
@ApplicationScoped
public class PersonDao {
@PersistenceContext(unitName = "only-unit")
private EntityManager em;
@Transactional
public Person create(Person person) {
em.persist(person);
return person;
}
public List<Person> findAll() {
String queryString = "SELECT p FROM Person p";
var query = em.createQuery(queryString, Person.class);
var resultList = query.getResultList();
return resultList;
}
}
src\main\java\myapp\model\Person.java
:
package myapp.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
}
I was expecting to execute mvn clean package cargo:run
or mvn clean package wildfly:dev
, and rest endpoint will become available: curl.exe -X GET http://localhost:8080/myapp/rest/person/all/
mvn clean package cargo:run
results in this stacktrace:
[INFO] [talledLocalContainer] Payara 6.2023.5 starting...
[INFO] [talledLocalContainer] WARNING: You are running the product on an unsupported JDK version and might see unexpected results or exceptions.
[INFO] [talledLocalContainer] Attempting to start cargo-domain.... Please look at the server log for more details.....
[INFO] [talledLocalContainer] Payara 6.2023.5 is stopping...
[INFO] [talledLocalContainer] Payara 6.2023.5 is stopped
[WARNING] [talledLocalContainer] org.codehaus.cargo.util.CargoException: At least one GlassFish deployment has failed: org.codehaus.cargo.util.CargoException: GlassFish admin command with args (--interactive=false --host localhost --port 4848 --user admin --passwordfile C:\JAVA2023\JakartaPlatformHiberRestTinyH2\target\cargo\configurations\payara\password.properties deploy --force --contextroot myapp C:\JAVA2023\JakartaPlatformHiberRestTinyH2\target\myapp.war) failed: asadmin exited 1: remote failure: Error occurred during deployment: Exception while preparing the app : org/hibernate/bytecode/enhance/spi/EnhancementContextWrapper. Please see server.log for more details.
Command deploy failed.
[ERROR] Starting container [org.codehaus.cargo.container.payara.PayaraInstalledLocalContainer@3c6bd624] failed
org.codehaus.cargo.util.CargoException: At least one GlassFish deployment has failed: org.codehaus.cargo.util.CargoException: GlassFish admin command with args (--interactive=false --host localhost --port 4848 --user admin --passwordfile C:\JAVA2023\JakartaPlatformHiberRestTinyH2\target\cargo\configurations\payara\password.properties deploy --force --contextroot myapp C:\JAVA2023\JakartaPlatformHiberRestTinyH2\target\myapp.war) failed: asadmin exited 1: remote failure: Error occurred during deployment: Exception while preparing the app : org/hibernate/bytecode/enhance/spi/EnhancementContextWrapper. Please see server.log for more details.
Command deploy failed.
And mvn clean package wildfly:dev
results in this:
[INFO] STANDALONE server is starting up.
13:34:48,329 INFO [org.jboss.modules] (main) JBoss Modules version 2.0.3.Final
java.lang.IllegalStateException: WFLYCTL0214: Could not load configuration file: glassfish-resources.xml. The configuration file argument must specify the path to a file located in the configuration directory. The path must be a relative path, and must be relative to the configuration directory C:\JAVA2023\JakartaPlatformHiberRestTinyH2\target\server\standalone\configuration.
at org.jboss.as.controller@19.0.1.Final//org.jboss.as.controller.persistence.ConfigurationFile.determineMainFile(ConfigurationFile.java:387)
at org.jboss.as.controller@19.0.1.Final//org.jboss.as.controller.persistence.ConfigurationFile.<init>(ConfigurationFile.java:207)
at org.jboss.as.server@19.0.1.Final//org.jboss.as.server.ServerEnvironment.<init>(ServerEnvironment.java:590)
at org.jboss.as.server@19.0.1.Final//org.jboss.as.server.Main.determineEnvironment(Main.java:425)
at org.jboss.as.server@19.0.1.Final//org.jboss.as.server.Main.main(Main.java:97)
at org.jboss.modules.Module.run(Module.java:353)
at org.jboss.modules.Module.run(Module.java:321)
at org.jboss.modules.Main.main(Main.java:604)
13:34:48,687 FATAL [org.jboss.as.server] (main) WFLYSRV0239: Aborting with exit code 1
Provided sample application can be run with this single command:
mvn clean package org.codehaus.cargo:cargo-maven3-plugin:run -Dcargo.maven.containerId=glassfish7x
All plugins from pom.xml
can be removed, apart from maven-compiler-plugin
and maven-war-plugin
. No additional configuration is needed.
In console, there will be message:
[INFO] [talledLocalContainer] GlassFish 7.0.6 started on port [8080]
[INFO] Press Ctrl-C to stop the container...
But pressing Ctrl-C can result in failed attempt of stopping the container - you will have to kill java process manually.
To stop container more properly, first send web-request to server:
curl.exe -X GET http://localhost:4848/__asadmin/stop-domain -u admin:adminadmin
After that press Ctrl+C, all resources will be released and container will stop properly.
Other containers and usecases available in Codehaus Cargo Maven 3 Plugin can be found here: https://codehaus-cargo.github.io/cargo/Maven+3+Plugin.html