2016年の夏休み、Fedora 23 で、Tomcat を初めてさわったてんまつです。
Q. インストールしたけど、http://localhost:8080/ に何も見えません。
A. tomcat-webapps も入れてください。どうしても変だったら、アンインストールして入れ直しましょう。
$ rpm -qa | grep tomcat
tomcat-servlet-3.1-api-8.0.32-5.fc23.noarch
tomcat-admin-webapps-8.0.32-5.fc23.noarch
tomcat-javadoc-8.0.32-5.fc23.noarch
tomcat-lib-8.0.32-5.fc23.noarch
tomcat-8.0.32-5.fc23.noarch
tomcat-jsp-2.3-api-8.0.32-5.fc23.noarch
tomcat-docs-webapp-8.0.32-5.fc23.noarch
tomcat-taglibs-standard-1.2.5-1.fc23.noarch
tomcat-webapps-8.0.32-5.fc23.noarch
tomcat-el-3.0-api-8.0.32-5.fc23.noarch
それぞれが何のために必要なのかは知らない。
Q. ドキュメントルートはどこ?http://localhost:8080/ で見えるページを出しているのは誰?
A.
server.xml のこれによって、
<Host name="localhost" appBase="webapps"
これがドキュメントルート相当になるのだろうが、
$ ls /usr/share/tomcat/webapps/
ROOT docs examples host-manager manager sample
Context の docBase を ROOT にしているわけでもない。
見えているのはこれで、
$ rpm -qf /var/lib/tomcat/webapps/ROOT/index.jsp
tomcat-webapps-8.0.32-5.fc23.noarch
これが効いているのだろうが、
web.xml
<!-- The mapping for the default servlet -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
ROOT をなぜ見に行って、DefaultServlet が何をするのか、わかりません。
Q. なんで、ポート 8080?
A.
server.xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
Q. Server Status のページが、403で見えない。
A. /etc/tomcat/tomcat-users.xml の、<role rolename="admin"/> 以降のコメントを外す。パスワードは変えてね。
Q. ログは?
A. /var/log/tomcat/
Q. Hello World を動かすには
A. トップページの Examples をたどって、
http://localhost:8080/examples/servlets/servlet/HelloWorldExample
する。
/var/lib/tomcat/webapps/examples/WEB-INF/web.xml
に、
<servlet>
<servlet-name>HelloWorldExample</servlet-name>
<servlet-class>HelloWorldExample</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorldExample</servlet-name>
<url-pattern>/servlets/servlet/HelloWorldExample</url-pattern>
</servlet-mapping>
url とクラスのマッピングがあって、
/var/lib/tomcat/webapps/examples/WEB-INF/classes
$ ls HelloWorldExample.*
HelloWorldExample.class HelloWorldExample.java
このディレクトリに、クラスファイルを置けばよいらしい。war ファイルとは?
Q. エラー: パッケージjavax.servletは存在しません
A. クラスパスを指定しないと、 servlet がないと言われる。ヘッダファイルとバイナリの実体と同じなのね。
$ javac -classpath /usr/share/java/servlet.jar HelloWorldExample.java
Q. $ ps -e | grep tomcat
してもいないけど、実体は何?
A.
/usr/sbin/tomcat
if [ "$1" = "start" ]; then
systemctl start ${SRV}.service
/lib/systemd/system/tomcat.service
ExecStart=/usr/libexec/tomcat/server start
めんどうだ。動かしてみる。
# bash -x /usr/libexec/tomcat/server start
..
java コマンドを使う時と、 jsvc を使う時とあるんだ。
..
+ echo 'Java virtual machine used: /usr/share/java-utils/java-wrapper'
+ echo 'classpath used: /usr/share/tomcat/bin/bootstrap.jar:/usr/share/tomcat/bin/tomcat-juli.jar:/usr/lib/java/commons-daemon.jar'
..
オプションをどこから拾ってくるか、全部わかるし、役に立つ表示をしているけど、普通に実行するときには見えないんだ。
..
++ exec /usr/share/java-utils/java-wrapper
-Djavax.sql.DataSource.Factory=org.apache.commons.dbcp.BasicDataSourceFactory
-classpath /usr/share/tomcat/bin/bootstrap.jar:/usr/share/tomcat/bin/tomcat-juli.jar:/usr/lib/java/commons-daemon.jar
-Dcatalina.base=/usr/share/tomcat -Dcatalina.home=/usr/share/tomcat -Djava.endorsed.dirs=
-Djava.io.tmpdir=/var/cache/tomcat/temp -Djava.util.logging.config.file=/usr/share/tomcat/conf/logging.properties
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
org.apache.catalina.startup.Bootstrap start
動いた。
12-Aug-2016 20:17:59.273 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version: Apache Tomcat/8.0.32
Q. ソースは?
A.
$ dnf download --source tomcat
$ rpm -ivh tomcat-8.0.32-5.fc23.src.rpm
$ rpmbuild -bp rpmbuild/SPECS/tomcat.spec
$ cd rpmbuild/BUILD/apache-tomcat-8.0.32-src/java/org/apache/catalina/startup/
Server version: というログを出しているのはここ。
/**
* Logs version information on startup.
*/
public class VersionLoggerListener implements LifecycleListener {
private static final Log log = LogFactory.getLog(VersionLoggerListener.class);
private void log() {
log.info(sm.getString("versionLoggerListener.serverInfo.server.version",
ServerInfo.getServerInfo()));
log.info の実体は何?logging.properties ファイルで出力を増やそうとしたがうまくいかない。
Q. ビルドは、 ant を使う
$ rpmbuild -bi rpmbuild/SPECS/tomcat.spec
。。
+ ant -Dbase.path=. -Dbuild.compiler=modern -Dcommons-collections.jar=/usr/share/java/apache-commons-collections.jar -Dcommons-daemon.jar=/usr/lib/java/apache-commons-daemon.jar -Dcommons-daemon.native.src.tgz=HACK -Djasper-jdt.jar=/usr/share/java/ecj.jar -Djdt.jar=/usr/share/java/ecj.jar -Dtomcat-native.tar.gz=HACK -Dtomcat-native.home=. -Dtomcat-native.win.path=HACKDIR -Dcommons-daemon.native.win.mgr.exe=HACK -Dnsis.exe=HACK -Djaxrpc-lib.jar=/usr/share/java/jaxrpc.jar -Dwsdl4j-lib.jar=/usr/share/java/wsdl4j.jar -Dcommons-pool.home=HACKDIR -Dcommons-dbcp.home=HACKDIR -Dno.build.dbcp=true -Dversion=8.0.32 -Dversion.build=32 -Djava.7.home=/usr/lib/jvm/java deploy dist-prepare dist-source javadoc
Buildfile: /home/kanda/rpmbuild/BUILD/apache-tomcat-8.0.32-src/build.xml
Q. RUNNING.txt に従って実行する。
A.
$ pwd
/home/kanda/rpmbuild/BUILD/apache-tomcat-8.0.32-src/output/build
$ ls
bin conf lib logs temp webapps
$ CATALINA_HOME=`pwd`
$ JAVA_HOME="/usr/lib/jvm/jre"
$ ./bin/startup.sh
Using CATALINA_BASE: /home/kanda/rpmbuild/BUILD/apache-tomcat-8.0.32-src/output/build
Using CATALINA_HOME: /home/kanda/rpmbuild/BUILD/apache-tomcat-8.0.32-src/output/build
Using CATALINA_TMPDIR: /home/kanda/rpmbuild/BUILD/apache-tomcat-8.0.32-src/output/build/temp
Using JRE_HOME: /usr
Using CLASSPATH: /home/kanda/rpmbuild/BUILD/apache-tomcat-8.0.32-src/output/build/bin/bootstrap.jar:/home/kanda/rpmbuild/BUILD/apache-tomcat-8.0.32-src/output/build/bin/tomcat-juli.jar
Tomcat started.
Q. Tomcat 自身を、 eclipse debugger の元で開始する。
A. 少し先が長そうなので、メモ。
http://tomcat.apache.org/tomcat-8.0-doc/building.html#Building_with_Eclipse
https://dzone.com/articles/launching-and-debugging-tomcat
https://wiki.apache.org/tomcat/FAQ/Developing
SSL
apr を使う。
# dnf install apr tomcat-native
docs/apr.html に従って、 server.xml に以下を書く。私は openssl は詳しくないので、キーをこう作って、ここに置くのが正しいかは知らない。
<Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
SSLEnabled="true" scheme="https" secure="true"
SSLCertificateFile="/etc/ssl/server.crt"
SSLCertificateKeyFile="/etc/ssl/server.pem" />
$ openssl genrsa -out server.pem 1024
$ openssl req -new -x509 -nodes -sha1 -days 365 -key server.pem -out server.crt
。。
Common Name (eg, your name or your server's hostname) []:localhost
ログにこう出たから動いているんじゃない。
12-Aug-2016 18:41:34.644 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent Loaded APR based Apache Tomcat Native library 1.1.34 using APR version 1.5.2.
eclipse で、 servlet を作る
Q. IDE から上げるので、止めておかないといけない。
A. systemctl stop tomcat; systemctl disable tomcat
Q. Lomboz が無い。
A. wtp になりました。
Q. eclipse の、ヘルプメニュー、ソフトウェア更新、新規インストール、で、リポジトリを見に行くが、 wtp らしいものが無い。
A. 次項の fedora パッケージを入れるのが無難です。
Q. 新しいプロジェクトで、Web の下に、 static web project しかない。 dynamic がない。
A. eclipse-webtools なんとか、という fedora パッケージが多数あるので、それを入れてください。
インターネットの記事だと、何をどこから落として、というのが多いけど、 fedora で作業するなら、まず、dnf info で探すのがよい。jdk, eclipse, tomcat, plugins のバージョン依存がある。混ぜるな危険。
Q. could not load the tomcat server configuration
A. http://matome.naver.jp/odai/2137338804067721801
/usr/share/tomcat/conf/ の下をワークスペースにコピーしたらできた。
Q. 呼び出しスタックがとれた。
コンストラクタ
Tomcat v8.0 Server at localhost [Apache Tomcat]
org.apache.catalina.startup.Bootstrap at localhost:43705
Thread [main] (Running)
Daemon Thread [NioBlockingSelector.BlockPoller-1] (Running)
Daemon Thread [NioBlockingSelector.BlockPoller-2] (Running)
Daemon Thread [ContainerBackgroundProcessor[StandardEngine[Catalina]]] (Running)
Daemon Thread [http-nio-8080-ClientPoller-0] (Running)
Daemon Thread [http-nio-8080-Acceptor-0] (Running)
Daemon Thread [ajp-nio-8009-ClientPoller-0] (Running)
Daemon Thread [ajp-nio-8009-Acceptor-0] (Running)
Daemon Thread [http-nio-8080-exec-1] (Running)
Daemon Thread [http-nio-8080-exec-2] (Suspended (breakpoint at line 21 in Hello))
owns: StandardWrapper (id=65)
owns: NioChannel (id=66)
Hello.<init>() line: 21
NativeConstructorAccessorImpl.newInstance0(Constructor<?>, Object[]) line: not available [native method]
NativeConstructorAccessorImpl.newInstance(Object[]) line: 62
DelegatingConstructorAccessorImpl.newInstance(Object[]) line: 45
Constructor<T>.newInstance(Object...) line: 423
Class<T>.newInstance() line: 442
DefaultInstanceManager.newInstance(String) line: 119
StandardWrapper.loadServlet() line: 1102
StandardWrapper.allocate() line: 828
StandardWrapperValve.invoke(Request, Response) line: 135
StandardContextValve.invoke(Request, Response) line: 106
NonLoginAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 502
StandardHostValve.invoke(Request, Response) line: 141
ErrorReportValve.invoke(Request, Response) line: 79
AccessLogValve(AbstractAccessLogValve).invoke(Request, Response) line: 616
StandardEngineValve.invoke(Request, Response) line: 88
CoyoteAdapter.service(Request, Response) line: 522
Http11NioProcessor(AbstractHttp11Processor<S>).process(SocketWrapper<S>) line: 1095
Http11NioProtocol$Http11ConnectionHandler(AbstractProtocol$AbstractConnectionHandler<S,P>).process(SocketWrapper<S>, SocketStatus) line: 672
NioEndpoint$SocketProcessor.doRun() line: 1500
NioEndpoint$SocketProcessor.run() line: 1456
ThreadPoolExecutor(ThreadPoolExecutor).runWorker(ThreadPoolExecutor$Worker) line: 1142
ThreadPoolExecutor$Worker.run() line: 617
TaskThread$WrappingRunnable.run() line: 61
TaskThread(Thread).run() line: 745
Daemon Thread [http-nio-8080-exec-3] (Running)
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.101-1.b14.fc23.i386/bin/java (2016/08/12 8:18:25)
これは、 get メソッド
Hello.doGet(HttpServletRequest, HttpServletResponse) line: 30
Hello(HttpServlet).service(HttpServletRequest, HttpServletResponse) line: 622
Hello(HttpServlet).service(ServletRequest, ServletResponse) line: 729
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 292
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 207
WsFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 52
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 240
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 207
StandardWrapperValve.invoke(Request, Response) line: 213
StandardContextValve.invoke(Request, Response) line: 106
NonLoginAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 502
StandardHostValve.invoke(Request, Response) line: 141
ErrorReportValve.invoke(Request, Response) line: 79
AccessLogValve(AbstractAccessLogValve).invoke(Request, Response) line: 616
StandardEngineValve.invoke(Request, Response) line: 88
CoyoteAdapter.service(Request, Response) line: 522
Http11NioProcessor(AbstractHttp11Processor<S>).process(SocketWrapper<S>) line: 1095
Http11NioProtocol$Http11ConnectionHandler(AbstractProtocol$AbstractConnectionHandler<S,P>).process(SocketWrapper<S>, SocketStatus) line: 672
NioEndpoint$SocketProcessor.doRun() line: 1500
NioEndpoint$SocketProcessor.run() line: 1456
ThreadPoolExecutor(ThreadPoolExecutor).runWorker(ThreadPoolExecutor$Worker) line: 1142
ThreadPoolExecutor$Worker.run() line: 617
TaskThread$WrappingRunnable.run() line: 61
TaskThread(Thread).run() line: 745
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.101-1.b14.fc23.i386/bin/java (2016/08/11 19:51:12)
Q. http://localhost:8080/Hello/Hello
すると、確かに、作成したクラスが呼ばれるのだが、定義ファイルでこの url - class マッピングがどう決まったのか不明。
A.
$ cat Servers/Tomcat\ v8.0\ Server\ at\ localhost-config/server.xml
..
<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t "%r" %s %b" prefix="localhost_access_log" suffix=".txt"/>
<Context docBase="Hello" path="/Hello" reloadable="true" source="org.eclipse.jst.jee.server:Hello"/></Host>
$ cat Hello/WebContent/WEB-INF/web.xml
..
<display-name>Hello</display-name>
$ cat Hello/src/Hello.java
..
@WebServlet("/Hello") // 何このアノテーションで、 url マッピングができるの?
public class Hello extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("Hello, World\n");
}
eclipse プロジェクト名とクラス名と display-name と url とどういう関係?
localhost:8080/クラス名/@WebServlet の名前 になるらしい。
Q. 人が書いたサーブレットをデバッグするには。
A. プロジェクトを作る。
プロジェクトのランタイムに、 tomcat を指定する。
Servers タブから、tomcat にそのプロジェクトを Add する。このへんのくっつけ方が、中で何をしているか、不審。
サーブレットのソースコード上で、ブレークポイントをトグル。
サーバをデバッグ実行。
デバッグパースペクティブにすると、コールスタックや変数が見える。
eclipse wtp TODO
コマンドを打って、結果をテキストで得て、コピペするには?
tomcat のログはどこに出る?実行は、IDE を上げているユーザアカウントでされるのだよね。
既存ソースを使ったプロジェクトを作って、デバッグ実行すると、 404 と言われる。url マッピングのしかけがよくわからない。
参考
http://tomcat.apache.org/ 本家はバージョン8
http://www.jajakarta.org/tomcat/ 文書の日本語訳 バージョン 5.0 あたりまで。
http://homepage1.nifty.com/y-osumi/works/code/eclipse/ この記事は、私がやりたいこととほぼ同じでありがたかった。 Servers, Add and remove メニューが無いと思ったら、左のプロジェクトビューでなくて、下の、コンソールのとなりのタブの Servers だったのね。
ひとこと
本も、インターネットの記事も、情報が古い。レッドハットの文書に期待したけど、日本語が無い。
やあ、でも、 It just works. の状態にしてくれた方々に感謝。
以上