デバッグを行う前に

4.1 デバッグを行う前に

 本章では、Eclipseを利用したWebアプリケーションのデバッグについて学習していきます。ここでは、すでに「Java基礎Ⅰテキスト」で通常のJavaプログラムでのデバッグを学習している前提で解説します。そのため、デバッグパースペクティブの見方や、ステップオーバーやステップインの操作方法については省略しています。
 本来は、エラーやロジックのミスなどのバグを改善するためにデバッグを行いますが、ここでは、変数に格納されているデータの確認や、プログラムがどのような流れで動いているかなどを確認する形で説明していきます。基本的なデバッグの方法は通常のJavaプログラムと変わりませんが、Webアプリケーションではデバッグの実行方法が異なりますので注意してください。

 本章では以下の「数字当てゲーム(WEBセッション版)」を利用して、Webアプリケーションのデバッグ方法について解説しています。

数字当てゲーム(WEBセッション版)

 このプログラムは、入力フォームから0から9までの予想数字を入力し、ランダムで設定された正解数字の判定を行います。判定の結果が「はずれ」の場合はゲームが継続され、「当たり」の場合はゲームが終了します。
 ※ソースコードは以下URLからダウンロードして、対象の階層にコピーしてください
  https://kanda-it-school-square.com/?wpdmdl=4692

実行結果

アプリケーション構成

① ソース・フォルダ :web_basic/WEB-INF/src
② パッケージ :ch04
③ 名前 :NumberInfo

➢ NumberInfo.java
package ch04;

public class NumberInfo {

	private String userNumber;	//入力数値用変数
	private String judge;			//判定用変数

	NumberInfo(){
		userNumber = "";
 		judge = "";
 	}
 
 	//変数userNumberのアクセサメソッド
 	public String getUserNumber() {
 		return userNumber;
 	}
 	public void setUserNumber(String userNumber) {
 		this.userNumber = userNumber;
 	}
 
 	//変数judgeのアクセサメソッド
 	public String getJudge() {
 		return judge;
 	}
 	public void setJudge(String judge) {
 		this.judge = judge;
 	}
 }


① ソース・フォルダ :web_basic/WEB-INF/src
② パッケージ :ch04
③ 名前 :GuessServlet
④ スーパークラス :javax.servlet.http.HttpServlet
⑤ アクセスURL :http://localhost:8080/web_basic/GuessServlet

