org.archive.wayback.webapp.AccessPoint.java Source code

Java tutorial

Introduction

Here is the source code for org.archive.wayback.webapp.AccessPoint.java

Source

/* WaybackContext
 *
 * $Id$
 *
 * Created on 5:37:31 PM Apr 20, 2007.
 *
 * Copyright (C) 2007 Internet Archive.
 *
 * This file is part of wayback-webapp.
 *
 * wayback-webapp is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * any later version.
 *
 * wayback-webapp is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser Public License for more details.
 *
 * You should have received a copy of the GNU Lesser Public License
 * along with wayback-webapp; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package org.archive.wayback.webapp;

import java.io.IOException;
import java.util.Locale;
import java.util.Properties;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.archive.wayback.ExceptionRenderer;
import org.archive.wayback.QueryRenderer;
import org.archive.wayback.ReplayDispatcher;
import org.archive.wayback.ReplayRenderer;
import org.archive.wayback.RequestParser;
import org.archive.wayback.ResultURIConverter;
import org.archive.wayback.WaybackConstants;
import org.archive.wayback.accesscontrol.ExclusionFilterFactory;
import org.archive.wayback.core.CaptureSearchResult;
import org.archive.wayback.core.CaptureSearchResults;
import org.archive.wayback.core.Resource;
import org.archive.wayback.core.SearchResults;
import org.archive.wayback.core.UIResults;
import org.archive.wayback.core.UrlSearchResults;
import org.archive.wayback.core.WaybackRequest;
import org.archive.wayback.exception.AuthenticationControlException;
import org.archive.wayback.exception.BaseExceptionRenderer;
import org.archive.wayback.exception.BetterRequestException;
import org.archive.wayback.exception.ResourceNotAvailableException;
import org.archive.wayback.exception.ResourceNotInArchiveException;
import org.archive.wayback.exception.WaybackException;
import org.archive.wayback.util.operator.BooleanOperator;
import org.springframework.beans.factory.BeanNameAware;

/**
 * Retains all information about a particular Wayback configuration
 * within a ServletContext, including holding references to the
 * implementation instances of the primary Wayback classes:
 * 
 *       RequestParser
 *      ResourceIndex(via WaybackCollection)
 *      ResourceStore(via WaybackCollection)
 *      QueryRenderer
 *      ReplayDispatcher
 *      ExceptionRenderer
 *      ResultURIConverter
 *
 *
 * @author brad
 * @version $Date$, $Revision$
 */
public class AccessPoint implements RequestContext, BeanNameAware {

    private static final Logger LOGGER = Logger.getLogger(AccessPoint.class.getName());

    private boolean useServerName = false;
    private boolean useAnchorWindow = false;
    private boolean exactSchemeMatch = true;

    private int contextPort = 0;
    private String contextName = null;
    private String beanName = null;
    private WaybackCollection collection = null;
    private ReplayDispatcher replay = null;
    private ExceptionRenderer exception = new BaseExceptionRenderer();
    private QueryRenderer query = null;
    private RequestParser parser = null;
    private ResultURIConverter uriConverter = null;
    private Properties configs = null;
    private ExclusionFilterFactory exclusionFactory = null;
    private BooleanOperator<WaybackRequest> authentication = null;
    private String urlRoot = null;
    private Locale locale = null;

    /**
     * @return the contextName
     */
    public String getContextName() {
        return contextName;
    }

    /**
     * @return the replay
     */
    public ReplayDispatcher getReplay() {
        return replay;
    }

    /**
     * @return the query
     */
    public QueryRenderer getQuery() {
        return query;
    }

    /**
     * @return the parser
     */
    public RequestParser getParser() {
        return parser;
    }

    /**
     * @return the uriConverter
     */
    public ResultURIConverter getUriConverter() {
        return uriConverter;
    }

    public Locale getLocale() {
        return locale;
    }

    public void setLocale(Locale locale) {
        this.locale = locale;
    }

    /**
     * 
     */
    public AccessPoint() {

    }

