GradleでJavaServlet 3.1とJSF2.2を使ったEclipseプロジェクトを作成する

# 目的

Gradleを使って、JavaServlet 3.1とJSFの組み合わせによるWebアプリの作成に関するノウハウが、ググる先生に聞いても、なかなか出てこないので、断片的な情報をまとめる。
 
## 何故、Tomcatなのか？

政治的な理由…
というよりも、Jettyを使うケースは、おそらくTomcatを使うよりも楽なはずなので、Tomcatを選んだ。
といっても、gradleのビルドスクリプトを一部変えるだけで動くはず

## 何故、Eclipseなのか？

これも政治的な理由。
まぁ、Javaを使った開発のプロジェクトって、大抵がEclipseだから、個人的に使うとかでないと、IntelliJ IDEAとか使えないんだよね〜
ということで、Eclipse。
でも、実際のところ、Eclipseに特化した部分は、1箇所しかないので大丈夫。

## 何故、JSFなのか？

今時、JavaでHTMLを吐き出すなんてそうはやらないでしょ？
JAX-RSとかでREST API作って、HTMLはJavaScript（altJS系?）とかで、完全に分業するでしょ？作業者が別かは別として…
でないと、クライアントがネイティブアプリの場合にダメでしょ？

というのはおいておいて…

新人君にいきなりJAX-RSでAPI作って、HTMLとかJavaScript書いて…って、流石に酷なので、新人研修でも使えるように、JSFを使って、ソースコードは一緒になろうとも（プロジェクト分けれ一緒にはならないはず）、サクッっと書ける方がいいはず。
少なくとも、JavaScriptを覚えてもらうという部分を分けられる。

# 前提

以下のアプリがインストールされている環境(バージョンは最新のものを使えばいいはず)

- JDK 8
- Eclipse 4.5
- Gradle 2.9

# Gradleプロジェクトの作成

プロジェクト（化したい）のルートディレクトリに入って、以下を実行する。
```
gradle init
```

# build.gradleの編集

<code>// Apply the java plugin to add support for Java</code>の前の<code>/*</code>と、最終行の<code>*/</code>を消す。

## Servlet APIとJSFを依存関係に追加

dependenciesに、以下を加える。

```
    compile 'org.apache.myfaces.core:myfaces-api:2.2.9'
    compile 'org.apache.myfaces.core:myfaces-impl:2.2.9'
    compile 'javax.servlet:jstl:1.2'
    
    compile 'org.primefaces:primefaces:5.3'

    compile 'javax.servlet:javax.servlet-api:3.1.0'

    def tomcatVersion = '8.0.30'
    tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",
           "org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}"
    tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}") {
        exclude group: 'org.eclipse.jdt.core.compiler', module: 'ecj'
    }
```
## Tomcatプラグインの設定を行う

