【问题标题】:Executable war file that starts jetty without maven在没有 maven 的情况下启动码头的可执行战争文件
【发布时间】:2011-01-28 08:14:12
【问题描述】:

我正在尝试制作一个“可执行”war 文件 (java -jar myWarFile.war),它将启动一个 Jetty 网络服务器,该服务器托管我执行的 WAR 文件中包含的 web 应用程序。

我发现 a page 描述了如何制作我正在寻找的东西:

但是,遵循该建议以及我认为我应该如何制作可执行 jar (war) 并不起作用。

我有一个 Ant 任务创建一个 WAR 文件,其清单如下:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.1
Created-By: 1.5.0_18-b02 (Sun Microsystems Inc.)
Main-Class: Start

WAR 文件的内容如下:

> Start.class
> jsp
>   build.jsp 
> META-INF  
>   MANIFEST.MF
> WEB-INF
>   lib
>     jetty-6.1.22.jar
>     jetty-util.6.1.22.jar

当我尝试执行 WAR 文件时,错误是:

Exception in thread "main" java.lang.NoClassDefFoundError: org/mortbay/jetty/Handler
Caused by: java.lang.ClassNotFoundException: org.mortbay.jetty.Handler
        at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
Could not find the main class: Start. Program will exit.

这里似乎有两个错误:一个似乎找不到 JAR 文件,另一个是找不到 Start 类。

为了修复第一个问题,我将 Jetty JAR 文件放在 WAR 文件的基础中并再次尝试——同样的错误。我还尝试将WEB-INF/lib/<specific-JAR-files> 添加到清单的Class-Path 属性中。那也没用。

有没有人知道我在做什么正确/错误以及如何让这个可执行的 WAR 文件启动并运行?

【问题讨论】:

  • 有一个.war文件吗?为什么不拥有一个 .jar 文件,其中包含 .war 文件、jetty 分发和一个以编程方式启动 jetty 并将您的 web 应用程序加载到上下文的主类。

标签: java executable jetty manifest war


【解决方案1】:

尽管这有点旧,但 Jetty 8 的另一个替代方案是简单地将 Jetty jar 作为依赖项包含在您的 pom 中,并在您的 pom 中添加以下内容(相对于解包 war 并重新打包它的 ant 脚本):

            <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>1.4</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <createDependencyReducedPom>true</createDependencyReducedPom>
                        <transformers>
                            <transformer
                                implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>JettyStandaloneMain</mainClass>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <!-- The main class needs to be in the root of the war in order to be 
            runnable -->
        <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <executions>
                <execution>
                    <id>move-main-class</id>
                    <phase>compile</phase>
                    <configuration>
                        <tasks>
                            <move todir="${project.build.directory}/${project.build.finalName}">
                                <fileset dir="${project.build.directory}/classes/">
                                    <include name="JettyStandaloneMain.class" />
                                </fileset>
                            </move>
                        </tasks>
                    </configuration>
                    <goals>
                        <goal>run</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

