JavaEE使い方メモ（Servlet・JSP・EL式）


環境構築は [こちら](http://qiita.com/opengl-8080/items/aeda0d8bad42af3113bd)。

コードは GitHub に上げてます。
https://github.com/opengl8080-javaee-samples/servlet-jsp

#Servlet とは
Java で Web アプリを作るときの基礎となる仕組み・フレームワーク。

Java EE は知らないけど、 Servlet なら知っているという人は多いかと。

Java で Web アプリを作る場合、 Servlet を直接実装することは基本的に無い。普通は、フレームワークを使う（Struts, JSF, JAX-RS, Spark, etc...）。
しかし、フレームワークは Servlet の上で動いているので、フレームワークを使いこなすためには Servlet の知識が必要になるときがある。また、フレームワークが対応していない機能を補完するために、　Servlet が提供する API をプログラマが直接触る機会は結構多い（`HttpServletRequest` や `HttpServletResponse` など）。

#Hello World
あえて `web.xml` を使った基本的な方法で Servlet を実装する。

コンテキストルートを `servlet` にして [Web プロジェクトを作成する](http://qiita.com/opengl-8080/items/aeda0d8bad42af3113bd#%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B)。

```java:HelloServlet.java
package sample.javaee.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloServlet extends HttpServlet {
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try (PrintWriter writer = response.getWriter()) {
            writer.println("<h1>Hello Servlet!!</h1>");
        }
    }
}
```

```xml:web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1"
         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">
  
  <servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>sample.javaee.servlet.HelloServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
  
</web-app>
```

プロジェクトを実行して、 Web ブラウザで `http://localhost:8080/servlet/hello` を開く。

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/27ffe062-191a-9139-d9f5-7bf880ebc257.jpeg)

- Servlet は、 `HttpServlet` クラスを継承したクラスを作成する。
- リクエストのあった URL と実行する Servlet のマッピングは、 `web.xml` に記述する。
    - `<servlet>` タグを使って、 Servlet クラスを宣言する。
    - `<servlet-mapping>` タグを使って、 Servlet と URL をマッピングする。
- `HttpServlet` クラスが持つ `do***()` メソッドをオーバーライドすると、 Servlet の URL に送られてきた HTTP リクエストを受け取ることができる。
    - `***` の部分は、 HTTP で定義されているメソッド（GET, POST, PUT, DELETE など）が対応している。
    - オーバーライドしていない HTTP メソッドでリクエストが飛んでくると、 `HttpServlet` のデフォルトの実装が `405 - Method Not Allowed` を返すようになっている。


#Servlet のライフサイクル
Servlet のインスタンスがいつ生成されて、いつ破棄されるのかについて。

```java:LifeCycleServlet.java
package sample.javaee.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LifeCycleServlet extends HttpServlet {

    @Override
    public void init() throws ServletException {
        System.out.println("init() : hash=" + this.hashCode());
    }
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("doGet() : hash=" + this.hashCode());
    }

    @Override
    public void destroy() {
        System.out.println("destroy() : hash=" + this.hashCode());
    }
}
```

```xml:web.xml
  <servlet>
    <servlet-name>LifeCycleServlet</servlet-name>
    <servlet-class>sample.javaee.servlet.LifeCycleServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>LifeCycleServlet</servlet-name>
    <url-pattern>/lifecycle</url-pattern>
  </servlet-mapping>
```

Web ブラウザで `http://localhost:8080/servlet/lifecycle` を開き、何度か F5 を実行したあと GlassFish を停止させる。

```text:GlassFishのログ出力
情報:   init() : hash=1092915953, thread=http-listener-1(4)
情報:   doGet() : hash=1092915953, thread=http-listener-1(4)
情報:   doGet() : hash=1092915953, thread=http-listener-1(1)
情報:   doGet() : hash=1092915953, thread=http-listener-1(5)
情報:   doGet() : hash=1092915953, thread=http-listener-1(3)
情報:   Server shutdown initiated
情報:   Unregistered com.sun.enterprise.glassfish.bootstrap.osgi.EmbeddedOSGiGlassFishImpl@70e02081 from service registry.
情報:   FileMonitoring shutdown
情報:   JMXStartupService: Stopped JMXConnectorServer: null
情報:   JMXStartupService and JMXConnectors have been shut down.
情報:   destroy() : hash=1092915953, thread=RunLevelControllerThread-1419038746038
```

- Servlet は、最初に URL にアクセスされたタイミングでインスタンスが生成される。
    - このとき、 `init()` メソッドがコールバックされる。
- 一度生成された Servlet のインスタンスは、破棄されることなく使いまわされる。
    - このとき、インスタンスを使用するスレッドは毎回異なる。
    - よって、 Servlet を実装するときは、スレッドセーフにしなければならない。
- 一定時間 Servlet にアクセスが無かったり、サーバーが停止するときに Servlet のインスタンスは破棄される。
    - このとき、 `destroy()` メソッドがコールバックされる。


#HTTP リクエストの情報を取得する
```java:RequestParameterServlet.java
package sample.javaee.servlet;

import java.io.BufferedReader;
import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class RequestParameterServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        String queryParam = req.getParameter("queryParameter");
        System.out.println("queryParam=" + queryParam);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try (BufferedReader br = req.getReader()) {
            br.lines().forEach(System.out::println);
        }
    }
}
```

```xml:web.xml
  <servlet>
    <servlet-name>RequestParameterServlet</servlet-name>
    <servlet-class>sample.javaee.servlet.RequestParameterServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>RequestParameterServlet</servlet-name>
    <url-pattern>/request-param-servlet</url-pattern>
  </servlet-mapping>
```

```html:html/request.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Serlvet Request</title>
  </head>
  <body>
    <form action="/servlet/request-param-servlet" method="GET">
      <input type="text" name="queryParameter" />
      <input type="submit" value="GET" />
    </form>
    
    <form action="/servlet/request-param-servlet" method="POST">
      <input type="text" name="textbox" />
      <textarea name="textarea"></textarea>
      <input type="submit" value="POST" />
    </form>
  </body>
</html>
```

Web ブラウザで、 `http://localhost:8080/servlet/html/request.html` を開く。

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/24e97436-23d1-ccc4-1459-6609561944d2.jpeg)

**GET ボタンを押下**
```text:GlassFishコンソール出力
情報:   queryParam=get parameter
```

**POST ボタンを押下**
```text:GlassFishコンソール出力
情報:   textbox=textbox&textarea=textarea%0D%0Avalue
```

- HTTP リクエストの情報は、 `HttpServletRequest` から取得できる。
- クエリパラメータは、 `getParameter()` メソッドで取得できる。
- リクエストボディは、 `getReader()` メソッドで取得した `BufferedReader` から取得できる。
    - リクエストボディの取得は、 `getInputStream()` メソッドでも取得可能。
    - どちらか一方を先に呼び出した場合、他方のメソッドは使えなくなる（`IllegalStateException` がスローされる）。
