Results tagged “Tomcat”

特に何に使ってる訳でもないが、このブログの過去の記事で何か書いたのが残ってるし、Tomcatは筆者のお気に入りなので、Raspbianで作り直した自宅サーバーにもインストールしておくことにした。

▪️やったこと
  • Tomcatのインストール
    • aptitudeでtomcat7とtomcat7-examplesをインストール
    • http://localhost:8080/にアクセスしてServlets examplesが何らか動くことを確認
    • 昔作ったRandomTable Servletを/var/lib/tomcat7/webapps/servlet/に配置し、
      Tomcatを再起動し、
      http://localhost:8080/servlet/RandomTableにアクセスして動くことを確認
  • mod_jkのインストール
    • aptitudeでlibapache2-mod-jkをインストール
    • /etc/libapache2-mod-jk/workers.propertiesを書き換える
      workers.tomcat_home=/usr/share/tomcat7
      workers.java_home=/usr/lib/jvm/jdk-8-oracle-arm32-vfp-hflt
    • Tomcatはport 8009を待ち受ける設定に変更
      (上記ファイルにて"ajp13_worker"がport 8009に接続する設定になっている為)
      /etc/tomcat7/server.xml の Connector port="8009" ... の行のコメントアウトを外してTomcatを再起動
    • /servletへのアクセスをTomcatに転送する設定
      /etc/apache2/mods-available/jk.confの<IfModule jk_module>に
          <VirtualHost *:80>
              JkMount /servlet/* ajp13_worker
          </VirtualHost>
      
      または
          JkMount /servlet/* ajp13_worker
          JkMountCopy All
      
      を追加

Tomcat7でWARがwebappsに展開されない

FreeBSD 9.2のpackagesでインストールしたTomcat7を使うと、WARファイルのdeployには成功し、正常に動作するのに、WARが$CATALINA_BASE/webapps/に展開されなかった。
筆者は昔からTomcatを使用しており、conf/server.xmlにunpackWARs="true"と書いてあれば、WARは実行時にwebapps/に展開されるものだと思っていたが、そうならなかったのである。

Webで調べてみると、Tomcat7はappBaseの外にあるWARをappBaseに展開しないと書いてあるページを見つけた。
しかし、FreeBSD 9.3のpkgngでインストールしたTomcat7では、全く同じ設定で、WARがwebapps/に展開されていたので、納得できなかった。

何が違うのだろうと思って、設定ファイルやログをいくら調べても、Tomcatのバージョンは、FreeBSD 9.2のpackagesのは7.0.40、FreeBSD 9.3のpkgngのは7.0.54と僅かに異なる以外には違いは見つけられなかった。
まさかマイナーバージョンが違うくらいで根本的な動作が変わることは無いだろうと思っていたのだが、念の為探してみると、7.0.12〜7.0.47の間のバージョンだけ、Tomcatの仕様がそのように変わっていたことがわかった。

JIRAとOpenLDAPを連携させてみる

JIRAという課題管理システムがシェアを伸ばしてるらしく、興味を持ったので、評価用ライセンスでインストールしてみた。
JIRAのユーザー管理にはLDAPが使えるとのことなので、ついでにOpenLDAPとの接続を試してみたので、その過程を記録する。

使用環境
OS: FreeBSD 9.2
packages: OpenJDK6, Tomcat6, openldap-server
JIRA version: 5.2.11

●slapd.confの設定
・inetOrgPerson, posixAccountクラス用のincludeを追加

include /usr/local/etc/openldap/schema/cosine.schema
include /usr/local/etc/openldap/schema/inetorgperson.schema
include /usr/local/etc/openldap/schema/nis.schema

・ベースDN設定

suffix "dc=raffine,dc=moriguchi,dc=jp"

ldapsearch等のコマンドの利便のため、ldap.confのBASEも同じにしておくと良い。

・ルートDN(特権DN)設定

rootdn "cn=Manager,dc=raffine,dc=moriguchi,dc=jp"
rootpw {SSHA}s/98X4C3ex/vtSrL3wzP0CyRub2ZdYoL

暗号化パスワード文字列は、slappasswdで作成できる。

●基本コンテナの作成
以下の内容を、init.ldifとして保存する。

dn: dc=raffine,dc=moriguchi,dc=jp
objectClass: dcObject
objectClass: organization
o: Raffine Moriguchi
dc: raffine

dn: cn=Manager,dc=raffine,dc=moriguchi,dc=jp
objectClass: organizationalRole
cn: Manager

dn: ou=People,dc=raffine,dc=moriguchi,dc=jp
objectClass: organizationalUnit
ou: People

dn: ou=Group,dc=raffine,dc=moriguchi,dc=jp
objectClass: organizationalUnit
ou: Group

次に、ldapaddコマンドで実行する。

ldapadd -x -D "cn=Manager,dc=raffine,dc=moriguchi,dc=jp" -W -f init.ldif

パスワードが覗かれたりシェルのヒストリーに残っても良ければ、"-W"を"-w (password)"としても良い。

●ユーザー情報の登録
以下をuser01.ldifとして保存し、ldapaddで実行

dn: cn=user01,ou=people,dc=raffine,dc=moriguchi,dc=jp
objectClass: inetOrgPerson
cn: user01
sn: User No.01
mail: user01@localhost
userPassword: user01

dn: cn=user02,ou=people,dc=raffine,dc=moriguchi,dc=jp
objectClass: inetOrgPerson
cn: user02
sn: User No.02
mail: user02@localhost
userPassword: user02

dn: cn=jira-users,ou=group,dc=raffine,dc=moriguchi,dc=jp
objectClass: groupOfUniqueNames
cn: jira-users
uniqueMember: cn=user01,ou=people,dc=raffine,dc=moriguchi,dc=jp
uniqueMember: cn=user02,ou=people,dc=raffine,dc=moriguchi,dc=jp

●JIRAへのディレクトリー追加
1. システム管理メニューの"Users"→"User Directories"の画面で"Add Directory"ボタンを押し、Directory TypeとしてLDAPを選択
2. Configure画面にて以下のように設定し、"Save and Test"ボタン押下
Name: LDAP server 1
Directory Type: OpenLDAPを選択
Hostname: localhost
Username: cn=Manager,dc=raffine,dc=moriguchi,dc=jp
Password: (ルートDNのパスワード)
Base DN: dc=raffine,dc=moriguchi,dc=jp
3. "Test basic connection: Succeeded"と表示されているのを確認し、再バインド用に、"For extended testing ..."の所で 
User name: user01
として"Test Settings"ボタン押下
4. システム管理メニューの"Users"→"User Directories"の画面でuser01とuser02が追加されていること、Groupがjira-usersであることを確認

●ユーザー情報の追加
以下をuser03.ldifとして保存し、ldapaddで実行

dn: cn=user03,ou=people,dc=raffine,dc=moriguchi,dc=jp
objectClass: inetOrgPerson
cn: user03
sn: User No.03
mail: user03@localhost
userPassword: user03

dn: cn=user04,ou=people,dc=raffine,dc=moriguchi,dc=jp
objectClass: inetOrgPerson
cn: user04
sn: User No.04
mail: user04@localhost
userPassword: user04

以下をuser03m.ldifとして保存し、ldapmodifyで実行

dn: cn=jira-users,ou=group,dc=raffine,dc=moriguchi,dc=jp
add: uniqueMember
uniqueMember: cn=user03,ou=people,dc=raffine,dc=moriguchi,dc=jp
uniqueMember: cn=user04,ou=people,dc=raffine,dc=moriguchi,dc=jp

ldapmodify -x -D "cn=Manager,dc=raffine,dc=moriguchi,dc=jp" -W -f user03m.ldif

●JIRAでの確認
"User Directory"画面で"LDAP server 1"の"Synchronize"をクリックし、
"User"画面でuser03やuser04が追加されていることを確認


●ユーザー情報の登録(Posix Schemaのテスト用)
以下をuser11.ldifとして保存し、ldapaddで実行

dn: uid=user11,ou=people,dc=raffine,dc=moriguchi,dc=jp
objectClass: posixAccount
objectClass: inetOrgPerson
cn: user11
uid: user11
uidNumber: 10011
gidNumber: 10918
homeDirectory: /home/user11
userPassword: user11
mail: user11@localhost
sn: User No.11

dn: uid=user12,ou=people,dc=raffine,dc=moriguchi,dc=jp
objectClass: posixAccount
objectClass: inetOrgPerson
cn: user12
uid: user12
uidNumber: 10012
gidNumber: 10918
homeDirectory: /home/user12
userPassword: user12
mail: user12@localhost
sn: User No.12

#dn: cn=jira-users,ou=group,dc=raffine,dc=moriguchi,dc=jp
#is already used by the above groupOfUniqueName object.
dn: gidNumber=10918,ou=group,dc=raffine,dc=moriguchi,dc=jp
objectClass: posixGroup
description: the default group in JIRA
gidNumber: 10918
cn: jira-users
memberUid: user11
memberUid: user12


posixAccountは補助型なので、他に何か構造型が必要であるが、その構造型としてinetOrgPersonを選んだのは、mail属性を使用するため。
posixGroupオブジェクトのRDNは"cn=jira-users"とするのが普通だと思うが、user01.ldifの例で既に使ってしまっており、それと共存させたので、gidNumberをRDNに用いた。
ou=PosixGroupというサブグループを作って、その下に置くのがbetterだと思うが、手を抜いた。
なお、10918としたのは、A=1, B=2とするとJ=10, I=9, R=18だから。

●JIRAへのディレクトリー追加(Posix Schema)
1. システム管理メニューの"Users"→"User Directories"の画面で、
"LDAP server 1"の"Disable"をクリック(※理由は後述)し、
"Add Directory"ボタンを押し、Directory TypeとしてLDAPを選択
2. Configure画面にて以下のように設定し、"Save and Test"ボタン押下
Name: LDAP server 2
Directory Type: OpenLDAP (Read-Only Posix Schema)
Hostname: localhost
Username: cn=Manager,dc=raffine,dc=moriguchi,dc=jp
Password: (ルートDNのパスワード)
Base DN: dc=raffine,dc=moriguchi,dc=jp
3. "Test basic connection: Succeeded"と表示されているのを確認し、再バインド用に、"For extended testing ..."の所で 
User name: user11
として"Test Settings"ボタン押下
4. システム管理メニューの"Users"→"User Directories"の画面でuser11とuser12が追加されていること、Groupがjira-usersであることを確認

※Configure画面のGroup Schema Settingsセクションを開くと
Group Object Class: groupOfUniqueNames
となっているが、これを特に"posixGroup"に修正しなくても、posixGroupクラスのオブジェクトからグループ情報が取得されるようである。

●"LDAP server 1"と"LDAP server 2"の共存のための設定
ここまでの状態で"LDAP server 1"を有効にすると、use11やuser12がinetOrgPersonクラスである為、"LDAP server 1"から取得されてしまい、"LDAP server 2"のuser11やuser12が無効になり、posixGroupオブジェクトによるグループ設定も無効になり、user11やuser12がグループ無しになってしまう。
これをJIRA側で解決する方法は、2つほど考えられる。
(a) "LDAP server 2"を"LDAP server 1"より優先する
 "User Directories"の画面でディレクトリーの参照順序を入れ替えるだけである。
(b) 該当するユーザーが"LDAP server 1"から取得されないようにする
 "LDAP server 1"の"Edit"からConfigure画面に入ると、User Schema SettingsのセクションのUser Object Filterが"(objectClass=inetOrgPerson)"になっているのがユーザーの検索条件で、これを、例えば"(&(objectClass=inetOrgPerson)(!(objectClass=posixAccount)))"とすれば良い。

●ユーザー情報の追加、JIRAでの確認(Posix Schemaのテスト用)
以下をuser13.ldifとして保存し、ldapaddで実行

dn: uid=user13,ou=people,dc=raffine,dc=moriguchi,dc=jp
objectClass: posixAccount
objectClass: inetOrgPerson
cn: user13
uid: user13
uidNumber: 10013
gidNumber: 10918
homeDirectory: /home/user13
userPassword: user13
mail: user13@localhost
sn: User No.13

dn: uid=user14,ou=people,dc=raffine,dc=moriguchi,dc=jp
objectClass: posixAccount
objectClass: inetOrgPerson
cn: user14
uid: user14
uidNumber: 10014
gidNumber: 10918
homeDirectory: /home/user14
userPassword: user14
mail: user14@localhost
sn: User No.14

以下をuser13m.ldifとして保存し、ldapmodifyで実行

dn: gidNumber=10918,ou=group,dc=raffine,dc=moriguchi,dc=jp
add: memberUid
memberUid: user13
memberUid: user14

→"User Directory"画面で"LDAP server 2"の"Synchronize"をクリックし、
 "User"画面でuser13やuser14が追加されていることを確認

玄箱にDebianをクリーン再インストールしたので、Tomcatの動作環境も復旧することにした。結局、このサーバーでもPerlのCGIがメインで、JavaのservletやJSPはあまり作っていないのだが、筆者にはJavaへのこだわりがあり、まだ諦め切れないのである。

サーブレットの動作環境としては、引き続き、Tomcatを選択する。また、MySQLを使うservletがあるので、その為のJDBCが必要である。
以下、MySQLはセットアップ済だとする。

■手順
1. aptitudeでtomcat6とlibmysql-javaをインストール
2. HTTPの待ち受けポートを変えるなら、/etc/tomcat6/server.xmlを適当に編集する。
3. /etc/init.d/tomcat6 restart としてTomcatを再起動する
4. Webブラウザーからアクセスし、"It works !"の画面が出ることと、CATALINA_HOME とCATALINA_BASEを確認する
5. $(CATALINA_HOME)/sharedまたは $(CATALINA_BASE)/sharedディレクトリーに、
 /usr/share/java/mysql-connector-java.jarへのシンボリックリンクを作成する
 例:

ln -s /usr/share/java/mysql-connector-java.jar /usr/share/tomcat6/shared/

6. $(CATALINA_BASE)/webappsにservletを置いて、Tomcatを再起動する

先月この玄箱のwebサーバーが起動しなくなって、復旧に時間がかかると思ったので、PC上の仮想マシンにFreeBSD 7.3をインストールして、代替のwebサーバーを構築し始めた。
幸いにして玄箱がすぐに復旧したので、FreeBSDは中途半端な状態で放ったらかしたが、せっかくなので、予備のサーバーとして最後まで構築することにした。

今回のは、動けばいいだけの環境なので、手間がかからないよう、可能な限りportsを使わず、packagesで統一することに決めた。portsを使い出すと、依存するライブラリのバージョンがpackagesと合わなくなって、以後何もかもportsでインストールしないといけなくなる恐れがあるからである。

このサーバーの代替をするには、HTTPサーバーの機能とJava servletを動かす機能が必要である。そう決めた。TomcatはHTTPサーバーの機能も持つので、全てTomcatに任せるという手もあるのだが、TomcatはJavaで動いており、決して軽くない。当然、通常のHTTPリクエストはApacheで処理し、TomcatはJava servletやJSPを動かすのみとしたい。

ApacheとTomcatがそれぞれ別々のポート(80と8080とか)でリクエストを待つと、外部に2つのポートを公開しないといけないことになるし、アクセスURLにポート番号を含めることが必要になり、美しくないことになる。当然、Apacheが一旦全てのHTTPリクエストを受けて、必要なもののみTomcatに転送するようにしたい。

それは、Apacheのmod_proxyを使えば実現できる。このwebサーバーでもそうやっている。
FreeBSD 7.3のインストーラー(sysinstall)のパッケージ一覧でmod_proxyを検索すると、"mod_proxy_html"と"mod_proxy_xml"が見つかるが、どちらをインストールしてもmod_proxyはインストールされない。mod_proxyはApacheの本体と共に配布されているので、mod_proxyという名前のpackageが無ければ、どこかに含まれているとすればApache本体であるが、packagesのどのApacheをインストールしてもmod_proxyは入らない。従って、mod_proxyを使うにはそのようにオプションを設定してapacheをコンパイルするしか無いようである。セキュリティホールになり得るmod_proxyが不必要に有効にならないようにという配慮であろうか。

mod_proxyを使う以外に方法はあるのだろうか。Apacheから一部のリクエストをTomcatに転送する方法としては、次の3つの方法が一般的らしい。(参考:http://dev.ariel-networks.com/Members/inoue/tomcat-apache
・mod_proxy_http
・mod_proxy_ajp
・mod_jk
上の2つはFreeBSD7.3のpackagesには無いが、mod_jkはある。従って、mod_jkしか選択肢が無い。mod_jkも無ければApacheをコンパイルするしか無いと諦めがついたのだが、あるから仕方がない。mod_proxyが無いと、mod_perlを使うhttpdを別に動かして連携することができないが、仕方がない。packagesにmod_jkがあるので、この代替サーバーでのmod_perlの使用は諦める。

結局、以下の手順で、FreeBSD 7.3のpackagesだけでApache+Tomcatの連携ができるようになった。
1. packagesの一覧からmod_jk-ap2-1.2.30_1を選択してインストールする。
 apacheが入ってなければ、それも自動的にインストールされる。
2. Tomcatはpackagesに4.xも5.5も6.0もあるが、どれでも良いので、インストール動くようにしておく。
3. /usr/local/etc/apache2/のworkers.properties.sampleをコピーしてworkers.propertiesという名前にし、以下の行を書き換える。

worker.list=localhost
worker.jsp-hostname.port=8180
worker.jsp-hostname.host=localhost

4. 同ディレクトリのmod_jk.conf.sampleをIncludes/mod_jk.confとしてコピーし、JkWorkersFileの%%APACHEETCDIR%%の所とJkMountの行を書き換える。
JkWorkersFile /usr/local/etc/apache2/workers.properties
JkMount /*.jsp localhost
JkMount /servlet/* localhost
(以下略)

このようにしてApacheを再起動すると、httpdが受けたhttp://(hostname)/*.jspや
http://(hostname)/servlet/*にマッチするURLのリクエストがTomcatに転送されるようになった。

EclipseにTomcatプラグイン導入

仕事で科学技術に恩返しできないならインターネットで、と思ってサンプルプログラムをここに書いてきたが、1年前に公開した「3択首都当てクイズ」のソースコードは掲載しなかった。なぜかというと、大体動くようになったが最後、そのコードを触るのが嫌になったからである。なぜ嫌になったかというと、Javaのサーブレットの開発が大変面倒だったからだ。

この24時間運転のwebサーバーは玄箱HGという小型の機械で動いている。いわゆるパソコンではなく、消費電力は低いが計算能力も低い。サーブレットのプログラムを書き換えると、それをこの機械で動かすためにTomcatを再起動するのに2分以上かかる。加えて、思い通りに動かないと、解析するにはTomcatのログを見るしか無い。ちょっといじって動かなくなると、調べるのも嫌になるのである。

そんな苦い思い出を、パソコンにインストールしたEclipseのTomcatブラグインが吹き飛ばした。どうせServlet APIの補完が効くのがいいくらいでしょ、と期待せずに一応インストールしてみたのだが、とんでもなく便利である。何が便利かというと、servletをデバッグモードで動かせることと、servletのソースコードを書き換えると勝手にコンパイルされてTomcatに組み込まれて動作開始することである。

Javaのサーブレットのソースコード上でブレークポイントを設定してTomcatを起動しておくと、クライアントがEclipseのデバッグモードで起動したJavaアプレットであろうが普通のwebブラウザであろうが、クライアントからの要求が来ると、サーブレットの動作がブレークポイントで一時停止する。デバッグモードのTomcatはバックグラウンドで動くので、Tomcatを起動した後も何の支障もなくEclipse上で他の作業ができる。クライアントのJavaアプレットにもブレークポイントを設定しておくと、サーブレットとアプレットとを交互に一時停止させることもできる。両方の動作を時間順に追跡できるので、大変わかりやすい。

さらに、一時停止したサーブレットのコードを、その場で書き換えて、そのメソッドの先頭からやり直させることができる。既に到着したクライアントの要求はそのままに、書き忘れた処理を追加して続行させたり、試しに処理をコメントアウトして続行させたりできるのである。ある1つの要求に対して、処理を書き換えながら色々な処理を試せるのが便利だ。
サーブレットのコードを変えたつもりなのに動作が思い通りにならない時、きちんとコンパイルし直したか、正しくTomcatに組み込んで再起動したかをいちいち気にする必要も無くなった。ブレークしてその場でコードを追加してそこを通る様子を見ればいいのである。


という訳で、EclipseのTomcatプラグインを使って見るとボロボロだった3択首都当てクイズのコードを、デバッグして整形したので、その準備過程の一部を書き残す。

●Tomcatプラグインのインストール
インストール方法は色々な所に書かれているので、省略する。注意点として、一次配布元のSysdeoのサイトは長期間アクセス不能になっているので、Eclipse Wikiのベージからダウンロードするのが良い。
今回はV3.2.1をEclipse V3.4にインストールした。

●プラグインの設定方法
EclipseのPreferencesの画面で、Tomcatの設定を埋める。
といっても、今回明示的に設定したのは、以下の項目だけ。
・Tomcatバージョン
・Tomcatホーム
・JVM設定のJREのバージョン
・Tomcatアプリケーションマネージャーのユーザー名とパスワード

●Tomcatプロジェクトの作成
取っ掛かりの部分はeclipse@Wkiのページに書いてある。
JavaのソースはWEB-INF/srcに、web.xmlはWEB-INFに、JSPのソースはプロジェクトの直下に配置する。

●web.xmlの作成
Tomcatのサンプルをコピーしてテキストエディターで書き換えても良いが、今回は、Eclipseで.xmlを開くと自動的に起動する、Eclipse組み込みのXMLエディターを試してみた。

1. EclipseでXMLファイルを新規作成(wizardはXML/XMLを使用)
2. 最初から入ってる1行("?=? xml")を消して空にする
 (これが残っているとサーブレットが起動できなかった)
3. 右クリックでAdd child→New elementを選択して"web-app"エレメント追加
4. 右クリックでEdit namespacesを選択
 Edit Schema Informationの画面でAdd→Specify New Namesp...を選択
 Prefix: (空)
 Namespace Name: http://java.sun.com/xml/ns/j2ee
 Location Hint: http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd
 を指定して追加
 これで定型部分が埋まってweb.xml用の補完が効くようになる
5. web-appを右クリック→Add attribute→versionで"version"属性追加
6. web-appを右クリック→Add Child→(略)→servletで"servlet"エレメント追加
 "servlet"エレメント以下に追加された項目を埋める
7. web-appを右クリック→Add Child→(略)→servlet-mappingで"servlet-mapping"エレメント追加
 "servlet-mapping"エレメント以下に追加された項目を埋める

●WARファイルのエクスポート
1. 作成したTomcatプロジェクトのフォルダアイコンを右クリック→Preferences選択
 →"Tomcat"の「WARエクスポート設定」タブにて.warのファイル名を設定
2. Tomcatプロジェクトのフォルダアイコンを右クリック→「Tomcatプロジェクト」→
 「プロジェクト設定に従いWARファイルを作成」を選択
3. できた.warをサーバーのTomcatのwebapps/の直下にコピー

●結果
・サーバー(Tomcat)側コード
TrichoiceServlet.java --- サーブレット本体
web.xml
QuizReader.java --- JSP用
listquiz.jsp
※ソースコード中のURL等は当方の開発用PCでのものです。

・クライアント側コード
Start.java --- メイン
MainPanel.java --- 3択画面
ServletAgent.java --- HTTP通信部分

・このサイトの起動ページ
3択アプレット
listquiz.jsp

・過去の関連エントリー
[1] [2] [3] [4]

玄箱HGに移行

10年以上前に購入し、ここ3年連続運転のwebサーバーとして活躍してきたノートパソコン、Mebius MN-340(CPU:Pentium 150MHz)の中から、チュイイイン、ギャアアァァンという、回転刃が金属板を削るような音がしてきたので、先々月から準備し始めていた玄箱に本格的に移行することにした。

初代玄箱の試験用のDebian環境をddでパーティションごとバックアップして、試験用のHDDをまともなHDDに換装して、ddでリストアしようとしたら、Debianが起動しなかった。(ddで取ったHDDのイメージファイルはLinuxでループバックデバイスとして正常にマウントできたのに。なぜだろう?)

仕方なく、Debianの再インストールを始めた。何度かインストールに失敗した後のある夜、寝る前にインストール作業をしていて、操作ミスをして慌てて電源を抜いたら、最悪のタイミングだったようで、ファームウェア異常になって玄箱自体が立ち上がらなくなってしまった。
これを修復するには、JTAGの環境が必要だ。長いこと半田こても握ってないし、そもそも半田こてを用意していない。半田こてを入手しても、その先も険しい道のりだろう。

元々、初代玄箱を試験的に動かしてみて、このMebiusの代わりは荷が重いと感じており、これまでも玄箱HGの購入を少し考えていたので、これまでに調べたことが無駄にならないように、忘れない内に玄箱HGを購入した。


●玄箱HG Debian化メモ
http://www.revulo.com/にあるページの通りの方法で、debian-sarge-2.6.17.3-kuroHG-20060702.tgzを使って、一発で成功した。
具体的な手順は以下の通り。

1. ログイン(ファームウェア1.01の初期状態だと、user=root, pass=kuroadmin)
2. echo -n NGNG > /dev/fl3
3. reboot
 →EMモードで起動する
4. 上記.tgzをtmpimage.tgzという名前にしてzip圧縮、ファームウェア1.01のimage.zipをそれに置き換える
5. ファームウェアダウンロード

●Debianセットアップメモ
- IPアドレス変更
 /etc/network/interfacesを書き換える
- Debian upgrade
 apt-get update
 apt-get upgrade
 を順に実行する
- 時刻設定
 dateコマンドで設定する

- テキストエディタの設定
 デフォルト設定では、何かにつけnanoというエディタが起動するので、
 update-alternatives --all
 を実行して、適切な選択肢でviクローンを選択する。

- IBM JDKのインストール
http://chira-ura.seesaa.net/article/28761629.htmlとそのリンク先に、パッケージの入手方法からalternativesの設定方法まで書かれている。
なお、Tomcatの起動スクリプトに書かれているIBM JDKのインストール先のディレクトリ名は、上記ページの説明とは違って、/usr/lib/j2sdk1.5-ibmとなっている。
上記ページの説明のようにalternativesを適切に設定すれば、複数のJavaをインストールした後のJava VMやJavaコンパイラの切り替えは、それぞれ
update-alternatives --config java
update-alternatives --config javac
というコマンドでできる。

- Tomcat5が起動しない場合の対処
 JavaのセキュリティマネージャをOFFにする。具体的には、/etc/init.d/tomcat5内にて
  TOMCAT5_SECURITY=no
 と設定する。

- Tomcatが使うJava VMについて
javaコマンドに関係なく、/etc/init.d/tomcat5にて優先順が決まっている。
別のものを使う場合は、/etc/default/tomcat5のJAVA_HOMEを設定する。


JDBC+servlet連携appletを作ってみた

せっかくこの長老マシンに苦労してMySQLとTomcatをインストールしてJDBCとservletが動くようになったのに、これまで簡単なテストプログラムしか動かしておらず、有効活用できていなかったし、あまり勉強にもなっていなかったので、Javaの復習とSwingの勉強を兼ねて、JDBC servletと連携するJavaアプレットを作ってみた。
3択首都当てクイズのページへ
アプリケーションのネタが思いつかなかったので、とりあえず3択クイズにした。

Servletとappletの役割分担は、MVCモデルに従って、クイズの作成、正誤判定、終了判断といったモデルは全てservlet側で行い、applet側は表示だけを行うようにした。すなわち、Mをサーバー側、VとCをクライアント側に分けた。そのため、appletは1問ずつ問題をservletから取得し、解答者の選択をservletに送っている。クイズの終了判定もservletが行うため、servletから問題でなく成績表が送られてくると終了という設計にした。

セッション管理にはjavax.servlet.HttpSessionを使っており、複数のクライアントが同時にクイズを始めても、servletで全てのクライアントの途中経過が管理される。Cookieが無効にされているとサーバー側からセッションIDが送られ、クライアント側でURLのquery部にセッションIDを付けるようにしているので、CookieがOFFでも動くはず。

クイズの問題はMySQLのデータベース上にあり、servletは1問ずつJDBC経由で問題を取得する。1つのセッションの中で問題が重複しないように、出題する問題の番号だけはセッションの新規作成時に全て決めておくようにした。

クライアント側の描画にはSwingを使用した。CardLayoutでペインを切り替えるようにしている。クイズ画面の動く背景は、SwingタイマーとAWTを使って描画している。最初はこの背景描画が非常に重く、試行錯誤して改良している内に、事前に画像ファイルを用意してタイル貼りする方が負荷が軽いことが判明したが、画像ファイルを作成するのが面倒なので、そこまではしなかった。

TomcatでJavaのservletを動かす実験をしていて、ログを見ると、servletは正常に動くのにTomcatが起動時に下のようなもので始まる大量のエラーを出してることに気付いた。

[ERROR] Digester - Parse Error at line 37 column 15: The content of element type
"servlet" must match "(icon?,servlet-name,display-name?,description?,(servlet-c
lass|jsp-file),init-param*,(以下略)

"Parse"とあるので私が追加したweb.xml(XML形式の設定ファイル)がおかしいのだろうとは思ったが、そもそも例えば上で書かれてるような37行目に"servlet"という文字列は無いし、"must match ..."とか言われても、"servlet-name"エレメントと"servlet-class"エレメントしか使ってないので、きちんとmatchしてるように思え、何が悪いのかわからなかった。しばらくしてやっと、上記の右側が正規表現ぽいことから、"servlet"エレメントの中身がicon, servlet-name, display-name, ...がこの順でないといけない、"?"がついてるエレメントはあっても無くても良い、servlet-classまたはjsp-fileのどちらか1つが必須である、という意味だと気付いた。

<servlet>~</servlet>の間に<servlet-name>と<servlet-class>を交互に書いていたのが問題だったようだ。同様に、<web-app>~</web-app>の間では、<servlet>と<servlet-mapping>を交互に置いてはいけないらしい。

(FreeBSD)TomcatでServletする(2/2)

インストールし立てのTomcatを起動してHTMLブラウザでアクセスすると、予め用意されているコンテンツ(TomcatではWebアプリと言うらしい)が表示され、ブラウザがJavaに対応していれば、サンプルのServletやJSPがすぐに動く。サンプルのServletのソースを見ることもでき、開発意欲をかき立てられる。

なお、Tomcatのマニュアル(RUNNING.txt)には、デフォルトの設定ではポート8080で待ち受けると書いてあるが、FreeBSDのportsはそれを8180に書き換えるので、注意が必要だ。

そのサンプルソースを改造して、自前のServletを作成して、いざTomcatで動かそうとすると、作成したServletをどこに置いてどういう設定をすれば良いかがわからない。
とりあえず、最初のテストとしては、クラスファイルをサンプルServletのある所(webapp/examples/WEB-INF/classes/)に置いて、サンプルServletの設定があるwebapps/examples/WEB-INF/web.xmlに同じような設定(<servlet>と<servlet-mapping>)を加えればよい。そうして新たなServletが動けば、JDKの動作には問題なしだ。

Servletのきちんとした配置と設定は、大変見つけにかったが、TomcatのマニュアルのDeploymentのページ(TomcatのデフォルトのWebアプリのページから"Tomcat Documentation"→"App Developer Guide"→"Deployment"と順に辿る)に書いてあった。Servletを動かすための最低限の準備は、
(1) Tomcatのwebappsディレクトリに新たなディレクトリ(ここでは"sample"とする)を作成し、
(2) webapps/sample/WEB-INF/classes/ にクラスファイルを置き、
(3) webapps/sample/WEB-INF/ に設定ファイルであるweb.xmlを置く
だ。web.xmlは、conf/web.xml.sampleをコピーして編集するが、筆者が試した所、Tomcat 4.1では<web-app>の中に<servlet>と<servlet-mapping>だけでうまくいったが、Tomcat 5.5では<session-config>も必要だった。


今回、試験的なアプリとして、ランダムな表を出力するServletを作ってみた。
RandomTable Servletへのリンク
 ※アクセス後、ブラウザでページの更新(再読み込み)をすると違う表が出てくると思います。
 ※JRE 1.3.1 + Tomcat 4.1で動かしています。
RandomTable Servletのソースコードへのリンク
web.xmlへのリンク
 ※Tomcat 4.1で不要だった部分はコメントアウトしています。

(FreeBSD)TomcatでServletする(1/2)

HTTPのクライアント側で動作する小さなJavaアプリを指すアプレット(applet)と呼ぶが、それに対してサーバー側で動作する小さなJavaアプリを指すサーブレット(servlet)という言葉がある。昔かすかに聞いた覚えはあるが、その後servletなんて言葉はすっかり忘れていた。
先月から興味があってJDBCを勉強し始めているが、その関連の本を読むと、よくServletの話が出てくる。JavaはWebで使われることが多く、データベースをクライアント側で使うことは稀であるから、JDBCはServletで使われることが多い、ということになるのだろうか。

という訳で、このFreeBSDのWebサーバーにServletを動かす環境を作ってみたので、その過程を報告する。

まず必要なソフトであるが、Java開発環境であるJDK、Java動作環境であるJRE(Java VM)は当然として、クライアントの要求を受けてJavaアプリを起動するHTTPサーバーが必要だ。当初はCGIと似たような動作なのでApacheの設定かプラグインでできると思っていたが、過去にはJServというプログラムを使ってそのようなことをしていたようだが、今はTomcatという異なるHTTPサーバーを使うのがオープンソースでは主流のようだ。他にJRunやResinなどがあるが、これらは基本的には商用である。
蛇足だが、現在TomcatはApacheのプロジェクトで開発されているらしい。Jakarta TomcatとApache Tomcatは同じものである。

次に具体的なインストールの方法を記す。

●FreeBSDへのJDKのインストール
JDKにはJREが含まれているので、Java環境としてはJDKだけをインストールすれば良い。
しかし、最初につまづくのが、Sunが配布するJDKには、Linux用のバイナリはあるが、FreeBSD用のバイナリが無いことだ。従って、インストールするバイナリは次の3通りから選択する必要がある。
 (1) JDKのソースコードをコンパイルしてFreeBSD nativeなバイナリを作る
 (2) FreeBSDのLinux emulationによって、Linux用のバイナリを使う
 (3) Sun以外が配布するFreeBSD nativeなバイナリを使う

勿論(3)が一番楽である。FreeBSDのバージョンが5.5以降でJavaのバージョンが(1.4以前でなく)1.5で良いなら、FreeBSD Foundationが配布するDiablo JDKをインストールすれば良い。これはportsを使用してインストールすることもできる。

しかしながら、このサーバーのように、4.x以前のFreeBSDしか動かず(未だ原因不明)、HDDの空き容量が2G以上必要なJDK1.5のコンパイルができない場合は、別の選択をせざるを得ない。

JDK1.4については、(3)のようなバイナリは存在しない。CPUがPentium 150MHzであるこのサーバーでは、(2)のようにしてLinux emulationでLinuxのJREを動かすのでは、動作速度に難があり使い物にならない。
従ってJDK1.4をソースからコンパイルしたいのだが、少なくとも現時点のportsでは、なんとJDK1.4をコンパイルするのに(2)のLinux emulationによるLinuxのJDK1.4が必要になる。そして、portsのMakefileに書かれていたが、LinuxのJDK1.4を動かすには、FreeBSDが5.4以降である必要がある。
ついでに、JDK1.4のMakefileを動かし始めると、コンパイルには1.7GのHDDの作業領域が必要だという注意書きが表示された。このサーバーには0.4Mも空きが無いので、どの道無理であった。

Tomcat 5.5を動かすにはJDK1.4以上が必要なのだが、Tomcat 4.1なら、JDK1.2以上があれば良いので、このサーバーにはJDK1.3を入れることにした。
JDK1.3についても現在は(3)のようなバイナリが存在しない(過去にはDiablo JDKの1.3があったようだ)が、portsを使えば、JDK1.3のソースコードからのコンパイルは、Linuxのバイナリが必要になることも無く、実に簡単にできた。JDK本体のコンパイルはPentium 150MHzでも2~3時間でできたし、HDDの作業領域も300Mで足りた。

●Tomcatのインストール
packages/portsを使えば、特に難しいことは無かった。このサーバーにTomcat 4.1を、別のPCにTomcat 5.5をインストールしたが、どちらもバイナリのインストール自体は容易だった。
敢えて注意すべきことを挙げると、上に書いたことと重複するが、JDKが1.3以前だとTomcat 5.0以降は動かせないことと、packages/portsのパッケージ名ではtomcat??とapache-tomcat??は同じものだということがある。

Tomcatの起動スクリプトは/usr/local/etc/rc.d/に作られるので、それを参考に、/etc/rc.confにtomcat_enable等の行を追加すれば、Tomcatが起動できるようになる。(OS起動時にも自動的に起動されるようになる)
ちなみに、このサーバーの/etc/rc.confの設定は、

tomcat41_enable="YES"
tomcat41_java_home="/usr/local/jdk1.3.1"
tomcat41_java_vendor="sun"
tomcat41_java_version="1.3"
tomcat41_java_os="native"
tomcat41_java_opts="-Xverify:none"

である。最後の行は、JDBCのI/Fの都合で追加したものであり、Tomcat単体としては必要ない。

ただ1点問題なのは、Tomcat 4.1は、起動スクリプトを使ってTomcatの起動はできるが、状態表示や終了ができないことだ。通常運用だと特に問題無いのだが、Servletのクラスファイルを更新するとTomcatを再起動する必要がある(Tomcat自体がJava VM上で動いておりServletを1回起動したらその名前のクラスはロード済になってしまうため)ので、Servlet開発中は不便である。
原因はロックファイル(/var/run/tomcat41.pid)がうまく作られない(中身が空になる)ことなのだが、/usr/local/etc/rc.subrとにらめっこしているが、今の所修正方法を発見していない。
FreeBSD 5.5のTomcatだとそういう問題は起こらないので、Tomcat 4.1のportsがFreeBSD 4.x以前のことを考えていない可能性がある。
また、samba等他のサーバープログラムの起動スクリプトを一通り見たが、サーバープログラムが自前でdaemon起動しロックファイルを管理しているものばかりで、Tomcatのようにdaemonコマンドを使用してJavaのようなインタプリタをdaemon起動するものは無く、参考にならなかった。起動スクリプトにてJava起動後のpidを取得してロックファイルに格納するというのは、OSの助けが無い限り、並の手段では不可能のように思える。