実行可能warをmavenで作る場合、
warパッケージの直下にmainファイルを配備する必要がある。
しかし、warアプリケーションをpackageすると、
WEB-INF/classes以下にmainファイルも配備されてしまう。
そのため、maven3では、
antrunプラグインを用いて、mainファイルを移動する必要がある。
pom.xmlはこんな感じかな。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>move-main</id>
<phase>prepare-package</phase>
<configuration>
<tasks>
<move todir="target/${project.build.finalName}">
<fileset dir="target/classes">
<include name="app/AppMain.class" />
</fileset>
</move>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
これにより、warファイル直下に実行可能クラスが配備される。
また、jettyなどのembedを組み込む場合、
一緒に依存関係も持って行ってやる必要がある。
こんな感じ。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>jetty-classpath</id>
<phase>prepare-package</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeGroupIds>org.eclipse.jetty,javax.servlet,javax.websocket</includeGroupIds>
<excludes>META-INF/ECLIPSEF.*</excludes>
<outputDirectory>${project.build.directory}/${project.build.finalName}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
そして、mainファイルはこんな感じ。
package app;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
import java.net.URL;
public class AppMain {
public static void main(String[] args) throws Exception {
// 初期設定
int port = 8080;
System.setProperty("prefix", "/");
// サーバ生成
Server server = new Server(port);
// warを読み込むコンテクスト生成
URL warUrl = AppMain.class.getProtectionDomain().getCodeSource().getLocation();
String warLocation = warUrl.toExternalForm();
WebAppContext context = new WebAppContext();
context.setWar(warLocation);
context.setContextPath("/");
// コンテクストにサーバをセットする
// ※重要
context.setServer(server);
// websocketを使えるようにする
WebSocketServerContainerInitializer.configureContext(context);
// 開始
server.setHandler(context);
server.start();
server.join();
}
}
上記が非常に重要。
これで、websocketのendpointをServletContextListener側で指定してやるとうまくいく。
package app;
import websocket.WebsocketIndex;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.websocket.DeploymentException;
import javax.websocket.server.ServerContainer;
public class AppInitializeListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// embed用のwebsocketの追加
try {
ServerContainer wsContainer = (ServerContainer) sce.getServletContext().getAttribute(ServerContainer.class.getName());
System.out.println(String.format("wsContainer => %s", wsContainer));
if (wsContainer != null) {
wsContainer.addEndpoint(WebsocketIndex.class);
}
} catch (DeploymentException e) {
e.printStackTrace();
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
実行可能jarでwebsocketを作成する場合、
mainクラス内でendpointを設定してやればよいのだが、
実行可能warの場合、
mainクラスが実際のwebsocketのendpointのクラスの配備位置が異なるため、
クラスのロードが出来ない状態となる。
そのため、上記のような方法で、動的に指定してやることで、
実行可能warにおいて、websocketが利用できる。
【参考】
・実行可能warの生成の詳細がかかれています。
http://qiita.com/k_ui/items/1d3bbbd7993c4c9adf71
・実行可能jarでのwebsocketの設定方法が書かれています。
https://github.com/jetty-project/embedded-jetty-websocket-examples/blob/master/javax.websocket-example/src/main/java/org/eclipse/jetty/demo/EventServer.java
以上