使用Maven创建“可运行的WAR”

时间:2015-03-15 13:49:49

标签: java maven jetty

好的,我正在尝试创建一个可以使用Maven从命令行运行的war文件。

<plugin>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.6</version>
    <configuration>
        <warName>${project.artifactId}-${project.version}</warName>
        <warSourceDirectory>src\main\java\META-INF\webapp\WEB-INF</warSourceDirectory>
        <webXml>src\main\java\META-INF\webapp\WEB-INF\web.xml</webXml>
        <archive>
            <manifest>
                <mainClass>classes\ReportToolRunner</mainClass>
                <addClasspath>true</addClasspath>
            </manifest>
        </archive>
    </configuration>
    <executions>
        <execution>
            <id>default-war</id>
            <phase>package</phase>
            <goals>
                <goal>war</goal>
            </goals>
        </execution>
    </executions>
</plugin>

当我运行已编译的war文件时,我得到“错误:无法找到或加载主类类\ ReportToolRunner”,我已经为&lt; \ mainClass&gt;尝试了各种不同的路径。标签

我不想使用tomcat或类似的东西来运行战争,我只是希望能够像以下一样运行它:

java -jar reportTool.war

我正在使用Jetty作为我的网络服务器。

3 个答案:

答案 0 :(得分:4)

如果您嵌入像Jetty这样的servlet容器,那么这是可能的: Embedded Jetty Executable War

注意:可运行的战争并不常见。 (例如Jenkins这样做 - 它允许用户决定是否运行standealone应用程序 - 可能用于某些产品评估,不应安装其他基础结构 - 或者将其部署在(共享)servlet容器上,该容器将被管理并且监测)