- これら以外にも、 HTTP ヘッダーやクライアントの情報（IP アドレスなど）も、 `HttpServletRequest` から取得できる。

#Servlet 初期化時にパラメータを渡す
```xml:web.xml
  <servlet>
    <servlet-name>InitParamServlet</servlet-name>
    <servlet-class>sample.javaee.servlet.InitParamServlet</servlet-class>
    <init-param>
      <param-name>hoge</param-name>
      <param-value>HOGE</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>InitParamServlet</servlet-name>
    <url-pattern>/init-param</url-pattern>
  </servlet-mapping>
```

```java:InitServlet.java
package sample.javaee.servlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class InitParamServlet extends HttpServlet {

    @Override
    public void init(ServletConfig config) throws ServletException {
        String hoge = config.getInitParameter("hoge");
        System.out.println("hoge=" + hoge);
    }
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response){
    }
}
```

Web ブラウザから `http://localhost:8080/servlet/init-param` にアクセスすると、以下のようにコンソールに出力される。

```text:GlassFishコンソール出力
情報:   hoge=HOGE
```

- `web.xml` の `<servlet>` タグ内で、 `<init-param>` タグを設定すると、 Servlet 初期化時に任意のパラメータを渡すことができる。
- Servlet 側は、 `ServletConfig` を受け取る `init()` メソッドをオーバーライドすることで、このパラメータを受取ることができる。

#サーバー起動時に Servlet の初期化処理を実行する
```web.xml
  <servlet>
    <servlet-name>FirstStartupServlet</servlet-name>
    <servlet-class>sample.javaee.servlet.FirstStartupServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet>
    <servlet-name>SecondStartupServlet</servlet-name>
    <servlet-class>sample.javaee.servlet.SecondStartupServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
  </servlet>
  
  <servlet>
    <servlet-name>ThirdStartupServlet</servlet-name>
    <servlet-class>sample.javaee.servlet.ThirdStartupServlet</servlet-class>
    <load-on-startup>3</load-on-startup>
  </servlet>
```

```java:FirstStartupServlet.java
package sample.javaee.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

public class FirstStartupServlet extends HttpServlet {

    @Override
    public void init() throws ServletException {
        System.out.println("FirstStartupServlet.init()");
    }
}
```

※`SecondStartupServlet.java`, `ThirdStartupServlet.java` は出力文字列が違うだけで、同じ実装なので省略。

サーバーを再起動する。

```text:GlassFishコンソール出力
情報:   FirstStartupServlet.init()
情報:   SecondStartupServlet.init()
情報:   ThirdStartupServlet.init()
```

- `web.xml` の `<servlet>` タグ内で、 `<load-on-startup>` タグを設定することで、サーバー起動時に Servlet の初期化処理を実行させることができる。
- このとき、 `<load-on-startup>` タグで指定した順序で Servlet の初期化処理が実行される。


#URL のマッピング
```xml:web.xml
  <servlet>
    <servlet-name>UrlPatternWildCardServlet</servlet-name>
    <servlet-class>sample.javaee.servlet.UrlPatternWildCardServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>UrlPatternWildCardServlet</servlet-name>
    <url-pattern>/wildcard/*</url-pattern>
  </servlet-mapping>
  
  <servlet>
    <servlet-name>UrlPatternExtensionServlet</servlet-name>
    <servlet-class>sample.javaee.servlet.UrlPatternExtensionServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>UrlPatternExtensionServlet</servlet-name>
    <url-pattern>*.sample</url-pattern>
  </servlet-mapping>
```

##動作確認
| URL                           | 実行される Servlet                             |
| :--                           | :--                                        |
| /servlet/wildcard             | UrlPatternWildCardServlet  |
| /servlet/wildcard/test        | UrlPatternWildCardServlet  |
| /servlet/test.sample          | UrlPatternExtensionServlet |
| /servlet/test/test.sample     | UrlPatternExtensionServlet |
| /servlet/wildcard/test.sample | UrlPatternWildCardServlet  |

- `<url-pattern>` にはワイルドカード `*` を使用することができる。

#別の Servlet に処理を委譲する
```xml:web.xml
  <servlet>
    <servlet-name>FromServlet</servlet-name>
    <servlet-class>sample.javaee.servlet.FromServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>FromServlet</servlet-name>
    <url-pattern>/forward/from</url-pattern>
  </servlet-mapping>
  
  <servlet>
    <servlet-name>ToServlet</servlet-name>
    <servlet-class>sample.javaee.servlet.ToServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>ToServlet</servlet-name>
    <url-pattern>/forward/to</url-pattern>
  </servlet-mapping>
```

```java:FromServlet.java
package sample.javaee.servlet;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class FromServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("FromServlet.doGet()");
        req.getRequestDispatcher("/forward/to").forward(req, resp);
    }
}
```

```java:ToServlet.java
package sample.javaee.servlet;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ToServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        System.out.println("ToServlet.doGet()");
    }
}
```

Web ブラウザで `http://localhost:8080/servlet/forward/from` にアクセスする。

```text:GlassFishコンソール出力
情報:   FromServlet.doGet()
情報:   ToServlet.doGet()
```

- `request.getRequestDispatcher("<処理を委譲する Servlet のパス>").forward(request, response)` で、指定した Servlet に処理を委譲することができる。
- これを**フォワード**と呼ぶ。

#非同期処理を実装する
##基本
```xml:web.xml
  <servlet>
    <servlet-name>AsyncServlet</servlet-name>
    <servlet-class>sample.javaee.servlet.AsyncServlet</servlet-class>
    <async-supported>true</async-supported>
  </servlet>

  <servlet-mapping>
    <servlet-name>AsyncServlet</servlet-name>
    <url-pattern>/async</url-pattern>
  </servlet-mapping>
```

```java:AsyncServlet.java
package sample.javaee.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.AsyncContext;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class AsyncServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        System.out.println("AsyncServlet start.");
        
        AsyncContext ctx = req.startAsync();
        
        ctx.start(() -> {
            try (PrintWriter pw = ctx.getResponse().getWriter()) {
                Thread.sleep(5000);
                System.out.println("async process.");
                pw.println("<h1>Async Process</h1>");
            } catch (IOException | InterruptedException ex) {
                ex.printStackTrace();
            } finally {
                ctx.complete();
            }
        });
        
        resp.getWriter().println("<h1>AsyncServlet</h1>");
        
        System.out.println("AsyncServlet end.");
    }
}
```

Web ブラウザで `http://localhost:8080/servlet/async` にアクセスする。

```text:GlassFishコンソール出力
情報:   AsyncServlet start.
情報:   AsyncServlet end.
情報:   async process.
```

<ins>5秒ほど経過してから</ins>、画面が表示される

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/c4f4b16c-5e1a-e245-e1a2-08ee840c0e06.jpeg)

