Sunday, January 25, 2009

Apache in front of Tomcat and JBoss

Recently I have to deploy two Java web applications on a single server (one IP address). Where static content was to be served by Apache Http web server instead of Java application servers. When we have multiple applications deployed on an instance of Tomcat or JBoss we have their URLs something like http://server:port/appA, http://server:port/appB etc. This kind of URLs does not look good when we publish it to external world. So instead of 'http://domainA.tld/appA' we wanted to have URL as 'http://domainA.tld/'. This can be achieved by running applications on different instances of Tomcat / JBoss and fronting them with Apache using virtual hosts.

Running multiple instances of JBoss on a machine with single IP address is a nightmare, while it is much easier to run several Tomcat instances, as one has to change just four ports (8005, 8080, 8443, 8009). An application can be run at root context in Tomcat by adding a line like below, inside <host> element in $TOMCATHOME/conf/server.xml-

    <context path="" docbase="appA.war" unpackWAR="false" />

There are pretty decent documentation available on Apache, Tomcat and JBoss' website about their installation and configuration. So I will leave that discussion here itself. Though there is lots of documentation available for Apache virtual hosting also but it takes lots of effort for a newbie to do the configuration and put it in front of application servers. Here I am assuming that Apache Tomcat Connector (mod_jk) is also installed along with Apache web server.

To establish communication between Apache and application server, first we need to define connector workers as shown below-

    worker.list=appA,appB

    # Define appA
    worker.appA.port=9009
    worker.appA.host=localhost
    worker.appA.type=ajp13
    worker.appA.lbfactor=1

    # Define appB
    worker.appB.port=8009
    worker.appB.host=localhost
    worker.appB.type=ajp13
    worker.appB.lbfactor=1


