Skip to main content

Lazy coder's mapping of DNS prefix to a sub-directory in a web application.

Posted by jfalkner on February 19, 2008 at 11:50 AM PST

The Servlet specification provides a really elegant mechanism for packaging up a whole website in to a single WAR file and deploying that file as a website. Multiple websites can be mapped to different domain name prefixes, such as 'www.proteomecommons.org' versus 'tranche.proteomecommons.org'. This blog explains a hack to map the domain prefix to a sub directory of the same web application.

Why use this hack? Well, I had two good reasons. First is that when I first helped make the website we didn't put it in a proper build system. Thus it grew in to a hodgepodge of JSP, HTML, and Java code. Second, the domain prefix that we were using 'tranche.proteomecommons.org' wouldn't easily fit into its own web application because it relies on a shared in-memory database. Sure, I could invest a significant chunk of time refactoring the code and binding the database to a local, protected socket, but that'd take a lot more time than the ten minutes that it would take to hack out a URL mapping filter.

Here is the filter's source-code in full. I'll discuss how it works after.

package org.proteomecommons;

import java.io.File;
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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
*
* @author Jayson Falkner - jfalkner@umich.edu
*/
public class TrancheRedirectFilter implements Filter {
    // save the filter config -- needed later
    FilterConfig config = null;
    // the domain to match
    String domain = "tranche.proteomecommons.org";
    // uri to pre-append
    String preAppend = "/dev/dfs";
   
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest)request;
        HttpServletResponse res = (HttpServletResponse)response;
        // check the URL for the domain name
        String url = req.getRequestURL().toString();
        if (url.contains(domain)) {
            // get the resource
            String uri = tweak(url);
            // forward to the resource
            req.getRequestDispatcher(uri).forward(req, res);
        } else {
            chain.doFilter(req, res);
        }
    }
   
    // dynamically change the URL
    private String tweak(final String url) {
        String uri = url.split("proteomecommons.org|\\?")[1];
        // if it starts with 'tranche.proteomecommons.org' move to /dev/dfs directory
        if (url.contains(domain)) {
            uri = preAppend+uri;
           
            // check if this is a real file and a directory
            String path = config.getServletContext().getRealPath(uri);
            File pathFile = new File(path);
            if (!uri.endsWith("/") && pathFile.exists() && pathFile.isDirectory()) {
                uri += "/";
            }
        }
        return uri;
    }
   
    public void init(FilterConfig filterConfig) throws ServletException {
        this.config = filterConfig;
    }
   
    public void destroy() {
    }
}

The code assumes that you want to map all URLs that start with a certain prefix to a sub-directory of your website. For example, 'www.proteomecommons.org' goes to the normal website. The Tranche Project website was developed in the '/dev/dfs' folder of the website, thus by default making its URL http://www.proteomecommons.org/dev/dfs. However, Tranche grew up quickly and we wanted to give it a proper, top level domain 'tranche.proteomecommons.org' while not breaking any of the old '/dev/dfs' links. In short, we wanted to make all URLs starting with 'tranche.proteomecommons.org' automatically go to the '/dev/dfs' folder without redirecting to a ugly URL starting with www and /dev/dfs at the end.

The Filter is generic. The first two variables will swap any domain name prefix with a folder location. In this case, 'tranche.proteomecommons.org' is swapped with '/dev/dfs'.

    // the domain to match
    String domain = "tranche.proteomecommons.org";
    // uri to pre-append
    String preAppend = "/dev/dfs";

If you want to copy/paste the above code, simply swap these variables to be the domain name and local directory of files to map to. If you are up for it, you might abstract them to variables in the Filter's web.xml declaration.

Only the tweak() method of the filter merits more discussion. The other code causes the Filter to invoke or skip tweak() based on if the URL has the domain name specified. In the tweak() method, the code does the swapping.

   private String tweak(final String url) {
        String uri = url.split("proteomecommons.org|\\?")[1];
        // if it starts with 'tranche.proteomecommons.org' move to /dev/dfs directory
        if (url.contains(domain)) {
            uri = preAppend+uri;
           
            // check if this is a real file and a directory
            String path = config.getServletContext().getRealPath(uri);
            File pathFile = new File(path);
            if (!uri.endsWith("/") && pathFile.exists() && pathFile.isDirectory()) {
                uri += "/";
            }
        }
        return uri;
    }

First, the URL is split to remove the domain name with prefix, 'tranche.proteomecommons.org' and replace it with the default domain name, 'www.proteomecommons.org'. Next, the URL is padded to include the proper sub-directory, "/dev/dfs". Finally, the Filter forwards the request and response to the appropriate page. A little File check is made to handle a fringe case where a URLs go to a directory.

That is it! If it still isn't clear why the above is handy, note that the two URLs now work exactly the same:

Also, the two links go to the same exact file on the server. All of our old links work fine. All of the new links starting with 'tranche.proteomecommons.org' work. Best of all the whole hack took about 10 minutes...well, a half hour if you count writing this blog.

Related Topics >>

Comments

That is very true. If you are using a Filter for security, you can still have it apply; however, I'm not sure if the default web.xml security restrictions will apply. I'd guess not, but it would be worth checking.

For the particular use case above, the tranche.proteomecommons.org portion of the site does not have any sort of security restrictions.

You just need to be careful here regarding how security it set up. If you have security on /dev/dfs, the original url may well kick in the login page, whereas the new url may not. The forward may not honor the security obligation.