dk.netarkivet.viewerproxy.distribute.HTTPControllerServer.java Source code

Java tutorial

Introduction

Here is the source code for dk.netarkivet.viewerproxy.distribute.HTTPControllerServer.java

Source

/*
 * #%L
 * Netarchivesuite - harvester
 * %%
 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library,
 *             the National Library of France and the Austrian National Library.
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 2.1 of the
 * License, or (at your option) any later version.
 * 
 * This program 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 General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
 * #L%
 */

package dk.netarkivet.viewerproxy.distribute;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

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

import dk.netarkivet.common.exceptions.ArgumentNotValid;
import dk.netarkivet.common.exceptions.IOFailure;
import dk.netarkivet.common.utils.StringUtils;
import dk.netarkivet.viewerproxy.CommandResolver;
import dk.netarkivet.viewerproxy.Controller;
import dk.netarkivet.viewerproxy.Request;
import dk.netarkivet.viewerproxy.Response;
import dk.netarkivet.viewerproxy.URIResolver;

/**
 * Wrapper for an URIResolver, which calls the controller methods on given specific URLs, and forwards all others to the
 * wrapped handler. This allows you to access control methods by giving specific urls to this class.
 */
public class HTTPControllerServer extends CommandResolver {
    /**
     * The controller to call methods on.
     */
    private Controller c;
    /** Logger for this class. */
    private Log log = LogFactory.getLog(getClass().getName());

    /** Command for starting url collection. */
    static final String START_COMMAND = "/startRecordingURIs";
    /** Command for stopping url collection. */
    static final String STOP_COMMAND = "/stopRecordingURIs";
    /** Command for clearing collected urls. */
    static final String CLEAR_COMMAND = "/clearRecordedURIs";
    /** Command for getting collected urls. */
    static final String GET_RECORDED_URIS_COMMAND = "/getRecordedURIs";
    /** Command for changing index. */
    static final String CHANGE_INDEX_COMMAND = "/changeIndex";
    /** Command for getting status. */
    static final String GET_STATUS_COMMAND = "/getStatus";

    /**
     * Parameter defining the url to return to after doing start, stop, clear, or changeIndex.
     */
    static final String RETURN_URL_PARAMETER = "returnURL";
    /** Parameter for ids of jobs to change index to. May be repeated. */
    static final String JOB_ID_PARAMETER = "jobID";
    /** Parameter for label of an index. */
    static final String INDEX_LABEL_PARAMETER = "label";
    /** Parameter for locale to generate status. */
    static final String LOCALE_PARAMETER = "locale";

    /** Http header for location. */
    private static final String LOCATION_HEADER = "Location";
    /** Http header for content type. */
    private static final String CONTENT_TYPE_HEADER = "Content-Type";
    /** Http header value for content type text. */
    private static final String TEXT_PLAIN_MIMETYPE = "text/plain; charset=UTF-8";

    /** Http response code for redirect. */
    private static final int REDIRECT_RESPONSE_CODE = 303;
    /** Http response code for OK. */
    private static final int OK_RESPONSE_CODE = 200;

    /**
     * Make a new HTTPControllerServer, which calls commands on the given controller, and forwards all other requests to
     * the given URIResolver.
     *
     * @param c The controller which handles commands given in command URLs.
     * @param ur The URIResolver to handle all other uris.
     * @throws ArgumentNotValid if either argument is null.
     */
    public HTTPControllerServer(Controller c, URIResolver ur) {
        super(ur);
        ArgumentNotValid.checkNotNull(c, "Controller c");
        this.c = c;
    }

    /**
     * Handles parsing of the URL and delegating to relevant methods. The commands are of the form
     * http://<<localhostname>>/<<command>>?<<param>>=<<value>>* Known commands are the following: start - params:
     * returnURL - effect: start url collection return to returnURL stop - params: returnURL - effect: stop url
     * collection return to returnURL clear - params: returnURL - effect: clear url collection return to returnURL
     * getRecordedURIs - params: none - effect: write url collection to response changeIndex - params: jobID*, - effect:
     * generate index for jobs, returnURL return to returnURL getStatus - params: locale - effect: write status to
     * response.
     *
     * @param request The request to check
     * @param response The response to give command results to if it is a command. If the request is one of these
     * commands, the response code is set to 303 if page is redirected to return url; 200 if command url returns data;
     * otherwise whatever is returned by the wrapped resolver
     * @return Whether this was a command URL
     */
    protected boolean executeCommand(Request request, Response response) {
        // If the url is for this host (potential command)
        if (isCommandHostRequest(request)) {
            log.debug("Executing command " + request.getURI());
            // get path
            String path = request.getURI().getPath();
            if (path.equals(START_COMMAND)) {
                doStartRecordingURIs(request, response);
                return true;
            }
            if (path.equals(STOP_COMMAND)) {
                doStopRecordingURIs(request, response);
                return true;
            }
            if (path.equals(CLEAR_COMMAND)) {
                doClearRecordedURIs(request, response);
                return true;
            }
            if (path.equals(GET_RECORDED_URIS_COMMAND)) {
                doGetRecordedURIs(request, response);
                return true;
            }
            if (path.equals(CHANGE_INDEX_COMMAND)) {
                doChangeIndex(request, response);
                return true;
            }
            if (path.equals(GET_STATUS_COMMAND)) {
                doGetStatus(request, response);
                return true;
            }
        }
        return false;
    }

