Friday, October 16, 2009

Virtual Hosting with Tomcat 4.x





























Chapter 19 -
Shared Tomcat Hosting
byVivek Chopra, Ben Galbraithet al.
Wrox Press 2003































Virtual Hosting with Tomcat 4.x


Virtual host configuration in Tomcat 4.x is different from that in Tomcat 3.x in a number of ways. Mostly, this has been because Tomcat 4.x was rewritten from scratch and the whole architecture of the various components of Tomcat has changed.


One common factor between the two configurations is that the configuration in Apache for transferring requests for servlets and JSP pages to the Tomcat process has more or less remained the same while using the mod_jk adapter. In addition, Tomcat 4.x can also use mod_webapp, which we will see later.


We first take a look at configuring Tomcat 4.x as a standalone server implementing virtual hosts.




Tomcat 4.x As a Standalone Server


Virtual host definitions in Tomcat 4.x have to be provided in the server configuration file, server.xml itself. Once these additions have been done, we simply need to restart Tomcat to use the virtual host definitions.



Editing server.xml


The default sample server.xml file of the Tomcat 4.x build contains two services - one for the standalone server, and one for the server that cooperates with an Apache web server using the WARP protocol. We can remove the definition for the second service and reuse that of the first service.


The top-level <Service> element for the standalone server would look like the following:





<Server port="8005" shutdown="SHUTDOWN" debug="0">
<Service name="Tomcat-Standalone">

</Service>
</Server>


The rest of the configuration would go inside this <Service> container element. We then add the Connectors to be used for this service. Since this is a standalone server, we only need the HTTP/1.1 connector to communicate with the outside world.


We add the following Connector definition inside the <Service> element.




<Connector className="org.apache.catalina.connector.http.HttpConnector"
port="8080"
minProcessors="5"
maxProcessors="75"
enableLookups="true"
acceptCount="10"
debug="0"
connectionTimeout="60000"/>


This sets up Tomcat to listen to port 8080 for incoming web requests.


We now add the <Engine> element to the <Service> by adding the following lines just after the <Connector> element and inside the <Service> element:




<Engine name="Standalone" defaultHost="europa.dom" debug="0">

</Engine>


This specifies an engine for the service that processes incoming requests from the connectors. After any request has been received by the connector and passed on to the engine, the engine would then take a look at the HTTP headers, especially the Host:tag, and determine which of the virtual host definitions that it handles would receive the request. If none of the virtual host seems to match the request headers, the engine passes on the request to a default host. The name of the default virtual host is specified in the attribute defaultHost. The value of this attribute must match a <Host> definition in the engine.


For our purposes, we see that the virtual host definition of europa.dom is served by default when a web request is made using the IP address (instead of a host name).


We next add the virtual host definition of europa.dom to the engine. We add the following content inside the <Engine> element:




<Host name="europa.dom" debug="0"
appBase="/home/sites/europa.dom/webapps"
unpackWARs="true">
</Host>



This defines a virtual host entry for europa.dom in Tomcat. We further add some logging functionality to this virtual host by placing the following content within the <Host> element:




<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="/home/sites/europa.dom/logs"
prefix="europa_access."
suffix=".log"
pattern="common" />

<Logger className="org.apache.catalina.logger.FileLogger"
directory="/home/sites/europa.dom/logs"
prefix="europa_catalina."
suffix=".log"
timestamp="true"/>


This defines two logging services for this virtual host. The <Logger> element is covered in Chapter 5.


We now add the contexts to serve for this virtual host, inside the <Host> element.




<Context path="" docBase="ROOT" debug="0"/>
<Context path="/shop" docBase="shop" debug="0" />


We have added two contexts here. The first one is the default context with an empty context path. This context either has to be defined explicitly or provided automatically by Tomcat (that is, without you defining it here) if you have a web application called ROOT in the appBase of the virtual host.


As an example of how new web applications other than the default one have to be added to the site, we have also added the web application called shop which uses the /shop context path.


One nice thing about context definitions in Tomcat 4.x is that it provides automatic contexts in case you haven't defined them in the host definition. To provide this functionality, Tomcat looks at directories inside the appBase directory. If these directories follow the web application structure, specifically if they contain a WEB-INF/web.xml file in them, Tomcat automatically provides contexts with context paths equal to the name of the directory under appBase.




