java - 使用 OpenEJB 构建独立的可执行 JAR

标签 java maven ejb maven-shade-plugin openejb

我正在构建一个 CLI 工具,它集成了几个 EJB 模块。为此,我需要构建一个 fat jar,然后将其作为独立应用程序执行。

但是,使用java -jar 执行这个fat jar(注意:conf/openejb.xmlfat jar) 失败并显示以下堆栈跟踪:

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

直接从我的 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

基本设置

我能够在一个简单的工作示例中重现我的问题,我添加了 as an GitHub project进一步调查。

基本项目布局如下:

  | # 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();
        }
    }

}

问题

  • 我是否遗漏了 maven-shade-plugin 的配置?

  • 如何以独立方式使用 openejb 构建 fat jar

示例项目

更新 1:

我根据P. Merkle的回答修改了pom。我找到了另一篇文章 here专门为 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 Project 上添加了一个带有此更改的分支作进一步调查。

更新 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 中相同。我推送了一个相关的 branch到 GitHub 存储库。

所以我的问题是:

  • 什么其他需要从阴影中排除?

在其他答案的帮助下,我终于找到了构建独立 fat jar 的可行解决方案,它适用于我的用例。

更新 3:

步骤(目前)是:

  1. 使用 OpenWebBeansPropertiesTransformer 代替 AppendingTransformer,如 P. Merkle 所述

  2. Romain Manni-Bucau 所述,在阴影中排除 java.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>
    

  3. META-INF 中添加一个 scan.xml,仅包含应扫描的包/类。可以找到当前工作版本 here

问题:

  • 他们是官方的还是更好的方式?

最佳答案

http://tomee.apache.org/advanced/shading/index.html也许http://tomee.apache.org/advanced/applicationcomposer/index.html也是很好的起点。

现在您似乎扫描了类加载器为空的 xml 之类的意外类。可能将 javax.xml.* 从扫描甚至阴影中排除,它会起作用

关于java - 使用 OpenEJB 构建独立的可执行 JAR,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42293912/

相关文章:

java/hibernate查询问题

maven - jasperreports-maven-plugin 的稳定版本是什么?

java - CDI TransactionPhase.BEFORE_COMPLETION 不适用于 WebSphere

java - 从外部脚本启动/停止 java 应用程序

java - 在 Wicket 中使用枚举作为模型对象

java - 组播数据包脏了

maven - 将jar解压到与文件同名的文件夹中

java - 使用aspectj-maven-plugin AOP从另一个模块中剪切模块

java - Java Bean 和企业 Java Bean 的区别?

java - 如何优雅地处理Target Unreachable