mb_language("uni"); mb_internal_encoding("utf-8"); mb_http_input("auto"); mb_http_output("utf-8"); ?>
前回までの開発により、住所録として利用するための基本的な機能である表示/検索/登録/編集/削除の実装が終了しました。Xprioriを利用した実装の流れを理解していただけたのではないでしょうか。今回からは展開が変わります。今までは未完成の住所録に機能を追加していきましたが、今回からは出来上がった住所録アプリケーションに対して幾つかの機能を追加していくことにします。仮想の顧客が出来上がった住所録をみて、幾つかの機能の追加を要求してくるイメージです。
今回と次回では、住所録アプリケーションに認証機能を追加します。まずは追加する認証機能を設計します。
ここで第1回目のページ遷移図を思い出してみます。その遷移図にはログイン画面、ユーザ一覧画面、ユーザ追加画面がありました。それらの画面を実装後は以下のような動きとなります。
・ ログイン画面でログインしたユーザのみが住所録を閲覧できる
・ ログインできるユーザの情報はデータベースに登録されている
・ ログイン画面で管理者ユーザとしてログインした場合はユーザの閲覧/追加/削除ができる
ユーザ情報を取扱うデータベースには、引き続きXprioriを利用します。登録する項目は
・ ユーザID
・ ユーザ名
・ パスワード
の3項目です。
ツリー構造、スキーマは以下のようになります。
▲ツリー構造
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="user_info"> <xsd:complexType> <xsd:sequence> <xsd:element ref="user" maxOccurs="unbounded"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="user"> <xsd:complexType> <xsd:attribute name="id" type="xsd:string"/> <xsd:sequence> <xsd:element ref="name"/> <xsd:element ref="password"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="name" type="xsd:string"/> <xsd:element name="password" type="xsd:string"/> </xsd:schema> |
なお、ここではaddress_bookの場合と同様に読者にデータ構造を理解していただくためXMLスキーマを明記しましたが、Xpriori側ではXMLスキーマの登録などは必要ありません。
要素名 |
説明 |
user_info | ルートノード |
user | ユーザをあらわすノード、ユーザ名とパスワードを子ノードに持ち、属性値としてユニークなIDを持つ |
name | ユーザ名を表すノード、半角英数字のユーザ名が記述されたテキストノードを子ノードに持つ |
password | ユーザのパスワードを表すノード、半角英数字のパスワードが記述されたテキストノードを子ノードに持つ |
初期データの挿入時にあらかじめXprioriに管理者ユーザを登録します。管理者ユーザとは、ユーザ名が「admin」、ユーザIDが「000001」のユーザのことで、このユーザでログインした場合のみユーザ操作を行うことができます。
<user_info> <user id="000001" > <name>admin</name> <password>adminpass</password> </user> </user_info> |
上記のようなデータを元に認証を行えるようにします。本来ならばpasswordノードにはパスワードを直接格納せず、SHA1などでハッシュした文字列を格納すべきですが、本シリーズではユーザ登録の結果をXprioriでも確認できるよう、あえて暗号化は行いません。
今回の記事では今までの住所録アプリケーションでの実装で学んだことを生かし、ユーザ一覧画面、ユーザ登録画面を作成します。基本的な実装方法は住所録と同じです。なお、認証関連の機能については、次回実装を行います。作成するクラスは以下の3つです。
クラス名 |
概要 |
UserBean | ユーザ情報を取扱う |
SearchUserInfoBean | ユーザをXprioriから取得する部分を取扱う |
UpdateUserInfoBean | ユーザをXprioriに追加/削除する部分を取扱う |
それぞれのクラスは、これまで住所録のアイテム検索や登録を実装するために作成したクラスを応用したものです。以下にそれぞれのクラスを説明します。
UserBeanクラスはユーザ情報を取扱うクラスです。住所録のアイテム用にItemBeanクラスを作成しましたが、それと同じような機能を持っています。
● ユーザ情報の設定/取得
UserBeanのコンストラクタ呼び出し時にuser要素に対応するDOMのノードを渡し、そこからID属性、name要素、password要素を取得し、ユーザ情報を設定します。また、空のUserBeanクラスを作成後に、set***関数を利用してユーザ情報を設定することも可能です。なお、設定したユーザ情報はget***関数により取得することができます。
● user要素の取得
getUserXML関数により、UserBeanクラスに設定したユーザ情報をuser要素(XML)として取得することができます。
各関数の処理の流れは、ItemBeanクラスと同じですので、詳細はコードを参照してください。
SearchUserInfoBeanは、XprioriにXPathを発行し、ユーザ情報を取得するためのクラスです。住所録のアイテム取得のために作成したSearchAddressbookBeanクラスと同様の機能を持っています。
● ユーザの総数を取得する(CountAllUser)
登録済みのユーザ数を取得するXPath
count(/ND/user_info/user) |
をXprioriに問い合わせ、返って来たXML文字列を解析し、ユーザ数を取得します。
● 指定数のユーザを取得(getUsers)
取得開始番号とオフセット値から、指定した範囲のuser要素を取得するXPath
/ND/user_info/user[position() >= {取得開始番号} and position() < ( {取得開始番号} + {オフセット値} )] |
をXprioriに問い合わせます。返ってきたXML文字列中の複数のuser要素を取り出し、そこからUserBeanクラスを生成して指定数のユーザを取得します。
● ユーザIDからユーザを取得する(getUserById)
ユーザIDからuser要素を取得するXPath
/ND/user_info/user[@id = {ユーザIDの値}] |
をXprioriに問い合わせ、返ってきたuser要素からUserBeanオブジェクトを生成し、ユーザを取得します。
● 新規ユーザ用のIDを取得する(getNewId)
ユーザIDの最大値を取得するためのXPath
max(/ND/address_book/item/@id) |
をXprioriに問い合わせ、返ってきたXMLから最大値を取り出し、その値に+1した数を新規ユーザIDを返します。
● パラメータからユーザを取得する(getUserByParam)
リクエストパラメータを取扱うParamBeanクラスを引数として渡し、パラメータが持つユーザ情報を設定したUserBeanクラスを返します。
各関数の処理の流れは、SearchAddressbookBeanクラスと同じですので、詳細はコードを参照してください。
UpdateUserInfoBeanは、Xprioriに対してユーザ情報のデータ操作を行うためのクラスです。住所録のアイテム取得のために作成したUpdateAddressbookBeanクラスと同様の機能を持っています。
● ユーザを追加する(insertUser)
追加するユーザ情報を取扱うUserBeanクラスのgetUserXML関数から取得したuser要素と、user要素を追加する位置を指定するXPath
/ND/user_info/user[last()] |
を、ConnectXprioriBeanクラスのinsert関数に渡し、ユーザの追加を実行します。
● ユーザを削除する(deleteUser)
削除するユーザ情報を取扱うUserBeanクラスからユーザIDを取得し、削除するuser要素を指定するXPath
/ND/user_info/user[@id = {削除するuser要素のID}] |
を作成し、ConnectXprioriBeanクラスのdelete関数に渡し、ユーザの削除を実行します。
各関数の処理の流れは、UpdateAddressbookBeanクラスと同じですので、詳細はコードを参照してください。
ユーザ一覧画面では第04回で作成したPagerクラスを利用し、1ページあたりの表示ユーザ数を20件までとしたページング機能を実装します。表示する項目はユーザIDとユーザ名です。コードを以下に示します。
<%@ page language="java" contentType="text/html; charset=Shift_JIS" pageEncoding="Shift_JIS"%> <%@page import="addressbook.UserBean"%> <jsp:useBean id="searchuser" class="addressbook.SearchUserInfoBean"></jsp:useBean> <jsp:useBean id="pager" class="addressbook.PagerBean"></jsp:useBean> <jsp:useBean id="param" class="addressbook.ParamBean"></jsp:useBean> <jsp:setProperty name="pager" property="page_num" param="page" /> <% //(1)登録されているユーザの数を取得する int user_count = searchuser.CountAllUser(); pager.setMax_item(user_count); //PagerBeanにユーザ最大数を設定する int start_num = (pager.getOffset() * (pager.getPage_num() - 1) ) + 1 ; //(2)ユーザを範囲指定で取得する UserBean[] userlist = searchuser.getUsers(start_num,pager.getOffset()); int user_num = userlist.length; int end_num = start_num + user_num - 1; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS"> <title>ユーザ一覧</title> </head> <body> <center> <table width="400"><tr><td> <table border="0" cellpadding="0" cellspacing="0" width="100%"> <tr> <td ><h3>ユーザ一覧</h3></td> </tr> </table> <table border="0" cellspacing="0" cellpadding="3"> <tr><td width="65%" > </td> <td width="35%" align="right"> <font size="-1"><a href="/address_book/inputuser.jsp">新規ユーザを登録する</a></font> </td> </tr> </table> <table width="100%" border="0" cellspacing="0" cellpadding="3"> <tr> <td width="1%" nowrap ><font size="4" color="#000fff"><b><%= user_count %></b></font>件中 <%= start_num %>~ <%= end_num %>件を表示 </td> <%--(3)ページング処理--%> <td width="1%" nowrap > </td> <td width="99%" align="right" > < <% if(pager.getPrev_page() == 0){ %>前ページ<%}else{ %><a href="/address_book/itemlist.jsp?page= <%= pager.getPrev_page() %>" class="content">前ページ</a><% } %> | <% if(pager.getNext_page() == 0){ %>次ページ <%}else{%><a href="/address_book/itemlist.jsp?page= <%= pager.getNext_page() %>" class="content">次ページ</a><% } %> > </td> </tr> </table> <table width="100%" border="1" cellspacing="0" cellpadding="3"> <tr> <th width="100">ID</th> <th width="250">ユーザ名</th> <th width="50">削除</th> </tr> <%--(4)for文によるループでユーザ情報を表示する--%> <% for(int i = 0; i < user_num;i++){ %> <tr> <td width="100" align="center"><%= userlist[i].getID() %></td> <td width="250" align="center"><%= userlist[i].getName() %></td> <td width="50" align="center"><a href="/address_book/runuser.jsp?id=<%= userlist[i].getID() %> &mode=<%= param.DEL_MODE %>">削除</a></td> <% }%> </tr> </table> </td></tr> </table> </center> </body> </html> |
処理の流れを説明します。
(1)登録されているユーザの総数を、SearchUserInfoBeanクラスのCountAllUser関数を利用して取得する。
(2)SearchUserInfoBeanクラスのgetUsers関数を利用して、開始番号とオフセット値で指定した範囲のユーザを、UserBeanクラスの配列として取得する。
(3)PagerBeanを利用して、次/前ページ判定を行い、次/前ページへのリンクをつける。
(4)取得したUserBeanクラス配列をfor文でループし、ユーザのIDと名前、削除ページへのリンクを表示する。
実行結果は以下のようになります。
▲ユーザ一覧画面
ユーザ入力画面は、入力フォームにより登録するユーザのユーザ名とパスワードを入力する画面です。コードを以下に示します。
<%@ page language="java" contentType="text/html; charset=Shift_JIS" pageEncoding="Shift_JIS"%> <%@page import="addressbook.UserBean"%> <% request.setCharacterEncoding("Shift_JIS"); %> <jsp:useBean id="searchuser" class="addressbook.SearchUserInfoBean"></jsp:useBean> <jsp:useBean id="param" class="addressbook.ParamBean"></jsp:useBean> <jsp:setProperty name="param" property="id" param="id" /> <jsp:setProperty name="param" property="name" param="name" /> <jsp:setProperty name="param" property="errmsg" param="errmsg" /> <% // 入力用アイテムの雛形を定義 UserBean input_user = new UserBean(); //(1) 入力エラーで戻ってきてないか判断する if(param.getErrmsg().equals("")){ // (2-1)新規追加の場合は空のアイテムを作り、IDを割り当てる UserBean user = new UserBean(); user.setID(searchuser.getNewId()); input_user = user; } else{ //入力エラー発生時 //(2-2)パラメータからアイテムを作る UserBean user = searchuser.getUserByParam(param); input_user = user; } %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS"> <title>ユーザ追加</title> </head> <center> <table width="550"><tr><td> <table border="0" cellpadding="0" cellspacing="0" width="100%"> <tr><td ><h3>ユーザ情報追加</h3></td></tr> </table> <form action="/address_book/runuser.jsp" method="get"> <table width="100%" border="0" cellspacing="0" cellpadding="3"> <%-- 入力エラー発生時にはエラーメッセージを表示する --%> <%if(!param.getErrmsg().equals("")){ %> <tr> <td width="1%" nowrap >入力エラーが発生しました。<%= param.getErrmsg() %></td> <td width="99%" align="right"></td> </tr> <% } %> <tr> <td width="1%" nowrap ></td> <td width="99%" align="right"><a href="/address_book/userlist.jsp">一覧に戻る</a></td> </tr> </table> <%-- (3)ユーザ情報をテキストボックスにいれる --%> <table width="100%" border="1" cellspacing="0" cellpadding="3"> <tr><th>ユーザID</th><td><%= input_user.getID() %></td></tr> <tr><th>ユーザ名</th><td><input type="text" name="name" value="<%= input_user.getName() %>"> <font size="-1">(半角英数字で入力してください)</font></td></tr> <%-- パスワードボックスは常に空にする--%> <tr><th>パスワード</th><td><input type="password" name="password" value=""> <font size="-1">(半角英数字で入力してください)</font></td></tr> <tr><th>パスワード(確認用)</th><td><input type="password" name="passwordc" value=""></td></tr> <tr><td colspan="2" align="center"> <input type="hidden" name="id" value="<%= input_user.getID() %>"> <input type="hidden" name="mode" value="<%= param.INS_MODE %>"> <input type="submit" value="確認"> </td></tr> </table> </form> </td></tr></table> </center> </body> </html> |
処理の流れを説明します。
(1)実行画面で入力エラーが発生し、実行画面から戻ってきた場合、エラーメッセージがパラメータに含まれるので、それを見て入力エラーかどうかを判断する。
(2-1)入力エラーではない場合は、空のアイテムを作成し、SearchUserInfoBeanクラスのgetNewId関数で取得した新規IDを割り当てる。
(2-2)入力エラーの場合は、前回入力した値がパラメータで渡されるので、入力した値をフォームに再度表示するためにSearchUserInfoBeanクラスのgetUserByParam関数を利用してパラメータからユーザを作成する。
(3)ユーザ情報を入力するためのフォームを作成し、新規の場合は新規ユーザIDを割り当てた空のユーザの情報を、エラーの場合はパラメータから作成したユーザの情報を表示し、IDはhidden値に代入する。また、パスワードボックスは常に空にする。
実行結果は以下のようになります。
▲ユーザ入力画面
ユーザ登録の場合は、第06回で作成したCheckBeanクラスを拡張して、入力画面で入力されたユーザ情報のチェックを行います。チェックする項目は
・ ユーザIDが6桁の半角数字で構成されているか
・ ユーザ名、パスワードが半角英数字で入力されているか
・ パスワードが確認用のパスワードと一致しているか
です。エラーがあった場合はエラーメッセージをパラメータとして渡し、ユーザ入力画面に戻ります。
エラーがなかった場合、又はユーザ削除の場合は、ユーザ登録/削除を実行し、ユーザ一覧画面に戻ります。なお、この画面はユーザには見えない画面です。コードを以下に示します。
<%@ page language="java" contentType="text/html; charset=Shift_JIS" pageEncoding="Shift_JIS"%> <%@page import="addressbook.UserBean"%> <% request.setCharacterEncoding("Shift_JIS"); %> <jsp:useBean id="searchuser" class="addressbook.SearchUserInfoBean"></jsp:useBean> <jsp:useBean id="updateuser" class="addressbook.UpdateUserInfoBean"></jsp:useBean> <jsp:useBean id="param" class="addressbook.ParamBean"></jsp:useBean> <jsp:useBean id="check" class="addressbook.CheckBean"></jsp:useBean> <jsp:setProperty name="param" property="id" param="id" /> <jsp:setProperty name="param" property="name" param="name" /> <jsp:setProperty name="param" property="password" param="password" /> <jsp:setProperty name="param" property="passwordc" param="passwordc" /> <jsp:setProperty name="param" property="mode" param="mode" /> <% if(param.getMode() == param.INS_MODE){ // (1)入力内容をチェックし、入力エラーの場合はユーザ一覧画面に戻る check.checkUserParam(param); if(check.getErrflg()){//エラーがある場合は入力画面に戻る %> <jsp:forward page="inputuser.jsp"> <jsp:param name="errmsg" value="<%= check.getErrmsg() %>" /> <jsp:param name="id" value="<%= param.getId() %>" /> <jsp:param name="name" value="<%= param.getName() %>" /> </jsp:forward> <% } } //(2)パラメータidからユーザを取得する UserBean user = searchuser.getUserById(param.getId()); // パラメータmodeをみて新規追加か既存編集か既存削除かを決める if(param.getMode() == param.INS_MODE ){ // 新規追加の場合 //(3)同一IDを持つユーザが登録されていないかチェックする if(user.getID().equals("")){//まだ登録されていない //(4)パラメータからユーザを作成する UserBean insert_user = searchuser.getUserByParam(param); //(5)ユーザをXprioriに登録する updateuser.insertUser(insert_user); } } else if(param.getMode() == param.DEL_MODE){ // 既存削除の場合 //(3)同一IDを持つユーザが登録されていることをチェックする if(!user.getID().equals("")){// 登録済み //(4)パラメータからユーザを作成する UserBean delete_user = searchuser.getUserByParam(param); //(5)ユーザをXprioriから削除する updateuser.deleteUser(delete_user); } } %> <%--(6)処理終了後に一覧ページに戻る --%> <%-- 処理終了後に一覧ページに戻る --%> <jsp:forward page="userlist.jsp" ></jsp:forward> |
処理の流れを説明します。
(1)パラメータmodeがINS_MODE(値は1)だった場合は新規追加なので、CheckBeanクラスを利用して入力された値をチェックし、エラーがあった場合はエラーメッセージをパラメータに渡し、ユーザ一覧画面に戻る。
(2)SearchUserInfoBeanクラスのgetUserById関数を利用して、パラメータidからユーザを取得する。
(3)パラメータmodeがINS_MODEだった場合は、(2)で取得したアイテムが空かどうかチェックし、同一IDのユーザが登録されていないことを確認する。パラメータmodeがDEL_MODEだった場合は、(2)で取得したアイテムが空でないことをチェックし、、同一IDのユーザが登録されていることを確認する。
(4)SearchUserInfoBeanクラスのgetUserByParam関数を利用してパラメータからユーザを作成する。削除の場合はIDのみ設定されたユーザとなる。
(5)パラメータから作成したユーザの情報を利用して、UpdateUserInfoBeanクラスのinsertUser/deleteUser関数により、ユーザを登録/削除する。
(6)登録/削除後は、JSPのforwardタグを利用してユーザ一覧画面に遷移し、ユーザに画面が登録/実行画面が見えないようにする。
▲登録されたユーザ
今回作成した機能は、住所録のアイテム表示/登録/削除機能を応用したものです。今まで紹介してきた内容を思い出していただけたのではないでしょうか。ユーザの表示/登録/削除機能を実装しましたので、次回は登録されたユーザのみ住所録アプリケーションを利用できる認証部分の実装を紹介します。
今回作成したjavaクラス・jspファイルはこちらからダウンロードすることができます。
・ jspファイルはWebアプリケーションフォルダのaddress_book/WebContentに配置してください。
・ javaファイルはWebアプリケーションフォルダのaddress_book/WebContent/WEB-INF/srcに配置してください。
また、ご自分でクラスを新規作成する場合はEclipseのメニューバーより、[ファイル]→[新規]→[クラス]を選択し、[次へ]を押してください。
次の画面で[ソースフォルダ]に「address_book/src」、[パッケージ]に「addressbook」を入力し、[名前]に適切なクラス名を入力し、[終了]を押すことで、新規のクラスを作成することができます。
▲このページのTOPへ