➢ GuessServlet.java
package ch04;

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class GuessServlet extends HttpServlet {

 	public void doGet(HttpServletRequest request, HttpServletResponse response) 
       throws ServletException, IOException {
 
 		try {
 			// 文字コードの設定
 			request.setCharacterEncoding("UTF-8");
 			response.setContentType("text/plain; charset=UTF-8");
 
 			// セッションオブジェクトの取得
 			HttpSession session = request.getSession();
 
 			Integer answerNumber = null; // 正解数字用変数
 			ArrayList<NumberInfo> list = null; // 履歴用変数
 			String message = ""; // メッセージ用変数
 
 			// ゲーム制御フラグを取得
 			String cmd = request.getParameter("cmd");
 
 			// 初回アクセス、または再チャレンジ時の初期化処理
 			if (cmd == null || cmd.equals("first")) {
 				list = new ArrayList<NumberInfo>();
 				cmd = "first";
 				answerNumber = new Random().nextInt(10);
 				session.setAttribute("answer", answerNumber);
 
 				// ゲームが継続中の処理
 			} else if (cmd.equals("game")) {
 
 				// 入力数値、履歴、回答数値の取得
 				String strUserNumber = (String) request.getParameter("user_number");
 				list = (ArrayList<NumberInfo>) session.getAttribute("list");
 				answerNumber = (Integer) session.getAttribute("answer");
 
 				int intUserNumber = -1; // 入力数値用変数
 				String judge = ""; // 判定用変数
 
 				// エラーチェック
 				if (strUserNumber.equals("")) {
 					message = "何も入力されていません";
 				} else {
 					try {
 						intUserNumber = Integer.parseInt(strUserNumber);
 						if (intUserNumber < 0 || 9 < intUserNumber) {
 							message = "0から9までの数字を入力してください";
 						}
 					} catch (NumberFormatException e) {
 						message = "数字を入力してください";
 					}
 				}
 
 				// エラーがない場合、判定処理
 				if (message.equals("")) {
 					if (answerNumber == intUserNumber) {
 						judge = "当たり";
 						cmd = "end";
 						message = intUserNumber + " は、当たりです";
 					} else {
 						judge = "はずれ";
 						message = intUserNumber + " は、はずれです";
 					}
 					// 入力数値、判定を履歴に追加
 					NumberInfo objNumberInfo = new NumberInfo();
 					objNumberInfo.setUserNumber(Integer.toString(intUserNumber));
 					objNumberInfo.setJudge(judge);
 					list.add(objNumberInfo);
 				}
 			}
 
 			// ゲーム制御フラグ、メッセージをリクエストスコープへ登録
 			request.setAttribute("cmd", cmd);
 			request.setAttribute("message", message);
 			// 履歴をセッションへ登録
 			session.setAttribute("list", list);
 			// JSPへ画面遷移
 			request.getRequestDispatcher("/view/ch04/guess.jsp").forward(request, response);
 
 			// 予期しない例外が発生した場合の画面遷移
 		} catch (Exception e) {
 			String message = "予期せぬエラーが発生しました<br>" + e.toString();
 			request.setAttribute("cmd", "end");
 			request.setAttribute("message", message);
 			request.getRequestDispatcher("/view/ch04/guess.jsp").forward(request, response);
 		}
 	}
 }
➢ web.xml
<servlet>
	<servlet-name>GuessServletMapping</servlet-name>
	<servlet-class>ch04.GuessServlet</servlet-class>
</servlet>
<servlet-mapping>
	<servlet-name>GuessServletMapping</servlet-name>
	<url-pattern>/GuessServlet</url-pattern>
</servlet-mapping>


① 親フォルダの入力または選択 :web_basic/view/ch04
② ファイル名 :guess.jsp
③ アクセスURL :GuessServlet.javaからの画面遷移でアクセスされる

➢ guess.jsp
<%@page contentType="text/html; charset=UTF-8"%>
<%@page import="java.util.ArrayList,ch04.NumberInfo"%>

<%
String cmd = (String)request.getAttribute("cmd");
String message = (String)request.getAttribute("message");
ArrayList<NumberInfo> list =(ArrayList<NumberInfo>)session.getAttribute("list");
if(cmd == null){
	cmd = "first";
 }
 %>
 
 <html>
 	<head>
 		<title>数字当てゲーム(セッション版)</title>
 	</head>
 	<body>
 		<div style="text-align:center">
 			<h1 style="text-align:center">数字当てゲーム(セッション版)</h1>
 			<hr style="height:3; background-color:#0000ff" />
 			<br>
 			<br>
 			<br>
 			<p style="text-align:center">0~9の数字をいれてください</p>
 
 			<%
 			if(cmd.equals("end")){
 			%>
 				<input type="text">
 				<input type="button" value="送信" disabled>
 				<form action="<%=request.getContextPath()%>/GuessServlet">
 					<input type="hidden" name="cmd" VALUE="first">
 					<br>
 					<input type="submit" value="もう一度挑戦">
 				</form>
 			<%
 			}
 			else{
 			%>
 				<form action="<%=request.getContextPath()%>/GuessServlet">
 					<input type="text" name="user_number">
 					<input type="hidden" name="cmd" VALUE="game">
 					<input type="submit" VALUE="送信">
 				</form>
 			<%
 			}
 			%>
 
 			<br>
 
 			<%
 			if(message != null){
 			%>
 			<h3 style="text-align:center; color: red;"><%=message%></h3>
 			<%
 			}
 			%>
 
 			<h2 style="text-align:center">判定結果</h2>
 			<table style="border: 1px solid; margin:0 auto;">
 				<tr>
 					<td style="border: 1px solid;">挑戦回数</td>
 					<td style="border: 1px solid;">入力数字</td>
 					<td style="border: 1px solid;">判定</td>
 				</tr>
 				<%
 				if(list != null){
 					for(int i=0; i<list.size(); i++){
 						NumberInfo objNumberInfo = list.get(i);
 				%>
 					<tr>
 						<td style="border: 1px solid;"><%=i+1%>回目</td>
 						<td style="border: 1px solid;"><%=objNumberInfo.getUserNumber()%></td>
 						<td style="border: 1px solid;"><%=objNumberInfo.getJudge()%></td>
 					</tr>
 				<%
 					}
 				}
 				%>
 			</table>
 		</div>
 	</body>
 </html>

