Tomcat7 und Apache auf Debian 5.0
Veraltet: Dieser Artikel ist veraltet. Das heißt, die in ihm beschrieben Anleitungen werden NICHT mehr funktionieren. Bitte benutzt diesen Artikel nicht mehr.
Bitte hilf mit, diesen Artikel wieder benutzbar zu machen. |
Warnung: Dieses Toturial beschreibt die Installation von Tomcat und im weiteren Verlauf die Integration in die Apache-Webserver Umgebung, Datenverbindungspools und Deploymanagement mit Ant. Dieses Tutorial beschreibt jedoch in keinster Weise die Installation auf einem Produktionssystem ! |
Basisinstallation
Java (JDK) Installation
Zuerst öffnen wir dir Datei /etc/apt/sources.list und ergänzen, wenn noch nicht geschehen, folgende Zeile...
deb http://ftp.de.debian.org/debian/ lenny main
... um den Eintrag non-free.
deb http://ftp.de.debian.org/debian/ lenny main non-free
Anschliessend werden die Java-pakete installiert:
root@debian:~# apt-get install sun-java6-jdk
Hinweis:
|
Über "Debian-alternatives" wird nun Sun-JAVA ausgewählt. Das ist besonders dann erforderlich, wenn zuvor andere JVMs installiert wurden. Im zweifel sollte man die folgenden zwei Befehle abarbeiten:
root@debian:~# update-alternatives --config javac
root@debian:~# update-alternatives --config java
Wenn alles geklappt hat sollte der Befehl java -version folgendes ausgeben:
root@debian:~# java -version
java version "1.6.x_xx"
Java(TM) SE Runtime Environment (build 1.6.x_xx-xxx)
Java HotSpot(TM) Server VM (build xx.x-xxx, mixed mode)
Die Installation des JDK wäre damit abgeschlossen.
Tomcat Installation
Eine aktuelle Version des Apache-Tomcat kann hier heruntergeladen werden.
user@debian:~$ tar xvzf apache-tomcat-6.x.x
Im startup-skript bin/catalina.sh wird nun die JVM eingetragen:
JAVA_HOME=/usr/lib/jvm/java-1.x.x-sun
Nun kann Tomcat gestartet werden:
user@debian:~$ bin/catalina.sh start
Und wie gehts weiter ?
Wenn die Installation erfolgreich war, kann man nun die "welcome-site" vom Tomcat aufrufen.
Nachdem man sich ein wenig durchgeklickt hat (vor allem die jsp-examples) legen wir nun das obligatorische HelloWorld Programm an. Mit einem beliebigen Texteditor schreiben wir folgendes:
<% out.print("Hallo Welt!"); %>
... und speichern dies in <tomcat-root>/webapp/ROOT/HelloWorld.jsp
Hinweis: Achte immer auf Groß/Kleinschreibung bei den Dateinamen !
Der Aufruf erfolgt im Browser mit: http://localhost:8080/HelloWorld.jsp
Allerdings muss man dabei bedenken, das Projekte meist mehrere JSPs, Servlets und Klassen besitzen. Aus diesem Grund gibt es sog. "Web applications" kurz WepApps. Diese WepApps sind nichts weiter als komprimierte Dateien mit der Endung ".war" (war steht für web archive). WebApps können ausserdem Dateien mit Eigenschaften beinhalten. Eigenschaften die sich auf die WebApp selbst beziehen. Im Abschnitt #Weitere Tipps und Hinweise für Programmierer wird darauf näher eingegangen.
Der Tomcat-Manager
Bevor man sich in den Tomcat-Manager einloggen kann, muss zunächst ein rolename, Benutzer und Passwort in der Datei <tomcat-root>/conf/tomcat-user.xml vergeben werden.
<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
<role rolename="manager"/>
<user username="XXXXX" password="XXXXXX" roles="manager"/>
</tomcat-users>
Dann sollte, nachdem man sich eingeloggt hat, der Tomcat-Manager unter der URL http://localhost:8080/manager/html erreichbar sein:
Erweiterte Installation
Tomcat Natives und Apache Portable Runtime(APR)
Die Tomcat-natives bieten durch 'native' Prozesse (shared memory, unix-sockets etc.) eine bessere Performance und letzendlich eine höhere Skalierbarkeit. Weitere Informationen findest du in der Dokumentation Apache Portable Runtime.
Als erstes wird die Apache Portable Runtime apr.apache.org übersetzt.
tar xvjf apr-1.4.2.tar.bz2
cd apr-1.4.2
./configure
make
Für die Übersetzung der Tomcat-Natives werden noch die "libssl libraries" benötigt:
root@debian:~# aptitude install libssl-dev
Als nächstes werden die Tomcat-natives übersetzt. Diese findest du in der Tomcat-Distribution im Unterordner /bin/tomcat-native.tar.gz.
tar xvzf tomcat-native.tar.gz
cd tomcat-native
./configure --with-apr=<pfad zu apr-1.4.2> --with-java-platform=2 --with-java-home=<pfad zu JAVA_HOME>
make
Im nächsten Schritt wird der APRLifecycleListener in der <tomcat_home>/conf/server.xml eingetragen:
<Listener className="org.apache.catalina.core.AprLifecycleListener" />
Möglicherweise ist dieser Eintrag bereits vorhanden und muss evtl. nur auskommentiert werden.
Zum Abschluss muss die übersetzte Library nur noch der JVM bekannt gemacht werden. Generell wird dies über die java-option -Djava.library.path=<path> erledigt. Wenn du JSVC benutzt geschieht der Eintrag im start-stop-script. Ansonsten wird die Option Djava.library.path in der /bin/catalina.sh hinzugefügt:
JAVA_OPTS="-server -Xms512m -Xmx512m -Djava.library.path=<pfad-zu-tomcat-native/jni/native/.libs"
Beim Startup sollte nun folgende Zeile ohne anschliessender Exceptions auftauchen (unbedingt prüfen!):
13.08.2007 17:19:34 org.apache.catalina.core.AprLifecycleListener init
Java Service Daemon (init.d Start-Stop script)
Das Sourcepaket jsvc.tar.gz findet man praktischerweise im bin/ -Ordner der Tomcatdistribution.
tar xvzf jsvc.tar.gz
cd jsvc-src
./configure --with-java=/usr/lib/jvm/java-1.5.0-sun
make
Die kompilierte Datei 'jsvc' kann nun in den Ordner /sbin oder /usr/bin kopiert werden (Auf Dateirechte achten !).
Hinweis: In älteren Kernelversionen (<2.6.24) muss das Kernelmodul 'CAPABILITY' geladen werden:
root@debian:~# modprobe capability
Das eigentliche Init-Skripte start-stop-script wird in /etc/init.d abgelegt. Es könnte folgendermaßen aussehen:
# ######################
# Ab hier anpassen
JAVA_HOME=/usr/lib/jvm/java-1.6.0-sun
CATALINA_HOME=<tomcat-root>
DAEMON=/usr/bin/jsvc
TOMCAT_USER=tomcat
TMP_DIR=/var/tmp
PID_FILE=/var/run/jsvc.pid
TOMCAT_LOG=/var/log/tomcat.log
# Wenn vorhanden auskommentieren
#CATALINA_OPTS="-Djava.library.path=<pfad-zu-native-libs>"
# Anpassen ende
# ######################
SELF=$(cd $(dirname $0); pwd -P)/$(basename $0)
CLASSPATH=\
$JAVA_HOME/lib/tools.jar:\
$CATALINA_HOME/bin/commons-daemon.jar:\
$CATALINA_HOME/bin/bootstrap.jar
case "$1" in
start)
# Versuche modprobe capability (nur aeltere kernel)
#c=$(lsmod | grep -c "capability")
#if [ $c -eq 0 ]; then
# modprobe capability
# c=$(lsmod | grep -c "capability")
# if [ $c -eq 0 ]; then
# exit 1
# fi
#fi
$DAEMON \
-user $TOMCAT_USER \
-home $JAVA_HOME \
-Dcatalina.home=$CATALINA_HOME \
-Dcatalina.base=$CATALINA_HOME \
-Djava.io.tmpdir=$TMP_DIR -Djava.awt.headless=true \
-wait 10 \
-pidfile $PID_FILE \
-outfile $TOMCAT_LOG \
-errfile '&1' \
$CATALINA_OPTS \
-cp $CLASSPATH \
org.apache.catalina.startup.Bootstrap
exit $?
;;
stop)
$DAEMON \
-stop \
-pidfile $PID_FILE \
org.apache.catalina.startup.Bootstrap
exit $?
;;
restart)
$SELF stop
$SELF start
;;
*)
echo "Usage $SELF start/stop/restart"
exit 1;;
esac
Apache2 mit Tomcat verbinden
Eigentlich macht es keinen Sinn komplette Webanwendungen mit Tomcat zu hosten. Warum den in Tomcat integrierten Webserver (HttpConnector) mit Anfragen auf statischen Inhalt belasten? Abgesehen davon ist es vermutlich auch ein Sicherheitsrisiko Tomcat direkt im Inet zu hosten. Im folgenden soll dieses Problem mit mod_jk bzw. mit mod_proxy gelöst werden.
Das Prinzip dabei ist folgendermaßen:
- Alle Anfragen (Requests) gehen an den Webserver Apache.
- Handelt es sich um statischen Inhalt, liefert Apache diesen aus seinem Doc-root (/var/www/...) aus.
- Handelt es sich um dynamischen Inhalt, leitet Apache die Anfrage an Tomcat weiter. Tomcat liefert die Antwort (Response) erst zum Apache zurück, Apache leitet dann weiter zum Client. Der Apache aggiert somit als Vermittler.
mod_jk oder mod_proxy ?
Da sich mod_proxy sehr einfach konfigurieren lässt, sollten Einsteiger bzw. solche die ein schnelles Ergebnis benötigen, diese Variante vorziehen. Leider bietet mod_proxy bei Weitem nicht so viele Features wie das bei mod_jk der Fall ist. Mit anderen Worten: Wer sich mit mod_jk auskennt, verfügt, aufgrund der vielen Einstellungsmöglichkeiten, über maximale Flexibilät, muss sich allerdings ersteinmal einlesen. In diesem Tutorial kann unmöglich auf alle Features von mod_jk eingegangen werden. Es dient lediglich dazu, den Einstieg etwas zu erleichtern.
mod_jk (ajp13)
Zunächst wird das Apache-Modul mod_jk geladen und aktiviert:
apt-get install libapache2-mod-jk a2enmod jk
Nun öffnen wir die Datei /etc/apache2/apache2.conf und tragen folgendes ein:
<IfModule mod_jk.c> JkLogFile /var/log/apache2/mod_jk.log JkLogLevel info JkShmFile /var/cache/jk.shm JkWorkersFile /etc/apache2/worker.properties </IfModule>
Als nächstes legen wird die Datei /etc/apache2/worker.properties mit folgendem Inhalt an:
# TOMCAT_HOME workers.tomcat_home = /opt/tomcat # JAVA_HOME workers.java_home = /usr/lib/jvm/java-1.5.0-sun # Path separator ps = / # WORKER LIST worker.list = testworker # Die Einstellungen von testworker worker.testworker.port = 8009 worker.testworker.host = localhost worker.testworker.type = ajp13
Hinweis: Die Variable TOMCAT_HOME und JAVA_HOME muss evtl. noch angepasst werden. |
Damit Tomcat die Anfragen auch auf port 8009 annimmt muss in der <tomcat-home>/conf/server.xml der AJPConnector eingetragen werden. Möglicherweise ist dieser schon eingetragen und es müssen nur die Kommentar-Zeichen entfernt werden.
<Connector port="8009" enableLookups="false" protocol="AJP/1.3" />
Damit wäre die Basis fertig installiert. Nun muss man nur noch dem Apache mitteilen welche Anfragen für Tomcat bestimmt sind. Das funktioniert mit einem JkMount. Innerhalb eines <VirtualHost> könnte das so aussehen:
<VirtualHost *:80> ... JkMount /MyWebApp* testworker JkMount /MyWebApp/* testworker ... </VirtualHost>
Damit die Änderungen vom Apache und Tomcat übernommen werden müssen diese neu gestartet werden (force-reload).
mod_proxy
In der Datei <tomcat-home>/conf/server.xml wird zunächst der passende Connector freigegeben:
<Connector port="8082" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" acceptCount="100" connectionTimeout="20000" proxyPort="80" disableUploadTimeout="true" />
Hinweis: Dieser Abschnitt muss möglicherweise nur von Kommentarzeichen befreit werden. |
Als nächstes wird mod_proxy eingerichtet:
a2enmod proxy_http proxy /etc/init.d/apache2 force-reload
Innnerhalb eines <Virtualhost> könnte folgendes stehen:
ProxyPass /webapp-name http://localhost:8082/webapp-name/ ProxyPassReverse /webapp-name http://localhost:8082/webapp-name/
Eine Anfrage nach http://localhost/webapp-name/ wird nun komplett an Tomcat weitergeleitet.
Weitere Tipps und Hinweise für Programmierer
WebApps erzeugen mit Apache ANT
Das Apache-Projekt ANT dient der automatisierten Erzeugung von Programmen. Es übersetzt Programm-Quellen (sourcecodes), schnürt Softwarepakete und kann weitere komplexe Aufgaben erledigen. Da ANT in der Java-Runtime läuft, ergibt sich der Vorteil, das es Plattformunabhängig ist. Nicht nur das. ANT lässt sich in beinahe allen verfügbaren JAVA-IDEs integrieren. Für größere Projekte mit mehreren Entwicklern ergibt sich zusätzlich der Vorteil, daß die build-scripte über Versionsver- waltungssysteme verteilt und dadurch eine einheitliche Struktur gepflegt werden kann.
Die Main-Features von ANT:
- Kompilieren von javasourcen und JSPs (JSP-precompile)
- Dateien/Verzeichnisse kopieren, verschieben, löschen, erzeugen
- Schnüren und Komprimieren von Programmpaketen
- Deployment
- Macros
- Anbindung an Versionsverwaltungssysteme (SVN, CVS, …)
- Ausführen externer Programme (z.b. shell execution)
- und vieles mehr ....
Bevor es losgeht erstmal ein:
root@debian:~# apt-get install ant
Die Struktur und Organisation der sourcen sind einem Selbst überlassen. Wenn sie aber zu einer WebApp zusammengeschnürt werden, sind die Strukturen klar definiert. Dank Apache-ANT müssen wir uns glücklicherweise nicht selbst darum kümmern. Denoch sollte man in einem Projekt-verzeichnis für etwas Übersicht sorgen:
mkdir testprojekt;cd testprojekt
mkdir -p src/testpackage
mkdir -p WEB-INF/classes
mkdir resources
Die Verzeichnisstruktur sieht dann folgendermaßen aus:
testprojekt <---- projekt root-Ordner |-- src <---- Java sourcen |-- testpackage <---- JavaBeans werden stets innerhalb eines "Package" abgelegt. |-- resources <---- JSP-Files |-- WEB-INF |-- classes <---- die kompilierten Java-Klassen |-- web.xml <---- deployment descriptor (Eigenschaften einer WebApp)
Der Deployment descriptor wird in WEB-INF/web.xml abgelegt - hier eine minimal-version:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" >
<display-name>Ein Testprojekt</display-name>
</web-app>
Eine JSP-Seite soll hier zu Testzwecken ein JavaBean benutzen. In resources/index.jsp schreiben wir also folgendes:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<jsp:useBean scope="request" id="bean" class="testpackage.TestBean"/>
<% bean.setMessage("Das ist ein test"); %>
<html>
<head><title>test site</title></head>
<body><%=bean.getMessage()%></body>
</html>
Das Bean soll sich einen String merken können. In src/testpackage/TestBean schreiben wir folgendes:
package testpackage;
public class TestBean implements java.io.Serializable {
private String message;
public void setMessage(String msg){
this.message = msg;
}
public String getMessage() {
return message;
}
}
Nun gehts an das eigentliche Übersetzen und Schnüren einer WebApp. Dazu legen wir eine Datei im Projekt root-Verzeichnis mit dem namen build.xml an.
Hinweis: Das, was Make und Makefile zum übersetzen von C-sourcen ist, ist für Java ANT und die build.xml. |
Hier ein minimal Beispiel:
<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="testprojekt" default="all">
<!-- Default wenn ant ohne Angabe eines Task angeben wird -->
<target name="all" depends="compile,j2ee_build,deploy_local"/>
<!-- Der app-name wird später zum app-context -->
<property name="application_name" value="testprojekt"/>
<!-- Der Pfad zu <tomcat-root>/webapps/ -->
<property name="deploy_local_path" value="<tomcat_root>/webapps"/>
<!-- Kompilierte Klassen gehen nach /WEB-INF/classes -->
<property name="compiler_output_path" value="WEB-INF/classes"/>
<!-- Der source-path den wir zuvor angelegt hatten -->
<property name="source_path" value="src"/>
<!-- Der resource-path den wir zuvor angelegt hatten -->
<property name="resources_path" value="resources"/>
<!-- Der Kompiler wird angestoßen -->
<target name="compile">
<javac destdir="${compiler_output_path}">
<src>
<dirset dir="${source_path}"/>
</src>
</javac>
</target>
<!-- Die kompilierten Klassen, sowie JSP und Descriptor, wandern
nun in eine komprimierte WebApp-Datei, in diesem Fall nach testprojekt.war
-->
<target name="j2ee_build">
<property name="j2ee_build_path" value="${application_name}.war"/>
<jar destfile="${j2ee_build_path}" duplicate="preserve">
<manifest>
<attribute name="Manifest-Version" value="1.0"/>
</manifest>
<zipfileset dir="${compiler_output_path}" prefix="WEB-INF/classes"/>
<zipfileset dir="${resources_path}" prefix=""/>
<zipfileset dir="WEB-INF" prefix="WEB-INF"/>
<zipfileset file="WEB-INF/web.xml" prefix="WEB-INF"/>
</jar>
</target>
<!-- Die WebApp (testprojekt.war) wird zum Schluss einfach in den webapp-ordner von
Tomcat kopiert. Tomcat selbst kümmert sich um alles weitere ... -->
<target name="deploy_local">
<copy file="${application_name}.war" tofile="${deploy_local_path}/${application_name}.war"/>
</target>
</project>
In der Konsole geben wir nun folgendes ein:
user@debian:~$ ant all
Wobei das all eigentlich weggelassen werden kann, da es ohnehin als default gesetzt wurde. Parallel dazu sollte man im tomcat-log (<tomcat-root>/logs/catalina.out) den Deploy-Vorgang beobachten:
16.08.2007 15:41:26 org.apache.catalina.startup.HostConfig deployWAR INFO: Deploying web application archive testprojekt.war
Der Aufruf im Browser erfolgt mit: http://localhost:8080/testprojekt/index.jsp
Wenn alles funktioniert kann man das Ganze bequem auch für andere Projekte als Template verwenden.
Remote Deploy
Oft kommt es vor, das man nicht an dem Rechner entwickelt, wo auch Tomcat installiert wurde. In diesem Fall wäre vielleicht SCP oder ein FTP-Transfer eine möglichkeit, aber ANT hat dafür auch etwas parat.
Damit ANT mit dem Tomcat-Manager umgehen kann, müssen wir, durch setzten eines CLASSPATH, oder besser durch Kopieren der Bibliothek in den Lib-Ordner von ANT, die Datei catalina-ant.jar bekanntmachen:
root@debian:~# cp <tomcat>/server/lib/catalina-ant.jar /usr/share/ant/lib/
In der build.xml fügen wir nun ein weiteres "target" hinzu (User, Passwd und manager_path ersetzen !):
<property name="manager_path" value="http://xxxxxx:8080/manager"/>
<property name="user" value="xxxxxx"/>
<property name="passwd" value="xxxxxx"/>
<target name="deploy_remote">
<taskdef name="deploy" classname="org.apache.catalina.ant.DeployTask"/>
<deploy url="${manager_path}"
username="${user}"
password="${passwd}"
path="/${application_name}"
war="file:${application_name}.war"
update="true">
</deploy>
</target>
Damit der neue Task ausgeführt wird, starten wir den Build mit ant deploy_remote oder wir tragen es für default all ein:
<target name="all" depends="compile,j2ee_build,deploy_remote"/>
Weitere Möglichkeiten kann man in der Dokumentation Ant Manager-commands nachlesen.
MySQL - JNDI Datasources/Datenbank Verbindungspool
Ausgehend davon, daß Mysql installiert, eine test -datenbank/-tabelle angelegt und ein Testbenutzer mit den korrekten Privileges eingerichtet wurde, können wir gleich zur Sache kommen. Die Definition einer Data-Source erfolgt in der Datei context.xml. Welche context.xml verwendet wird, ergibt sich aus folgendem:
- <tomcat_home>/conf/context.xml -> Data-Source steht für alle Webanwendungen zur Verfügung
- <webapp_root>/META-INF/context.xml -> Data-Source sichtbar nur für die eigene Webanwendung
- ... und weitere, mehr Informationen hier
Im folgenden Beispiel wird die META-INF der Webanwendung benutzt:
<Context path="/DataSourceTestApp" debug="5" reloadable="true" crossContext="true">
<Resource name="jdbc/datasourcetest" auth="Container" type="javax.sql.DataSource"
maxActive="20" maxIdle="10" maxWait="10000" initialSize="10"
username="testuser" password="testuser" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/testdb?autoReconnect=true"/>
</Context>
Damit wäre nun eine Resource Deklariert. Wichtig ist natürlich die Anpassung von Context-Path in der 1. Zeile Nicht vergessen: Auch die Zugangsdaten zum MySQL-Server müssen noch angepasst werden.
Nun ergänzen wir die WEB-INF/web.xml um folgenden Eintrag:
...
<resource-ref>
<description>Test DataSource</description>
<res-ref-name>jdbc/datasourcetest</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
...
Das war es auch schon. Hier noch ein kleines JSP-Beispiel die den Zugriff auf eine Data-Source demonstriert:
<%@ page import="java.sql.*" %>
<%@ page import="javax.naming.*" %>
<%@ page import="javax.sql.DataSource" %>
<html>
<head><title>DataSource Test</title></head>
<body>
<%
Connection con = null;
Statement stmt = null;
ResultSet rset = null;
try {
Context ctx = (Context) new InitialContext().lookup("java:comp/env");
DataSource ds = (DataSource)ctx.lookup( "jdbc/datasourcetest" );
con = ds.getConnection();
stmt = con.createStatement();
rset = stmt.executeQuery( "Select * from test" );
while(rset.next()) {
out.println("prim_key: " + rset.getInt(1)+"<br>" );
out.println("wert: " + rset.getString(2)+"<br>" );
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(rset != null) rset.close();
if(stmt != null) stmt.close();
if(con != null) con.close();
catch( Exception ex ) {}
}
%>
</body>
</html>
Hinweis: Hinweise zum JSP-Beispiel:
Das SQL-statement muss an das eigenen Schema angepasst werden. Desweiteren sollte man immer darauf achten, daß Statement, Resultset und Connetion nach einer Transaktion geschlossen werden. Tut man dieses nicht, kann es zu sogenannten Leaks kommen. Daher sollte man einen Blick in die JNDI Datasource HOW-TO (Besonders unter Preventing connection pool leaks) werfen. |