org.apache.roller.weblogger.ui.rendering.WeblogRequestMapper.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.roller.weblogger.ui.rendering.WeblogRequestMapper.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  The ASF licenses this file to You
 * 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.  For additional information regarding
 * copyright in this work, please see the NOTICE file in the top level
 * directory of this distribution.
 */

package org.apache.roller.weblogger.ui.rendering;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.roller.weblogger.config.WebloggerConfig;
import org.apache.roller.weblogger.business.WebloggerFactory;
import org.apache.roller.weblogger.pojos.Weblog;

/**
 * Handles rendering requests for Roller pages/feeds by routing to the appropriate Servlet.
 *
 * This request mapper is used to map all weblog specific urls of the form
 * /<weblog handle>/* to the appropriate servlet for handling the actual
 * request.
 *
 * TODO: we should try and make this class easier to extend and build upon
 */
public class WeblogRequestMapper implements RequestMapper {

    private static Log log = LogFactory.getLog(WeblogRequestMapper.class);

    private static final String PAGE_SERVLET = "/roller-ui/rendering/page";
    private static final String FEED_SERVLET = "/roller-ui/rendering/feed";
    private static final String RESOURCE_SERVLET = "/roller-ui/rendering/resources";
    private static final String MEDIA_SERVLET = "/roller-ui/rendering/media-resources";
    private static final String SEARCH_SERVLET = "/roller-ui/rendering/search";
    private static final String RSD_SERVLET = "/roller-ui/rendering/rsd";

    private static final String COMMENT_SERVLET = "/roller-ui/rendering/comment";
    private static final String TRACKBACK_SERVLET = "/roller-ui/rendering/trackback";

    // url patterns that are not allowed to be considered weblog handles
    Set restricted = null;

    public WeblogRequestMapper() {

        this.restricted = new HashSet();

        // build roller restricted list
        String restrictList = WebloggerConfig.getProperty("rendering.weblogMapper.rollerProtectedUrls");
        if (restrictList != null && restrictList.trim().length() > 0) {
            String[] restrict = restrictList.split(",");
            this.restricted.addAll(Arrays.asList(restrict));
        }

        // add user restricted list
        restrictList = WebloggerConfig.getProperty("rendering.weblogMapper.userProtectedUrls");
        if (restrictList != null && restrictList.trim().length() > 0) {
            String[] restrict = restrictList.split(",");
            this.restricted.addAll(Arrays.asList(restrict));
        }
    }

    public boolean handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // kinda silly, but we need to keep track of whether or not the url had
        // a trailing slash so that we can act accordingly
        boolean trailingSlash = false;

        String weblogHandle = null;
        String weblogLocale = null;
        String weblogRequestContext = null;
        String weblogRequestData = null;

        log.debug("evaluating [" + request.getRequestURI() + "]");

        // figure out potential weblog handle
        String servlet = request.getRequestURI();
        String pathInfo = null;

        if (servlet != null && servlet.trim().length() > 1) {

            if (request.getContextPath() != null)
                servlet = servlet.substring(request.getContextPath().length());

            // strip off the leading slash
            servlet = servlet.substring(1);

            // strip off trailing slash if needed
            if (servlet.endsWith("/")) {
                servlet = servlet.substring(0, servlet.length() - 1);
                trailingSlash = true;
            }

            if (servlet.indexOf("/") != -1) {
                weblogHandle = servlet.substring(0, servlet.indexOf("/"));
                pathInfo = servlet.substring(servlet.indexOf("/") + 1);
            } else {
                weblogHandle = servlet;
            }
        }

        log.debug("potential weblog handle = " + weblogHandle);

        // check if it's a valid weblog handle
        if (restricted.contains(weblogHandle) || !this.isWeblog(weblogHandle)) {
            log.debug("SKIPPED " + weblogHandle);
            return false;
        }

        String weblogAbsoluteURL = WebloggerConfig.getProperty("weblog.absoluteurl." + weblogHandle);
        if (weblogAbsoluteURL != null) {
            // An absolute URL is specified for this weblog, make sure request URL matches
            if (!request.getRequestURL().toString().startsWith(weblogAbsoluteURL)) {
                log.debug("SKIPPED " + weblogHandle);
                return false;
            }
        }

