IBM®
本文へジャンプ
    Japan [変更]    ご利用条件
 
 
検索範囲検索:    
    ホーム    製品    サービス & ソリューション    サポート & ダウンロード    マイアカウント    
skip to main content

developerWorks Japan  >  WebSphere  >

実践アプリケーション開発講座「Web掲示板の開発」: 第4回 コーディングあれこれ

developerWorks
ページオプション

JavaScript を要するドキュメントオプションは表示されません


レベル: 中級

山元 和斗志, , リコーテクノシステムズ株式会社

2004年 9月 22日

実際に動作する掲示板ソフトの開発をとおして、アプリケーション開発の上流過程からコーディングまでを実践的にご紹介します。

※この講座はリコーテクノシステムズ株式会社 山元和斗志様の投稿講座です。

0. はじめに

皆様いかがお過ごしでしょうか。掲示板システムをWebアプリケーションで構築するまでを、設計段階からご照会するレポートの第4回をお届けします。ここに(冗談ですが)受領印をお願いします。
第2回、第3回のレポートでしつこすぎるぐらいに分析・設計を行ってしまい、飽きてしまった方がいるのではないかと内心不安に思っていますが、顔には出さずに続けます。
今回は実際のコードを多めに紹介できればと思っています。よろしくお付き合いください。




上に戻る


1. エラーと例外の処理を考える(Action編)

これまで皆さんに隠し事をしていたことをお詫びしなければなりません。どんなことかと申しますと・・・。
例えば、ログインの処理を行うLoginActionクラスのexecuteメソッドで、以下のように、エラー処理に手を抜いて開発を行っていました次第です。


図1-1:LoginAction(おざなりエラー処理)
図1-1:LoginAction(おざなりエラー処理)

図1-1を見ればお分かりいただけると思いますが、ログインに失敗した場合(ユーザーIDまたはパスワードが正しくない、あるいはその他の例外発生)は、ログイン画面が再度表示されるのみで、利用者に対して何もアナウンスがない状態でした。これは少々無愛想に過ぎますし、下手をすると「ログインできないぞ!」と管理者に連絡する忍耐の足りない方もいらっしゃるかもしれません。
ここまでのレポートでせっせと利便性について考えながら設計してきたのに、これでは画竜点睛を欠くというものですので、エラー処理について考えていくことにします。

エラー処理(例外処理)の目的

エラー処理を行う目的とは、何でしょうか?そもそもエラー処理とは?
「エラー処理は適切に」とはよく言われる言葉ですが、抽象的ですね。もっとも、具体的な情報は時間と共に劣化するので、具体的情報よりは抽象的情報のほうが、長期的に見て価値は高いわけですが。
閑話休題。エラー(例外)処理とは・・・

  1. ユーザーの操作・運用によるものは、原因となった問題点を指摘する。
  2. 管理者または開発者に発生した問題の詳細を知らせるため、記録を残す。
  3. システム・ダウンに直結しないように後始末を行う。

という処理であると定義して、話を進めることにします。

エラー処理(例外処理)を楽に

エラー処理は重要な課題ですが、それでもなるべく楽にやってしまいたいのというのが人の常です。
StrutsにはActionErrorクラス及びActionErrorsクラスがあり、これをstruts-config.xmlに記載した内容と連動して入力内容のチェックを行うvalidate機能を利用して、ユーザーの操作・運用上の問題点を画面に表示することができるようになっています。
詳しい使用法はJakartaプロジェクトのStruts解説ページを参考にしてください。

The Apache Software Foundation - Struts (英語)
Ja-Jakarta Project - Struts (日本語)

それから、管理者または開発者に発生した問題の詳細を知らせるために記録を残す・・・いわゆる「ログ」というものですが、ログを標準出力に頼ると、ある程度以上の規模のアプリケーション(ログ出力を数千以上含むぐらいの規模でしょうか・・・)において、重大なパフォーマンス上の問題を引き起こす可能性があることが知られています。これを解決するには、毎度おなじみJakartaプロジェクトのLog4Jが人気を博しています。

Log4j Project (英語)
Ja-Jakarta Project - Log4j (日本語)

