2009
11.04

Setting up Apache Tomcat for Upgrades

Plan for Change.

Note: This article is based on Apache Tomcat 6.0.20

If you set up a lot of Tomcat sandboxes, here is a sample structure to make it easy to upgrade to a new version of Tomcat without having to reinstall all applications. The trick is to have a number of Tomcat sandboxes pointing to a symbolic link, which in turn points to the latest version of Tomcat.

Download Tomcat

Get latest version of Tomcat from Apache. Check signature. Decompress. Untar.

> cd ...
> tar -xvf apache-tomcat-6.X.X.tar

Now create a symbolic link to this latest version

> ln -s apache-tomcat-6.X.X apache-tomcat-latest

Create a sandbox

A sandbox refers to a directory where you are testing a web application. Portions of the sandbox are linked back to the latest version of Tomcat. Other portions of the sandbox are original to it.

> mkdir tomcat.sandbox
> cd tomcat.sandbox
> ln -s ../apache-tomcat-latest/bin .
> ln -s ../apache-tomcat-latest/lib .
> ln -s ../apache-tomcat-latest/conf .
> mkdir work
> mkdir logs
> mkdir webapps

To add a WAR file to Tomcat, simply copy it to the tomcat.sandbox/webapps directory. It will be auto detected when Tomcat starts and deployed.

Finally, start Tomcat.

> cd bin
> ./catalina.sh run

Obviously, there is more to configuring and running Tomcat. You can get more information by starting at the Tomcat Homepage.

Upgrading

When a new version of Tomcat becomes available, repeat the first step. Download, verify, decompress and untar the latest version, alongside the first version. Then, change the symbolic link of latest Tomcat to the new one.

> cd ...
> tar -xvf apache-tomcat-6.Y.Y.tar
> rm apache-tomcat-latest
> ln -s apache-tomcat-6.Y.Y apache-tomcat-latest

Now, all sandboxes are pointing at the latest libraries. Restart all sandboxes.

Unless major changes have occurred in configuration files, this trick should let you upgrade painlessly.

2009
11.04

Setting up Jetty for Upgrades

Plan for change.

Note: this article refers to Jetty-7

If you set up a lot of Jetty sandboxes, here is a sample structure to make it easy to upgrade to a new version of Jetty without having to reinstall all applications. The trick is to have a number of Jetty sandboxes pointing to a symbolic link, which in turn points to the latest version of Jetty.

Download Jetty

Get latest version of Jetty from http://www.eclipse.org/jetty/downloads.php Check signature. Decompress. Untar.

> cd ...
> tar -xvf jetty-distribution-7.X.X

Now create a symbolic link to this latest version

