Programmatic equivalents of web.xml sections for Tomcat

Most documentation for J2EE configuration is based on having a web.xml file, but I want to configure my Tomcat programmatically. Here are some of the things I have found out.

Please use the comments below to correct what I got wrong, and mention equivalents for other parts of web.xml.

Getting started

<web-app ...

in code becomes something like:

import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
...
Tomcat tomcat = new Tomcat();
Context context = tomcat.addContext( "", "WebContent" );

Adding a Servlet

<web-app ...
    <servlet>
        ...
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>com.example.MyServlet</servlet-class>
        ...
    </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/foo/*</url-pattern>
    </servlet-mapping>

in code becomes something like:

...
Class servletClass = MyServlet.class;
// MyServlet extends javax.servlet.http.HttpServlet 
String servletName = servletClass.getSimpleName(); // Or something else if you like
Tomcat.addServlet( context, servletName, servletClass.getName() );
context.addServletMapping( "/foo/*", servletName );

Adding a filter

<web-app ...
    <filter>
        <filter-name>MyFilter</filter-name>
        <filter-class>com.example.MyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>MyFilter</filter-name>
        <url-pattern>/bar/*</url-pattern>
    </filter-mapping>

in code becomes something like:

...
import org.apache.catalina.deploy.FilterDef;
import org.apache.catalina.deploy.FilterMap;
...
Class filterClass = MyFilter.class;
// MyFilter implements javax.servlet.Filter
String filterName = filterClass.getSimpleName(); // Or something else if you like
FilterDef def = new FilterDef();
def.setFilterName( filterName );
context.addFilterDef( def );
FilterMap map = new FilterMap();
map.setFilterName( filterName );
map.addURLPattern( "/bar/*" );
context.addFilterMap( filterMap );

Adding a Listener

<web-app ...
    <listener>
    	<listener-class>com.example.MyContextListener</listener-class>
    </listener>

in code becomes something like:

...
context.addApplicationListener( MyContextListener.class.getName() );
// MyContextListener implements javax.servlet.ServletContextListener

This is for a ServletContextListener: it may be similar for other listeners, but I’m not sure.

JAX-RS (using Apache CXF) in embedded Tomcat example

I had serious trouble today firing up an embedded Tomcat server that serves up REST resources using JAX-RS via Apache CXF.

Here’s minimal example, hopefully saving you the same trouble:

src/HelloJaxRs.java:

import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;

public class HelloJaxRs
{
    public static void main( String[] args ) throws Exception
    {
        Tomcat tomcat = new Tomcat();
        Context context = tomcat.addContext( "/", "" );

        Wrapper servlet = context.createWrapper();
        servlet.setName( "jaxrs" );
        servlet.setServletClass(
            "org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet" );

        servlet.addInitParameter(
            "jaxrs.serviceClasses",
            Hello.class.getName()
        );

        servlet.setLoadOnStartup( 1 );
        context.addChild( servlet );
        context.addServletMapping( "/rest/*", "jaxrs" );

        tomcat.start();
        tomcat.getServer().await();
    }
}

src/Hello.java:

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;

@Path( "/hello/{name}" )
public class Hello
{
    @GET
    public String getThing( @PathParam( "name" ) String name )
    {
        return "Hello, " + name;
    }
}

build.xml:


<?xml version="1.0" encoding="UTF-8"?>
<project default="run">

    <target name="run" depends="compile">
        <mkdir dir="bin/tomcat.8080/webapps"/>
        <java
            classname="HelloJaxRs"
            fork="true"
            failonerror="true"
            dir="bin"
        >
            <classpath>
                <pathelement location="bin/"/>
                <fileset dir="lib"><include name="**/*.jar"/></fileset>
            </classpath>
        </java>
    </target>

    <target name="compile">
        <mkdir dir="bin"/>
        <javac srcdir="src" destdir="bin">
            <classpath>
                <fileset dir="lib"><include name="**/*.jar"/></fileset>
            </classpath>
            <compilerarg value="-Werror"/>
        </javac>
    </target>

</project>

To download the dependencies (or you can do something clever with Maven):

$ mkdir lib
$ cd lib
$ wget http://www.webhostingjams.com/mirror/apache/tomcat/tomcat-7/v7.0.57/bin/apache-tomcat-7.0.57.tar.gz
$ tar -xzf apache-tomcat-7.0.57.tar.gz
$ wget http://www.apache.org/dyn/closer.cgi?path=/cxf/3.0.3/apache-cxf-3.0.3.tar.gz
$ tar -xzf apache-cxf-3.0.3.tar.gz
$ cd ..

To run Tomcat:

$ ant

Then, in another terminal, you can see it’s working:

$ curl http://localhost:8080/rest/hello/andy
Hello, andy

Vim persistent buffer list combined with saved sessions

I like to use several saved Vim sessions to remember what files I was working on in a particular project.

I also like to have a list of buffers I have open on the left-hand side of the screen that is always visible and up-to-date.

Sometimes, the existence of a generated buffer like the list of buffers can confuse the session restore code, but I have found a way to make this work nicely using the Buffergator plugin and the built-in mksession command.

Here are the relevant snippets from my .vimrc.

I am a beginner: please comment if you see bugs or possible improvements.

" Prerequisites: install the Buffergator plugin


" Save session on exit, excluding problematic stuff like options

set sessionoptions=blank,buffers,curdir,tabpages,resize,winpos,winsize
au VimLeavePre * if v:this_session != '' | exec "mks! " . v:this_session | endif


" Open Buffergator when you press F5

nnoremap <F5> :BuffergatorOpen<CR>


" Various Buffergator options to make it persistent,
" and displayed how I like

let g:buffergator_autodismiss_on_select = 0
let g:buffergator_display_regime = "bufname"
let g:buffergator_sort_regime    = "filepath"
let g:buffergator_autoupdate = 1


" Suppress the error message about the Buffergator buffer
" when you restore a session

function! OpenBuffergatorIfItsBufferExists()
    if bufnr("[[buffergator-buffers]]") > ''
        BuffergatorOpen
        normal o
    endif
endfunction

autocmd VimEnter * call OpenBuffergatorIfItsBufferExists()

Now when I start vim with vim -S path/to/session.vim the session is restored and saved when I exit, with a persistent buffer list on the left, and no errors during the session restore.