Monday, August 22, 2011

Configuring a Web Application in Tomcat

Suppose you have a web application configured for Tomcat using a context configuration file (META-INF/context.xml), as I mentioned in this post. You read Tomcat's documentation saying (not exactly in these words though):
If your .war file contains META-INF/context.xml it will be copied into $CATALINA_HOME/conf/[enginename]/[hostname]/[appname].xml and if there is such a file it won't be overwritten.

You happily created .war packages and shipped to the production, just to start receiving complaints about context configuration being overwritten. This is when you go and read another part of Tomcat's documentation saying:
An update to a WAR which has been expanded will trigger an undeploy (with a removal of the expanded webapp), followed by a deployment.

If we combine these two together, we can conclude that: If you install an application via copying a .war file into the webapp folder, then you are gonna end up losing changes made to the context configuration. There is a bug report associated with this problem, which is marked as WON'T FIX by Tomcat development team. So, if you still insist not to use Application Server's administrative interface, keep reading for a possible fix.

What I have come up with is this:

1- Install an exploded WAR directory somewhere outside Tomcat's appBase, let's assume it is in /usr/local/MyApp. 2- Copy the context configuration file into [tomcat.conf]/[engine]/[hostname] directory, let's call it MyApp.xml. This file will point to the location of the application:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Context configuration file for my web application -->
<Context docBase="/usr/local/MyApp" privileged="true" antiResourceLocking="false" antiJARLocking="false">
<Resource name="jdbc/myapp-ds" auth="Container" type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000" username="XXX" password="XXX" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/mydb" />
</Context>


3- You are now free to go and modify the configuration file.

4- Update the application by copying new version of your application in /usr/local/MyApp

Notes:

a) This solution applies to an unexpanded .war file, but since we use Spring's Log4JConfigListener it wouldn't run from an unexploded .war file. Tomcat doesn't explode .war files put outside appBase (webapps) folder.

b) This approach doesn't prevent you from having context.xml in /usr/local/MyApp/META-INF/context.xml since it will not be used by Tomcat in this configuration. You can use it in your dev environment, where you dump your .war file into the appBase (webapps) folder.

I am still looking out for simpler solutions, though.

Thursday, August 18, 2011

A simple Vaadin application for Google App Engine

Here is a very simple application written in Vaadin that can be installed on Google App Engine. This application uses the users API to log users in and out. It also saves users last login time in the database as an example on how to connect to database. It is built by Maven and separated in two different modules, Java codes and GAE stuff.

Browse the code here, or check it out on your machine using this command:
svn co http://tinywebgears-samples.googlecode.com/svn/trunk/simplegae simplegae

These are the steps to get it working on your own box, as outlined in the README file:
  1. Run install-gae-sdk.sh in order to install appengine-local-runtime (not included in Maven repository).
  2. Run mvn clean install to compile the application.
  3. Run run-gae.sh to run the application locally.
  4. Create a new AppEngine application (here).
  5. Modify ./simplegae-appengine/src/main/webapp/WEB-INF/appengine-web.xml and put in name and version number of your application.
  6. Run deploy-gae.sh to deploy the application.
The provided scripts shouldn't put Windows users off, since they only run a sequence of simple commands.

One of the biggest impacts of moving to App Engine on your Vaadin application is the fact that your application will end up being serialized/deserialized between requests. As a prerequisite, all your classes must be Serializable. Unsupported features (like polymorphic queries) and some specific requirements (transactions and entity groups) should also be considered.