目次へ

PerlからJavaへWebアプリ移行プロジェクト

Apache-Perl-PostgreSQLによる家計簿、PIMM等ウェブDBアプリ。全く支障なく使ってはいる。でもこのたび思い切ってこれらの機能をJavaに移行することにした!理由はっていうとJavaの学習のためだ!練習問題の丸写しじゃない のにコード をバンバン公開しますので、みなさん御指導よろしくお願いいたします。「これは変だぞ」「このほうがクールだ」とお思いの方、こちらにご連絡いただければ幸いです。新しいトピックほど上に来る積み上げ方式でイカ刺していただきます!

もともとのPerlアプリについては以下(家計簿お買い物支援PIM)をご覧ください。

・予定表アプリもJava化

/最大イベント、ごみ回収/

・なんとごっそりPowerEdgeへ

・家計簿プログラムにマジで取り組む

/今月の引き落とし状況の表示/入力データの削除/tomcatに配備/さて、月の集計だ!/定額の引き落としと、ゼロデータ/やっぱり苦労した「おでかけ」データ処理/すべての支出データをひとつの表に/日付取得もBean化/一度に複数のデータを送信する「コンビニのお買い物」/

・お買い物支援アプリと、それに必要な家計簿アプリの一部

/最終処理プログラムの抜本的改革/HTML書出し一応成功/いよいよお買い物リストの列挙/足りないものリスト/家計簿データから最新購入日を取得する/家計簿入力ページが必要だ!/日本語データをなるべく減らす/おみそしるの具・お総菜は直接選ぶ/JSP-Servlet-JavaBean-JSP/初めての俺JavaBean!/hiddenなパラメータ/選んだ主菜を確認(まだ不完全)/まず主菜を選びます/実際にデータを移行/まずはお買い物支援アプリから/PostgreSQLからMySQLへ/背景/


予定表アプリもJava化


最大イベント、ごみ回収

予定表を管理するテーブルschedulezeroはお買い物アプリですでにできている。今度はこれに他の予定を入れていくのだ。主婦ののに子は出かけたり人に会ったりという予定は年に二、三度程度だが、週や月ごとのごみ・資源回収は大きなイベントだ。これらの予定は4月に一気に入力することになる。
そこで、WebではなくSwingアプリを使うことにした。理由はこの前覚えたJTableだ。これだと今見ている表を直接叩いて連続的にデータ編集するアプリができる。Webフォームだと「送信」ボタンを押さないとデータの処理に入れない。かなり大量のデータなのでいちいち画面を切り替えるのはめんどいし、たくさん入力して一気に送信する方式だと途中でブラウザに落ちられたり送信エラーが出たりするとがっかりである。

「セルにさわれば即insert」
「直前の操作の取り消しボタンつき」

みたいなアプリを作りたかった。たとえば、リストから「不燃ごみ」を選んだ状態で、表の該当期日の行をクリックすると、そこに「不燃ごみ」という値が表示され、同時にその日付と「不燃ごみ」の値がデータベーステーブルにも挿入)されるようなしくみだ。
残念ながらセルにさわっただけでは処理を受け付けてくれなかった。行を選択しかつ適当なボタンをクリックするという、一手だけよけいな操作になった。それにしたって、以前Palm Desktopなどでいちいち予定表のカレンダーの日付をクリックしては「ふねんごみ」と入力して「OK」ボタンを押す操作よりはよほど楽ちんだった。

コードを別ページに示す。こんな感じのアプリケーションになった。この状態で「set」ボタンをクリックすると4月22日に「不燃ごみ」が登録される。
大事なのはこの裏でSQL処理が行われていることで、JTable上への表示は処理したヨということを示すためのものである。

2003年04月15日

ページ先頭に戻る


なんとごっそりPowerEdgeへ

とにかくUltraサーバに載った、などと言っているうちにもっとすごいことが起こった。今開発中のアプリケーションがそっくりPowerEdgeのNT上で動くことが判明したのだ。ということでこれからは、BladeのSun ONE Studioで修正や追加を行ってはPowerEdgeに送ることになった。

2003年04月14日

ページ先頭に戻る


家計簿プログラムにマジで取り組む


今月の引き落とし状況の表示

毎月、電気やガスなどの引き落とし請求書が送られてくると、それを今月の出費として登録するようにしている。月の集計の前には全ての請求書がそろっていなければならない。引き落とし額の登録のとき、請求書の出揃い状況を確認したい。
本日の日付を取得し、その月の、その日までに登録された項目を検索して表示すればいいわけだが、そこにはサラリーマンの妻がのに越えなければならない問題がある。

給料日が25日なので集計は前月25日から当月24日までなのだ

つまり、3月28日に登録ページを開くと、4月分の引き落とし状況が表示されなければならない!
もっともめんどくさいのが、12月25日から31日までの処理だ。翌年翌月の引き落とし状況を表示しなければならない・・・

わたしはDate関数をどーも信用できないため、日付は20030101のようなシリアル番号として処理している。日付の足し算や引き算をするときは、

  1. 数値20030101を文字列にする
  2. substringで2003, 01, 01という各文字列に変換
  3. 計算したい文字列をまた数値変換して計算
  4. 再び文字列変換して文字列を足し合わせる

こっちのほうが百倍めんどくさいとおっしゃる向きもあるかも知れない。そして足し算引き算は単純ではなく、月や年の繰り越しに関してガンガン場合分けしなければならない。
そこで、それまで本日の日付取得メソッドくらいしか記述していなかったtoDayBeanにもっと細かいメソッドを追加して、jspから呼び出すようにした。