- 非同期処理を実装する場合は、 Servlet の定義に `<asyncasync-supported>` を追加して true を指定する。
    - もし Servlet の処理の前に Filter が処理を挟む場合は、その Filter にも `async-supported` を設定する必要がある。
- `request.startAsync()` で `AsyncContext` のインスタンスを取得する。
- `AsyncContext#start(Runnable)` で、非同期処理を開始する。
- 非同期処理中に `HttpServletRequest` や `HttpServletResponse` を取得したい場合は、 `AsyncContext` から取得する。
- 非同期処理が完了したら、 `AsyncContext#complete()` メソッドを実行して非同期処理が完了したことをコンテナに知らせる（知らせないとタイムアウトまで待つことになる）。

###レスポンスは非同期処理が完了されるまでコミットされない
「非同期」とあるので、まるで非同期処理が完了する前にクライアントにレスポンスが返されるのかと勘違いしそうになるけど、そうではない。
レスポンスは、 `AsyncContext#complete()` が実行されるまでクライアントに返されることはない（コミットされない）。

__Java™ Servlet Specification Version 3.1 - 2.3.3.3 Asynchronous processing (P.2-11)__

> The response isn't committed till complete (see below) is called on the AsyncContext.

日本語訳版はこちら→ [サーブレット3.0仕様書邦訳版](http://www.cresc.co.jp/tech/java/Servlet_Specifications/servlet_3-0_about.htm)。

> その応答はAsyncContext上でcompleteが呼ばれるまで（下記参照）コミットされない。

つまり、クライアントは非同期の分も含めて、処理が完了するまで待たされる。

非同期処理にするメリットは、サーブレットの実行に割り当てられているスレッドをコンテナがさっさと回収して、別のリクエストの処理に割り当てられて無駄が少なくなる、という点。

**前述の日本語訳版の仕様書より抜粋**

> （前略）
> サーブレット内での待機は非効率な使い方である。
> （中略）
> 多くのスレッドをアクセス待ちの状態でブロックさせ、ウェブ・コンテナ全体のスレッドの枯渇とサービス品質劣化をもたらす。
> 
> サーブレット第3版では要求の非同期処理を導入しており、そのスレッドをコンテナに返して他のタスクを実行できるようにしている。

クライアントを待たせたくないのであれば、自分でスレッドを作って非同期処理を実装する必要がある。
その場合は、作成したスレッドからコンテナが管理しているリソース（`HttpServletRequest` など）にするのは避けたほうが良い。
その辺については [Servletがスレッドを生成してはいけないのか | code up](http://frmmpgit.blog.fc2.com/blog-entry-93.html) を参照。

リソースを非同期処理中で使いたいのであれば、 Java EE 7 で追加された Concurrency Utilities for Java EE を使うのがいいのかもしれない。
参考：[JavaEE - Concurrency Utilities for Java EEをつかってみる - Qiita](http://qiita.com/TTakakiyo/items/fa93fdec0e52b80fbc41)

##非同期処理のイベントを監視する
```java:AsyncServlet.java
package sample.javaee.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class AsyncServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        System.out.println("AsyncServlet start.");
        
        AsyncContext ctx = req.startAsync();
        
        ctx.addListener(new AsyncListenerImpl());
        
        ctx.start(() -> {
            try (PrintWriter pw = ctx.getResponse().getWriter()) {
                Thread.sleep(5000);
                System.out.println("async process.");
                pw.println("<h1>Async Process</h1>");
            } catch (IOException | InterruptedException ex) {
                ex.printStackTrace();
            } finally {
                ctx.complete();
            }
        });
        
        resp.getWriter().println("<h1>AsyncServlet</h1>");
        
        System.out.println("AsyncServlet end.");
    }
    
    private static class AsyncListenerImpl implements AsyncListener {

        @Override
        public void onStartAsync(AsyncEvent event) throws IOException {
            System.out.println("onStartAsync");
        }
        
        @Override
        public void onComplete(AsyncEvent event) throws IOException {
            System.out.println("onComplete");
        }

        @Override
        public void onTimeout(AsyncEvent event) throws IOException {
            System.out.println("onTimeout");
        }

        @Override
        public void onError(AsyncEvent event) throws IOException {
            System.out.println("onError");
        }
    }
}
```

- `AsyncListener` を実装したクラスを作成して、 `AsyncContext#addListener()` で登録する。
- 非同期処理のイベントごとに、各メソッドがコールバックされる。


#セッション管理
```xml:web.xml
  <servlet>
    <servlet-name>SessionServlet</servlet-name>
    <servlet-class>sample.javaee.servlet.SessionServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>SessionServlet</servlet-name>
    <url-pattern>/session/*</url-pattern>
  </servlet-mapping>
```

```java:SessionServlet.java
package sample.javaee.servlet;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class SessionServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        if (this.notExistsSession(req)) {
            this.createSession(req);
        }
        
        if ("/delete".equals(req.getPathInfo())) {
            this.deleteSession(req);
        } else {
            this.countUp(req);
        }
    }
    
    private boolean notExistsSession(HttpServletRequest request) {
        return request.getSession(false) == null;
    }
    
    private void createSession(HttpServletRequest request) {
        HttpSession session = request.getSession();
        session.setAttribute("count", 0);
        
        System.out.printf("session is created. id=%s%n", session.getId());
    }

    private void deleteSession(HttpServletRequest request) {
        HttpSession session = request.getSession(true);
        session.invalidate();
        
        System.out.printf("session is deleted. id=%s%n", session.getId());
    }

    private void countUp(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        int count = (int)session.getAttribute("count");
        session.setAttribute("count", ++count);
        
        System.out.printf("count up. id=%s, count=%d%n", session.getId(), count);
    }
}
```

Web ブラウザで、 `http://localhost:8080/servlet/session/` に何度かアクセスする。

```text:GlassFishコンソール出力
情報:   session is created. id=7d32d5e4a2958576fd139692be1a
情報:   count up. id=7d32d5e4a2958576fd139692be1a, count=1
情報:   count up. id=7d32d5e4a2958576fd139692be1a, count=2
情報:   count up. id=7d32d5e4a2958576fd139692be1a, count=3
情報:   count up. id=7d32d5e4a2958576fd139692be1a, count=4
```

ブラウザの Cookie を見ると、 `JSESSIONID` という名前でセッションの ID が保存されている。

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/c15c022e-8369-5998-2bb7-fcb9b03d25a8.jpeg)

`http://localhost:8080/servlet/session/delete` にアクセスする。

```text:GlassFishコンソール出力
情報:   session is deleted. id=7d32d5e4a2958576fd139692be1a
```

もう一度 `http://localhost:8080/servlet/session/` にアクセスする。

```text:GlassFishコンソール出力
情報:   session is created. id=7d6023ef3e12cc524fd29fa7f5ad
情報:   count up. id=7d6023ef3e12cc524fd29fa7f5ad, count=1
```

- `request.getSession(boolean)` でセッションを取得する。
    - true を渡した場合、セッションが存在しないとセッションが新規に作成される。
    - false を渡した場合、セッションが存在しないと null が返される。
- セッションの ID はブラウザに保存される（普通は Cookie）。
    - ブラウザは、リクエストのたびにこの ID をサーバーに渡す。
    - サーバーは、受け取った ID をもとにクライアントを識別してセッションを管理する。
- `session.invalidate()` でセッションを破棄できる。


#セッションタイムアウトの時間を設定する
```xml:web.xml
  <session-config>
    <session-timeout>60</session-timeout>
  </session-config>
```

- `<session-config>` タグを `web.xml` に記述することで、セッションタイムアウトの時間を設定できる。
- 値は分数を指定する。
- デフォルトは 60 （1時間）。
- -1 を指定した場合は、セッションタイムアウト無しになる。


#アプリケーション起動・終了時に処理を実行する
##基本
```xml:web.xml
  <listener>
    <listener-class>sample.javaee.servlet.listener.WebAppListener</listener-class>
  </listener>
```

```java:WebAppListener.java
package sample.javaee.servlet.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class WebAppListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent event) {
        System.out.println("contextInitialized()");
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        System.out.println("contextDestroyed()");
    }
}
```

GlassFish を起動する。

```text:GlassFishコンソール出力
情報:   contextInitialized()
```

GlassFish を停止する。

```text:GlassFishコンソール出力
情報:   contextDestroyed()
```

- `ServletContextListneer` を実装したクラスを作成し、 `<listener>` タグを使って `web.xml` に登録する。
- アプリケーション起動・終了時に、 `contextInitialized()` と `contextDestroyed()` がそれぞれ呼ばれる。

##パラメータを渡す
```xml:web.xml
  <context-param>
    <param-name>hoge</param-name>
    <param-value>HOGE</param-value>
  </context-param>
  
  <listener>
    <listener-class>sample.javaee.servlet.listener.WebAppListener</listener-class>
  </listener>
```

```java:WebAppListener.java
package sample.javaee.servlet.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class WebAppListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent event) {
        String value = (String)event.getServletContext().getInitParameter("hoge");
        System.out.println("contextInitialized() hoge=" + value);
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        System.out.println("contextDestroyed()");
    }
}
```

GlassFish を起動する。

```text:GlassFishコンソール出力
情報:   contextInitialized() hoge=HOGE
```

- `<contex-param>` タグを `web.xml` に記述することで、 `ServletContextEvent` から値を取得することができる。

#セッションの作成・終了時に処理を実行する
```xml:web.xml
  <listener>
    <listener-class>sample.javaee.servlet.listener.SessionListener</listener-class>
  </listener>
```

```java:SessionListener.java
package sample.javaee.servlet.listener;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class SessionListener implements HttpSessionListener {

    @Override
    public void sessionCreated(HttpSessionEvent event) {
        System.out.printf("sessionCreated() id=%s%n", event.getSession().getId());
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
        System.out.printf("sessionDestroyed() id=%s%n", event.getSession().getId());
    }
}
```

先ほど作成した `SessionServlet` にアクセスしてみる。

```text:GlassFishコンソール出力
情報:   sessionCreated() id=7f5d729e7319528e4a8316d16555
情報:   session is created. id=7f5d729e7319528e4a8316d16555
情報:   count up. id=7f5d729e7319528e4a8316d16555, count=1
情報:   sessionDestroyed() id=7f5d729e7319528e4a8316d16555
情報:   session is deleted. id=7f5d729e7319528e4a8316d16555
```

- `HttpSessionListener` を実装したクラスを作成して、 `<listener>` タグを使って `web.xml` に登録する。
- セッションの作成と削除のときに、 `sessionCreated()` と `sessionDestroyed()` がそれぞれ呼ばれる。
- 引数の `HttpSessionEvent` から、対象の `HttpSession` オブジェクトを取得できる。

#リクエストの前後で処理を挟む
```xml:web.xml
  <filter-mapping>
    <filter-name>RequestFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
```

```java:RequestFilter.java
package sample.javaee.servlet.filter;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class RequestFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("RequestFilter before");
        chain.doFilter(request, response);
        System.out.println("RequestFilter after");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void destroy() {
    }
}
```

`http://localhost:8080/servlet/test.sample` にアクセスする。

```text:GlassFishコンソール出力
情報:   RequestFilter before
情報:   UrlPatternExtensionServlet.doGet()
情報:   RequestFilter after
```

- `Filter` を実装したクラスを作成して、 `<filter>` タグで `web.xml` に登録する。
- `<filter-mapping>` タグで、フィルターを適用する URL パターンをマッピングする。
- URL パターンにマッチするリクエストがあると、 `doFilter()` メソッドが実行される。
    - `FilterChain#doFilter()` メソッドで、次の処理へ進むことができる（呼ばなくても良い）。

#トップページを指定する
```xml:web.xml
  <welcome-file-list>
    <welcome-file>welcome.html</welcome-file>
  </welcome-file-list>
```

```html:welcome.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Servlet JSP Sample</title>
  </head>
  <body>
    <h1>Welcome Page</h1>
  </body>
</html>
```

Web ブラウザで `http://localhost:8080/servlet` にアクセスする。

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/2609da37-d20f-e453-c670-25e07cef6914.jpeg)

- `<welcome-file-file>` タグで、ルートにアクセスしたときに表示するパスを指定できる。
- `<welcome-file>` タグは複数記述できる。
    - 複数記述した場合は、先頭から順番に存在をチェックして、最初に存在見つかったファイルが表示される。
- ファイルだけでなく、以下のように Servlet にマッピングしたパスも指定可能。

```xml:web.xml
  <welcome-file-list>
    <welcome-file>hello</welcome-file>
  </welcome-file-list>
```

#エラーが発生したときのページを指定する
##HTTP のステータスコードごとにページを指定する
```xml:web.xml
  <error-page>
    <error-code>404</error-code>
    <location>/error/404.html</location>
  </error-page>
```

```html:404.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>404 Not Found</title>
  </head>
  <body>
    <h1>404 Not Found</h1>
  </body>
</html>
```

Web ブラウザを開いて、 `http://localhost:8080/servlet/xxx` にアクセスする

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/8a827a5d-8b40-ee8b-f752-3e4b3d79dbd3.jpeg)

