Strutsでwebアプリケーション初期化


webアプリケーションが起動するとき自動的に一回だけ実行される

あらかじめユーザーリストを表示させたい時など、DBから読み込んで
アプリケーションスコープなどに入れておくなどの使い方がある。
ソース → http://d.hatena.ne.jp/wani2000/20041205#p2

Strutsプラグインとして機能が用意されている

(1)PlugInを実装するクラス Initを作成
(2)initをオーバーライドして、
(3)初期化で入れておきたい情報をスコープに入れる
(4)タグでstruts-config.xmlに登録する

Strutsでwebアプリケーション初期化 ソース Init.java


package jp.co.exercise.;

import javax.servlet.ServletContext;
import org.apache.struts.action.ActionServlet;
import org.apache.struts.action.PlugIn;
import org.apache.struts.config.ModuleConfig;

public class Init implements PlugIn{
public void init( ActionServlet servlet, ModuleConfig config){
ServletContext application = servlet.getServletContext();
application.setAttribute("list", new ArticleList());
}
public void destroy(){}
}

スコープのまとめ


http://www.fk.urban.ne.jp/home/kishida/kouza/kishou/jsp05.html

HttpServletRequest requestでリクエストを得る

・リクエストスコープに格納
HttpServletRequest request = request.get
request.setAttribute("userid", userid);

//requestオブジェクトを、forwardメソッドで、次のリクエスト先に引継ぎ
RequestDispatcher rd = request.getRequestDispatcher("./xxx");
rd.forward(request, res);


・セッションスコープに格納
HttpSession session = request.getSession();
session.setAttribute("userid", userid);


・アプリケーションスコープに格納
ServletContext sc = getServletContext( );
sc.setAttribute("userid", userid);

struts-config.xml

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC
			"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
			"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<struts-config>

	<!-- フォームBeanの登録 -->
	<form-beans> 
		<form-bean name="registForm" type="sec2.exercise.RegistForm" />
		<form-bean name="userRegistForm" type="sec5.example.UserRegistForm" />
	</form-beans>

	<!-- アクションクラスの登録 -->
	<action-mappings>
		<action path="/regist" type="sec2.exercise.RegistAction" 
				name="registForm">		<!-- name属性に関連するフォームBeanを入れる -->
			<forward name="success" path="/jsp/registFinish.jsp" />	 
			<forward name="error" path="/jsp/registError.jsp" />
		</action>
			
		<!-- validate()メソッドを利用した場合 -->
		<action path="/userRegist"
				 type="sec5.example.UserRegistAction"
				 name="userRegistForm"
				 input="/jsp/userRegistForm.jsp"			<!-- validatorでエラーした場合の遷移先 -->
				 validate="true">							<!-- validatorを有効にする -->
			<forward name="success" path="/jsp/registFinish.jsp" />
		</action>

		<!-- JSPからのリンク用 -->
		<action path="/exercise4_1" type="sec4.exercise.Exercise4_1">
			<forward name="success" path="/jsp/exercise4_1.jsp" />
		</action>
	</action-mappings>
	
	<message-resources parameter="resources.application"/>

	<!-- validatorプラグインの追加 -->
	<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
		<set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
	</plug-in>
	
	<!-- webアプリケーション初期化plug-inの追加 -->
	<plug-in className="sec7.exercise.Init" />
	
</struts-config>

コネクションプーリング


コネクションプーリングとはDBとの接続をいくつかはりっぱなしにしておいて、
そのひとつを利用してDB接続をすることで、接続の時間を短縮する
接続が早い

アプリケーションがDBを閲覧するには
接続 → SQL送信 → レスポンス → 切断

いちいち接続から行わないということ
http://interstage.fujitsu.com/jp/technical/sample/sam000005_02.html

validatorを使った妥当性のチェック


・良くある入力チェックはvalidatorを利用して実装する

(1)フォームBeanでValidatorFormクラスを継承
(ValidatorFormはActionFormクラスのサブクラスなのでActionFormは継承しなくて良い)

