実行可能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の設定方法が書かれています。
以上
コメントがあればどうぞ