見出し画像

「スッキリわかるサーブレット&JSP入門」やってみた


画像1


ソースコード

web付録


目次

第0章 サーブレット/JSPを学ぶにあたって

第Ⅰ部 Webのしくみを知ろう
第1章 HTMLとWebページ
第2章 Webのしくみ

第Ⅱ部 開発の基礎を身に付けよう
第3章 サーブレットの基礎
第4章 JSPの基本
第5章 フォーム

第Ⅲ部 本格的な開発を始めよう
第6章 MVCモデルと処理の遷移
第7章 リクエストスコープ
第8章 セッションスコープ
第9章 アプリケーションスコープ
第10章 アプリケーション作成

第Ⅳ部 応用的な知識を深めよう
第11章 サーブレットクラスの実行のしくみとフィルタ
第12章 アクションタグとEL式
第13章 JDBCプログラムとDAOパターン

第Ⅳ部 設計手法を身に付けよう
第14章 Webアプリケーションの設計

第1章 HTMLとWebページ


第2章 Webのしくみ


第3章 サーブレットの基礎

サーブレットはJavaを使ってサーバ再度プログラムを作るための技術。
サーブレットクラスを開発することでアプリケーションサーバ上で実行できる。
サーブレットクラスはブラウザからのリクエストによって実行され、その実行結果をHTMLで出力する。
出力されたHTMLはアプリケーションサーバによってブラウザにレスポンスされる。

サーブレットの基本形
1)javax.servlet.http.HttpServletを継承する

2)doGetメソッドをオーバーライドする
3)サーブレット関係のクラスをインポートする
Eclipseを使えば自動的にやってくれる

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 HelloServlet extends HttpServlet {

     protected void doGet(HttpServletRequest request,
         HttpServletResponse response)
         throws ServletExceptionIOException {
             response.setContentType("text/html; charset=UTF-8");
             PrintWriter out = response.getWriter();
             out.println("<html>");
             out.println("Hello");
             out.println("</html>");
     }
}

ブラウザからリクエストが届く -> アプリケーションサーバはサーブレットクラスのdoGetを呼び出す。
この時の引数がHttpServletRequest
レスポンスがHttpServletResponse



サーブレットクラスは
HttpServletRequestインスタンスに格納されているリクエストの詳細情報を取り出し、計算などの処理を行う。
HttpServletResponseインスタンスを用いて結果画面のHTML情報をブラウザに送り返す。

Webアプリ特有の処理のほとんどはこの2つのインスタンスを用いて行う


HTMLの出力は、
・Content-Typeヘッダの設定
・PrintWriterのgetWriterを使う
で行う

java.io.PrintWriterをインポート
HttpServletResponseを使う

response.setContentType("text/html; charset=HTMLの文字コード");
             PrintWriter out = response.getWriter();
             out.println("HTMLタグ");



サーブレットクラスのコンパイルとインスタンス化

作成したサーブレットクラスのコンパイルには当然コンパイルとインスタンス化が必要。
ただ、Eclipseなら、
・自動でコンパイルされる。
・サーブレットクラスをリクエストしたらアプリケーションサーバが自動的にインスタンス化を行う。


サーブレットクラスのURLとURLパターンについて

サーブレットクラスにURLパターンを設定したら、ブラウザからリクエストして実行できるようになる。

・URLは以下の形式になる
http://サーバ名/アプリケーション名/URLパターン

・URLパターンは@WebServletアノテーションを使って設定する
@WebServlet("/URLパターン")
「/」から始める
javax.servlet.annotation.WebServletをインポートする

・Eclipse使用時は自動的にクラス名がURLパターンに設定される


URLの入力、またはリンクのクリックでサーブレットクラスをリクエストした場合、doGet()メソッドが実行される。
サーブレットクラスが実行するメソッドは、リクエストメソッドによって決まる。GetリクエストされたらdoGetが、POSTリクエストされたらdoPostメソッドを事項する。


メモ: URLパターンの設定には、アノテーション以外にもweb.xmlという設定ファイルを使用する方法がある。


サンプルクラス

http://localhost:8080/example/SampleServlet にアクセスすれば表示される

package servlet;

// これはEclipseが自動で追加する
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

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

// URLパターンを設定
@WebServlet("/SampleServlet")
public class SampleServlet extends HttpServlet {
 
 // Eclipseでサーブレットクラスを作成すると、このフィールドが定義される
 private static final long serialVersionUID = 1L;

 protected void doGet(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletExceptionIOException {

   // 運勢をランダムで決定
   String[] luckArray = { "超スッキリ""スッキリ""最悪" };
   int index = (int) (Math.random() * 3);
   String luck = luckArray[index];

   // 実行日を取得
   Date date = new Date();
   SimpleDateFormat sdf = new SimpleDateFormat("MM月dd日");
   String today = sdf.format(date);

   // HTMLを出力
   response.setContentType("text/html; charset=UTF-8");
   PrintWriter out = response.getWriter();
   out.println("<html>");
   out.println("<head>");
   out.println("<title>スッキリ占い</title>");
   out.println("</head>");
   out.println("<body>");
   out.println("<p>" + today + "の運勢は「" + luck + "」です</p>");
   out.println("</body>");
   out.println("</html>");
 }
}


第4章 JSPの基本

printlnを毎回書くのは大変なのでJSPを使う。
JSP(JavaServer Pages)はリクエストされるとサーブレットクラスに変換されるのでサーブレットクラスでできることはJSPファイルでも行うことができる。

JSPファイルの特徴

・リクエストして実行する
・サーブレットクラスに変換され、サーブレットクラスと同じことができる
・HTMLの中にjavaコードを埋め込む
・サーブレットクラスより楽にHTMLを出力できる

HTML部分はテンプレート、Javaコード部分はスクリプトという

<% 
  Javaコード
%>





サンプル

SimpleDateFormatをインポートしてtodayを設定
HTMLでtodayを表示

<%@ page language="java" contentType="text/html; charset=UTF-8"
   pageEncoding="UTF-8" %>
<%@ page import="java.util.Date,java.text.SimpleDateFormat" %>
<%
// 運勢をランダムで決定
String[] luckArray = { "超スッキリ""スッキリ""最悪" };
int index = (int) (Math.random() * 3);
String luck = luckArray[index];

// 実行日を取得
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("MM月dd日");
String today = sdf.format(date);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>スッキリ占い</title>
</head>
<body>
<p><%= today %>の運勢は「<%= luck %>」です</p>
</body>
</html>


第5章 フォーム

ブラウザでデータを入力して送信する仕組み

formタグの例

action属性: 送信先を指定
・サーブレットクラスの場合 
  ->  /アプリケーション名/URLパターン
・JSPファイルの場合 
  -> /アプリケーション名/WebContentからのパス

method属性: リクエストメソッドを指定
getかpostを指定する

<form action="送信先" method="リクエストメソッド">

inputタグ

</form>

inputタグの例

テキストボックス
<input type="text" name="名前">

ラジオボタン
<input type="radio" name="名前">



リクエストパラメータの取得

リクエストパラメータはアプリケーションサーバによって、
HttpServletRequestインスタンスに格納され、
・送信先のサーブレットクラスまたはJSPファイルに渡される。


サーブレットやJSPファイルは、HttpServletRequestのメソッドを使用してリクエストパラメータを取り出すことができる。


// 送信元フォームのaction属性で指定されたURLパターンと一致させる必要がある
@WebServlet("/FormSampleServlet")
public class FormSampleServlet extends HttpServlet {

 // method属性で指定されたリクエストメソッドと対応させる必要がある
 protected void doPost(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {

   // 文字コードを指定
   request.setCharacterEncoding("UTF-8");
   
   // リクエストパラメータを取得
   // getParameterでリクエストパラメータの名前を指定し取得する
   String name = request.getParameter("inputタグのname");
   String gender = request.getParameter("inputタグのname");
   
   ...
  }
}

各リクエストメソッドが使われるブラウザの操作は、
・GETなら
 アドレスバーにURLを入力した時
 リンクをクリックした時
 getのフォームの送信ボタンを押した時
・POSTなら
 postのフォームの送信ボタンを押した時




JSPファイルでリクエストパラメータの値を取得する

<%
  request.setCharacterEncoding("UTF-8");
  String name = request.getParameter("name");
  String gender = request.getParameter("gender");
%>


暗黙オブジェクト
(定義済みのため、宣言せずに利用できるオブジェクト)

pageContext
request
response
session
application
など




フォームを使ったプログラムのサンプル

formSample.jsp

http://localhost:8080/example/formSample.jspで名前と性別を入力して、登録ボタンを押す。

FormSampleServlet.javaのdoPostを使う。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ユーザー登録もどき</title>
</head>


<body>
<form action="/example/FormSampleServlet" method="post">
名前:<br>
<input type="text" name="name"><br>
性別:<br>
男<input type="radio" name="gender" value="0">
女<input type="radio" name="gender" value="1">
<input type="submit" value="登録">
</form>
</body>


</html>


リクエストパラメータを取得し登録結果画面をレスポンスする。
リクエストが間違っていたらエラーメッセージが返る。

package servlet;

import java.io.IOException;
import java.io.PrintWriter;

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("/FormSampleServlet")
public class FormSampleServlet extends HttpServlet {

 private static final long serialVersionUID = 1L;

