こんにちわ、みけです。
諸事情があって、というよりは、
ちょっとやってみたいなと思ったので、
herokuでGrizzly2+JAX-RS(Jersey)を動かすアプリケーションを
書いています。
この組み合わせはよくあるというか、
簡単にコードを書くことができます。
public class Main { private final Logger LOG = LoggerFactory.getLogger(Main.class); private static final int DEFAULT_PORT = 8080; private final URI uri; private final boolean runOnHeroku; public static void main(String... args) { ExtendedOptional<String> env = ExtendedOptional.ofNullable(System.getenv("PORT")); env.ifPresent(p -> new Main(p).start()) .ifNotPresent(() -> new Main().start()); } public Main(String port) { this.uri = UriBuilder.fromUri("http://0.0.0.0").port(Integer.parseInt(port)).build(); this.runOnHeroku = true; } public Main() { this.uri = UriBuilder.fromUri("http://localhost").port(DEFAULT_PORT).build(); this.runOnHeroku = false; } public void start() { ResourceConfig config = new ResourceConfig().package("org.mikeneck.jaxrs.app"); HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(this.uri, config, false); try { httpServer.start(); String msg = String.format("Jersey and Grizzly application started with WADL" + " available at %sapplication.wadl", uri); LOG.info(msg); if (!runOnHeroku) { LOG.info("hit enter to stop server."); System.in.read(); } else { while (true) { System.in.read(); } } } catch (IOException e) { LOG.error("server stopped by an error", e); throw new ProcessingException(LocalizationMessages.FAILED_TO_START_SERVER(e.getMessage()), e); } finally { httpServer.shutdownNow(); } } }
ここで、使ってる
ExtendedOptional<T>
というのは
Optional<T>
を少し改造したクラスで、
Optional<T>
のメソッドifPresent(Consumer<T>)
の
戻り値をvoid
からExtendedOptional<T>
にして、
さらにメソッドifNotPresent(VoidConsumer)
を加えたものです。
ここに、次のようなリソースのクラスを付け加えます。
package org.mikeneck.jaxrs.app; import javax.ws.rs.*; import static javax.ws.rs.core.MediaType.TEXT_PLAIN; @Path("hello") public class Hello { @GET @Produces(TEXT_PLAIN) public String text(@QueryParam("q") @DefaultValue("world") String q) { return "hello, " + q; } }
これを、ローカルで走らせた時に、http://localhost:8080/hello
にアクセスすると、
hello, world
と返ってきます。
本題
このアプリケーションサーバーにスタティックなコンテンツを返す機能を
取り付けたいと思います。
アプリケーションをコンパイルしてjarにした後でも、
コンテンツを返すことができるように、
org.glassfish.grizzly.http.server.CLStaticHttpHandler
を使います。
修正したMain
クラスは次のとおり(一部)になります。
public void start() { ResourceConfig config = new ResourceConfig().package("org.mikeneck.jaxrs.app"); HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(this.uri, config, false); httpServer.getServerConfiguration().addHttpHandler( new CLStaticHttpHandler(getClass().getClassLoader(), "/public/")); try { httpServer.start(); String msg = String.format("Jersey and Grizzly application started with WADL" + " available at %sapplication.wadl", uri); LOG.info(msg); if (!runOnHeroku) { LOG.info("hit enter to stop server."); System.in.read(); } else { while (true) { System.in.read(); } } } catch (IOException e) { LOG.error("server stopped by an error", e); throw new ProcessingException(LocalizationMessages.FAILED_TO_START_SERVER(e.getMessage()), e); } finally { httpServer.shutdownNow(); } }
このプロジェクトは次のようなパスの構成になっています。
src ├main └resources ├public │ └index.html └logback.groovy
そしてこの変更のおかげで、
http://localhost:8080/
にアクセスすると、
public
の下にあるindex.html
が返ってくるようになります。
困ったこと
これは、これでよいのですが、
http://localhost:8080/hello
にアクセスすると、
404
が返ってきてしまうわけです。
原因
ソースコードを読みます。
org.glassfish.grizzly.http.server.CLStaticHttpHandler
は
152行目のprotected boolean handle(String resourcePath, Request request, Response response)
メソッドで、リクエストをさばきます。
173行目からのfor
ループでクラスローダーからリソースを探します。
for (int i = 0; i < docRootsLocal.length; i++) { String docRoot = docRootsLocal[i]; final String fullPath = docRoot + resourcePath; url = classLoader.getResource(fullPath); if (url == null) { url = classLoader.getResource(fullPath + "/index.html"); } if (url != null) { break; } }
(一部省略)
ここで見つかったurl
に対して、
ディレクトリーのチェック、プロトコルのチェックを行って、
リソースが存在するか確認した後、boolean
の変数found
にtrue
が設定されます。
この後の256行目では次のようになっています。
if (!found) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Resource not found {0}", resourcePath); } return false; }
なんと、org.glassfish.grizzly.http.server.CLStaticHttpHandler
では、
リソースが見つからなかった時にfalse
を返していた!
いや、いいんですけどね(´・ω・`)
次のhandler
が適切に処理してくれれば…
CLStaticHttpHandler#handle
をコールしているのが、
StaticHttpHandlerBase#service
ですので、
今度はこちらを読みます。
@Override public void service(final Request request, final Response response) throws Exception { final String uri = getRelativeURI(request); if (uri == null || !handle(uri, request, response)) { onMissingResource(request, response); } }
先ほどのCLStaticHttpHandler#handle
はfalse
を返していましたので、
onMissingResource
に進みます。
protected void onMissingResource(final Request request, final Response response) throws Exception { response.sendError(404); }
見事に404
返してますね(´・ω・`)
ということで、誰かどうすればよいのかエロイ人教えて
なお、次のようなのは断る。
- Servlet使う(いや、まあ、これでもいいんですけどね。楽だし)