システム・ダウンを引き起こさないようにするエラー処理は、実はエラー処理というよりはコーディングの作法のようなものなので、この点についてはいまさら、筆者が特に書くことはありません。
例外が発生した場合でも、きちんとDBへの接続が切断されるか、とか。おそらくWebアプリケーション開発についての基本が抑えられていれば問題はないと思います。


図1-2:エラー処理関連クラス
図1-2:エラー処理関連クラス

早速クラスを設計してみました。エラーはユーザーに通知する役割を考えると、同時に複数利用したい場合があるかもしれませんので、格納用にJadeErrorsクラスも作りました。
ActionクラスではJadeErrorsクラスの中身の有無によって、画面遷移先を制御します。JSPではこのクラスに格納された内容を表示する(toStringメソッドを利用する)ことで、ユーザーにエラーの内容を通知します。
具体的な例は、次の図でご覧に入れることにしましょう。


図1-3:LoginAction(改訂版)
図1-3:LoginAction(改訂版)

図1-1と図1-3を比べていただけると、画面遷移を制御する部分がすっきりしたことがお分かりいただけるかと思います。しかも、数箇所にあった標準出力も全て削除しています。
これまで、筆者はログインできない場合、開発ツール(WebSphere Application Developer V5)に表示される標準出力を見て「あ、失敗」などとつぶやいていたわけですが、これからは違います。


図1-4:エラーの画面表示
図1-4:エラーの画面表示

try-catch文のオプションである finally の記述により、ユーザーの望むとおりにならなかった原因=エラーの内容が、画面に表示されるようになったからです。

ここまでで、Actionクラスにおけるエラー処理の定型ができましたので、今度はLogicクラスのエラー処理について考えてみることにしましょう。




上に戻る


2. エラーと例外の処理を考える(Logic編)

とりあえず、図2-1をご覧いただくと、ここでも標準出力を見る気マンマンであることがわかります。
これはいけませんのでJadeErrorクラスを駆使して、どうにかきれいにしたいところです。


図2-1:LoginLogicクラスの抜粋(おざなりエラー処理)
図2-1:LoginLogicクラスの抜粋(おざなりエラー処理)

しかしながら、Logicクラスは呼出元(主にActionクラス)に様々な値を返す役割がありますので、Action編で考えたような方法では、Actionクラスにエラーの通知ができません。Actionクラスにエラーを通知しさえすれば、あとは何もしなくて良いのですが・・・。
ひとつの解決策として、参照渡しを利用して、Actionクラスで生成したJadeErrorsクラスにエラーを追加させる方法もあるにはあるのですが、同じ業界に勤める(敬意を込めて畏友の称号を勝手に贈っている)友人と技術論じみた話をしたときに「メソッド呼んだら、値が返ってきてナンボの世界。参照渡しはアテにならん」という見解で一致したため、参照渡しは却下です。
そこで、JadeErrorクラスに変更を加え、より役に立つクラスへと進化させることにしましょう。


図2-2:エラー関連クラス(ちょっと拡張)
図2-2:エラー関連クラス(ちょっと拡張)

とはいえ、変更はたったの2点です。

  1. Java.lang.Exceptionクラスの拡張クラスにする。
  2. 内部に任意の例外クラスを格納できる。

この変更で何ができるようになるか、皆さんにはすぐお分かりのことと思います。
最初のエラー処理とは何か、という定義に従い、図2-1のような標準出力を無くし、同時にユーザー(テスト中の開発者もこれに含みます)にエラーの内容を通知するために、例外が発生した時点でActionクラスに通知してしまうことにしたのです。 もっとも、こうすることでLogicクラスのexecuteメソッドはthrows節を持つことになり、その呼出元では例外処理の記述が義務化されるわけですが、図1-1から図1-3にかけての過程で、既にtry-catch文が追加されていますので、Actionクラスの変更はなくて済みます。順調ですね。
早速LoginLogicクラスに変更を加えました。


図2-3:LoginLogicクラス(部分)
図2-3:LoginLogicクラス(部分)

なお、図1-4のように表示するためには、JSPで以下のような記述をする必要があります。