 protected void doPost(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletExceptionIOException {

   // リクエストパラメータを取得
   request.setCharacterEncoding("UTF-8");
   String name = request.getParameter("name");
   String gender = request.getParameter("gender");

   // リクエストパラメータをチェック
   String errorMsg = "";
   if (name == null || name.length() == 0) {
     errorMsg += "名前が入力されていません<br>";
   }
   if (gender == null || gender.length() == 0) {
     errorMsg += "性別が選択されていません<br>";
   } else {
     if (gender.equals("0")) {
       gender = "男性";
     }
     else if (gender.equals("1")) {
       gender = "女性";
     }
   }

   // 表示するメッセージを設定
   String msg = name + "さん(" + gender + ")を登録しました";
   if (errorMsg.length() != 0) {
     msg = errorMsg;
   }

   // HTMLを出力
   response.setContentType("text/html; charset=UTF-8");
   PrintWriter out = response.getWriter();
   out.println("<!DOCTYPE html >");
   out.println("<html>");
   out.println("<head>");
   out.println("<meta charset=\"UTF-8\">");
   out.println("<title>ユーザー登録結果</title>");
   out.println("</head>");
   out.println("<body>");
   out.println("<p>" + msg + "</p>");
   out.println("</body>");
   out.println("</html>");
 }
}


リクエストパラメータの応用

規模が大きいwebアプリでは利用者がフォームの送信ボタンを押した場合などに、利用者自身は何も指定していなくてもリクエストパラメータがこっそりとサーバに送信される仕組みが欲しい時もある。

方法1) hiddenパラメータを使用
フォームにhiddenを入れる。
以下ならhoge=fooというリクエストパラメータが送られる。
request.getParameter("hoge")とすればfooを取得できる。

<input type="hidden" name="hoge" value="foo">


方法2) リクエスト先の指定に「?名前=値」をつける


第6章 MVCモデルと処理の遷移


MVCモデル
Model: 処理やデータの格納を行う。一般的なJavaクラスの役割。
      データモデル(情報の保持)とロジックモデル(処理)がある
View: 表示部分。JSPの役割。
Controller: URLごとのリクエストとレスポンスのルーティングを行う。
                 サーブレットクラスの役割。

これらの要素から以下を行う
1)ユーザーがアプリケーションの提供する機能を要求
2)コントローラが要求を受け付け
3)コントローラがモデルに処理の実行を依頼
4)モデルが処理を実行
5)コントローラが処理結果の表示をビューに依頼
6)ビューがユーザーの要求の結果を表示
7)ユーザーは要求の結果を見る


処理の転送

フォワードとリダイレクトがある。
JSPファイルはJavaのクラスのようにnewで生成して呼び出せないのでフォワードを使う。
サーブレットクラスからJSPファイルにフォワードして出力処理の担当をサーブレットからJSPファイルへ移す。

フォワード:同じアプリ内のサーブレットクラスやJSPファイルに処理を移す
リダイレクト:ブラウザに別のサーブレットクラスやJSPファイルなどを
      リクエストさせる

フォワードの構文

フォワードの指定方法
JSPファイルの場合: /WebContentからのパス
サーブレットクラスの場合: /URLパターン

import javax.servlet.RequestDispatcher;


// フォワード
   RequestDispatcher dispatcher =
       request.getRequestDispatcher("フォワード先");
       
   dispatcher.forward(request, response);

MVCモデルに従ってwebアプリを作るとブラウザからリクエストされるのは基本的にサーブレットクラスになる。
JSPファイルはサーブレットクラスからフォワードされて動くことを前提に作成するので、ブラウザから直接呼び出されるとエラーや不具合が発生することになる。

そこで、フォワード先としてしか利用しないJSPファイルは直接リクエストできないようにする。
具体的には、JSPファイルをWEB-INFディレクトリ以下に配置する。
ブラウザはこのディレクトリ如何に配置されたファイルを直接リクエストできない。


フォワードのプログラムの流れ

・ForwardSampleServlet.java
 リクエストを処理する
・forwardSample.jsp
 フォワード結果画面を出力する


ForwardSampleServletが出力処理を要求

forwardSample.jspが表示





フォワードを行うサーブレットクラス

package servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
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("/ForwardSampleServlet")
public class ForwardSampleServlet extends HttpServlet {
 private static final long serialVersionUID = 1L;

 protected void doGet(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {

   // フォワード
   RequestDispatcher dispatcher =
       request.getRequestDispatcher("/WEB-INF/jsp/forwardSample.jsp");
   dispatcher.forward(request, response);
 }
}

forwardSample.jsp
フォワード先のJSPファイル

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>フォワードのサンプル</title>
</head>
<body>
<h1>フォワードのサンプル</h1>
<p>フォワードされたページです</p>
</body>
</html>



リダイレクト

ブラウザのリクエスト先を変更して処理の転送を行う。

流れ
・ブラウザがリクエストを出すと、サーブレットクラスからレスポンスが返る
・命令を受けたブラウザは、指示された先に自動的に再リクエストを行う
・再リクエストの結果がブラウザに表示される


リダイレクトの構文

response.sendRedirect("リダイレクト先のURL")


package 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("/RedirectSampleServlet")
public class RedirectSampleServlet extends HttpServlet {
 private static final long serialVersionUID = 1L;

 protected void doGet(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {
   // リダイレクト
   response.sendRedirect("/example/SampleServlet");
 }
}


フォワードとリダイレクトの動作と転送の比較
転送元と転送先のアプリケーションが「別」の場合はリダイレクトを使うしかない。
転送元/先のアプリケーションが「同じ」なら、フォワード/リダイレクトの両方が使用できる

フォワード
・同じアプリケーション内のサーブレットクラスやJSPファイルに処理を移す。
・リクエスト/レスポンスは1往復する

リダイレクト

・ブラウザに別のサーブレットクラスやJSPファイルをリクエストさせ、実行しなおす
・リクエスト/レスポンスは2往復する


第7章 リクエストスコープ

webアプリケーションでは、あるサーブレットクラスで生成したインスタンスを別のサーブレットクラスやJSPファイルで利用したい場面が多くある。


しかし、
それぞれのサーブレットクラスやJSPファイルは独立したもの
-> フォワード元のサーブレットクラスで生成したインスタンスの名前を
    フォワード先のJSPファイルで指定しても利用できない

利用するには、
スコープ(インスタンスを保存できる領域)を経由させることで、サーブレットクラスとJSPファイルの間でインスタンスを共有する。



スコープの種類
・ページスコープ
・リクエストスコープ
・セッションスコープ

スコープに保存するものはインスタンスだけ。intやdouble型など基本データ型はできない。ラッパークラスはできる。
StringやIntegerなど通常のクラスのインスタンスを保存できるが、基本はJavaBeansというクラスのインスタンスを保存する。

JavaBeansは以下の特徴を持つ
・java.io.Serializableを実装して直列化可能
・クラスpublicでパッケージに所属する
・publicの引数のないコンストラクタを持つ
・フィールドはカプセル化する
・命名規則に従ったgetter/setterを持つ

役割は、関連する複数の情報をひとまとめに保存すること

サーブレットクラスでは、
複数の情報をJavaBeansに格納し、そのJavaBeansをスコープに保存する。

JSPファイルで、
JavaBeansをスコープから取り出し、複数の情報をまとめて受け取る。

package model;

import java.io.Serializable;

public class Human implements Serializable {
 private String name;

 public Human() {
   
 }

 public String getName() {
    return this.name;
 }

 public void setName(String name) {
    this.name = name;
 }
}



リクエストスコープの基礎
HttpServletRequestがこれにあたる。
これはリクエストごとに生成されるスコープ。
スコープに保存したインスタンスは、レスポンスが返されるまで利用できる


Human human = new Human("XXXXXX");

// リクエストスコープにインスタンスを保存
request.setAttribute("human", human);

// リクエストスコープからインスタンスを取得
Human h = (Human) request.getAttirbute("human");

JSPファイル側でこうする

<%
// リクエストスコープからインスタンスを取得
Human h = (Human) request.getAttirbute("human");
%>




<%= h.getName() %>


・インスタンスの保存はsetAttribute
・インスタンスの取得はgetAttribute
・保存したインスタンスはブラウザにレスポンスが返されるまで使用できる。
・保存したインスタンスはリクエストをまたいで使用できない。
・保存したインスタンスはフォワード先で取得できるが、リダイレクト先では一度レスポンスが返されるため取得できない。




サンプルプログラム) 
http://localhost:8080/example/HealthCheck 

健康診断情報を保持するJavaBeans


package model;

import java.io.Serializable;

public class Health implements Serializable {
 private double height, weight, bmi;
 private String bodyType;

 public double getHeight() {
   return height;
 }

 public void setHeight(Double height) {
   this.height = height;
 }

 public double getWeight() {
   return weight;
 }

 public void setWeight(Double weight) {
   this.weight = weight;
 }

 public void setBmi(Double bmi) {
   this.bmi = bmi;
 }

 public double getBmi() {
   return this.bmi;
 }

 public void setBodyType(String bodyType) {
   this.bodyType = bodyType;
 }

 public String getBodyType() {
   return this.bodyType;
 }
}


健康診断の処理を行うロジッククラス


package model;

public class HealthCheckLogic {
 public void execute(Health health) {
   // BMIを算出して設定
   double weight = health.getWeight();
   double height = health.getHeight();
   double bmi = weight / (height / 100.0 * height / 100.0);
   health.setBmi(bmi);

   // BMI指数から体格を判定して設定
   String bodyType;
   if (bmi < 18.5) {
     bodyType = "痩せ型";
   } else if (bmi < 25) {
     bodyType = "普通";
   } else {
     bodyType = "肥満";
   }
   health.setBodyType(bodyType);
 }
}

コントローラ


package servlet;

import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import model.Health;
import model.HealthCheckLogic;

@WebServlet("/HealthCheck")
public class HealthCheck extends HttpServlet {
 private static final long serialVersionUID = 1L;