<强>解决方案: 以下步骤是必要的,可以使用标准Maven插件实现:

  1. 编写一个Main类,启动Jetty服务器并添加webapp上下文
  2. 在prepare-package阶段,将Main类和用于服务器的所有类从$ {project.build.directory} / classes /启动到目标war目录以实现jar布局(在war文件中,类是在&#34; classes&#34;文件夹中,但在jar文件中,这些类位于根文件夹中)
  3. <plugin>
      <artifactId>maven-antrun-plugin</artifactId>
      <executions>
        <execution>
          <id>classes-copy</id>
          <phase>prepare-package</phase>
          <configuration>
            <tasks>
              <move todir="${project.build.directory}/${project.artifactId}-${project.version}/">
                <fileset dir="${project.build.directory}/classes/">
                  <include name="your.package.Main.class" />
                </fileset>
              </move>
            </tasks>
          </configuration>
          <goals>
            <goal>run</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
    
    1. 解包Jetty服务器启动所必需的所有依赖项,因此这些类是可执行文件的一部分。 (你可以跳过这个,但是当战争以jar形式执行时,这些依赖关系必须在类路径上可用:java -cp&lt; your classpath&gt; -jar&lt; your war&gt ;.(请注意:依赖列表,取决于在您的Jetty服务器实现上)
    2. <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>2.3</version>
        <executions>
          <execution>
            <id>jetty-classpath</id>
            <phase>prepare-package</phase>
            <goals>
              <goal>unpack-dependencies</goal>
            </goals>
            <configuration>
              <includeGroupIds>org.eclipse.jetty,javax.servlet</includeGroupIds>
              <outputDirectory>
                ${project.build.directory}/${project.artifactId}-${project.version}
              </outputDirectory>
            </configuration>
          </execution>
        </executions>
      </plugin>
      
      1. 指定清单中的主类,以便war文件可以作为jar执行。 (请注意:我还指定了战争名称。此名称在之前的插件配置中用作&#34;移动todir&#34;以及&#34; outputDirectory&#34;)
      2. <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>2.1.1</version>
          <configuration>
            <warName>${project.artifactId}-${project.version}</warName>
            <archive>
              <manifest>
                <mainClass>your.package.Main</mainClass>
              </manifest>
            </archive>
          </configuration>
          <executions>
            <execution>
              <id>default-war</id>
              <phase>package</phase>
              <goals>
                <goal>war</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
        

        这是我使用的服务器代码(Jetty版本8.1.0.v20120127)。它配置一个新的Jetty服务器并添加一个webapp上下文(参见最后的代码片段) - 如果配置正确,可以使用server.start()/ server.stop()启动和停止服务器:

        // Create connector
        SocketConnector connector = new SocketConnector();
        connector.setMaxIdleTime(1000 * 60 * 60);
        connector.setSoLingerTime(-1);
        connector.setPort(8080);
        
        // Create handler collection
        ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection();
        HandlerCollection handlerCollection = new HandlerCollection();
        handlerCollection.setHandlers(new Handler[] { contextHandlerCollection });
        
        // Add webapp context
        context.setServer(server);
        contextHandlerCollection.addHandler(context);
        
        server.setConnectors(new Connector[] { connector });
        server.setHandler(handlerCollection);
        

        最后是webapp上下文代码:

        public class ServerContextImpl extends WebAppContext {
        
          private static final Logger LOGGER = Logger.getLogger(ServerContextImpl.class);
        
          protected static final String[] JETTY_PLUS_CONFIGURATION_CLASSES;
        
          static {
            JETTY_PLUS_CONFIGURATION_CLASSES = new String[7];
            JETTY_PLUS_CONFIGURATION_CLASSES[0] = "org.eclipse.jetty.webapp.WebInfConfiguration";
            JETTY_PLUS_CONFIGURATION_CLASSES[1] = "org.eclipse.jetty.webapp.WebXmlConfiguration";
            JETTY_PLUS_CONFIGURATION_CLASSES[2] = "org.eclipse.jetty.webapp.MetaInfConfiguration";
            JETTY_PLUS_CONFIGURATION_CLASSES[3] = "org.eclipse.jetty.webapp.FragmentConfiguration";
            JETTY_PLUS_CONFIGURATION_CLASSES[4] = "org.eclipse.jetty.plus.webapp.EnvConfiguration";
            JETTY_PLUS_CONFIGURATION_CLASSES[5] = "org.eclipse.jetty.plus.webapp.PlusConfiguration";
            JETTY_PLUS_CONFIGURATION_CLASSES[6] = "org.eclipse.jetty.webapp.JettyWebXmlConfiguration";
          }
        
          ServerContextImpl() {
            setConfigurationClasses(JETTY_PLUS_CONFIGURATION_CLASSES);
            setContextPath("/");
            setWar(getWarLocation());
          }
        
          /**
           * Returns the location of the war (a trick, which is necessary for executable
           * wars please see: <a target="_blank" href=
           * "http://uguptablog.blogspot.de/2012/09/embedded-jetty-executable-war-with.html"
           * >Embedded Jetty with executable WAR</a>).
           * 
           * @return The war location.
           */
          protected String getWarLocation() {
            ProtectionDomain protectionDomain = ServerImpl.class.getProtectionDomain();
            URL location = protectionDomain.getCodeSource().getLocation();
            return location.toExternalForm();
          }
        }
        

        请注意getWarLocation()方法。它使用打包的战争本身作为位置。

答案 1 :(得分:3)

我最近对此进行了一些研究,并发现了大多数方法的缺陷(要么减慢构建(阴影,装配,依赖jar),要么过于冗长,要么不是自包含。

我发现这样做的好方法是spring-boot-maven-plugin,它提供了最好的结果(快速构建,自包含,简单的设置)。就这么简单:

<packaging>war</packaging>

<build>
  <outputDirectory>${basedir}/src/main/webapp/WEB-INF/classes</outputDirectory>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <version>1.4.3.RELEASE</version>
      <executions>
        <execution>
          <goals>
            <goal>repackage</goal>
          </goals>
        </execution>
      </executions>
      <configuration>
        <mainClass>com.MyMainClass</mainClass>
      </configuration>
    </plugin>
  </plugins>
</build>

通过一些简单的调整,它甚至可以构建一个可以部署和执行的战争。

即使您的项目不使用Spring Boot,也可以使用此插件。

答案 2 :(得分:1)

我认为您使用<mainClass>

并不正确

来自:http://maven.apache.org/shared/maven-archiver/examples/classpath.html

  

如果要创建可执行jar文件,则需要进行配置   相应的Maven Archiver。你需要告诉它哪个主类   使用。这是通过配置元素完成的。这是   一个示例pom.xml,配置为添加类路径并使用该类   fully.qualified.MainClass作为主类:

 <project>   ...   <build>
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-jar-plugin</artifactId>
         ...
         <configuration>
           <archive>
             <manifest>
               <addClasspath>true</addClasspath>
               <mainClass>fully.qualified.MainClass</mainClass>
             </manifest>
           </archive>
         </configuration>
         ...
       </plugin>
     </plugins>   </build>   ...   <dependencies>
     <dependency>
       <groupId>commons-lang</groupId>
       <artifactId>commons-lang</artifactId>
       <version>2.1</version>
     </dependency>
     <dependency>
       <groupId>org.codehaus.plexus</groupId>
       <artifactId>plexus-utils</artifactId>
       <version>1.1</version>
     </dependency>   </dependencies>   ... </project>
  

使用上述配置生成的清单看起来像   这样:

     

清单 - 版本:1.0 Archiver-版本:Plexus Archiver创建者:   Apache Maven $ {maven.version}内置:$ {user.name} Build-Jdk:   $ {java.version} Main-Class:fully.qualified.MainClass Class-Path:   plexus-utils-1.1.jar commons-lang-2.1.jar