- `<error-page>` タグで、エラーが発生したときに表示するページを指定できる。
- `<error-code>` タグを指定すると、 HTTP のステータスコードごとにページを指定できる。

##スローされた例外ごとにページを指定する
```xml:web.xml
  <servlet>
    <servlet-name>NullPointerServlet</servlet-name>
    <servlet-class>sample.javaee.servlet.NullPointerServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>NullPointerServlet</servlet-name>
    <url-pattern>/null</url-pattern>
  </servlet-mapping>
  
  <error-page>
    <exception-type>java.lang.NullPointerException</exception-type>
    <location>/error/null.html</location>
  </error-page>
```

```java:NullPointerServlet.java
package sample.javaee.servlet;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class NullPointerServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        throw new NullPointerException("NullPointerServlet");
    }
}
```

```html:null.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>NullPointerException</title>
  </head>
  <body>
    <h1>NullPointerException!!</h1>
  </body>
</html>
```

Web ブラウザで `http://localhost:8080/servlet/null` にアクセスする。

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/939583d9-08e4-3980-976f-c8440ce5dad2.jpeg)

- `<exception-type>` タグで、例外ごとにエラーページを指定できる。

#アノテーションで設定する
Servlet 3.0 からは、 `web.xml` で記述していた設定をアノテーションで指定することができる。

