org.hdiv.urlProcessor.AbstractUrlProcessor.java Source code

Java tutorial

Introduction

Here is the source code for org.hdiv.urlProcessor.AbstractUrlProcessor.java

Source

/**
 * Copyright 2005-2012 hdiv.org
 *
 * 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 org.hdiv.urlProcessor;

import java.util.Arrays;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hdiv.config.HDIVConfig;
import org.hdiv.util.Constants;
import org.hdiv.util.HDIVUtil;
import org.springframework.util.Assert;

/**
 * This class contains methods to process urls.
 * 
 * @author Gotzon Illarramendi
 * @since HDIV 2.1.0
 */
public abstract class AbstractUrlProcessor {

    /**
     * Commons Logging instance.
     */
    private static Log log = LogFactory.getLog(AbstractUrlProcessor.class);

    /**
     * Hdiv configuration.
     */
    private HDIVConfig config;

    /**
     * Create a new instance of {@link UrlData}.
     * 
     * @param url
     *            original url
     * @param isFormUrl
     *            is form or link url?
     * @param request
     *            {@link HttpServletRequest} object
     * @return new instance of {@link UrlData}
     */
    public UrlData createUrlData(String url, boolean isFormUrl, HttpServletRequest request) {

        Assert.notNull(this.config);

        UrlData urlData = new UrlData(url, isFormUrl);

        // Extract the anchor
        if (url.indexOf('#') >= 0) {
            String anchor = url.substring(url.indexOf('#') + 1);
            urlData.setAnchor(anchor);

            url = url.substring(0, url.indexOf('#'));
        }

        // Remove parameters
        if (url.indexOf("?") > 0) {
            String urlParams = url.substring(url.indexOf("?") + 1);
            Map ulrParamsMap = this.getUrlParamsAsMap(request, urlParams);
            urlData.setOriginalUrlParams(ulrParamsMap);
            url = url.substring(0, url.indexOf("?"));

        }

        // Extract protocol, domain and server if exist
        String serverUrl = this.getServerFromUrl(url);
        if (serverUrl != null && serverUrl.length() > 0) {
            urlData.setServer(serverUrl);

            // Remove server and port
            url = url.replaceFirst(serverUrl, "");
        }

        // Detect if the url points to actual app
        boolean internal = this.isInternalUrl(request, url, urlData);
        urlData.setInternal(internal);

        // Remove jsessionid
        url = this.stripSession(url);

        // Calculate contextPath beginning url
        String contextPathRelativeUrl = this.getContextPathRelative(request, url);
        urlData.setContextPathRelativeUrl(contextPathRelativeUrl);

        // Calculate url without the context path for later processing
        if (contextPathRelativeUrl.startsWith(request.getContextPath())) {
            String urlWithoutContextPath = contextPathRelativeUrl.substring(request.getContextPath().length());
            urlData.setUrlWithoutContextPath(urlWithoutContextPath);
        } else {
            // If contextPath is not present, the relative url is out of application
            urlData.setInternal(false);
        }

        return urlData;

    }

    /**
     * Generates a Map with request parameter name and values.
     * 
     * @param request
     *            {@link HttpServletRequest} object
     * @param urlParams
     *            urls query string
     * @return Map
     */
    protected Map getUrlParamsAsMap(HttpServletRequest request, String urlParams) {

        Map params = new LinkedHashMap();

        if (urlParams == null) {
            return params;
        }

        String value = urlParams.replaceAll("&", "&");

        String hdivParameter = (String) request.getSession().getAttribute(Constants.HDIV_PARAMETER);

        StringTokenizer st = new StringTokenizer(value, "&");
        while (st.hasMoreTokens()) {
            String token = st.nextToken();
            int index = token.indexOf("=");
            String param = "";
            String val = "";
            if (index > -1) {
                param = token.substring(0, index);
                val = token.substring(index + 1);
            } else {
                param = token;
            }

            // Ignore Hdiv state parameter
            if (!param.equals(hdivParameter)) {
                // Add value to array or create it
                String[] values = (String[]) params.get(param);
                if (values == null) {
                    values = new String[] { val };
                } else {
                    int l = values.length;
                    values = (String[]) Arrays.copyOf(values, l + 1);
                    values[l] = val;
                }
                params.put(param, values);
            }
        }

        return params;
    }

    /**
     * Determines if the url is a startPage
     * 
     * @return boolean is startPage?
     */
    protected boolean isStartPage(UrlData urlData) {

        // If this is a start page, don't compose
        if (this.config.isStartPage(urlData.getContextPathRelativeUrl(), urlData.isFormUrl())) {
            return true;
        }

        // If the url contains the context path and is a start page, don't
        // compose
        if (this.config.isStartPage(urlData.getUrlWithoutContextPath(), urlData.isFormUrl())) {
            return true;
        }

        return false;
    }