(2)エラーメッセージの登録 application.properties
参照 → http://d.hatena.ne.jp/wani2000/20041205#p8

(3)validation.xmlを作成する。
このファイルで、どのような検証を行うか設定する。
ファイル名は何でもいい
参照 → http://d.hatena.ne.jp/wani2000/20041205#p7
検証ルール 参照 → http://d.hatena.ne.jp/wani2000/20041205#p9

(4)エラーメッセージを表示できるようにjspファイルを修正
・エラーをすべて表示


・name属性のエラーだけ表示

javascriptでのチェックも行うことができる
(1)javascriptを利用することを宣言。フォームBean名を入れる

(2)フォームの送信先を指定。「return validateフォームBean名(this)とする」

(5)タグでstruts-config.xmlに登録する
参照 → http://d.hatena.ne.jp/wani2000/20041205#p4

validation.xml

<?xml version="1.0" encoding="Shift_JIS" ?>

<!DOCTYPE form-validation PUBLIC
		  "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN"
		  "http://jakarta.apache.org/commons/dtds/validator_1_0.dtd">

<form-validation>
	<formset>
	  <form name="postForm">
		 <field property="name"
			depends="required">		<!-- 検証ルールの種類。複数の場合カンマで区切る -->
			<msg name="required" key="errors.text"/>		<!-- 検証ルールの種類。エラーの際のメッセージのリソースファイルを指定 -->
			<arg0 key="名前" resource="false"/>		<!-- メッセージリソースファイルに埋め込む内容 -->
			<arg1 key="1文字以上" resource="false"/>
		 </field>
		 
		 <field property="body"
			depends="required">
			<msg name="required" key="errors.text"/>
			<arg0 key="内容" resource="false"/>
		 </field>
	  </form>
	</formset>
</form-validation>

application.properties

errors.header=<ul><font color="red">
errors.footer=</font></ul>
errors.prefix=<li>
errors.suffix=</li>
errors.text={0}が入力されていません
errors.age={0}が不正な値が入力されました
errors.range={0}文字以上、{1}文字以内で入力してください

validatorの検証ルール

validation.xmlのdepends属性に入れることができる値
<field property="name" depends="required">

参考 → http://struts.apache.org/userGuide/dev_validator.html

required	未入力チェック

byte		byte型の範囲の数字かどうかチェック

short		short型の範囲の数字かどうかチェック

integer		int型の範囲の数字かどうかチェック

float		float型の範囲の数字かどうかチェック

double		double型の範囲の数字かどうかチェック

date		日付の書式かチェック

email		メールアドレスの形式かチェック

maxlength	文字列の長さをチェック 最大値
	<var>
		<var-name>maxlength</var-name>
		<var-value>30</var-value>
	</var>
	
minlength	文字列の長さをチェック 最小値

mask		指定した正規表現にマッチしているかチェック
	<var>
		<var-name>mask</var-name>
		<var-value>^[a-zA-Z]*$</var-value>
	</var>

intRange	整数型として指定された範囲かをチェック
	<field property="age" depends="required,integer,intRange">
		<arg0 key="employee.age"/>
		<arg1 name="intRange" key="${var:min}" resource="false"/>
		<arg2 name="intRange" key="${var:max}" resource="false"/>
		<var><var-name>min</var-name><var-value>18</var-value></var>
		<var><var-name>max</var-name><var-value>65</var-value></var>
	</field>

validate()メソッドを使った妥当性のチェック


(1)エラーメッセージの登録 application.properties
参照 → http://d.hatena.ne.jp/wani2000/20041205#p8

(2)struts-config.xmlに設定を追加
アクションクラスの設定の記述で
input属性 validate属性を追加する

input="/jsp/userRegistForm.jsp" validatorでエラーした場合の遷移先
validate="true" validatorを有効にする

(3)フォームBeanに検証用メソッド validate() メソッドを定義する
参照 → http://d.hatena.ne.jp/wani2000/20041205#p11