##Servlet
```java:AnnotatedServlet.java
package sample.javaee.servlet;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/annotated")
public class AnnotatedServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        System.out.println("AnnotatedServlet.doGet()");
    }
}
```

Web ブラウザで `http://localhost:8080/servlet/annotated` にアクセスする。

```text:GlassFishコンソール出力
情報:   AnnotatedServlet.doGet()
```

設定できるパラメータについては [API ドキュメント](http://docs.oracle.com/javaee/7/api/javax/servlet/annotation/WebServlet.html) を参照。

##Listener
```java:AnnotatedListener.java
package sample.javaee.servlet.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class AnnotatedListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("AnnotatedListener.contextInitialized()");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    }
}
```

GlassFish を起動する。

```text:GlassFishコンソール出力
情報:   AnnotatedListener.contextInitialized()
```

##Filter
```java:AnnotatedFilter.java
package sample.javaee.servlet.filter;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;

@WebFilter("/*")
public class AnnotatedFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("AnnotatedFilter before");
        chain.doFilter(request, response);
        System.out.println("AnnotatedFilter after");
    }
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}
    @Override
    public void destroy() {}
}
```

Web ブラウザで `http://localhost:8080/servlet` にアクセスする。

```text:GlassFishコンソール出力
情報:   AnnotatedFilter before
情報:   AnnotatedFilter after
```

ただし、アノテーション指定の場合は Filter の順序を定義できない。
順序を定義したい場合は[こちら](http://qiita.com/opengl-8080/items/db385b934690d6c3d13a) を参照。

#JSP
##JSP とは
JavaServer Pages の略。
ビュー（画面）を動的に作るための仕組み（テンプレートエンジン）。 HTML タグと JSP が持つタグなどを使って画面を記述できる。

Servlet は MVC の C (Controller) を担い、 JSP は V (View) を担う。
しかし、書き方によってはゴリゴリ Java の処理を書くことができるため、魔窟のような JSP も世の中には存在する。

ただし、 Java EE 6 からは、 Facelets という別のテンプレートエンジンがメインとなっている（JSP は無くなってはいない）。

##Hello World
```jsp:jsp/hello.jsp
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello JSP</title>
  </head>
  <body>
    <h1>Hello JSP!!</h1>
  </body>
</html>
```

Web ブラウザで、 `http://localhost:8080/servlet/jsp/hello.jsp` にアクセスする。

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/51d00a4d-0b93-f827-9318-1ddeb8266ad4.jpeg)

- 普通に HTML を記述することができる。
- 拡張子を `.jsp` にして保存し、直接 URL をアクセスすれば JSP として実行される。

##JSP は Servlet の実装に変換されて実行される
JSP にアクセスがあると、サーバーは JSP を Servlet の実装に変換する。
GlassFish の場合、以下のフォルダを見ると生成された Servlet の実装を確認できる。

`<GlassFish 本体が存在するフォルダ>\domains\<ドメイン名>\generated\jsp\<アプリケーション名>\`

以下が、実際に出力された Servlet のソースコード。

```java:hello_jsp.java
package org.apache.jsp.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;

public final class hello_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent {

  private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();

  private static java.util.List<String> _jspx_dependants;

  private org.glassfish.jsp.api.ResourceInjector _jspx_resourceInjector;

  public java.util.List<String> getDependants() {
    return _jspx_dependants;
  }

  public void _jspService(HttpServletRequest request, HttpServletResponse response)
        throws java.io.IOException, ServletException {

    PageContext pageContext = null;
    HttpSession session = null;
    ServletContext application = null;
    ServletConfig config = null;
    JspWriter out = null;
    Object page = this;
    JspWriter _jspx_out = null;
    PageContext _jspx_page_context = null;

    try {
      response.setContentType("text/html");
      response.setHeader("X-Powered-By", "JSP/2.3");
      pageContext = _jspxFactory.getPageContext(this, request, response,
               null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;
      _jspx_resourceInjector = (org.glassfish.jsp.api.ResourceInjector) application.getAttribute("com.sun.appserv.jsp.resource.injector");

      out.write("<!DOCTYPE html>\r\n");
      out.write("<html>\r\n");
      out.write("  <head>\r\n");
      out.write("    <meta charset=\"UTF-8\">\r\n");
      out.write("    <title>Hello JSP</title>\r\n");
      out.write("  </head>\r\n");
      out.write("  <body>\r\n");
      out.write("    <h1>Hello JSP!!</h1>\r\n");
      out.write("  </body>\r\n");
      out.write("</html>\r\n");
    } catch (Throwable t) {
      if (!(t instanceof SkipPageException)){
        out = _jspx_out;
        if (out != null && out.getBufferSize() != 0)
          out.clearBuffer();
        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
        else throw new ServletException(t);
      }
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}
```

単純に HTML で記述した JSP ファイルは、上記のように Servlet による実装に置き換えられてから実行され、処理結果がクライアントに返されている。

##ページのエンコーディングを指定する
```jsp:encoding.jsp
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JSP Page</title>
  </head>
  <body>
    <h1>日本語</h1>
  </body>
</html>
```

HTML の `<meta>` タグを使って文字コードを指定しているが、このままで日本語を記述すると文字化けが発生する。

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/3438d368-af8d-e7b6-b318-db3211dd5bf8.jpeg)

これは、 JSP が HTML を生成するときの文字コードがデフォルトで ISO-8859-1 になっているのが原因。

HTML を出力するときの文字コードは、以下のように指定する。

```jsp:encoding.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JSP Page</title>
  </head>
  <body>
    <h1>日本語</h1>
  </body>
</html>
```

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/4990eb81-14f0-dd49-8903-f7d58a56375d.jpeg)

##任意の Java コードを埋め込む
```jsp:scriptlet.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello JSP</title>
  </head>
  <body>
    <% String msg = "Hello Scriptlet!!"; %>
    <h1><%=msg %></h1>
  </body>
</html>
```

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/cff85759-36f1-d7e1-a0f5-5ca14b55e817.jpeg)

- `<% %>` は**スクリプトレット**と呼び、任意の Java コードを記述できる。
- `<%= %>` で任意の Java コードを評価した結果を HTML 中に埋め込むことができる。

##JSP 内で暗黙的に使用できる変数
```jsp:implicit.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello JSP</title>
  </head>
  <body>
    <% out.println("<h1>Implicit Objects</h1>"); %>
  </body>
</html>
```

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/28c62d1c-a32a-51bd-c149-2765b2934925.jpeg)

- JSP 内では、宣言せずに暗黙的に使用できる変数が存在する。
    - 上記例の場合は、 `out` という変数を使用している。
    - これの実体は `PrintWriter` で、 HTML へ任意の文字列を出力できる。

`out` 以外にも、以下のような変数を使用できる。

| 変数        | 型                  | 説明                                                |
| :--         | :--                 | :--                                                 |
| out         | PrintWriter         | HTML に任意の文字列を出力できる。                   |
| request     | HttpServletRequest  | 現在のリクエストオブジェクト。                      |
| response    | HttpServletResponse | 現在のレスポンスオブジェクト。                      |
| pageContext | PageContext         | 現在の JSP ページのコンテキストを持つオブジェクト。 |
| session     | HttpSession         | 現在のセッションオブジェクト。                      |
| application | ServletContext      | アプリの ServletContext オブジェクト。              |
| page        | HttpJspPage         | JSP から生成された Servlet のオブジェクト。         |

##Java のクラスをインポートする
```jsp:java_import.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@page import="java.util.List" %>
<%@page import="java.util.Arrays" %>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello JSP</title>
  </head>
  <body>
    <ul>
      <%
      List<String> list = Arrays.asList("hoge", "fuga", "piyo");

      for (String value : list) {
          out.println("<li>" + value + "</li>");
      }
      %>
    </ul>
  </body>
</html>
```

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/6f86f655-0284-2061-2937-dc47754d2b29.jpeg)

- `<%@page import="..." %>` で、任意の Java クラスをインポートできる。

##コメントを記述する
```jsp:comment.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello JSP</title>
  </head>
  <body>
    <!--HTML comment-->
    <%-- JSP comment --%>
  </body>
</html>
```

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/daeae7f5-8b00-e2ae-ad28-5cde454e7cd4.jpeg)

- `<%-- --%>` で JSP のコメントを記述できる。
- JSP のコメントは、 HTML に出力されない。

##別のファイルを埋め込む
```jsp:embedded.jsp
<%@include file="hello.jsp" %>
```

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/9f26de65-1576-a85c-bbd5-df180d3828b8.jpeg)

- `<%@include file="..." %>` で、任意のファイルを JSP 内に埋め込むことができる。

##Servlet から JSP に処理を移す
```java:JspServlet.java
package sample.javaee.servlet;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/jsp-servlet")
public class JspServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("JspServlet.doGet()");
        req.getRequestDispatcher("jsp/hello.jsp").forward(req, resp);
    }
}
```

Web ブラウザで `http://localhost:8080/servlet/jsp-servlet` にアクセスする。

```text:GlassFishコンソール出力
情報:   JspServlet.doGet()
```

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/16f18cd6-1598-d1dd-1a99-7112cdc47e04.jpeg)

- Servlet と同じ要領でフォワードすることができる。

##EL 式
###EL 式とは
Expression Language の略。
式言語とも呼ばれ、スクリプトレットよりも簡潔な記述で演算結果の出力などができる。

最初は JSP の一部だったが、 Java EE 7 からは独立した仕様となった。
Facelets でも使える。

###基本
```jsp:el.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JSP Page</title>
  </head>
  <body>
    <h1>${"Hello Expression Language"}</h1>
  </body>
</html>
```

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/d8c27e4a-82e3-39b6-078b-d3058c9d017d.jpeg)

