■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2008年04月20日

    楽しいJava講座 - 初心者から達人へのパスポート
                  vol.100

                                セルゲイ・ランダウ
 バックナンバー: http://www.flsi.co.jp/Java_text/
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■


[このメールマガジンは、画面を最大化して見てください。]


========================================================
◆ 01.Tomcatのアプリケーション開発
========================================================


では、前回の答えです。


今まで何度かお話してきたように、Webアプリケーションの開発
にあたって面倒なことの一つは、文字コードの取り扱いです。

というわけで、このアプリケーションで扱われる文字コードに
ついて整理しておきましょう。


まず、Javaはプログラム内部の文字コードとしてUnicodeを採用
しているのでしたね。
つまり、vol.015でお話したように、Javaのchar型やString型で
保持される文字はUnicodeの形式になっています。

ところが、MySQLのデータベースに保持される文字はShift_JISに
設定していました(vol.084参照)。

さて、この間の文字コードの変換は如何?

このJavaプログラム内のUnicodeとデータベース上のShift_JIS
との間の変換はどうなっているのかというと、実は、MySQLの
JDBCドライバーが自動的に変換してくれます。
(この変換はMySQLのJDBC URLを指定するときに明示的に指定する
こともできますが、デフォルトで変換されることになっていますの
で、明示的な指定は不要です。)

というわけで、Javaプログラムとデータベースの間の文字コードの
変換については問題はないはずですな。


あと問題になるのは、Webブラウザー上の文字コードとサーバー側
(サーブレットやJSP = Javaプログラム)の文字コードとの間の
変換ですな。

そこで、vol.087を復習してみましょう。
サーブレットはその変換の機能を持っているのですが、あらかじめ
どの文字コードで送られてくるのかをサーブレットに教えてあげな
ければ変換できません。

そこで、vol.087でお話したように、たとえばWebブラウザーでの
エンコーディングがShift_JISの場合(つまり、文字コードがShift_JIS
の場合)は、HttpServletRequestオブジェクト(正確にはそのスーパー
クラス)のsetCharacterEncoding("Shift_JIS")メソッドを呼び出すこと
によって、文字コードがShift_JISであることをサーブレットに教え
ます。

そうすると、サーブレットは、送られてきたデータをShift_JISから
Unicodeに変換して取り込んでくれます。


ところが、もしsetCharacterEncoding()メソッドに指定する引数(エンコー
ディング)が間違っていた場合はどうなるでしょう。
サーブレットは、そのまま間違った変換をしてしまいます。

すると、MySQLのJDBCドライバーがいくらUnicodeからShift_JISへの変換を
行っても、元のUnicodeのデータが間違っているわけですから、データベース
に保管された文字は文字化けした状態になります。


そこで、各サーブレットを確認してみましょう。

まず、vol.087で説明したItemServletでは、setCharacterEncoding()メソッド
の引数は"Shift_JIS"になっていますが、これは、このサーブレットを呼び出す
HTML(itemSelect.html)がShift_JISでエンコーディングされているからです。


ところが、その後、JSPによって生成されるWebページは、Shift_JISではなく、
UTF-8でエンコーディングされます。
というのは、各JSPでは、
--------------------------------------------------------
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="Shift_JIS" %>
--------------------------------------------------------
というようにcharset=UTF-8を指定をしておきましたから、これらのJSPから
生成されるWebページのエンコーディングはUTF-8になります。

そうすると、これらのWebページから入力されるデータを受け取るサーブレット
では、setCharacterEncoding()メソッドを呼び出すときの引数にはUTF-8を
指定しなければなりません。

ところが、今までのサーブレットはすべて、

req.setCharacterEncoding("Shift_JIS");

というように、Shift_JISを指定していました。

これは、最初のItemServletのソース・コードをコピーして利用し、そのまま
修正するのを忘れていたからなのですが、ユーザー登録の機能を組み込むまでは
気がつきませんでした。それまで文字化けが発生しなかったからです。

では、なぜそれまで文字化けが発生せず、ユーザー登録の機能を組み込んだ時点
で文字化けが発生したかというと、ユーザー登録の機能を組み込む前は、Webページ
に入力されるデータは半角英数字のみだったからです。