 protected void doGet(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {

   // フォワード
   RequestDispatcher dispatcher =
       request.getRequestDispatcher
           ("/WEB-INF/jsp/healthCheck.jsp");
   dispatcher.forward(request, response);
 }

 protected void doPost(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {

   // リクエストパラメータを取得
   String weight = request.getParameter("weight"); // 体重
   String height = request.getParameter("height"); // 身長

   // 入力値をプロパティに設定
   Health health = new Health();
   health.setHeight(Double.parseDouble(height));
   health.setWeight(Double.parseDouble(weight));
   // 健康診断を実行し結果を設定
   HealthCheckLogic healthCheckLogic = new HealthCheckLogic();
   // 参照渡し
   // インスタンスを引数で渡さした場合、呼び出し先のメソッドで引数のインスタンを変更すると呼び出し元のインスタンスも変更される
   // executeメソッドではsetBodyTypeを使っているので設定した値に変更される
   healthCheckLogic.execute(health);

   // リクエストスコープに保存
   request.setAttribute("health", health);

   // フォワード
   RequestDispatcher dispatcher =
       request.getRequestDispatcher
           ("/WEB-INF/jsp/healthCheckResult.jsp");
   dispatcher.forward(request, response);
 }
}


健康診断画面

healthCheck.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>スッキリ健康診断</title>
</head>
<body>
<h1>スッキリ健康診断</h1>
<form action="/example/HealthCheck" method="post">
身長:<input type="text" name="height">(cm)<br>
体重:<input type="text" name="weight">(kg)<br>
<input type="submit" value="診断">
</form>
</body>
</html>

健康診断結果

healthCheckResult.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ page import="model.Health" %>
<%
// リクエストスコープに保存されたHealthを取得
Health health = (Health) request.getAttribute("health");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>スッキリ健康診断</title>
</head>
<body>
<h1>スッキリ健康診断の結果</h1>
<p>
身長:<%= health.getHeight() %><br>
体重:<%= health.getWeight() %><br>
BMI:<%= health.getBmi() %><br>
体格:<%= health.getBodyType() %>
</p>
<a href="/example/HealthCheck">戻る</a>
</body>
</html>


第8章 セッションスコープ

保存したインスタンスがリクエストをまたげないのは不便なのでセッションスコープを使う。
セッションスコープでは保存したインスタンスの有効期間は開発者が決めることが出来る。

リクエストスコープはHttpServletRequestを使ったが、セッションスコープはHttpSessionを使う。

Human human = new Human();
human.setName("XXX");

// HttpSessionインスタンスの取得
HttpSession session = request.getSession();

// セッションスコープにインスタンスを保存
session.setAttribute("human",human);

// セッションスコープからインスタンスを取得
Human h = (Human) session.getAttribute("human");

// セッションスコープからインスタンスを削除
session.removeAttribute("human");


セッションスコープに保存


session.setAttibute("属性名", インスタンス)

セッションスコープからインスタンスを取得


取得するインスタンスの型 変数名 = (取得するインスタンスの型) session.getAttibute("属性名")

セッションスコープからインスタンスを削除

session.removeAttribute("属性名")


セッションスコープに格納されたインスタンスを取得するJSPファイル

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ page import="model.Health" %>
<%
Human h = (Human) session.getAttibute("human");
%>
<!DOCTYPE html>
<%= h.getName() %>


セッションスコープを使ったプログラムの作成)
ユーザー登録機能


3つの画面から成る。
・ユーザー登録入力画面
・ユーザー登録確認画面
・ユーザー登録完了画面


User.java 登録するユーザー情報

package model;

import java.io.Serializable;

public class User implements Serializable {
 private String id;
 private String name;
 private String pass;

 public User() {
 }

 public User(String id, String name, String pass) {
   this.id = id;
   this.name = name;
   this.pass = pass;
 }

 public String getId() {
   return id;
 }

 public String getPass() {
   return pass;
 }

 public String getName() {
   return name;
 }
}

RegisterUserLogic.java ユーザー登録処理のロジッククラス

package model;

public class RegisterUserLogic {
 public boolean execute(User user) {
   // 登録処理(サンプルでは登録処理を行わない)
   return true;
 }
}

RegisterUser.java コントローラクラス

doGetはリクエストパラメータによpる処理の振り分けをしている。
1つの実行メソッドに対してリクエスト元が複数ある場合はその実行メソッドではどこからのリクエストかを判断する必要がある。
リクエストパラメータはその判断材料となる。

下記では、actionというリクエストパラメータでリクエスト元を判断している。
RegisterUserをGetリクエストするときに、
・プログラムの開始時ならactionの値を送信しない
・登録時にはdoneを設定して送信する
ここから、
actionの値がnull -> リクエスト元はプログラム開始時のリクエスト
done -> ユーザー情報登録時のリクエスト
と判断できる。

package servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import model.RegisterUserLogic;
import model.User;

@WebServlet("/RegisterUser")
public class RegisterUser extends HttpServlet {
 private static final long serialVersionUID = 1L;

 protected void doGet(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {

   // フォワード先
   String forwardPath = null;

   // サーブレットクラスの動作を決定する「action」の値を
   // リクエストパラメータから取得
   String action = request.getParameter("action");

   // 「登録の開始」をリクエストされたときの処理
   if (action == null) {
     // フォワード先を設定
     forwardPath = "/WEB-INF/jsp/registerForm.jsp";
   }

   // 登録確認画面から「登録実行」をリクエストされたときの処理
   else if (action.equals("done")) {
     // セッションスコープに保存された登録ユーザ
     // doPostでセッションスコープに保存していた
     HttpSession session = request.getSession();
     User registerUser = (User) session.getAttribute("registerUser");

     // 登録処理の呼び出し
     RegisterUserLogic logic = new RegisterUserLogic();
     logic.execute(registerUser);

     // 不要となったセッションスコープ内のインスタンスを削除
     session.removeAttribute("registerUser");

     // 登録後のフォワード先を設定
     forwardPath = "/WEB-INF/jsp/registerDone.jsp";
   }

   // 設定されたフォワード先にフォワード
   RequestDispatcher dispatcher =
       request.getRequestDispatcher(forwardPath);
   dispatcher.forward(request, response);
 }

 protected void doPost(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {

   // リクエストパラメータの取得
   request.setCharacterEncoding("UTF-8");
   String id = request.getParameter("id");
   String name = request.getParameter("name");
   String pass = request.getParameter("pass");

   // 登録するユーザーの情報を設定
   User registerUser = new User(id, name, pass);

   // セッションスコープに登録ユーザーを保存
   HttpSession session = request.getSession();
   session.setAttribute("registerUser", registerUser);

   // フォワード
   RequestDispatcher dispatcher =
       request.getRequestDispatcher
           ("/WEB-INF/jsp/registerConfirm.jsp");

   // 登録ユーザーの情報を削除
   dispatcher.forward(request, response);
 }
}

registerForm.jsp ユーザー登録入力画面

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ユーザー登録</title>
</head>
<body>
<form action="/example/RegisterUser" method="post">
ログインID:<input type="text" name="id"><br>
パスワード:<input type="password" name="pass"><br>
名前:<input type="text" name="name"><br>
<input type="submit" value="確認">
</form>
</body>
</html>


registerConfirm.jsp ユーザー登録確認画面

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ page import="model.User" %>
<%
User registerUser = (User) session.getAttribute("registerUser");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ユーザー登録</title>
</head>
<body>
<p>下記のユーザーを登録します</p>
<p>
ログインID:<%= registerUser.getId() %><br>
名前:<%= registerUser.getName() %><br>
</p>
<a href="/example/RegisterUser">戻る</a>
<a href="/example/RegisterUser?action=done">登録</a>
</body>
</html>

registerDone.jsp ユーザー登録完了画面

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ユーザー登録</title>
</head>
<body>
<p>登録完了しました</p>
<a href="/example/RegisterUser">戻る</a></body>
</html>


ここの処理の最大のポイントはUserインスタンスの扱い。
UserインスタンスはRegisterUser#doPostで作成される。

// リクエストパラメータの取得
request.setCharacterEncoding("UTF-8");
String id = request.getParameter("id");
String name = request.getParameter("name");
String pass = request.getParameter("pass");

// 登録するユーザーの情報を設定
User registerUser = new User(id, name, pass);



このインスタンスの情報はユーザー登録確認画面のリクエスト先のRegisterUserでも利用できないといけないのでセッションスコープに保存しないといけない。

<a href="/example/RegisterUser">戻る</a>
<a href="/example/RegisterUser?action=done">登録</a>

UserインスタンスはRegisterUserLogicの呼び出し時に使われる。

HttpSession session = request.getSession();
User registerUser = (User) session.getAttribute("registerUser");

// 登録処理の呼び出し
RegisterUserLogic logic = new RegisterUserLogic();
logic.execute(registerUser);


セッションスコープの仕組み

HttpSessionインスタンスはユーザー(ブラウザ)ごとに作成される。

・アプリケーションサーバは、HttpSessionインスタンスを作成すると、
内部でセッションIDを発行し、HttpSessionインスタンスとブラウザに設定する。

・セッションIDを設定されたブラウザは以降のリクエストのたびに設定されたセッションIDを送信するようになる。

・アプリケーションサーバは送られてきたセッションIDを取得し、2回目以降のgetSessionメソッドを実行する際に、取得したセッションIDと同じIDを持つHttpSessionインスタンスを取得する。


このような仕組みにより、各ユーザーは自分専用のセッションスコープを使うことが出来る。


セッションIDとクッキー

セッションIDをサーバとブラウザ間でやり取りするにはクッキーを使う。

クッキー: webサーバがブラウザにデータを保存、送信させる仕組み

アプリケーションサーバはクッキーにセッションIDを含めることで、
ブラウザ/サーバ間でのセッションIDのやり取りを可能にする

クッキーは有効期限を設定することが出来る。
ただし特に設定しないと、アプリケーションサーバはブラウザが閉じられるまでがセッションIDの有効期限となる。


セッションスコープの注意点

セッションスコープを多用しすぎると、アプリケーションサーバがメモリ不足になり性能の低下やサーバの停止を引き起こす。
(アプリケーションサーバはユーザーを見れないのでブラウザを閉じられたことに気づかず、HttpSessionインスタンスはブラウザから使われない状態になってもすぐには消滅せずガベージコレクションの対象にならないため)

アプリケーションサーバはそこで、一定時間利用されていないHttpSessionインスタンスは不要と判断しガベージコレクションの対象とする
(セッションタイムアウト)
Tomcatならデフォルトで30分に設定されている。


セッションタイムアウトがあっても短時間にリクエストが集中するとガベージコレクションが間に合わずメモリがパンクする。
なので、開発者自身がセッションスコープに格納するインスタンスを積極的に管理する必要がある。


セッションスコープを破棄する
これはamazonとかでユーザーがログアウトした時などに行う。
これでログイン中のユーザーIDやカートに入れた商品をクリアできる。

session.invalidate();


セッションスコープと直列化

直列化: java.io.Serializableで行う
    インスタンスのフィールドの内容をバイト列に変換して
   ファイルなどに保存し、それをまたインスタンスに復元すること

アプリケーションサーバは停止時にセッションスコープ内のインスタンスを直列化してファイルに保存し、再起動時にはそのファイルからインスタンスの復元を行う。

割り当てられたメモリがいっぱいになっても、アプリケーションサーバはセッションスコープのインスタンスを直列化してメモリからファイルへ対比させることができる。

セッションスコープ内のインスタンスは直列化されることがあるので、java.io.Serializableインタフェースを実装して直列化ができるようにする必要がある。これをしないと、NotSerializableExceptionが発生する。


第9章 アプリケーションスコープ

アプリケーションスコープは、1つのアプリケーションにつき1つ作成されるスコープ。

アプリケーションスコープに保存したインスタンスは、
webアプリケーションが終了するまでの間
アプリケーション内の全てのサーブレットクラスとJSPファイルで利用できる。


webアプリケーションの開始と終了の例

・サーバの起動と停止
・オートリロード機能
・管理ツールによる開始と終了


使い方
アプリケーションスコープのjavax.servlet.ServletContextインタフェースを使う。

Human human = new Human();
human.setName("XXX");

// ServletContextインスタンスの取得
ServletContext application = this.getServletContext();

// アプリケーションスコープにインスタンスを保存
application.setAttribute("human",human);

// アプリケーションスコープからインスタンスを取得
Human h = (Human) application.getAttribute("human");

// アプリケーションスコープからインスタンスを削除
application.removeAttribute("human");


アプリケーションスコープの取得

ServletContext application = this.getServletContext();

アプリケーションスコープに保存する

application.setAttribute("属性名",インスタンス);

アプリケーションスコープからインスタンスを取得する

取得するインスタンスの型 変数名 = (取得するインスタンスの型) application.getAttribute("属性名");

アプリケーションスコープからインスタンスを削除する

application.removeAttribute("属性名");


アプリケーションスコープに格納されたインスタンスを取得するJSPファイル

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ page import="model.Human" %>
<%
// アプリケーションスコープからインスタンスを取得
Human h = (Human) application.getAttribute("human");
%>


<%= h.getName() %>



サンプルプログラム) 評価ボタン機能

・SiteEV.java: サイト評価に関する情報のJavaBeans
・SiteEVLogic.java: 評価処理を行うロジッククラス
・minatoIndex.jsp: トップ画面
・MinatoIndex.java: コントローラ

サイト評価の情報はアプリケーションスコープに保存する。
MinatoIndexはリクエストパラメータactionの値が、likeなら「いいね」、dislikeなら「よくないね」、がクリックされたと判断し、サイトの評価を変更する。



SiteEV.java

いいね、よくないね、を格納

package model;

public class SiteEV {
 private int like; // よいねの数
 private int dislike; // よくないねの数