    /**
     * Check parameter map for exactly the parameter names given. If any are missing , throws IOFailure naming the
     * expected parameters.
     *
     * @param request The request to check parameters in
     * @param parameterNames The parameters to check for.
     * @throws IOFailure on missing parameters.
     */
    private void checkParameters(Request request, String... parameterNames) {
        for (String parameter : parameterNames) {
            if (!request.getParameterMap().containsKey(parameter)) {
                throw new IOFailure("Bad request: '" + request.getURI() + "':\n" + "Wrong parameters. Expected: "
                        + StringUtils.conjoin(",", parameterNames));
            }
        }
    }

    /**
     * Helper method to handle start command.
     *
     * @param request The HTTP request we are working on
     * @param response Response to handle result
     */
    private void doStartRecordingURIs(Request request, Response response) {
        setReturnResponseFromParameter(response, request);
        c.startRecordingURIs();
    }

    /**
     * Helper method to handle stop command.
     *
     * @param request The HTTP request we are working on
     * @param response Response to handle result
     */
    private void doStopRecordingURIs(Request request, Response response) {
        setReturnResponseFromParameter(response, request);
        c.stopRecordingURIs();
    }

    /**
     * Helper method to handle clear command.
     *
     * @param request The HTTP request we are working on
     * @param response Response to handle result
     */
    private void doClearRecordedURIs(Request request, Response response) {
        setReturnResponseFromParameter(response, request);
        c.clearRecordedURIs();
    }

    /**
     * Helper method to handle getRecordedURIs command.
     *
     * @param request The HTTP request we are working on
     * @param response Response to handle result
     */
    private void doGetRecordedURIs(Request request, Response response) {
        checkParameters(request);
        Set<URI> uris = c.getRecordedURIs();
        response.addHeaderField(CONTENT_TYPE_HEADER, TEXT_PLAIN_MIMETYPE);
        OutputStream os = response.getOutputStream();
        try {
            for (URI recordedUri : uris) {
                os.write(recordedUri.toString().getBytes());
                os.write('\n');
            }
        } catch (IOException e) {
            throw new IOFailure("Error trying to write missing " + "uris to http response!", e);
        }
        response.setStatus(OK_RESPONSE_CODE);
    }

    /**
     * Helper method to handle changeIndex command.
     *
     * @param request The HTTP request we are working on
     * @param response Response to handle result
     */
    private void doChangeIndex(Request request, Response response) {
        checkParameters(request, JOB_ID_PARAMETER);
        setReturnResponseFromParameter(response, request);
        String[] jobIDStrings = request.getParameterMap().get(JOB_ID_PARAMETER);
        Set<Long> jobIDs = new HashSet<Long>();
        for (String jobIDString : jobIDStrings) {
            try {
                jobIDs.add(Long.parseLong(jobIDString));
            } catch (NumberFormatException e) {
                log.debug("Ignoring illegal job ID in change index " + "command for uri '" + request.getURI() + "'",
                        e);
            }
        }
        String label = getParameter(request, INDEX_LABEL_PARAMETER);
        c.changeIndex(jobIDs, label);
    }

    /**
     * Helper method to handle getStatus command.
     *
     * @param request The HTTP request we are working on
     * @param response Response to handle result
     */
    private void doGetStatus(Request request, Response response) {
        String localeString = getParameter(request, LOCALE_PARAMETER);
        response.addHeaderField(CONTENT_TYPE_HEADER, TEXT_PLAIN_MIMETYPE);
        OutputStream os = response.getOutputStream();
        try {
            os.write(c.getStatus(new Locale(localeString)).getBytes());
        } catch (IOException e) {
            throw new IOFailure("Error trying to write status " + "to http response!", e);
        }
        response.setStatus(OK_RESPONSE_CODE);
    }

    /**
     * Set up the appropriate headers and return code for doing a redirect to the URL given by the returnURL parameter.
     *
     * @param response The response to set to be a redirect
     * @param request The request to read the returnURL parameter from
     */
    private void setReturnResponseFromParameter(Response response, Request request) {
        String returnURL = getParameter(request, RETURN_URL_PARAMETER);
        response.addHeaderField(LOCATION_HEADER, returnURL);
        response.setStatus(REDIRECT_RESPONSE_CODE);
    }

    /**
     * Get a single parameter out of a request.
     *
     * @param request A request to look up parameters in.
     * @param parameterName The name of the parameter to look up.
     * @return The value of one instance of the parameter in the request. If more than one instance exists, an arbitrary
     * one is picked.
     * @throws IOFailure if the parameter is not given.
     */
    private String getParameter(Request request, String parameterName) {
        checkParameters(request, parameterName);
        String localeString = request.getParameterMap().get(parameterName)[0];
        return localeString;
    }
}