        log.debug("WEBLOG_URL " + request.getServletPath());

        // parse the rest of the url and build forward url
        if (pathInfo != null) {

            // parse the next portion of the url
            // we expect [locale/]<context>/<extra>/<info>
            String[] urlPath = pathInfo.split("/", 3);

            // if we have a locale, deal with it
            if (this.isLocale(urlPath[0])) {
                weblogLocale = urlPath[0];

                // no extra path info specified
                if (urlPath.length == 2) {
                    weblogRequestContext = urlPath[1];
                    weblogRequestData = null;

                    // request contains extra path info
                } else if (urlPath.length == 3) {
                    weblogRequestContext = urlPath[1];
                    weblogRequestData = urlPath[2];
                }

                // otherwise locale is empty
            } else {
                weblogLocale = null;
                weblogRequestContext = urlPath[0];

                // last part of request is extra path info
                if (urlPath.length == 2) {
                    weblogRequestData = urlPath[1];

                    // if we didn't have a locale then we have split too much
                    // so we reassemble the last 2 path elements together
                } else if (urlPath.length == 3) {
                    weblogRequestData = urlPath[1] + "/" + urlPath[2];
                }
            }

        }

        // special handling for trailing slash issue
        // we need this because by http standards the urls /foo and /foo/ are
        // supposed to be considered different, so we must enforce that
        if (weblogRequestContext == null && !trailingSlash) {
            // this means someone referred to a weblog index page with the 
            // shortest form of url /<weblog> or /<weblog>/<locale> and we need
            // to do a redirect to /<weblog>/ or /<weblog>/<locale>/
            String redirectUrl = request.getRequestURI() + "/";
            if (request.getQueryString() != null) {
                redirectUrl += "?" + request.getQueryString();
            }

            response.sendRedirect(redirectUrl);
            return true;

        } else if (weblogRequestContext != null && "tags".equals(weblogRequestContext)) {
            // tags section can have an index page at /<weblog>/tags/ and
            // a tags query at /<weblog>/tags/tag1+tag2, buth that's it
            if ((weblogRequestData == null && !trailingSlash) || (weblogRequestData != null && trailingSlash)) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND);
                return true;
            }
        } else if (weblogRequestContext != null && trailingSlash) {
            // this means that someone has accessed a weblog url and included
            // a trailing slash, like /<weblog>/entry/<anchor>/ which is not
            // supported, so we need to offer up a 404 Not Found
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return true;
        }

        // calculate forward url
        String forwardUrl = calculateForwardUrl(request, weblogHandle, weblogLocale, weblogRequestContext,
                weblogRequestData);

        // if we don't have a forward url then the request was invalid somehow
        if (forwardUrl == null) {
            return false;
        }

        // dispatch to forward url
        log.debug("forwarding to " + forwardUrl);
        RequestDispatcher dispatch = request.getRequestDispatcher(forwardUrl);
        dispatch.forward(request, response);

        // we dealt with this request ourselves, so return "true"
        return true;
    }

    /**
     * Convenience method for caculating the servlet forward url given a set
     * of information to make the decision with.
     *
     * handle is always assumed valid, all other params may be null.
     */
    private String calculateForwardUrl(HttpServletRequest request, String handle, String locale, String context,
            String data) {

        log.debug(handle + "," + locale + "," + context + "," + data);

        StringBuilder forwardUrl = new StringBuilder();

        // POST urls, like comment and trackback servlets
        if ("POST".equals(request.getMethod())) {
            // posting to permalink, this means comment or trackback
            if (context.equals("entry")) {
                // trackback requests are required to have an "excerpt" param
                if (request.getParameter("excerpt") != null) {

                    forwardUrl.append(TRACKBACK_SERVLET);
                    forwardUrl.append("/");
                    forwardUrl.append(handle);
                    if (locale != null) {
                        forwardUrl.append("/");
                        forwardUrl.append(locale);
                    }
                    forwardUrl.append("/");
                    forwardUrl.append(context);
                    if (data != null) {
                        forwardUrl.append("/");
                        forwardUrl.append(data);
                    }

                    // comment requests are required to have a "content" param
                } else if (request.getParameter("content") != null) {

                    forwardUrl.append(COMMENT_SERVLET);
                    forwardUrl.append("/");
                    forwardUrl.append(handle);
                    if (locale != null) {
                        forwardUrl.append("/");
                        forwardUrl.append(locale);
                    }
                    forwardUrl.append("/");
                    forwardUrl.append(context);
                    if (data != null) {
                        forwardUrl.append("/");
                        forwardUrl.append(data);
                    }
                }

            } else {
                // someone posting data where they aren't supposed to
                return null;
            }

        } else {
            // no context means weblog homepage
            if (context == null) {

                forwardUrl.append(PAGE_SERVLET);
                forwardUrl.append("/");
                forwardUrl.append(handle);
                if (locale != null) {
                    forwardUrl.append("/");
                    forwardUrl.append(locale);
                }

                // requests handled by PageServlet
            } else if (context.equals("page") || context.equals("entry") || context.equals("date")
                    || context.equals("category") || context.equals("tags")) {

                forwardUrl.append(PAGE_SERVLET);
                forwardUrl.append("/");
                forwardUrl.append(handle);
                if (locale != null) {
                    forwardUrl.append("/");
                    forwardUrl.append(locale);
                }
                forwardUrl.append("/");
                forwardUrl.append(context);
                if (data != null) {
                    forwardUrl.append("/");
                    forwardUrl.append(data);
                }

                // requests handled by FeedServlet
            } else if (context.equals("feed")) {

                forwardUrl.append(FEED_SERVLET);
                forwardUrl.append("/");
                forwardUrl.append(handle);
                if (locale != null) {
                    forwardUrl.append("/");
                    forwardUrl.append(locale);
                }
                if (data != null) {
                    forwardUrl.append("/");
                    forwardUrl.append(data);
                }

                // requests handled by ResourceServlet
            } else if (context.equals("resource")) {

                forwardUrl.append(RESOURCE_SERVLET);
                forwardUrl.append("/");
                forwardUrl.append(handle);
                if (data != null) {
                    forwardUrl.append("/");
                    forwardUrl.append(data);
                }

                // requests handled by MediaResourceServlet
            } else if (context.equals("mediaresource")) {

                forwardUrl.append(MEDIA_SERVLET);
                forwardUrl.append("/");
                forwardUrl.append(handle);
                if (data != null) {
                    forwardUrl.append("/");
                    forwardUrl.append(data);
                }

                // requests handled by SearchServlet
            } else if (context.equals("search")) {

                forwardUrl.append(SEARCH_SERVLET);
                forwardUrl.append("/");
                forwardUrl.append(handle);

                // requests handled by RSDServlet
            } else if (context.equals("rsd")) {

                forwardUrl.append(RSD_SERVLET);
                forwardUrl.append("/");
                forwardUrl.append(handle);

                // unsupported url
            } else {
                return null;
            }
        }

        log.debug("FORWARD_URL " + forwardUrl.toString());

        return forwardUrl.toString();
    }

    /**
     * convenience method which determines if the given string is a valid
     * weblog handle.
     *
     * TODO 3.0: some kind of caching
     */
    private boolean isWeblog(String potentialHandle) {

        log.debug("checking weblog handle " + potentialHandle);

        boolean isWeblog = false;

        try {
            Weblog weblog = WebloggerFactory.getWeblogger().getWeblogManager().getWeblogByHandle(potentialHandle);

            if (weblog != null) {
                isWeblog = true;
            }
        } catch (Exception ex) {
            // doesn't really matter to us why it's not a valid website
        }

        return isWeblog;
    }

    /**
     * Convenience method which determines if the given string is a valid
     * locale string.
     */
    private boolean isLocale(String potentialLocale) {

        boolean isLocale = false;

        // we only support 2 or 5 character locale strings, so check that first
        if (potentialLocale != null && (potentialLocale.length() == 2 || potentialLocale.length() == 5)) {

            // now make sure that the format is proper ... e.g. "en_US"
            // we are not going to be picky about capitalization
            String[] langCountry = potentialLocale.split("_");
            if (langCountry.length == 1 && langCountry[0] != null && langCountry[0].length() == 2) {
                isLocale = true;

            } else if (langCountry.length == 2 && langCountry[0] != null && langCountry[0].length() == 2
                    && langCountry[1] != null && langCountry[1].length() == 2) {

                isLocale = true;
            }
        }

        return isLocale;
    }

}