Saturday, August 26, 2006

A JUnit test for UrlRewriteFilter

I recently had the need for URL Rewriting. Although my needs were rather simple, I resisted the urge to roll my own and opted instead to use Paul Tuckey's UrlRewriteFilter. I had a little trouble actually setting up the URL patterns, so at one point, I broke down and wrote myself a JUnit test which I could use to test my patterns without having to restart the application server every time. This blog entry contains details of this test.

Basically, the need for URL Rewriting arose because I re-implemented an existing application which was serving XML using JSP files. The JSP file was referred to in the calling URL by name. So for example:

1
http://my.company.com/myapp/d/foo/bar/show.jsp?id=123456

would route the request to ${docroot}/foo/bar/show.jsp on the web application myapp. All the processing logic was contained in the JSP file. When the need arose to create an XML which was slightly different from an existing one, the recommended approach was to copy show.jsp to a sibling directory bar1 and modify the copy to do the job. So the new JSP would now be accessible at:

1
http://my.company.com/myapp/d/foo/bar1/show.jsp?id=123456

Obviously, not the best way to reuse code, and refactoring is also much harder. My approach was to pull most of the common functionality into Java classes on the server and use a single dispatcher which uses a type parameter to decide which format to show. So, the new URL for the first URL above will look like this:

1
http://my.company.com/myapp/shownew.html?type=bar&fooId=123456

However, it turns out that the original design was done for a reason - the intent was to provide friendly and easy to remember URLs to clients. Obviously the new URL structure is not as friendly, and the client(s) should not have to suffer because we switched out the backend. The solution was to rewrite the URL internally, so the human friendly client URL is rewritten to a machine friendly URL for our dispatcher's consumption. The rewriting is not dynamic, there was no clear pattern in the existing URLs, so I planned to have entries in the urlrewrite.xml file for each of them. Something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 2.6//EN" "http://tuckey.org/res/dtds/urlrewrite2.6.dtd">
<urlrewrite>

    <rule>
        <from>/d/foo/bar1/show.jsp\?id=(\d+)</from>
        <to>/shownew.html\?type=bar1&amp;fooId=$1</to>
    </rule>

    <rule>
        <from>/d/foo/bar2/show.jsp\?id=(\d+)</from>
        <to>/shownew.html\?type=bar2&amp;fooId=$1</to>
    </rule>

</urlrewrite>

The JUnit test reads this configuration file from the classpath, then applies it to a set of specified fromUrl values and asserts that the fromUrl is rewritten with these rules into corresponding specified toUrl values. I sneaked a peek at the JUnit tests in the source distribution of the UrlRewriteFilter to come up with the right sequence of incantations to make the rewrite happen. Here is the code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import java.io.InputStream;

import junit.framework.TestCase;

import org.apache.log4j.Logger;
import org.tuckey.web.MockRequest;
import org.tuckey.web.MockResponse;
import org.tuckey.web.filters.urlrewrite.Conf;
import org.tuckey.web.filters.urlrewrite.RewrittenUrl;
import org.tuckey.web.filters.urlrewrite.UrlRewriter;
import org.tuckey.web.filters.urlrewrite.utils.Log;

/**
 * Simple test case to determine if the rewrite configuration works as
 * expected.
 */
public class UrlRewriteConfigurationTest extends TestCase {

    private static final Logger log = Logger.getLogger(UrlRewriteConfigurationTest.class);
    private static final String REWRITE_CONF = "urlrewrite.xml";

    private Conf conf;

    /**
     * Setup the UrlRewriteFilter configuration.
     */
    protected void setUp() {
        Log.setLevel("DEBUG"); // to make the RewriteFilter code log messages
        InputStream istream = getClass().getResourceAsStream("/" + REWRITE_CONF);
        conf = new Conf(istream, REWRITE_CONF);
    }

    public void testRewrite1() throws Exception {
        String fromUrl = "/d/foo/bar1/show.jsp?id=321456";
        String toUrl = "/shownew.html?type=bar1&fooId=321456";
        assertRewriteSuccess(fromUrl, toUrl, conf);
    }

    public void testRewrite2() throws Exception {
        String fromUrl = "/d/foo/bar2/show.jsp?id=321456";
        String toUrl = "/shownew.html?type=bar2&fooId=321456";
        assertRewriteSuccess(fromUrl, toUrl, conf);
    }

    /**
     * Assertion to rewrite the URL using the UrlRewriteFilter and verify
     * that fromUrl is rewritten to toUrl using rewriting rules in conf.
     * @param fromUrl the URL to be rewritten from.
     * @param toUrl the URL to be rewritten to.
     * @param conf the UrlRewriteFilter configuration.
     * @throws Exception if one is thrown.
     */
    private void assertRewriteSuccess(String fromUrl, String toUrl, Conf conf) throws Exception {
        UrlRewriter rewriter = new UrlRewriter(conf);
        MockRequest request = new MockRequest(fromUrl);
        MockResponse response = new MockResponse();
        RewrittenUrl rewrittenUrl = rewriter.processRequest(request, response);
        assertNotNull("Could not rewrite URL from:" + fromUrl + " to:" + toUrl, rewrittenUrl);
        String rewrittenUrlString = rewrittenUrl.getTarget();
        log.debug("URL Rewrite from:[" + fromUrl + "] to [" + rewrittenUrlString + "]");
        assertEquals("Rewrite from:" + fromUrl + " to:" + toUrl + " did not succeed", toUrl, rewrittenUrlString);
    }
}

This test succeeds and we have a working urlrewrite.xml file as a side effect. Making small changes to the source and target regular expressions and hitting [Alt]-[Shift]-X-T to run the JUnit test (in Eclipse) is far more convenient than having to restart the application server each time we need to test a change in the regular expression.

If you are in the process of creating your urlrewrite.xml file, I am sure you will find this JUnit test useful.