Wednesday, 11 August 2010

XPath Expression Testbed

I've just been writing some code to parse an XML file using XPath and came across this very useful web page at www.whitebeam.org.

XPath Expression Testbed

It allows you to upload a sample of the XML file you are trying to parse and experiment with XPath expressions. Much easier than trying things in Java code, recompiling and running. There is also a good XPath tutorial linked on the testbed page.

Very handy!

Tuesday, 3 August 2010

Java and Gnome

I don't often write GUI applications but I have been experimenting over the last few days with the Java-Gnome User Interface Library for writing Gnome-based applications in Java.

The disadvantage of this of course is that you forego the "write-once-run-anywhere" ethos behind Java; the finished application will only run on a system that uses Gnome as its window manager. The standard, cross-platform way of doing things is to use Swing but the "native" look & feel widgets are only imitations of the real thing and, Windows aside, can look like poor imitations.

Compare these screenshots for example, of a colour chooser dialog in Swing on a Gnome desktop (Ubuntu 10.04) and a native Gnome application:

I suppose the best compromise would be to use the Standard Widget Toolkit (SWT), as used by the Eclipse platform, which is a standard interface underpinned by native windowing system widgets.

The Java Gnome User Interface Library

Subject to the above caveats, my first impressions of the Java-Gnome UI Library is very good. I don't think all of GTK is covered as yet, but there is certainly enough there to write perfectly serviceable applications for Gnome desktops. As an exercise, I managed to knock together a perfectly serviceable text editor in a couple of days, complete with syntax highlighting, cut/copy/paste, undo/redo, open/save dialogs and shortcut keys.

I found the code quite simple to write and was up and running quite quickly. Creating things like menus and toolbars is made very easy with the concept of actions, and standard framework actions (stock actions) keep things consistent. Handling events is straightforward when you have written a couple of event handlers. The trickiest part was getting round a few subtleties of the undo/redo mechanism, which kept crashing the application because I didn't understand it properly. But rock solid now.

As with any GUI framework, code can get into a mess very quickly, so some organisation is essential. The approach I used on the editor exercise was to make the main application window a subclass of the Window class, set it up as a singleton and have it create the other components it needed in its constructor. Any code in the components can then look up the singleton instance and access methods and data in the other components in a controlled way.

There is an excellent tutorial available to get started and the API documentation is very good.

Well worth a try if you want to write some applications for the Gnome desktop in Java.

Thursday, 29 July 2010

Maven Web Projects and Eclipse

Apart from dependency management, perhaps the biggest benefit I get from using Maven is being able to distribute sample code to people from my training courses and not worry too much about which IDE they are using. Because Maven separates the build process from the IDE, it makes it reasonably straightforward to open Maven projects created in other IDEs.

NetBeans is my IDE of choice and Maven support is bundled with the IDE from 6.7 onwards. There is no setup to do in this case (apart from perhaps pointing Netbeans at a local installation of Maven rather than the built-in version). Simply open the project and Netbeans will identify it as a Maven project by the presence of a pom.xml file.

With Eclipse, Maven support is not included in the base IDE. Other offerings, such as the excellent SpringSource Tool Suite, do include it. Mainly For the benefit of people on whom I inflict my demo projects and examples, here is a quick run-down on getting started with Maven and Eclipse.

Setting up Eclipse for Maven Projects

I'm assuming that you have installed your base plugins for whatever type of Java development you intend to do with Maven: the Web Tools Project (WTP) plugins if you are going to do some web development for example.

To add the required plugins to Eclipse, you need to go to and add the following update sites:

  • http://m2eclipse.sonatype.org/sites/m2e
  • http://m2eclipse.sonatype.org/sites/m2e-extras

The names don't matter of course. "M2Eclipse Plugin" and "M2Eclipse Plugin Extras" are as good as any others.

For the M2Eclipse Plugin, there is only one component to select. Make sure you accept any licence agreements and certificates during the installation. This will give you basic Maven support for building Java (non-Web) projects. It's a common mistake to either forget or not be aware of the "extras" plugin but if you try and open a web project you'll end up with a complete mess in the Project Explorer because Eclipse doesn't understand the Maven web project layout.