    /**
     * Generate a url with all parameters and include hdiv state parameter.
     * 
     * @param request
     *            {@link HttpServletRequest} object
     * @param urlData
     *            url data object
     * @param stateParam
     *            hdiv state parameter value
     * @return complete url
     */
    public String getProcessedUrlWithHdivState(HttpServletRequest request, UrlData urlData, String stateParam) {

        String hdivParameter = (String) request.getSession().getAttribute(Constants.HDIV_PARAMETER);

        // obtain url with parameters
        String url = this.getParamProcessedUrl(urlData);

        if (stateParam == null || stateParam.length() <= 0) {
            return url;
        }

        String separator = (urlData.containsParams()) ? "&" : "?";

        StringBuffer sb = new StringBuffer();
        sb.append(url).append(separator).append(hdivParameter).append("=").append(stateParam);

        url = appendAnchor(sb.toString(), urlData.getAnchor());

        return url;
    }

    /**
     * Generate a url with all parameters.
     * 
     * @param urlData
     *            url data object
     * @return complete url
     */
    public String getParamProcessedUrl(UrlData urlData) {

        Map params = null;
        if (urlData.getProcessedUrlParams() != null) {
            params = urlData.getProcessedUrlParams();
        } else {
            params = urlData.getOriginalUrlParams();
        }

        StringBuffer sb = new StringBuffer();
        if (urlData.getServer() != null) {
            sb.append(urlData.getServer());
        }
        sb.append(urlData.getContextPathRelativeUrl());

        if (params == null || params.size() == 0) {
            return sb.toString();
        }

        String separator = "?";

        Iterator it = params.keySet().iterator();
        while (it.hasNext()) {
            String key = (String) it.next();
            String[] values = (String[]) params.get(key);

            for (int i = 0; i < values.length; i++) {
                String value = values[i];
                sb.append(separator).append(key).append("=").append(value);
                if (separator.equals("?")) {
                    separator = "&";
                }
            }
        }

        return sb.toString();
    }

    /**
     * Generate final url with all parameters and anchor.
     * 
     * @param urlData
     *            url data object
     * @return complete url
     */
    public String getProcessedUrl(UrlData urlData) {

        String url = this.getParamProcessedUrl(urlData);

        url = appendAnchor(url, urlData.getAnchor());
        return url;
    }

    /**
     * Append anchor to url if constains any.
     * 
     * @param url
     *            url
     * @param anchor
     *            anchor
     * @return url with the anchor
     */
    protected String appendAnchor(String url, String anchor) {
        if (anchor != null) {
            // it could be ""
            StringBuffer sb = new StringBuffer(url);
            sb.append("#").append(anchor);
            url = sb.toString();
        }
        return url;
    }

    /**
     * Determines if Hdiv state is necessary for the url.
     * 
     * @param urlData
     *            url data object
     * @return is necessary?
     */
    public boolean isHdivStateNecessary(UrlData urlData) {

        if (urlData.getOriginalUrl().startsWith("javascript:")) {
            return false;
        }

        if (!urlData.isInternal()) {
            return false;
        }

        boolean startPage = this.isStartPage(urlData);
        if (startPage) {
            return false;
        }

        if (this.hasExtensionToExclude(urlData)) {
            return false;
        }

        boolean validateParamLessUrls = this.config.isValidationInUrlsWithoutParamsActivated();
        // if url is a link (not a form action) and has not got parameters, we do not have to include HDIV's state
        if (!urlData.isFormUrl() && !validateParamLessUrls && !urlData.containsParams()) {
            return false;
        }

        return true;

    }

    /**
     * Detects if the url points to this application
     * 
     * @param request
     *            {@link HttpServletRequest} object
     * @param url
     *            request url
     * @param urlData
     *            url data
     * @return is internal?
     */
    protected boolean isInternalUrl(HttpServletRequest request, String url, UrlData urlData) {

        if (urlData.getServer() != null) {
            // URL is absolute: http://...

            String serverName = request.getServerName();
            if (!urlData.getServer().contains(serverName)) {
                // http://www.google.com
                return false;
            }

            if (url.startsWith(request.getContextPath() + "/") || url.equals(request.getContextPath())) {
                // http://localhost:8080/APP/... or
                // http://localhost:8080/APP
                return true;
            }
            // http://localhost:8080/anotherApplication... or
            return false;

        } else {
            if (url.startsWith(request.getContextPath() + "/") || url.equals(request.getContextPath())) {
                // url of type /APP/... or /APP
                return true;
            } else if (url.startsWith("/")) {
                // url of type /anotherApplication/...
                return false;
            } else {
                // url of type section/action...
                return true;
            }
        }
    }

