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

org.apache.wink.server.internal.contexts.UriInfoImpl Maven / Gradle / Ivy

/*******************************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  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.
 *  
 *******************************************************************************/

package org.apache.wink.server.internal.contexts;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;

import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.PathSegment;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;

import org.apache.wink.common.internal.MultivaluedMapImpl;
import org.apache.wink.common.internal.PathSegmentImpl;
import org.apache.wink.common.internal.i18n.Messages;
import org.apache.wink.common.internal.runtime.RuntimeContextTLS;
import org.apache.wink.common.internal.uri.UriEncoder;
import org.apache.wink.common.internal.utils.UriHelper;
import org.apache.wink.server.handlers.MessageContext;
import org.apache.wink.server.internal.handlers.SearchResult;
import org.apache.wink.server.internal.registry.ResourceInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UriInfoImpl implements UriInfo {

    private static final Logger            logger = LoggerFactory.getLogger(UriInfoImpl.class);

    private MessageContext                 messageContext;

    private URI                            absolutePath;
    private URI                            baseUri;
    private String                         baseUriString;
    private String                         path;

    private MultivaluedMap pathParameters;
    private MultivaluedMap decodedPathParameters;

    private MultivaluedMap queryParameters;
    private MultivaluedMap decodedQueryParameters;

    private List              pathSegments;
    private List              decodedPathSegments;

    private List                   matchedURIsStrings;
    private List                   decodedMatchedURIsStrings;

    public UriInfoImpl(MessageContext msgContext) {
        messageContext = msgContext;
        absolutePath = null;
        baseUri = null;
        baseUriString = null;
        path = null;
        pathParameters = null;
        pathSegments = null;
        decodedPathSegments = null;
        matchedURIsStrings = null;
        decodedMatchedURIsStrings = null;
    }

    public URI getAbsolutePath() {
        if (absolutePath == null) {
            String requestPath = getPath(false);
            absolutePath = getBaseUri().resolve(requestPath);
        }
        logger.trace("getAbsolutePath() returning: {}", absolutePath); //$NON-NLS-1$
        return absolutePath;
    }

    public UriBuilder getAbsolutePathBuilder() {
        UriBuilder builder = UriBuilder.fromUri(getAbsolutePath());
        logger.trace("getAbsolutePathBuilder() returning: {}", builder); //$NON-NLS-1$
        return builder;
    }

    public URI getBaseUri() {
        if (baseUri == null) {
            String baseUriString = getBaseUriString();
            try {
                baseUri = new URI(baseUriString);
            } catch (URISyntaxException e) {
                if (logger.isErrorEnabled()) {
                    logger.error(Messages.getMessage("uriBadBaseURI", baseUriString), e); //$NON-NLS-1$
                }
            }
        }
        logger.trace("getBaseUri() returning: {}", baseUri); //$NON-NLS-1$
        return baseUri;
    }

    public UriBuilder getBaseUriBuilder() {
        UriBuilder builder = UriBuilder.fromUri(getBaseUri());
        logger.trace("getBaseUriBuilder() returning: {}", builder); //$NON-NLS-1$
        return builder;
    }

    public List getMatchedResourceInstances() {
        List resources =
            Collections.unmodifiableList(messageContext.getAttribute(SearchResult.class).getData()
                .getMatchedResources());
        logger.trace("getMatchedResourceInstances() returning: {}", resources); //$NON-NLS-1$
        return resources;
    }

    public List getMatchedResources() {
        List matchedResources =
            messageContext.getAttribute(SearchResult.class).getData().getMatchedResources();
        List resourceList = new ArrayList(matchedResources.size());
        for (ResourceInstance resourceInstance : matchedResources) {
            resourceList.add(resourceInstance.getInstance(messageContext));
        }
        logger.trace("getMatchedResources() returning: {}", resourceList); //$NON-NLS-1$
        return Collections.unmodifiableList(resourceList);
    }

    public List getMatchedURIs() {
        return getMatchedURIs(true);
    }

    public List getMatchedURIs(boolean decode) {
        logger.trace("getMatchedURIs({}) called", decode); //$NON-NLS-1$
        List> matchedURIs =
            messageContext.getAttribute(SearchResult.class).getData().getMatchedURIs();
        if (matchedURIsStrings != null && matchedURIsStrings.size() != matchedURIs.size()) {
            matchedURIsStrings = null;
            decodedMatchedURIsStrings = null;
        }

        if (matchedURIsStrings == null) {
            matchedURIsStrings = new ArrayList(matchedURIs.size());
            for (List segments : matchedURIs) {
                logger.trace("Adding matched URI: {}", segments); //$NON-NLS-1$
                matchedURIsStrings.add(PathSegmentImpl.toString(segments));
            }
        }

        List list = matchedURIsStrings;
        if (decode) {
            if (decodedMatchedURIsStrings == null) {
                decodedMatchedURIsStrings = new ArrayList(matchedURIsStrings.size());
                for (String uri : matchedURIsStrings) {
                    String decodedUri = UriEncoder.decodeString(uri);
                    decodedMatchedURIsStrings.add(decodedUri);
                    logger.trace("Adding decoded URI: {} from URI: {}", decodedUri, uri); //$NON-NLS-1$
                }
            }
            list = decodedMatchedURIsStrings;
        }
        logger.trace("getMatchedURIs({}) returning {}", decode, list); //$NON-NLS-1$
        return Collections.unmodifiableList(list);
    }

    public String getPath() {
        return getPath(true);
    }

    public String getPath(boolean decode) {
        logger.trace("getPath({}) called", decode); //$NON-NLS-1$
        if (path == null) {
            path = buildRequestPath(messageContext.getAttribute(HttpServletRequest.class));
        }

        if (decode) {
            String decodedPath = UriEncoder.decodeString(path);
            logger.trace("getPath({}) returning {}", decode, decodedPath); //$NON-NLS-1$
            return decodedPath;
        }
        logger.trace("getPath({}) returning {}", decode, path); //$NON-NLS-1$
        return path;
    }

    public MultivaluedMap getPathParameters() {
        return getPathParameters(true);
    }

    public MultivaluedMap getPathParameters(boolean decode) {
        logger.trace("getPathParameters({}) called", decode); //$NON-NLS-1$
        if (pathParameters == null) {
            pathParameters = new MultivaluedMapImpl();
            SearchResult searchResult = messageContext.getAttribute(SearchResult.class);
            if (searchResult == null) {
                throw new IllegalStateException(Messages
                    .getMessage("methodCallOutsideScopeOfRequestContext")); //$NON-NLS-1$
            }
            MultivaluedMapImpl.copy(searchResult.getData().getMatchedVariables(), pathParameters);
            logger.trace("getPathParameters({}) encoded path parameters are: {}", //$NON-NLS-1$
                         decode,
                         pathParameters);
        }

        MultivaluedMap map = pathParameters;
        if (decode) {
            if (decodedPathParameters == null) {
                decodedPathParameters = UriEncoder.decodeMultivaluedMapValues(pathParameters);
            }
            map = decodedPathParameters;
        }

        logger.trace("getPathParameters({}) returning {}", decode, map); //$NON-NLS-1$
        return map;
    }

    public void resetPathParameters() {
        pathParameters = null;
    }

    public List getPathSegments() {
        return getPathSegments(true);
    }

    public List getPathSegments(boolean decode) {
        logger.trace("getPathSegments({}) called", decode); //$NON-NLS-1$
        if (pathSegments == null) {
            pathSegments = UriHelper.parsePath(getPath(false));
            logger.trace("getPathSegments({}) encoded path parameters are: {}", //$NON-NLS-1$
                         decode,
                         pathSegments);
        }

        List list = pathSegments;
        if (decode) {
            if (decodedPathSegments == null) {
                decodedPathSegments = UriHelper.parsePath(getPath(true));
            }
            list = decodedPathSegments;
        }
        logger.trace("getPathSegments({}) returning {}", decode, list); //$NON-NLS-1$
        return list;
    }

    public MultivaluedMap getQueryParameters() {
        return getQueryParameters(true);
    }

    public MultivaluedMap getQueryParameters(boolean decode) {
        logger.trace("getQueryParameters({}) called", decode); //$NON-NLS-1$
        if (queryParameters == null) {
            queryParameters = new MultivaluedMapImpl();
            String query = messageContext.getAttribute(HttpServletRequest.class).getQueryString();
            logger.trace("getQueryParameters({}) query string is: {}", decode, query); //$NON-NLS-1$
            queryParameters = UriHelper.parseQuery(query);
            logger.trace("getQueryParameters({}) encoded query parameters are: {}", //$NON-NLS-1$
                         decode,
                         queryParameters);
        }

        MultivaluedMap map = queryParameters;
        if (decode) {
            if (decodedQueryParameters == null) {
                if (queryParameters.size() == 0) {
                    /*
                     * shortcut here if the query parameters don't exist
                     */
                    decodedQueryParameters = queryParameters;
                } else {
                    decodedQueryParameters = UriEncoder.decodeMultivaluedMapValues(queryParameters);
                }
            }
            map = decodedQueryParameters;
        }
        logger.trace("getQueryParameters({}) returning {}", decode, map); //$NON-NLS-1$
        return map;
    }

    public URI getRequestUri() {
        logger.trace("getRequestUri() called"); //$NON-NLS-1$
        UriBuilder builder = getAbsolutePathBuilder();
        String query = messageContext.getAttribute(HttpServletRequest.class).getQueryString();
        logger.trace("getRequestUri() query string: {}", query); //$NON-NLS-1$
        builder.replaceQuery(query);
        logger.trace("getRequestUri() build after query replacement: {}", builder); //$NON-NLS-1$
        URI uri = builder.build();
        logger.trace("getRequestUri() returning: {}", uri); //$NON-NLS-1$
        return uri;
    }

    public UriBuilder getRequestUriBuilder() {
        UriBuilder builder = UriBuilder.fromUri(getRequestUri());
        logger.trace("getRequestUriBuilder() returning: {}", builder); //$NON-NLS-1$
        return builder;
    }

    private String getBaseUriString() {
        if (baseUriString == null) {
            baseUriString =
                buildBaseUriString(messageContext.getAttribute(HttpServletRequest.class),
                                   messageContext.getProperties());
        }
        logger.trace("getBaseUriString() returned {}", baseUriString); //$NON-NLS-1$
        return baseUriString;
    }

    private String buildBaseUriString(HttpServletRequest request, Properties properties) {
        String httpURI = getURI(properties, "wink.http.uri"); //$NON-NLS-1$
        String httpsURI = getURI(properties, "wink.https.uri"); //$NON-NLS-1$
        if (httpURI != null || httpsURI != null) {
            if (httpsURI == null) {
                throw new IllegalStateException(Messages
                    .getMessage("parameterHttpsIsEmptyOrNotInitialized")); //$NON-NLS-1$
            }
            if (httpURI == null) {
                throw new IllegalStateException(Messages
                    .getMessage("parameterHttpIsEmptyOrNotInitialized")); //$NON-NLS-1$
            }
        } else {
            logger.trace("Endpoint is not set up in the configuration; using request detection"); //$NON-NLS-1$
        }

        String baseURI = httpURI;
        if (request.isSecure()) {
            logger.trace("buildBaseUriString request is secure"); //$NON-NLS-1$
            baseURI = httpsURI;
        }
        logger.trace("buildBaseUriString baseURI from properties is: {}", baseURI); //$NON-NLS-1$
        if (baseURI == null) {
            baseURI = autodetectBaseUri(request);
            logger.trace("buildBaseUriString baseURI from autodetectBaseUri is: {}", baseURI); //$NON-NLS-1$
        }
        return appendContextAndServletPath(baseURI, request, properties);
    }

    private String getURI(Properties properties, String propertyName) {
        String uri = properties.getProperty(propertyName);
        logger.trace("getURI({}, {}) called", properties, propertyName); //$NON-NLS-1$
        if (uri != null && uri.length() != 0) {
            try {
                URI uriParsed = new URI(uri);
                logger.trace("getURI({}, {}) returning {}", new Object[] {properties, propertyName, //$NON-NLS-1$
                    uriParsed});
                return uriParsed.toString();
            } catch (URISyntaxException e) {
                throw new IllegalArgumentException(Messages.getMessage("uriInfoInvalidURI"), e); //$NON-NLS-1$
            }
        }
        logger.trace("getURI({}, {}) returning null", properties, propertyName); //$NON-NLS-1$
        return null;
    }

    private static String autodetectBaseUri(HttpServletRequest request) {
        try {
            return new URI(request.getScheme(), null, request.getServerName(), request
                .getServerPort(), "/", null, null).toString(); //$NON-NLS-1$
        } catch (URISyntaxException e) {
            throw new IllegalStateException(e);
        }
    }

    private String appendContextAndServletPath(String basePath,
                                               HttpServletRequest request,
                                               Properties properties) {
        logger.trace("appendContextAndServletPath({}, {}, {}) called", new Object[] {basePath, //$NON-NLS-1$
            request, properties});
        StringBuilder builder = new StringBuilder(basePath);
        if (builder.charAt(builder.length() - 1) == '/') {
            builder.deleteCharAt(builder.length() - 1);
        }
        String contextURI = properties.getProperty("wink.context.uri"); //$NON-NLS-1$
        String contextPath =
            ((contextURI != null && contextURI.length() > 0) ? contextURI : request
                .getContextPath());
        if (contextPath != null) {
            builder.append(contextPath);
            logger.trace("appendContextAndServletPath after contextPath called is: {} ", builder); //$NON-NLS-1$
        }

        boolean isServlet =
            RuntimeContextTLS.getRuntimeContext().getAttribute(FilterConfig.class) == null;
        logger.trace("appendContextAndServletPath isServlet: {} ", isServlet); //$NON-NLS-1$
        if (request.getServletPath() != null && isServlet) {
            builder.append(request.getServletPath());
            logger
                .trace("appendContextAndServletPath after getServletPath called is: {} ", builder); //$NON-NLS-1$
        }
        if (builder.charAt(builder.length() - 1) != '/') {
            builder.append('/');
        }
        String builderStr = builder.toString();
        logger.trace("appendContextAndServletPath returning: {} ", builderStr); //$NON-NLS-1$
        return builderStr;
    }

    private static String buildRequestPath(HttpServletRequest request) {
        // we cannot use request.getPathInfo() since it cuts off the ';'
        // parameters on Tomcat
        String requestPath = request.getRequestURI();
        logger.trace("buildRequestPath requestPath is: {}", requestPath); //$NON-NLS-1$
        // Syntax-Based Normalization (RFC 3986, section 6.2.2)
        requestPath = UriHelper.normalize(requestPath);
        logger.trace("buildRequestPath requestPath normalized is: {}", requestPath); //$NON-NLS-1$
        // cut off the context path from the beginning
        if (request.getContextPath() != null) {
            requestPath = requestPath.substring(request.getContextPath().length());
            logger.trace("buildRequestPath after context path removed: {}", requestPath); //$NON-NLS-1$
        }

        // cut off the servlet path from the beginning
        boolean isServlet =
            RuntimeContextTLS.getRuntimeContext().getAttribute(FilterConfig.class) == null;
        logger.trace("buildRequestPath isServlet: {}", isServlet); //$NON-NLS-1$
        if (request.getServletPath() != null && isServlet) {
            requestPath = requestPath.substring(request.getServletPath().length());
            logger
                .trace("buildRequestPath requestPath after servlet path removed: {}", requestPath); //$NON-NLS-1$
        }

        // cut off all leading /
        int index = 0;
        while (index < requestPath.length() && requestPath.charAt(index) == '/') {
            ++index;
        }
        requestPath = requestPath.substring(index);
        logger.trace("buildRequestPath returning requestPath: {}", requestPath); //$NON-NLS-1$
        return requestPath;
    }
}