Google App Engine standard Java8上でScalaアプリケーションを稼働させる

つい先日、Google App Engine standardにJava8サポートのニュースがGoogleから発表されました（まだBetaです）。

https://cloudplatform.googleblog.com/2017/06/Google-App-Engine-standard-now-supports-Java-8.html

というわけで、Scala2.12でWebアプリケーションをデプロイしてみました。

# 制限
GAE standardはGAE flexibleのようにjetty環境もあればjdkだけの環境もある、というのとは異なり、jettyのみとなっています。

[Java8 User Guide GAE standard](https://docs.google.com/document/d/1Jd3wjffXF6D5ZKh3VMwT2YRP0Msil8jE7FhubRF734k/edit?ts=595274a0#) のFAQにもありますが、

> - Can I use SpringBoot fat jars?
>     - No, fat Jars are not War archives, and GAE only accepts War structure.

というわけで、akka-httpを稼働させるのは厳しそう。
※ちなみに以前、 [Akka-http on Google App Engine flexible environment](http://qiita.com/mtoyoshi/items/7d6ccfa8b2cbf04e73df)　という記事も書きましたのでakka-httpを使いたい場合は読んでみて下さい。

今回は[skinny-micro](http://skinny-framework.org/documentation/micro.html)を利用してアプリケーションを作ってみました。

あと、[GAE standard Java8のサンプル](https://github.com/GoogleCloudPlatform/getting-started-java/tree/master/appengine-standard-java8)のようにmvnやgradleではなくsbtからデプロイしたかったので、sbt-appengineを利用しました。

# フォルダ構成

```
myfirstapp
├── appengine.sbt
├── build.sbt
├── project
│   ├── build.properties
│   ├── plugins.sbt
│   ├── project
│   └── target
└── src
    └── main
        ├── scala
        │   └── app.scala
        └── webapp
            └── WEB-INF
                ├── appengine-web.xml
                └── web.xml
```

## build.sbt

```scala
lazy val skinnyMicroVersion = "1.2.+"

lazy val root = (project in file("."))
  .settings(
    inThisBuild(List(
      organization := "com.example",
      scalaVersion := "2.12.2",
      version      := "0.1.0-SNAPSHOT"
    )),
    name := "MyFirstApp",
    libraryDependencies ++= Seq(
      "org.skinny-framework" %% "skinny-micro"         % skinnyMicroVersion,
      "ch.qos.logback"       %  "logback-classic"      % "1.2.3",
      "javax.servlet"        %  "javax.servlet-api"    % "3.1.0"
    )
  )
```

## sbt-appengine関連

```scala:project/plugins.sbt
addSbtPlugin("com.eed3si9n" % "sbt-appengine" % "0.7.0")
```

```scala:appengine.sbt
libraryDependencies += "org.mortbay.jetty" % "jetty" % "6.1.22" % "container"

enablePlugins(AppenginePlugin)
```

## Scalaプログラム

```scala:src/main/scala/app.scala
import javax.servlet._
import skinny.micro._

object Hello extends WebApp {
  get("/say-hello") {
    s"Hello, ${params.getOrElse("message", "Anonymous")}!\n"
  }
}

class Bootstrap extends LifeCycle {
  override def init(ctx: ServletContext) {
    Hello.mount(ctx)
  }
}
```

~~※ skinny-microのサンプルにあったようにエンドポイントを `/say-hello` にするとローカルでは動作したもののGAE上ではうまく動作しなかった。そこの深掘りはしてない。~~
※ 無事動作しました（`sbt clean` してみたりbuild.sbtに `logback` 追加したりで・・・）。 

## xml関連

```xml:src/main/webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app 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_3_0.xsd"
         version="3.0">
    <listener>
        <listener-class>skinny.micro.SkinnyListener</listener-class>
    </listener>
</web-app>
```

```xml:src/main/webapp/WEB-INF/appengine-web.xml
<?xml version="1.0" encoding="utf-8"?>
<!--
  Copyright 2017 Google Inc.
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
        http://www.apache.org/licenses/LICENSE-2.0
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<!-- [START config] -->
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
  <application>自分のgcloud-app-idを指定</application>
  <version>GAE上で管理するバージョン番号（たとえばalpha-001とか）</version>
  <runtime>java8</runtime>
  <threadsafe>true</threadsafe>
</appengine-web-app>
  <!-- [END config] -->
```

# sbt-appengineを使う準備
以下からSDKをダウンロードしておく。
https://cloud.google.com/appengine/docs/standard/java/download#java8

解答したフォルダをAPPENGINE_SDK_HOMEという名前で環境変数に登録する。
例えば以下のような感じで。
`export APPENGINE_SDK_HOME=/Applications/appengine-java-sdk-1.9.54`

# デプロイ

```
$ sbt
> appengineDeploy
```

しばらく待てば、GCP ConsoleのGAEのところに上で設定したバージョン番号で表示されます。

あとはブラウザで `https://【自分のgcloud-app-id】.appspot.com/` でhello, Anonymousと表示されるはずです。