【讨论】:

    【解决方案2】:

    这是对 @RobHruska 答案的 Maven 的改编。它只是复制主类的文件,并将 Jetty JAR 文件合并到 WAR 文件中,没什么新鲜的,只是为了简化你的生活,如果你是 Maven 新手的话:

    <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
        <executions>
            <execution>
                <id>move-main-class</id>
                <phase>compile</phase>
                <configuration>
                    <tasks>
                        <copy todir="${project.build.directory}/${project.build.finalName}">
                            <fileset dir="${project.build.directory}/${project.build.finalName}/WEB-INF/classes/">
                                <include name="main/*.class" />
                            </fileset>
                        </copy>
    
                        <unjar dest="${project.build.directory}/${project.build.finalName}">
                            <!-- you'll have to locate these jars or appropriate versions; note that these include JSP support -->
                            <!-- you might find some of them in the downloaded Jetty .tgz -->
                            <fileset dir="${project.build.directory}/${project.build.finalName}/WEB-INF/lib/">
                                <include name="ant-1.6.5.jar"/>
                                <!--<include name="core-3.1.1.jar"/>-->
                                <include name="jetty*"/>
                                <include name="servlet-api*"/>
                            </fileset>
    
                            <patternset><!-- to exclude some of the stuff we don't really need -->
                                <exclude name="META-INF/**/*"/>
                                <exclude name="images/**/*"/>
                                <exclude name=".options"/>
                                <exclude name="about.html"/>
                                <exclude name="jdtCompilerAdapter.jar"/>
                                <exclude name="plugin*"/>
                            </patternset>
                        </unjar>
                    </tasks>
                </configuration>
                <goals>
                    <goal>run</goal>
                </goals>
            </execution>
        </executions>
    </plugin> 
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.2</version>
        <configuration>
            <archiveClasses>true</archiveClasses>
            <archive>
                <manifest>
                    <mainClass>main.Main</mainClass> 
                </manifest>
            </archive>
        </configuration>
    </plugin>
    

    【讨论】:

    • 如何让它执行?我通常使用mvn clean compile war:war 构建,但从未执行过 maven-antrun-plugin。
    【解决方案3】:

    我们已经通过使用 jetty-console-maven-plugin 解决了这个问题。

    每当您运行 mvn package 时,它​​都会创建另一个可与 java -jarwhateverpackage-runnable.war 一起使用的战争

            <plugin>
                <groupId>org.simplericity.jettyconsole</groupId>
                <artifactId>jetty-console-maven-plugin</artifactId>
                <version>1.45</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>createconsole</goal>
                        </goals>
                    </execution>
                </executions>
    
                <configuration>
                    <additionalDependencies>
                        <additionalDependency>
                            <artifactId>jetty-console-requestlog-plugin</artifactId>
                        </additionalDependency>
                        <additionalDependency>
                            <artifactId>jetty-console-gzip-plugin</artifactId>
                        </additionalDependency>
                        <additionalDependency>
                            <artifactId>jetty-console-ajp-plugin</artifactId>
                        </additionalDependency>
                        <additionalDependency>
                            <artifactId>jetty-console-startstop-plugin</artifactId>
                        </additionalDependency>
                    </additionalDependencies>
                </configuration>
            </plugin>
    

    它还会为您生成 init.d 脚本和所有内容!

    【讨论】:

    • 是否有任何关于其工作原理的文档?你如何声明启动 Jetty 的类?最后,这是否要求码头罐子成为战争的根源?
    • 它会为你做所有事情......你只需运行“mvn package”它就会为你生成一场战争。
    【解决方案4】:

    您在问题中的link 提供了您需要的大部分内容。但是,除此之外,还有一些事情需要做。

    Jetty 需要启动的任何类文件在打包时都需要位于 war 文件的根目录。在我们 &lt;war&gt; 文件之前,我们可以利用 Ant 为我们做这件事。战争的清单文件还需要一个Main-Class 属性来执行服务器。

    这里是一步一步的:

    创建您的 Jetty 服务器类:

    这是改编自您提供的链接。

    package com.mycompany.myapp;
    
    import java.io.File;
    import java.net.URL;
    import java.security.ProtectionDomain;
    
    import org.mortbay.jetty.Server;
    import org.mortbay.jetty.webapp.WebAppContext;
    
    public final class EmbeddedJettyServer
    {
        public static void main(String[] args) throws Exception
        {
            int port = Integer.parseInt(System.getProperty("port", "8080"));
            Server server = new Server(port);
    
            ProtectionDomain domain = EmbeddedJettyServer.class.getProtectionDomain();
            URL location = domain.getCodeSource().getLocation();
    
            WebAppContext webapp = new WebAppContext();
            webapp.setContextPath("/");
            webapp.setDescriptor(location.toExternalForm() + "/WEB-INF/web.xml");
            webapp.setServer(server);
            webapp.setWar(location.toExternalForm());
    
            // (Optional) Set the directory the war will extract to.
            // If not set, java.io.tmpdir will be used, which can cause problems
            // if the temp directory gets cleaned periodically.
            // Your build scripts should remove this directory between deployments
            webapp.setTempDirectory(new File("/path/to/webapp-directory"));
    
            server.setHandler(webapp);
            server.start();
            server.join();
        }
    }
    

    要查看您可以在此处配置的所有内容,请查看Jetty API documentation

    与 Ant 建立战争:

    这使用暂存目录将必要的类文件解压到战争的根目录中,以便在战争执行时可以访问它们。

    <target name="war" description="--> Creates self-executing war">
      <property name="staging.dir" location="${basedir}/staging"/>
      <property name="webapp.dir" location="${basedir}/src/webapp"/>
    
      <mkdir dir="${staging.dir}"/>
    
      <!-- assumes you have all of your war content (excluding classes and libraries) already structured in a directory called src/webapp -->
      <!-- e.g. -->
      <!-- src/webapp/index.html -->
      <!-- src/webapp/WEB-INF/web.xml -->
      <!-- src/webapp/WEB-INF/classes/my.properties -->
      <!-- etc ... -->
      <copy todir="${staging.dir}">
        <fileset dir="${webapp.dir}" includes="**/*"/>
      </copy>
    
      <unjar dest="${staging.dir}">
        <!-- you'll have to locate these jars or appropriate versions; note that these include JSP support -->
        <!-- you might find some of them in the downloaded Jetty .tgz -->
        <fileset dir="path/to/jetty/jars">
          <include name="ant-1.6.5.jar"/>
          <include name="core-3.1.1.jar"/>
          <include name="jetty-6.1.24.jar"/>
          <include name="jsp-2.1-glassfish-2.1.v20091210.jar"/><!-- your JSP implementation may vary -->
          <include name="jsp-api-2.1-glassfish-2.1.v20091210.jar"/><!-- your JSP implementation may vary -->
          <include name="servlet-api-2.5-20081211.jar"/><!-- your Servlet API implementation may vary -->
        </fileset>
        <patternset><!-- to exclude some of the stuff we don't really need -->
          <exclude name="META-INF/**/*"/>
          <exclude name="images/**/*"/>
          <exclude name=".options"/>
          <exclude name="about.html"/>
          <exclude name="jdtCompilerAdapter.jar"/>
          <exclude name="plugin*"/>
        </patternset>
      </unjar>
    
      <!-- copy in the class file built from the above EmbeddedJettyServer.java -->
      <copy todir="${staging.dir}">
        <fileset dir="path/to/classes/dir" includes="com/mycompany/myapp/EmbeddedJettyServer.class"/>
      </copy>
    
      <war destfile="myapp.war" webxml="${webapp.dir}/WEB-INF/web.xml">
        <fileset dir="${staging.dir}" includes="**/*"/>
        <classes dir="path/to/classes/dir"/><!-- your application classes -->
        <lib dir="path/to/lib/dir"/><!-- application dependency jars -->
        <manifest>
          <!-- add the Main-Class attribute that will execute our server class -->
          <attribute name="Main-Class" value="com.mycompany.myapp.EmbeddedJettyServer"/>
        </manifest>
      </war>
    
      <delete dir="${staging.dir}"/>
    </target>
    

    执行战争:

    如果上面的一切设置正确,您应该能够:

    java -jar myapp.war
    
    // or if you want to configure the port (since we are using the System property in the code)
    
    java -Dport=8443 -jar myapp.war
    

    【讨论】:

    • 非常非常小的注释:当从 jars(在 中)排除文件夹时,我们可以使用 **/META-INF/** 而不是 META-INF/**/* 来排除实际文件夹以及文件夹的内容.否则仍包含根文件夹。
    • 你为什么要unjaring 所有的依赖jar?它们被包含在 WEB-INF/lib 目录中
    • @RTF - 已经有一段时间了,但我相信这是因为这些类是可执行战争本身的依赖项(即码头、EmbeddableJettyServer 等),当你执行战争时它无法从嵌入的 jar(在其自身中)加载它们 - 它们必须是捆绑到战争中的类。
    【解决方案5】:

    我认为,通过“没有 maven”,您需要一个可以自行运行的 jar,而不是“mvn jetty:run”——并不是说您根本不想使用 maven。

    我花了很长时间才弄清楚这一点,因为我发现了很多选择——没有一个很简单。最终我找到了this maven plugin from simplericity。效果很好。

    【讨论】:

    • 谢谢,但是您知道如何配置它以添加上下文路径吗?
    【解决方案6】:

    这是我的示例 ANT 提取。这个想法是解包 Jetty 依赖项,然后像普通的 JAR 文件一样在本地包含它们:

    <!-- Hack: Java doesn't support jars within jars/wars -->
    <unjar src="${lib.dir}/container/jetty.jar" dest="${build.dir}/unjar"/>
    <unjar src="${lib.dir}/container/jetty-util.jar" dest="${build.dir}/unjar"/>
    <unjar src="${lib.dir}/container/servlet-api.jar" dest="${build.dir}/unjar"/>
    <unjar src="${lib.dir}/container/jsp-api.jar" dest="${build.dir}/unjar"/>
    
    <!-- Build war file as normal, just including the compiled and unjar'ed files -->
    <war destfile="${war.file}" webxml="${config.dir}/web.xml">
        <fileset dir="${build.dir}/classes"/>
        <fileset dir="${build.dir}/unjar"/>
        <fileset dir="${resources.dir}" excludes="*.swp"/>
        <lib dir="${lib.dir}/runtime"/>
        <manifest>
            <attribute name="Main-Class" value="Start"/>
        </manifest>
    </war>
    

    注意:

    WEB-INF/lib 目录用于 Web 应用程序依赖项。在这种情况下,我们正在打包 WAR 文件,以便它在启动时像正常的 Jetty JAR 文件一样工作

    【讨论】:

      【解决方案7】:

      Hudson 使用 Winstone servlet 容器解决了这个确切的问题,该容器直接支持这个用例。 http://winstone.sourceforge.net/#embedding

      也许这对你有用?

      【讨论】:

      • Jetty 最终对我能找到的零在线帮助感到困惑。试图让我的战争包含 winstone 也被证明是有问题的,但是一旦我将我的应用程序放入 winstone un-jar'd 源,然后重新将其重新 jar,它就可以了。
      • Kohsuke 已经厌倦了维护 Winstone,因此他用与 Winstone 兼容的 Jetty 包装器替换了它。 jenkins-ci.361315.n4.nabble.com/…
      【解决方案8】:

      我之前做过类似的事情,但你是否将应用程序启动为“java -jar xxx.war”?你只有 2 个罐子,我认为这还不够。还可以尝试使用 Jetty 7.0.0M1(这是最新版本)。当我添加 jetty-server 和 jetty-webapp 作为两个依赖项(它们来自 org.eclipse.jetty)时,我在 lib 目录中得到以下 jar。仅供参考,org.mortbay.jetty.Handler 在 jetty-server*.jar 中。

      • jetty-continuation-7.0.0.M1.jar
      • jetty-http-7.0.0.M1.jar
      • jetty-io-7.0.0.M1.jar
      • jetty-security-7.0.0.M1.jar
      • jetty-server-7.0.0.M1.jar
      • jetty-servlet-7.0.0.M1.jar
      • jetty-util-7.0.0.M1.jar
      • jetty-webapp-7.0.0.M1.jar
      • jetty-xml-7.0.0.M1.jar

      【讨论】:

        【解决方案9】:
        • 将 .jars 放在 .war 文件根目录中没有任何作用
        • 将 .jars 放入 WEB-INF/lib 并不能帮助 JVM 找到 Jetty 文件,甚至开始启动您的 .war。将它们放在那里“为时已晚”。
        • 将 .jar 放入清单 Class-Path 仅适用于外部 .jar 文件,不适用于 .jar 中包含的文件

        那该怎么办呢?

        • 使用构建脚本将您需要的所有 .jar 文件简单地合并到 .war 文件中。这需要一些额外的工作。这也有点难看,因为编译后的代码是 .war 中可服务文件的一部分
        • 使用“java -cp jetty.jar:... ...”将依赖的 .jars 添加到 JVM 的类路径中,尽管这种方式违背了独立 .war 的目的

        【讨论】:

        • @Sean 他没有将 jars 放在 .war 文件根目录中,他正在放置类文件,它完全有效(正如他的链接所指向的那样)
        • 他的原始帖子显示了 .war 中的 .jar 文件,位于 WEB-INF/lib 中。然后他提到将 .jars 放在 .war 的基础上。您的后续帖子显示 lib/ 目录中的 .jars,那么您是什么意思?我可能错了,因为我自己没有尝试过,但是在这种情况下 JVM 如何找到 Jetty 类?您提到的帖子显示了 Maven 一起构建依赖项,而我见过的 Maven 输出只是将 .class 文件放在一个 .jar/.war 中,这是我的第一个答案。
        猜你喜欢
        • 2017-07-30
        • 2013-10-28
        • 2015-07-30
        • 2016-01-18
        • 2015-12-07
        • 1970-01-01
        • 2016-01-10
        • 2018-03-20
        • 1970-01-01
        相关资源
        最近更新 更多