I'm looking for advice on the best way to map REST requests onto a
collection of Tomcat apps all running in the same JVM. The REST name space
was designed for client use and doesn't reflect how the apps implement it.
For example, the resource "/v1/x/123" is implemented by app X, but the
resource "/v1/x/123/y" is implemented by app Y.

A proxy (e.g. Apache mod_proxy or Squid) in front of Tomcat can rewrite the
URLs to go to the correct app, but this gives us some pretty ugly proxy
configurations which have to be kept in lock-step with the Tomcat apps.
Relying on a proxy also makes it a bit harder to use Amazon's load balancer
because it doesn't do rewrites (I think we'd have to run a proxy on each
Tomcat instance).

I'm trying to implement the rewrite as a Valve (code outline below)
registered with the Engine which will run before any Hosts or Contexts. This
seems like a good approach and may even let me grab the JAX-RS annotations
from the apps to dynamically build the rewrite rules.

Does anyone have advice for REST name spaces in Tomcat in general?

Has anyone had good experiences with a rewrite proxy in front of Tomcat on
Amazon EC2 with Amazon's ELB?

Has anybody tried a rewrite Valve similar to this? It has to modify the
CoyoteRequest and generate new Request.mappingData which seems kind of
risky. (Though I think it will work in Tomcat 7, I've only tried Tomcat 6.)
This is my favorite approach so far.

Thanks,

- Ken


public void invoke(Request request, Response response) {
    if ("/v1".equals(request.getContextPath())) {
        // map the in-bound REST URI to the app handling it
        String newRequestURI = "/new-app/some/derived/uri";

        org.apache.coyote.Request req = request.getCoyoteRequest();
        req.requestURI().setString(newRequestURI);
        req.decodedURI().setString(newRequestURI);

        MessageBytes uriMB = MessageBytes.newInstance();
        uriMB.duplicate(req.decodedURI());

        MessageBytes hostMB = MessageBytes.newInstance();
        hostMB.setString(request.getHost().getName());

        MappingData mappingData = request.getMappingData();
        mappingData.recycle();
        request.getConnector().getMapper().map(hostMB, uriMB, mappingData);

        request.setContext((Context) mappingData.context);
        request.setWrapper((Wrapper) mappingData.wrapper);
    }

    getNext().invoke(request, response);
}

Reply via email to