新しいtoDayBeanとそれを用いた引き落とし入力フォームを別ページに示す。結果こんなふうに表示されるようになる。

でも必死で作ってちょっと使って見たら、25日から月末までの間に請求書が来るってほとんどない、ということに気がついた・・・・

2003年04月14日

ページ先頭に戻る


入力データの削除

家計簿入力フォームには確認画面がない。一日に10件など入れていくので、いちいちよろしいですか?などときーてらんねーというのがその理由だ。まあ2.3円間違ったところでアレだし。だがたまに送信してからあっ違った、ということはある。
Perl家計簿でも、いったん入力したデータを削除するページを作った。だが、汎用性を考慮したので、結構めんどくさくなった。今回は汎用性はちょっと追いといて早急に作ることを優先した。そこで以下のような前提を作った。

今(本日)入力したデータのみを取り扱う。
どのデータを削除するかは、日付と品目で一意に決める。つまり検索条件を「where dserial=...and good='....'」ですますのだ。

インターフェイスもちょっと変えた。その時点まで入力した本日のデータをリストアップするとき、チェックボックスをつけておく。そこにチェックを入れて送信すると、削除されるということにする。簡単に複数データを指定できる、という利点はまあ、ある。そんなにいくつも一気に間違うことはないと思うんだけど・・・。
データを入力するフォームから、本日の年、月、日をパラメータにリンクする。コードはこちら。

2003年04月14日

ページ先頭に戻る


tomcatに配備

以上相当駆け足で家計簿プログラムの基本的な部分は作った。まだ、データの削除とかいろいろな確認とか細かいページは全然作っていないが、とにかくこいつをUltraサーバに持っていくことにした。
これまでの開発環境はSolari9上のJDK1.4.01上のSun ONE Studio CEで行い、その内蔵tomcatで動作確認をしていた。
そこで、後日詳細を記すがUltraのSolaris8にもっていうかより新しいJDK1.4.1を導入した。
それからtomcatも導入することにしたのだが、最新版の4.1.18では、日本語の文字化けという問題が起こった。
ざっと調べてみたがどうやらSolaris OSのコード処理の問題なのかも知れない。違ったらゴメンナチャイ。これも後日詳細な調査の上改めて報告するつもりである(特令リサーチ200Xみたいだな)。
今のところはtomcat-4.0.6を入れてうまく行っている。非常に暫定的な使用なので、起動も手動という状態だ。アプリケーションの完成度がもっと上がったら自動起動なども考える予定である。

2003年03月18日

ページ先頭に戻る


予期せぬ落とし穴

この集計処理だが、実は予想だにせぬ落とし穴がいくつかあって、かなりげんなりさせられた。
その原因は、前のデータベースでは、分類項目に「食費」「野菜」...などと日本語名をまるっと入れていたのを、より効率的に検索するためと思って全部英文字に変更したことにある。
確かに、おかげでデータの取り扱いは非常にラクになった。telnetでリモートから操作すると日本語入力に対応してくれないのだが、分類項目の取り扱いについてはその問題を気にする必要がないし、プログラムを書くにもいちいち英字と日本語の切換をしなくていいので効率的だ。
だが、データ入力や結果表示については、それらの分類項目の表記が暗号のような英字では非常に間が抜けている。やはり項目は日本語でビシッと表示したい。そのための変換操作でコードがだいぶめんどくさくなった。

もっと致命的な問題は、「調味料」に該当する分類項目に"add"という英文字を当てたことである。
insert 文やselect文において'add'と引用符で囲まれた形で扱うぶんには問題なかったのだが、「食費の詳細一覧」テーブルを作るにあたって、