> ln -s jetty-distribution-7.X.X jetty-7-latest
> chmod a+x jetty-7-latest/bin/*.sh

Create a sandbox

A sandbox refers to a directory where you are testing a web application. Portions of the sandbox are linked back to the latest version of Jetty. Other portions are copied from the latest Jetty. Finally, some portions of the sandbox are original to it.

> mkdir jetty.sandbox
> cd jetty.sandbox
> ln -s ../jetty-7-latest/bin .
> ln -s ../jetty-7-latest/lib .
> ln -s ../jetty-7-latest/start.ini .
> ln -s ../jetty-7-latest/start.jar .
> cp -rp ../jetty-7-latest/etc .
> cp -rp ../jetty-7-latest/resources .
> mkdir contexts
> mkdir logs
> mkdir webapps

At this point, we must configure the sandbox. There are two steps to adding a WAR file:

  1. Copy WAR file to jetty.sandbox/webapps
  2. Create a context file (.xml) located in jetty.sandbox/contexts to enable application. Look at jetty-7-latest/contexts for inspiration.

Finally, start jetty.

> bin/jetty.sh run

Obviously, there is more to configuring and running Jetty. You can get more information by starting at the Jetty Homepage.

Upgrading

When a new version of Jetty becomes available, repeat the first step. Download, verify, decompress and untar the latest version, alongside the first version. Then, change the symbolic link of latest Jetty distribution to the new one.

> cd ...
> tar -xvf jetty-distribution-7.Y.Y
> rm jetty-7-latest
> ln -s jetty-distribution-7.Y.Y jetty-7-latest
> chmod a+x jetty-7-latest/bin/*.sh

Now, all sandboxes are pointing at the latest libraries. Restart all sandboxes.

Unless major changes have occurred in configuration files, this trick should let you upgrade painlessly.

2009
10.31

GWT Module using Maven

Note, this discussion is based on:

  • GWT libraries 1.6.4
  • Apache Maven 2.2.1

I like Maven because it makes me much more productive. It takes care of a lot a project management details and makes the code saved in my repository smaller. When it comes to creating GWT modules (reusable libraries), I was hoping that I could use Maven to keep track of dependencies between applications and modules, as well as dependencies between modules. This discussion presents what is needed to make a GWT module in a Maven project.

There are two important points to remember when making a GWT module using Maven:

  1. The produced JAR file must contain a module file (*.gwt.xml)
  2. The produced JAR file must contain the source files needed in the module. This is because these source files must be compiled in the context of the application.

Maven Project

The first step is to set up a normal Maven project to produce a JAR file artifact.

> mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=<group-id> -DartifactId=<artifact-id>

This command will generate two java files called App.java and AppTest.java. You can delete them.

In an example:

> mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=gwt -DartifactId=gwt-module0
> cd gwt-module0
> rm src/main/java/gwt/App.java
> rm src/test/java/gwt/AppTest.java
> find .
.
./src
./src/main
./src/main/java
./src/main/java/gwt
./src/test
./src/test/java
./src/test/java/gwt
./pom.xml

Modules must be compiled using Java 6, therefore the pom.xml file must be modified accordingly. Edit the pom.xml file

> vi pom.xml

and add the following content at the appropriate place:

<build>
   <plugins>
      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
         <configuration>
            <source>1.6</source>
            <target>1.6</target>
         </configuration>
      </plugin>
   </plugins>
</build>

Module File

A module file is required. In Maven, this file should be in the resource directory. Create a module file in the usual resources directory with an appropriate name and an extension of .gwt.xml

> mkdir -p src/main/resources
> vi src/main/resources/Module0.gwt.xml

Use the following content as a starting point for the module file:

<!DOCTYPE module PUBLIC "//gwt-module/" "http://google-web-toolkit.googlecode.com/svn/tags/1.6.2/distro-source/core/src/gwt-module.dtd">
<module>
</module>

There is nothing else required in the module file. Note that the name of the module is “Module0″, in the global name space. If one wants to use a longer name space such as “com.example.Module0″, then the full modulfe file name would be: “src/main/resources/com/example/Module0.gwt.xml”.

Since the Java source files for the module are to be located in a subdirectory of the module other than the standard “client” subdirectory (in this case, the name of the directory is “gwt”), one must add a “source” directive to the module file, as such:

<!DOCTYPE module PUBLIC "//gwt-module/" "http://google-web-toolkit.googlecode.com/svn/tags/1.6.2/distro-source/core/src/gwt-module.dtd">
<module>
   <source path="command"/>
</module>

Include Source Files in JAR artifact

As stated above, source files must be added to the JAR artifact. Therefore, the pom.xml file must be augmented to include the source files as well as the standard resources directory. Edit pom.xml and add the following:

<build>
   <resources>
      <resource>
         <directory>src/main/java</directory>
         <includes>
            <include>gwt/**</include>
         </includes>
      </resource>
      <resource>
         <directory>src/main/resources</directory>
      </resource>
   </resources>
</build>

Test JAR generation

For example, create a Bean class to test:

> vi src/main/java/gwt/MyBean.java

with the following content:

package gwt;

import java.io.Serializable;

@SuppressWarnings("serial")
public class MyBean implements Serializable {

   private String id;

   public String getId() {
      return id;
   }
   public void setId(String id) {
      this.id = id;
   }
}

Now, generate artifact:

> mvn install

You can verify the content of your JAR artifact:

> jar -tf target/gwt-module0-1.0-SNAPSHOT.jar
META-INF/
META-INF/MANIFEST.MF
gwt/
gwt/MyBean.java
gwt/MyBean.class
Module0.gwt.xml
META-INF/maven/
META-INF/maven/gwt/
META-INF/maven/gwt/gwt-module0/
META-INF/maven/gwt/gwt-module0/pom.xml
META-INF/maven/gwt/gwt-module0/pom.properties

Note that the module file, the source and compiled version of the class are included.

Include Module in an application

To include the new module in an application or another module, two steps are required:

  1. Add a reference to the module file in the upstream application/module.
  2. Add the module JAR as a dependency of the upstream application/module.

Edit the GWT module file (.gwt.xml) in the upstream application/module and add the following directive:

<inherits name='Module0'/>

Do not forget to put the fully qualified name, including the name space, if required.

If the upstream application/module uses Maven, then adding the module as a dependency is simple. Edit the pom.xml file in the upstream project and add the JAR artifact produced by the module:

</dependencies>
   <dependency>
      <groupId>gwt</groupId>
      <artifactId>gwt-module0</artifactId>
      <version>1.0-SNAPSHOT</version>
   </dependency>
</dependencies>

Create a module with GWT Widgets

In the module, if one wishes to create GWT widgets, then one must include the core Web toolkit module from GWT. Two steps are required:

  1. Add dependency in the module file
  2. Add dependency in the Maven pom.xml file.

Edit the module file

> vi src/main/resources/Module0.gwt.xml

and add the following directive:

<inherits name='com.google.gwt.user.User'/>

Then, edit the pom.xml file

> vi pom.xml

and add the dependency

<dependencies>
   <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-user</artifactId>
      <version>1.6.4</version>
      <scope>provided</scope>
   </dependency>
</dependencies>
2009
10.30

Rocky Road Ahead -> Defensive Programming Required!

You have been warned.

Old programmers tend to revert to printf() when it is time to find where the bugs are. It is much nicer to have a debugger handy. However, while trying our first Google Wave Robot, I longed for a better comprehension of the events passed to the robot and started printing the received events directly in the wave. This is when I found out that minor changes, that looked otherwise inconspicuous, would greatly affect the robot’s behaviour. In fact, small changes would stop the robot altogether.

As it turns out, there exist currently calls to the Google Wave Java API that throws exceptions, although the signatures do not report any. If an exception is raised in the printing while trying to debug a piece of code, then the robot appears unresponsive. For more specifics, look at bugs:

Therefore, I propose here a set of functions to print an instance of RobotMessageBundle received in the robot processEvents() call. NOTE: be careful in using these functions only in situations where receiving new submitted blips are not the trigger of submitting newer blips. This would be an unwanted recursive situation.

The following example is safe since the debug print happens only on WAVELET_SELF_ADDED events:

   Wavelet wavelet = bundle.getWavelet();
   if( bundle.wasSelfAdded() ) {
      Blip blip = wavelet.appendBlip();
      TextView textView = blip.getDocument();
      textView.delete();

      printBundle(bundle,textView,null);
   }

Here are the printing/debugging functions:

   public void printBundle(RobotMessageBundle bundle, TextView printTextView, String prefix) {
      if( null == prefix ) {
         prefix = "";
      }

      // is new wave
      try {
         printTextView.append(prefix+"bundle.isNewWave() "+bundle.isNewWave()+" \n ");
      } catch(Exception e) {
         printTextView.append(prefix+"bundle.isNewWave() "+exceptionToString(e)+" \n ");
      }

      // was self added
      try {
         printTextView.append(prefix+"bundle.wasSelfAdded() "+bundle.wasSelfAdded()+" \n ");
      } catch(Exception e) {
         printTextView.append(prefix+"bundle.wasSelfAdded() "+exceptionToString(e)+" \n ");
      }

      // was self removed
      try {
         printTextView.append(prefix+"bundle.wasSelfRemoved() "+bundle.wasSelfRemoved()+" \n ");
      } catch(Exception e) {
         printTextView.append(prefix+"bundle.wasSelfRemoved() "+exceptionToString(e)+" \n ");
      }

      // robot address
      try {
         printTextView.append(prefix+"bundle.getRobotAddress() "+bundle.getRobotAddress()+" \n ");
      } catch(Exception e) {
         printTextView.append(prefix+"bundle.getRobotAddress() "+exceptionToString(e)+" \n ");
      }

      // Events
      try {
         List events = bundle.getEvents();
         printTextView.append(prefix+"bundle.getEvents() "+((events==null)?"null":"list of "+events.size()+" events")+" \n ");
         if( null != events ) {
            for(Event event : events) {
               printBundleEvent(event, printTextView, prefix+"   ");
            }
         }
      } catch(Exception e) {
         printTextView.append(prefix+"*** Error on printing event: "+e.getMessage()+" \n ");
      }
   }

   public void printBundleEvent(Event event, TextView printTextView, String prefix) {
      if( null == prefix ) {
         prefix = "";
      }

      // Event type
      try {
         EventType eventType = event.getType();
         printTextView.append(prefix+"Event.getType() "+((eventType==null)?"null":eventType.name())+" \n ");
      } catch(Exception e) {
         printTextView.append(prefix+"Event.getType() "+exceptionToString(e)+" \n ");
      }

      // button name
      try {
         printTextView.append(prefix+"Event.getButtonName() "+event.getButtonName()+" \n ");
      } catch(Exception e) {
         printTextView.append(prefix+"Event.getButtonName() "+exceptionToString(e)+" \n ");
      }

      // changed title
      try {
         printTextView.append(prefix+"Event.getChangedTitle() "+event.getChangedTitle()+" \n ");
      } catch(Exception e) {
         printTextView.append(prefix+"Event.getChangedTitle() "+exceptionToString(e)+" \n ");
      }

      // changed title
      try {
         printTextView.append(prefix+"Event.getCreatedBlipId() "+event.getCreatedBlipId()+" \n ");
      } catch(Exception e) {
         printTextView.append(prefix+"Event.getCreatedBlipId() "+exceptionToString(e)+" \n ");
      }

      // removed blip id
      try {
         printTextView.append(prefix+"Event.getRemovedBlipId() "+event.getRemovedBlipId()+" \n ");
      } catch(Exception e) {
         printTextView.append(prefix+"Event.getRemovedBlipId() "+exceptionToString(e)+" \n ");
      }

      // modified by
      try {
         printTextView.append(prefix+"Event.getModifiedBy() "+event.getModifiedBy()+" \n ");
      } catch(Exception e) {
         printTextView.append(prefix+"Event.getModifiedBy() "+exceptionToString(e)+" \n ");
      }

      // changed version
      try {
         printTextView.append(prefix+"Event.getChangedVersion() "+event.getChangedVersion()+" \n ");
      } catch(Exception e) {
         printTextView.append(prefix+"Event.getChangedVersion() "+exceptionToString(e)+" \n ");
      }

      // time stamp
      try {
         printTextView.append(prefix+"Event.getTimestamp() "+event.getTimestamp()+" \n ");
      } catch(Exception e) {
         printTextView.append(prefix+"Event.getTimestamp() "+exceptionToString(e)+" \n ");
      }

      // blip
      try {
         printTextView.append(prefix+"Event.getBlip() "+event.getBlip()+" \n ");
      } catch(Exception e) {
         printTextView.append(prefix+"Event.getBlip() "+exceptionToString(e)+" \n ");
      }

      // Added participants
      try {
         Collection participants = event.getAddedParticipants();
         printTextView.append(prefix+"Event.getAddedParticipants() "+
               ((participants==null)?"null":"collection of "+participants.size()+" strings")+" \n ");
         if( null != participants ) {
            for(String participant : participants) {
               printTextView.append(prefix+"   "+participant+" \n ");
            }
         }
      } catch(Exception e) {
         printTextView.append(prefix+"Event.getAddedParticipants() "+exceptionToString(e)+" \n ");
      }

      // Removed participants
      try {
         Collection participants = event.getRemovedParticipants();
         printTextView.append(prefix+"Event.getRemovedParticipants() "+
            ((participants==null)?"null":"collection of "+participants.size()+" strings")+" \n ");
         if( null != participants ) {
            for(String participant : participants) {
               printTextView.append(prefix+"   "+participant+" \n ");
            }
         }
      } catch(Exception e) {
         printTextView.append(prefix+"Event.getRemovedParticipants() "+exceptionToString(e)+" \n ");
      }
   }

   public String exceptionToString(Exception e) {
      return "ERROR:"+e.getClass().getName()+"("+e.getMessage()+")";
   }
2009
10.25

Install VirtualBox on Ubuntu

VirtualBox is by far the best virtual machine software for Ubuntu. Here is a simple recipe to install it on Karmic.

> sudo bash -c 'echo "deb http://download.virtualbox.org/virtualbox/debian $0 non-free" > /etc/apt/sources.list.d/virtualbox.list' `lsb_release -cs`
> wget -q http://download.virtualbox.org/virtualbox/debian/sun_vbox.asc -O- | sudo apt-key add -
> sudo apt-get install virtualbox-3.0