 public SiteEV() {
   like = 0;
   dislike = 0;
 }

 public int getLike() {
   return like;
 }

 public void setLike(int like) {
   this.like = like;
 }

 public int getDislike() {
   return dislike;
 }

 public void setDislike(int dislike) {
   this.dislike = dislike;
 }
}

SiteEVLogic.java
いいね、よくねいね、の数を数える

package model;

public class SiteEVLogic {
 public void like(SiteEV site) {
   int count = site.getLike();
   site.setLike(count + 1);
 }

 public void dislike(SiteEV site) {
   int count = site.getDislike();
   site.setDislike(count + 1);
 }
}


MinatoIndex.java

package servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import model.SiteEV;
import model.SiteEVLogic;

@WebServlet("/MinatoIndex")
public class MinatoIndex extends HttpServlet {
 private static final long serialVersionUID = 1L;

 protected void doGet(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {

   // アプリケーションスコープに保存されたサイト評価を取得
   ServletContext application = this.getServletContext();
   SiteEV siteEV = (SiteEV) application.getAttribute("siteEV");

   // サイト評価の初期化(初回リクエスト時実行)
   if (siteEV == null) {
     siteEV = new SiteEV();
   }

   // リクエストパラメータの取得
   request.setCharacterEncoding("UTF-8");
   String action = request.getParameter("action");

   // サイトの評価処理(初回リクエスト時は実行しない)
   SiteEVLogic siteEVLogic = new SiteEVLogic();
   if (action != null && action.equals("like")) {
     siteEVLogic.like(siteEV);
   } else if (action != null && action.equals("dislike")) {
     siteEVLogic.dislike(siteEV);
   }

   // アプリケーションスコープにサイト評価を保存
   application.setAttribute("siteEV", siteEV);

   // フォワード
   RequestDispatcher dispatcher =
       request.getRequestDispatcher
           ("/WEB-INF/jsp/minatoIndex.jsp");
   dispatcher.forward(request, response);
 }
}


minatoIndex.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ page import="model.SiteEV" %>
<%
SiteEV siteEV = (SiteEV) application.getAttribute("siteEV");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>湊くんのページ</title>
</head>
<body>
<h1>湊くんのページへようこそ</h1>
<p>
<a href="/example/MinatoIndex?action=like">よいね</a>:
<%= siteEV.getLike() %>人
<a href="/example/MinatoIndex?action=dislike">よくないね</a>:
<%= siteEV.getDislike() %>人
</p>
<h2>湊くんとは!? </h2>
<p>・・・</p>
</body>
</html>



アプリケーションスコープの注意点

・保存したインスタンスの更新を複数のユーザーがほぼ同時に行った場合、不整合が発生することがある
・アプリケーションスコープを削除、またはアプリケーションを終了するまで、メモリにインスタンスが残るので、大量のインスタンスを保存するとメモリを圧迫する
・保存したインスタンスはアプリケーションが終了すると失われる


第10章 アプリケーション作成



作成するアプリの機能
1)ログイン
2)ログアウト
3)つぶやき投稿
4)つぶやき閲覧


画面は、
・トップ画面
・ログイン結果
・メイン
・ログアウト
となる。


JavaBeansの作成

まずはこれを行う。

User.java
 ユーザーの情報を持つ
Mutter.java
 つぶやき情報を持つ

User.java

package model;

import java.io.Serializable;

public class User implements Serializable {
 private String name; // ユーザー名
 private String pass; // パスワード

 public User() {
 }

 public User(String name, String pass) {
   this.name = name;
   this.pass = pass;
 }

 public String getName() {
   return name;
 }

 public String getPass() {
   return pass;
 }
}

Mutter.java

package model;

import java.io.Serializable;

public class Mutter implements Serializable {
 private String userName; // ユーザー名
 private String text; // つぶやき内容

 public Mutter() {
 }

 public Mutter(String userName, String text) {
   this.userName = userName;
   this.text = text;
 }

 public String getUserName() {
   return userName;
 }

 public String getText() {
   return text;
 }
}


トップ画面のJSPファイル
index.jsp

<%-- リスト10-3の状態 --%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>どこつぶ</title>
</head>
<body>
<h1>どこつぶへようこそ</h1>
</body>
</html>


次にログイン機能を作成する。

ここでは、以下を作成する。

LoginLogic.java
 ログイン処理を行う
index.jsp
 上記を修正する

ログイン機能は
トップ画面を表示し、ユーザー名とパスワードを入力し、
・成功なら、ログイン結果画面が表示され、ログイン成功のメッセージを表示する
・失敗なら、ログイン結果画面が表示され、失敗のメッセージがでる
(セッションスコープを使う)


LoginLogic.java

package model;

public class LoginLogic {
 public boolean execute(User user) {
   if (user.getPass().equals("1234")) {
     return true;
   }
   return false;
 }
}

Login.java
ログインのコントローラクラス

package servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import model.LoginLogic;
import model.User;

@WebServlet("/Login")
public class Login extends HttpServlet {
 private static final long serialVersionUID = 1L;

 protected void doPost(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {

   // リクエストパラメータの取得
   request.setCharacterEncoding("UTF-8");
   String name = request.getParameter("name");
   String pass = request.getParameter("pass");

   // Userインスタンス(ユーザー情報)の生成
   User user = new User(name, pass);

   // ログイン処理
   LoginLogic loginLogic = new LoginLogic();
   boolean isLogin = loginLogic.execute(user);

   // ログイン成功時の処理
   if (isLogin) {
     // ユーザー情報をセッションスコープに保存
     HttpSession session = request.getSession();
     session.setAttribute("loginUser", user);
   }
   // ログイン結果画面にフォワード
   RequestDispatcher dispatcher =
       request.getRequestDispatcher
           ("/WEB-INF/jsp/loginResult.jsp");
   dispatcher.forward(request, response);
 }
}

index.jsp
ユーザー名とパスワードのフォーム追加

<%-- リスト10-6の状態 --%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>どこつぶ</title>
</head>
<body>
<h1>どこつぶへようこそ</h1>
<form action="/docoTsubu/Login" method="post">
ユーザー名:<input type="text" name="name"><br>
パスワード:<input type="password" name="pass"><br>
<input type="submit" value="ログイン">
</form>
</body>
</html>

loginResult.jsp
ログイン結果画面

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ page import="model.User" %>
<%
// セッションスコープからユーザー情報を取得
User loginUser = (User) session.getAttribute("loginUser");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>どこつぶ</title>
</head>
</head>
<body>
<h1>どこつぶログイン</h1>
<% if(loginUser != null) { %>
<p>ログインに成功しました</p>
<p>ようこそ<%= loginUser.getName() %>さん</p>
<a href="/docoTsubu/Main">つぶやき投稿・閲覧へ</a>
<% } else { %>
<p>ログインに失敗しました</p>
<a href="/docoTsubu/">TOPへ</a>
<% } %>
</body>
</html>


次は、メイン画面の表示を行う。つまりここでユーザーのつぶやきを表示する。
つぶやきはアプリケーションスコープ内に保存する。

MainはセッションスコープのUserインスタンスの取得を試みて、ユーザーがログインしているかを確認する。取得できたらmain.jspにフォワードしてメイン画面を出力する。

Userインスタンスがセッションスコープから取得できないなら、ログインせずにリクエストしていると判定し、トップ画面にリダイレクトする。


Main.java

// リスト10-8の状態
package servlet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import model.Mutter;
import model.User;

@WebServlet("/Main")
public class Main extends HttpServlet {
 private static final long serialVersionUID = 1L;

 protected void doGet(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletExceptionIOException {

   // つぶやきリストをアプリケーションスコープから取得
   ServletContext application = this.getServletContext();
   List<Mutter> mutterList =
       (List<Mutter>) application.getAttribute("mutterList");
   // 取得できなかった場合は、つぶやきリストを新規作成して
   // アプリケーションスコープに保存
   if (mutterList == null) {
     mutterList = new ArrayList<>();
     application.setAttribute("mutterList", mutterList);
   }
   // ログインしているか確認するため
   // セッションスコープからユーザー情報を取得
   HttpSession session = request.getSession();
   User loginUser = (User) session.getAttribute("loginUser");
   if (loginUser == null) { // ログインしていない場合
   // リダイレクト
   // デフォルトページへリダイレクトとなる
     response.sendRedirect("/docoTsubu/");
   } else { // ログイン済みの場合
   // フォワード
     RequestDispatcher dispatcher =
         request.getRequestDispatcher("/WEB-INF/jsp/main.jsp");
     dispatcher.forward(request, response);
   }
 }
}

main.jsp

<%-- リスト10-9の状態 --%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ page import="model.User" %>
<%
// セッションスコープに保存されたユーザー情報を取得
User loginUser = (User) session.getAttribute("loginUser");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>どこつぶ</title>
</head>
<body>
<h1>どこつぶメイン</h1>
<p>
<%= loginUser.getName() %>さん、ログイン中
</p>
</body>
</html>


次に、ログアウト機能を作成する

Logout.java

package servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet("/Logout")
public class Logout extends HttpServlet {
 private static final long serialVersionUID = 1L;

