トムキャット:Javaサーブレット コンテナ
Tomcat4まではSSLには対応していなかったが、Tomcat5.0からはSSLも使えるようになったらしい。
※インストール先には、「C:\Program Files」等の“スペース入りのディレクトリー名”は使わない方がよい。[2009-12-18]
→RMI使用時のバグ
環境変数のCATALINA_HOMEには、インストールしたディレクトリ(例:C:\apache-tomcat-5.5.23)を設定する。
が、設定しなくても動く。
インストールしたら、起動してみる。
> cd C:\apache-tomcat-5.5.23\bin > startup.bat
Tomcat用のウィンドウ(コマンドプロンプトで実行されている)が開く。このウィンドウは×ボタンで閉じてはいけないことになっては いるらしい。閉じる時はshutdown.batを実行すること。
→シャットダウン時の障害調査方法
ブラウザから http://127.0.0.1:8080/
を入力して画面が表示されればOK。
ブラウザにプロキシを設定していないか、もしくは(IEの場合は)「ローカルアドレスにはプロキシサーバーを使用しない」にしているなら、http://localhost:8080/
と入力してもよい。
Tomcatのコンソールに以下のようなエラー(例外)が出ているときは、ポート8080が既に使用されている。
2007/07/07 8:48:37 org.apache.coyote.http11.Http11BaseProtocol init 致命的: エンドポイントを初期化中のエラーです java.net.BindException: Address already in use: JVM_Bind:8080 2007/07/07 8:48:37 org.apache.catalina.startup.Catalina load 致命的: Catalina.start LifecycleException: プロトコルハンドラの初期化に失敗しました: java.net.BindException: Address already in use: JVM_Bind:8080 2007/07/07 8:48:39 org.apache.coyote.http11.Http11BaseProtocol start 致命的: エンドポイントを起動中のエラーです java.net.BindException: Address already in use: JVM_Bind:8080 2007/07/07 8:48:39 org.apache.catalina.startup.Catalina start 致命的: Catalina.start: LifecycleException: service.getName(): "Catalina"; プロトコルハンドラの起動に失敗しました: java.net.BindException: Address already in use: JVM_Bind:8080
コマンドプロンプトからnetstatを実行すれば、ポートが何に使われているか分かる。
> netstat -nao | findstr 8080 TCP 0.0.0.0:8080 0.0.0.0:0 LISTENING 1252 ←一番右に出ているのがPID(プロセスID) > tasklist | findstr 1252 TNSLSNR.EXE 1252 Console 0 6,276 K →TNSLISTENERといえば、Oracleだな…
みっちーさんの「Tomcatが起動しない場合の対処方法」によると、Oracle9iをデフォルト状態でインストールするとXDB(XMLデータベース)という機能が入ってしまい、これがポート8080を使っているのだそうだ。
→XDBの削除方法
startup.batと同じ場所(C:\apache-tomcat-5.5.23\bin)に「setenv.bat」という名前のバッチファイルを置いておくと、起動時に実行される。[2009-12-30]
環境変数設定を書いておけば、それが使用される。
以下の様にJAVA_HOMEの設定を書いておけば、稼動するJREを指定することが出来る。
set JAVA_HOME=C:\Program Files\Java\jdk1.6.0_13
ディレクトリ・ファイル | 概要 | 備考 | 更新日 | ||||
---|---|---|---|---|---|---|---|
インストールディレクトリ (C:\apache-tomcat-5.5.23\) |
bin | startup.bat | Tomcatを起動するバッチ | ||||
shutdown.bat | Tomcatを終了するバッチ | ||||||
conf | server.xml | Tomcatの設定 | 使用するポート番号(8080)とか。 →NaokiさんのTomcat5 - server.xml |
2009-10-14 | |||
catalina.policy | 実行時のJavaのセキュリティーポリシー | →Tomcat5.0のセキュリティマネージャ設定方法 →@ITのTomcatを安全にするセキュリティマネージャとは?(Tomcat6) |
2008-05-02 | ||||
web.xml | 全アプリ共通(Tomcatのデフォルト)のweb.xml | →Tomcatのweb.xml | 2009-12-29 | ||||
common | lib | Tomcatのライブラリ | HttpServlet等はservlet-api.jarにある。 taglib関連はjsp-api.jarにある。 |
||||
shared | classes lib |
全アプリ共通のライブラリ | インストール時は空っぽ | ||||
logs | catalina.YYYY-MM-DD.log | Tomcat自体のログ | Tomcatのコンソールに出ている内容 | ||||
localhost.YYYY-MM-DD.log | jspのコンパイルに失敗した場合の例外が出ている。 | ||||||
webapps | xxx | xxxアプリ固有のコンテンツ | htmlファイルやjspファイル・画像ファイル等 | ||||
WEB-INF | classes lib |
xxxアプリ固有のプログラム | |||||
web.xml | xxxアプリ固有のweb.xml | ||||||
work | 作業用ディレクトリ | jspを変換したjavaファイルやclassファイル |
webappsの下に適当なディレクトリ(例えばtest)を作り、その中にhtmlファイル(例えばsample.html)を置いてやる。
すると、http://localhost:8080/test/sample.htmlで そのHTMLを表示できる。
webappsの下に適当なディレクトリ(例えばtest)を作り、その中にjspファイル(例えばsample.jsp)を置いてやる。
(→jspファイルの記述例)
すると、http://localhost:8080/test/sample.jspで そのJSPを表示できる。
なお、変換されたjavaファイルやclassファイルは以下のような場所に置かれる。
>cd C:\apache-tomcat-5.5.23\work\Catalina\localhost\test >tree /f C:. │ tldCache.ser │ └─org └─apache └─jsp sample_jsp.class sample_jsp.java
「sample.jsp」というファイルは、「sample_jsp.java」になっている。当然、クラス名も「sample_jsp
」。
また、このクラスはパッケージが「org.apache.jsp
」になっている。したがって、testというディレクトリの下に「org/apache/jsp」というディレクトリが作られている。
testの下にサブディレクトリを作った場合は、そこからパッケージ名に加わる。
すなわち http://localhost:8080/test/sub/sample2.jspの場合、クラス名は「org.apache.jsp.sub.sample2_jsp
」となり、配置される場所は以下のようになる。
>cd C:\apache-tomcat-5.5.23\work\Catalina\localhost\test >tree /f C:. │ tldCache.ser │ └─org └─apache └─jsp │ sample_jsp.class │ sample_jsp.java │ └─sub sample2_jsp.class sample2_jsp.java
アプリ固有のWEB-INF/classesの下に自分で作ったクラスを置いておけば、JSPからそのクラスを使える。
設定ファイルにパスを書いたりする必要は無いのだが、WEB-INF直下に(内容はほとんど空でもいいが)web.xmlが必要。
webapps/test/WEB-INF/web.xml:
<?xml version="1.0" encoding="Shift_JIS"?> <web-app> </web-app>
webapps/test/WEB-INF/classes/jp/hishidama/sample/Hello.class: (コンパイル前のソースは以下のようなもの)
package jp.hishidama.sample; public class Hello { public String get() { return "hello"; } }
webapps/test/sample.jsp:
<%@ page import="jp.hishidama.sample.*" %> <%@ page contentType="text/html; charset=Shift_JIS" %> <html> <head> <title>呼び出しサンプル</title> </head> <body> 呼び出し:<%= new Hello().get() %> </body> </html>
HTTPサーブレットを呼び出すにはweb.xmlを記述する必要がある。
webapps/test/WEB-INF/web.xml:
<?xml version="1.0" encoding="Shift_JIS"?> <web-app> <servlet> <servlet-name>jikken</servlet-name> <servlet-class>jp.hishidama.sample.SampleServlet</servlet-class> </servlet> <servlet-mapping> <url-pattern>/sample_servlet.do</url-pattern> <servlet-name>jikken</servlet-name> </servlet-mapping> </web-app>
servlet-mappingでは、url-patternよりも先にservlet-nameを書くのが正当?っぽいが、Tomcat5.5は逆でも動く。
webapps/test/WEB-INF/classes/jp/hishidama/sample/SampleServlet.class: (コンパイル前のソースは以下のようなもの)
package jp.hishidama.sample; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.*; public class SampleServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/html; charset=Shift_JIS"); PrintWriter out = res.getWriter(); out.println("<html>"); out.println("<head>"); out.println("<title>servlet-sample</title>"); out.println("</head>"); out.println("<body>"); out.println("サーブレットのサンプル"); out.println("</body>"); out.println("</html>"); } }
http://localhost:8080/test/sample_servlet.doでSampleServlet#doGet()が呼び出され、その出力結果が返る。
なお、SampleServlet.javaをコンパイルする為にはHttpServlet等のライブラリが必要だが、common/lib/servlet-api.jarをコンパイル時のクラスパスに加えておけばよい。
ちなみに、サーブレットの中からSystem.out.println()で出力するとTomcatのコンソールに出力される(catalina.logには出力されない)。
webappsの下にwarファイルを置くと、同じくwebappsの下にそのwarファイルが(自動的に)展開される。
展開された後はHTMLやJSPやサーブレットの例と同じ。
なお、warファイルを作り直してwebappsの下に置き直した(warファイルを上書きした)場合、再度展開される(ちょっと時間はかかる)のだが、以前展開されたものは消されずに残る。
つまり削除するつもりでwarファイル内から消しても、ワークには残っているのでブラウザーからアクセスできてしまう。
Tomcatを終了させるにはshutdown.bat(Windowsの場合)を実行する。[2009-12-13]
しかし、いつまで経ってもTomcatが終了しないことがある。
この場合の調査として、Tomcatのスレッドダンプをとって、どのスレッドで止まっているのか確認する。
> jps -l …動いているJavaプロセス一覧を表示 5416 org.apache.catalina.startup.Bootstrap ←これがTomcat 5844 sun.tools.jps.Jps 2720 > jstack 5416 > thread_dump.txt …スレッドダンプを取得 > type thread_dump.txt …取得したスレッドダンプを表示 2009-12-13 17:14:05 Full thread dump Java HotSpot(TM) Client VM (11.3-b02 mixed mode, sharing): "DestroyJavaVM" prio=6 tid=0x00317800 nid=0xc94 waiting on condition [0x00000000..0x0093fd4c] java.lang.Thread.State: RUNNABLE "GC Daemon" daemon prio=2 tid=0x02ffd000 nid=0x10fc in Object.wait() [0x03fdf000..0x03fdfc94] java.lang.Thread.State: TIMED_WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x23323a80> (a sun.misc.GC$LatencyLock) at sun.misc.GC$Daemon.run(GC.java:100) - locked <0x23323a80> (a sun.misc.GC$LatencyLock) 〜
スレッド名の後ろに「daemon」と書かれているのがデーモンスレッド。書かれていないのが非デーモンスレッド。
非デーモンスレッドが全て終了するとJavaアプリ全体が終了するらしいので、残っている非デーモンスレッドを探す。
ウェブアプリ内でRMIオブジェクトのエクスポートをしている場合、RMI
Reaperというスレッドが作られる。[2009-12-13]
エクスポートしたRMIオブジェクトをアンエクスポートしないでいると、RMI Reaperスレッドが残り続けて、Tomcatがシャットダウンできない。
"RMI Reaper" prio=6 tid=0x031c2800 nid=0x94c in Object.wait() [0x03f8f000..0x03f8fd14] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x23323af0> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116) - locked <0x23323af0> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132) at sun.rmi.transport.ObjectTable$Reaper.run(ObjectTable.java:333) at java.lang.Thread.run(Thread.java:619)
このスレッドダンプ(スタックトレース)からはRMIオブジェクトが残っているのかどうか分からないが、このスレッドがある以上、何かしら残っていると考えて差し支えないと思う。