For the M2Eclipse Plugin Extras, there are more choices and you probably don't need them all. In fact the only ones I tend to use are the Maven Integration for WTP and Maven SCM Integration. The latter is only required if you want to check out Maven projects from a version control system like Subversion (in which case you'll need the relevant Eclipse plugins to support that directly, as well as the Maven SCM Integration).

And that should be it. Open up the Maven project and Eclipse should show that it has recognised it with an "M" in the top left corder of the project icon in the Project Explorer.

In Eclipse, Maven goals are invoked using the Run menu or toolbar icon. To clean the project for example, right click on the project node, select "Run As", then "Maven Clean". To build the project and deploy the resulting JAR or WAR file to your local Maven repository, select "Run As", "Maven Install". To run a web application on an Eclipse-controlled application server, just choose "Run As", "Run on server" as usual.

Thursday, 3 June 2010

Continuous Integration

I've been spending some time this week looking into setting up a continuous integration server for some of my training and personal projects. Although these projects are not developed by a team, which would be where continuous integration would really become useful, I was finding that I was spending quite a bit of time just keeping projects up and running following changes to upstream dependencies.

There are plenty of choices out there for continuous integration and related tools. My setup is now based on:

Certainly the result is working well. No more scuttling around checking that example software builds before I go off to teach a course. I can be pretty certain now that I just need to check the latest versions of my projects out of Subversion and that I haven't broken anything.

Apart from my desktop machine, which is my main development box, and my laptop, which I take on courses, I'm running the core tools on a very modest server: dual Celeron 500MHz with only 768M memory. I've had to do some performance tuning with the JVMs to reduce the overall memory footprint (my poor server was working hard yesterday and the swap partition was getting hammered), especially as the server also runs Apache, Tomcat and acts as a RAID 5 fileserver.

I'll be making some articles over the coming weeks on experiences I've had with this setup and the benefits I'm seeing from these tools.

Saturday, 29 May 2010

Oracle Sun JDK vs OpenJDK

These days Ubuntu comes with Open JDK installed as standard. Many Java programmers replace this immediately with the Oracle Sun JDK.

So what's the difference? Well, the OpenJDK is a 100% open-source implementation of the Java language specification. The Oracle Sun JDK is largely open-source but still contains some precompiled binaries that Sun didn't have copyright to release under an open-source license.

You can read more here:

Oracle - Free and Open Source Java

I've always tended to replace the OpenJDK with Sun Java, but since installing Ubuntu 10.04 Lucid Lynx, I've been seeing how I get on with the OpenJDK. To date, developing straightforward Java, JSP and servlets with Spring, Struts2, Hibernate and Maven deployed on Tomcat, has worked perfectly well. I've also been using the IcedTea plugin in Firefox with no issues.

But yesterday, I hit my first snag. If you read my previous post you'll know that I've been looking into the new annotations in the Servlet 3.0 specification: @WebServlet, @WebFilter and @WebListener. To get examples to run I had to install Glassfish v3. All was well until I tried to run the Admin Console and I got this error:

java.lang.UnsupportedOperationException: Cannot create XMLStreamReader or XMLEventReader from a
javax.xml.transform.stream.StreamSource
        at com.sun.xml.internal.stream.XMLInputFactoryImpl.jaxpSourcetoXMLInputSource(XMLInputFactoryImpl.java:283)
        at com.sun.xml.internal.stream.XMLInputFactoryImpl.createXMLStreamReader(XMLInputFactoryImpl.java:143)
        at org.jvnet.hk2.config.ConfigParser.parse(ConfigParser.java:109)
        at org.jvnet.hk2.config.ConfigParser.parse(ConfigParser.java:104)
        at org.jvnet.hk2.config.ConfigParser.parse(ConfigParser.java:100)
        at org.glassfish.admingui.plugin.ConsolePluginService.init(ConsolePluginService.java:121)
        at org.glassfish.admingui.plugin.ConsolePluginService.getIntegrationPoints(ConsolePluginService.java:423)
        at org.glassfish.admingui.common.handlers.PluginHandlers.getIntegrationPoints(PluginHandlers.java:160)
        at org.glassfish.admingui.handlers.ThemeHandlers.getThemeFromIntegrationPoints(ThemeHandlers.java:98)
        ... 47 more

