net.sf.j2ep.UrlRewritingResponseWrapper.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.j2ep.UrlRewritingResponseWrapper.java

Source

/*
 * Copyright 2000,2004 The Apache Software Foundation.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.sf.j2ep;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import net.sf.j2ep.model.Server;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * A wrapper for the normal HttpServletResponse, based on the content-type
 * either the normal output stream of a wrapped stream will be returned. The
 * wrapped stream can handle rewrite of links found in the source.
 * 
 * This class also handles rewriting of the headers Location and Set-Cookie.
 * 
 * @author Anders Nyman
 */
public final class UrlRewritingResponseWrapper extends HttpServletResponseWrapper {

    /**
     * Stream we are using for the response.
     */
    private UrlRewritingOutputStream outStream;

    /**
     * Writer we are using for the response.
     */
    private PrintWriter outWriter;

    /**
     * Writer that writes to the underlying stream.
     */
    private PrintWriter originalWriter;

    /**
     * Server used for this page
     */
    private Server server;

    /**
     * The location for this server, used when we rewrite absolute URIs
     */
    private String ownHostName;

    /**
     * The contextPath, needed when we rewrite links.
     */
    private String contextPath;

    /**
     * The servers.
     */
    private ServerChain serverChain;

    /**
     * the request uri
     */
    private String requestURI;

    /**
     * Regex to find absolute links.
     */
    private static Pattern linkPattern = Pattern.compile("\\b([^/]+://)([^/]+)([\\w/]*)",
            Pattern.CASE_INSENSITIVE | Pattern.CANON_EQ);

    /**
     * Regex to find the path in Set-Cookie headers.
     */
    private static Pattern pathAndDomainPattern = Pattern.compile("\\b(path=|domain=)([^;\\s]+);?",
            Pattern.CASE_INSENSITIVE | Pattern.CANON_EQ);

    /**
     * Logging element supplied by commons-logging.
     */
    private static Log log;

    /**
     * Basic constructor.
     * 
     * @param response
     *            The response we are wrapping
     * @param server
     *            The server that was matched
     * @param ownHostName
     *            String we are rewriting servers to
     * @throws IOException
     *             When there is a problem with the streams
     */
    public UrlRewritingResponseWrapper(HttpServletResponse response, Server server, String ownHostName,
            HttpServletRequest request, ServerChain serverChain) throws IOException {
        super(response);
        this.server = server;
        this.ownHostName = ownHostName;
        this.contextPath = request.getContextPath();
        this.requestURI = request.getRequestURI();
        this.serverChain = serverChain;

        log = LogFactory.getLog(UrlRewritingResponseWrapper.class);
        outStream = new UrlRewritingOutputStream(response.getOutputStream(), ownHostName, contextPath, serverChain);
        outWriter = new PrintWriter(outStream);
        originalWriter = new PrintWriter(response.getOutputStream());
    }

    /**
     * Checks if we have to rewrite the header and if so will rewrite it.
     * 
     * @see javax.servlet.http.HttpServletResponse#addHeader(java.lang.String,
     *      java.lang.String)
     */
    public void addHeader(String name, String originalValue) {
        String value;
        if (name.equalsIgnoreCase("location")) {
            value = rewriteLocation(originalValue);
        } else if (name.equalsIgnoreCase("set-cookie")) {
            value = rewriteSetCookie(originalValue);
        } else {
            value = originalValue;
        }
        super.addHeader(name, value);
    }

    /**
     * Checks if we have to rewrite the header and if so will rewrite it.
     * 
     * @see javax.servlet.http.HttpServletResponse#setHeader(java.lang.String,
     *      java.lang.String)
     */
    public void setHeader(String name, String originalValue) {
        String value;
        if (name.equalsIgnoreCase("location")) {
            value = rewriteLocation(originalValue);
        } else if (name.equalsIgnoreCase("set-cookie")) {
            value = rewriteSetCookie(originalValue);
        } else {
            value = originalValue;
        }
        super.setHeader(name, value);
    }