図2-4:画面に直接記述する場合
図2-4:画面に直接記述する場合

しかし、この記述を全ての画面で追加するというのは当然NGですし、第一、この記述自体があまりにも具体的過ぎますので、HTMLの記述を補足するクラスHtmlUtilsにカプセル化してしまいましょう。


図2-5:画面表示のカプセル化
図2-5:画面表示のカプセル化

図2-5のようにすることで、HtmlUtilsクラスのimportと、一行の記述で、簡単にエラーを表示させる準備ができました。もちろん、記述する場所を別のJSPに切り出して共通化するという方法も良いかもしれませんが、ひとまずこのようにしておきましょう。




上に戻る


3. DBにバイナリデータを挿入する

データベースに(主に画像などの)バイナリデータを挿入する処理は、大変手間がかかるように思っていましたし、データベース・ソフトによってバイナリデータの扱いが違ったりするので、筆者はこれまで表題のような機能の実装が決定すると、頭を低くして逃げていました。今回は、そうはいきませんね。
何はともあれ、早速コードをご覧いただきます。このコードはバイナリデータの挿入のテストクラスです。このままのコードは掲示板システムにありませんが、ほとんど同じ形で利用されています。


図3-1:バイナリデータ挿入のテスト用Actionクラス
図3-1:バイナリデータ挿入のテスト用Actionクラス


このクラスはstrutsのV1.1リリース版以降で利用できます。このFormFileクラスはファイルサイズやContent-Typeなどを返す豊富なAPIを持っていますので、ぜひ一度ご覧になることをオススメします。
Jakarta-Commonsのfile-uploadではなく、strutsの内部ですので、間違えないようにしてください。
筆者は一生懸命file-uploadを1日調べ上げ、時間を浪費してしまいましたので・・・。
ちなみに、パッケージはorg.apache.struts.upload.FormFileとなっています。

ところで、このFormFileクラスをFormクラスから取り出してごちゃごちゃ処理をするのはなんだかコードが無意味に増えそうですし、おそらく添付ファイルが使える機能のあらゆる局面で同じ記述をするでしょう。
そこで、あらかじめFormクラスにFormFileのラッパーメソッドを記述しておきましょう。同様に、ラッパーメソッドではないけれども、将来何度も使いそうなメソッドは記述しておきます。詳細は図3-3に示します。
こうすることで、ActionクラスにもLogicクラスにもFormFileをimportする必要がなくなるので、クラスの依存度を下げることができるので一石二鳥といったところでしょうか。


図3-3:FormクラスのFormFileのラッパーメソッド+α
図3-3:FormクラスのFormFileのラッパーメソッド+α

特にこのメソッドをラップしたのは、何かと使い道がありそうだ、という予想に基づいています。

ファイルサイズを取得するメソッドは、データベースの容量との兼ね合いで、1MBとか4MBとかの制限が設けられるはずなので、そのチェックに使用できそうです。OracleのBLOBは2GBまで入りますが、さすがにそんなファイルを取り込んだら、一ヶ月経たないうちにハードディスクが悲鳴を上げるでしょう。

ファイル名及びContent-Typeについては、データベースにバイナリデータとして挿入されたファイルをダウンロードする時に必要となります。もちろん、データベースのバイナリデータ本体と一緒に保存しておかなくてはなりませんので、挿入する時点でも使用します。

画像データと一口に言っても、JPEGやGIFはたまたPNG形式など、Content-Typeは非常に重要です。重要なのですが、データベースに挿入した時点で、のっぺりとしたバイナリデータになります。バイナリデータからファイル名やContent-Typeを取得することはできないのです。

ファイルをbyte[]で取得するメソッドは、ラッパーメソッドではありません。このメソッドは、ファイルの内容をBLOBに流し込む際に、出力ストリームの引数となるべきbyte[]を取り出します。この操作は、あらゆる添付ファイルを作成する操作で再利用できそうです。 次はLogicクラスの抜粋をご覧ください。


図3-4:Logicクラスの記述(抜粋)
図3-4:Logicクラスの記述(抜粋)

