如果您嵌入像 Jetty 这样的 servlet 容器,这是可能的:
Embedded Jetty Executable War.
注意:可运行的战争并不常见。 (例如 Jenkins 就是这样做的——它允许用户决定是独立运行应用程序——也许是为了某些产品评估,不应该安装进一步的基础设施——或者将它部署在一个(共享的)servlet 容器上,该容器得到管理和监控)
解决方案:
以下步骤是必要的,可以使用标准 Maven 插件来实现:
- 编写一个 Main 类,用于启动 Jetty 服务器并添加 webapp 上下文
- 在准备包阶段,将 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>
- 解包所有依赖项,这是 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>
- 在清单中指定主类,以便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() 方法。它使用打包的战争本身作为位置。