The Source for Java Technology Collaboration
User: Password:
Register | Login help    

Search

Online Books:
java.net on MarkMail:


Jayson Falkner

Jayson is a long time Java developer who is currently a member of the JSP 2.0 expert group, and he has been developing with JavaServer Pages and Servlets since the technologies were initially released. Jayson is currently the CTO of Amberjack Software LLC and the web master of both JSP Insider, http://www.jspinsider.com, and the book support site for Servlets and JSP; the J2EE Web Tier, http://www.jspbook.com. He strives to provide good, free information about the J2EE web tier and he is a proponent of Linux/Java based web applications.

 

Jayson Falkner's blog

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 >> J2EE      
Comments
Comments are listed in date ascending order (oldest first)

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.
Syndicate content