CDIをTomcatで動かす - Weld Servlet

# 概要
本来はJava EE環境(Java EE サーバ上)で動くCDIを、Tomcat(非Java EE サーバ)で動かしてみます。
CDIの参照実装であるWeldを使います。
シンプルな構成にしたいので、素のServletにDIするものとします。

今回作成したサンプルコードは、GitHubにアップしてあります。
https://github.com/sumeragizzz/weldservlet-examples

# 環境
Windows 10 Pro 64bit
Java SE 8 Update 102 64bit
Apache Tomcat 8.0.36 64bit
Weld Servlet 2.4.0.Final

# 準備
## Maven
ビルドツールにMavenを使用します。
pom.xmlに以下の依存情報を追加します。

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

<dependency>
    <groupId>org.jboss.weld.servlet</groupId>
    <artifactId>weld-servlet</artifactId>
    <version>2.4.0.Final</version>
</dependency>
```

前者はHttpServletなどのServlet関連クラスをコンパイル時に参照できるようにする為。
後者は今回のテーマであるCDIの実装です。

## web.xml
Sevlet 3.0からは自動的にSevletを検出してくれるので、ほぼ何も定義していません。

```xml: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">
    <display-name>Archetype Created Web Application</display-name>
</web-app>
```

## beans.xml
CDIを有効にする為、beans.xmlを用意します。
前回の[投稿](http://qiita.com/sumeragizzz/items/3ac4bfe81484d0ff899f "CDIをJava SE環境で動かす - Weld SE")にも書きましたが、0byteファイルでも有効になります。
サンプルの都合でインターセプターを定義したくらいで、有効化の為に必要な定義はありません。

```xml:beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans
    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/beans_1_1.xsd"
    bean-discovery-mode="all">
    <interceptors>
        <class>examples.weldservlet.interceptor.ZonedInterceptor</class>
    </interceptors>
</beans>
```


# 実装
サンプルなので処理内容は特に意味の無いものになっています。

Sevletに@InjectでDIできます。

```java:NowServlet.java
@WebServlet("/NowServlet")
public class NowServlet extends HttpServlet {
    @Inject
    private DateTimeFormatBean dateTimeFormatBean;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().append(dateTimeFormatBean.get());
    }
}
```

DIしたCDI Beanに別のCDI BeanをDIできます。

```java:DateTimeFormatBean.java
public class DateTimeFormatBean {
    @Inject
    private NowBean nowBean;

    public String get() {
        return nowBean.get().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
    }
}
```

別のCDI Beanはインターセプターを適用しました。

```java:NowBean.java
@ZonedInterceptorBinding
public class NowBean {
    public ZonedDateTime get() {
        return ZonedDateTime.now();
    }
}
```

インターセプターの実装は下記の通り。

```java:ZonedInterceptor.java
@Interceptor
@ZonedInterceptorBinding
public class ZonedInterceptor {
    @AroundInvoke
    public Object invoke(InvocationContext context) throws Exception {
        ZonedDateTime result = ZonedDateTime.class.cast(context.proceed());
        return result.withZoneSameInstant(ZoneId.of("America/New_York"));
    }
}
```

```java:ZonedInterceptorBinding.java
@Inherited
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface ZonedInterceptorBinding {
}
```

Tomcatにデプロイして、下記のURLにブラウザからアクサスします。
http://localhost:8080/weldservlet-examples/NowServlet

ブラウザに日時が表示されたら成功です。

```console:実行結果
2016-10-12T11:08:42.372
```

# まとめ
pom.xmlに依存情報の追加と、beans.xmlの作成くらいなので、意外と手軽に導入できます。
ただ、実際には素のSevletを使う機会は少ないでしょう。
現実的にはJAX-RSなどとの組み合わせになるので、いずれこちらも投稿したいと思います。

# 参考
http://docs.jboss.org/weld/reference/latest/en-US/html_single/