- `${<評価したい式>}` で記述する。
- 評価結果は、そのまま HTML 上に出力される。

###演算する
```jsp:el2.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JSP Page</title>
  </head>
  <body>
    <h1>100 + 10 + 1 = ${100 + 10 + 1}</h1>
  </body>
</html>
```

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/483f3fc3-854e-95ba-9efc-ca4451481fb4.jpeg)

- `${}` の中には、任意の演算式を記述できる。

###暗黙的に参照できるオブジェクト
```jsp:el3.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JSP Page</title>
  </head>
  <body>
    <h1>${pageContext.request.serverName}</h1>
  </body>
</html>
```

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/d0c621bd-49f6-3d5a-2fb5-e98124338504.jpeg)

- JavaBeans の仕様に従って命名された `getter` メソッドは、 `get` を省略してプロパティ名だけで参照することができる。
- スクリプトレットと同じように、 EL 式内では暗黙的に参照できる変数が存在する。

| 変数名           | 説明                                 |
| :---             | :--                                  |
| sessionScope     | セッションスコープ                   |
| requestScope     | リクエストスコープ                   |
| applicationScope | アプリケーションスコープ             |
| pageSope         | ページスコープ                       |
| pageContext      | 現在のページのコンテキスト           |
| param            | リクエストパラメータのマップ         |
| paramValues      | リクエストパラメータ（配列）のマップ |
| header           | HTTP のヘッダ                        |
| headerValues     | HTTP のヘッダ（配列）                |
| cookie           | クッキー（マップ）                   |

###各スコープへのアクセス
```java:ELScopeServlet.java
package sample.javaee.servlet;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/el-scope-servlet")
public class ELScopeServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setAttribute("hoge", "HOGE");
        req.getSession().setAttribute("fuga", "FUGA");
        req.getServletContext().setAttribute("piyo", "PIYO");
        
        req.setAttribute("same", "RequestValue");
        req.getSession().setAttribute("same", "SessionValue");
        req.getServletContext().setAttribute("same", "ApplicationValue");
        
        req.getRequestDispatcher("jsp/el4.jsp").forward(req, resp);
    }
}
```

```jsp:el4.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JSP Page</title>
  </head>
  <body>
    <h1>${requestScope.hoge} - ${sessionScope.fuga} - ${applicationScope.piyo}</h1>
    <h2>${hoge} - ${fuga} - ${piyo}</h2>
    
    <ul>
      <li>${same}
      <li>${requestScope.same}
      <li>${sessionScope.same}
      <li>${applicationScope.same}
    </ul>
    
    <% pageContext.setAttribute("same", "PageScopeValue"); %>
    
    <h3>${same}</h3>
  </body>
</html>
```

Web ブラウザで `http://localhost:8080/servlet/el-scope-servlet` にアクセスする。

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/ab24628f-a95b-a246-bc84-16f2ee840c3d.jpeg)

- `requestScope`、 `sessionScope`、 `applicationScope` は、それぞれ `HttpServletRequest`、 `HttpSession`、 `ServletContext` の `setAttribute()` でセットする。
- 各スコープに保存された値は、 EL 式上ではいきなりキーに指定した名前で参照することができる。
- 異なるスコープに同じ名前で保存されている値を EL 式で参照した場合、より狭いスコープで設定されている値が取得される。

