--サーブレットな話題----------
◆サーブレットって?
- サーブレットって何?
- 1インスタンス・マルチスレッドとは
◆日本語処理
- サーブレットの日本語出力
- クライアントからの日本語入力
- サーバー上での日本語ファイル書き込み・読み出し
- JSPの日本語出力
- SSIの日本語出力
- eLite Servlet Engineの日本語出力
◆環境設定
- JRun
- JServ
- GNUJSP
◆HTTP
- GETか、POSTか
◆こんな時どうする
- アプレット通信(ObjectStream)
- ファイル・アップロード(サイズ制限)
- サブミット・ボタンが連打されたら
- JDBC Driverが日本語をサポートしていない場合
- サーバー上で読み書きするファイルのパスを取得する
--その他の話題----------
◆ERR メッセージ
- couldn't connect to...from 'local-classpath-classes'
- Exception..."main" java.lang.NoClassDefFoundError...
◆Javaの言語仕様
- CLASSPATHって何だ
- リスナって何だ
- staticって何だ
- JDBCって何だ
■■ サーブレットな話題 ■■
■サーブレットって?
- サーブレットって何?
そんな貴方のための素敵なページを紹介します(笑)
【ひよこサーブレット】
http://homepage3.nifty.com/uzblend/servlet/
- 1インスタンス・マルチスレッドとは
[情報提供:UZ、FAQ登録:5-27,2000]
サーブレット・コンテナ(エンジン)はパフォーマンスを向上させるためスレッドプールにスレッド
を蓄えており、リクエストを受けると即時に空きスレッドを割り当てて待ち時間なしにサービスを実行します。
なお、スレッドは使い回したり、足りなくなったら新たに生成して補ったりします。よく出来ていますね。
◆MultiThreadModel
で、1インスタンス・マルチスレッドとは上図の様な実行形態を意味します。
そう、1つのサーブレット・インスタンス上を複数のスレッドが平行して走っているんです。
プログラマが気をつけなくてはならないのは、これらのスレッドは他のスレッドが何をしていようがオカマイなし
に動き回る点で、1つのメソッドが複数のスレッドから同時に実行される場合を考慮しなくてはなりません。
メソッド間で共有されるフィールド等も然りで、複数のスレッドから並行アクセスを受けるため、ある瞬間のフィールドの
「値」がどのスレッドの実行結果であるのかの予測は困難です。つまり、「Servletの状態」をフィールドで管理するのは問題あります。一般的には、
フィールドは定数(しばしばstatic宣言)として扱い、またサーブレットの「状態」はHttpSessionオブジェクト等を
利用してサーブレットの「外」に出して管理します。
#では各メソッドのローカル変数も状態が保証されないの?
いやいや、そんな話ではないです。
スレッドA、Bがあって、今スレッドAが実行しているローカル変数の値が、同時にきたスレッドBに書き換えられて
違うものにすりかえられると言う事はありません。
なお、1インスタンス1スレッドを保証する手段としてSingleThreadModelをimplementsする策があります。
下図の様な実行形態です。パフォーマンスを落とさない工夫として複数のインスタンスをプールする策がとられて
います。これで1つのインスタンスの1つのメソッドが複数のスレッドからアクセスされることは無くなりましたが、
困ったことに各インスタンスのスレッドは他のインスタンスのスレッドが何をしていようがオカマイなしなので、
やはり注意が必要です。
◆SingleThreadModel
■日本語処理
- サーブレットの日本語出力
[情報提供:UZ、FAQ登録:9-14,1999]
下記が日本語出力の典型例です。エンコード方法/文字セットを指定して出力します。
なお、日本語のエンコード方法/文字セットには"JIS"、"SJIS"、"EUCJIS"、"iso-2022-jp"、
"shift_jis"、"euc-jp"等がありますが、"JIS"と"iso-2022-jp"で利用されるのが無難と思われます。
■出力例その1
res.setContentType("text/html; charset=iso-2022-jp");
OutputStreamWriter osw =new OutputStreamWriter(res.getOutputStream(),"JIS");
PrintWriter pw = new PrintWriter(osw);
pw.println("こんにちは");
■出力例その2
res.setContentType(text/html; charset=iso-2022-jp);
PrintWriter pw = res.getWriter();
pw.println("こんにちは");
※注意:getWriter()する以前にcharsetを指定しておく必要がある
【Encodingのめも】
http://www.ke.ics.saitama-u.ac.jp/~masa/java/JDK1.1/i18n/Encoding.html
【日本語と文字コード】
http://kanzaki.com/docs/jcode.html
- クライアントからの日本語入力
[情報提供:UZ、FAQ登録:9-14,1999]
Java内部では文字列はUNICODE表現で扱われます。
FORMで入力された文字列はGET或いはPOSTと言ったメソッドのパラメータとして、ブラウザからサーブレットに渡
されるのですが、サーブレットは受け取った文字列を容赦なく"8859_1"表現と勘違いしてしまう様で、誤った形で
UNICODE変換されるため修正が必要となります。一般的には下記の様にバイト配列に戻した後、ブラウザのエンコ
ード方式を自動認識し正しいUNICODE表現を得ます。
String value = req.getParameter(key);
String value_enc = new String(value.getBytes("8859_1"),"JISAutoDetect");
- サーバー上の日本語ファイル書き込み・読み出し
◎サーバーの環境設定による対処[情報提供:hashi、FAQ登録:9-14,1999]
サーバー上で日本語ファイルをディスクに書き込み或いは読み込み時に文字化けする場合には、サーブレット・エンジンのプロパティ
設定でエンコード方式を指定してやると解決するようです。例えば、
jserv.propertiesに次の設定を追加することで回避できます。
# Arguments passed to java interpreter (optional)
wrapper.bin.parameters=-Dfile.encoding=EUCJIS
◎サーバー環境に依存しない対処[情報提供:UZ、FAQ登録:9-30,1999]
ディスクの読み書きで文字化けを生じるのは上記のとおりサーバーの環境設定に起因すると推察されますが、
一般にサーブレットは「サーバーの環境に頼ることなく動作すべき」と考えられるため、サーバーが
日本語/英語どちらの環境であっても(或いは途中で変更されても)安定して動作させる配慮が望ましい、
と思われます。更にJWS1.1の場合はCGI環境に問題を抱えており、Lang=jaな環境では動作しないと言った
特異な事情があるため、英語環境での利用を強いられる場合もあるでしょう。
http://jeeves.java-conf.gr.jp:8512/Servlet/FAQ/
「CGI のサンプルが・・・」参照
この問題は、読み書きするデータのエンコード方式をプラットフォームのプロパティ設定に頼ることなく、
サーブレット側で明示的に指定することで回避出来ます。以下参照。
String file_encode="EUCJIS";
String path = "hogehoge/hoge.dat";
//読み出し例
BufferedReader in = new BufferedReader(
new InputStreamReader(new FileInputStream(path),file_encode));
//書き込み例
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(path),file_encode));
- JSPの日本語出力
[情報提供:UZ 、FAQ登録:9-17,1999]
JSPでの日本語出力時に生じる文字化けは、下記のとおり、コーディング上で文字セットを指定することで回避します。
なお、JSPファイル自身のエンコード方式は「JIS」が、また、文字セットは"iso-2022-jp"の指定が無難ですネ。
<%@ content_type="text/html; charset=iso-2022-jp" %>
- SSIの日本語出力
[情報提供:UZ 、FAQ登録:9-18,1999]
Windowsプラットフォーム上のJRun並びにJServ環境でSSI(Server Side Include)機能を利用する場合に、
読み込ませるHTML構文(*.jhtml)の文字エンコードがネイティブなシフトJISなのに文字化けを
生じる事があります。これはどうも、サーブレット・エンジンの仕様に起因する様です。利用する文字
エンコードが「JIS」以外の場合は出力が保証されないと考えていた方が良いです。つまり対策は・・・多少
不便ですが「JISでエンコードしたファイル(*.jhtml)を用意する」ですね。
- eLite Servlet Engineの日本語出力
[情報提供:UZ 、FAQ登録:5-20,2000]
WebAppCabaretさんのところのeLite Servlet Engineは英語環境であるものの
国際化対応のJ2SE JVM上で動作している様なので、DISK読み書き等の外部入出力関係の日本語処理は問題ありません。
(i18n.jar)
しかしながら、(困ったことに)WEBサーバーを通過するデータはJISしか通らない設定となっているため、JIS以外の文字列を出力すると
文字バケを生じます。と、言うことで出力するHTMLのエンコードは"JIS"で、また文字セットは"iso-2022-jp"として下さい。
■環境設定
- JRun
- JServ
- Windows Socketとの相性
[情報提供:UZ、FAQ登録:9-14,1999]
「wsock32.dll」を検索してバージョンを確認して下さい。ApacheのFAQによると[4.10.x]のWindows Socketを利用した場合に
不具合が多い様です。UZは4.0.950と4.0.1111の2つの環境で正常な動作を確認しています。最新は[WinSock2]ってやつでDLLの名称も
上記と違っていたと記憶します。なお、[WinSock2]はApacheとの相性に問題ない様です。
- JServとDB2 UDBの連携
[情報提供:UZ、FAQ登録:6-30,2000]
JServからIBMのデータベースDB2 UDBを利用する場合は、jserv.properties内で、JDBCドライバが含まれる「db2java.zip」にクラスパス
を通すのは勿論のこと、下記例のとおり「/SQLLIB/binフォルダ」にパスを通す必要があります。
wrapper.path=c:\SQLLIB\bin
- GNUJSP
- WORKING DIRのパーミッション
[情報提供:hashi、FAQ登録:9-14,1999]
JSPが作業するディレクトリの所有者とパーミションには注意して下さい。
このディレクトリ下は、JSPエンジンがxxx.jspファイルからxxx.javaをソースを生成して、更にxxx.classにコンパイルするための
一時作業領域として使われます。JServはApacheからforkされるので、nobody属性で
動作しているはずです。Apacheのデフォルト属性は、nobodyなため。従って、gnujspをrootで作成していたりすると、
パーミションによっては、このディレクトリ下にコンパイルするための一時ファイル群を書き込めなくなります。
- *.jspの起動
[情報提供:UZ、FAQ登録:9-14,1999]
・・・"*.jsp"の呼び出しは"http:<server_name>/*.jsp"でOKです。JSPは内部SERVLETとして動作しますので、リクエストURLに"/servlets"
(若しくは"/servlet")は不要です。なお、"*.jsp"を/usr/local/apache/htdocs/exampleに置いた場合は、HTML文書を呼び出すのと同様に
http://<server_name>/example/*.jspで呼び出します。
■HTTP
- GETか、POSTか
[情報提供:UZ、FAQ登録:9-14,1999]
クライアントの入力をパラメータとして受け取る場合にPOST、GETの2つの選択肢があります。
これらの違いについては
こちらをお読み下さい。なおブラウザ、サーバーの要求ヘッダー、応答ヘッダー解析ツール
を下記で紹介しています。\(^v^)
http://homepage3.nifty.com/uzblend/tips/index.html#q8
■こんな時どうする
- アプレット通信(ObjectStream)
[情報提供:Tmokazu、FAQ登録:9-14,1999]
ObjectInputStream/ObjectOutputStreamを利用したApplet-Servlet通信の不具合は、
どうやら、connect.setRequestPropertyが悪さをしていたようです。
setRequestProperty("Content-type","application/octet-stream")と明記したら動きました。
尚、サーブレットのエラーログには、Reading POST parameters: java.lang.IllegalArgumentException
とありました。環境は・・・;
| WEBサーバー | Solaris2.5 で apache + JRun 2.3.2 JDK1.2pre3 |
| クライアント | WindowsNT4.0 or Windows98 JDKのバージョン JDK1.2 |
| ブラウザ | Netscape4.6 |
- ファイル・アップロード(サイズ制限)
[情報提供:NYRL、FAQ登録:7-25,2001]
アップロードのファイル・サイズを監視して所定のサイズを超えるファイルを受け取った場合に
エラーページを出力したい場合には、下記の様にコネクションが切れないようにバイト数をSkipしてから
応答ページを出力すると良い。
int length = req.getContentLength();
if (length > サイズ) {
BufferedReader reader = req.getReader();
reader.skip(length);
pw.println(ここにエラーページを記述);
}
- サブミット・ボタンが連打されたら
[情報提供:uz、FAQ登録:9-30,2001]
入力フォームなどでサブミット・ボタンが連打されると、リクエストが重複するので何かと問題と
なりますね。この問題は、JavaScriptを利用してボタンを押す回数を監視することでとりあえずは回避可能です。
<script language="JavaScript">
var submitcount = 0;
function check() {
if (submitcount > 0) {
alert("ただ今処理中です。");
return false;
} else {
submitcount++;
return true;
}
}
</script>
<!-- INPUT FORM -->
<form method="post" action="****" onsubmit="return check()">
- JDBC Driverが日本語をサポートしていない場合
[情報提供:uz、FAQ登録:11-2,2001]
ご利用のデータベースのJDBCドライバが日本語をサポートしていない場合は、プログラマの責でクエリをエンコードしてやる必要が
あります。まずはこちらの記事をお読み鼓ください。
http://www.gimlay.org/~javafaq/S134.html#S134-03
では、ひよこサーブレットで紹介している書籍データベースで試してみます。
一見うまくいっていますが文字コードが完全にマッピングされているわけではなく、保証された方法とは言えない点に注意。
http://www.webappcabaret.com/uzy/servlet/sql.SimpleDBConnWithEncode
SimpleDBConnWithEncode.java
- サーバー上で読み書きするファイルのパスを取得する
[情報提供:uz、FAQ登録:11-3,2001]
WEBアプリケーション上で読み書きするファイルの絶対パスを生成する方法を紹介します。
絶対パスをハードコーディングするとポータビリティが損なわれますので、この様な手法を採るがベターと思われます。
■WEBアプリケーションのルートを取得してパスを生成する
String root_path = this.getServletContext().getRealPath("/");
String file_path = root_path + "hoge.dat"
■■ その他の話題 ■■
■ERR メッセージ
- couldn't connect to...from 'local-classpath-classes'
[情報提供:UZ、FAQ登録:9-14,1999]
Netscapeで上記エラーを生じた場合は、CLASSPATH環境変数の設定が原因の可能性があります。
NetscapeはAppletを実行するとき、クライアントの設定しているCLASSPATH環境変数
(autoexec.bat等)を優先すると言われています。上記のErrorは、Appletタグの"CODE="に
書かれているクラスファイルと同じものが、たまたまクライアントに設定してあるクラスパス上
にある時に、クライアント(ローカル)側のクラスファイルを実行しようとしてセキュリティ
・エラーを生じるのです。
IEはCLASSPATH環境変数を優先しないので、問題とはなりません。
このエラーはJava開発者の環境にありがちであり、しばしば話題になります。対策としては、
問題のクラスファイルをクラスパスに含まれないパス上に移動するか、CLASSPATH環境変数を
変更するかでしょう。 JavaFolderでは"autoexec.bat"等での永続的な環境設定は避け、
バッチ・ファイルないしはスクリプトを実行することを推奨しています。
- Exception..."main" java.lang.NoClassDefFoundError...
[情報提供:UZ、FAQ登録:9-14,1999]
実行時に上記エラーを生じる場合は、カレント・ディレクトリにクラスパスが通っていない可能性があります。
OSのCLASSPATH環境変数にクラスパスを記述しているにも関わらず「カレント・ディレクトリ
を含めるのを忘れる」と言ったことで、上記エラーを生じる様です。対策としては、CLASSPATH環境変数
の先頭にに「.」を追加して下さい。例えばWindowsの場合は・・・
"set CLASSPATH = .;c:\java\hoge\classes.jar;%CLASSPATH%"です。
■Javaの言語仕様
- CLASSPATHって何だ
[情報提供:UZ、FAQ登録:12-1,2001]
環境設定の初期トラブルの殆どはCLASSPATHに起因するものです。CLASSPATHの考え方はJAVAを学習する上でも
避けて通ることは出来ませんのでしっかりと要所を押えましょう。
- シンプルな例題
まずは簡単な例題を用意しました。JAVA開発中に遭遇する「よくある例」です。貴方の理解で
コンパイル、実行出来るか確認して下さい。出来なかった場合は以降の記事を一読の上再挑戦!
- 例題1
以下のアーカイブを展開し、適当に配置します。SimpleTest.jarにCLASSPATHを通した上でSimpleTestMain_1.javaをコンパイル、実行
して下さい。
SimpleTest1.zip
- 例題2
以下のアーカイブを展開し、適当に配置します。SimpleTestMain_2.javaをコンパイル、実行して下さい。
SimpleTest2.zip
- CLASSファイルとパッケージ宣言
CLASSPATHの話に入る前にクリアにしておくべきことがあります。
- CLASSファイルのおさらい
CLASSファイルとはコンパイル後に生成されるバイトコードですね。おさらいすると・・・、"***.java"
をコンパイルして生成される"***.class"などです。また、ライブラリとして配布される
"***.jar"と言ったアーカイブも細かなCLASSファイル群で概ね構成されています。
|
JAR形式の圧縮アルゴリズムはZIP形式をベースとしています。即ちJARファイルはZIP対応のツール
で解凍することが可能です。何が含まれているのか、どんな構造になっているのか、を実際に覗いて
みると理解が早いと思います。
|
- パッケージ宣言の意味
で、CLASSファイルが増えてくると整理が大変になってきます。これは一般のビジネス文書と
同じ状況であり、こういった場合フォルダを利用して文書を整理するのが自然な発想です。そう、パッケージとは
CLASSファイルを名前空間で整理するためのしくみであり、フォルダ階層と構造が一致します。
例えば"package uzblend.test.classpath;"で宣言したソースは下図の様なフォルダ階層で配置
してコンパイルする必要があります。
なお、ライブラリを配布する場合は世の中に2つと無いようなユニークな
パッケージ名を命名(企業の場合はドメイン等)して名前の衝突を回避するのが一般です。
JVMはパッケージ名を含めた名称、即ち「完全なクラス名」でCLASSファイルを識別するのです。
例えば"uzblend.test.classpath"でパッケージ宣言した"SimpleTest_2"クラスの「完全なクラス名」
は"uzblend.test.classpath.SimpleTest_2"となります。

