I am building a CLI tool, which integrates with several EJB modules. For this reason, I need to build a fat jar
, which is then executed as a standalone application.
However, executing this fat jar
with java -jar
(Note: conf/openejb.xml
is in the same directory as the fat jar
) fails with the following stacktrace:
INFORMATION - PersistenceUnit(name=demo, provider=org.hibernate.jpa.HibernatePersistenceProvider) - provider time 2706ms
INFORMATION - Jndi(name="java:global/DemoMain/demo-shade-1.0-SNAPSHOT/MEJB!javax.management.j2ee.ManagementHome")
INFORMATION - Jndi(name="java:global/DemoMain/demo-shade-1.0-SNAPSHOT/MEJB")
INFORMATION - Jndi(name="java:global/DemoMain/demo-shade-1.0-SNAPSHOT/openejb/Deployer!org.apache.openejb.assembler.Deployer")
INFORMATION - Jndi(name="java:global/DemoMain/demo-shade-1.0-SNAPSHOT/openejb/Deployer")
INFORMATION - Jndi(name="java:global/DemoMain/demo-shade-1.0-SNAPSHOT/openejb/ConfigurationInfo!org.apache.openejb.assembler.classic.cmd.ConfigurationInfo")
INFORMATION - Jndi(name="java:global/DemoMain/demo-shade-1.0-SNAPSHOT/openejb/ConfigurationInfo")
INFORMATION - Jndi(name="java:global/DemoMain/demo-shade-1.0-SNAPSHOT/DemoServiceImpl!com.github.rzo1.service.DemoService")
INFORMATION - Jndi(name="java:global/DemoMain/demo-shade-1.0-SNAPSHOT/DemoServiceImpl")
INFORMATION - Existing thread singleton service in SystemInstance(): org.apache.openejb.cdi.ThreadSingletonServiceImpl@557c8e7e
INFORMATION - Closing DataSource: demoDS
INFORMATION - Closing DataSource: demoDSNonJTA
Exception in thread "Thread-0" java.lang.RuntimeException: org.apache.openejb.OpenEjbContainer$AssembleApplicationException: javax.enterprise.inject.spi.DeploymentException: couldn't start owb context
at com.github.rzo1.DemoMain.run(DemoMain.java:116)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.openejb.OpenEjbContainer$AssembleApplicationException: javax.enterprise.inject.spi.DeploymentException: couldn't start owb context
at org.apache.openejb.OpenEjbContainer$Provider.createEJBContainer(OpenEjbContainer.java:346)
at javax.ejb.embeddable.EJBContainer.createEJBContainer(EJBContainer.java:56)
at com.github.rzo1.DemoMain.run(DemoMain.java:90)
... 1 more
Caused by: javax.enterprise.inject.spi.DeploymentException: couldn't start owb context
at org.apache.openejb.cdi.ThreadSingletonServiceImpl.initialize(ThreadSingletonServiceImpl.java:191)
at org.apache.openejb.cdi.CdiBuilder.build(CdiBuilder.java:41)
at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:913)
at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:717)
at org.apache.openejb.OpenEjbContainer$Provider.createEJBContainer(OpenEjbContainer.java:342)
... 3 more
Caused by: org.apache.webbeans.exception.WebBeansException: Wrong startup object.
at org.apache.webbeans.web.lifecycle.WebContainerLifecycle.getServletContext(WebContainerLifecycle.java:227)
at org.apache.webbeans.web.lifecycle.WebContainerLifecycle.startApplication(WebContainerLifecycle.java:86)
at org.apache.openejb.cdi.ThreadSingletonServiceImpl.initialize(ThreadSingletonServiceImpl.java:189)
... 7 more
Executing the code directly from my IDE (IntelliJ) the standalone container comes up and behaves as expected.
Version Summary:
openejb
in version1.7.0
/openejb-server
in version7.0.2
maven-shade-plugin
in version2.4.3
Maven
in version3.3.9
hibernate
in version5.2.7
Basic Setup
I was able to reproduce my problem on a simple working example, which I added as an GitHub project for further investigation.
The basic project layout is as follows:
| # demo-shade
| - demo-services (EJB-Module)
| - demo-main (Shading happens here)
The configuration of the maven-shade-plugin
is as follows:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>demo-shade-${project.version}</finalName>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>com.github.rzo1.DemoMain</Main-Class>
</manifestEntries>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/openwebbeans/openwebbeans.properties</resource>
</transformer>
</transformers>
<filters>
<filter> <!-- we don't want JSF to be activated -->
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/faces-config.xml</exclude>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<shadedClassifierName>dist</shadedClassifierName>
</configuration>
</execution>
</executions>
</plugin>
The code to start up the container:
EJBContainer ejbContainer = null;
try {
final Properties properties = new Properties();
properties.setProperty(EJBContainer.APP_NAME, applicationName);
properties.setProperty(EJBContainer.PROVIDER, OpenEjbContainer.class.getName());
properties.setProperty(OpenEjbContainer.OPENEJB_EMBEDDED_REMOTABLE, "false");
properties.setProperty("ejbd.disabled", "true");
properties.setProperty("ejbds.disabled", "true");
properties.setProperty("admin.disabled", "true");
properties.setProperty("openejb.jaxrs.application", "false");
Path launchPath = Paths.get(DemoMain.class.getProtectionDomain().getCodeSource().getLocation().toURI());
properties.setProperty("openejb.configuration", launchPath.toAbsolutePath() + "/conf/openejb.xml");
properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
properties.put("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver");
// This is the line starting the EJB container
ejbContainer = EJBContainer.createEJBContainer(properties);
ejbContainer.getContext().bind("inject", this);
ejbContainerReady = true;
final CountDownLatch latch = new CountDownLatch(1);
// Graceful shutdown
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
logger.info("Shutting down..");
latch.countDown();
logger.info("Shutdown completed successfully.");
} catch (final Exception e) {
logger.error("Graceful shutdown went wrong. SIGKILL (kill -9) if you want.", e);
}
}
});
try {
latch.await();
} catch (final InterruptedException e) {
// ignored
}
} catch (final Exception e) {
ejbContainerReady = false;
throw new RuntimeException(e);
} finally {
if (ejbContainer != null) {
ejbContainer.close();
}
}
}
Questions
Did I miss something in the configuration of the
maven-shade-plugin
?How can I build a
fat jar
usingopenejb
in a standalone manner?
Example Project
UPDATE 1:
I changed the pom according to the answer by P. Merkle. I found an other article here describing the process of shading specifically for TomEE.
pom
changed to
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>demo-shade-${project.version}</finalName>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>com.github.rzo1.DemoMain</Main-Class>
</manifestEntries>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.openwebbeans.maven.shade.OpenWebBeansPropertiesTransformer"/>
</transformers>
<filters>
<filter> <!-- we don't want JSF to be activated -->
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/faces-config.xml</exclude>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<shadedClassifierName>dist</shadedClassifierName>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.apache.openwebbeans</groupId>
<artifactId>openwebbeans-maven</artifactId>
<version>1.7.0</version>
</dependency>
</dependencies>
</plugin>
Extecuting this fat jar
brings:
INFORMATION - OpenWebBeans Container is starting...
INFORMATION - Adding OpenWebBeansPlugin : [CdiPlugin]
SCHWERWIEGEND - CDI Beans module deployment failed
java.lang.NullPointerException
at org.apache.openejb.cdi.CdiScanner.handleBda(CdiScanner.java:271)
at org.apache.openejb.cdi.CdiScanner.init(CdiScanner.java:148)
at org.apache.openejb.cdi.OpenEJBLifecycle.startApplication(OpenEJBLifecycle.java:179)
at org.apache.openejb.cdi.ThreadSingletonServiceImpl.initialize(ThreadSingletonServiceImpl.java:189)
at org.apache.openejb.cdi.CdiBuilder.build(CdiBuilder.java:41)
at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:913)
at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:717)
at org.apache.openejb.OpenEjbContainer$Provider.createEJBContainer(OpenEjbContainer.java:342)
at javax.ejb.embeddable.EJBContainer.createEJBContainer(EJBContainer.java:56)
at com.github.rzo1.DemoMain.run(DemoMain.java:90)
at java.lang.Thread.run(Unknown Source)
INFORMATION - Closing DataSource: demoDS
INFORMATION - Closing DataSource: demoDSNonJTA
Exception in thread "Thread-0" java.lang.RuntimeException: org.apache.openejb.OpenEjbContainer$AssembleApplicationException: javax.enterprise.inject.spi.DeploymentException: couldn't start owb context
at com.github.rzo1.DemoMain.run(DemoMain.java:116)
at java.lang.Thread.run(Unknown Source)
Caused by: org.apache.openejb.OpenEjbContainer$AssembleApplicationException: javax.enterprise.inject.spi.DeploymentException: couldn't start owb context
at org.apache.openejb.OpenEjbContainer$Provider.createEJBContainer(OpenEjbContainer.java:346)
at javax.ejb.embeddable.EJBContainer.createEJBContainer(EJBContainer.java:56)
at com.github.rzo1.DemoMain.run(DemoMain.java:90)
... 1 more
Caused by: javax.enterprise.inject.spi.DeploymentException: couldn't start owb context
at org.apache.openejb.cdi.ThreadSingletonServiceImpl.initialize(ThreadSingletonServiceImpl.java:191)
at org.apache.openejb.cdi.CdiBuilder.build(CdiBuilder.java:41)
at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:913)
at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:717)
at org.apache.openejb.OpenEjbContainer$Provider.createEJBContainer(OpenEjbContainer.java:342)
... 3 more
Caused by: org.apache.openejb.OpenEJBRuntimeException: java.lang.NullPointerException
at org.apache.openejb.cdi.OpenEJBLifecycle.startApplication(OpenEJBLifecycle.java:200)
at org.apache.openejb.cdi.ThreadSingletonServiceImpl.initialize(ThreadSingletonServiceImpl.java:189)
... 7 more
Caused by: java.lang.NullPointerException
at org.apache.openejb.cdi.CdiScanner.handleBda(CdiScanner.java:271)
at org.apache.openejb.cdi.CdiScanner.init(CdiScanner.java:148)
at org.apache.openejb.cdi.OpenEJBLifecycle.startApplication(OpenEJBLifecycle.java:179)
... 8 more
I added a branch with this changes on the GitHub Project for further investigation.
UPDATE 2
I excluded javax.xml.*
from the shade:
<excludes>
<exclude>META-INF/faces-config.xml</exclude>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>javax/xml/**</exclude>
</excludes>
However, the exception remains the same as in Update 1. I pushed a related branch to the GitHub repository.
So my question is:
- What else needs to be excluded from the shade?
With the help of the other answers , I was finally able to find a working solution for building a standalone fat jar
, which is working for my use-case.
UPDATE 3:
The steps (for now) are:
Usage of
OpenWebBeansPropertiesTransformer
insteadAppendingTransformer
as stated by P. MerkleExclude
java.xml.*
in the shade as stated by Romain Manni-Bucau:<excludes> <exclude>META-INF/faces-config.xml</exclude> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> <exclude>javax/xml/**</exclude>
Add a
scan.xml
inMETA-INF
including only packages / classes, which should be scanned. Current working version can be found here
Question:
- Is their an official or better way of doing this?