前の目標が終わっていない人はそちらを先行のこと。 前回の 30 ページ検討問題「shouhinedit.html、shouhinedit.jsp」の解答例を示す。 これは XMLHttpRequest(HTTP 通信を行うための JavaScript 組み込みクラス)による非同期通信を利用 して、変更ボタンで指定商品名を変更する問題であった。 なお、XMLHttpRequest のインスタンス取得方法が、ブラウザによって異なるため、それに対応した取得 関数「getMyXmlHttp」が定義してあるファイル「httpRequest.js」を、別途に用意していた。 (「httpRequest.js」を public フォルダからコピーして、それもアップロードする必要がある。) 「shouhinedit.html」の解答例以下に示します。(前のプリントの指示結果を含む) 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <!DOCTYPE html> <html lang="ja"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script type="text/javascript"src="httpRequest.js"></script>
<script type="text/javascript"> function getZaiko(id){
var nameID = document.getElementById(id).value;//入力商品名取得 var httpObj = getMyXmlHttp();//サーバと通信するためのオブジェクト取得 httpObj.open("post",
"http://f270na-53:8080/all/a200/shouhinedit.jsp");//HTTP の POST で情報を要求 httpObj.setRequestHeader('Content-Type',
'application/x-www-form-urlencoded');//文字化け対策 httpObj.onreadystatechange = function(){//HTTP のレスポンスで実行するように関数登録
if(httpObj.readyState == 4 && httpObj.status == 200){
document.getElementById("zaiko").value = httpObj.responseText; alert("取得しました"); } } httpObj.send("nameID=" + nameID);//POST で送るリクエスト要求の送信 } function setZaiko(id){
var httpObj = getMyXmlHttp();//サーバと通信するためのオブジェクト取得 httpObj.open("post",
"http://f270na-53:8080/all/a200/shouhinedit.jsp");//HTTP の POST で情報を要求 httpObj.setRequestHeader('Content-Type',
'application/x-www-form-urlencoded');//文字化け対策
httpObj.onreadystatechange = function(){//HTTP のレスポンスで実行するように関数登録 if(httpObj.readyState == 4 && httpObj.status == 200){
if(httpObj.responseText == -1) alert("変更しましませんでた"); else alert("変更しました");
} }
var nameID = document.getElementById(id).value;//入力商品名取得 var zaiko = document.getElementById("zaiko").value;
httpObj.send("nameID=" + nameID + "&zaiko=" + zaiko);//POST のリクエスト送信 }
</script></head> <body>
<p >商品名:<input type="text" value="goodsA" id="nameID"></p> <p >在庫数:<input type="text" value="0" id="zaiko"></p>
<p>在庫数の<input type="button" value="取得" onclick="getZaiko('nameID')">、 <input type="button" value="変更" onclick="setZaiko('nameID')"></p>
取得ボタンで、06 行 getZaiko 関数を実行。 その中で、httpRequest.js 内の getMyXmlHttp で、XMLHttpRequest を取得し、それで、サーバの shouhinedit.jsp にリクエストする。その応答で、 13 行の function が起動する。以下はその応答メッセ ージを alert で出している。
動作概要 各ボタン操作で、対応するクエリ文字列指定のshouhinedit.jsp を実行させている。 この応答情報が、31 ページソース内の破線部である。この時使うshouhinedit.jsp を下に示す。 上記 21 行や、23 行の out.println が、プリント 31 の「shouhinedit.html」内の破線部分内の httpObj.responseText で得られる文字列になる。それは在庫取得の場合は「在庫数」であり、 変更処理の場合は「変更したレコード数」で、失敗の場合はどちらも「-1」の文字列が返る。 なお、上記で使っているpkg.GoodsDb クラスは、27ページで紹介したクラスに、30ページで示した メソッドを追加したクラスで、all 作品内の「WEB-INF¥classes¥pkg」に用意してある。 (データベースは一つなので、自分が変更した直後に、他人が変更している可能性があるのでデバック の時、そのことを考慮のこと。) pkg.GoodsDb のクラス図を参考に示す。 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
<%@page contentType="text/html; charset=UTF-8" language="java" import="java.util.*"
%>
<%! //メソッド定義
int getZaiko(String nameID){//在庫取得 int r = -1;//リターン用
HashMap <String,Integer> hashMap = new pkg.GoodsDb().getSqlResult(
"SELECT nameID,zaiko FROM shouhin WHERE nameID ='" + nameID + "'"); if( hashMap != null) r = hashMap.get(nameID);//エラー対策していないので注意 return r;
}
int changeZaiko(String nameID, int zaiko){//在庫変更 return new pkg.GoodsDb().sqlUpdate(String.format(
"UPDATE shouhin SET zaiko=%d WHERE nameID = '%s'", zaiko, nameID) ); }
%>
<% //メインの処理 (Ajax の応答用の出力処理なので、HTML のタグはいらない!) String nameID = request.getParameter("nameID");
String zaiko = request.getParameter("zaiko"); if( zaiko == null ) {
out.println( getZaiko(nameID));//在庫取得 } else {
int n = changeZaiko(nameID, Integer.parseInt(zaiko)); //在庫変更 out.println(n); } %> 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 10 <body>
<p >商品名:<input type="text" value="goodsA" id="nameID"></p> <p >在庫数:<input type="text" value="0" id="zaiko"></p>
<p>在庫数の<input type="button" value="取得" onclick="getZaiko('nameID')">、 <input type="button" value="変更" onclick="setZaiko('nameID')"></p>
<p><a href="shouhinPrint.jsp" target="_blank">商品テーブル(shouhin)の確認</a></p> <p><a href="shouhinedit.jsp?nameID=goodsA" target="_blank">
shouhinedit.jsp 取得のデバック用</a></p>
<p><a href="shouhinedit.jsp?nameID=goodsA&zaiko=999" target="_blank"> 変更のデバック用</a></p> </body></html> 上記 07 行で呼び出しており、SELECT で得ら れる 2 列の表を HashMap に入れて返す 上記13行で呼び出しており、 UPDATE の変更処理を行う。
最後にもう一つのAjax 作品例を示す。時間が少なくてもできるようにほとんど作ってあり、public に 置いてある「memo.html」のファイルをコピーし、それを変更するとすぐ実験できるので確認せよ。 この作品は、Twiter のようにメッセージを追加、表示、削除することができるものである。 以下で作った過程を示す。(簡単なメッセージサーバを Servlet で実現する例。) 複数のグループのメッセージ群を、サーバ側の一つのテキストファイル(message01.txt)で管理するこ とにする。まず、このファイルの操作を実現するだけのクラスを pkg. Memo で実現する。 そして、それを操作するサーブレットを pkg.MemoServlet で実現する。 そして、このサーブレットクラスと Ajax 通信で、操作や画面反映を担当する memo.html を作る。 なお、このような作品は、確実に動作ができるサーブレットを作ってから、対応する HTML を作らないと、 どこでエラーが発生しているのか分からなくなる。そこで pkg.MemoServlet の動作確認の単純な HTML を別途に作成する(memotest.html で作成した)。 まず、pkg. Memo クラスに必要な仕様を次のように決めた。サーバ側「message1.txt」のファイルにメ ッセージを記録し、操作(追加、取得、削除)に応じた情報をクライアントに返す簡単な仕様とした。 まず「message1.txt」の構造を次のような構造のレコードの集合に決めた。 (1)グループ識別文字列,(2)メッセージ生成時間の文字列,(3)改行を含まないメッセージ文字列 改行 上記(1)(2)(3)は改行を含めないものとし、「message1.txt」内の改行が、メッセージの数と一致するよ うに制御する。そして各行は、(2)の降順に整列されてファイルに記憶するよう制御する。 上記(1),(2)の直後のコンマを区切文字に使い、それぞれの文字列にはコンマが含まれないよう制御する。 また、各行の個々の情報は変更できないものとする。(削除は可能) このファイルに対するクライアントからの操作で使う HTTP リクエストメッセージのクエリ文字列で使 い名前は、「OP」「GROUP」「TIME」「MESSAGE」の4つで、「TIME」「MESSAGE」は使わない場合がある。 「GROUP」「TIME」「MESSAGE」は、各(1)(2)(3)の文字列に対応する情報用とする。
なお(1)の「GROUP」の値となる文字列は、クライアントからのアクセス制限用に使い、この情報が異な るグループのメッセージを操作することがないように制御する。 「OP」の値となる文字列が操作を意味し、次の通りです。(memo.html からクエリ文字列例も示す) グループで指定した メッセージだけが 発言した時間の 降順で見ることが できます。 追加ボタンで、 メッセージが追加さ れ、匿名でメッセー ジが追加されます。 だれでも、削除ボタ ンで、選択メッセー ジが削除可能。
(a)追加 ( #Memo.add メソッド 内部で、load や save メソッドを利用) 『OP=add&GROUP=▽▽&MESSAGE=○△□』現在時間で▽▽のメッセージ(○△□)を追加します。 応答メッセージは「○△□」の追加したメッセージ文字列 (b)メッセージ文字列群取得 ( #Memo.getd メソッド ) 『OP=get&GROUP=▽▽』▽▽のグループの全てメッセージを、新しい順で取得する。 応答メッセージは『(2)メッセージ生成時間の文字列,(3)改行を含まないメッセージ文字列 改行』が列挙されたイメージで得られる。
(c)削除 ( #Memo.load メソッド 内部で、load や save メソッドを利用)
『OP=del& GROUP=▽▽&TIME=□□』▽▽のグループで□□の時間のメッセージを削除する。 応答メッセージは削除した(3)のメッセージ文字列 以 上 の 操 作 を 行 う た め の Memo.java を 作 り 、 そ れ を サ ー ブ レ ッ ト 上 で 実 現 す る MemoServlet.java を次のように作成した。(MemoServlet.java で上記操作以外は、要求したク エリ文字列を返します) 上記を実現した、pkg. Memo とこれを利用する pkg.MemoServlet のクラス図を示す。 以下に memo.html を示す。(public からコピーして削除の部分を追加してみよう。) 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <!DOCTYPE html> <html><head> <meta charset="UTF-8">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache"> <meta http-equiv="Expires" content="0">
<style type="text/css"> //ここは省略・・します。実際のソースを参照のこと</style> <title>memo</title> <script type="text/javascript"src="httpRequest.js"></script> </head><body><h1>メッセージ操作</h1> <div id="msg"></div>
グループ名:<input type="text" id="GROUP" size="10" value="test"><br> メッセージ:<input type="text" size="70" id="MESSAGE"><br><br>
メッセージの<input type="button" value="取得" onclick="getMsg()"> 、<input type="button" value="追加" onclick="addMsg()">
、<input type="button" value="削除" onclick="delMsg()"> </body>
<script type="text/javascript">
var inputGROUP = document.getElementById("GROUP"); var inputMESSAGE = document.getElementById("MESSAGE");
function sendMessage(postMsg){
var httpObj = getMyXmlHttp();//サーバと通信するためのオブジェクト取得 httpObj.open("post",
"../MemoServlet", false);//HTTP の POST で要求(第2引数で、同期式の使い方を指定) //第 3 引数が、false で同期式を指定している。 httpObj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');//文字化け対策 httpObj.send(postMsg);//POST でリクエスト要求の送信,同期式の場合は、次で応答を受信 return httpObj.responseText;//レスポンス文字列を返す。 } function getMsg(){ var msg=sendMessage("OP=get&GROUP="+encodeURI(inputGROUP.value)); if(inputGROUP.value == "")document.getElementById("msg").innerHTML = msg; else{ var a = msg.split("¥r¥n");
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 function getMsg(){ var msg=sendMessage("OP=get&GROUP="+encodeURI(inputGROUP.value)); if(inputGROUP.value == "")document.getElementById("msg").innerHTML = msg; else{ var a = msg.split("¥r¥n");
if(a == null || a.lenght ==0) return; var i,len = a.length;
var s = "";
for(i=0; i < len; i++){ if(a[i] == "") continue; //alert(a[i]);
var i1 = a[i].indexOf(","); if(i1 == -1) continue;
var t = a[i].substring(0, i1);
s+="<div class='M'><input type='radio' name='R' value='"; s+=t+"'>";
s+= new Date(parseInt(t)) . toLocaleString(); s+= " [" + inputGROUP.value + "]のメッセージ <br>"; s+= a[i].substring(i1+1); s+="</div>¥n"; } //alert(s); document.getElementById("msg").innerHTML = s; } } function addMsg(){ var s = "OP=add&GROUP="+encodeURI(inputGROUP.value); s+="&MESSAGE="+encodeURI(inputMESSAGE.value); //alert(s); var msg=sendMessage(s); //alert(msg); getMsg(); } function delMsg(){ var a = document.getElementsByName("R"); var delTime = null;
if(a == null || a.length == 0) { alert("削除項目がありません。"); return;
}
var i=0;
for(i=0; i < a.length; i++){ if(a[i].checked) { delTime = a[i].value; break; } } if(delTime == null){ alert("削除項目が選択されていません。"); return; } var s = "OP=del&GROUP="+encodeURI(inputGROUP.value); s+="&TIME="+delTime; //alert(s);
var msg=sendMessage(s); //alert(msg); getMsg();
}
</script> </html>
pkg.MemoServlet のソースを示す。(all 作品内の「WEB-INF¥classes¥pkg」に用意してある。) pkg. Memo クラスの呼び出しは、37 行、39 行、41 行です。 それぞれで、テキストファイル(message01.txt)に対する追加、取得、削除の処理ができるように pkg. Memo クラスを作った。 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package pkg; import java.io.*; import javax.servlet.*; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; @WebServlet("/MemoServlet")
public class MemoServlet extends HttpServlet { private static final long serialVersionUID = 2L; Memo memo = new Memo();
public void doGet(HttpServletRequest req,HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8");//日本語が化け対策。
PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<body>" ); out.println("<H1>メッセージサーバ</H1>"); out.println("</body>"); out.println("</html>"); }
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter(); String op = request.getParameter("OP");
String group = request.getParameter("GROUP"); String time = request.getParameter("TIME"); if(group == null) group ="";
if(time == null || time.equals("")) time ="0"; if(op == null){
out.print("操作なし"); } else {
try {
if(op.equals("add")){
String message = request.getParameter("MESSAGE"); //out.print("op:"+op+",group:"+group+",message:" + message); out.print(memo.add(group, message)); }else if(op.equals("get")){ out.print(memo.get(group)); }else if(op.equals("del")){ out.print(memo.del(group, Long.parseLong(time))); }else{ out.print("不正命令"); } }catch(Exception e){ out.print(e.getMessage()); } } } }
pkg. Memo のソースを示す。(all 作品内の「WEB-INF¥classes¥pkg」に用意してある。) 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package pkg; import java.io.*; import java.util.*; public class Memo {
String path="E:¥¥xampp¥¥htdocs¥¥a200¥¥message01.txt"; File file = new File(path);
ArrayList <String> list; public Memo(){
if(file.exists()==false) {
new RuntimeException("no file:" + file.getAbsolutePath()); }
}
static long getTime(String s){//最初コンマ, から次のコンマの文字列を数値変換して返す int i1 = s.indexOf(',');
int i2 = s.indexOf(',', i1+1); String t = s.substring(i1+1, i2); //System.out.println("Time:"+t); return Long.parseLong(t);// 生成時間 } void sort(){//list の生成時間によるソート(降順) int i,k; //添え字 String work; //挿入に使う作業用 String temp; i = 0; while(i < list.size()-1){ k =i+1; work = list.get(k); while(k-1 >= 0){ temp = list.get( k-1 ); long wi = getTime(work); long ti = getTime(temp); if( ti >= wi )break; list.set(k, temp); k--; } list.set(k, work); i++; } }
void load() throws Exception {
FileInputStream fis = new FileInputStream(file);
BufferedReader br = new BufferedReader(new InputStreamReader(fis,"utf-8")); list = new ArrayList<String>();
String s;
while((s = br.readLine()) != null){ if(s.equals(""))continue; list.add(s); } fis.close(); sort();// 並び替え }
void save() throws Exception {
PrintWriter pw = new PrintWriter(file,"utf-8"); for(int i=0; i < list.size(); i++){
pw.print(list.get(i) + "¥r¥n"); }
pw.close(); }
synchronized public String add(String groupId, String message)throws Exception {
Date date = new Date();
String s = groupId + ',' + date.getTime() + ',' + message; load();
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
void save() throws Exception {
PrintWriter pw = new PrintWriter(file,"utf-8"); for(int i=0; i < list.size(); i++){
pw.print(list.get(i) + "¥r¥n"); }
pw.close(); }
synchronized public String add(String groupId, String message)throws Exception { Date date = new Date();
String s = groupId + ',' + date.getTime() + ',' + message; load();
list.add(s); save();
return message; }
synchronized public String get(String groupId)throws Exception { boolean debug = groupId.equals("");//デバック用で、確認後に false にする load();
StringBuilder sb = new StringBuilder(); groupId += ",";
int k = groupId.length();
for(int i=0; i < list.size(); i++){ String s = list.get(i); if(s.startsWith(groupId)){ sb.append(s.substring(k)+"¥r¥n"); }else if(debug){ sb.append(s+"¥r¥n"); } } return sb.toString(); }
synchronized public String del(String groupId, long time)throws Exception { load();
groupId += ",";
int k = groupId.length(); String rtnval ="";
for(int i=0; i < list.size(); i++){ String s = list.get(i);
if(s.startsWith(groupId) && getTime(s) == time) { list.remove(i);//削除 save(); int n = s.indexOf(',') + 1; rtnval=s.substring(s.indexOf(',',n)+1);//メッセージ部取得 break; } } return rtnval; } }