実は、Shift_JISもUTF-8も半角英数字の部分だけに限ると同じコードを使用して
います。半角英数字の部分はどちらもASCII(American Standard Code for
Information Interchange)という文字コードと同じコードを使用しているので、
文字化けが発生しなかったのです。

しかし、ユーザー登録の機能では、漢字など(いわゆる全角文字)を入力します
から、Shift_JISとUTF-8ではコード体系が違うために文字化けが発生したのです。


というわけで、今回の問題は、少なくとも、ユーザー登録の機能に関わるサーブレッ
トの中で、

req.setCharacterEncoding("Shift_JIS");



req.setCharacterEncoding("UTF-8");

に書き換えれば解決します。


では、UserRegServletを修正しましょう。
--------------------------------------------------------
package jp.co.flsi.lecture.webapp.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import jp.co.flsi.lecture.webapp.db.CustomerDbManager;
import jp.co.flsi.lecture.webapp.entity.Customer;

public class UserRegServlet extends HttpServlet {
   public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException {
      req.setCharacterEncoding("UTF-8");
      HttpSession session = req.getSession();
      String customerNum = req.getParameter("userId");
      String customerName = req.getParameter("name");
      String customerZipCode = req.getParameter("zipCode");
      String customerAddress = req.getParameter("address");
      if (customerNum == null
            || customerName == null
            || customerZipCode == null
            || customerAddress == null) {
         getServletContext().getRequestDispatcher("/wrongUserInfo.jsp").forward(req,res);
      }
      else {
         Customer customer = new Customer();
         customer.setNum(customerNum);
         customer.setName(customerName);
         customer.setZipCode(customerZipCode);
         customer.setAddress(customerAddress);
         session.setAttribute("CUSTOMER",customer);
         CustomerDbManager customerDb = new CustomerDbManager();
         customerDb.selectData(customerNum);
         if (customerDb.getCustomerList().size() > 0) {
            getServletContext().getRequestDispatcher("/userReg2.jsp").forward(req,res);
         }
         else {
            customerDb.insertData(customer);
            getServletContext().getRequestDispatcher("/userInfo.jsp").forward(req,res);
         }
      }
   }

   public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException {
      getServletContext().getRequestDispatcher("/bookmarkinvalid.html").forward(req,res);
   }

}
--------------------------------------------------------
(念のためにUserRegServletの全ソース・コードを提示しましたが、
修正したのは言うまでも無く
      req.setCharacterEncoding("UTF-8");
の部分だけです。)



では、これでテストしてみましょう。

EclipseでTomcatを起動し、Webブラウザーを起動して、下記のURLを
入力してください。

http://localhost:8080/JStudy2/itemSelect.html

(1) 「キーワード」欄、「カテゴリー」欄ともに何も入力せずに
「送信」ボタンをクリックしてみてください。

(2) 次のページ(商品リストのページ)で商品をどれか(たとえば「製品001」)
選択(チェック・マークを入れる)し、「選択した商品をカートに入れる」
ボタンをクリックします。

(3) 選択した商品が次のページ(ショッピング・カートのページ)で
リストされていることを確認し、「選択した商品を購入する」ボタンを
クリックします。

(4) 次のページ(購入手続きのページ)で、
「ユーザー登録がお済みでない方は、先にここをクリックしてユーザー登録を行ってください。 」
をクリックします。

(5) 次のページ(ユーザー登録のページ)で、ユーザーIDに   UUUU0、パスワードはxxxxx
、氏名に山本山太郎、郵便番号は222-2222、住所はほにゃら県ほにゃら町1-1-1と入力
して、「規約に同意しユーザー登録をする」ボタンをクリックしましょう。

(6) 次のページ(ユーザー登録のページ)で「注文手続きに進む」ボタンをクリック
しましょう。

(7) 次のページで、購入する商品がちゃんとリストされていることを確認し、
商品の送付先を確認しましょう。今度は文字化けしていないですね。
めでたし、めでたし。

