動いた！TomcatでJSF+CDI (環境設定編)

# はじめに
もっと気軽にEEを実装してみたい！　試してみたい！　
ということで、Tomcatで、JSF + CDI を動作させてみたいと思います。
Tomcatでは通常はJSFとCDIは動きませんが、Tomcat9はCDIをサポートしているようなので試してみます。

動作確認用のサンプルアプリは、ログイン画面と、成功時に遷移するトップ画面を作ります。
ざっくり、こんな感じ。
<img width="560px" alt="サンプルアプリ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701083/4eb34ad0-90b0-ea2d-8b2b-4afedca5bff8.png">
環境は以下のとおりです。

* mac os
* java13
* Eclipse for Enterprise Java Developers (2020-09)
* Tomcat 9.0.39

# 実装の前に必要なこと
CDIを利用するには、`Apache OpenWebBeans`
JSFを利用するには、`Apache MyFaces`が必要です。

以下のような手順で環境設定していきます。　
1. Apache OpenWebBeansのzipをダウンロードして解凍
2. Apache MyFacesのzipをダウンロード（解凍はしない）
3. １で解凍したフォルダ内でシェルを実行
4. web.xmlの設定

## 1. Apache OpenWebBeansのダウンロード
Apache OpenWebBeansのページから[ダウンロード](https://openwebbeans.apache.org/download.html)します。
今回は、`openwebbeans-distribution-2.0.18-binary.zip`をダウンロードしました。
ダウンロード後、解凍します。

## 2. Apache MyFacesのダウンロード
Apache MyFacesのページから[ダウンロード](http://myfaces.apache.org/#/core23next)します。
今回は、`myfaces-core-assembly-2.3-next-M4-bin.zip`をダウンロードしました。
このzipは解凍不要です。このまま利用します。

## 3.シェルの実行
OpenWebBeansを解凍したフォルダ内に`install_owb_tomcat7_myfaces.sh`があるので実行します。
引数は、ダウンロードしたmyfacesのzipファイルとTomcatのパスです。

```sh
./install_owb_tomcat7_myfaces.sh /ダウンロードパス/myface-core-assembly-2.3-next-M4-bin.zip tomcatのパス
```

実行すると必要なjarファイルが`tomcat/lib`にコピーされ、context.xmlにListenerが追加されます。

## 4. web.xmlの設定
CDIとJSFを有効にするためListenerを設定します。それとServletのMappingを設定します。

```xml:web.xml
<listener>
    <listener-class>org.apache.webbeans.servlet.WebBeansConfigurationListener</listener-class>
</listener>
<listener>
    <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
</listener>

<servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
</servlet-mapping>

<welcome-file-list>
    <welcome-file>index.xhtml</welcome-file>
</welcome-file-list>
```

これで準備完了です。TomcatでCDI/JSFが動くようになったはずです！

# 実装
動作確認のため、サンプルアプリを実装してみます。まずは、mavenの設定です。

## mavenの設定

```xml:pom.xml 
<!-- Servlet -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.0</version>
    <scope>provided</scope>
</dependency>

<!-- JSF -->
<dependency>
    <groupId>org.apache.myfaces.core</groupId>
    <artifactId>myfaces-api</artifactId>
    <version>2.3-next-M4</version>
    <scope>provided</scope>
</dependency>

<!-- CDI -->    
<dependency>
    <groupId>javax.enterprise</groupId>
    <artifactId>cdi-api</artifactId>
    <version>2.0</version>
    <scope>provided</scope>
</dependency>

<!-- Lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
</dependency>
```

CDIやJSFのコンパイルを通すためにprovidedで追加します。動作時にはtomcat/libにあるので
不要です。
ついでにlombokも導入しました。（これは必須ではありません）

## 画面の作成
JSFでは、画面(xhtml)と、画面に対応するBackingBeanを作成します。
まずは画面を作成します。デザインは無視して簡潔に実装します。

```html:index.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" 
	xmlns:ui="http://java.sun.com/jsf/facelets" 
	xmlns:h="http://java.sun.com/jsf/html" 
	xmlns:ph="http://xmlns.jcp.org/jsf/passthrough"
	xmlns:f="http://java.sun.com/jsf/core">

<h:head>
    <title>JSFサンプル</title>
</h:head>

<body>

    <h:messages escape="true" id="messages" showDetail="false" closable="true"/>
    <h:form id="form">
        <br/>
        <h:inputText value="#{index.loginId}" ph:placeholder="Username" /><br/>
        <h:inputSecret value="#{index.password}" ph:placeholder="Password" /><br/>
        <h:commandButton action="#{index.loginEvent()}" value="ログイン" update="@form" />
    </h:form>
</body>
</html>
```

* シンプルに項目を配置しただけの画面です。
* `h:messages`はメッセージを表示するエリアです。
* ログインボタンを押すとBackingBeanで定義している`loginEvent`メソッドを実行するようにしています。

次にBackingBeanですね。

```java:IndexBean.java
@Named("index")    // ・・・①
@ViewScoped
@Getter
@Setter
public class IndexBean implements Serializable {

    private String loginId;        // ・・・②
    private String password;

    public String loginEvent() {
        if (!loginId.isEmpty() && !password.isEmpty()) {
            return "top.xhtml?redirect=true";     // ・・・③

        } else {
            // ・・・④
            FacesContext context = FacesContext.getCurrentInstance();
            FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "認証エラーです", "");
            context.addMessage(null, message);
            return null;
        }
    }
}
```
① Namedアノテーションで、index.xhtmlのEL式から見えるように設定します。
② 画面の項目に合わせて属性を定義します。
③ ログインIDとパスワードの両方になにか入力されていれば、画面遷移するようにしました。
④ それ以外のときは、自画面でメッセージを表示するようにしました。

次に、遷移先のTop画面です。

```html:top.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" 
	xmlns:ui="http://java.sun.com/jsf/facelets" 
	xmlns:h="http://java.sun.com/jsf/html" 
	xmlns:ph="http://xmlns.jcp.org/jsf/passthrough"
	xmlns:f="http://java.sun.com/jsf/core">

<h:head>
	<title>JSFサンプル</title>
</h:head>

<body>
	<h:form id="form">
		<div>TOP画面 </div>
	</h:form>
</body>
</html>
```

# 完成
メッセージも表示されるし、画面遷移もできました。思ったより簡単でした！
以上です。

# 参考
[Apache OpenWebBeans](https://openwebbeans.apache.org/documentation.html#module-overview)