解説
 これは、サーブレット内でランダムに生成された正解の数字とフォームから入力された数字を判定し、結果を画面に表示するプログラムです。またゲームが継続している間、履歴も一覧で表示します。

 このプログラムは以下の3つのファイルで構成されています。

・ NumberInfo.java
 1回分の入力された値と判定の情報を管理するJavaBeanクラスです。
 入力数字の判定を管理する変数や、変数にアクセスするためのアクセサメソッドが定義されています。

・ GuessServlet.java
 正解数字の生成や、エラー判定、正否判定などを行うサーブレットです。

・ guess.jsp
 GuessServletで生成されたデータを画面に表示するためのJSPです。

 GuessServlet.javaでは、26行目でゲームの進行を管理するためのフラグを取得しています。cmdは「コマンド」の略称です。
   26:String cmd = request.getParameter("cmd");

 ゲームの進行を制御するフラグには以下の4つの値があり、変数に格納された値が何なのかをもとにゲームの継続や終了の判定が行われています。

 【cmd変数の値】
 ① null
  最初のアクセスであることを表す値です。
  ブラウザから最初にアクセスされた際に変数cmdに格納されます。
 ② ”first”
  ゲームが初期化された状態であることを表す値です。
  ゲームが終了し、「もう1度挑戦」が選択された際に変数cmdに格納されます。
 ③ ”game”
  ゲームが継続している状態であることを表す値です。
  入力された数字が正解ではなかった場合に、変数cmdに格納されます。
 ④ ”end”
  ゲームが終了している状態であることを表す値です。
  入力された数字が正解だった場合に、変数cmdに格納されます。

 GuessServlet.javaには、次の3つの処理が記述されていて、取得されたcmdの値によってどこの処理が行われるかが決まります。

 ① 29行目から33行目
  初回アクセス、または再チャレンジ時の初期化処理
 ② 36行目から76行目
  ゲームが継続中の処理
 ③ 79行目から84行目
  データを登録する処理と画面遷移の処理

 29行目から33行目では初回アクセス、または再チャレンジの場合の初期化処理を行なっています。
 30行目では履歴を管理するオブジェクトを生成します。31行目ではcmdの値を「first」で初期化しています。32行目は正解数字を生成し、33行目でセッションへ登録します。
   29:if(cmd == null || cmd.equals("first")){
   30: list = new ArrayList<NumberInfo>();
   31: cmd = "first";
   32; answerNumber = new Random().nextInt(10);
   33: session.setAttribute("answer",answerNumber);

 36行目から76行目のelseブロックではゲームが継続中の処理を行います。
 39行目から41行目では入力数値、履歴、正解数値の値を取得しています。
   39:String strUserNumber = (String)request.getParameter("user_number");
   40;list = (ArrayList<NumberInfo>)session.getAttribute("list");
   41:answerNumber = (Integer)session.getAttribute("answer");

 43行目から58行目では入力チェックを行い、問題がある値だった場合には、変数messageにエラーメッセージを代入します。
   47:if(strUserNumber.equals("")){
   48: message = "何も入力されていません";
   49:}else{
   50: try{
   51: intUserNumber = Integer.parseInt(strUserNumber);
   52: if(intUserNumber > 0 || 9 < intUserNumber){
   53: message = "0から9までの数字を入力してください";
   54: }
   55: }catch(NumberFormatException e){
   56: message = "数字を入力してください";
   57: }
   58:}

 エラーメッセージがない場合には、62行目から69行目で正解の判定を行います。この時入力された数字が当たりの場合は、cmd変数の値に「end」を代入します。
   62:if(answerNumber == intUserNumber){
   63: judge = "当たり";
   64:cmd = "end";
   65: message = intUserNumber + " は、当たりです";
   66:}else{
   67: judge = "はずれ";
   68: message = intUserNumber + " は、はずれです";
   69:}

 71行目から74行目では入力された数字と判定結果を履歴情報に追加しています。
   71:NumberInfo objNumberInfo = new NumberInfo();
   72:objNumberInfo.setUserNumber(Integer.toString(intUserNumber));
   73:objNumberInfo.setJudge(judge);
   74:list.add(objNumberInfo);

 79行目から80行目ではゲーム制御フラグとメッセージをリクエストスコープへ登録します。
   79:request.setAttribute("cmd",cmd);
   80:request.setAttribute("message", message);

 82行目で履歴情報をセッションへ登録します。
   82:session.setAttribute("list", list);

 登録後は、84行目のフォワード処理でguess.jspへ画面遷移を行います。
   84:request.getRequestDispatcher("/view/ch04/guess.jsp").forward(request,response);

 guess.jspでは5行目から8行目でゲーム制御用フラグ、メッセージ、履歴オブジェクトを取得しています。
   5:String cmd = (String)request.getAttribute("cmd");
   6:String message = (String)request.getAttribute("message");
   7:ArrayList<NumberInfo> list =(ArrayList<NumberInfo>)session.getAttribute("list");

 guess.jspのHTML部分は取得されたデータの有無や状態によって制御されており、 29行目から35行目は変数cmdの値が「end」だった場合に表示される画面になっています。
   29:<input type="text">
   30:<input type="button" value="送信" disabled>    31:<form action="<%=request.getContextPath()%>/GuessServlet">    32: <input type="hidden" name="cmd" VALUE="first">    33: <br>    34: <input type="submit" value="もう一度挑戦">    35:</form>

 40行目から44行目は変数cmdの値が「game」だった場合に出力されます。
   40:<form action="<%=request.getContextPath()%>/GuessServlet">
   41: <input type="text" name="user_number">
   42: <input type="hidden" name="cmd" VALUE="game">
   43: <input type="submit" VALUE="送信">
   44:</form>

図 4.1.1: cmdの値による入力画面の違い

 54行目はエラーメッセージや正否のメッセージがある場合に、そのメッセージを表示します。
   54:<h3 style="text-align:center; color: red;"><%=message%></h3>

図 4.1.2: メッセージの表示画面

 67行目から78行目では、履歴がある場合にfor文を利用して履歴情報をテーブル形式で出力しています。
   67:if(list != null){
   68: for(int i=0; i<list.size(); i++){
   69: NumberInfo objNumberInfo = list.get(i);
   70:%>
   71: <tr>
   72: <td><%=i+1%>回目</td>
   73: <td><%=objNumberInfo.getUserNumber()%></td>
   74: <td><%=objNumberInfo.getJudge()%></td>
   75: </tr>
   76:<%
   77: }
   78:}

図 4.1.3: 履歴情報の表示画面

 これで、デバッグに利用するプログラムの解説は終了です。
 次の節からは、実際にデバッグを行う手順について解説していきます。


NEXT>> 4.2 サーブレットのデバッグ