[gradle-tomcat-pluginの公式サイト](https://github.com/bmuschko/gradle-tomcat-plugin)の説明に従い、build.gradleへ、buildscriptやらgradle pluginの設定やら、dependenciesの追加などを行う。

# Eclipse関連のお膳立て

Eclipseでなければ、それぞれの環境に合わせてやったりやらなかったりしてください。

## build.gradleにEclipseのための設定を追記する

※ {projectName}は、各自のプロジェクト名に読み替えてください

```
apply plugin: 'eclipse-wtp'

eclipse {

    wtp {
        facet {
            facet name: 'java', version: '1.8'
            facet name: 'jst.web', version: '7.0'
        }

        component {
            contextPath  = '{projectName}'
        }
    }
}
```

## Eclipseプロジェクト化

以下を実行する。

```
gradle eclipse
```

## Eclipseへのインポート

（普通にインポート出来るのでインポートします）

## サーブレットのための諸々を追加する

## ソースディレクトリの追加

### src/main配下にjavaディレクトリとWEB-INFディレクトリを追加する

```
mkdir -p src/main/java
mkdir -p src/main/webapp/WEB-INF
```

### Eclipseで src/main/javaをsource directoryに追加する

# web.xmlの追加

src/main/webapp/WEB-INF配下に以下の内容のweb.xmlを配置する

```
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <!-- JSF mapping -->
    <listener>
        <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
    </listener>

    <context-param>
        <description>Defines which packages to scan for beans, separated by commas. Necessary for when using tomcat7:run.</description>
        <param-name>org.apache.myfaces.annotation.SCAN_PACKAGES</param-name>
        <!-- exampleは、各自のパッケージに置き換えてください -->
        <param-value>example</param-value>
    </context-param>

    <context-param>
        <param-name>javax.faces.FACELETS_REFRESH_PERIOD</param-name>
        <param-value>0</param-value>
    </context-param>

    <context-param>
        <description>
            Set the project stage to "Development", "UnitTest", "SystemTest", or "Production".
            An optional parameter that makes troubleshooting errors much easier.
            You should remove this context parameter before deploying to production!
        </description>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>

    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- Map these files with JSF -->
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.jsf</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.faces</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>
</web-app>
```

# faces-config.xmlの追加

src/main/webapp/WEB-INF配下に以下の内容のfaces-config.xmlを配置する

```
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
              version="2.2">

</faces-config>
```

# Managed Beanの追加

任意のパッケージに、任意のクラス名で以下のようなサーブレットを追加します。
ここでは、とりあえず、example.IndexBeanとしています。

```
package example;


import java.io.Serializable;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

@ViewScoped
@ManagedBean
public class IndexBean implements Serializable {

	private static final long serialVersionUID = 1L;

	private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String doAction() {
        System.out.println("message at MessageAction#doAction = " + message);
        return null;
    }
}
```

# index.xhtmlの追加

src/main/webapp配下に以下の内容のindex.xhtmlを配置する

```
<!DOCTYPE html>
<html template="/WEB-INF/templates/layout.xhtml"
                xmlns="http://www.w3.org/1999/xhtml"
                xmlns:f="http://xmlns.jcp.org/jsf/core"
                xmlns:h="http://xmlns.jcp.org/jsf/html"
                xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
                xmlns:p="http://primefaces.org/ui">
<h:head>
  <title>Top Menu</title>
</h:head>
<h:body>
  <h:form>
    <p><h:inputText value="#{indexBean.message}" p:placeholder="Enter text"/></p>
    <p><h:outputText value="#{indexBean.message}" p:placeholder="Enter text"/></p>
  </h:form>
</h:body>
</html>
```

# 実行

```
gradle tomcatRun
```

すると、

```
*******************************************************************
*** WARNING: Apache MyFaces-2 is running in DEVELOPMENT mode.   ***
***                                         ^^^^^^^^^^^         ***
*** Do NOT deploy to your live server(s) without changing this. ***
*** See Application#getProjectStage() for more information.     ***
*******************************************************************

Started Tomcat Server
The Server is running at http://localhost:8080/{projectName}
> Building 75% > :tomcatRun
```

で停止します。
この時点で、Tomcatが8080ポートで起動しているため、<code>http://localhost:8080/{projectName}/hello</code>にブラウザーでアクセスすると、ブラウザー上には何も表示されませんが、

```
*******************************************************************
*** WARNING: Apache MyFaces-2 is running in DEVELOPMENT mode.   ***
***                                         ^^^^^^^^^^^         ***
*** Do NOT deploy to your live server(s) without changing this. ***
*** See Application#getProjectStage() for more information.     ***
*******************************************************************

Started Tomcat Server
The Server is running at http://localhost:8080/{projectName}
HelloServlet.doGet()
> Building 75% > :tomcatRun
```

と出力されます。

また、<code>http://localhost:8080/{projectName}/index.xhtml</code>にブラウザーでアクセスすると、index.xhtmlが表示されるため、テキストボックスに適当に入力してEnterキーを押すと、入力した内容がテキストボックスの下に出力されます。

## Tomcatの終了

Ctrl+Cで終わらせてあげてください（他に方法があるのかもしれませんが…）

# あとがき

Servlet 3.1とJSF2.2とか、組み合わせが微妙っぽいですね。
JSF2.2（というか、JSFを使う場合全般？）は、Servlet 2.5とか、旧いバージョンのほうが、web.xmlなどがすっきり書けるようです。

しかし、myfaces以外のJSFの実装が、Tomcat8とServlet API3.1では動かないとは…
Jettyなら動きそうなのに。
やっぱり、Tomcatは時代遅れなのかな？（普通に仕事とかだとJetty使ってるし）