diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/BaseUri.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/BaseUri.java new file mode 100644 index 000000000..301132ede --- /dev/null +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/BaseUri.java @@ -0,0 +1,132 @@ +/* + * Copyright 2014 the original author or authors. + * + * 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.springframework.data.rest.webmvc; + +import java.net.URI; +import java.util.Collections; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.hateoas.UriTemplate; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.web.util.UrlPathHelper; + +/** + * Value object to be able to extract the lookup path within a configured base URi that forms a URI namespace. + * + * @author Oliver Gierke + */ +public class BaseUri { + + public static final BaseUri NONE = new BaseUri(URI.create("")); + private static final UrlPathHelper URL_PATH_HELPER = new UrlPathHelper(); + + private final URI baseUri; + + /** + * Creates a new {@link BaseUri} with the given URI as base. + * + * @param uri must not be {@literal null}. + */ + public BaseUri(URI uri) { + + Assert.notNull(uri, "Base URI must not be null!"); + + this.baseUri = uri; + } + + /** + * Returns the base URI. + * + * @return + */ + public URI getUri() { + return baseUri; + } + + /** + * Extracts the actual lookup path within the Spring Data REST managed URI space. This includes stripping the + * necessary parts of the base URI from the source lookup path. + * + * @param request must not be {@literal null}. + * @return the stripped lookup path with then the repository URI space or {@literal null} in case the lookup path is + * not pointing into the repository URI space. + */ + public String getRepositoryLookupPath(NativeWebRequest request) { + return getRepositoryLookupPath(request.getNativeRequest(HttpServletRequest.class)); + } + + /** + * Extracts the actual lookup path within the Spring Data REST managed URI space. This includes stripping the + * necessary parts of the base URI from the source lookup path. + * + * @param request must not be {@literal null}. + * @return the stripped lookup path with then the repository URI space or {@literal null} in case the lookup path is + * not pointing into the repository URI space. + */ + private String getRepositoryLookupPath(HttpServletRequest request) { + + String lookupPath = URL_PATH_HELPER.getLookupPathForRequest(request); + lookupPath = new UriTemplate(lookupPath).expand().toString(); + return getRepositoryLookupPath(lookupPath); + } + + /** + * Extracts the actual lookup path within the Spring Data REST managed URI space. This includes stripping the + * necessary parts of the base URI from the source lookup path. + * + * @param lookupPath must not be {@literal null}. + * @return the stripped lookup path with then the repository URI space or {@literal null} in case the lookup path is + * not pointing into the repository URI space. + */ + public String getRepositoryLookupPath(String lookupPath) { + + Assert.notNull(lookupPath, "Lookup path must not be null!"); + + lookupPath = StringUtils.trimTrailingCharacter(lookupPath, '/'); + + if (!baseUri.isAbsolute()) { + + String uri = baseUri.toString(); + + if (!StringUtils.hasText(uri)) { + return lookupPath; + } + + uri = uri.startsWith("/") ? uri : "/".concat(uri); + return lookupPath.startsWith(uri) ? lookupPath.substring(uri.length(), lookupPath.length()) : null; + } + + List baseUriSegments = UriComponentsBuilder.fromUri(baseUri).build().getPathSegments(); + Collections.reverse(baseUriSegments); + String tail = ""; + + for (String tailSegment : baseUriSegments) { + + tail = "/".concat(tailSegment).concat(tail); + + if (lookupPath.startsWith(tail)) { + return lookupPath.substring(tail.length(), lookupPath.length()); + } + } + + return null; + } +} diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMapping.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMapping.java index f22bc4111..525b55c24 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMapping.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMapping.java @@ -17,9 +17,7 @@ package org.springframework.data.rest.webmvc; import static org.springframework.util.StringUtils.*; -import java.net.URI; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -36,7 +34,6 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; -import org.springframework.web.util.UriComponentsBuilder; /** * {@link RequestMappingHandlerMapping} implementation that will only find a handler method if a @@ -113,7 +110,7 @@ public class RepositoryRestHandlerMapping extends RequestMappingHandlerMapping { acceptType = config.getDefaultMediaType().toString(); } - String uri = extractRepositoryLookupPath(lookupPath, config.getBaseUri()); + String uri = new BaseUri(config.getBaseUri()).getRepositoryLookupPath(lookupPath); if (uri == null) { return null; @@ -135,50 +132,6 @@ public class RepositoryRestHandlerMapping extends RequestMappingHandlerMapping { return null; } - /** - * Extracts the actual lookup path within the Spring Data REST managed URI space. This includes stripping the - * necessary parts of the base URI from the source lookup path. - * - * @param lookupPath must not be {@literal null}. - * @param baseUri must not be {@literal null}. - * @return the stripped lookup path with then the repository URI space or {@literal null} in case the lookup path is - * not pointing into the repository URI space. - */ - private static String extractRepositoryLookupPath(String lookupPath, URI baseUri) { - - Assert.notNull(lookupPath, "Lookup path must not be null!"); - Assert.notNull(baseUri, "Base URI must not be null!"); - - lookupPath = StringUtils.trimTrailingCharacter(lookupPath, '/'); - - if (!baseUri.isAbsolute()) { - - String uri = baseUri.toString(); - - if (!StringUtils.hasText(uri)) { - return lookupPath; - } - - uri = uri.startsWith("/") ? uri : "/".concat(uri); - return lookupPath.startsWith(uri) ? lookupPath.substring(uri.length(), lookupPath.length()) : null; - } - - List baseUriSegments = UriComponentsBuilder.fromUri(baseUri).build().getPathSegments(); - Collections.reverse(baseUriSegments); - String tail = ""; - - for (String tailSegment : baseUriSegments) { - - tail = "/".concat(tailSegment).concat(tail); - - if (lookupPath.startsWith(tail)) { - return lookupPath.substring(tail.length(), lookupPath.length()); - } - } - - return null; - } - /* * (non-Javadoc) * @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#isHandler(java.lang.Class) diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java index 43312e374..21271a0ad 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java @@ -61,6 +61,7 @@ import org.springframework.data.rest.core.projection.ProxyProjectionFactory; import org.springframework.data.rest.core.support.DomainObjectMerger; import org.springframework.data.rest.core.support.RepositoryRelProvider; import org.springframework.data.rest.core.util.UUIDConverter; +import org.springframework.data.rest.webmvc.BaseUri; import org.springframework.data.rest.webmvc.RepositoryRestController; import org.springframework.data.rest.webmvc.RepositoryRestHandlerAdapter; import org.springframework.data.rest.webmvc.RepositoryRestHandlerMapping; @@ -240,6 +241,11 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon return config; } + @Bean + public BaseUri baseUri() { + return new BaseUri(config().getBaseUri()); + } + /** * {@link org.springframework.beans.factory.config.BeanPostProcessor} to turn beans annotated as * {@link org.springframework.data.rest.repository.annotation.RepositoryEventHandler}s. @@ -286,13 +292,13 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon @Bean public ResourceMetadataHandlerMethodArgumentResolver resourceMetadataHandlerMethodArgumentResolver() { - return new ResourceMetadataHandlerMethodArgumentResolver(repositories(), resourceMappings()); + return new ResourceMetadataHandlerMethodArgumentResolver(repositories(), resourceMappings(), baseUri()); } @Bean public BackendIdHandlerMethodArgumentResolver backendIdHandlerMethodArgumentResolver() { return new BackendIdHandlerMethodArgumentResolver(backendIdConverterRegistry(), - resourceMetadataHandlerMethodArgumentResolver()); + resourceMetadataHandlerMethodArgumentResolver(), baseUri()); } /** diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/ResourceMetadataHandlerMethodArgumentResolver.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/ResourceMetadataHandlerMethodArgumentResolver.java index a06534c16..3ad208be6 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/ResourceMetadataHandlerMethodArgumentResolver.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/ResourceMetadataHandlerMethodArgumentResolver.java @@ -23,6 +23,7 @@ import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.support.Repositories; import org.springframework.data.rest.core.mapping.ResourceMappings; import org.springframework.data.rest.core.mapping.ResourceMetadata; +import org.springframework.data.rest.webmvc.BaseUri; import org.springframework.data.rest.webmvc.util.UriUtils; import org.springframework.util.Assert; import org.springframework.web.bind.support.WebDataBinderFactory; @@ -40,6 +41,7 @@ public class ResourceMetadataHandlerMethodArgumentResolver implements HandlerMet private final Repositories repositories; private final ResourceMappings mappings; + private final BaseUri baseUri; /** * Creates a new {@link ResourceMetadataHandlerMethodArgumentResolver} for the given {@link Repositories} and @@ -47,14 +49,18 @@ public class ResourceMetadataHandlerMethodArgumentResolver implements HandlerMet * * @param repositories must not be {@literal null}. * @param mappings must not be {@literal null}. + * @param baseUri must not be {@literal null}. */ - public ResourceMetadataHandlerMethodArgumentResolver(Repositories repositories, ResourceMappings mappings) { + public ResourceMetadataHandlerMethodArgumentResolver(Repositories repositories, ResourceMappings mappings, + BaseUri baseUri) { Assert.notNull(repositories, "Repositories must not be null!"); Assert.notNull(mappings, "ResourceMappings must not be null!"); + Assert.notNull(baseUri, "BaseUri must not be null!"); this.repositories = repositories; this.mappings = mappings; + this.baseUri = baseUri; } /* @@ -74,7 +80,8 @@ public class ResourceMetadataHandlerMethodArgumentResolver implements HandlerMet public ResourceMetadata resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { - String repositoryKey = UriUtils.findMappingVariable("repository", parameter, webRequest); + String lookupPath = baseUri.getRepositoryLookupPath(webRequest); + String repositoryKey = UriUtils.findMappingVariable("repository", parameter, lookupPath); if (!hasText(repositoryKey)) { return null; diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/BackendIdHandlerMethodArgumentResolver.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/BackendIdHandlerMethodArgumentResolver.java index e4a8c6589..595cbc319 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/BackendIdHandlerMethodArgumentResolver.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/BackendIdHandlerMethodArgumentResolver.java @@ -19,6 +19,7 @@ import java.io.Serializable; import org.springframework.core.MethodParameter; import org.springframework.data.rest.core.mapping.ResourceMetadata; +import org.springframework.data.rest.webmvc.BaseUri; import org.springframework.data.rest.webmvc.config.ResourceMetadataHandlerMethodArgumentResolver; import org.springframework.data.rest.webmvc.spi.BackendIdConverter; import org.springframework.data.rest.webmvc.spi.BackendIdConverter.DefaultIdConverter; @@ -40,22 +41,26 @@ public class BackendIdHandlerMethodArgumentResolver implements HandlerMethodArgu private final PluginRegistry> idConverters; private final ResourceMetadataHandlerMethodArgumentResolver resourceMetadataResolver; + private final BaseUri baseUri; /** * Creates a new {@link BackendIdHandlerMethodArgumentResolver} for the given {@link BackendIdConverter}s and * {@link ResourceMetadataHandlerMethodArgumentResolver}. * - * @param idConverters the {@link BackendIdConverter}s registered in the system. - * @param resourceMetadataResolver the resolver to obtain {@link ResourceMetadata} from. + * @param idConverters the {@link BackendIdConverter}s registered in the system, must not be {@literal null}.. + * @param resourceMetadataResolver the resolver to obtain {@link ResourceMetadata} from, must not be {@literal null}. + * @param baseUri must not be {@literal null}. */ public BackendIdHandlerMethodArgumentResolver(PluginRegistry> idConverters, - ResourceMetadataHandlerMethodArgumentResolver resourceMetadataResolver) { + ResourceMetadataHandlerMethodArgumentResolver resourceMetadataResolver, BaseUri baseUri) { Assert.notNull(idConverters, "Id converters must not be null!"); Assert.notNull(resourceMetadataResolver, "ResourceMetadata resolver must not be null!"); + Assert.notNull(baseUri, "BaseUri must not be null!"); this.idConverters = idConverters; this.resourceMetadataResolver = resourceMetadataResolver; + this.baseUri = baseUri; } /* @@ -91,6 +96,7 @@ public class BackendIdHandlerMethodArgumentResolver implements HandlerMethodArgu } BackendIdConverter pluginFor = idConverters.getPluginFor(metadata.getDomainType(), DefaultIdConverter.INSTANCE); - return pluginFor.fromRequestId(UriUtils.findMappingVariable("id", parameter, request), metadata.getDomainType()); + String lookupPath = baseUri.getRepositoryLookupPath(request); + return pluginFor.fromRequestId(UriUtils.findMappingVariable("id", parameter, lookupPath), metadata.getDomainType()); } } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/util/UriUtils.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/util/UriUtils.java index 28b39912e..d62740ea4 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/util/UriUtils.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/util/UriUtils.java @@ -17,14 +17,9 @@ package org.springframework.data.rest.webmvc.util; import java.util.Map; -import javax.servlet.http.HttpServletRequest; - import org.springframework.core.MethodParameter; -import org.springframework.hateoas.UriTemplate; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.util.UrlPathHelper; /** * Utility methods to work with requests and URIs. @@ -33,8 +28,6 @@ import org.springframework.web.util.UrlPathHelper; */ public abstract class UriUtils { - private static final UrlPathHelper URL_PATH_HELPER = new UrlPathHelper(); - private UriUtils() {} /** @@ -45,13 +38,11 @@ public abstract class UriUtils { * @param request * @return */ - public static String findMappingVariable(String variable, MethodParameter parameter, NativeWebRequest request) { + public static String findMappingVariable(String variable, MethodParameter parameter, String lookupPath) { Assert.hasText(variable, "Variable name must not be null or empty!"); Assert.notNull(parameter, "Method parameter must not be null!"); - Assert.notNull(request, "Request must not be null!"); - String lookupPath = getCleanLookupPath(request); RequestMapping annotation = parameter.getMethodAnnotation(RequestMapping.class); for (String mapping : annotation.value()) { @@ -66,11 +57,4 @@ public abstract class UriUtils { return null; } - - private static String getCleanLookupPath(NativeWebRequest request) { - - HttpServletRequest httpServletRequest = request.getNativeRequest(HttpServletRequest.class); - String lookupPath = URL_PATH_HELPER.getLookupPathForRequest(httpServletRequest); - return new UriTemplate(lookupPath).expand().toString(); - } }