Remember that the default parameters of these contexts are picked up from $CATALINA_HOME/conf/web.xml.



If you need to override some global parameters to these contexts, you need to place them within the <Context></Context> elements. Examples would be, logging for this context in a separate file, context parameters, resource definitions, and so on.


This completes the virtual host definition for europa.dom. For the virtual host callisto.dom, we add another virtual host entry similar to that of europa.dom:


We save this file as $CATALINA_HOME/conf/server.xml and restart the Tomcat service.


We first check the test JSP file in the europa.dom virtual host using the URL http://europa.dom:8080/test.jsp:






We then check out the callisto.dom using the URL http://callisto.dom:8080/test.jsp:





We then check whether the default host setting of the <Engine> element is working properly. For this we use a host name other than the ones specified explicitly as <Host> definitions. Let's try accessing using the IP address 10.0.0.1:





As we can see, Tomcat serves the contents of the europa.dom virtual host, as defined in the default virtual host entry of the engine.


Now that we have Tomcat 4.0 working as a standalone server for the virtual hosts, let's make it work along with Apache.








Tomcat 4.0 with Apache


As we saw in the Tomcat 3.3 Apache configuration section, Apache communicates with Tomcat using connectors like mod_jk. In Tomcat 4.x, support for another kind of adapter called mod_webapp has also been added. While we will concentrate on using mod_jk in this section, as it is considered to be more stable than the new mod_webapp, we will touch mod_webapp briefly at the end.