 protected void doGet(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {
   // セッションスコープを破棄
   HttpSession session = request.getSession();
   session.invalidate();

   // ログアウト画面にフォワード
   RequestDispatcher dispatcher =
       request.getRequestDispatcher("/WEB-INF/jsp/logout.jsp");
   dispatcher.forward(request, response);
 }
}

main.jsp

User loginUser = (User) session.getAttribute("loginUser"); が追加されている

またログアウトのリンクもある

<%-- リスト10-11の状態 --%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ page import="model.User" %>
<%
// セッションスコープに保存されたユーザー情報を取得
User loginUser = (User) session.getAttribute("loginUser");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>どこつぶ</title>
</head>
<body>
<h1>どこつぶメイン</h1>
<p>
<%= loginUser.getName() %>さん、ログイン中
<a href="/docoTsubu/Logout">ログアウト</a>
</p>
</body>
</html>


logout.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>どこつぶ</title>
</head>
</head>
<body>
<h1>どこつぶログアウト</h1>
<p>ログアウトしました</p>
<a href="/docoTsubu/">トップへ</a>
</body>
</html>


つぶやき投稿と閲覧機能を追加する


PostMutterLogic.java
つぶやきの投稿に関する処理を行うモデル


package model;

import java.util.List;

public class PostMutterLogic {
 public void execute(Mutter mutter, List<Mutter> mutterList) {
   mutterList.add(0, mutter); // 先頭に追加 解説①
 }
}

Main.java
doPostを追加

package servlet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import model.Mutter;
import model.PostMutterLogic;
import model.User;

@WebServlet("/Main")
public class Main extends HttpServlet {
 private static final long serialVersionUID = 1L;