    /**
     * Rewrites the location header. Will first locate any links in the header
     * and then rewrite them.
     * 
     * @param value
     *            The header value we are to rewrite
     * @return A rewritten header
     */
    private String rewriteLocation(String value) {
        StringBuffer header = new StringBuffer();

        Matcher matcher = linkPattern.matcher(value);
        while (matcher.find()) {

            String link = matcher.group(3).replaceAll("\\$", "\\\\$");
            if (link.length() == 0) {
                link = "/";
            }
            String location = matcher.group(2) + link;

            Server matchingServer = serverChain.getServerMapped(location);
            if (matchingServer != null) {
                link = link.substring(matchingServer.getPath().length());
                link = matchingServer.getRule().revert(link);
                matcher.appendReplacement(header, "$1" + ownHostName + contextPath + link);
            }
        }
        matcher.appendTail(header);
        log.debug("Location header rewritten " + value + " >> " + header.toString());
        return header.toString();
    }

    /**
     * Rewrites the header Set-Cookie so that path and domain is correct.
     * 
     * @param value
     *            The original header
     * @return The rewritten header
     */
    private String rewriteSetCookie(String value) {
        StringBuffer header = new StringBuffer();
        Matcher matcher = pathAndDomainPattern.matcher(value);
        while (matcher.find()) {
            if (matcher.group(1).equalsIgnoreCase("path=")) {
                String path = server.getRule().revert(matcher.group(2).replace(server.getPath(), contextPath)); // server.getRule().revert(matcher.group(2));
                path = "/";
                matcher.appendReplacement(header, "$1" + path + ";");
            } else {
                matcher.appendReplacement(header, "");
            }

        }
        matcher.appendTail(header);
        log.debug("Set-Cookie header rewritten \"" + value + "\" >> " + header.toString());
        return header.toString();
    }

    /**
     * Based on the value in the content-type header we either return the
     * default stream or our own stream that can rewrite links.
     * 
     * @see javax.servlet.ServletResponse#getOutputStream()
     */
    public ServletOutputStream getOutputStream() throws IOException {
        if ((getContentType() != null && shouldRewrite(getContentType())) || isRss(requestURI)) {
            return outStream;
        } else {
            return super.getOutputStream();
        }
    }

    /**
     * Based on the value in the content-type header we either return the
     * default writer or our own writer. Our own writer will write to the stream
     * that can rewrite links.
     * 
     * @see javax.servlet.ServletResponse#getWriter()
     */
    public PrintWriter getWriter() throws IOException {
        if (getContentType() != null && shouldRewrite(getContentType())) {
            return outWriter;
        } else {
            return originalWriter;
        }
    }

    /**
     * Rewrites the output stream to change any links. Also closes all the
     * streams and writers. We need the user to flush and close the streams
     * himself as usual but we can't be sure that the writers created are used
     * by the client and therefor we close them here.
     * 
     * @throws IOException
     *             Is thrown when there is a problem with the streams
     */
    public void processStream() throws IOException {
        if (getContentType() != null && shouldRewrite(getContentType())) {
            outStream.rewrite(server);
        } else if (isRss(requestURI)) {
            outStream.rewriteRss(server);
        }

        super.getOutputStream().flush();
        super.getOutputStream().close();
        outStream.close();
        originalWriter.close();
        outWriter.close();

    }

    /**
     * Checks the contentType to evaluate if we should do link rewriting for
     * this content.
     * 
     * @param contentType
     *            The Content-Type header
     * @return true if we need to rewrite links, false otherwise
     */
    private boolean shouldRewrite(String contentType) {
        String lowerCased = contentType.toLowerCase();
        return (lowerCased.indexOf("html") > -1 || lowerCased.indexOf("css") > -1
                || lowerCased.indexOf("javascript") > -1);
    }

    /**
     * Checks the requestURI to evaluate if we should do link rewriting as RSS.
     * 
     * @param requestURI
     *            the requestURI
     * @return true if we need to rewrite links, false otherwise
     */
    private boolean isRss(String requestURI) {
        String lowerCased = requestURI.toLowerCase();
        return (lowerCased.endsWith("rss") || lowerCased.endsWith("xml") || lowerCased.endsWith("feed"));
    }
}