Создание автономного исполняемого JAR с OpenEJB
Я создаю инструмент CLI, который интегрируется с несколькими модулями EJB. По этой причине мне нужно построить fat jar
, который затем выполняется как отдельное приложение.
Однако выполнение этого fat jar
с помощью java -jar
(Примечание: conf/openejb.xml
находится в том же каталоге, что и fat jar
), с помощью следующей команды 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(): [email protected]
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
Выполняя код непосредственно из моей IDE (IntelliJ), создается автономный контейнер и ведет себя как ожидалось.
Резюме версии:
-
openejb
в версии 1.7.0
/openejb-server
в версии 7.0.2
-
maven-shade-plugin
в версии 2.4.3
-
Maven
в версии 3.3.9
-
hibernate
в версии 5.2.7
Основная настройка
Я смог воспроизвести свою проблему на простом рабочем примере, который я добавил как проект GitHub для дальнейшего изучения.
Основной макет проекта выглядит следующим образом:
| # demo-shade
| - demo-services (EJB-Module)
| - demo-main (Shading happens here)
Конфигурация maven-shade-plugin
выглядит следующим образом:
<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>
Код для запуска контейнера:
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();
}
}
}
Вопросы
Пример проекта
ОБНОВЛЕНИЕ 1:
Я изменил пом в соответствии с ответом П. Меркле. Я нашел еще одну статью здесь, описывающую процесс затенения специально для TomEE.
pom
изменен на
<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>
Выполнение этого fat jar
приводит:
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
Я добавил ветку с этими изменениями в Проект GitHub для дальнейшего изучения.
ОБНОВЛЕНИЕ 2
Я исключил javax.xml.*
из тени:
<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>
Однако исключение остается таким же, как в обновлении 1. Я нажал связанный ветвь на GitHub хранилище.
Итак, мой вопрос:
- Что else необходимо исключить из тени?
С помощью других ответов я наконец смог найти рабочее решение для создания автономного fat jar
, который работает для моего использования.
ОБНОВЛЕНИЕ 3:
Шагами (пока) являются:
-
Использование OpenWebBeansPropertiesTransformer
вместо AppendingTransformer
, как указано P. Merkle
-
Исключить java.xml.*
в тени, как указано 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>
-
Добавьте scan.xml
в META-INF
, включая только пакеты/классы, которые необходимо отсканировать. Текущую рабочую версию можно найти здесь
Вопрос:
- Это их официальный или лучший способ сделать это?
Ответы
Ответ 1
http://tomee.apache.org/advanced/shading/index.html и, возможно, http://tomee.apache.org/advanced/applicationcomposer/index.html также являются хорошими отправными точками.
Теперь кажется, что вы сканируете неожиданные классы, такие как xml, где classloader равен null. Вероятно, исключить javax.xml. * Из сканирования или даже тени, и он будет работать
Ответ 2
Исключение вызвано слиянием нескольких файлов openwebbeans.properties
из разных модулей в один файл свойств через AppendingTransformer
.
Это связано с тем, что openwebbeans.properties
файлы структурированы особым образом:
Все эти файлы содержат одно свойство configuration.ordinal, которое определяет их "важность". Любая настройка из файла свойств с более высокой конфигурацией.ординарный будет перезаписывать настройки с одного с более низкой конфигурацией.
Теперь, если вы наивно объедините эти файлы - как AppendingTransformer
делает--, вы получите несколько конкурирующих порядковых свойств в том же файле.
Решение состоит в замене AppendingTransformer
на OpenWebBeansPropertiesTransformer
, которое сохраняет приоритеты при слиянии.
Пример pom.xml доступен здесь: http://openwebbeans.apache.org/meecrowave/meecrowave-maven/index.html
Плохая новость заключается в том, что это решение раскрывает другое исключение:
java.lang.NullPointerException на org.apache.openejb.cdi.CdiScanner.handleBda(CdiScanner.java:271)
До сих пор я не мог определить причину этого.