・・・・と思ったら、「購入(注文確定)する」ボタンをクリックすると、
何やら訳のわからないエラーが出ますね。エラーの文を読んでいくと、

The requested resource (/JStudy2/completeorder) is not available.

というのが書かれているはずです。これは、/JStudy2/completeorder
というURL(最初から全部書くと
http://localhost:8080/JStudy2/completeorder
というURL)で獲得できるはずの物が見つからないという意味です。


何でこんなことになったかというと、これまでは、

Webページ → サーブレット → JSP(またはHTML)[これがWebブラウザーにWebページを返す]

という順番で呼び出しを行っていたのですが、今回は

Webページ → JSP
(具体的にはuserInfo.jspが生成するWebページ → confirmOrder.jsp)

というようにサーブレットをすっ飛ばしてJSPを呼び出したために、
相対的なURLの指定にずれが生じてしまったのです。

サーブレットの場合は、

/JStudy2/servlet/xxxxxx

というようなURLで呼び出すところが、JSPの場合は

/JStudy2/xxxxxx.jsp

というようなURLで呼び出しますので、サーブレットを経由してconfirmOrder.jsp
を呼び出す場合と、サーブレットをすっ飛ばしてconfirmOrder.jspを呼び出す
場合では、servlet/というディレクトリー名が付くか付かないかで相対的な
URLに違いが生じてしまいます。

というわけで、confirmOrder.jspの中で指定されている

<FORM action="completeorder" method="POST">

のcompleteorderという相対的URLが、サーブレット経由のときには

http://localhost:8080/JStudy2/servlet/completeorder

を意味していたのに対して、今回のようにサーブレットをすっ飛ばす


http://localhost:8080/JStudy2/completeorder

を意味することになり、completeorderという相対的URLで指定され
るサーブレットが見つからないというエラーになってしまうのです。

うーん、何だか面倒ですな。サーブレットをすっ飛ばさなければ
よかったですな。では、サーブレットを経由するように修正しま
しょか。

・・・・いやいや、そうではなくて、サーブレットを経由したのか
経由しなかったのかを区別して相対URLを変更するようにすればいいわけ
です。

では、対応策としてconfirmOrder.jspを下記のように修正してください。
--------------------------------------------------------
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="Shift_JIS" %>

<HTML>
<HEAD>
<TITLE>購入(注文)内容の確認</TITLE>
</HEAD>
<BODY bgcolor="#77ffff" text="#fa5a00">
<H1>購入する商品:</H1>

<%@ page import="java.util.*" %>
<% String directory = "";
   Enumeration paramNames = request.getParameterNames();
   while(paramNames.hasMoreElements()) {
      String aParamName = (String)paramNames.nextElement();
      if (aParamName.equals("checkout")) directory = "servlet/";
   }
%>

<FORM action="<%= directory %>completeorder" method="POST">
<TABLE border="1" width="100%">
  <TBODY>
    <TR>
      <TH>商品番号</TH>
      <TH>商品名</TH>
      <TH>価格</TH>
      <TH>カテゴリー</TH>
      <TH>購入個数</TH>
    </TR>

<%@ page import="jp.co.flsi.lecture.webapp.entity.*" %>
<% int totalPrice = 0; %>
<jsp:useBean class="jp.co.flsi.lecture.webapp.entity.Order" id="ORDER" scope="session" />
<%      OrderItem anOrderItem;
      for (int i = ORDER.getOrderItems().size() - 1; i >= 0 ; i--) {
         anOrderItem = ORDER.getOrderItems().elementAt(i); %>
         <TR>
            <TD><%= anOrderItem.getNum() %></TD>
            <TD><%= anOrderItem.getName() %></TD>
            <TD><%= anOrderItem.getPrice() %>円</TD>
            <TD><%= anOrderItem.getCategory() %></TD>
            <TD align="right"><%= anOrderItem.getOrderQuantity() %></TD>
          </TR>
<%         totalPrice += anOrderItem.getPrice() * anOrderItem.getOrderQuantity();
      } %>

  </TBODY>
