All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.agapsys.rcf.ActionDispatcher Maven / Gradle / Ivy

There is a newer version: 0.1.0-20170604
Show newest version
/*
 * Copyright 2015 Agapsys Tecnologia Ltda-ME.
 *
 * 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 com.agapsys.rcf;

import com.agapsys.rcf.exceptions.NotFoundException;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Pattern;
import javax.servlet.ServletException;

/**
 * Action dispatcher.
 *
 * The dispatcher is responsible by mapping request to actions.
 */
public class ActionDispatcher {

    // 
    // =========================================================================
    private static final String PATH_PATTERN = "^/\\*?$|^/([a-zA-Z]+[a-zA-Z0-9_]*)+(/\\*)?$";

    private static String __getWildcardPath(String path) {
        if (path.endsWith("/*"))
            return path;

        if (path.endsWith("/"))
            return path + "*";

        return path + "/*";
    }
    // =========================================================================
    // 

    private final Map> actionMap = new LinkedHashMap<>();

    /**
     * Registers an action with given URL.
     * @param method associated HTTP method.
     * @param path relative path associated with given action.
     * @param action action to be associated with given path and HTTP method.
     * @return this.
     */
    public synchronized ActionDispatcher registerAction(HttpMethod method, String path, Action action) {
        if (method == null)
            throw new IllegalArgumentException("Null method");

        if (!Pattern.matches(PATH_PATTERN, path))
            throw new IllegalArgumentException(String.format("Invalid path: '%s'", path));

        if (action == null)
            throw new IllegalArgumentException("Null action");

        Map methodMap = actionMap.get(method);

        if (methodMap == null) {
            methodMap = new LinkedHashMap<>();
            actionMap.put(method, methodMap);
        }

        if (methodMap.containsKey(path))
            throw new IllegalArgumentException(String.format("Mapping already exists: %s %s", method, path));

        methodMap.put(path, action);
        return this;
    }

    /** Removes all registered actions. */
    public synchronized void clearActions() {
        actionMap.clear();
    }

    /**
     * Dispatches a request to an action.
     *
     * @param request HTTP request.
     * @param response HTTP response.
     * @throws ServletException if the HTTP request cannot be handled.
     * @throws IOException if an input or output error occurs while the servlet is handling the HTTP request.
     * @throws NotFoundException if there is not action to process given request.
     */
    public synchronized void dispatch(ActionRequest request, ActionResponse response) throws ServletException, IOException, NotFoundException {
        String pathInfo = request.getPathInfo();
        int secondSlashIndex = pathInfo.indexOf("/", 1);

        String actionPath = secondSlashIndex == -1 ? pathInfo : pathInfo.substring(0, secondSlashIndex);

        Map methodMap = actionMap.get(request.getMethod());

        Action action;
        boolean usingWildcard = false;

        action = methodMap.get(actionPath);

        if (action == null) { // <-- Test for wildcard action
            action = methodMap.get(__getWildcardPath(actionPath));

            if (action != null) {
                usingWildcard = true;
            } else { // <-- Test for root-wildcard action
                action = methodMap.get("/*");

                if (action != null) {
                    actionPath = "/";
                    usingWildcard = true;
                }
            }
        }

        if (action == null)
            throw new NotFoundException();

        if (actionPath.equals(pathInfo) && !pathInfo.endsWith("/") && usingWildcard) { // <-- mapping: '/foo/*', uri: '/foo[?query=string]'. => redirects to '/foo/[?query=string]'
            String queryString = request.getQueryString();
            String redirectPath = request.getRequestUri() + "/";
            if (queryString != null)
                redirectPath = redirectPath + "?" + queryString;

            response.sendPermanentRedirect(redirectPath);
        } else {
            if (!usingWildcard && !pathInfo.equals(actionPath)) { // <-- mapping: '/foo', uri: '/foo/[?query=string]'. => redirects to '/foo[?query=string]'
                if (ActionRequest._getRelativePath(actionPath, pathInfo).equals("/")) {

                    String requestUri = request.getRequestUri();
                    String redirectPath = requestUri.substring(0, requestUri.length() - 1);
                    String queryString = request.getQueryString();
                    if (queryString != null)
                        redirectPath = redirectPath + "?" + queryString;

                    response.sendPermanentRedirect(redirectPath);
                } else {
                    throw new NotFoundException();
                }
            } else {
                if (!actionPath.equals("/")) {
                    request = new ActionRequest(actionPath, request);
                }

                beforeAction(request, response);
                action.processRequest(request, response);
                afterAction(request, response);
            }
        }
    }

    /**
     * Called before an action. Default implementation does nothing.
     *
     * @param request HTTP request.
     * @param response HTTP response.
     * @throws ServletException if the HTTP request cannot be handled.
     * @throws IOException if an input or output error occurs while the servlet is handling the HTTP request.
     */
    protected void beforeAction(ActionRequest request, ActionResponse response) throws ServletException, IOException {}

    /**
     * Called after an action processing. Default implementation does nothing.
     *
     * @param request HTTP request.
     * @param response HTTP response.
     * @throws ServletException if the HTTP request cannot be handled.
     * @throws IOException if an input or output error occurs while the servlet is handling the HTTP request.
     */
    protected void afterAction(ActionRequest request, ActionResponse response) throws ServletException, IOException {}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy