Google App Engine で Spring Boot を動かす

注意点は 5 つです。

`spring-boot-legacy` を使う
---

AppEngine は Servlet 2.5 なので、Servlet 3.0 を前提とした Spring Boot は動きません。
ですので、2.5 に対応している `spring-boot-legacy` を使います。

```groovy
compile 'org.springframework.boot:spring-boot-legacy'
```

あとは通常通り `web` と `actuator` に依存します。

```groovy
compile 'org.springframework.boot:spring-boot-starter-web'
compile 'org.springframework.boot:spring-boot-starter-actuator'
```

web.xml で `SpringBootContextLoaderListener` を置く
---

[Spring 公式の Servlet 2.5 ガイド](http://docs.spring.io/spring-boot/docs/current/reference/html/howto-traditional-deployment.html#howto-servlet-2-5) 通りです。ただし、後述の 2 点は変更する必要があります。

(`contextConfigLocation` は自身の Application クラスと置き換えてください。)

```xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.kaiinui.appenginetest.Application</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.boot.legacy.context.web.SpringBootContextLoaderListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextAttribute</param-name>
            <param-value>org.springframework.web.context.WebApplicationContext.ROOT</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
```

`metricFilter` では filter しないようにする
---

[Spring の公式ガイド](http://docs.spring.io/spring-boot/docs/current/reference/html/howto-traditional-deployment.html#howto-servlet-2-5) では DelegatingFilterProxy で filter するようにしていますが、次の項と同じ理由で動かなくなるので外します。

先ほど記載した web.xml 通りになっていれば問題有りません。

`DispatcherServlet` の `publishEvents` を `false` にしておく
---

Spring 4.1 以降では `DispatcherServlet` の `publishEvents` によって `HttpServletResponse` の `getStatus` が呼ばれますが、この API は 2.5 では実装されていないのでエラーが起こります。

以下の init-param を `DispatcherServlet` に追加して無効にします。

```xml
<init-param>
    <param-name>publishEvents</param-name>
    <param-value>false</param-value>
</init-param>
```

#### 以上、ちゃんと動く web.xml

```xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.kaiinui.appenginetest.Application</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.boot.legacy.context.web.SpringBootContextLoaderListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextAttribute</param-name>
            <param-value>org.springframework.web.context.WebApplicationContext.ROOT</param-value>
        </init-param>
        <init-param>
            <param-name>publishEvents</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
```

`multipartResolver` に `gmultipart` を使う
---

標準の `CommonsMultipartResolver ` が一時保存先としてファイルシステムを使ってしまうので、置き換えます。

メモリに全て読んでくれれば何でも良いのですが、イイ感じに実装してくれてるっぽい [gmultipart](https://code.google.com/p/gmultipart/) を使います。

```groovy
repositories {
    maven {
        url 'http://gmultipart.googlecode.com/svn/repo/m2'
    }
}

dependencies {
    compile 'gmultipart:gmultipart:0.4'
}
```

```java
// @Configuration なクラスで

@Bean(name = "multipartResolver")
public GMultipartResolver multipartResolver() {
    GMultipartResolver resolver = new GMultipartResolver();
    resolver.setMaxUploadSize(100000);

    return resolver;
}
```

まとめ
---

Spring MVC (+ Spring Boot) はいろいろ充実してるし、AppEngine にも載りやすいのでイイ感じかなーと。

Java の Web フレームワークでは Play Framework の人気が高いですが、載せるのに魔改造が必要なのであまりオススメできません…

References
---

- http://docs.spring.io/spring-boot/docs/current/reference/html/howto-traditional-deployment.html#howto-servlet-2-5
- http://stackoverflow.com/questions/1302072/how-can-i-get-the-http-status-code-out-of-a-servletresponse-in-a-servletfilter