We can use the same mod_jk connector in both versions of Tomcat (it's the same at the Apache end after all). However, if you rely on Tomcat to automatically generate the mod_jk.conf file, the procedure for Tomcat 4.x is a bit different to the one in Tomcat 3.3 and will be explained shortly.



Tomcat 4.0 with the AJP 1.3 Connector


To use Tomcat along with the AJP connectors of Apache, we need to modify the previously used server.xml file.



First of all we change the name of the <Service> element to reflect the fact that Tomcat is now working along with Apache. While this is not strictly necessary, it helps in distinguishing between multiple configuration files of Tomcat lying in your machine:




[...]
<Service name="Tomcat-Apache-mod_jk">
[...]


We then replace the HTTP/1.1 <Connector> definition with the given AJP1.3 <Connector> definition instead.




The HTTP connector could be left in place if you still want Tomcat to handle web requests directly at port 8080.





<Connector className="org.apache.ajp.tomcat4.Ajp13Connector"
port="8009"
minProcessors="5"
maxProcessors="75"
acceptCount="10"
debug="0"/>


That is all we have to do to configure Tomcat to communicate with the Apache mod_jk adapter. However, to make our configuration of Apache easier, we can ask Tomcat to auto-generate the required Apache configuration files.





Auto Generating modjk.conf


To auto-generate the Apache configuration files, we have to add appropriate <ApacheConfig> listeners at various points in the server.xml file.


We start by generating the global mod_jk configuration, which is done by adding the following just after the <Service> opening element in the server.xml file:




<Listener className="org.apache.ajp.tomcat4.config.ApacheConfig"
modJk="/usr/local/apache/libexec/mod_jk.so"
jkDebug="info"
workersConfig="/usr/local/tomcat/conf/jk/workers.properties"
jkLog="/usr/local/tomcat/logs/mod_jk.log"
noRoot="false"
forwardAll="false" />


You would notice the familiar noRoot and forwardAll attributes from the <ApacheConfig> element of Tomcat 3.3. They have similar functions here:




  • The modJk attribute contains the path to the mod_jk connector module for Apache. This path would be used in the auto-generated output to inform Apache where to load the module from.





  • jkDebug attribute indicates the level of logging which could be one of debug, info, error, or emerg. Not setting this attribute would cause logging to be disabled.





  • workersConfig contains the path to the worker.properties file that needs to be mentioned in the generated file for the connector to find the workers to send the request to. jkLog contains the path to the file to be used for logging.





To generate the <VirtualHost> sections we add the following just after the opening element of all the <Host> entries.




<Listener className="org.apache.ajp.tomcat4.config.ApacheConfig"
append="true" />


The final server.xml file should look like this:




<Server port="8005" shutdown="SHUTDOWN" debug="0">
<Service name="Tomcat-Apache-modjk">
<Listener className="org.apache.ajp.tomcat4.config.ApacheConfig"
modJk="/usr/local/apache/libexec/mod_jk.so"
jkDebug="info"
workersConfig="/usr/local/tomcat/build/conf/jk/workers.properties"
jkLog="/usr/local/tomcat/logs/mod_jk.log"
noRoot="false"
forwardAll="false" />

<Connector className="org.apache.ajp.tomcat4.Ajp13Connector"
port="8009"
minProcessors="5"
maxProcessors="75"
acceptCount="10"
debug="0"/>

<Engine name="Standalone" defaultHost="europa.dom" debug="0">

<!-- Virtual host definition for europa.dom -->
<Host name="europa.dom" debug="0"
appBase="/home/sites/europa.dom/webapps"
unpackWARs="true">
<Listener className="org.apache.ajp.tomcat4.config.ApacheConfig"
append="true" />
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="/home/sites/europa.dom/logs"
prefix="europa_access."
suffix=".log"
pattern="common"/>

<Logger className="org.apache.catalina.logger.FileLogger"
directory="/home/sites/europa.dom/logs"
prefix="europa_catalina."
suffix=".log"
timestamp="true"/>
<Context path="" docBase="ROOT" debug="0"/>

<Context path="/shop" docBase="shop" debug="0" />

</Host>

<!-- Virtual host definition for callisto.dom -->
<Host name="callisto.dom"
debug="0"
appBase="/home/sites/callisto.dom/webapps"
unpackWARs="true">
<Listener className="org.apache.ajp.tomcat4.config.ApacheConfig"
append="true" />

<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="/home/sites/callisto.dom/logs"
prefix="callisto_access."
suffix=".log"
pattern="common"/>

<Logger className="org.apache.catalina.logger.FileLogger"
directory="/home/sites/callisto.dom/logs"
prefix="callisto_catalina."
suffix=".log"
timestamp="true"/>
<Context path="" docBase="ROOT" debug="0"/>

<Context path="/shop" docBase="shop" debug="0" />

</Host>
</Engine>
</Service>
</Server>


Restart Tomcat to get the auto-generated mod_jk.conf file. It will be in $CATALINA_HOME/conf/auto.


While we can use the auto-generated files directly in our Apache configuration with the Include directive, we should remember that this time Tomcat generates these files every time it starts. So any changes made to this file will be lost. In cases where you would like to make any custom changes to the virtual host definitions, for example, adding the Apache ACL to certain file locations, we should instead write this configuration file by hand. This is in contrast to Tomcat 3.3, which generates the file only when asked to do so. Of course, in Tomcat 4.0.x, once you have got the generated mod_jk.conf, you can always remove the ApacheConfig <Listener> elements from the server.xml file. This way when Tomcat restarts it won't regenerate the file.


In either case, the final mod_jk.conf file should look like the following:




<IfModule !mod_jk.c>
LoadModule jk_module /usr/local/apache/libexec/mod_jk.so
</IfModule>

JkWorkersFile "/usr/local/tomcat/build/conf/jk/workers.properties"
JkLogFile "/usr/local/tomcat/build/logs/mod_jk.log"

JkLogLevel emerg

NameVirtualHost *

<VirtualHost *>
ServerName europa.dom
DocumentRoot "/home/sites/europa.dom/webapps/ROOT"
<Directory "/home/sites/europa.dom/webapps/ROOT">
Options Indexes FollowSymLinks
DirectoryIndex index.jsp index.html index.htm
</Directory>

<Location "/WEB-INF/*">
AllowOverride None
deny from all
</Location>

<Location "/META-INF/*">
AllowOverride None
deny from all
</Location>

JkMount /servlet ajp13
JkMount /servlet/* ajp13
JkMount /*.jsp ajp13
JkMount /shop ajp13
JkMount /shop/* ajp13

Alias /shop "/home/sites/europa.dom/webapps/shop"

<Directory "/home/sites/europa.dom/webapps/shop">
Options Indexes FollowSymLinks
DirectoryIndex index.jsp index.html index.htm
</Directory>

<Location "/shop/WEB-INF/*">
AllowOverride None
deny from all
</Location>

<Location "/shop/META-INF/*">
AllowOverride None
deny from all
</Location>

JkMount /shop/servlet ajp13
JkMount /shop/servlet/* ajp13
JkMount /shop/*.jsp ajp13
</VirtualHost>

<VirtualHost *>
ServerName callisto.dom

DocumentRoot "/home/sites/callisto.dom/webapps/ROOT"
<Directory "/home/sites/callisto.dom/webapps/ROOT">
Options Indexes FollowSymLinks
DirectoryIndex index.jsp index.html index.htm
</Directory>

<Location "/WEB-INF/*">
AllowOverride None
deny from all
</Location>

<Location "/META-INF/*">
AllowOverride None
deny from all
</Location>

JkMount /servlet ajp13
JkMount /servlet/* ajp13
JkMount /*.jsp ajp13

Alias /shop "/home/sites/callisto.dom/webapps/shop"

<Directory "/home/sites/callisto.dom/webapps/shop">
Options Indexes FollowSymLinks
DirectoryIndex index.jsp index.html index.htm
</Directory>

<Location "/shop/WEB-INF/*">
AllowOverride None
deny from all
</Location>

<Location "/shop/META-INF/*">
AllowOverride None
deny from all
</Location>

JkMount /shop/servlet ajp13
JkMount /shop/servlet/* ajp13
JkMount /shop/*.jsp ajp13
</VirtualHost>


Restart Tomcat and Apache and check the test files for the virtual domains again.





Tomcat 4.0 with mod_webapp


Setting up the new Apache-Tomcat mod_webapp connector is not much different from setting up the mod_jk connector, with the important difference that Tomcat does not have a module like <ApacheConfig> for automatic generation of mod_webapp Apache configuration files. Even though Tomcat's automatic generation of mod_jk.conf is convenient, commercial shared hosting providers won't rely on it too much, because of the myriad different directives that they would like to put in the <VirtualHost> entry. They would rather have their own automated scripts to generate the mod_jk.conf files with their additions. Given that, the syntax for writing a mod_jk configuration and a mod_webapp configuration is not much different


If you want a comparison between the two connectors, see Chapter 11.




Configuring Apache

To configure Apache for mod_webapp, instead of the mod_jk-specific commands in the Apache configuration file, we add the mod_webapp directives. Instead of the LoadModule and AddModule directives of mod_jk, we add:




LoadModule webapp_module libexec/mod_webapp.so
AddModule mod_webapp.c


Also, replace the other global Jk* directives with:




WebAppConnection tomcat_warp warp localhost:8008


This defines a WARP connection with the name tomcat_warp for communicating with the Tomcat server on the same host at port 8008. We now replace all the JkMount directives with WebAppDeploy directives. WebAppDeploy directives are of the form:




WebAppDeploy <webappname> <warpconnection> <contextpath>


Thus the new Apache configuration file, which we would name as mod_webapp.conf, would look like this:




LoadModule webapp_module libexec/mod_webapp.so
AddModule mod_webapp.c

WebAppConnection tomcat_warp warp localhost:8008

NameVirtualHost *

<VirtualHost *>
ServerName europa.dom

WebAppDeploy ROOT tomcat_warp /
WebAppDeploy shop tomcat_warp /shop

DocumentRoot "/home/sites/europa.dom/webapps/ROOT"

<Directory "/home/sites/europa.dom/webapps/ROOT">
Options Indexes FollowSymLinks
DirectoryIndex index.jsp index.html index.htm
</Directory>

<Location "/WEB-INF/*">
AllowOverride None
deny from all
</Location>

<Location "/META-INF/*">
AllowOverride None
deny from all
</Location>

Alias /shop "/home/sites/europa.dom/webapps/shop"
<Directory "/home/sites/europa.dom/webapps/shop">
Options Indexes FollowSymLinks
DirectoryIndex index.jsp index.html index.htm
</Directory>

<Location "/shop/WEB-INF/*">
AllowOverride None
deny from all
</Location>

<Location "/shop/META-INF/*">
AllowOverride None
deny from all
</Location>

</VirtualHost>

<VirtualHost *>
ServerName callisto.dom

WebAppDeploy ROOT tomcat_warp /
WebAppDeploy shop tomcat_warp /shop

DocumentRoot "/home/sites/callisto.dom/webapps/ROOT"

<Directory "/home/sites/callisto.dom/webapps/ROOT">
Options Indexes FollowSymLinks
DirectoryIndex index.jsp index.html index.htm
</Directory>

<Location "/WEB-INF/*">
AllowOverride None
deny from all
</Location>

<Location "/META-INF/*">
AllowOverride None
deny from all
</Location>

Alias /shop "/home/sites/callisto.dom/webapps/shop"

<Directory "/home/sites/callisto.dom/webapps/shop">
Options Indexes FollowSymLinks
DirectoryIndex index.jsp index.html index.htm
</Directory>

<Location "/shop/WEB-INF/*">
AllowOverride None
deny from all
</Location>

<Location "/shop/META-INF/*">
AllowOverride None
deny from all
</Location>

</VirtualHost>


Note how the default context is sent to the ROOT webapp. Another point to be noted is that in this configuration, Tomcat would serve even the static files of the web application. Also, when Tomcat serves the default context of the virtual host through WARP protocol, the <Directory> directives are ignored, and hence the <Directory> directives given above are strictly redundant. However, the <Location> directives are still processed by Apache. These are in a way redundant too. Without these <Location> directives, any requests of files in META-INF and WEB-INF directories would be passed by mod_webapp to Tomcat, which according to the Servlet API specification would reject such requests. Having the <Location> ACL directives in the Apache configuration actually stops such requests before they are passed to Tomcat.


This configuration file should be added to the Apache configuration using the Include directive, replacing the previous mod_jk configuration.





Configuring Tomcat 4.x

For configuring Tomcat to receive requests only through the WARP protocol of mod_webapp, we need to modify the server.xml file. In the last used server.xml, we have to replace the AJP <Connector> elements with WARP <Connector> ones. Also, since <ApacheConfig> can't handle WARP-based configurations, we remove all the <Listener> elements previously used to auto-generate Apache configuration files. The final server.xml file would look like this.




<Server port="8005" shutdown="SHUTDOWN" debug="0">
<Service name="Tomcat-Apache-warp">

<Connector className="org.apache.catalina.connector.warp.WarpConnector"
port="8008" minProcessors="5" maxProcessors="75"
acceptCount="10" debug="0"/>

<Engine className="org.apache.catalina.connector.warp.WarpEngine"
name="Apache" defaultHost="europa.dom" debug="0">

<!-- Virtual host definition for europa.dom -->
<Host name="europa.dom" debug="0"
appBase="/home/sites/europa.dom/webapps"
unpackWARs="true">
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="/home/sites/europa.dom/logs"
prefix="europa_access."
suffix=".log"
pattern="common"/>

<Logger className="org.apache.catalina.logger.FileLogger"
directory="/home/sites/europa.dom/logs"
prefix="europa_catalina."
suffix=".log"
timestamp="true"/>
<Context path="" docBase="ROOT" debug="0"/>

<Context path="/shop" docBase="shop" debug="0" />

</Host>

<!-- Virtual host definition for callisto.dom -->
<Host name="callisto.dom" debug="0"
appBase="/home/sites/callisto.dom/webapps"
unpackWARs="true">

<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="/home/sites/callisto.dom/logs"
prefix="callisto_access."
suffix=".log"
pattern="common"/>

<Logger className="org.apache.catalina.logger.FileLogger"
directory="/home/sites/callisto.dom/logs"
prefix="callisto_catalina."
suffix=".log"
timestamp="true"/>

<Context path="" docBase="ROOT" debug="0"/>

<Context path="/shop" docBase="shop" debug="0" />

</Host>
</Engine>
</Service>
</Server>


We should now restart Apache and Tomcat and check the functioning of the two virtual hosts.























No comments:

Post a Comment