In the above configuration appA's server's connector is listening on port 9009 and appB's on 8009. This connector port is different from http port (8080 by default for Tomcat). Now define the virtual hosts, this can be done either in Apache's httpd.conf or in a separate file and include that in httpd.conf.

   NameVirtualHost *:80

   LoadModule jk_module modules/mod_jk.so

   # Where to find workers defined above
   JkWorkersFile conf/extra/worker.properties

   <virtualhost *:80>
       ServerAdmin someone@domainA.tld
       DocumentRoot "/path/to/the/content"
       ServerName domainA.tld
       ErrorLog "logs/domainA-error.log"
       CustomLog "logs/domainA-access.log" common

       JkMount / appA
       JkMount /* appA
       JkUnMount /static|/* appA

       JkLogFile logs/mod_jk_appA.log
       JkLogLevel info
       JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"
       JkOptions +ForwardKeySize +ForwardURICompatUnparsed -ForwardDirectories
       JkRequestLogFormat "%w %V %T"
   </virtualhost>

   <virtualhost *:80>
       ServerAdmin someone@domainB.tld
       DocumentRoot "/path/to/the/content"
       ServerName domainB.tld
       ErrorLog "logs/domainB-error.log"
       CustomLog "logs/domainB-access.log" common

       JkMount / appB
       JkMount /* appB
       JkUnMount /static|/* appB

       JkLogFile logs/mod_jk_appB.log
       JkLogLevel info
       JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"
       JkOptions +ForwardKeySize +ForwardURICompatUnparsed -ForwardDirectories
       JkRequestLogFormat "%w %V %T"
   </virtualhost>


With above configuration all requests to domainA.tld are served by worker appA and domainB.tld is served by worker appB. The 'JkUnMount /static|/* appA' causes any URL like http://domainA.tld/static/* to be served by Apache instead of the application server. As Apache performs far better while serving the static content like images, html, css, java script etc. so it is good to unmount their URLs from the mod_jk worker. Most of the Jk* properties can be defined out of the <virtualhost> element at global level, that will avoid duplicate entries. Though defining them inside <virtualhost> element gives flexibility to have different values for each virtual host. Detailed information about Jk* is available here.

I have tested above configuration with following environment-

  • Java: 1.6.0_10
  • Tomcat: 6.0.18
  • JBoss: 5.0.0.GA
  • Apache: 2.2.11
  • OS: Fedora 9, Red Hat Enterprise Linux 5 and Windows Vista
Hope this will be useful to others and save their time.

27 Comments:

Anonymous said...

A few years ago I saw someone doing almost the same thing by using Tomcat in a reverse proxy setup with Apache. It was actually quite simple to configure. Have you thought about that?

Anonymous said...

Hi, is mod_jk still the best way to put an apache server in front of tomcat/jboss. I'm just now upgrading from v3.2.5 to v5.

Thanks,
Brent
brent@efficio.us.com

Anonymous said...

mod_jk looks a good option to put in front of JBoss. Though using ProxyPass and ProxyReverse thing one can avoid the mod_jk.

Suneel said...

whcih to use either mod_jk ProxyPass or ProxyReverse in front of jboss using apache.

what's different between both... performance , security etc

Anonymous said...

Apache 2.2 with mod_proxy and related module has made mod_jk redundant. Though mod_jk is still better than mod_proxy in load balancing.

Abhishek Goel said...

Can you possibly share what version of mod_jk you used. I have the following config:

Java: 1.6.0_13
Tomcat: 6.0.18
Apache: 2.2.11
OS: Windows XP

I tried the following two jk modules, but I can't seem to connect from Apache to Tomcat:
- mod_jk-1.2.28-httpd-2.2.3.so
- mod_jk-1.2.27-httpd-2.2.10.so

Thanks.

AG

Anonymous said...

I used mod_jk-1.2.27-httpd-2.2.6.so on Linux and on Windows it was mod_jk-1.2.27-httpd-2.2.10.so

Ravi said...

DocumentRoot "/path/to/the/content"

"/path/to/the/content" what should I give in this path?
is it path from Tomcat server or Apache web server?
and one more, how to access these static content say .js file in application?

Anonymous said...

Ravi,

DocumentRoot "/path/to/the/content" is the absolute path of the content on disk not relative to Tomcat or Apache. Static content like JS or CSS are just a include in HTML/JSP files, so just write correct URL in the files.

Ravi said...

thanks vinod...
still i have one doubt , i hav kept all my javascript files in apache htdocs(C:/apache/htdocs/js) in that js folder i have Example.js , now i want to use this Example.js file in my jsp so what is the url path i have to give?

My apache is running on http://localhost:81 and tomcat is running on http://localhost:8080 . I am able to connect to tomcat through apache and modJk

Anonymous said...

Suppose your JS and JSP files are accessible using following URLs-
JSP- http://localhost:81/jsp/myPage.jsp
JS- http://localhost:81/js/myScript.js

To include the Javascript in JSP page, you should write something like-
@import url(/js/myScript.js);

Hope this helps.

Ravi said...

no vinod am not able to access like this..
JSP- http://localhost:81/jsp/myPage.jsp
JS- http://localhost:81/js/myScript.js

If i request http://localhost:81, at ll directly go to the tomcat (http://localhost:8080) this is my problem, how to get rid of this ? please help me..

Anonymous said...

See the JkMount and JkUnMount references in the post. You need to unmount the /js URL and get it served by Apache.

Also if required you may need to add something like below in server.xml to run application at root context in Tomcat.

<context path="" docbase="appA.war" unpackWAR="false" />

Ravi said...

i have checked, i think am right, but its not working...

VirtualHost localhost:8080>
ServerName localhost
ServerAdmin rbparagi@gmail.com
DocumentRoot "C:/Apache2.2/htdocs"

# All requests go to worker1 by default
JkMount /* ajp13
# Serve html, jpg and gif using httpd
#JkUnMount /*.html ajp13
#JkUnMount /*.jpg ajp13
#JkUnMount /*.gif ajp13
JkUnMount /*.js ajp13

Directory "C:/Apache2.2/htdocs"
# Options Indexes FollowSymLinks MultiViews
Options None
AllowOverride None
Order allow,deny
allow from all
Directory

VirtualHost

this is my configuration..

is it work?
and how to give JkMount and Jkunmount ? can u give me one example? am new to this so..

Anonymous said...

Not sure if 'JkUnMount /*.js ajp13' will work or not. I kept all static content in a different directory (say static) and then unmounted that like below-

JkUnMount /static|/* appB

Also you need to have two JkMount directives instead of one-
JkMount / appB
JkMount /* appB

Why don't you give it a try on same pattern as shown in this blog entry, that may help in narrowing down to the root cause of the problem.

Ravi said...

ok i ll check..
where u kept that static folder?
in apache or in tomcat or independent of these two folders?
And one more what is appB ? is it war file?

Anonymous said...

static goes under Apache DocumentRoot. Yes appB is a war file.

Ravi said...

still i am not getting..
i am thinking i need to give url pattern using RewriteRule . am i right?

Ravi said...

hi vinod..
i am able to connect to C:/apache-tomcat-6.0.18/webapps/ROOT instead of C:\Apache2.2\htdocs..
could u plz help me how to go to this C:\Apache2.2\htdocs ?

Anonymous said...

how to configure the static content like css,js and dynamic content like jsp using apache + weblogic

Anonymous said...

Just unmount the URLs, which serve the static content as shown in the configuration in the post-

JkUnMount /static|/* appB

Do not forget to store the static content in a directory, which is accessible to Apache.

Anonymous said...

Hi Vinod,

I hope you can help you in this problem.

I have Fedora 13, jdk 1.6, jboss 5.0 and eclipse 3.5. Right now everything seems to be working fine but I have to include a separate Tomcat (tomcat 6) in this environment. i.e. I need to disable the tomcat instant that coming with Jboss and need to start a separate tomcat. Also need to use Apache to serve the static contents. Could you please tell me how I can disable the tomcat instant in Jboss and start a separate tomcat server?

Thanks, Vinod.

Nikhil said...

Please share some sample applications to deploy on Tomcat and Jboss 5.0.1 I cant find any on google even applications that worked on JBoss 4.2.3 not working with JBoss 5

Anonymous said...

@Nikhil,

See here http://blog.vinodsingh.com/2008/09/jax-ws-web-service-and-jboss.html
http://blog.vinodsingh.com/2008/09/building-jax-ws-web-service.html

there is an sample application attached there, I believe that should work on both Tomcat and JBoss.

Annu said...

Hi vinod ... i followed your advice but could not get static content served from apache. Do I need to do some configuration changes at tomcat as well. The images folder was in /webapps/appB/ but I removed it since I wanted to serve the images from apache. I put the images folder under my document root as mentioned below but still didn't work out. Can you tell what is wrong in this. I have solaris system to play with apache 2.2 and tomcat 6.0.18.


ServerName www.xyz.com
ServerAdmin webmaster@abc.com
DocumentRoot /opt/apache/www/fundoo/foo
# To turn off rewrite logging is 0, 3 is debug
RewriteLogLevel 0
RewriteEngine off
RewriteCond %{REQUEST_METHOD} ^TRACE
RewriteRule .* - [F]
#Alias /appB /opt/apache/www/fundoo/foo
JkMount /appB/*.gft balancer
JkMount /appB/*.jsp balancer
JkMount /appB/* balancer
JkUnMount /images/* appB
#JkUnMount /*.png balancer
#JkUnMount /*.jpg balancer
#JkUnMount /*.gif balancer
#JkUnMount /*.js balancer

suresh said...

Hi Vinod,

i have some requirement for configuring apache http server 2.0.64 with JBOSS 4.2.3.
I have installed JBOSS with port:8088 and Apache http with port: 8080
Both are up and running.
Now
--->i have downloaded the mod_jk.so and added in the below path:
Apache/modules
--->I have created a new file "mod-jk.conf" and added under Apache/conf with the below content.

mod-jk.conf --->
LoadModule jk_module modules/mod_jk.so
JkWorkersFile conf/workers.properties
JkShmFile logs/mod_jk.shm
JkLogFile logs/mod_jk.log
JkLogLevel info
JkMount /App1/* worker1

--->I have added the below file andd added the line in the last line of the file "Include conf/mod-jk.conf" Apache/conf/httpd.conf

--->I have created workers.properties inside apache/conf folder with below content.
worker.list=worker1
worker.worker1.type=ajp13
worker.worker1.host=localhost
worker.worker1.port=8009


Now both the ervers are up and running successfully. But how can i know that these 2 are connected and configured properly.

Thanks in Advance,
Suresh.
suvunnam@in.ibm.com

Vinod Singh said...

@Suresh,

Try to access the application via Apache (port 80) that will let you know whether integration is working or not.