- CLASSPATHをとおす意味
CLASSPATHとは、JVMが広大なHD内をCLASSファイルを検索して回る為の「巡回先」を指します。
JVMはこのCLASSPATHで指定されたフォルダを巡回して(先に紹介した)完全なクラス名にマッチする
目的のCLASSファイルを見つけるんです。
なお、この「巡回先」の設定方法には以下の選択肢があります。以降詳しく見ていきます。
- "-classpathオプション"で一時的に指定する方法
- J2SDK_root/jre/lib/extにJARアーカイブを放り込んでおく方法
- バッチファイルで一時的に指定する方法
- CLASSPATHをとおす手法
では実際にCLASSPATHを設定する例を見ていきます。
- パッケージ宣言したソースのコンパイル
上の記事でCLASSPATHについて概ね掴めたことと思います。
ここではパッケージ宣言したソースの取扱いについて補足します。
パッケージ宣言したソースをコンパイル・実行するには、そのルート・ディレクトリにCLASSPATHを
通してJVMからCLASSファイルが「見える」様にする必要があります。例えば以下のとおりです。
これはJVMが「完全なクラス名」でCLASSファイルを検索しているのだから、ルート・ディレクトリが
判れば相対パスで目的のCLASSファイルにたどり着くといふ事を意味します。
| クラス名 | SimpleTest_2 |
| 完全なクラス名 | uzblend.test.classpath.SimpleTest_2 |
| 配置 | c:\java\uzblend\test\classpath\SimpleTest_2.java |
| CLASSPATH設定 | c:\java |
- リスナって何だ
[情報提供:UZ、FAQ登録:5-11,2000]
リスナとはJDK1.1で採用された概念で、「オブジェクトの状態変化を監視する代理人」です。
状態とはAWTで例えると「ボタン押した」とかです。ボタン部品にリスナを立てると、コード上のロジック部
(押したら何が起きる)とイベント監視部(ボタンの状態管理)に明確な境が出来、以下のメリットを生みだします。
- アプリ部とGUI部の明確な区別が生まれ、イベントフロー変化に柔軟対応可能
- プログラマから見てソースが判り易い。コンパイラのエラーCHKも強力になった。
- 部品として再利用が容易。特にJavaBean化。
この様な概念が生まれたのは「大規模プログラム」や「Java Bean」が背景にあります。
特にJava Beanは究極の再利用方法で、ビーンを部品として取込み、ビジュアルツールビルダでプログラム出来るんですね。
この様なことが出来るのも「状態」をロジックの外に出した事がポイントで、代理人の功績です。パチパチパチ
でも簡単なサンプル・プログラムでは、いちいちオブジェクト毎に代理人をたてたく無い場合があります。
何故なら代理人だらけになるからです。そんな時はメインのクラスでリスナを多重継承(=implements)して
自分自身に代理人を兼任させることも出来ます。UZはしばしばこれを利用します。
- staticって何だ
[情報提供:UZ、FAQ登録:5-09,2000]
STATICなフィールド(変数)の値は唯一の存在であり、メモリ上の領域確保もオブジェクト毎には行われないです。
言い換えれば、オブジェクトを生成することなく利用出来ます。サンプルとしては、しばしばColorクラスが紹介
されます。「Color.red;」はSTATICなフィールドを呼び出した例で、「new Color(255,0,0);」はコンストラクタ
を生成した例ですが、結果は同意です。
STATIC利用の目的は、オーバーヘッド低減して軽く、そしてメモリ節約する工夫と解釈してます。Colorの例の様
に常用で固定のフィールドはしばしばSTATIC宣言します。
また、STATIC宣言したメソッドは(ローカル変数と)STATICなメンバしか扱えない制約を受けるので注意が必要です。
なんでアプリケーションの入り口であるmain()がSTATICなメソッドなのかの議論は・・・こちらに面白い読み物があります。
【mainがstaticだったことの不幸】
http://java-house.etl.go.jp/ml/archive/j-h-b/-/005904.html
- JDBCって何だ
[情報提供:UZ、yuji(参考URL)、FAQ登録:5-27,2000]
簡単にさわりだけ説明します。
JDBCはJavaプログラムからDBに接続するためのAPIで、対象とするDBに依存しないコードを書くために生まれて
きました。で、実際のDB接続にはJDBC APIとDBを仲介するJDBCドライバなるものが必要になります。つまり、この
JDBCドライバがDB毎の固有差を吸収してるんですネ。尚JDBCドライバは、下記のとおりType1,2,3,4の4種類に分類され
ており、Type 1はJDKやJ2SDKに含まれて配布されています。また、Type 2,3,4は各DBベンダーから配布されています。
- Type1:JDBC-ODBC Bridge Driver
ODBCを操るためのドライバ。つまるところマイクロソフト製品に接続するのにType1ドライバを配布するから、ODBC
(Open Database Conectivity)を直接叩くなってこと。戦略的な背景の産物ナノダ。
- Type2:Native API Party-Java Driver
Nativeメソッドをコールするドライバ。分類上Type1も当てはまるが区別してる。
- Type3:Net Protocol All-Java Driver
中継サーバーを置いてこれにDB接続をまかせる
- Type4:Native Protocol All-Java Driver
DBに組み込まれたNet Protocolを利用してDBに直接ソケット接続する。
なお、もっと詳しく知りたい方はこちらの解説を参照下さい。
http://www.inprise.co.jp/interbase/papers/java_ib_connect/connect02.html
|