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

时间:2017-02-17 09:17:40

标签: java maven ejb maven-shade-plugin openejb

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

但是,使用fat jar执行此java -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配置中的内容?

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

示例项目

更新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> 

但是,异常与Update 1中的异常相同。我将相关的branch推送到GitHub存储库。

所以我的问题是:

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

在其他答案的帮助下,我终于找到了一个可行的解决方案,用于构建一个独立的fat jar,这对我的用例很有用。

更新3:

步骤(现在)是:

  1. OpenWebBeansPropertiesTransformer

  2. 所述AppendingTransformer代替java.xml.*的用法
  3. 按照P. Merkle

    的说明排除<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

  4. META-INF中添加var,仅包括应扫描的包/类。可以找到当前的工作版本Romain Manni-Bucau

  5. 问题:

    • 这是他们的官方或更好的方式吗?

2 个答案:

答案 0 :(得分:2)

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

现在看来你扫描类似于xml的意外类,其中类加载器为null。可能会从扫描甚至阴影中排除javax.xml。*,它会起作用

答案 1 :(得分:1)

异常是通过openwebbeans.properties将来自不同模块的多个AppendingTransformer文件合并到一个属性文件中引起的。

这是因为openwebbeans.properties个文件是structured in a special way

  

所有这些文件都包含属性 configuration.ordinal ,用于定义其重要性&#39;。具有更高configuration.ordinal的属性文件中的任何设置都将覆盖具有较低configuration.ordinal的设置。

现在,如果你天真地合并这些文件 - 就像AppendingTransformer那样 - 你将在同一个文件中结束多个竞争序数属性。

解决方案是将AppendingTransformer替换为OpenWebBeansPropertiesTransformer ,这会在合并时保留优先级。

此处提供了一个示例pom.xml:http://openwebbeans.apache.org/meecrowave/meecrowave-maven/index.html

然而,坏消息是,这个解决方案揭示了另一个例外:

  

org.apache.openejb.cdi.CdiScanner.handleBda(CdiScanner.java:271)中的java.lang.NullPointerException

到目前为止,我还无法找出原因。