 protected void doGet(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {

   // つぶやきリストをアプリケーションスコープから取得
   ServletContext application = this.getServletContext();
   List<Mutter> mutterList =
       (List<Mutter>) application.getAttribute("mutterList");
   // 取得できなかった場合は、つぶやきリストを新規作成して
   // アプリケーションスコープに保存
   if (mutterList == null) {
     mutterList = new ArrayList<>();
     application.setAttribute("mutterList", mutterList);
   }
   // ログインしているか確認するため
   // セッションスコープからユーザー情報を取得
   HttpSession session = request.getSession();
   User loginUser = (User) session.getAttribute("loginUser");
   if (loginUser == null) { // ログインしていない場合
     // リダイレクト
     response.sendRedirect("/docoTsubu/");
   } else { // ログイン済みの場合
     // フォワード
     RequestDispatcher dispatcher =
         request.getRequestDispatcher("/WEB-INF/jsp/main.jsp");
     dispatcher.forward(request, response);
   }
 }

 protected void doPost(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {

   // リクエストパラメータの取得
   request.setCharacterEncoding("UTF-8");
   String text = request.getParameter("text");

   // 入力値チェック
   if (text != null && text.length() != 0) {
     // アプリケーションスコープに保存されたつぶやきリストを取得
     ServletContext application = this.getServletContext();
     List<Mutter> mutterList =
         (List<Mutter>) application.getAttribute("mutterList");

     // セッションスコープに保存されたユーザー情報を取得
     HttpSession session = request.getSession();
     User loginUser = (User) session.getAttribute("loginUser");

     // つぶやきをつぶやきリストに追加
     Mutter mutter = new Mutter(loginUser.getName(), text);
     PostMutterLogic postMutterLogic = new PostMutterLogic();
     postMutterLogic.execute(mutter, mutterList);

     // アプリケーションスコープにつぶやきリストを保存
     application.setAttribute("mutterList", mutterList);
   }

   // メイン画面にフォワード
   RequestDispatcher dispatcher =
       request.getRequestDispatcher("/WEB-INF/jsp/main.jsp");

   dispatcher.forward(request, response);
 }
}

main.jsp

<%-- リスト10-15の状態 --%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ page import="model.User,model.Mutter,java.util.List" %>
<%
// セッションスコープに保存されたユーザー情報を取得
User loginUser = (User) session.getAttribute("loginUser");
// アプリケーションスコープに保存されたつぶやきリストを取得
List<Mutter> mutterList =
(List<Mutter>) application.getAttribute("mutterList");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>どこつぶ</title>
</head>
<body>
<h1>どこつぶメイン</h1>
<p>
<%= loginUser.getName() %>さん、ログイン中
<a href="/docoTsubu/Logout">ログアウト</a>
</p>
<p><a href="/docoTsubu/Main">更新</a></p>
<form action="/docoTsubu/Main" method="post">
<input type="text" name="text">
<input type="submit" value="つぶやく">
</form>
<% for(Mutter mutter : mutterList) {%>
 <p><%= mutter.getUserName() %>:<%= mutter.getText() %></p>
<% } %>
</body>
</html>


エラーメッセージの表示機能

Main.java

doPostにelseを追加している。

package servlet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import model.Mutter;
import model.PostMutterLogic;
import model.User;

@WebServlet("/Main")
public class Main extends HttpServlet {
 private static final long serialVersionUID = 1L;

 protected void doGet(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {

   // つぶやきリストをアプリケーションスコープから取得
   ServletContext application = this.getServletContext();
   List<Mutter> mutterList =
       (List<Mutter>) application.getAttribute("mutterList");
   // 取得できなかった場合は、つぶやきリストを新規作成して
   // アプリケーションスコープに保存
   if (mutterList == null) {
     mutterList = new ArrayList<>();
     application.setAttribute("mutterList", mutterList);
   }
   // ログインしているか確認するため
   // セッションスコープからユーザー情報を取得
   HttpSession session = request.getSession();
   User loginUser = (User) session.getAttribute("loginUser");
   if (loginUser == null) { // ログインしていない場合
     // リダイレクト
     response.sendRedirect("/docoTsubu/");
   } else { // ログイン済みの場合
     // フォワード
     RequestDispatcher dispatcher =
         request.getRequestDispatcher("/WEB-INF/jsp/main.jsp");
     dispatcher.forward(request, response);
   }
 }

 protected void doPost(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {

   // リクエストパラメータの取得
   request.setCharacterEncoding("UTF-8");
   String text = request.getParameter("text");

   // 入力値チェック
   if (text != null && text.length() != 0) {
     // アプリケーションスコープに保存されたつぶやきリストを取得
     ServletContext application = this.getServletContext();
     List<Mutter> mutterList =
         (List<Mutter>) application.getAttribute("mutterList");

     // セッションスコープに保存されたユーザー情報を取得
     HttpSession session = request.getSession();
     User loginUser = (User) session.getAttribute("loginUser");

     // つぶやきをつぶやきリストに追加
     Mutter mutter = new Mutter(loginUser.getName(), text);
     PostMutterLogic postMutterLogic = new PostMutterLogic();
     postMutterLogic.execute(mutter, mutterList);

     // アプリケーションスコープにつぶやきリストを保存
     application.setAttribute("mutterList", mutterList);
   } else {
     //エラーメッセージをリクエストスコープに保存
     request.setAttribute("errorMsg""つぶやきが入力されていません");
   }

   // メイン画面にフォワード

   RequestDispatcher dispatcher =
       request.getRequestDispatcher("/WEB-INF/jsp/main.jsp");

   dispatcher.forward(request, response);
 }
}

main.jsp

errorMsgが追加されている

<%-- リスト10-17の状態 --%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ page import="model.User,model.Mutter,java.util.List" %>
<%
// セッションスコープに保存されたユーザー情報を取得
User loginUser = (User) session.getAttribute("loginUser");
// アプリケーションスコープに保存されたつぶやきリストを取得
List<Mutter> mutterList =
(List<Mutter>) application.getAttribute("mutterList");
// リクエストスコープに保存されたエラーメッセージを取得
String errorMsg = (String) request.getAttribute("errorMsg");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>どこつぶ</title>
</head>
<body>
<h1>どこつぶメイン</h1>
<p>
<%= loginUser.getName() %>さん、ログイン中
<a href="/docoTsubu/Logout">ログアウト</a>
</p>
<p><a href="/docoTsubu/Main">更新</a></p>
<form action="/docoTsubu/Main" method="post">
<input type="text" name="text">
<input type="submit" value="つぶやく">
</form>
<% if(errorMsg != null){ %>
<p><%= errorMsg %></p>
<% } %>
<% for(Mutter mutter : mutterList){%>
<p><%=mutter.getUserName()%>:<%=mutter.getText()%></p>
<% } %>
</body>
</html>


第11章 サーブレットクラスの実行のしくみとフィルタ



サーブレットクラスが実行される仕組み

doGetなどのメソッドを動かすことをサーブレットクラスを実行としているが、これらを実行するにはインスタンス化を行う必要がある(アプリケーションサーバが実行しているので開発者は行わない)


・通常のクラス -> newでインスタンス化を行う
・サーブレットクラス -> ブラウザからリクエストされたときに、アプリケーションサーバ内のサーブレットコンテナによってインスタンス化される

インスタンスの生成は負荷がかかるのでアプリケーションサーバはリクエスト応答後もサーブレットクラスのインスタンスを破棄せずメモリに残し次のリクエストでも利用する。
ただし、サーバの終了などでwebアプリケーションが終了したら破棄される。
インスタンスの生成と破棄にはinitとdestroyメソッドを使う

実行されるタイミング
init: サーブレットクラスのインスタンスが作成された直後
destroy: インスタンスが破棄される直前


public void init(ServletConfig config) throws ServletException {

   super.init(config); // これを書かないとだめ

  

   ...
  }


public void destroy() {
   // 最後に一度だけ実行される処理
 }


init/destroyを持つサーブレットクラスのサンプル

package servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
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("/CounterServlet")
public class CounterServlet extends HttpServlet {
 private static final long serialVersionUID = 1L;

 public void init(ServletConfig config) throws ServletException {

   super.init(config);

   // 訪問回数を表すIntegerインスタンスを新規作成し
   // アプリケーションスコープに保存
   Integer count = 0;
   ServletContext application = config.getServletContext();
   application.setAttribute("count"count);

   System.out.println("init()が実行されました");
 }

 protected void doGet(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletExceptionIOException {

   // アプリケーションスコープに保存された訪問回数を増加
   ServletContext application = this.getServletContext();
   Integer count = (Integer) application.getAttribute("count");
   count++;
   application.setAttribute("count"count);

   // HTMLを出力
   response.setContentType("text/html; charset=UTF-8");
   PrintWriter out = response.getWriter();
   out.println("<html>");
   out.println("<head>");
   out.println("<title>訪問回数を表示</title>");
   out.println("</head>");
   out.println("<body>");
   out.println("<p>訪問回数:" + count + "</p>");
   out.println("<a href=\"/example/CounterServlet\">更新</a>");
   out.println("</body>");
   out.println("</html>");
 }

 public void destroy() {
   System.out.println("destroy()が実行されました");
 }
}


initで初期化するときの注意点: 実行のタイミングはサーブレットクラスがリクエストされる順番に左右される



サーブレットクラスのフィールド

フィールドを使用するプログラムの例

countがある。
サーブレットクラスのフィールドは、ほかのサーブレットクラスやJSPファイルでは使用できない。
また、同時にリクエストしているユーザーがいればそのユーザー間でインスタンス内のフィールドが共有されることになる。リクエストのタイミングが重なってフィールドの値に不整合が生じるかもしれない。
メモ: サーブレットクラスのフィールドは扱いが難しいので、なるべき使用しないほうがいい。

package servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletConfig;
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("/CounterServlet")
public class CounterServlet extends HttpServlet {
 private static final long serialVersionUID = 1L;
 
 private Integer count// 訪問回数

 public void init(ServletConfig config) throws ServletException {

   super.init(config);

   // 訪問回数を表すIntegerインスタンスを新規作成し
   // アプリケーションスコープに保存
   count = 0;


   System.out.println("init()が実行されました");
 }

 protected void doGet(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletExceptionIOException {

   // 訪問回数を増加
   count++;

   // HTMLを出力
   response.setContentType("text/html; charset=UTF-8");
   PrintWriter out = response.getWriter();
   out.println("<html>");
   out.println("<head>");
   out.println("<title>訪問回数を表示</title>");
   out.println("</head>");
   out.println("<body>");
   out.println("<p>訪問回数:" + count + "</p>");
   out.println("<a href=\"/example/CounterServlet\">更新</a>");
   out.println("</body>");
   out.println("</html>");
 }

 public void destroy() {
   System.out.println("destroy()が実行されました");
 }
}


リスナー

リスナーは特別なクラスで、メソッドはリクエストで実行されるのではなく特定のイベントが発生したら自動的に実行される。


リスナーの例

・@WebListenerを付与しないといけない
・リスナーインタフェースとそのメソッドの実装
ServletContextListenerインタフェースは、contextInitializedとcontextDestroyedの2つのメソッドが定義されている

package listener;

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

@WebListener
public class ListenerSample implements ServletContextListener {

 public void contextInitialized(ServletContextEvent arg0) {
   ServletContext context = arg0.getServletContext();
   Integer count = 0;
   context.setAttribute("count", count);
 }

 public void contextDestroyed(ServletContextEvent arg0) {
 }
}



フィルタ

これは特殊なクラスであり、フィルタをサーブレットクラスに設定すると、そのサーブレットクラスのdoGetなどが実行される前後のタイミングでフィルタのメソッドが自動的に実行される。

フィルタを複数のサーブレットクラスに対して設定すると、それらのサーブレットクラスで共通の処理をまとめることができる。

フィルターの例

・@WebFilterの付与
URLパターンが「/Sample」のサーブレットクラスに設定する 
-> @WebFilter("/Sample")
URLパターンが「/Sample/~」のサーブレットクラスに設定する 
-> @WebFilter("/Sample/*")
すべてのサーブレットクラスに設定する 
-> @WebFilter("/*")

・Filterインタフェースの実装
・init/destroy/doFileterの実装
・doFilterの「chain.doFilter(request, response);」の前後における前処理、後処理それぞれの記述

package 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 FilterSample implements Filter {

 public void init(FilterConfig fConfig) throws ServletException {
 }

 public void doFilter(ServletRequest request,
     ServletResponse response, FilterChain chain)
     throws IOException,
     ServletException {
   request.setCharacterEncoding("UTF-8");
   chain.doFilter(request, response);
 }

 public void destroy() {
 }
}


第12章 アクションタグとEL式


動的インクルード

ヘッダーやフッターなど共通の内容のjspファイルを取り組むというようなほかのJSPファイルを読み込むことが出来る


主な標準アクションタグ

<jsp:useBean>
 スコープからJavaBeansインスタンスを取得
 取得できないときはJavaBeansインスタンスを新規作成してスコープに保存する

<jsp:setProperty>
 JavaBeansのプロパティの値を設定

<jsp:getProperty>
 JavaBeansのプロパティの値を取得

<jsp:include>
 インクルード

<jsp:forward>
 フォワード



例
<jsp:include page="インクルード先">

動的インクロードのJSPファイルの例

フッターを読み込む


footer.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<p>Copyright どこつぶ制作委員会 All Rights Reserved.</p>

includeTagSample.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>インクルードのサンプル</title>
</head>
<body>
<h1>どこつぶへようこそ</h1>
<p>「どこつぶ」は・・・</p>
<jsp:include page="/footer.jsp" />
</body>
</html>


静的インクルードの例

includeディレクティブを使う。

<%@ include file="インクルード先" %>>


静的インクルードの例

common.jsp

<%@ page import="java.util.ArrayList" pageEncoding="UTF-8" %>
<%@ page import="java.util.Date,java.text.SimpleDateFormat" %>
<% String name="湊 雄輔"; %>

includeDirectiveSample.jsp

インクルード先でimportしたクラスを使用している

nameはインクルード先で定義した変数

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ include file="/common.jsp" %>
<%
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("MM月dd日");
String today = sdf.format(date);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>静的インクルードのサンプル</title>
</head>
<body>
<%= name %>さんの<%= today %>の運勢は・・・
</body>
</html>




動的インクルード
 実行中にほかのJSPファイルの出力結果を取り込む

静的インクルード
 実行前にほかのJSPファイルの内容を取り込む



EL式

JSPファイルで、JavaBeansインスタンスのプロパティを出力するときにEL式を使用すると記述を簡単にできる。

${属性名}
${属性名.プロパティ}

<%@ page import="model.Human" %>

<%
Human human= (Human) session.getAttribute("human");
%>

<%= human.getName() %>




↓

${human.name}

EL式により、
・インポートとスコープからの取得が不要になる
・プロパティ名を指定すると自動的にgetterが実行される


EL式を使用したJSPファイルの例

healthCheckResult.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>スッキリ健康診断</title>
</head>
<body>
<h1>スッキリ健康診断の結果</h1>
<p>
身長:${health.height}<br>
体重:${health.weight}<br>
BMI:${health.bmi}<br>
体型:${health.bodyType}
</p>
<a href="/example/HealthCheck">戻る</a>
</body>
</html>


EL式はJavaと同じような演算子を使うことが出来るが、emptyのような独特なものもある。

コレクションを扱うには以下のように書く。

・リスト
${属性名[インデックス]}
・マップ
${属性名["キー"]}


humanList
 Human[0]
    name="A"
    age=23
  Human[1]
    name="B"
    age=22
 
 
${humanList}
${humanList[0].name}
と書く


for文ならこうかく
<% for(int i=0; i < ${humanList.size()}; i++) { %> ... <% } %>
<% for(Human human : ${humanList}) { %> ... <% } %>



JSTL

EL式で分岐や繰り返しを行うにはJSTLのカスタムタグを使用する。

JSTLの構成

・Core
・i18N
・Database
・XML
・Functions

これらを使うにはtaglibを使う


<%@ taglib prefix="接頭辞" uri="使用するタグライブラリのURI" %>


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>どこつぶ</title>
</head>
<body>
<h1>どこつぶメイン</h1>
<p>
<c:out value="${loginUser.name}" />さん、ログイン中
<a href="/docoTsubu/Logout">ログアウト</a>
</p>

<p><a href="/docoTsubu/Main">更新</a></p>
<form action="/docoTsubu/Main" method="post">
<input type="text" name="text">
<input type="submit" value="つぶやく">
</form>

<c:if test="${not empty errorMsg}">
<p>${errorMsg}</p>
</c:if>
<c:forEach var="mutter" items="${mutterList}">
  <p><c:out value="${mutter.userName}" />:
  <c:out value="${mutter.text}" /></p>
</c:forEach>
</body>
</html>


第13章 JDBCプログラムとDAOパターン


ここではH2データベースを使っている




Javaプログラムでデータベースを利用するには、プログラムからデータベースにSQLを送信して結果を取得する必要がある。
それを行うのがjava.sqlパッケージに含まれているクラスやインタフェースとなる。具体的には、
・DriverManager
  DBMSへの接続準備を行う
・Connection
  DBMSへの接続や切断を行う
  プログラムとデータベースを結ぶ道路役
・PreparedStatement
  SQLの送信を行う
  SQLを運ぶ車のようなもの
・ResultSet
  DBMSから検索結果を受け取る
・SQLException
  データベースに関するエラー情報を提供する


Javaプログラムからデータベースを利用するための準備

・java.sqlパッケージのクラス/インタフェース
 インポートして使用する
・JDBCドライバ
 これはデータベース操作のためのクラスやインタフェース群。
 それぞれのデータベース製品の開発元がJarファイルとして提供している
 JDBCドライバに格納されたクラスなどは直接使用するのではなく、 
 java.sqlパッケージのクラスなどを介して間接的に使用する。
Eclipseの動的webプロジェクトで使用する場合は、
 Apache Tomcatディレクトリ/libに配置する。




JDBCプログラムの例

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class SelectEmployeeSample {
 public static void main(String[] args) {
   // データベースに接続
   // 順に、接続先DB、ユーザ名、パスワード、を記述
   try (Connection conn = DriverManager.getConnection(
       "jdbc:h2:tcp://localhost/~/example""sa""")) {

     // SELECT文を準備
     String sql = "SELECT ID,NAME,AGE FROM EMPLOYEE";
     // SQLをDBに届けるPreparedStatementインスタンスを取得する
     PreparedStatement pStmt = conn.prepareStatement(sql);

     // SELECTを実行し、結果表(ResultSet)を取得
     // ResultSetインスタンスにSELECTの結果が格納される
     ResultSet rs = pStmt.executeQuery();

     // 結果表に格納されたレコードの内容を表示
     while (rs.next()) {
       String id = rs.getString("ID");
       String name = rs.getString("NAME");
       int age = rs.getInt("AGE");

       // 取得したデータを出力
       System.out.println("ID:" + id);
       System.out.println("名前:" + name);
       System.out.println("年齢:" + age + "\n");
     }
   } catch (SQLException e) {
     e.printStackTrace();
   }
 }
}


DAOパターン

いちいちデータ接続とか書くと大変なのでこのパターンを使う。
これを使うと、データベースを利用するクラスからJDBCプログラム特有のコードがなくなる。
そのようにして、コードの見通しもよくなり、仕様変更もやりやすくなる。


Daoの例

Employee.java

package model;

public class Employee {

 private String id;
 private String name;
 private int age;

 public Employee(String id, String name, int age) {
   this.id = id;
   this.name = name;
   this.age = age;
 }

 public String getId() {
   return id;
 }

 public String getName() {
   return name;
 }

 public int getAge() {
   return age;
 }
}


EmployeeDAO.java

package dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import model.Employee;

public class EmployeeDAO {
 // データベース接続に使用する情報
 private final String JDBC_URL =
         "jdbc:h2:tcp://localhost/~/example";
 private final String DB_USER = "sa";
 private final String DB_PASS = "";

 public List<Employee> findAll() {
   List<Employee> empList = new ArrayList<Employee>();

   // データベースへ接続
   try (Connection conn = DriverManager.getConnection(
       JDBC_URLDB_USERDB_PASS)) {

     // SELECT文を準備
     String sql = "SELECT ID, NAME, AGE FROM EMPLOYEE";
     PreparedStatement pStmt = conn.prepareStatement(sql);

     // SELECTを実行し、結果表を取得
     ResultSet rs = pStmt.executeQuery();

     // 結果表に格納されたレコードの内容を
     // Employeeインスタンスに設定し、ArrayListインスタンスに追加
     while (rs.next()) {
       String id = rs.getString("ID");
       String name = rs.getString("NAME");
       int age = rs.getInt("AGE");
       Employee employee = new Employee(id, name, age);
       empList.add(employee);
     }
   } catch (SQLException e) {
     e.printStackTrace();
     return null;
   }
   return empList;
 }
}

SelectEmployeeSample.java

こちらのファイルから、JBDCプログラム特有のコードである、Connection,ResultSet、SQL、例外処理などの記述がなくなった。

import java.util.List;

import model.Employee;
import dao.EmployeeDAO;

public class SelectEmployeeSample {
 public static void main(String[] args) {

   // employeeテーブルの全レコードを取得
   EmployeeDAO empDAO = new EmployeeDAO();
   List<Employee> empList = empDAO.findAll();

   // 取得したレコードの内容を出力
   for (Employee emp : empList) {
     System.out.println("ID:" + emp.getId());
     System.out.println("名前:" + emp.getName());
     System.out.println("年齢:" + emp.getAge() + "\n");
   }
 }
}


Webアプリケーションの場合、Daoはサーブレットクラス、JSPファイル、Modelのクラスのいずれからでも利用できるが、一般的にはModel(~Logicクラス)から利用する。


第10章のwebアプリにデータベースを追加する

H2 Databaseの準備
1)データベースdocoTsubuを作成
2)テーブルMUTTERを作成
3)MUTTERテーブルにレコードを追加


データベース化の仕組み
・Mutter.java

 MUTTERテーブルのドメインクラス
・MutterDAO.java
 新規に作成する。MUTTERテーブルのDaoクラス。
 全レコード取得/レコード追加のメソッドを持つ。
・GetMutterListLogic.java
 新規に作成する。全つぶやきをデータベースから取得する処理を担当する
・PostMutterLogic.java
 つぶやきの保存処理を変更する
・Main.java
 つぶやきの取得と追加の処理を変更する


プログラム

Mutter.java

package model;

import java.io.Serializable;

public class Mutter implements Serializable {
 private int id; // id
 private String userName; // ユーザー名
 private String text; // つぶやき内容

 public Mutter() {
 }

 public Mutter(String userName, String text) {
   this.userName = userName;
   this.text = text;
 }

 public Mutter(int id, String userName, String text) {
   this.id = id;
   this.userName = userName;
   this.text = text;
 }

 public int getId() {
   return id;
 }

 public String getUserName() {
   return userName;
 }

 public String getText() {
   return text;
 }
}

MutterDao.java

package dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import model.Mutter;

public class MutterDAO {

 // データベース接続に使用する情報
 private final String JDBC_URL =
     "jdbc:h2:tcp://localhost/~/docoTsubu";
 private final String DB_USER = "sa";
 private final String DB_PASS = "";

 public List<Mutter> findAll() {
   List<Mutter> mutterList = new ArrayList<Mutter>();

   // データベース接続
   try(Connection conn = DriverManager.getConnection(
         JDBC_URL, DB_USER, DB_PASS)) {

     // SELECT文の準備
     String sql =
         "SELECT ID,NAME,TEXT FROM MUTTER ORDER BY ID DESC";
     PreparedStatement pStmt = conn.prepareStatement(sql);

     // SELECTを実行
     ResultSet rs = pStmt.executeQuery();

     // SELECT文の結果をArrayListに格納
     while (rs.next()) {
       int id = rs.getInt("ID");
       String userName = rs.getString("NAME");
       String text = rs.getString("TEXT");
       Mutter mutter = new Mutter(id, userName, text);
       mutterList.add(mutter);
     }
   } catch (SQLException e) {
     e.printStackTrace();
     return null;
   }
   return mutterList;
 }
 
 public boolean create(Mutter mutter) {
   // データベース接続
   try(Connection conn = DriverManager.getConnection(
         JDBC_URL, DB_USER, DB_PASS)) {

     // INSERT文の準備(idは自動連番なので指定しなくてよい)
     String sql = "INSERT INTO MUTTER(NAME, TEXT) VALUES(?, ?)";
     PreparedStatement pStmt = conn.prepareStatement(sql);
     // INSERT文中の「?」に使用する値を設定しSQLを完成
     pStmt.setString(1, mutter.getUserName());
     pStmt.setString(2, mutter.getText());

     // INSERT文を実行
     int result = pStmt.executeUpdate();

     if (result != 1) {
       return false;
     }
   } catch (SQLException e) {
     e.printStackTrace();
     return false;
   }
   return true;
 }
 
}

PostMutterLogic.java

package model;

import dao.MutterDAO;

public class PostMutterLogic {

 public void execute(Mutter mutter) // 引数を1つに変更
   MutterDAO dao = new MutterDAO();
   dao.create(mutter);
 }

}

GetMutterListLogic.java

package model;

import java.util.List;

import dao.MutterDAO;

public class GetMutterListLogic {

 public List<Mutter> execute() {
   MutterDAO dao = new MutterDAO();
   List<Mutter> mutterList = dao.findAll();
   return mutterList;
 }
}

Main.java

package servlet;

import java.io.IOException;
import java.util.List;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import model.GetMutterListLogic;
import model.Mutter;
import model.PostMutterLogic;
import model.User;

@WebServlet("/Main")
public class Main extends HttpServlet {
 private static final long serialVersionUID = 1L;

 protected void doGet(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {

   // つぶやきリストを取得して、リクエストスコープに保存
   GetMutterListLogic getMutterListLogic =
       new GetMutterListLogic();
   List<Mutter> mutterList = getMutterListLogic.execute();
   request.setAttribute("mutterList", mutterList);

   // ログインしているか確認するため
   // セッションスコープからユーザー情報を取得
   HttpSession session = request.getSession();
   User loginUser = (User) session.getAttribute("loginUser");

   if (loginUser == null) { // ログインしていない
   // リダイレクト
     response.sendRedirect("/docoTsubu/");
   } else { // ログイン済み
   // フォワード
     RequestDispatcher dispatcher = request.
         getRequestDispatcher("/WEB-INF/jsp/main.jsp");
     dispatcher.forward(request, response);
   }
 }

 protected void doPost(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {

   // リクエストパラメータの取得
   //request.setCharacterEncoding("UTF-8");
   String text = request.getParameter("text");

   // 入力値チェック
   if (text != null && text.length() != 0) {

     // セッションスコープに保存されたユーザー情報を取得
     HttpSession session = request.getSession();
     User loginUser = (User) session.getAttribute("loginUser");

     // つぶやきをつぶやきリストに追加
     Mutter mutter = new Mutter(loginUser.getName(), text);
     PostMutterLogic postMutterLogic = new PostMutterLogic();
     postMutterLogic.execute(mutter);


   } else {
     // エラーメッセージをリクエストスコープに保存
     request.setAttribute("errorMsg",
         "つぶやきが入力されていません");
   }

   // つぶやきリストを取得して、リクエストスコープに保存
   GetMutterListLogic getMutterListLogic =
       new GetMutterListLogic();
   List<Mutter> mutterList = getMutterListLogic.execute();
   request.setAttribute("mutterList", mutterList);

   // フォワード
   RequestDispatcher dispatcher = request.getRequestDispatcher(
       "/WEB-INF/jsp/main.jsp");
   dispatcher.forward(request, response);
 }
}


第14章



アプリケーションの要件とは
・アプリケーションに求める機能とその仕様のことである。

アプリケーションの設計手法
・要件をプログラムに落とし込む作業である。
・本格的な大規模開発向けにはさまざまな設計の方法論がある。
・まずは、小規模開発向けの簡易な手法から始め、経験を積むことが重要である。


入門者向けの設計開発方法
・入門者なら、次の4つに設計を分ける
1)テーブルの設計
2)画面の設計
3)サーブレットクラスとJSPファイルの設計
4)サーバサイドの設計

・まず、テーブル設計と機能を開発する順序を決定する。
・1つの機能について、次の手順で設計を行う。
①画面だけの画面遷移図を作成する。
② 利用するサーブレットや JSP を画面遷移図に書き込む。
③ サーブレットと JSPの連携内容を基本アーキテクチャ図にまとめる。

・設計した機能について、次の手順で開発を行う。
① Entity の作成
② DAO の作成
③ DAO のテスト
④ BOの作成
⑤ BOのテスト
⑥ サーブレットと JSPの連携の作成
⑦画面遷移の確認
⑧ サーブレットクラスの仕上げ
⑨ JSP ファイルの仕上げ

⑩ 機能の最終動作確認


プログラム

Login.java

package model;

public class Login {
 private String userId;
 private String pass;