</TABLE>
<BR>
<TABLE border="1">
    <TR>
      <TH>合計金額:</TH>
      <TH><%= totalPrice %>円</TH>
    </TR>
</TABLE>
<BR>
<BR>
<H1>商品の送付先:</H1>
<jsp:useBean class="jp.co.flsi.lecture.webapp.entity.Customer" id="CUSTOMER" scope="session" />
<TABLE border="1">
    <TR>
      <TH>郵便番号</TH>
      <TH>住所</TH>
      <TH>氏名</TH>
    </TR>
    <TR>
      <TD><%= CUSTOMER.getZipCode() %></TD>
      <TD><%= CUSTOMER.getAddress() %></TD>
      <TD><%= CUSTOMER.getName() %></TD>
    </TR>
</TABLE>
<BR>
<BR>
この内容で購入(注文)を確定するには、下の「購入(注文確定)する」ボタンを押してください。
<BR>
<FONT color="#ff0000"><STRONG>
なお、「購入(注文確定)する」ボタンを押すと注文が確定してしまい、取り消しはできませんのでご注意ください。
</STRONG></FONT>
<BR>
<BR>
<INPUT TYPE="submit" NAME="completeOrder" VALUE="購入(注文確定)する">
</FORM>
</BODY>
</HTML>
--------------------------------------------------------
どこをどう修正したのかはわかりますね。


これでテストしてみると、今度は、さっきの問題は発生しませんね。
めでたし。めでたし。

このように、サーブレットを経由しなくても、JSPも所詮はサーブレットの
一種なので臨機応変にプログラミングできるわけです。
ですが、これはあくまで技術上の話であり、本来はサーブレット経由でJSPを
呼び出すようにすべきであり、上記のようなJSPのプログラミングは好ましく
ありません。



では、今度は、ユーザー登録のページを開くためのボタンを最初の商品検索の
Webページにも付けてみましょう。
これは読者の皆様への課題としておきます。

自力で課題を解けないという人は、下記のWebページにて質問してください。



では、いよいよ、このWebアプリケーションをデプロイしてみましょう。

(次回に続く)

次回は、デプロイを行ったあと、Strutsのお話に入ります。

Strutsは、これまでに作ってきたようなWebアプリケーションの開発を
楽にするためのフレームワーク(Framework、元々はアプリケーション・
フレームワーク(Application Framework)と呼ばれていたものが、最近は
たんにフレームワークと略されるようになってきたもの)で、これを
使うと、Webページ→サーブレット(→Bean)→JSP型のアプリケーション開発
が整理されて、開発が楽になります。(もちろん、Strutsを使っても
これまでに学んできた知識は必要になります。)

我々がこれまで作成してきたWebアプリケーションでは、サーブレット
がやたらにごちゃごちゃたくさん出てきましたが、Strutsでは既成の
サーブレット一つだけになります。
という話をすると、非常に簡単になるような感じがするかも知れません
が、実際はそんなに生易しいわけでもありませんけど。

でも、とにかく現在のWebアプリケーション開発では、Strutsを使う
ことが主流になりつつありますので、これを使いながら、Webアプリ
ケーション開発の話をさらに詳しくしていくことにします。

なお、フレームワークとは何ぞや、というお話も次回いたします。
日本語の翻訳書を見ると、Frameworkを枠組みと訳しているものが
多いようですが、これでは何だかよくわかりませんでしょうな。


では、今日はここまでにします。

何か、わからないところがありましたら、下記のWebページまで質問を
お寄せください。



┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
★ホームページ:
      http://www.flsi.co.jp/Java_text/
★このメールマガジンは
     「まぐまぐ(http://www.mag2.com)」
 を利用して発行しています。
★バックナンバーは
      http://www.flsi.co.jp/Java_text/
 にあります。
★このメールマガジンの登録/解除は下記Webページでできます。
      http://www.mag2.com/m/0000193915.html
★このメールマガジンへの質問は下記Webページにて受け付けて
 います。わからない所がありましたら、どしどしと質問をお寄
 せください。
      http://www.flsi.co.jp/Java_text/
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Copyright (C) 2008 Future Lifestyle Inc. 不許無断複製