掲示板システムでは添付ファイルは「メッセージに添付する」というものであるという前提があることに加えて、メッセージの編集は、そのメッセージの投稿者にも、管理者にもできない(管理者は、削除のみ可能)という設計ですので、必ずBLOBを初期化する(空のBLOBをINSERTする)処理が入っています。
初期化したら、今度はそのレコードをSELECT FOR UPDATEで取得し、ロックします。その状態で、取得した空のBLOBに対して、出力ストリームを通じてbyte[]を流し込むことで更新しています。 実際の画面も見て頂きましょう。


図3-5:実際の画面


図3-6:ダウンロード可能

図3-5の右にある「添付:Blue hills.jpg」のリンクをクリックすると、このようにダウンロードできます。
このダウンロードはブラウザの機能です。

使用するConnectionについて

基本的に、ひとつのLogicクラスに記述された処理を行うにはひとつのConnectionを使用します。
サンプル掲示板ではConnectionを取得する専門のクラスを定義しており、Connectionを管理するクラスのメソッドの引数になる値は、すべて定数を管理するConstantsクラスに格納されています。
別の環境でサンプル掲示板を動かすときには、Constantsの値を変更すればOKなはずです。
Connectionを取得する専門のクラスConnectionManagerを以下に紹介します。


図3-7:ConnectionManagerクラス

バイナリデータを取得する際に、データソースでの接続を使用すると、どうしてもClassCastException等が発生してしまいましたので、ためしにドライバからConnectionを取得したところ、うまくいきました。
もっとも、これは使用しているドライバがタイプ4だったからだ、という説もあります。 ドライバはタイプ1からタイプ4までが存在し、通常はタイプ2またはタイプ4が使用されます。タイプ2のドライバを使用するとどうなるかは、今後の課題にしておきましょう。




上に戻る


4. 縁の下の力持ち=ユーティリティー・クラス

最後に、掲示板システムを作っていく上で、一番苦労した(と思われる)ものをご紹介しましょう。
これは投稿内容のHTMLタグを、使ってよいタグはタグのまま、使わせたくないタグは>や<に変換して掲示板の表示形式などを崩壊させるタグ(またはクロスサイト・スクリプティング)を排除する目的のユーティリティー・クラスの一部です。
全てのタグを無効にするメソッドと、有効なタグを復元するメソッドに分かれており、それを組み合わせて使うわけです。紙面の問題もありますので、解説はあえてしませんし、このメソッドではカバーしきれない状況もあると思います。そういった点は、いずれ私自身改善すると思いますし、あなたが改善して、自分の開発に利用するかもしれません。継承はクラスが行うのではなく、人間が行うことですから。
このようなユーティリティー・クラスの充実は、あなたの開発に大きく貢献することでしょう。


図4-1:ConvertUtilsクラス(抜粋)




上に戻る


5. おわりに

以上で4回に渡ってお付き合いいただいた、サンプル掲示板の開発レポートは終了です。

最後に、このレポートを書くチャンスを与えてくださった上司をはじめ関係者の方々、アドバイスを下さった先輩の方々、いつも溜まりがちな疲れを癒してくれる相方、レポートの掲載を喜んでくれた盟友、そして頼りになる仲間であり良きライバルの同期の皆、そして飽きずにここまで読んでくださったあなたに感謝します。 ありがとうございました。ご縁があれば、また。



参考文献



著者について

山元 和斗志,リコーテクノシステムズ株式会社




記事の評価


サイト改善のため、ご意見をお寄せください。こちらのフォームからお願いいたします。



 


 


不充分・不完全である大変素晴らしい
 


この記事を共有する

del.icio.us del.icio.us newsing newsing FC2ブックマーク FC2ブックマーク
Choix! Choix! ニフティクリップ ニフティクリップ Yahoo!ブックマーク Yahoo!ブックマーク
MM/memo MM/memo CZブックマーク CZブックマーク livedoorクリップ livedoorクリップ
はてなブックマーク はてなブックマーク Buzzurl(バザール) Buzzurl(バザール)




上に戻る


    日本IBMについて プライバシー お問い合わせ