 public Login(String userId, String pass) {
   this.userId = userId;
   this.pass = pass;
 }

 public String getUserId() {
   return userId;
 }

 public String getPass() {
   return pass;
 }
}

Account.java

package model;

public class Account {
 private String userId;
 private String pass;
 private String mail;
 private String name;
 private int age;

 public Account(String userId, String pass, String mail,
     String name, int age) {
   this.userId = userId;
   this.pass = pass;
   this.mail = mail;
   this.name = name;
   this.age = age;
 }

 public String getUserId() {
   return userId;
 }

 public String getPass() {
   return pass;
 }

 public String getMail() {
   return mail;
 }

 public String getName() {
   return name;
 }

 public int getAge() {
   return age;
 }
}

AccountDAO.java

package dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import model.Account;
import model.Login;

public class AccountDAO {
 // データベース接続に使用する情報
 private final String JDBC_URL =
         "jdbc:h2:tcp://localhost/~/sukkiriShop";
 private final String DB_USER = "sa";
 private final String DB_PASS = "";
 
 public Account findByLogin(Login login) {
   Account account = null;

   // データベースへ接続
   try (Connection conn = DriverManager.getConnection(
       JDBC_URL, DB_USER, DB_PASS)) {

     // SELECT文を準備
     String sql = "SELECT USER_ID, PASS, MAIL, NAME, AGE FROM ACCOUNT WHERE USER_ID = ? AND PASS = ?";
     PreparedStatement pStmt = conn.prepareStatement(sql);
     pStmt.setString(1, login.getUserId());
     pStmt.setString(2, login.getPass());

     // SELECTを実行し、結果表を取得
     ResultSet rs = pStmt.executeQuery();

     // 一致したユーザーが存在した場合
     // そのユーザーを表すAccountインスタンスを生成
     if (rs.next()) {
       // 結果表からデータを取得
       String userId = rs.getString("USER_ID");
       String pass = rs.getString("PASS");
       String mail = rs.getString("MAIL");
       String name = rs.getString("NAME");
       int age = rs.getInt("AGE");
       account = new Account(userId, pass, mail, name, age);
     }
   } catch (SQLException e) {
     e.printStackTrace();
     return null;
   }
   // 見つかったユーザーまたはnullを返す
   return null;
 }
}

AccountDAOTest.java
Daoのテスト

package test;

import model.Account;
import model.Login;
import dao.AccountDAO;

public class AccountDAOTest {
 public static void main(String[] args) {
   testFindByLogin1(); // ユーザーが見つかる場合のテスト
   testFindByLogin2(); // ユーザーが見つからない場合のテスト
 }