That didn't worry me too much at the time, but I was also getting some odd behaviour with Java Server Faces (JSF) applications that I hadn't seen on Tomcat. Most of the time I'm able to switch freely between one and the other with web applications.

So, based on a forum post about the problems with the Glassfish admin console, I installed all the Sun Java 6 components. My problems with the admin console disappeared and my JSF application started behaving normally again. So, on that basis, there seem to be glitches with JSF using the current version of the OpenJDK. The Glassfish admin console is based on JSF.

If you need to replace your OpenJDK installation on Ubuntu 10.04, these are the steps:

  1. Enable the partner repositories: System -> Administration -> Software Sources and check the box for http://archive.canonical.com/ubuntu lucid partner in the Other Sources tab. You might also want the sources checked too.
  2. Install the Sun Java6 packages: sun-java6-jdk, sun-java6-jre, sun-java6-source, sun-java6-fonts, sun-java6-bin and possibly sun-java6-plugin.

You'll then need to switch your default Java to the Oracle Sun version:

$ sudo update-java-alternatives -v -s java-6-sun

Do also check that you haven't hard-coded OpenJDK into any JAVA_HOME variables or application settings.

Thursday, 27 May 2010

Servlet 3.0 Annotations

The specification for JSR315: Servlet 3.0 was released in December last year with some interesting new ideas and features. This is still very new and the application servers haven't quite caught up with it yet. At the time of writing, the only server I know of that supports Servlet 3.0 is Glassfish v3. Tomcat 7 will provide support but is apparently not due for release until toward the end of this year.

In this post, I want to take a brief look at the new annotations in Servlet 3.0, and show how, although not ground-breaking, they will reduce the amount of configuration in web.xml.

To run any of these examples, you will need a copy of Glassfish 3.0 or, if you are feeling adventurous, you could try Tomcat 7.0 RC2.

Getting hold of the Servlet 3.0 API

Assuming you are using Maven, you'll need to find yourself a repository artifact that corresponds to the new specification. The central repository now has the required artifacts, so no need to mess around adding repositories:

pom.xml
<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>6.0</version>
</dependency>

Note you may also find an artifact called javaee-web-api. As far as I can tell from a cursory glance, this contains the same classes as javaee-api.

Servlet with Annotations

Servlet 3.0 provides the @WebServlet annotation to define a servlet, obviating the need for configuration in web.xml.

SampleServlet.java
package blogger;

import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(value="/sampleServlet", loadOnStartup=1)
public class SampleServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        response.setContentType("text/plain");
        response.getWriter().write(new Date().toString());
    }

}

Simply create the class, deploy the project and invoke the servlet using the path specified in the annotation, e.g. http://localhost:8080/<contextPath>/sampleServlet. Note that things like load on startup can be specified for servlets using the annotation.

Listener with Annotations

Listeners can be defined in much the same way. Again, no configuration is required in web.xml.

SampleListener.java
package blogger;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@WebListener
public class SampleListener implements HttpSessionListener {

    private static final Logger log = LoggerFactory.getLogger(SampleListener.class);

    @Override
    public void sessionCreated(HttpSessionEvent arg0) {
        log.info("Session created");
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent arg0) {
        log.info("Session destroyed");
    }

}

I've used SLF4J for logging here, which is becoming more popular as a logging framework wrapper, used by Spring 3.0 for example. To make this work, you'll need to add the following to your pom.xml. Note there's no need to add an artifact for Log4J.

pom.xml
...

<properties>
    <slf4j.version>1.5.8</slf4j.version>
</properties>

...

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>${slf4j.version}</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>${slf4j.version}</version>
</dependency>

To configure the underlying Log4J, you'll need something like this copied out to your classpath:

log4j.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//LOGGER" "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <param name="target" value="system.out"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p %d{ABSOLUTE} %c{1} - %m%n"/>
        </layout>
    </appender>

    <root>
        <priority value="info"/>
        <appender-ref ref="console" />
    </root>

</log4j:configuration>

Filter with Annotations

Same idea again here. No configuration in web.xml.

SampleFilter
package blogger;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@WebFilter("/sampleServlet")
public class SampleFilter implements Filter {

