【问题标题】:Using Maven to create "runnable WAR"使用 Maven 创建“可运行的 WAR”
【发布时间】:2015-03-15 13:49:48
【问题描述】:

好的,我正在尝试创建一个可使用 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”,我已经为标签尝试了各种不同的路径。

我不想使用 tomcat 或类似的东西来运行战争,我只想能够像这样运行它:

java -jar reportTool.war

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

【问题讨论】:

  • WAR 文件适用于旨在部署在容器(如 Tomcat 或 Jetty)中的 Web 应用程序,其设计生命周期与主要驱动方法不同。你到底想达到什么目的?
  • 在项目中,我创建了一个webservice,使用Jetty作为web服务器。这一切在 IDE 中运行良好,但是当我尝试构建 jar 文件时,我遇到了一个问题,即它不包含“META-INF”目录,Google 告诉我你需要用于此类事情的战争文件,所以这就是我正在尝试做的事情。

标签: java maven jetty


【解决方案1】:

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

注意:可运行的战争并不常见。 (例如 Jenkins 就是这样做的——它允许用户决定是独立运行应用程序——也许是为了某些产品评估,不应该安装进一步的基础设施——或者将它部署在一个(共享的)servlet 容器上,该容器得到管理和监控)

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

  1. 编写一个 Main 类,用于启动 Jetty 服务器并添加 webapp 上下文
  2. 在准备包阶段,将 Main 类和所有用于服务器的类从 ${project.build.directory}/classes/ 移动到目标 war 目录以完成 jar 布局(在 war 文件中,类是在“类”文件夹中,但在 jar 文件中,类位于根文件夹中)
<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 -jar 。(请注意:依赖项列表取决于你的码头服务器实施)
<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 执行。 (请注意:我还指定了战争名称。这个名称在以前的插件配置中用作“移动到目录”和“输出目录”的一部分)
<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 上下文(参见最后的代码 sn-p) - 如果配置正确,则可以使用 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() 方法。它使用打包的战争本身作为位置。

【讨论】:

  • 如果我在战争中依赖其他职业怎么办?不只是外部罐子?我已经全部安装好了,以便包含所有正确的 groupID,并指向主类,但是我现在得到“线程中的异常”“main”java.lang.NoClassDefFoundError:文件/CacheFileManager“
  • 缓存文件管理器是用于启动还是在您的 Web 应用程序中使用?如果它用于服务器启动,则必须将其移至根目录(参见步骤 2)。如果该类来自依赖项,请同时解包该依赖项(参见步骤 3) - 您可以检查 war 文件,所有必要的类都在那里。 webapp 所需的所有其他类和库将由 Jetty 从 WEB-INF/classes/* 和 WEB-INF/lib/* 加载
  • 例如,我的 war 文件如下所示: 在根目录下,我的服务器启动类 /de/...、Jetty 类 /org/eclipse/eclipse/jetty/* 和 servlet api 类/javax/servlet/*.在 WEB-INF/classes 下有我的 webapp 特定 servlet、rest 资源等,在 WEB-INF/lib 下有 webapp 特定库(Maven 依赖项),如 jersey、gson 和 commons-io。我希望这会有所帮助并说明清楚。
  • 这不起作用,我得到了Could not find or load main class 并且确实创建的战争没有可以找到它的classes 目录。
  • 你能不能再看一下第2步和第4步。启动 Jetty 服务器的主类必须复制到 classes/(第 2 步)并在清单中指定(第 4 步)
【解决方案2】:

我最近对此进行了一些研究,发现大多数方法存在缺陷(构建速度慢(阴影、组装、jar-with-dependencies))、过于冗长或不独立。

我发现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,也可以使用此插件。

【讨论】:

    【解决方案3】:

    我认为你使用&lt;mainClass&gt;是不对的

    发件人:http://maven.apache.org/shared/maven-archiver/examples/classpath.html

    如果要创建可执行的jar文件,需要配置 相应的 Maven 归档器。你需要告诉它哪个主类 利用。这是通过配置元素完成的。这是 配置为添加类路径并使用类的示例 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>
    

    使用上述配置生成的清单如下所示 这个:

    Manifest-Version: 1.0 Archiver-Version: Plexus Archiver Created-By: Apache Maven ${maven.version} 构建者:${user.name} Build-Jdk: ${java.version} 主类:fully.qualified.MainClass 类路径: plexus-utils-1.1.jar commons-lang-2.1.jar

    【讨论】:

      猜你喜欢
      • 2014-04-01
      • 2013-06-14
      • 1970-01-01
      • 1970-01-01
      • 2017-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-16
      相关资源
      最近更新 更多