用OpenEJB构建一个独立的可执行罐



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

但是,使用java -jar执行此fat jar(注意:conf/openejb.xmlfat 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(): 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中in版本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();
        }
    }
}

问题

  • 我是否错过了maven-shade-plugin的配置中的一些东西?

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

示例项目

  • 完整的代码示例可作为github项目
  • 提供

更新1:

我根据P. Merkle的答案更改了POM。我在这里找到了另一篇文章,描述了专门针对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>

XENTING删除此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存储库。

所以我的问题是:

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

在其他答案的帮助下,我终于能够找到一个用于构建独立fat jar的工作解决方案,该解决方案正在为我的用例工作。

更新3:

(目前)的步骤是:

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

  2. 所述
  3. 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>
    

  4. META-INF中添加scan.xml,其中仅包括软件包/类,应扫描。当前的工作版本可以在此处找到

问题:

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

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

现在看来您扫描了像XML这样的意外类,其中classloader为null。可能排除javax.xml。*从扫描甚至阴影中排除,它将起作用

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

这是因为openwebbeans.properties文件以特殊方式构造:

所有这些文件都包含a single 属性 configuration.ordinal ,它定义了其"重要性"。来自具有较高配置的属性文件的任何设置。ORDINAL将从一个具有较低configuration.ordinal的设置覆盖设置。

现在,如果您天真地合并了这些文件 - 就像AppendingTransformer一样 - 您最终将在同一文件中获得多个竞争的序属性。

解决方案是用OpenWebBeansPropertiesTransformer 替换AppendingTransformer,该在合并时保留优先级。

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


坏消息是,该解决方案发现了另一个例外:

java.lang.nullpointerexception at org.apache.openejb.cdi.cdiscanner.handlebda(cdiscanner.java:271)

到目前为止,我无法确定其原因。

相关内容

最新更新