###配列・リストへのアクセス
```jsp:el5.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@page import="java.util.*" %>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JSP Page</title>
  </head>
  <body>
    <%
    String[] array = {"hoge", "fuga", "piyo"};
    List<String> list = Arrays.asList("HOGE", "FUGA", "PIYO");
    
    pageContext.setAttribute("array", array);
    pageContext.setAttribute("list", list);
    %>
    
    <h2>${array[0]}, ${array[1]}, ${array[2]}</h2>
    <h2>${list[0]}, ${list[1]}, ${list[2]}</h2>
    
  </body>
</html>
```

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/c59063b5-d87a-f610-b6b5-7f6a875d1f1a.jpeg)

- 配列、 List は、 `[]` を使ったインデックス指定で要素にアクセスできる。

###マップのエントリへのアクセス
```jsp:el6.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@page import="java.util.*" %>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JSP Page</title>
  </head>
  <body>
    <%
    Map<String, String> map = new HashMap<String, String>();
    map.put("hoge", "HOGE");
    map.put("fuga", "FUGA");
    map.put("piyo", "PIYO");
    
    pageContext.setAttribute("map", map);
    %>
    
    <h2>${map.hoge} - ${map["fuga"]} - ${map['piyo']}</h2>
    
  </body>
</html>
```

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/bee69916-470e-3058-2afe-87b48fe57fc9.jpeg)

- Map の各エントリへのアクセスには、 `map.キー名` か `map["キー名"]` のいずれかの方法が利用できる。

##標準タグライブラリ(JSTL)を使用する
```jsp:jstl.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JSP Page</title>
  </head>
  <body>
    <c:set var="flag" value="true" />
    
    <c:if test="${flag}">
      flag is true
    </c:if>
  </body>
</html>
```

Web ブラウザで `http://localhost:8080/servlet/jsp/jstl.jsp` を表示する。

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/e45a7678-1a96-d4b8-433c-94fb9d012436.jpeg)

- JSP には、標準タグライブラリ（JSP Standard Tag Library）というロジックなどを扱うためのタグが用意されている。
- タグライブラリを使う場合は、 `<%@taglib prefix="..." uri="..." %>` で使用するタグを宣言する。
    - `prefix` 属性には、タグを使用するときの名前空間を指定する。
    - `uri` 属性には、使用するタグを識別するための URI を指定する。
    - URI に指定する値は、 jar ファイルに入っている `*.tld` ファイルを見ると書いている。

**c.tld**

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/53843c17-5d6e-7c34-7805-0e67dc8c6a77.jpeg)

```xml:c.tld
<taglib 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-jsptaglibrary_2_1.xsd"
    version="2.1">
    
  <description>JSTL 1.2 core library</description>
  <display-name>JSTL core</display-name>
  <tlib-version>1.2</tlib-version>
  <short-name>c</short-name>
  <uri>http://java.sun.com/jsp/jstl/core</uri>

（略）
```

- 標準カスタムタグには、以下の５種類が存在する。

| 名前                    | 説明                                                               | URI                                    |
| :--                     | :--                                                                | :--                                    |
| core                    | if や for など、最もよく使うロジック処理用のタグが用意されている。 | http://java.sun.com/jsp/jstl/core      |
| i18n-capable formatting | i18n 対応したフォーマット出力などのタグが用意されている。          | http://java.sun.com/jsp/jstl/fmt       |
| functions               | 文字列操作ようのタグが用意されている。                             | http://java.sun.com/jsp/jstl/functions |
| sql                     | データベース接続用のタグが用意されている。                         | http://java.sun.com/jsp/jstl/sql       |
| xml                     | XML の解析、処理するためのタグが用意されている。                   | http://java.sun.com/jsp/jstl/xml       |

- 各タグの使い方については、 [ＪＳＴＬリファレンス | 忘れっぽいエンジニアのJakarta Strutsリファレンス](http://struts.wasureppoi.com/jstl/00_jstl.html) が日本語でまとめられてて重宝します。

##複数のJSPで共通する宣言をまとめる
```xml:web.xml
  <jsp-config>
    <jsp-property-group>
      <url-pattern>*.jsp</url-pattern>
      <page-encoding>UTF-8</page-encoding>
      <include-prelude>/jsp/taglib.jsp</include-prelude>
    </jsp-property-group>
  </jsp-config>
```

```jsp:taglib.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
```

```jsp:jsp-config.jsp
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JSP Page</title>
  </head>
  <body>
    <c:set var="flag" value="true" />
    <c:set var="message" value="日本語" />
    
    <c:if test="${flag}">
      ${message}
    </c:if>
  </body>
</html>
```

Web ブラウザで `http://localhost:8080/servlet/jsp/jsp-config.jsp` を開く。

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/e14a7f47-0734-ace1-98c4-5f92f1e60317.jpeg)

- `web.xml` で `<jsp-config>` タグを使用することで、複数ファイルで共通する設定を記述することができる。
- `<url-pattern>` で指定したパターンにマッチする URL にアクセスがあった場合に限り、ここで指定した共通設定が適用される。
- `<include-prelude>` で指定したファイルは、全ての JSP ファイルの先頭に埋め込まれる。ここで標準タグライブラリの使用宣言を記述することで、各ページでの読み込み宣言を省略できる。

#セキュリティ
Servlet では、特定の URL へのアクセスに対して認証による制限を設けることができる。

このアクセス制御は、ロール単位に行える。
つまり、「`/admin/*` という URL に対しては `admin` というロールを持ったユーザしかアクセスできない」といった制御ができる。

Servlet が定めているのはアクセス制御のところだけで、ユーザの定義方法やユーザとロールの紐付けはアプリケーションサーバー毎に方法が異なる。

##ユーザを定義する
- Web ブラウザを開いて `http://localhost:4848/` にアクセスする。
- [Configurations] → [server-config] → [Security] → [Realms] → [file] を選択する。

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/88b6072e-3a8e-3ef5-d425-df712b1d1ec1.jpeg)

- [Manage Users] をクリックする。
- [New...] をクリックして、以下のユーザーを追加する。

| User ID | Group List  | New Password |
| :--     | :--         | :--          |
| admin   | admin_group | admin        |
| user    | user_group  | user         |

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/d40d8538-ae43-d671-4b20-7940d4e43ef5.jpeg)

###レルム
ここで定義したものを**レルム**と呼ぶ。

> レルムとは、ユーザ名とパスワードの組み合わせのように、 Webアプリケーションのユーザを一意に定めるための"データベース"と、 認証された各ユーザに付与されているロールの一覧を列挙するものの両方を合わせたものを指します。

