Sunday, 7 June 2009

setup tomcat6 (with native library) with apache2 integration



In this post, I'll setup a apache2 with php5 and tomcat6 integrated with apache2.

Since Tomcat 5, you don't need a front http server anymore, but since I want to have php5 and tomcat6 on port 80, I need the tomcat6-apache2 integration with modjk


since a lot of operation need the root privilege, switch to root :

sudo -s

Apache2 & PHP5 installation



The following command install php5, php5 command line interface (CLI to use php as shell script) and some common php extension.

aptitude install php5 php5-cli apache2 php5-xmlrpc php5-mysql php5-gd php5-imagick php-pear php5-mcrypt
a2enmod headers #Activation of Mod headers
a2enmod deflate #Activation of Mod deflate


Setup the timezone :

vi /etc/php5/apache2/php.ini
[Date]
; Defines the default timezone used by the date functions
date.timezone = Europe/Paris


Java installation

aptitude install sun-java6-jdk
vi /etc/profile

Append the following line :

export JAVA_HOME=/usr/lib/jvm/java-6-sun

reload your .profile :

(command to type is "dot space dot profile")
. .profile

check :

echo $JAVA_HOME
/usr/lib/jvm/java-6-sun
root@rdp:/home/special/tomcat# java -version
java version "1.6.0_13"
Java(TM) SE Runtime Environment (build 1.6.0_13-b03)
Java HotSpot(TM) 64-Bit Server VM (build 11.3-b02, mixed mode)


Tomcat6 installation



In this installation, I install the tomcat binaries in /usr/local and separate the CATALINA_HOME and the CATALINA_BASE

CATALINA_HOME is in /usr/local/tomcat (the engine)
CATALINA_BASE is in /home/special/tomcat (the configuration files to configure tomcat for your app)

Like this I can have several CATALINA_BASE (with differnent tomcat config) with one HOME (which is a link to the last current version, upgrading to the next version will mean change a link, switch back to the previous version : change the link to its original point).

