/******************************************************************************* * 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<String, String> pathParameters; private MultivaluedMap<String, String> decodedPathParameters; private MultivaluedMap<String, String> queryParameters; private MultivaluedMap<String, String> decodedQueryParameters; private List<PathSegment> pathSegments; private List<PathSegment> decodedPathSegments; private List<String> matchedURIsStrings; private List<String> 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<ResourceInstance> getMatchedResourceInstances() { List<ResourceInstance> resources = Collections.unmodifiableList(messageContext.getAttribute(SearchResult.class).getData() .getMatchedResources()); logger.trace("getMatchedResourceInstances() returning: {}", resources); //$NON-NLS-1$ return resources; } public List<Object> getMatchedResources() { List<ResourceInstance> matchedResources = messageContext.getAttribute(SearchResult.class).getData().getMatchedResources(); List<Object> resourceList = new ArrayList<Object>(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<String> getMatchedURIs() { return getMatchedURIs(true); } public List<String> getMatchedURIs(boolean decode) { logger.trace("getMatchedURIs({}) called", decode); //$NON-NLS-1$ List<List<PathSegment>> matchedURIs = messageContext.getAttribute(SearchResult.class).getData().getMatchedURIs(); if (matchedURIsStrings != null && matchedURIsStrings.size() != matchedURIs.size()) { matchedURIsStrings = null; decodedMatchedURIsStrings = null; } if (matchedURIsStrings == null) { matchedURIsStrings = new ArrayList<String>(matchedURIs.size()); for (List<PathSegment> segments : matchedURIs) { logger.trace("Adding matched URI: {}", segments); //$NON-NLS-1$ matchedURIsStrings.add(PathSegmentImpl.toString(segments)); } } List<String> list = matchedURIsStrings; if (decode) { if (decodedMatchedURIsStrings == null) { decodedMatchedURIsStrings = new ArrayList<String>(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<String, String> getPathParameters() { return getPathParameters(true); } public MultivaluedMap<String, String> getPathParameters(boolean decode) { logger.trace("getPathParameters({}) called", decode); //$NON-NLS-1$ if (pathParameters == null) { pathParameters = new MultivaluedMapImpl<String, String>(); 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<String, String> 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<PathSegment> getPathSegments() { return getPathSegments(true); } public List<PathSegment> 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<PathSegment> 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<String, String> getQueryParameters() { return getQueryParameters(true); } public MultivaluedMap<String, String> getQueryParameters(boolean decode) { logger.trace("getQueryParameters({}) called", decode); //$NON-NLS-1$ if (queryParameters == null) { queryParameters = new MultivaluedMapImpl<String, String>(); 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<String, String> 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; } }