Why url mapping sucks in Java Servlet land, and what I did about it.

This is more of ‘notes to self’ (like anyone else actually reads this!) than anything else. I bounce around so much at my current job that I forget everything and have to figure it out again. One such example: Servlets. I’ve only written servlets when absolutely necessary, i.e. when I’ve had to prototype a service and didn’t really care about what paths were coming in, how the web app was deployed, etc. So I always go through a bit of a learning curve when working with Servlets, because I usually have forgotten everything I know about them.

I am working on converting a set of services that offer POX over HTTP (see this example)into something more RESTful. I’ll spare you the RESTafarian evangelism and just say that my life has become much easier once I started thinking of infinite resources constrained by a (very) finite set of verbs. As the number of brain cells I kill increases, I have had to put my remaining ones to work figuring out how to be as effective as I was back in the day, when I had brainpower to spare.

As part of that assignment, we have had to think about combining separate services into a single, meaningful, easy to grasp API. I will say that thinking in resources helps here, because it’s easy to have a resource Foo that has sub resources Bar, Star, and Var, and request those resources as /foo/bar, etc. But mapping that elegant and simple layer to a sub strata of what are basically RPC calls has taken some thought.

One thing we decided to do was to access all services that are currently residing in separate WARs into one web app. The original goal was to have this web app be a very simple shell, and let web.xml route messages to specific services. All was good, less code was to be written, and we were supposed to live happily ever after….except in order to map objects to messages, we would end up routing requests from path foo/bar to servlet X and requests from /foo/bar/something to servlet Y. This is because unlike the happy world of self contained RESTful resources, our services actually provide different kinds of functionality for the same resources. But we really want to fake ‘resourceful’ ness.

The thing about web.xml servlet mapping is that it is limited to heirarchical path mapping, i.e.

map /foo/bar/star/* to servlet x

map /foo/glar/* to servlet y

map *.bat to servlet z

you cannot take /foo/bar/star/mar and map it to servlet z if you’ve already mapped /foo/bar/star/* to servlet x. So you can’t mix and match path heirarchies to servlets.

The solution, after not much time spent browsing the Servlet spec (good read, btw) is to use the built in RequestDispatcher object created from the ServletContext. In web.xml, we mapped all of our service servlets to private paths that would never be called from the clientAPI:


<servlet-mapping>
<servlet-name>ServiceX</servlet-name>
<url-pattern>/service_x/*</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>ServiceY</servlet-name>
<url-pattern>/service_y/*</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>ServiceZ</servlet-name>
<url-pattern>/service_z/*</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>Default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

Note that I’ve got a Default servlet catching all requests, because the paths above aren’t exposed in documentation (even if they are hit, they resolve to no ops). In the Default servlet init method, we created request dispatchers for all servlets that we had specified:


_servletXDispatcher = this.getServletContext().getRequestDispatcher("/service_x/*");
_serviceYDispatcher = this.getServletContext().getRequestDispatcher("/service_y/*");
_serviceZDispatcher = this.getServletContext().getRequestDispatcher("/service_z/*");

Note that in order to get valid request dispatchers, we had to specify the servlet mappings as specified in web.xml

Now we have RequestDispatchers, which can forward requests on to servlets:
_servletXDispatcher.include(httpRequest,httpResponse);

However I still needed a way to map partial paths to different request dispatchers. I ended up creating an ObjectMatcher class that regex matched incoming strings to specified objects:

public class ObjectMatcher {

Map _patternsMatchServlets;

public ObjectMatcher() {
_patternsMatchServlets = new HashMap();
}

public void load(Map servletMap) {

Set keys = servletMap.keySet();

for(String key : keys) {

_patternsMatchServlets.put(Pattern.compile(key),servletMap.get(key));
}
}

public T match(String uriPattern) {
T servlet = null;
boolean matches = false;
Set patterns = _patternsMatchServlets.keySet();

for(Pattern pattern : patterns) {

Matcher match = pattern.matcher(uriPattern);

matches = match.find();
if(matches == true) {
servlet = _patternsMatchServlets.get(pattern);
break;
}

}

return servlet;
}
}

The load method in this object takes a map of regex values to objects. It then compiles the regex values into pattern objects. The match method uses those regexes to match against inbound strings, and returns the appropriate object, or null if an object isn’t found.

I loaded this object with a map of regex values to objects as follows:

Map rdMap = new HashMap();

// TODO: put all new paths for client API here.
rdMap.put(".*/entities.*", _queryReqDispatcher);
rdMap.put(".*/media.*", _queryReqDispatcher);
rdMap.put(".*/actions.*", _queryReqDispatcher);
rdMap.put("person/.*", _entityReqDispatcher);
rdMap.put("popular/*", _zgReqDispatcher);
rdMap.put("media/.*", _zgReqDispatcher);

_matcher.load(rdMap);
and called it from my default servlet doGet method like this:

public void doGet( HttpServletRequest request, HttpServletResponse response )
throws ServletException, IOException {
dispatch(request,response);

}
to get (fairly) pain free routing in a central location.

About these ads

3 Responses to Why url mapping sucks in Java Servlet land, and what I did about it.

  1. Palm Oil Land…

    [...]Why url mapping sucks in Java Servlet land, and what I did about it. « Wherever I go, there I am[...]…

  2. Dle| Ucoz| Skripts| NULL| 1 PSD| 12 Frames For Photoshop| 12 FRAMES FOR PHOTOSHOP| 15 FRAMES FOR PHOTOSHOP| 16 FRAMES FOR PHOTOSHOP| 2 Frames for photoshop| 2nd Edition| 2| 2010| 3ds| 6 Frames for Photoshop| 8 Frames For Photoshop| 8 Frames For Photo…

    [...]Why url mapping sucks in Java Servlet land, and what I did about it. « Wherever I go, there I am[...]…

  3. The best bongs money can buy!…

    [...]Why url mapping sucks in Java Servlet land, and what I did about it. « Wherever I go, there I am[...]…

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: