TomcatのWebListener、HttpServlet、jspのあれこれ

# 設定ファイル

もうアノテーションで全部指定できるから、個人的な開発ではほぼ不要かな。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>tomcatTest20150719</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <listener>
    <listener-class>com.example.ListenerSample</listener-class>
  </listener>
  <context-param>
    <param-name>hoge</param-name>
    <param-value>HOGE</param-value>
  </context-param>
  <!-- フィルター -->
  <filter>
    <filter-name>FilterSample</filter-name>
    <filter-class>com.example.FilterSample</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>FilterSample</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>
```

#webリスナー。起動時、終了時

tomcatが起動した時とかwarファイルを更新した時とかにcontextInitializedが一度だけ、終了時にはcontextDestroyedが一度だけ。

```java
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
@javax.servlet.annotation.WebListener
public class WebListener implements ServletContextListener {
	@Override
	public void contextDestroyed(ServletContextEvent paramServletContextEvent) {}
	@Override
	public void contextInitialized(ServletContextEvent paramServletContextEvent) {
		paramServletContextEvent.getServletContext().setAttribute("hoge", "ok");
	}
}
```

##コンテキストに共通のインスタンスを作成する方法

コンテキスト全体で共通のインスタンスを持たせたい時がある。例えばアプリ全体で共通のカウンターが欲しい時、共通のIntegerオブジェクトが必要になる。staticでシングルトンを使うとjvm共通になって別のwebアプリと被ってしまうから出来ない。
そんな時はServletContext.setAttribute(String,Object);を使う。これならwebアプリでそれぞれインスタンスを持てる。
もちろん、webアプリを再起動させた時はクリアされるから、ServletContextListenerクラスのcontextDestroyedメソッドの中でonSaveとかの処理をする。contextDestroyedメソッドの中で時間のかかる処理をしていると、webアプリの再起動がその分遅くなるから注意。必要ならServletContextListenerクラスのcontextInitializedメソッドで初期化を行っても良い。

以下のクラスを作る。特に何かを継承したりしなくてもよい。

```java:CustomWebListenerManager.java
import java.io.File;
import java.util.Locale;
import java.util.ResourceBundle;

import javax.servlet.ServletContext;

public class CustomWebListenerManager {
	private static final String getTag(ServletContext sc) {
		String tag = String.format(Locale.US, "%s-%x", CustomWebListenerManager.class.getName(), System.identityHashCode(sc));
		return tag;
	}

	public static CustomWebListenerManager getInstance(ServletContext sc) {
		String tag = getTag(sc);
		Object attribute = sc.getAttribute(tag);
		if (attribute != null) {
			return (CustomWebListenerManager) attribute;
		}
		CustomWebListenerManager instance = new CustomWebListenerManager(sc);
		sc.setAttribute(tag, instance);
		return instance;
	}

	public final String storagePath;

	private CustomWebListenerManager(ServletContext sc) {
		//好きな処理を書く
	}
}
```

そして実際にインスタンスを取得する時はこうする

```java:HogeGet.java
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({ "/hogeGet" })
public class FriendServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException {
		String path = CustomWebListenerManager.getInstance(request.getServletContext()).storagePath;
		System.err.println(path);
	}
}
```

#フィルター

```java
↓リクエスト内容を弄くるフィルター
import javax.servlet.Filter;
public class FilterSample implements Filter {
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		chain.doFilter(request, response);
	}
}
```
#サーブレット


```java
@WebServlet({ "/url1","/url/sub2"})
public class AnyServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setAttribute("tweets", any object);
		//サーブレットで色々なデータをsetAttributeして、jspで表示する
		request.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(request, response);
		//サーブレットを用意するのが面倒な時はこれでよい
		responce.getWriter().write("hoge");
	}
}
```
上記コード内の様にjspを指定した場合、そのjspを配置する場所は以下のとおり
![imgTemp-2015-12-06-17-55-14.png](https://qiita-image-store.s3.amazonaws.com/0/5825/82ddd65e-0934-e5db-c0fb-aa20777765bc.png)

#jspファイル

プログラムの中で```println("<html>");```ってやるのがめんどいのでphpっぽく書く方法。基本的にはサーブレットの中でsetAttributeした物をgetAttributeして色々使う。getAttributeの結果はObjectなのでキャストしないといけない。

```jsp:hoge.jsp
<%@ page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=utf-8"	pageEncoding="utf-8"%>
<% String title=(String)request.getAttribute("title");
  //複数行書ける
%>
<%!
  // メソッドはここに書く
  String getHoge(){
    return "hoge";
  }
%>
<!DOCTYPE html>
<meta charset="utf-8">
<title><%=title%></title>

```

つまり、```<%なんとか%>```で囲った部分でjavaの処理が書けるという事。ループとか条件分岐もこれで囲う。クラスに対してgetterがしっかり実装されている時は```${hoge.kage}```というより短い書き方が出来る。

##jspファイルをincludeする

jspファイルをinclude出来る。注意点として、paramで指定できるvalueは文字列型のみ。オブジェクト駄目。パラメーターが色々あるjspファイルをincludeする時はparamタグの量が凄いことになる。jspの中でも```<%request.setAttribute("key", object);%>```って出来るけど、グローバル変数的な感じでinclude先限定じゃなくなるから扱いが大変。attributeのキーが被ってもeclipse警告してくれないし。child.jsp側では```request.getParameter("")```で文字列を受け取る。俺の環境のEclipseでは自動補完のままjsp:paramタグを作ると、name valueでなくvalue nameの順番で属性値が入力されてしまい、それに気が付かずにnameに変数を入れて怒られる。
valueの項目に、例えば文字結合とかでダブルクオーテーションを使う時は、二重にエスケープする必要がある。複雑な値は```<%String val=xxxx;%>``` で別途変数に入れてからvalue属性として指定したほうがいいね

```jsp:parent.jsp
<jsp:include page="include/child.jsp" >
	<jsp:param name="userId" value="<%=userId%>"/>
	<jsp:param name="userId2" value="<%=userId2%>"/>
	<jsp:param name="userId3" value="<%=(\"./\"+user.getUserId()) %>"/>
</jsp:include>
```

##jspファイルの中で使える変数

jspファイルの中ではrequest変数とかが宣言ナシで使えるけど、宣言ナシで使える変数の一覧。使いみちがあるのはrequest、responce、session、application、config、out くらいかな

```java:xxx_jsp.java
javax.servlet.http.HttpServletRequest request;
javax.servlet.http.HttpServletResponse response;
javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = pageContext.getSession();
javax.servlet.ServletContext application=pageContext.getServletContext();
javax.servlet.ServletConfig config=pageContext.getServletConfig();
javax.servlet.jsp.JspWriter out = pageContext.getOut();
java.lang.Object page = this;
```