mkdir ~/download
cd ~/download
wget http://apache.cict.fr/tomcat/tomcat-6/v6.0.20/bin/apache-tomcat-6.0.20.zip
unzip apache-tomcat-6.0.20.zip
mv apache-tomcat-6.0.20 /usr/local/
ln -s /usr/local/apache-tomcat-6.0.20 /usr/local/tomcat
chmod 755 /usr/local/tomcat/bin/*.sh
mkdir /home/special/tomcat
cp -r /usr/local/tomcat/* /home/special/tomcat
cd /home/special/tomcat
rm -r bin/ lib/ LICENSE NOTICE RELEASE-NOTES RUNNING.txt webapps/*


Disable the SSL Engine

as I won't use it :

switch the value from 'on' to 'off' on this line :
<listener classname="org.apache.catalina.core.AprLifecycleListener" sslengine="off"/>


Edit tomcat init script

with this script you'll be able to start/stop/restart tomcat with the service command.

It's in this script I'll specify where the CATALINA_HOME and CATALINA_BASE are.
If you want another instance of tomcat with another CATALINA_BASE location, write a second script with a different CATALINA_BASE.

In this script I set the jvm in server mode "-server" and specify some memory settings.

vi /etc/init.d/tomcat
# Tomcat auto-start
#
# description: Auto-starts tomcat
# processname: tomcat
# pidfile: /var/run/tomcat.pid

JAVA_HOME=/usr/lib/jvm/java-6-sun; export JAVA_HOME
JAVA_OPTS="-server -Xms100m -Xmx512m"; export JAVA_OPTS
CATALINA_HOME=/usr/local/tomcat; export CATALINA_HOME
CATALINA_BASE=/home/special/tomcat; export CATALINA_BASE


case $1 in
start)
sh $CATALINA_HOME/bin/startup.sh
;;
stop)
sh $CATALINA_HOME/bin/shutdown.sh
;;
restart)
sh $CATALINA_HOME/bin/shutdown.sh
sh $CATALINA_HOME/bin/startup.sh
;;
*)
echo $"Usage: $0 {start|stop|restart}"
exit 1
esac
exit 0


chmod 755 /etc/init.d/tomcat

#auto start & stop with the machine :
ln -s /etc/init.d/tomcat /etc/rc1.d/K99tomcat
ln -s /etc/init.d/tomcat /etc/rc2.d/S99tomcat


MOD_JK installation


MOD_JK is here to link apache2 and tomcat.

aptitude install libapache2-mod-jk

Edit the jk.conf file, which is here to setup general config option of mod_jk

vi /etc/apache2/mods-available/jk.conf
JkWorkersFile      /etc/apache2/workers.properties
JkLogFile "|/usr/bin/rotatelogs /var/log/apache2/mod_jk.log 86400"
JkLogLevel info
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
JkRequestLogFormat "%w %V %T"
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories

Activation of modjk :
cd /etc/apache2/mods-enabled
sudo ln -s ../mods-available/jk.conf

In workers.properties we specify the links between apache2 and J2EE servers (tomcat/jetty etc..)
You can have multiple J2EE servers declared in this file.

here I setup one J2EE server which is on the same server (yes, the tomcat could have been on another server)

vi /etc/apache2/workers.properties

worker.list=tomcat6

worker.tomcat6.type=ajp13
worker.tomcat6.host=localhost
worker.tomcat6.port=8009


Apache2 virtualhost configuration


Here we add a virtualhost for the tomcat application.
Based on the servername sent by the browser, apache will select the virtualhost to send the http request.
(At the end of this post I'll explain how to use this)

let's say the application is named "crf-rdp"

mkdir -p /home/special/tomcat/www/crf-rdp
Add a virtual host:
vi /etc/apache2/sites-available/crf-rdp
<VirtualHost *>
ServerAdmin webmaster@localhost
DocumentRoot /home/special/tomcat/www/crf-rdp

ServerName crf-rdp.server.com

<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>

<Directory /home/special/tomcat/www/crf-rdp>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>


ErrorLog "|/usr/local/apache/bin/rotatelogs /var/log/apache2/error_crf-rdp.log 86400"
CustomLog "|/usr/local/apache/bin/rotatelogs /var/log/apache2/access_crf-rdp.log 86400" combined

# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
ServerSignature On

RewriteEngine on
RewriteLog /var/log/apache2/mod_rewrite_crf-rdp.log
RewriteLogLevel 1
RewriteCond %{REQUEST_URI} !^/crf-rdp
RewriteRule ^(.*) /crf-rdp$1 [PT,L]

JkMount /crf-rdp/* tomcat6


</VirtualHost>

With the rewrite rule and the JkMount all http request sent to crf-rdp.domain.com will be forwarded to the tomcat webapp.


Set the new virtual host as available :
ln -s /etc/apache2/sites-available/crf-rdp 001-crf-rdp


Installation of Apache Tomcat Native Library


This "library allows optimal performance in production environment".

Note : libapr1 is installed with apache2

aptitude  install make gcc gcc-4.2 gcc-4.2-locales gcc-4.2-multilib libapr1-dev
cd /usr/local/apache-tomcat-6.0.20/bin/
tar zxf tomcat-native.tar.gz
cd /usr/local/apache-tomcat-6.0.20/bin/tomcat-native-1.1.16-src/jni/native
export JAVA_HOME=/usr/lib/jvm/java-6-sun
./configure --with-apr=/usr/bin/apr-config; make; make install
cd /usr/lib
ln -s /usr/local/apr/lib/libtcnative-1.so.0.1.16 libtcnative-1.so


when your tomcat will start, you can check the catalina.out log file and this the following :

thomas@home:/home/special/tomcat/logs$ cat catalina.out
Oct 18, 2008 9:13:02 PM org.apache.catalina.core.AprLifecycleListener init
INFO: Loaded APR based Apache Tomcat Native library 1.1.16.
Oct 18, 2008 9:13:02 PM org.apache.catalina.core.AprLifecycleListener init
INFO: APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
Oct 18, 2008 9:13:02 PM org.apache.catalina.core.AprLifecycleListener lifecycleEvent
INFO: Failed to initialize the SSLEngine.
Oct 18, 2008 9:13:02 PM org.apache.coyote.http11.Http11AprProtocol init
INFO: Initializing Coyote HTTP/1.1 on http-8080
Oct 18, 2008 9:13:02 PM org.apache.coyote.ajp.AjpAprProtocol init
INFO: Initializing Coyote AJP/1.3 on ajp-8009
Oct 18, 2008 9:13:02 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 708 ms
Oct 18, 2008 9:13:02 PM org.apache.catalina.core.StandardService start
INFO: Starting service Catalina
Oct 18, 2008 9:13:02 PM org.apache.catalina.core.StandardEngine start
INFO: Starting Servlet Engine: Apache Tomcat/6.0.18
Oct 18, 2008 9:13:02 PM org.apache.coyote.http11.Http11AprProtocol start
INFO: Starting Coyote HTTP/1.1 on http-8080
Oct 18, 2008 9:13:02 PM org.apache.coyote.ajp.AjpAprProtocol start
INFO: Starting Coyote AJP/1.3 on ajp-8009
Oct 18, 2008 9:13:02 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 109 ms



Now you should have a working installation with tomcat6 linked with apache2.

You start/stop/restart tomcat with
service tomcat start
service tomcat stop
service tomcat restart



Now here is some useful tips you may find valuable :

Define a dataSource in tomcat


It's useful and more secure to define a dataSource (database connection) inside the J2EE server instead of inside the application itself.

Because developpers should know production credentials and with credentials inside your application properties files, it goes into your cvs/svn/bazaar/git source control.
While if it's a dataSource that you use as a JDNI ressource, the person who operate tomcat (the server admin) is the only one knows the dataSource credentials.

You first need to get the jdbc driver of the datasource.
The example is here for mysql (but works too with other db servers)

cd ~/download
wget http://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.7.tar.gz/from/http://mir2.ovh.net/ftp.mysql.com/
tar zxf mysql-connector-java-5.1.7.tar.gz
cp mysql-connector-java-5.1.7/mysql-connector-java-5.1.7-bin.jar /usr/local/tomcat/lib/

cd /home/special/tomcat/conf
mkdir -p Catalina/localhost

Example for the CRF-RDP application :



(where crf-rdp is the context-path of the webapp, see first chapter here http://tomcat.apache.org/tomcat-6.0-doc/config/context.html)

vi /home/special/tomcat/conf/Catalina/localhost/crf-rdp.xml
<Context path="crf-rdp" docBase="/home/special/tomcat/crf/crf-rdp.war" debug="0">

<Resource name="jdbc/crfirpDS" auth="Container" type="javax.sql.DataSource"
username="crfirpUser"
password="***********"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost/crfirpDb?autoReconnect=true&useUnicode=true&characterEncoding=UTF8"
validationQuery="Select 1"
/>

</Context>

here we setup a datasource named "crfrdpDS" for the webapplication "crf-rdp" which has its war file here "/home/special/tomcat/crf/crf-rdp.war".
It connects to the localhost mysql database, with 'crfrdpUser' database user, on crfrdpDb database schema.

In the application, the datasource should be named 'java:comp/env/jdbc/crfrdpDS' (without quote) (and not 'jdbc/crfrdpDS' as you may think it would be... to easy ;))

Notice : about the password, don't use < or & since it's XML special characters (it should be encoded)

A virtualhost for phpMyAdmin



PhpMyAdmin is a great tool to operate your mysql databases (even more usefull than mysql-query-browser in many situation).
But your database is your most precious thing...
So expose a phpMyAdmin over internet is not the best thing to do.

But you can still enjoy phpMyAdmin in a secure way :

Create a virtualhost for phpMyAdmin that accept http connection only for localhost client,
and then connect to your server using ssh with a tunnel so that Apache see localhost connection.


mkdir /home/special/phpMyAdmin
cd /home/special/phpMyAdmin
wget http://freefr.dl.sourceforge.net/sourceforge/phpmyadmin/phpMyAdmin-3.1.5-all-languages.tar.bz2
tar jxf phpMyAdmin-3.1.5-all-languages.tar.bz2
mv phpMyAdmin-3.1.5-all-languages/* .
rm -r phpMyAdmin-3.1.5-all-languages phpMyAdmin-3.1.5-all-languages.tar.bz2


vi /etc/apache2/sites-available/phpmyadmin
<VirtualHost 127.0.0.1:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/phpmyadmin

ServerName localhost

<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>

<Directory /var/phpmyadmin/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>

# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn

ErrorLog /var/log/apache2/phpmyadmin/error.log
CustomLog /var/log/apache2/phpmyadmin/access.log combined

ServerSignature On
</VirtualHost>

cd /etc/apache2/sites-enabled
ln -s ../sites-available/phpmyadmin 001-phpmyadmin
mkdir /var/log/apache2/phpmyadmin
service apache2 restart


Now, connect to your server using this command :

sudo ssh -lthomas -L 81:localhost:80 my.server.com


(you can do the same with putty SSH->Tunnel)

and type in your browser :

http://localhost:81

you should see phpMyAdmin.


Virtualhost and DNS trick



Sometime you need to access you website by its full domain name while the domain is not yet setup or the website is not open yet (you're still developping it...).


So, you either access to your site with it's IP or maybe mainserverDNSName.com/~username

or if you block the access you may have set up your virtualhost like the phpMyAdmin one with <virtualhost 127.0.0.1:80/> and ServerName localhost

To access you website as if it were already with its production url (which is www.mydomain.com) do this :


Edit on your client machine the etc/hosts file :

[c:\windows\system32\drivers]/etc/hosts file, add

127.0.0.1 www.mydomain.com
#blank line
(replace 127.0.0.1 with the server ip if you were accessing your server by its IP)


open an ssh connection with a tunnel

sudo ssh -lthomas -L 80:localhost:80 serverIp

(or do the same with putty in windows)

type in your browser
http://www.mydomain.com

What happen :

Your local client machine will resolve http://www.mydomain.com as localhost because of your /etc/hosts file.
The http connection will go through your ssh tunnel and hit your web server as a local(from the server point of view) connection with a servername in the http request "www.mydomain.com" so that Apache will select the right virtualhost.

(It might not be that clear... I'll maybe update this part with some more detailed explanation).

1 comment:

Alex said...

Hi,

Just wanted to thank you for posting this guide.

Regards,