(4)エラーメッセージを表示できるようにjspファイルを修正
・エラーをすべて表示


・name属性のエラーだけ表示

フォームBean

import java.io.UnsupportedEncodingException;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;

public class UserRegistForm extends ActionForm {
	private String name;
	private String password;
	private String mailaddress;
	private int age;

	//文字化け対策 参照 → http://www.atmarkit.co.jp/fjava/onepoint/svltjsp/svltjsp12.html
	public void reset(ActionMapping mapping, HttpServletRequest request) {
		try {
			request.setCharacterEncoding("JISAutoDetect");
		} catch (UnsupportedEncodingException ex) {
			ex.printStackTrace();
		}
	}

	public int getAge() {
		return age;
	}

	public String getMailaddress() {
		return mailaddress;
	}

	public String getName() {
		return name;
	}

	public String getPassword() {
		return password;
	}

	public void setAge(int i) {
		age = i;
	}

	public void setMailaddress(String string) {
		mailaddress = string;
	}

	public void setName(String string) {
		name = string;
	}

	public void setPassword(String string) {
		password = string;
	}

	public ActionErrors validate(ActionMapping mapping,
		HttpServletRequest request) {
		ActionErrors errors = new ActionErrors();

		if ((name == null) || name.equals("")) {
			errors.add("name", new ActionError("errors.text", "名前"));
		}

		if ((password == null) || password.equals("")) {
			errors.add("password", new ActionError("errors.text", "パスワード"));
		}
		
		if (password.length() < 5 || password.length() > 10){
			errors.add("password2", new ActionError("errors.password", "パスワード"));
		}
		
		if ((mailaddress == null) || mailaddress.equals("")) {
			errors.add("mailaddress", new ActionError("errors.text", "メールアドレス"));
		}

		if ((age < 0) || (age > 120)) {
			errors.add("age", new ActionError("errors.num", "年齢"));
		}
		
		return errors;
	}
}

アクションクラス

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

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class UserRegistAction extends Action{
	public ActionForward execute(ActionMapping mapping, ActionForm form,
	  HttpServletRequest request, HttpServletResponse response) {

		//フォームBean型でキャスト
		UserRegistForm registForm = (UserRegistForm)form;

		//処理
		//たとえばログイン成功かどうかなど。
		//それによってページ遷移を作る。
		//"success"はstruts-config.xmlに渡され、struts-config.xmlで遷移先のページを指定する
		if(){
			  return mapping.findForward("success");
		  }else{
			 return mapping.findForward("error");
		}
	}
}

JSPファイル

<%@ page contentType="text/html; charset=Windows-31J" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<!-- html開始 -->
<html:html>

<head>

<!-- ブラウザのURLではなく、JSPファイルの実際のパスをベースにして相対パスを解釈してくれる -->
<html:base />

<title>JavaBoostScheduler -ログイン画面-</title>
<link rel="stylesheet" href="base.css" type="text/css">
</head>
<body bgcolor="#FFFFFF" text="#000000" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">

<!-- formタグ -->
<html:form action="/login">

<!-- validatorのエラー表示 -->
<html:errors property="loginerror"/>

<table width="400" border="0" cellspacing="0" cellpadding="5">
<tr>
<td bgcolor="#F6F6F6"> <br />
	<table width="100%" border="0" cellspacing="0" cellpadding="2">
	<tr valign="top"> 
	<td width="120">■ユーザ名<font color="#990000">(*)</font></td>
	<td >

	<!-- セレクトボックスで選択 -->
	<html:select property="id">
	<html:options collection="nameList" property="value" labelProperty="key" />
	</html:select>

	</td>
	</tr>
	<tr valign="top"> 
	<td width="120">■パスワード</td>
	<td> 

	<!-- text形式 -->
	<html:password property="password" size="30"/>

	</td>
	</tr>
	</table>