    Logger log = LoggerFactory.getLogger(SampleFilter.class);

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {
        chain.doFilter(request, response);
        if (response instanceof HttpServletResponse) {
            log.info("Applying cache control filter to response");
            HttpServletResponse httpServletResponse = (HttpServletResponse)response;
            httpServletResponse.setHeader("Cache-Control", "nocache");
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void destroy() {}

}

In this case, the parameter in the @WebFilter annotation specifies the paths to which the filter is applied. In my case, just to the sample servlet above.

One of the limitations of the annotations for filters is that there is no way of specifying the ordering. If multiple filters were in use, and the ordering was important, you'd have to revert to a schema-based configuration in web.xml.

Web Deployment Descriptor

None of the above examples need a web.xml file. By default, the welcome file list includes index.html and index.jsp. If you need to configure things like session timeouts, prelude/coda, etc., you will need a web.xml file. This is an example of a basic Servlet 3.0 version:

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
        version="3.0">
    <session-config>
        <session-timeout>15</session-timeout>
    </session-config>
</web-app>

Settings in web.xml, by the way, can also be used to override the settings provided by the annotations. The web.xml takes precedence. You can also set metadata-complete to true in web.xml to tell the container only to consider configuration in web.xml and skip annotations altogether.

Conclusion

So, there you have it. Servlet 3.0 annotations. As I said in the introduction, they aren't ground-breaking in terms of setting up your application, but they do make it slightly easier to configure servlets, filters and listeners in your web applications. I suspect 99% of the time, we will still need a web.xml file, but it could be less cluttered.

Monday, 3 May 2010

In Praise of Twitter

To be honest, I always thought Twitter was a way for teenagers to amuse themselves by inflicting incomprehensible text-speak globally, instead of just upon their friends ... lol

But with the new release of Ubuntu incorporating the "social networking" application Gwibber, I thought it time to see what the fuss is all about.

Social networking aside, I have to say, it's a great way for people working in technology to keep tabs on what's going on. We all know how quickly things move and how it's often difficult to find time to keep up to date with websites like SpringSource and Hibernate. Well Twitter's text-speak-sized "tweets" are nice little bite-sized chunks of information and, more importantly, are regularly spat out in the name of publicity by the organisations we are interested in ... Apache Software Foundation, Hibernate, SpringSource, Oracle, etc, etc.

Certainly much easier to digest than irritating emails that are quarter-disclaimer and quarter-virus-check. Easy to ignore, easy to read and easy to pass on the juicy bit to your contacts. (You'll notice the new Twitter updates section in the blog).

Go on, do yourself a favour. Check out Twitter. You might be surprised how useful it, and similar "social networking" sites, can be.

Monday, 26 April 2010

Static Imports

I've been working with Java for a long time, since version 1.2 in fact. I suppose it's human nature to stick with things we know and, as a consequence, and despite reviewing the change logs when they come out, I sometimes find myself becoming aware of things in new versions of Java that I was blissfully ignorant of.

Today was an example. I was writing some JUnit test scripts and using my usual approach with assertions:

import org.junit.Assert;
import org.junit.Test;

public class ConversionUtilTest {

    @Test
    public void testMetresToFeet() {
        Assert.assertEquals(0, ConversionUtil.metresToFeet(0));
        Assert.assertEquals(3117, ConversionUtil.metresToFeet(950));
        Assert.assertEquals(3114, ConversionUtil.metresToFeet(949));
        Assert.assertEquals(3110, ConversionUtil.metresToFeet(948));
    }

    ...

}

The example I was working on was more involved than that, and had many more assertions going on of different types: assertEquals(), assertTrue(), etc. But you get the idea.

I couldn't remember how to test for an exception with a JUnit annotation, so I searched for an example. Here's how you do it, by the way:

    @Test(expected=java.lang.IllegalArgumentException.class)
    public void testMetresToFeetInvalid() {
        ConversionUtil.metresToFeet(-1);
    }

And that's when I discovered an example with static imports, meaning the above can be rewritten more succinctly like this:

import static org.junit.Assert.assertEquals;
import static mypackage.ConversionUtil.*;
import org.junit.Test;

public class ConversionUtilTest {

    @Test
    public void testMetresToFeet() {
        assertEquals(0, metresToFeet(0));
        assertEquals(3117, metresToFeet(950));
        assertEquals(3114, metresToFeet(949));
        assertEquals(3110, metresToFeet(948));
    }

    ...

}

Not an earth-shattering revelation, and I wouldn't want to overuse it, but it does save a bit of typing in unit tests where the assertions become repetitive. Makes it more readable too, without all those class names.

You can read more about static imports in the Java 1.5 documentation, in particular when it's a good idea to use them, and when not:

Java 1.5 Static Import

Given the repetition and naming of methods in the org.junit.Assert class, I think I'd even be happy with a wildcard import in most of my test classes, i.e. import org.junit.Assert.*;.

Thursday, 15 April 2010

Hibernate Validator Disappeared from Maven?

I updated a sample project for a training course a few weeks ago, adding in a little taster of JSR-303 validation. I used org.hibernate:hibernate-validator:4.0.2.GA from the Maven central repositories.

When one of my students tried to build the application, he got some dependency errors. Deleting my local repository and rebuilding, I found the Hibernate Validator 4.0.2.GA was no longer available from the central repository.

I've not found out why it might have disappeared (I don't recall installing it manually so I'm reasonably sure that it did disappear), but the simple solution is to add the JBOSS repository the POM:

pom.xml
<repositories>
    <repository>
        <id>JBOSS</id>
        <name>JBoss Repository</name>
        <url>http://repository.jboss.org/maven2/</url>
    </repository>
</repositories>

Saturday, 27 March 2010

License Project in NetBeans

I resurrected an old project last week, with the intention of sharing some of the code. Specifically I wanted to add a GPL license to the project but it's not uncommon to want to add some sort of standard header to source code files. Achieving that with NetBeans 6.8 threw up some interesting aspects of NetBeans itself, its interaction with Maven and a nice trick with Bash scripting ...

Licenses in NetBeans

I think Templates in NetBeans are fairly well known and commonly modified. Perhaps less well known is the default inclusion of a license statement which can be configured on a project by project basis.

If you go to Tools -> Templates and open the template for a Java class file, you'll find this little section at the top:

<#assign licenseFirst = "/*">
<#assign licensePrefix = " * ">
<#assign licenseLast = " */">
<#include "../Licenses/license-${project.license}.txt">

Out of the box, the project.license variable is set to a default license, which produces this, which you will have no doubt seen if you use NetBeans:

/* 
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

If you are using a standard NetBeans project, by editing the nbproject/project.properties file in your NetBeans project directory, you can modify the license applied to each new file. (In the rest of this post, where I'm using shell commands, I'm going to assume you are working in your project directory).

Edit nbproject/project.properties with your favourite text editor, or a quick way if you are using a Unix is:

$ echo "project.license=gpl30" >> nbproject/project.properties

The variable is set to gpl30 in this case for GPL 3.0. The other licenses are listed under Tools -> Templates -> Licenses. Note that NetBeans automatically prefixes "license-" and appends ".txt", so just specify the middle part.

Now when you create new source files, NetBeans will, where appropriate, add the license comment that you specified.

If you aren't quite happy with the format, you can edit the license template itself. This can be used in conjunction with the User.properties template in Tools -> Templates -> User Configuration Properties.

User.properties
user=Your Full Name
email=yourEmail@yourDomain
company=Your Company
license-gpl30
<#if licenseFirst??>
${licenseFirst}

${licensePrefix} Copyright (c) ${company} ${date?date?string("yyyy")}
${licensePrefix}
${licensePrefix} This program is free software: you can redistribute it and/or modify
...
${licensePrefix} Please refer any queries to ${user} <${email}>

But what if you are using Maven? (If not, why not?) In this case, there is no nbproject/project.properties file. The solution is actually quite simple. Edit your POM and add a new properties section, or extend an existing one, with this hint:

pom.xml
...
<properties>
    <netbeans.hint.license>gpl30</netbeans.hint.license>
</properties>
...

Obviously it would be nice if you could configure these licenses into your project using NetBeans project properties, but for now, you have to edit nbproject/project.properties or pom.xml.

Adding License Header to Existing Code using Bash

N.B. I'm using Ubuntu, other Unix variants that don't use Bash or Bourne shell deriviatives will be slightly different.

My first thought was to use a combination of find and cat to prepend the license, stored in a text file, to all the Java files in my project. Something like this:

$ find . -name '*.java' -exec cat $HOME/Documents/gpl.txt {} > {} \;

But it doesn't work. Aside from the possibility that such a command would clobber the input file before it was even read, I could find neither documentation nor examples of using shell redirection within the -exec.

So I took the easy way out and made a simple Bash script to prepend the GPL license to a list of files on the command line. Default on Ubuntu is to add $HOME/bin to your path. Don't forget to:

$ chmod +x $HOME/bin/prepend_gpl

$HOME/bin/prepend_gpl
#!/bin/bash

# Use Ubuntu's auto-mounted RAM disk for temporary files
TMP=/dev/shm

# Use GPL environment variable if set, otherwise use default
if [ ! $GPL ]; then
    GPL=$HOME/Documents/gpl.txt
fi

# Check the license file exists
if [ ! -e $GPL ]; then
    echo $GPL not found
    exit -1
fi

# Prepend the license to each file listed on the command line
while [ $1 ]; do
    cat $GPL $1 > $TMP/prepend_gpl.tmp 
    mv $TMP/prepend_gpl.tmp $1 
    shift
done

Nothing too tricky there, making use of a temporary file to hold the composited file and renaming back to overwrite the original. Note the /dev/shm mount point on Ubuntu, which is an auto-mounted RAM disk in shared memory, ideal for temporary files.

$ prepend_gpl *.java

That deals with all .java files in a directory. To apply to a whole project, it can be used with find from the root of the project:

$ find . -name '*.java' -exec prepend_gpl {} \;

Job done, but I was still curious if I could do it in a one-liner and found this neat trick of redirecting a command into a Bash loop structure. This uses a while loop, but other constructs work in a similar way.

$ while read f; do cat $HOME/Documents/gpl.txt "$f" > /dev/shm/addgpl.tmp && mv /dev/shm/addgpl.tmp "$f"; done < <(find . -name '*.java')

Does it count as a one-liner? Will I be able to recall the syntax off the top of my head? I'll leave you to decide!

Monday, 22 March 2010

Apache and Tomcat on Ubuntu

I've had a frustrating time today, getting Tomcat working behind Apache on Ubuntu. Frustrating because there are a lot of conflicting HOWTOs, blog posts, forum posts and documentation around. Here's another! ;)

Anyway, while I remember, here's how I got it working ...

Versions

I think part of the documentation problem is down to differences between versions, so, for the record:

  • Ubuntu 9.04 Jaunty Jackalope
  • Tomcat 6.0.18
  • Apache 2.2.11
  • Java 1.6.0.16
  • mod_jk 1.2.26

All packages installed using apt-get from the Jaunty repositories.

EDIT [15th April 2010]

Since posting this, I've had a chance to confirm these instructions on Ubuntu 10.04 Lucid Lynx and, generally speaking they work OK. Differences are covered in italics at appropriate points in the text, these being the versions:

  • Ubuntu 10.04 Lucid Lynx
  • Tomcat 6.0.24
  • Apache 2.2.14
  • Java 1.6.0.18
  • mod_jk 1.2.28

Prerequisites

I'm going to assume that you have Tomcat up and running on the default port of 8080 and serving requests. Also that you have installed and tested Apache on the default port of 80, keeping the default configuration of virtual hosts as provided by the Ubuntu repositories.

Installing Apache on Ubuntu Installing Tomcat on Ubuntu

Requests are delegated from Apache to Tomcat by Tomcat listening on port 8009 for AJP1.3 requests and Apache being configured to divert some request URIs through "workers". In our case, the worker is Tomcat.

Setting up Tomcat

I've used mod_jk to connect Apache to Tomcat. It's not the only way. See the Tomcat WIKI for information on which is the recommended connector protocol.

The Ubuntu-supplied Tomcat doesn't listen for for AJP1.3 requests on port 8009 by default, so edit /var/lib/tomcat6/conf/server.xml, search for "8009" and uncomment the AJP1.3 connector module:

/var/lib/tomcat6/conf/server.xml
...
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
...

Restart Tomcat, giving it time to start the listeners, and check that it is listening on port 8009:

$ sudo /etc/init.d/tomcat6 restart
 * Stopping Tomcat servlet engine tomcat6                                [ OK ] 
 * Starting Tomcat servlet engine tomcat6                                [ OK ] 
$ sudo netstat -ln | grep :8009
tcp6       0      0 :::8009                 :::*                    LISTEN     

For Lucid, restart Tomcat with $ sudo service tomcat6 restart.

If the listener is listening, Tomcat should be good to go.

Setting up Apache

To get Apache to talk to Tomcat, the first step is to install the AJP1.3 connector module mod_jk:

$ sudo apt-get install libapache2-mod-jk2

For Lucid, package is libapache2-mod-jk.

With the default Ubuntu packaging, this is all set up and should be loaded without any further intervention. The key to this is that /etc/apache2.conf includes all modules listed in /etc/apache2/mods-enabled. There's no need to add the LoadModule directive to apache2.conf as you might see quoted in some places. Such as the Tomcat documentation for example.

Then it's just a matter of configuring Apache to route some requests through to Tomcat.

The core of the Apache configuration looks like this on a default Ubuntu setup:

JkWorkersFile /etc/apache2/workers.properties
JkShmFile     /var/log/apache2/mod_jk.shm
JkLogFile     /var/log/apache2/mod_jk.log
JkLogLevel    error
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
JkMount /MyWebApplication/* tomcat

You have a choice at this point. You can configure this within the default virtual host that Ubuntu's Apache comes with, or globally.

If you want to configure just for the virtual host, add the above section to the bottom of /etc/apache2/sites-available/default.

If you want to configure the forwarding globally, add the above section to the bottom of /etc/apache2/apache2.conf. In this case, you also need to add an extra line to the sites-available file to get the virtual site to read the mounts. Immediately under DocumentRoot is a reasonable place.

/etc/apache2/sites-available/default
...
DocumentRoot  /var/www
JkMountCopy   On
...

The final part of the jigsaw is to set up the worker.properties file now referred to in the Apache config files. So make a new file ...

$ sudo touch /etc/apache2/workers.properties

... and edit it. With sudo of course

/etc/apache2/workers.properties
worker.list=tomcat

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

Look back at the lines we added to the Apache config files and note that the JkMount directives refer Apache to entries in workers.properties. If you had multiple Tomcat servers, you could achieve some simple application-level load balancing by routing different applications to different servers:

/etc/apache2/workers.properties
worker.list=tomcat_a, tomcat_b

worker.tomcat_a.type=ajp13
worker.tomcat_a.host=hosta
worker.tomcat_a.port=8009

worker.tomcat_b.type=ajp13
worker.tomcat_b.host=hostb
worker.tomcat_b.port=8009

Anyway, I digress. Restart Apache and you should be up and running.

$ sudo /etc/init.d/apache2 restart

For Lucid, restart Apache with $ sudo service apache2 restart.

Thursday, 11 March 2010

It's been a while!

I can't believe it's been almost a year since I posted anything on this blog! I feel as though I should make excuses but, hey, I've just been doing other things and it's gone to the back of my mind. It happens I guess.

I'm still going strong with Ubuntu. My laptop is upgraded to Ubuntu 9.10 and NetBeans 6.8 and that's where I do most work in terms of examples for training courses. My old desktop machine is currently running Xubuntu 9.10 until I get round to a much-needed motherboard and CPU upgrade. That will probably become a test bed for Ubuntu 10.04. The headless server box is on Ubuntu 9.04 with all of the GUI stripped back so it's more like Ubuntu Server. It was running Oracle XE and Glassfish v2 but I replaced those with MySQL and Tomcat as I've not been doing much with EJB. Most of the work done by that box is RAID1 file service, DNS and Subversion

I've nothing against Microsoft operating systems per se but I don't miss the rebooting and what always seemed a painful process of upgrading on Vista. ;)

The past few weeks have been spent looking at Spring 3.0, particularly Spring MVC. I am really liking the new features, especially the simplified web development, and I'll try and get a few posts together over the next month or so.

ps. Just discovered an unpublished draft post from last October about Hibernate and multiple bags. There was probably a good reason why I didn't publish it at the time but it seemed to make sense, so up it went.

Followers