S-JIS[2007-07-07/2009-12-30] 変更履歴

Tomcat5.5

トムキャット:Javaサーブレット コンテナ

Tomcat4まではSSLには対応していなかったが、Tomcat5.0からはSSLも使えるようになったらしい。


インストール(Windows)

  1. まずはJava(JDK1.5以上)をインストールしておく。 (環境変数JAVA_HOMEを設定しておく)
    (JDK1.4にも対応しているようだが、別途設定が必要なようだ)
  2. アーカイブをダウンロードしてくる。(「Binary Distributions」の「Core」にあるapache-tomcat-5.5.23.zip)
  3. アーカイブを展開する。(例:C:\apache-tomcat-5.5.23)

※インストール先には、「C:\Program Files」等の“スペース入りのディレクトリー名”は使わない方がよい。[2009-12-18]
RMI使用時のバグ

環境変数のCATALINA_HOMEには、インストールしたディレクトリ(例:C:\apache-tomcat-5.5.23)を設定する。
が、設定しなくても動く。


起動(Windows)

インストールしたら、起動してみる。

> 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ファイル  

サンプル実験

最も単純なHTML

webappsの下に適当なディレクトリ(例えばtest)を作り、その中にhtmlファイル(例えばsample.html)を置いてやる。
すると、http://localhost:8080/test/sample.htmlで そのHTMLを表示できる。


最も単純なJSP

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

JSPからの独自クラスの呼び出し

アプリ固有の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.doSampleServlet#doGet()が呼び出され、その出力結果が返る。

なお、SampleServlet.javaをコンパイルする為にはHttpServlet等のライブラリが必要だが、common/lib/servlet-api.jarをコンパイル時のクラスパスに加えておけばよい。

ちなみに、サーブレットの中からSystem.out.println()で出力するとTomcatのコンソールに出力される(catalina.logには出力されない)。


WARファイルの展開

webappsの下にwarファイルを置くと、同じくwebappsの下にそのwarファイルが(自動的に)展開される。
展開された後はHTMLJSPサーブレットの例と同じ。

なお、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 Reaper

ウェブアプリ内で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オブジェクトが残っているのかどうか分からないが、このスレッドがある以上、何かしら残っていると考えて差し支えないと思う。


Tomcatへ戻る / 技術メモへ戻る / Javaサーブレットへ行く / Strutsへ行く
メールの送信先:ひしだま