</td>
</tr>
<tr> 
<td class="moko" bgcolor="EEEEEE"> 
	<table width="100%" border="0" cellspacing="0" cellpadding="2">
	<tr> 
	<td> 
	
	<!-- submitボタン -->
	<html:submit value="ログイン"/>

	</td>
	</tr>
	</table>
</td>
</tr>
</table>
</html:form>
<br />

<!-- リンク -->
<html:link action="/toRegistUser">新規ユーザ登録はこちら</html:link>

</body>
</html:html>

DAOTemplate

import java.sql.*;
import javax.naming.InitialContext;
import javax.sql.DataSource;

/**
 * Data Access Objectの共通機能を実装したクラス
 * @author ITBoost
 */
public class DAOTemplate {

	/**
	 * <pre>DAOで管理するデータベースのデータソース名を返す
	 * ※サブクラスでオーバーライドする※</pre>
	 * @return データソース名
	 */
	public String getDataSourceName() {
		return "dummy";
	}

	/**
	 * データベースに接続する
	 * @return Conncetionオブジェクト
	 */
	public Connection createConnection() throws SQLException {
		Connection con = null;
		try {
			// 初期コンテキストを取得
			InitialContext ic = new InitialContext();
			// ルックアップしてデータソースを取得
			DataSource ds =
				(DataSource) ic.lookup("java:comp/env/" + getDataSourceName());
			con = ds.getConnection();
		} catch (Exception e) {
			// テスト用のデータベース情報
			try {
				Class.forName("org.postgresql.Driver");
				// 初期コンテキストを取得
				con =
					DriverManager.getConnection(
						"jdbc:postgresql://sv:5432/strutsdev",
						"postgres",
						"");
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
		return con;
	}

	/**
	 * データベースから切断する
	 * @param con Conncetionオブジェクト
	 */
	protected void closeConnection(Connection con) {
		try {
			if (con != null)
				con.close();
			con = null;
		} catch (SQLException ex) {
			;
		}
	}

	/**
	 * <pre>次のIDを獲得する
	 * ※トランザクションの中で呼び出すこと※</pre>
	 * @param con Conncetionオブジェクト
	 * @param tableName テーブル名
	 * @return 次のID
	 */
	protected synchronized int getNextId(Connection con, String tableName)
		throws SQLException {
		Statement stmt = con.createStatement();
		stmt.execute("LOCK TABLE " + tableName);
		ResultSet rs =
			stmt.executeQuery("select max(id)+1 as next_id from " + tableName);
		rs.next();
		return rs.getInt("next_id");
	}

	/**
	 *  <pre>SQL式をエスケープする。
	 *  PostgreSQLを想定。DBMSによってエスケープすべき文字は異なるので注意が必要。</pre>
	 *  @param aStr エスケープしたいSQL式
	 *  @return エスケープ済みの文字列
	 */
	public static String escapeSQL(String aStr) {
		char c;
		if (aStr == null)
			return null;
		StringBuffer returnStr = new StringBuffer();
		int length = aStr.length();
		for (int i = 0; i < length; i++) {
			c = aStr.charAt(i);
			if (c == '\'') {
				returnStr = returnStr.append("''");
			} else if (c == '\\') {
				returnStr = returnStr.append("\\\\");
			} else if (c == '"') {
				returnStr = returnStr.append("\\\"");
			} else if (c == ';') {
				returnStr = returnStr.append("\\;");
			} else {
				returnStr = returnStr.append(c);
			}
		}
		return new String(returnStr);
	}
}

DAO

import jp.co.itboost.scheduler.common.DAOTemplate;
import java.sql.*;
import java.util.LinkedHashMap;

/**
 * User用DAOクラス
 * @author ITBoost
 */
public class UserDAO extends DAOTemplate {
    /**
     * テーブル名
     */
    private final static String USER_TABLE_NAME = "schedule_user";

    /**
     * DAOで管理するデータベースのデータソース名を返す
     * @return データソース名
     */
    public String getDataSourceName() {
        return "jdbc/strutsdev";
    }    


    /**
     * 指定されたsqlを元にUserオブジェクトを生成する
     * @param sql 任意のSQL
     * @return Userオブジェクト
     */
    private User getUser(String sql) {
        Connection con = null;
        try {
            con = createConnection();
            Statement stmt = con.createStatement();
            ResultSet rs = stmt.executeQuery(sql);
            if (!rs.next()) return null;
            User user = new User();
            user.setId(rs.getInt("id"));
            user.setName(rs.getString("name"));
            user.setPassword(rs.getString("password"));
            user.setMailAddress(rs.getString("mailaddress"));
            return user;
        } catch (SQLException ex) {
            System.out.println("in UserDAO:SQLException! sql=" + sql);
            ex.printStackTrace();
        } finally {
            closeConnection(con);
        }
        return null;
    }

    /**
     * ユーザ情報をデータベースからロードする
     * @param id ユーザID
     * @return Userオブジェクト
     */
    public User load(int id) {
        String sql =
            "select id, name,password,mailaddress from "
                + USER_TABLE_NAME
                + " where id="
                + id;
        return getUser(sql);
    }

    /**
     * ユーザ情報をデータベースに新規保存する
     * @param user Userオブジェクト
     */
    public void create(User user) {
        String sql =
            "insert into "
                + USER_TABLE_NAME
                + " (id, name,password,mailaddress) values(?,?,?,?)";
        Connection con = null;
        try {
            con = createConnection();
            con.setAutoCommit(false);
            int id = getNextId(con, USER_TABLE_NAME);
            PreparedStatement stmt = con.prepareStatement(sql);
            stmt.setInt(1, id);
            stmt.setString(2, user.getName());
            stmt.setString(3, user.getPassword());
            stmt.setString(4, user.getMailAddress());
            stmt.executeUpdate();
            con.commit();
        } catch (SQLException ex) {
            System.out.println("in UserDAO:SQLException! sql=" + sql);
            ex.printStackTrace();
        } finally {
            closeConnection(con);
        }
    }

    /**
     * データベースのユーザ情報を更新する
     * @param user Userオブジェクト
     */
    public void store(User user) {
        String sql =
            "update "
                + USER_TABLE_NAME
                + " set name=?, password=? ,mailaddress=? where id=?";
        Connection con = null;
        try {
            con = createConnection();
            int id = user.getId();
            PreparedStatement stmt = con.prepareStatement(sql);
            stmt.setString(1, user.getName());
            stmt.setString(2, user.getPassword());
            stmt.setString(3, user.getMailAddress());
            stmt.setInt(4, id);
            stmt.executeUpdate();

        } catch (SQLException ex) {
            System.out.println("in UserDAO:SQLException! sql=" + sql);
            ex.printStackTrace();
        } finally {
            closeConnection(con);
        }
    }

    /**
     * ユーザIDとパスワードを指定して合致するユーザ情報を取り出す
     * @param id ユーザID
     * @param password パスワード
     * @return Userオブジェクト
     */
    public User findByIdAndPassword(int id, String password) {
        String sql =
            "select id,name,password,mailaddress from "
                + USER_TABLE_NAME
                + " where id="+id+" and password='"+escapeSQL(password)+"'";        
        return getUser(sql);
    }

    /**
     * ユーザIDと名前の一覧を取り出す
     * @return keyに名前, valueにユーザIDが入ったLinkedHashMapオブジェクト
     */
    public LinkedHashMap findAllIdAndName() {
        String sql = "select id, name from "+USER_TABLE_NAME+" order by id";
        Connection con = null;
		LinkedHashMap ret = new LinkedHashMap();
		try {
			con = createConnection();
			Statement stmt = con.createStatement();
			ResultSet rs = stmt.executeQuery(sql);
			while(rs.next()) {
                ret.put(rs.getString("name"), new Integer(rs.getInt("id")));
			}
		} catch (SQLException ex) {
            System.out.println("in UserDAO:SQLException! sql=" + sql);
            ex.printStackTrace();
        } finally {
            closeConnection(con);
        } 
        return ret;
    }
}