 public static void testFindByLogin1() {
   Login login = new Login("minato""1234");
   AccountDAO dao = new AccountDAO();
   Account result = dao.findByLogin(login);
   if (result != null &&
       result.getUserId().equals("minato") &&
       result.getPass().equals("1234") &&
       result.getMail().equals("minato@sukkiri.com") &&
       result.getName().equals("湊 雄輔") &&
       result.getAge() == 23) {
     System.out.println("findByLogin1:成功しました");
   } else {
     System.out.println("findByLogin1:失敗しました");
   }
 }

 public static void testFindByLogin2() {
   Login login = new Login("minato""12345");
   AccountDAO dao = new AccountDAO();
   Account result = dao.findByLogin(login);
   if (result == null) {
     System.out.println("findByLogin2:成功しました");
   } else {
     System.out.println("findByLogin2:失敗しました");
   }
 }
}

LoginLogic.java
ログイン処理を担当するBO

package model;

import dao.AccountDAO;

public class LoginLogic {
 public boolean execute(Login login) {
   AccountDAO dao = new AccountDAO();
   Account account = dao.findByLogin(login);
   return account != null;
 }
}

LoginLogicTest.java
LoginLogicをテストする

package test;

import model.Login;
import model.LoginLogic;

public class LoginLogicTest {
 public static void main(String[] args) {
   testExecute1(); // ログイン成功のテスト
   testExecute2(); // ログイン失敗のテスト
 }

 public static void testExecute1() {
   Login login = new Login("minato""1234");
   LoginLogic bo = new LoginLogic();
   boolean result = bo.execute(login);
   if (result) {
     System.out.println("testExcecute1:成功しました");
   } else {
     System.out.println("testExcecute1:失敗しました");
   }
 }

 public static void testExecute2() {
   Login login = new Login("minato""12345");
   LoginLogic bo = new LoginLogic();
   boolean result = bo.execute(login);
   if (!result) {
     System.out.println("testExcecute2:成功しました");
   } else {
     System.out.println("testExcecute2:失敗しました");
   }
 }
}

WelcomeServlet.java
トップに関するリクエストを処理するコントローラ

package servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
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("/WelcomeServlet")
public class WelcomeServlet extends HttpServlet {
 private static final long serialVersionUID = 1L;

 protected void doGet(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {
   RequestDispatcher dispatcher = request.getRequestDispatcher(
       "/WEB-INF/jsp/welcome.jsp");
   dispatcher.forward(request, response);
 }
}

welcome.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>スッキリ商店</title>
</head>
<body>
<ul>
<li><a href="/sukkiriShop/LoginServlet">ログイン</a></li>
<li>ユーザー登録</li>
</ul>
</body>
</html>

LoginServlet.java
ログインに関するリクエストを処理するコントローラ

package servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
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("/LoginServlet")
public class LoginServlet extends HttpServlet {
 private static final long serialVersionUID = 1L;

 protected void doGet(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {
   RequestDispatcher dispatcher = request.getRequestDispatcher(
       "/WEB-INF/jsp/login.jsp");
   dispatcher.forward(request, response);
 }

 protected void doPost(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {
   RequestDispatcher dispatcher = request.getRequestDispatcher(
       "/WEB-INF/jsp/loginOK.jsp");
   dispatcher.forward(request, response);
 }
}

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>スッキリ商店</title>
</head>
<body>
<form action="/sukkiriShop/LoginServlet" method="post">
ユーザーID:<input type="text" name="userId"><br>
パスワード:<input type="password" name="pass"><br>
<input type="submit" value="ログイン">
</form>
</body>
</html>

loginOK.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>スッキリ商店</title>
</head>
<body>
<p>ようこそ{ユーザーID}さん</p>
<a href="/sukkiriShop/WelcomeServlet">トップへ</a>
</body>



LoginServlet.java
上記に処理を追加

package servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import model.Login;
import model.LoginLogic;

@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
 private static final long serialVersionUID = 1L;

 protected void doGet(HttpServletRequest request,
     HttpServletResponse response)

     throws ServletException, IOException {

   // フォワード
   RequestDispatcher dispatcher = request.getRequestDispatcher(
       "/WEB-INF/jsp/login.jsp");
   dispatcher.forward(request, response);
 }

 protected void doPost(HttpServletRequest request,
     HttpServletResponse response)
     throws ServletException, IOException {

   // リクエストパラメータの取得
   request.setCharacterEncoding("UTF-8");
   String userId = request.getParameter("userId");
   String pass = request.getParameter("pass");

   // ログイン処理の実行
   Login login = new Login(userId, pass);
   LoginLogic bo = new LoginLogic();
   boolean result = bo.execute(login);

   // ログイン処理の成否によって処理を分岐
   if (result) { // ログイン成功時

     // セッションスコープにユーザーIDを保存
     HttpSession session = request.getSession();
     session.setAttribute("userId", userId);

     // フォワード
     RequestDispatcher dispatcher =
         request.getRequestDispatcher("/WEB-INF/jsp/loginOK.jsp");
     dispatcher.forward(request, response);
   } else { // ログイン失敗時
   // リダイレクト
     response.sendRedirect("/sukkiriShop/LoginServlet");
   }
 }
}

loginOK.jsp
上記に処理を追加

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>スッキリ商店</title>
</head>
<body>
<p>ようこそ<c:out value="${userId}" />さん</p>
<a href="/sukkiriShop/WelcomeServlet">トップへ</a>
</body>
</html>


画像2



フォーム作成の注意点


フォームで送信したデータをサーブレットクラスで正しく取得するには、
・送信元のフォーム
・送信先のサーブレットクラス
の両方で対応させるべきポイントが4つある。

1)
 送信元のフォーム:  action属性
 送信先のサーブレットクラス: サーブレットクラスのURLパターン
2)
 送信元のフォーム:  method属性
 送信先のサーブレットクラス: サーブレットクラスの実行メソッド
3)
 送信元のフォーム:  HTMLの文字コード
 送信先のサーブレットクラス: setCharacterEncoding()メソッドの引数
4)
 送信元のフォーム:  各部品のname属性
 送信先のサーブレットクラス: getParameter()メソッドの引数


JSP側

<%@ page contentType="text/html; charset=UTF-8" %>

<form action="/example/Hoge" method="post">
    名前:<input type="text" name="name"><br>
    <input type="submit" value="送信">
</form>

Java側

// form actionのURL
@WebServlet("/Hoge")
public class Hoge extends HttpServlet {
     // メソッドはこちらでもPost
     protected void doPost(HttpServletRequest request, HttpServletResponse response)
         throws ServletException, IOException {
         // UTF-8を設定
         request.setCharacterEncoding("UTF-8");
         // パラメータ取得
         // inputのname属性がnameなので、こちらでもそのようにして取得
         String name = request.getParameter("name");
     }
     
}


Java EEの基礎知識

サーブレットやJSPによるwebアプリケーションを作成するには、JavaSEに加え、Java EEも必要になる

Java EEの機能はアプリケーションサーバが提供しているので、アプリケーションサーバをインストールしていれば入手できる。
なので、Apache Tomcatをインストールすればいい。
また、JavaSEもEclipseに含まれている。

PleiadesをインストールすればJava SEとJava EEの用意はできている。


Web付録

こちらでEclipseやTomcatなどのインストール方法などが載ってある。



この記事が気に入ったらサポートをしてみませんか?