create table mfood( add text, beva text....

とやったら、この裸の addがSQLコマンドと見事にバッティングしてエラーになったのだ!

どーする!すでに1万件のお買い物データが入っているpurchaseテーブルのカラム名からプログラム中の記載から全部変更か!・・・とかなり青まったが、調べてみるとこれが問題になるのは集計結果をテーブルに挿入するのとそれを表示する2つの場合だけだ。そこでこの場合だけ、Javaのプログラム上で、
文字列が"add"の場合は"adds"に変換
というベリーマッチ付け焼き刃的処理で涙をのむことにした。そしてこの処理をした場合にはまあ当然逆変換を行わないと矛盾するというトラブルにもいくつか遭遇した。これもやはり日本人ゆえの悲しさか?

2003年03月17日

ページ先頭に戻る


集計結果の取り扱い

集計結果はまず確認して、オッケーだったら初めてテーブルに挿入するようにしたい。
それには集計結果をJSPで表示して、送信ボタンでテーブル挿入の処理をスタートさせる感じにしたいのだが、問題はパラメータとして何を送信するかである。

表示ページはshowcalc.jspを作った。そこまではいい。だがJSPとServletの連携をまだよく勉強してないのに子は、今BeanからVectorの形で持ってきたデータの内容がいつまで保持されるかがわからない。scopeをapplicationとかにすれば今のアプリが動作している限り保持されるらしいのだが、そうなると他のデータの扱いはどうなるのだろうか?

いずれ研究すべき課題ではあるが、実は今かなり急いでいる。とにかく早急に集計システムまでは実用化する必要があるのだ。そこで二度手間になるが、一番簡単な方法として、日付データを送信することにした。
今処理をしたgoCalcBeanから改めて、データ抽出に使った集計開始日と終了日を受け取る。
それを、集計結果処理用のサーブレットgoInputに渡す。
サーブレットgoInputでは改めてgoCalcBeanを呼び出し、同じ集計処理をしなおす。結果のテーブル挿入処理もgoCalcBeanに用意しておき、今度はそれを実行する。そうして、集計一覧のテーブルを表示するJSPにディスパッチする。

これもコードを示すが、う〜ん、なんかすげえ二度手間でダサい。でも今のところは勘弁してくださ〜い。

2003年03月17日

ページ先頭に戻る


さて、月の集計だ!

ではいよいよ月の集計。入力フォームはここに記すまでもなく簡単だ。該当月の前月25日から該当月の24までという日付データを送るだけである。
今日の日付から月だけを取得すれば、処理プログラム中でそういうことをしてもいいのだが、年間集計とか上半期集計とかもできるように、月日は規定値で与えておいて変更自由にしておく。

サーブレットgoCalcでこれを受け取り、さらにyyyyMMという形のシリアル番号mserialを作る。Beanプログラム中で作ってもいいんだけどめんどくさい。
サーブレットではgoCalcBeanを呼び出して、日付データを渡す。そして、

prepare()という処理で、下記の定額引き落としやゼロデータを揃える。
calcTocal()という処理で総計を算出。
calcCl1(String cl1name)という処理で、コンピュータ、通信費、光熱費、食費についてさらに詳細な集計。

goCalc, goCalcBeanの生データを別ページに示すが、goCalcBeanにはさらに説明を加えなければならない。
それは、集計結果の処理である。月別集計結果を一覧する別のテーブルに、これらの結果をそれぞれ入れるのだ。

2003年03月17日

ページ先頭に戻る


定額の引き落としと、ゼロデータ

さて入力フォームは全部整えた。PostgreSQLに3つのテーブルに分かれて収まっていたデータも修正をほどこしすべてひとつのテーブルに納めた。いざ集計・・・と行きたいところだが、このまま集計するととんだぬか喜びになってしまう。
毎月定額で引き落とされる、家賃と、インターネット接続料については、なんの処理もまだしていないのだ。

Perl家計簿を作った際に、自分のプログラム設計ミスであとからいろいろトラブり、キモに命じたのは、テーブルからSQLをかます以上、集計に必要な全てのデータはテーブルに納めるべしということだ。
つまり、例外的な出費データを、集計プログラム上で後づけ的に計算に入れたりすると、そのプログラムを使わなかったときにはそのデータは見逃されてしまうからなのだ(年間集計をしたときに、一年分の家賃が見逃されて、本当にぬか喜びしたことがある)。

だからなんらかの機会に、家賃と接続料のデータをpurchaseテーブルに入れる必要がある。いつにするかが問題だ。
cronでも使って、月のついたちの12時にでも自動挿入、なんてカッコいいが、我が家のサーバは常時稼働というわけではない。万が一その時刻にサーバやDBサーバが動いてなかったら、なんかめんどくさいことになりそうだ。
やはり、毎月の集計をしようというときが、一番いいだろう。具体的には入力フォームから、集計するべき年と月のデータを送信したときということになる。

だがこれにも問題がある。結構データ入力をミスって、集計してからヘンだなと気づき、もう一度集計しなおすということが結構あるからだ。
几帳面すぎるのではと思われるかも知れないが、そういうケースはだいたい代引き宅配や、銀行振り込み、ネットショッピングをしたときなどで、そういう出費は大抵パソコン関係の買い物であるから、じゅうなんまんとかクソでかかったりする。いくら監査なしの家計簿でもさすがに無視できない。

集計しなおしを行った場合、今度は定額データが二重に送信されてしまい、今度は逆ぬか喜びになってしまう危険がある。
そこで、定額データの入力には、一度テーブルをクエって、そのデータがまだ入力されていない場合にのみ行うようにしよう。

同じような作業に「ゼロデータ」がある。分類によっては、その月の出費がないというものもあるのだ。一番確かなのは隔月で引き落とされる水道代。あと「その他」がないこともある。ダンナさんが超忙しくて休日も出勤、その月どこへも遊びに出かけないということも、今まで結構あった。
JavaではNull値は大抵うまくゼロと見なしてくれるようだが、やはり数値として0を入力しておいたほうが無難だ。出費項目をdummyにしておけば、insert文も簡単であろう。
これも、定額引き落としと同じタイミングで行えばいい。そしてゼロだから全ての分類について機械的に入力しても、あるいは重複入力しても問題はないだろうが、あまりいくつも同じデータがあるとウザい。
そこで、同様にまずテーブルをクエって、
出費がちゃんとあるものについては入力しない。
すでに入力されているものについては入力しない。
とするのがキレイだろう。

そういう処理を、InsertIfNull というひとつのメソッドにまとめ、集計処理を行う goCalcBeanの中に納めることにした。

2003年03月17日

ページ先頭に戻る


やっぱり苦労した「おでかけ」データ処理

下記の方針をもっとも顕著に表しているのが、Perl家計簿でも相当苦労した、「おでかけデータ」の処理ではないだろうか。これは以下のような特徴を持っている。

  1. おでかけ行動には、たいてい交通費と外食費を含む。
  2. だがたまにうちでお昼を食べてから出かけることもあるし、うちにいるのだが宅配ピザを頼むとこりゃ外食ってことになるよナということがある。
  3. おでかけ場所とお昼をいただく場所は、それぞれが2,3のパターンにほぼ限られている。
  4. だが年に数回里帰りや、たまにもっと高い場所でお昼にすることなど、リスト外から入力したほうが便利な場合もないでもない。
  5. 通常パターンの場合交通費(JR)および外食費(Mac, KFC, ピザ)の価格はほぼ一定だが、JRはいずれ値上げするだろうし新たなメニューに凝り出すこともあるかも知れない。
  6. おでかけから帰宅後にデータ入力をするのでいい加減くたびれているから、フォームからの入力はなるべく簡単にしたい。

このような特徴を鑑み、Perl家計簿の頃から、「おでかけデータ」の入力フォームは

月日、おでかけ場所(リスト選択/リスト外入力)、お昼(同左)

だけから構成され、

別に表を用意し、「おでかけ場所/交通費」「お昼の場所/料金」のデータを入れておく。
JRの運賃値上げ、新たな場所やメニューの追加などあった場合は、ここを修正すればよい。このテーブルを参照して出費を換算し支出データに入力する。

という方法をとった。今回もそれを踏襲する。さらに、

「場所」「お昼」の両方が入力された場合は、二つまとめて入力完了のメッセージを出す。どちらかのデータがNullの場合はあるが、両方Nullの場合はエラーメッセージを返す。

という処理もしなければならない。ということでおでかけデータの処理についてはOutgoBeanというBeansを別に作った。

そんなこんなで作り上げた入力用JSP、処理用Servlet、処理用Beanをセットにして、別ページに示す。
いったん作ってしまえば、アプリからの入力は超ラクである。

2003年03月17日

ページ先頭に戻る


すべての支出データをひとつの表に

「お買い物」だけでなく、「引き落とし」「おでかけ」データも、全部ひとつの表に・・・あったりまえの話だったのかも知れない。もし鉛筆と手帳もしくはエクセルのようなスプレッドシートにつけていたのであればまったくあったりまえにそうしていただろう。だが最初のアプリでなぜかそれらを別々の表に入れてしまっていた。
その理由はおそらく、クエリーをかますときにあまり長い選択条件( where dserial=...and cl1=...and cl2=...みたく)を書きたくなかったということではないだろうか。表を別にしておけばそれを選ぶことですでに大きな抽出がなされていることになる。insert文も同様、あまり細かく分類する必要のない出費項目はより少ないカラムの表にまとめたほうがinsert文が短くてすむ・・・だが、そのおかげで、結局は集計をとるのがすっごいめんどくさくなってしまった。Perlで書いた集計スクリプトは、あまりの煩雑さに怖くて見れないほどである。

今回Javaに移行するにあたって、その問題を解決するために、このような方針を立てた。

  1. JSPで、必要な情報を入れるだけの入力フォームを提供し、Servletへ。
  2. Servletで、JSPから受け取ったデータに、暗黙のデータや他の表との参照で得られるデータなどを加え、「purchase」表のカラムに入れるデータを全部揃えてBeanへ。
  3. Beanではどの件についても基本的に同じメソッドで、同じ表にデータを放り込む。

3.のBeanの方針で「基本的に」というのは、入力完了を告げるメッセージの出し方がいくつかの場合について異なるとか、一部の作業はデータを揃える仕事ではあるがちょいと複雑なのでBeanで請け負ったほうがいいと考えられた、というようなことである。
とにかく、「データ入力システムに工夫をすることによって、集計システムがより容易になるようにした」というのが今回の大幅な改良点かも知れない。

2003年03月17日

ページ先頭に戻る


日付取得もBean化

そのあと本のお買い物、灯油のお買い物など、お買い物の内容によって異なる入力フォームを作った。これは効率悪いなという気がかなりするが、今のところはまあしょうがない労力と考える。
だがどー考えても効率悪いという結論に達したのは、フォームを作るたびに、本日を規定値にした日付の入力フィールドを書くという作業だ。コピペすればいいんだから人的労力というよりは、コードがやたら長くなるという意味でのことだな。せめて本日の取得だけでもBean化することにした。

だが、最初の動作テストで、今まで正常だったはずのアプリが動作しなくなってしまった。それはなぜか。
入力フォームのほとんどは、データ送信後続けて再入力できるようにするため、初めて開いたときと一度データを送信した後で異なるメッセージを出すJSPファイルに、入力フォームを記述する本体JSPをincludeさせている。
一番簡単な構造を持つ灯油のお買い物入力フォームについて例示すれば以下のようになる。
fuelenter.jsp、fuelinput.jspというファイルがそれぞれfuel.jspというファイルをincludeしている。
トップページからのアクセスはfuelenter.jspである。これにincludeされているfuel.jspのフォームに入力して送信すると、ServletはデータをBeanに引き渡した後、fuelinput.jspにディスパッチする。2件目の入力はこのfuelinput.jpsにincludeされた形のfuel.jsp に対して行う。
という形だ。

問題になったのはBean化した日付取得作業のimport宣言だった。

<%@page import="noniworld.toDayBean"%>

という宣言を、受け皿のfuelenter.jsp及びfuelinput.jspにつけておかないと、いくら本体fuel.jspに

<jsp:useBean id="mytoDayBean" class="noniworld.toDayBean" scope="page" />

とフルネームで書いておいても今度は動かないというわけ。わかってみれば大したことない問題なんだけどネ。

では別ページにBean化した日付取得作業toDayBeanと、灯油お買い物入力システムでそれを使用している例を示す。
今考えたんだけど、うちでは灯油は18リッター用のポリタンクを1個、わたしがカートでひっぱって、歩いて15分程度のガソリンスタンドまで直接入れに行く。だから灯油の入力を連続的に行うということはよっぽどサボってレシートを溜めているとき以外にはないのだが。あと別に灯油だけ別の入力フォームを作らなくても他のお買い物入力フォームに組み込んじゃってもよかったんだが。それだけわたしにとって灯油買いは大きなイベントということだな。関係ないけど。健康にはいいゾ。

2003年03月17日

ページ先頭に戻る


 

一度に複数のデータを送信する「コンビニのお買い物」

さて。より壮大な「家計簿アプリ」のうち、「お買い物支援アプリ」作成のために必要な「日用品のお買い物」入力システムだけは作っておいたが、いよいよこちらの壮大なアプリの作成へと移ろう。

「日用品のお買い物」は、家計簿アプリのうち、もっともめんどくさい入力システムではあった。次にめんどくさそうなのは、「コンビニのお買い物」だ。
決定的な違いは、「日用品」が、一度に一件のお買い物データを送信するのに対し、コンビニ・フォームでは、一度に複数のデータを送信する。
理由は、Perlのときもそうだったが、「コンビニのお買い物」とはすなわち休日のお昼ごはんの買い物(作れよ・・・)であり、夫婦ふたりでパン、おにぎり、お弁当を1個か2個ずつ、あとは缶コーヒーとヨーグルト、とだいたい相場が決まっているからである。

詳細はPerl版とほとんど変わらない。特に入力フォーム生成コードは、もっぱらPerlをJavaに翻訳したっていうか、正体はどちらもJavaScriptだもんね。
ただし、データ処理系を新たに作らなければならない。
これまでのデータ処理では、(購入日、品目、分類1、分類2、価格、個数)という属性を持つ購入データ一件を受け取っていたが、ここでは複数だ。
だが、JSPからBeanにデータを渡すServletでは、これらのパラメータを連番つきで一気にBeanに渡してしまった。そして受け取るBeanのほうに、これを複数の購入データとして処理するメソッドを用意した。コードを別ページに示す

2003年01月13日

ページ先頭に戻る


お買い物支援アプリと、それに必要な家計簿アプリの一部


最終処理プログラムの抜本的改革

だが、下の方法を他のアプリケーションにも使ったりなんだりしているうちに、これは危険ではないかと感じられてきた。
「これ」とは、htmlタグを含む文字列をパラメータとしてプログラム間で送信する方法だ。
データを受け取ったサーブレットに、パラメータであるはずのhtml文の一部が表示されてしまったり、サーブレットの他の処理ができなくなったりする。
そう、あの、プロレスの技かなにかのような名前を持つ、「クロスサイトスプリクティングエラー」というヤツではないかと思われる。

実は他にも改善すべき点はあったので、お買い物リスト列挙のためのwhattobuy.jspから抜本的にプログラムを修正してみた。whattobuyex.jsp, summaryBeanである。 summaryBeanでは処理毎にバラバラだったJava Beansファイルをひとつにまとめた上、サーブレットでやっていたファイル生成処理も行うようにした。whattobuy.jspはsummaryBeanを呼び出すだけで、Beanに対して何らかのデータを自分から渡す必要はなくなった。また、結果はわざわざこのjsp上で表示しなくても、書き出したHTMLファイルへのリンクを張っておき、そっちを見ることにした。かなりすっきりしたかも。

こうしてようやく「お買い物支援アプリ」が一応、完成を見た。

2003年01月13日

ページ先頭に戻る


HTML書出し一応成功

whattobuy.jspからhtml文writeStrを文字列として受け取り、ファイルに書き出すサーブレットwriteHTMLである。
サーブレット中で動作するファイル作成プログラムでは、ファイルの書き出し先にちょっと迷った。
というのは、

File f="written.html";

とファイル名を指定してこいつを書き出すと、ファイルはどこに行くか。
Sun ONE StudioCEの内部Tomcatで動かしたときは、/s1studio_jdk/s1studio/binへ。
Tomcatで動かしたときは$TOMCAT_HOME/binへ。

だからコンテキストルート上に書き出すには、相対パスであればこれらのディレクトリからの相対パスをたどっていく必要がある。それはなんかめんどくさいので、絶対パスにした。

writeHTMLサーブレットでは、最終的にこのHTMLファイルへのリンク先が示される。これで一応、完成したかに見えた。

2003年01月13日

ページ先頭に戻る


いよいよお買い物リストの列挙

こうして、いよいよ今日買う物を列挙するwhattobuy.jspに到達だ。これは、こちらから処理すべきデータを送信する必要はもうない。ゆえに、データベースに接続してこれまで登録したものを全部引っ張ってくるselectAllBeanを、scopeをpageにして呼び出してくる(いままでのscopeはrequestだった)。
だが、その前に、足りない物データを購入データと比較して適宜抹消するlistBean.removeList()を呼び出さなければならない。
ていうか、Beanを呼び出すだけだから、すごいラク〜。

whattobuy.jspで特記すべき点は二つだ。
(1)列挙する項目はチェックボックス形式で表示する。ただし、どこに何を送信するわけでもない。お買い物のときに買ったらチェックをつける、それだけの理由だ。
(2)結果は最終的にhtml形式で別保存する。だから発行するhtml文はひとつのでかい文字列にまとめ、あとでファイルにそれを書き出す。
(3)そのために、html文をまとめた文字列をサーブレットwriteHTMLに送信する。

ついでに、少しずつプログラムも洗練されていく。同じデータベースに接続したり接続を閉じたりするんだから、指定のデータベースに接続する過程をメソッドにまとめた。戻り値はConnectionである。

2003年01月13日一部修正

ページ先頭に戻る


足りないものリスト

さて一方で日頃「足りなくなってきたな」と思いついたときに、それを登録しておくフォームが必要だ。このフォームはこれまでずっと使ってきた「よく買う品目」リストからチェックボックスで入力することにする。リスト外で登録する必要がある場合は、これから作るつもりでいる通常の予定表入力フォームから備考を「shopping」にして入れれば同じことだ。
lacks.jspは非常に簡単である。ぜーんぶリストアップすればいいだけだから。複数選択可だがあっチェックすんの忘れたという場合のために一応連続送信できるように受け皿lackenter.jspやinputlack.jspなどを用意する。

送信データをBeanに渡すlackListServletも、これまでと同様のものだ。

さて、足りない物データを扱うlackBeanである。足りない物登録は食事材料の登録とは異なり、「よく買う品目リスト」に用意したlack欄に、日付を登録することによって実現する。
その意味は、これを補充したときの登録抹消を簡単にすることにある。それを補充したとき、ただ家計簿にそれを入力すれば、家計簿に入力した購入日と足りない物登録をした登録日を比べて、前者のほうが新しい(数値として大きい)はずである。そうしたらlack欄にゼロを入力すればよい。
このようにして、足りないものはその登録を抹消されない限り、すなわちlack>0である限り毎日の買い物リストにリストアップされる。「そろそろおトイレペーパー買いたいけど今日雨だし明日にしよ」と言って次の日ピーカンに晴れときながらすっかり忘れてる、などということを防止できる。それはいいとして、ゆえにlackBeanの主なメソッドは登録用のmakeList()と登録抹消用のremoveList()である。

2002年11月25日

ページ先頭に戻る


家計簿データから最新購入日を取得する

ようやく家計簿入力ページを作ったので、「定期的に買う物」についてひとつずつ、家計簿データから最新購入日を取得して表示するページを作ることにした。
あまりたいしたものではない。コードはこちら

2002年11月24日

ページ先頭に戻る


家計簿入力ページが必要だ!

最後は定期的に買うものを入力する。だがこれには家計簿アプリのうち、最低限日用品の入力ページを作ることが必要だ。というのは、定期的に買う「牛乳」「食パン」などは、それぞれの最終購入日を入力フォーム上に表示し、それを見て今日買うかどうか判断するようにしたいからである。
ということは、家計簿の入力ページを作って適当な入力を行えるようにしなければならない。
そこで、いったんお買い物アプリを中断して、家計簿アプリの作成を行うことにした。

入力フォームはPerlで作ったのと同じように、食費の小分類ごとに別のスクローリングリストを作って配置する形式だ。各リストの筆頭には該当する小分類名を冠する。
この小分類名は、具体的な頻出品目名と一緒にテーブルgoodslistに納められているが、id=0を与えられることで具体的な品目名と区別できる。
送信時には、すべてのスクローリングリストから値が送信されるが、デフォルトでは小分類名が送信されるようにする。リストをつついて品目を選び出したものだけid>0として抽出することができるしくみだ。

問題は、送信したあとも同じ入力フォームが表示され(次のデータを入力するのに別のアンサーページから戻ってくる必要がない)ただし「送信しました」系メッセージが同じページに表示される必要があることだ。Perl CGIではひとつのページに一連の処理を全部突っ込んだが、Javaでは・・・
いや、PerlだからどうJavaだからどうというのではなく、プログラムを作り直すという機会にあたってアルゴリズムも洗練したい、ということである。Perlでもおそらくモジュール化とかなんか、のにヘボアプリなんかよりもっと優れた方法がたくさんあるのだと思う。
とにかく、今回Javaを使うにあたって、入力フォームはJSPだがデータ処理はServletそしてBeanに渡すようにした。そこで入力フォームを表示させるコードを含んだJSPを、さらにホントに表示だけのJSPにincludeさせることにした。そして処理が終われば、今度は送信結果を表示するJSPに、入力フォームのJSPをincludeさせたものを呼び出せばよい。

考え方を図示したものとともにコードをこちらに示す。

2002年11月24日

ページ先頭に戻る


日本語データをなるべく減らす

ところで、家計簿やスケジュールなどのデータをPostgreSQLからMySQLに移行するにあたって、データそのものも少し変更した。
それは、「分類」項目に、これまでは「食費」「野菜」など日本語名を与えていたのだが、これらをfood,vegなど英数字データにしたのである。
品目そのものまでは、あまりにもボーダイだし、リスト外入力の場合もあるし、英字化するのはやめた。
しかし、機械が処理しやすくても人間にわかりにくくなっては意味ないので、アプリケーション上では日本語のラベルを表示し、それを選んだときに該当する英字データがやりとりされるようにした。

<select name='cls1' value='veg' >野菜
<option value='cls1' value='food'>食費

のようなHTML形式の書き方で、それは実現できる。

2002年11月16日

ページ先頭に戻る


おみそしるの具・お総菜は直接選ぶ

次はおみそ汁の具を選ぶが、料理下手のに子の場合おみそ汁の具にそれほどバリエーションはない。だからチェックボックス形式で一覧表示して、複数選択可(とーふとねぎとか)で一気に送信する(selectsoup.jsp)。
この場合、以下の点が主菜の場合と違う。

複数のデータを受け取る。
受け取ったデータを直接お買い物リストに登録する。

そこで別のサーブレットsoupListServletでListBeanにデータの受け渡しをする。そのあとsoupConfirmed.jspに操作を渡す。この場合は「実行しました」という表示だけにした。

お総菜も同じようにListBeanを使うためのJSPとServletを作成する。

2002年11月16日

ページ先頭に戻る


JSP-Servlet-Bean-JSP

さて、下記のBeanに異なるJSPからアクセスする。

まず、「主菜を選んで、該当する材料を登録する」。
showmaterials.jspで、主菜名confirmedmenuの送信先を、toMakeListServletに変える。
toMakeListServletは
・ListBeanからMakeList()メソッドを呼び出し、showmateials.jspから送られてきたconfirmedmenuの値を渡して処理を行わせる。
・一応ListBeanからconfirmedmenuの値を返してもらい(もともとshowmaterials.jspから送られてきた値なんだけど)、「実行しました」というメッセージを返すmenuConfirmed.jspに値を渡す。

これで、お買い物リストに、主菜分のお買い物材料が登録された。

2002年11月16日

ページ先頭に戻る


初めての俺JavaBean!

その後、JSPとServletの連携をSun ONE Studio上でバンバン試せることがわかり、開発が非常にしやすくなった。そこで、データベースに接続し材料をお買い物リストのテーブルに挿入する処理はJavaBeanで行うことにした。
JAVA Developer10月号に「JSP-Servlet-JavaBean」連携の作り方が詳しく説明してある。これを頼りに自分で作ってみた。その名もListBean()。

本アプリケーションで「お買い物リスト」に登録する方法は以下の2通りの場合がある。

入力フォーム          処理
----------------------+-------------------------------------------------------------------
「主菜」をひとつ選んで送信->データベースに接続しその主菜名から複数の材料を抽出->お買い物リストに日付とともに登録(1)
「材料」を複数選んで送信 ->お買い物リストに日付とともに登録(2)

そこで、ListBeanには、以下の3つのメソッドを用意した。

本日の日付を取得しそれをシリアル番号に変換するdserial()
処理(1)を行うmakeList()
複数の材料についてひとつひとつ処理(2)を行うmakeList2()

ListBeanのコードを考え方とともにこちらに示す

2002年11月16日

ページ先頭に戻る


hiddenなパラメータ

下の確認画面「showmaterials.jsp」は、前のjspから「選んだ主菜」を受け取って表示し、「これでいいですか?」「はい」と確認するだけである。
それでもパラメータを送信しなきゃならないが、テキストフィールドにするのかチェックボックスにするのかと悩んでいた。けどhiddenというのが見つかった。

<input type=hidden value="<%=menu%>" name="menu">

としておけば、選択の必要がないデータをわざわざもう一度ブラウザ上に表示する必要がなくなった。

2002年11月05日

ページ先頭に戻る


選んだ主菜を確認(まだ不完全)

selectMenu.jspで「まーぼ豆腐」を選んで送信すると、showmaterials.jspでそのデータを受け取る。
ちょーっと二度手間なってるかも知れないが、今度は(主菜、材料)のmaterialsテーブルから主菜=「まーぼ豆腐」に対する「材料」を抽出する。そして、

「まーぼ豆腐」を選びましたね。その材料は
豆腐 まーぼ豆腐のもと です。これでいいですか?

と、確認画面を表示。インスタントかヨ!などという御意見もあるかも知れない。

showmaterials.jsp以下は2002年11月05日現在あいにくまだ不完全である。予定としては確認を送信すると、「豆腐」「まーぼ豆腐のもと」を「今日のお買い物リスト」に記録することにしたい。

で、ここで少し実験をしてみた。jspから、サーブレットへの連絡が(わたしにも)できるかという実験である。で、ShoppingData2というサーブレットに連絡してみた。これは$TOMCAT_HOME/webapps/noniworld/WEB-INF/classes/noniworld/ShoppingData2においてある。今のところ受け取ったものを表示するだけだが、わたしにもできるということが一応わかった。けど無理にServletに処理を渡さずともjspでもいいような気がするしどうするかまだ考慮中である。

2002年11月05日

ページ先頭に戻る


まず主菜を選びます

ではまずブラウザ上に主菜リストをラジオボタン付きで出す画面を作ることにした。
これにはちょっと工夫をつける。「タマネギが余っているのでこれを使用した主菜にしぼって表示する」ことができるようにするのだ。

そのために、まずは「スーパーでよく買う商品」を格納したgoodslistテーブル(品目、分類)から、品目リストを表示し、そのリストから値を選んで送信するフォームが必要である。
「スーパーでよく買う商品」とはいえ「しょうゆ」を材料に献立を組み立てることはあまりないから、分類名が「肉魚卵」及び「野菜」であるものだけを選択して表示すればよいだろう。これがselectbymaterial.jsp

selectMenu.jspでそのデータを受け取る。そして(主菜、材料)の構造を取るテーブルmaterialsから

select distinct menu from materials where menu='(goodslistから選んだ品目)'

で主菜を選択して表示する。goodslistテーブルの「品目」が必ずしもmaterialsテーブルの「材料」になくてもよいだろう。結果がなければ空白表示でもいいや、今は。

だがそのたびにselectMenu.jspとselectbymaterial.jspのページを切り替えるのはうっとーしい。そこでselectMenu.jspがselectbymaterial.jspのページをincludeするようにした。こうすると材料をいろいろ切り替えながら主菜リストを絞れる。

selectMenu.jspでは最終的に、そうやって表示した「主菜」リストの中からひとつ、主菜を選んで送信する。

2002年11月04日

ページ先頭に戻る


実際にデータを移行

下記のデータベースは、実はすでにPostgreSQLにできている。これをMySQLに移行しようというのだ。さてどうするか。
Perl上でMySQLに接続するためには、MySQL用のモジュールが必要だ。一度さらっと見てみたことがあるが、これはこれでまた少し命令体系が違っている。それぞれのモジュールは別の方々がお作りになっているのだから仕方のないことだ。仕方のないことではあるがちとめんどくさい。

そこでこのようにした。

MySQLに、PostgreSQLで作っていたのと同じ構造のデータベースやテーブルを作る。ここではやはりnoniworldというデータベースに、主菜と材料の関係を示すテーブルmaterials(menu text, material text)を作っておく。
一方、PerlでPg.pmを用い、PostgreSQLから必要なデータをひとつひとつ読み込んでは、そのデータをinsertせよというSQL文を作り一行一行ファイルに書き出す。(プログラム:01)
そのファイルをmaterials.txtとしよう。内容は

insert into materials values('まーぼ豆腐','豆腐');
insert into materials values('まーぼ豆腐','まーぼ豆腐のもと');
insert into materials values('とん汁','豆腐');
....

という繰り返しになっている。コンソールから

$mysql noniworld <materials.txt

とやればこれらのコマンドが全て実行される。

まわりくどいと言えばまわりくどいが、確実にデータを移行することができた。
テーブルgoodslistについても同様。ただし、Perl版もお読みくださった方のために御説明すると、Perl版では「主食」「野菜」「生活雑貨」などの「分類1」ごとに別個のテーブルを作っていた。どーしてだったかよく覚えていないがその方がパッと見で家計簿計算での処理が簡単そうだと思ったはずなのだ。たぶん。だが今回MySQLへのデータ移行にともなってやはりひとつのテーブルにまとめることにした。

2002年11月04日

ページ先頭に戻る


まずはお買い物支援アプリから

家計簿データベースはPerlでもかなりボーダイなシステムに仕上がっている。まず、扱うのはまず文字列だけで比較的短い「お買い物支援アプリ」をJavaで作ってみることにした。

Perl版のページにわざわざリンクしていただくのは申し訳ないので、ここでその説明をさせていただく。

(1)最初のページで、メニューリストを表示する。ここから今夜の主菜を選んで送信。
(2) その主菜で使われる材料の一覧が表示され、これでいいですか?と確認画面。
(3)確認して送信すると、その材料が「今日のお買い物リスト」に記録される。
(4)一方、調味料や洗剤など、「足りないな」と思ったときに別途Webアプリからデータを送信し「今日のお買い物リスト」に記録できるシステムを作っておく。
(5)「今日のお買い物リスト」を全表示させる。

という流れである。そのためには、以下のデータベースが必要である。

a.よく買うものテーブルgoodslist。構造は、(トマト、野菜)(洗剤、生活雑貨)(しょうゆ、調味料)のような、品目と分類名からなる。
b.主菜と材料テーブルmaterials。(まーぼ豆腐、豆腐)(まーぼ豆腐、まーぼ豆腐のもと)(とん汁、豆腐)など、主菜と材料の関係は多対多である。

2002年11月04日

ページ先頭に戻る


PostgreSQLからMySQLへ

だがそれには非常に大きな決断をしなければならなかった。PostgreSQLからMySQLへ、データの移行である。
JavaでPostgreSQLを使うと、のに子の腕ではどーも文字化けしてしまうのだ。バージョン7.1以降ではpostgres.jarは日本語に対応しているという話なのだが・・・
一方MySQLではほとんど特別な注意を払わなくてもEUC-JPで日本語を出し入れできるようだ。今のわたしはまずとにかくアプリケーションの作成に邁進しなければならない。というわけでMySQLを使うことを潔く決断した。
そのあとSolaris9を買って入れてみたら、Companion CDにMySQLがついていた。これでMySQLへの移行心がますます強まった。

2002年11月04日

ページ先頭に戻る


背景

毎日の家計簿日記予定献立を選べばお買い物リストが自動で作成されるアプリ・・・これらをApache-Perl-PostgreSQLの三頭政治(ププッ)で開発・運用してきた。今のところ不満は全くない。トラブルもない。
だが、このたび、これらのシステムを少しずつJavaに移行することにした。
なぜか!?・・・理由はひとつ。Javaアプリケーション作成の勉強のためだ!
今までいくつものサンプルコードを真似して打って動作させてホホーとか言ってきた。サンプルコードを少し変える練習問題もやってみた。だがイチから自分の目的のためにJavaでアプリケーションを作ったことは一度もない。
だがこれからイチから全く新しいアプリケーションを作るのはとてもむづかしそーだッ!
というわけで、アプリケーションの骨組みは完成しているヤツを、コードだけJavaで書いてみるという試みから始めることにしたのだ。

2002年11月04日

ページ先頭に戻る