    /**
     * Returns from url the part related with the server side in an absolute url.
     * 
     * @param url
     *            absolute url
     * @return url protocol, domain and port
     */
    protected String getServerFromUrl(String url) {

        int pos = url.indexOf("://");
        if (pos > 0) {
            int posicion = url.indexOf("/", pos + 3);
            if (posicion > 0) {
                url = url.substring(0, posicion);
                return url;
            } else {
                return url;
            }
        }
        return null;
    }

    /**
     * Determines if the url contains a extension to exclude for Hdiv state inclusion.
     * 
     * @param urlData
     *            url data object
     * @return is excluded or not
     */
    protected boolean hasExtensionToExclude(UrlData urlData) {
        String contextPathRelativeUrl = urlData.getContextPathRelativeUrl();
        if (contextPathRelativeUrl.charAt(contextPathRelativeUrl.length() - 1) == '/') {
            return false;
        }

        List excludedExtensions = this.config.getExcludedURLExtensions();

        if (excludedExtensions != null) {

            for (Iterator iter = excludedExtensions.iterator(); iter.hasNext();) {

                String extension = (String) iter.next();
                if (contextPathRelativeUrl.endsWith(extension)) {
                    return true;
                }
            }
        }

        Hashtable protectedExtension = this.config.getProtectedURLPatterns();

        // jsp is always protected
        if (contextPathRelativeUrl.endsWith(".jsp")) {
            return false;
        }

        if (protectedExtension != null) {

            for (Enumeration extensionsIds = protectedExtension.elements(); extensionsIds.hasMoreElements();) {

                Pattern extensionPattern = (Pattern) extensionsIds.nextElement();
                Matcher m = extensionPattern.matcher(contextPathRelativeUrl);

                if (m.matches()) {
                    return false;
                }
            }
        }

        return (!contextPathRelativeUrl.startsWith("/")) && (contextPathRelativeUrl.indexOf(".") == -1);
    }

    /**
     * Composes the url starting with context path. Removes any relative url.
     * 
     * @param request
     *            {@link HttpServletRequest} object
     * @param url
     *            url
     * @return url starting with context path
     */
    protected String getContextPathRelative(HttpServletRequest request, String url) {

        String returnValue = null;

        // Base url defined by <base> tag in some frameworks
        String baseUrl = HDIVUtil.getBaseURL(request);
        if (baseUrl != null) {
            // Remove server part from the url
            String serverUrl = this.getServerFromUrl(baseUrl);
            if (serverUrl != null && serverUrl.length() > 0) {
                // Remove server and port
                baseUrl = baseUrl.replaceFirst(serverUrl, "");
            }
        } else {
            // Original RequestUri before Jsp processing
            baseUrl = HDIVUtil.getRequestURI(request);
        }

        if (url.equals("")) {
            return baseUrl;
        } else if (url.startsWith("/")) {
            returnValue = url;
        } else if (url.startsWith("..")) {
            returnValue = url;
        } else {
            // relative path
            String uri = baseUrl;
            uri = uri.substring(uri.indexOf("/"), uri.lastIndexOf("/"));
            returnValue = uri + "/" + url;
        }

        return removeRelativePaths(returnValue, baseUrl);
    }

    /**
     * Removes from <code>url<code> references to relative paths.
     * 
     * @param url
     *            url
     * @param originalRequestUri
     *            originalRequestUri
     * @return returns <code>url</code> without relative paths.
     */
    protected String removeRelativePaths(String url, String originalRequestUri) {

        String urlWithoutRelativePath = url;

        if (url.startsWith("..")) {
            Stack stack = new Stack();
            String localUri = originalRequestUri.substring(originalRequestUri.indexOf("/"),
                    originalRequestUri.lastIndexOf("/"));
            StringTokenizer localUriParts = new StringTokenizer(localUri.replace('\\', '/'), "/");
            while (localUriParts.hasMoreTokens()) {
                String part = localUriParts.nextToken();
                stack.push(part);
            }

            StringTokenizer pathParts = new StringTokenizer(url.replace('\\', '/'), "/");
            while (pathParts.hasMoreTokens()) {
                String part = pathParts.nextToken();

                if (!part.equals(".")) {
                    if (part.equals("..")) {
                        stack.pop();
                    } else {
                        stack.push(part);
                    }
                }
            }

            StringBuffer flatPathBuffer = new StringBuffer();
            for (int i = 0; i < stack.size(); i++) {
                flatPathBuffer.append("/").append(stack.elementAt(i));
            }

            urlWithoutRelativePath = flatPathBuffer.toString();
        }

        return urlWithoutRelativePath;
    }

    /**
     * Strips a servlet session ID from <tt>url</tt>.
     * 
     * @param url
     *            url
     * @return url without sessionId
     */
    protected String stripSession(String url) {

        return HDIVUtil.stripSession(url);
    }

    /**
     * @param config
     *            the config to set
     */
    public void setConfig(HDIVConfig config) {
        this.config = config;
    }

}