[Tomcat5 サーブレット/JSP コンテナ - レルム設定方法](http://www.jajakarta.org/tomcat/tomcat5.0/ja/docs/tomcat-docs/realm-howto.html#%E3%83%AC%E3%83%AB%E3%83%A0%E3%81%A8%E3%81%AF?)

つまり、複数のユーザ情報をまとめてレルムという単位で管理する。
アプリケーションからは、使用するレルムを指定する。

##ロールを定義する
```xml:web.xml
  <security-role>
    <role-name>admin_role</role-name>
  </security-role>
  
  <security-role>
    <role-name>user_role</role-name>
  </security-role>
```

- 管理者用のロールと、一般ユーザ用のロールを定義している。

##ユーザ（グループ）とロールを紐付ける。
```xml:glassfish-web.xml
  <security-role-mapping>
    <role-name>admin_role</role-name>
    <group-name>admin_group</group-name>
  </security-role-mapping>

  <security-role-mapping>
    <role-name>user_role</role-name>
    <principal-name>user</principal-name>
    <group-name>admin_group</group-name>
  </security-role-mapping>
```

- ユーザおよびグループとロールの紐付けは、 GlassFish の場合は `glassfish-web.xml` で定義する。
    - `glassfish-web.xml` は `WEB-INF` の直下に配置する。
- ユーザとロールを紐付ける場合は `<principal-name>` タグを使用する。
- グループとロールを紐付ける場合は、 `<group-name>` タグを使用する。
- 上記設定は、以下のような意味になる。
    - グループ「`admin_group`」に所属するユーザは、ロール「`admin_role`」を持つ。
    - `user` というユーザとグループ「`admin_group`」に所属ユーザは、ロール「`user_role`」を持つ。

##アクセス制御を定義する
```xml:web.xml
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>User Resource</web-resource-name>
      <url-pattern>/secure/user/*</url-pattern>
      <http-method>GET</http-method>
    </web-resource-collection>
    <auth-constraint>
      <role-name>user_role</role-name>
    </auth-constraint>
  </security-constraint>
  
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Admin Resource</web-resource-name>
      <url-pattern>/secure/admin/*</url-pattern>
      <http-method>GET</http-method>
    </web-resource-collection>
    <auth-constraint>
      <role-name>admin_user</role-name>
    </auth-constraint>
  </security-constraint>
```

- `<security-constraint>` タグで、１つのアクセス制御を定義できる。
- `<web-resource-collection>` タグで、アクセス制御の対象となる URL パターンや HTTP メソッドを指定できる。
    - `<http-method>` は省略可。省略した場合は、全てのメソッドが制限の対象になる。
    - `<web-resource-collection>`、 `<url-pattern>`、 `<http-method>` は複数指定可能。
- `<auth-constraint>` タグで、そのリソースにアクセスしていいロールを指定する。
    - `<role-name>` タグは複数指定可。
- 上記設定は、以下のような意味になる。
    - `/secure/user/*` への GET リクエストは、 `user_role` ロールを持つユーザだけが許可される。
    - `/secure/admin/*` への GET リクエストは、 `admin_role` ロールを持つユーザだけが許可される。

##ログイン方法と使用するレルムを指定する
```xml:web.xml
  <login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>file</realm-name>
  </login-config>
```

- `<auth-method>` でログイン方法を定義する。
    - ここでは、 BASIC 認証を指定している。
- `<realm-name>` で、使用するレルムを指定している。
    - ここでは、先ほどユーザ定義を追加した `file` レルムを指定している。

ログイン方法には、以下のようなものが使える。

| 設定値 | ログイン方法                  |
| :--    | :--                           |
| BASIC  | BASIC 認証                    |
| DIGEST | DIGEST 認証                   |
| FORM   | HTML の form タグを使った認証 |
| SSL    | SSL 認証                      |

##ページを作成する
Web ページの下に、 `secure/user/user.html` と `secure/admin/admin.html` を作成する。

##動作確認
Web ブラウザで `http://localhost:8080/servlet/secure/admin/admin.html` を開く。

BASIC 認証用のログインダイアログが表示されるので、 `admin` ユーザでログインする。

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/0fcbf5fd-1b71-1061-5408-3bc7047abca3.jpeg)

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/cc68e418-3115-bf5a-846d-97065328bb5c.jpeg)

続いて、そのまま `http://localhost:8080/servlet/secure/user/user.html` を開く。

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/dadc59cf-d15d-2073-82aa-464b02d1aa80.jpeg)

ログインダイアログが表示されることなく、ページが表示される。

次に、ブラウザを一旦終了させてから、 `http://localhost:8080/servlet/secure/user/user.html` にアクセスする。

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/4495df12-56e5-79f2-fddf-054f9fb899ce.jpeg)

再びログインダイアログが表示されるので、今度は `user` ユーザでログインする。

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/1e4cac2a-622f-0966-4016-39a1fd7e02d2.jpeg)

続いて `http://localhost:8080/servlet/secure/admin/admin.html` にアクセスする。

![javaee-servlet.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/3613444c-522c-63c9-9003-c18e168581d7.jpeg)

403 で弾かれる。

#参考
- [initメソッドとdestroyメソッド - サーブレットの基本 - サーブレット入門](http://www.javadrive.jp/servlet/ini/index6.html)
- [2. サーブレットの基本  | TECHSCORE(テックスコア)](http://www.techscore.com/tech/Java/JavaEE/Servlet/2/)
- [web.xml デプロイメント記述子の要素](http://otndnld.oracle.co.jp/document/products/wls/docs103/webapp/web_xml.html)
- [5. includeとforward (3) | TECHSCORE(テックスコア)](http://www.techscore.com/tech/Java/JavaEE/Servlet/5-3/)
- [7. リスナー  | TECHSCORE(テックスコア)](http://www.techscore.com/tech/Java/JavaEE/Servlet/7/)
- [8. リスナー2  | TECHSCORE(テックスコア)](http://www.techscore.com/tech/Java/JavaEE/Servlet/8/)
- [Tomcat 7の新機能で何ができるようになるのか？（2）：Tomcat 7も対応したServlet 3.0の変更点 後編 (1/3) - ＠IT](http://www.atmarkit.co.jp/ait/articles/1106/10/news116.html)
- [JavaServer Pages - Wikipedia](http://ja.wikipedia.org/wiki/JavaServer_Pages)
- [ＪＳＰリファレンス（@page：import、session）](http://struts.wasureppoi.com/jsp/05_import.html)
- [12. EL式  | TECHSCORE(テックスコア)](http://www.techscore.com/tech/Java/JavaEE/JSP/12/)
- [ＪＳＴＬリファレンス（ＪＳＴＬの種類）](http://struts.wasureppoi.com/jstl/01_kind.html)
- [Home: Java Platform, Enterprise Edition (Java EE) 7 Release 7](http://docs.oracle.com/javaee/7/)
- [＠IT：Java TIPS -- 個別のJSPページでJSTL宣言を省略する](http://www.atmarkit.co.jp/fjava/javatips/119jspservlet39.html)
- [ユーザー、グループ、ロール、およびレルムについて (Sun GlassFish Enterprise Server 2.1 管理ガイド)](https://docs.oracle.com/cd/E19879-01/820-7432/abloe/index.html)