    /* (non-Javadoc)
     * @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String)
     */
    public void setBeanName(String beanName) {
        this.beanName = beanName;
        this.contextName = "";
        int idx = beanName.indexOf(":");
        if (idx > -1) {
            contextPort = Integer.valueOf(beanName.substring(0, idx));
            contextName = beanName.substring(idx + 1);
        } else {
            try {
                this.contextPort = Integer.valueOf(beanName);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }
    }

    public String getBeanName() {
        return beanName;
    }

    /**
     * @param httpRequest
     * @return the prefix of paths recieved by this server that are handled by
     * this WaybackContext, including the trailing '/'
     */
    public String getContextPath(HttpServletRequest httpRequest) {
        //      if(contextPort != 0) {
        //         return httpRequest.getContextPath();
        //      }
        String httpContextPath = httpRequest.getContextPath();
        if (contextName.length() == 0) {
            return httpContextPath + "/";
        }
        return httpContextPath + "/" + contextName + "/";
    }

    /**
     * @param httpRequest
     * @param includeQuery
     * @return the portion of the request following the path to this context
     * without leading '/'
     */
    private String translateRequest(HttpServletRequest httpRequest, boolean includeQuery) {

        String origRequestPath = httpRequest.getRequestURI();
        if (includeQuery) {
            String queryString = httpRequest.getQueryString();
            if (queryString != null) {
                origRequestPath += "?" + queryString;
            }
        }
        String contextPath = getContextPath(httpRequest);
        if (!origRequestPath.startsWith(contextPath)) {
            if (contextPath.startsWith(origRequestPath)) {
                // missing trailing '/', just omit:
                return "";
            }
            return null;
        }
        return origRequestPath.substring(contextPath.length());
    }

    /**
     * @param httpRequest
     * @return the portion of the request following the path to this context, 
     * including any query information,without leading '/'
     */
    public String translateRequestPathQuery(HttpServletRequest httpRequest) {
        return translateRequest(httpRequest, true);
    }

    /**
     * @param httpRequest
     * @return the portion of the request following the path to this context, 
     * excluding any query information, without leading '/'
     */
    public String translateRequestPath(HttpServletRequest httpRequest) {
        return translateRequest(httpRequest, false);
    }

    /**
     * Construct an absolute URL that points to the root of the context that
     * recieved the request, including a trailing "/".
     * 
     * @return String absolute URL pointing to the Context root where the
     *         request was revieved.
     */
    private String getAbsoluteContextPrefix(HttpServletRequest httpRequest, boolean useRequestServer) {

        StringBuilder prefix = new StringBuilder();
        prefix.append(WaybackConstants.HTTP_URL_PREFIX);
        String waybackPort = null;
        if (useRequestServer) {
            prefix.append(httpRequest.getLocalName());
            waybackPort = String.valueOf(httpRequest.getLocalPort());
        } else {
            prefix.append(httpRequest.getServerName());
            waybackPort = String.valueOf(httpRequest.getServerPort());
        }
        if (!waybackPort.equals(WaybackConstants.HTTP_DEFAULT_PORT)) {
            prefix.append(":").append(waybackPort);
        }
        String contextPath = getContextPath(httpRequest);
        prefix.append(contextPath);
        return prefix.toString();
    }

    /**
     * @param httpRequest
     * @return absolute URL pointing to the base of this WaybackContext, using
     * Server and port information from the HttpServletRequest argument.
     */
    public String getAbsoluteServerPrefix(HttpServletRequest httpRequest) {
        return getAbsoluteContextPrefix(httpRequest, true);
    }

    /**
     * @param httpRequest
     * @return absolute URL pointing to the base of this WaybackContext, using
     * Canonical server and port information.
     */
    public String getAbsoluteLocalPrefix(HttpServletRequest httpRequest) {
        if (urlRoot != null) {
            return urlRoot;
        }
        return getAbsoluteContextPrefix(httpRequest, useServerName);
    }

    private boolean dispatchLocal(HttpServletRequest httpRequest, HttpServletResponse httpResponse)
            throws ServletException, IOException {

        String translated = "/" + translateRequestPathQuery(httpRequest);

        WaybackRequest wbRequest = new WaybackRequest();
        wbRequest.setContextPrefix(getAbsoluteLocalPrefix(httpRequest));
        wbRequest.setAccessPoint(this);
        wbRequest.fixup(httpRequest);
        UIResults uiResults = new UIResults(wbRequest, uriConverter);
        try {
            uiResults.forward(httpRequest, httpResponse, translated);
            return true;
        } catch (IOException e) {
            // TODO: figure out if we got IO because of a missing dispatcher
        }
        return false;
    }

    /**
     * @param httpRequest
     * @param httpResponse
     * @return true if the request was actually handled
     * @throws ServletException
     * @throws IOException
     */
    public boolean handleRequest(HttpServletRequest httpRequest, HttpServletResponse httpResponse)
            throws ServletException, IOException {

        WaybackRequest wbRequest = null;
        boolean handled = false;

        try {
            wbRequest = parser.parse(httpRequest, this);

            if (wbRequest != null) {
                handled = true;

                // TODO: refactor this code into RequestParser implementations
                wbRequest.setAccessPoint(this);
                wbRequest.setContextPrefix(getAbsoluteLocalPrefix(httpRequest));
                wbRequest.fixup(httpRequest);
                // end of refactor

                if (authentication != null) {
                    if (!authentication.isTrue(wbRequest)) {
                        throw new AuthenticationControlException("Not authorized");
                    }
                }

                if (exclusionFactory != null) {
                    wbRequest.setExclusionFilter(exclusionFactory.get());
                }
                // TODO: refactor this into RequestParser implementations, so a
                // user could alter requests to change the behavior within a
                // single AccessPoint. For now, this is a simple way to expose
                // the feature to configuration.
                wbRequest.setExactScheme(exactSchemeMatch);

                if (wbRequest.isReplayRequest()) {

                    handleReplay(wbRequest, httpRequest, httpResponse);

                } else {

                    handleQuery(wbRequest, httpRequest, httpResponse);
                }
            } else {
                handled = dispatchLocal(httpRequest, httpResponse);
            }

        } catch (BetterRequestException e) {
            httpResponse.sendRedirect(e.getBetterURI());
            handled = true;

        } catch (WaybackException e) {
            logNotInArchive(e, wbRequest);
            exception.renderException(httpRequest, httpResponse, wbRequest, e, uriConverter);
        }

        return handled;
    }

    private void handleReplay(WaybackRequest wbRequest, HttpServletRequest httpRequest,
            HttpServletResponse httpResponse) throws IOException, ServletException, WaybackException {
        Resource resource = null;
        try {
            SearchResults results = collection.getResourceIndex().query(wbRequest);
            if (!(results instanceof CaptureSearchResults)) {
                throw new ResourceNotAvailableException("Bad results...");
            }
            CaptureSearchResults captureResults = (CaptureSearchResults) results;

            // TODO: check which versions are actually accessible right now?
            CaptureSearchResult closest = captureResults.getClosest(wbRequest, useAnchorWindow);
            resource = collection.getResourceStore().retrieveResource(closest);
            ReplayRenderer renderer = replay.getRenderer(wbRequest, closest, resource);
            renderer.renderResource(httpRequest, httpResponse, wbRequest, closest, resource, uriConverter,
                    captureResults);
        } finally {
            if (resource != null) {
                resource.close();
            }
        }
    }

    private void handleQuery(WaybackRequest wbRequest, HttpServletRequest httpRequest,
            HttpServletResponse httpResponse) throws ServletException, IOException, WaybackException {

        SearchResults results = collection.getResourceIndex().query(wbRequest);
        if (results instanceof CaptureSearchResults) {
            CaptureSearchResults cResults = (CaptureSearchResults) results;
            cResults.markClosest(wbRequest);
            query.renderCaptureResults(httpRequest, httpResponse, wbRequest, cResults, uriConverter);

        } else if (results instanceof UrlSearchResults) {
            UrlSearchResults uResults = (UrlSearchResults) results;
            query.renderUrlResults(httpRequest, httpResponse, wbRequest, uResults, uriConverter);
        } else {
            throw new WaybackException("Unknown index format");
        }
    }

    public void shutdown() throws IOException {
        if (collection != null) {
            collection.shutdown();
        }
    }

    private void logNotInArchive(WaybackException e, WaybackRequest r) {
        // TODO: move this into ResourceNotInArchiveException constructor
        if (e instanceof ResourceNotInArchiveException) {
            String url = r.getRequestUrl();
            StringBuilder sb = new StringBuilder(100);
            sb.append("NotInArchive\t");
            sb.append(contextName).append("\t");
            sb.append(contextPort).append("\t");
            sb.append(url);

            LOGGER.info(sb.toString());
        }
    }

    /**
     * @param contextPort the contextPort to set
     */
    public void setContextPort(int contextPort) {
        this.contextPort = contextPort;
    }

    /**
     * @param contextName the contextName to set
     */
    public void setContextName(String contextName) {
        this.contextName = contextName;
    }

    /**
     * @param replay the replay to set
     */
    public void setReplay(ReplayDispatcher replay) {
        this.replay = replay;
    }

    /**
     * @param query the query to set
     */
    public void setQuery(QueryRenderer query) {
        this.query = query;
    }

    /**
     * @param parser the parser to set
     */
    public void setParser(RequestParser parser) {
        this.parser = parser;
    }

    /**
     * @param uriConverter the uriConverter to set
     */
    public void setUriConverter(ResultURIConverter uriConverter) {
        this.uriConverter = uriConverter;
    }

    /**
     * @return the contextPort
     */
    public int getContextPort() {
        return contextPort;
    }

    /**
     * @return the configs
     */
    public Properties getConfigs() {
        return configs;
    }

    /**
     * @param configs the configs to set
     */
    public void setConfigs(Properties configs) {
        this.configs = configs;
    }

    /**
     * @return the useServerName
     */
    public boolean isUseServerName() {
        return useServerName;
    }

    /**
     * @param useServerName the useServerName to set
     */
    public void setUseServerName(boolean useServerName) {
        this.useServerName = useServerName;
    }

    /**
     * @return the useAnchorWindow
     */
    public boolean isUseAnchorWindow() {
        return useAnchorWindow;
    }

    /**
     * @param useAnchorWindow the useAnchorWindow to set
     */
    public void setUseAnchorWindow(boolean useAnchorWindow) {
        this.useAnchorWindow = useAnchorWindow;
    }

    /**
     * @return the exactSchemeMatch
     */
    public boolean isExactSchemeMatch() {
        return exactSchemeMatch;
    }

    /**
     * @param exactSchemeMatch the exactSchemeMatch to set
     */
    public void setExactSchemeMatch(boolean exactSchemeMatch) {
        this.exactSchemeMatch = exactSchemeMatch;
    }

    public ExclusionFilterFactory getExclusionFactory() {
        return exclusionFactory;
    }

    public void setExclusionFactory(ExclusionFilterFactory exclusionFactory) {
        this.exclusionFactory = exclusionFactory;
    }

    public BooleanOperator<WaybackRequest> getAuthentication() {
        return authentication;
    }

    public void setAuthentication(BooleanOperator<WaybackRequest> authentication) {
        this.authentication = authentication;
    }

    public WaybackCollection getCollection() {
        return collection;
    }

    public void setCollection(WaybackCollection collection) {
        this.collection = collection;
    }

    public ExceptionRenderer getException() {
        return exception;
    }

    public void setException(ExceptionRenderer exception) {
        this.exception = exception;
    }

    public String getUrlRoot() {
        return urlRoot;
    }

    public void setUrlRoot(String urlRoot) {
        this.urlRoot = urlRoot;
    }
}