diff --git a/spring-data-rest-hal-explorer/src/main/java/org/springframework/data/rest/webmvc/halexplorer/HalExplorerConfiguration.java b/spring-data-rest-hal-explorer/src/main/java/org/springframework/data/rest/webmvc/halexplorer/HalExplorerConfiguration.java index 7c9519f52..c99e0ad4d 100644 --- a/spring-data-rest-hal-explorer/src/main/java/org/springframework/data/rest/webmvc/halexplorer/HalExplorerConfiguration.java +++ b/spring-data-rest-hal-explorer/src/main/java/org/springframework/data/rest/webmvc/halexplorer/HalExplorerConfiguration.java @@ -15,6 +15,8 @@ */ package org.springframework.data.rest.webmvc.halexplorer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.webmvc.config.StaticResourceProvider; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; @@ -23,9 +25,11 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry * {@link StaticResourceProvider} to expose the HAL Browser WebJar content via a static resource route. * * @author Oliver Drotbohm + * @author Christoph Strobl * @since 3.2 * @soundtrack Tedeschi Trucks Band - Signs, High Times (Signs) */ +@Configuration(proxyBeanMethods = false) class HalExplorerConfiguration implements StaticResourceProvider { /* @@ -39,4 +43,17 @@ class HalExplorerConfiguration implements StaticResourceProvider { registry.addResourceHandler(basePath.concat("/**")).addResourceLocations(rootLocation); } + + /** + * The controller that exposes the {@link HalExplorer}. + * + * @return never {@literal null}. + * @since 3.4 + */ + @Bean + HalExplorer halExplorer() { + + // TODO: maybe register this one directly on mvc via WebMvcConfigurer.addRedirectVieController() + return new HalExplorer(); + } } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RestControllerConfiguration.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RestControllerConfiguration.java new file mode 100644 index 000000000..d9c636e5f --- /dev/null +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RestControllerConfiguration.java @@ -0,0 +1,141 @@ +/* + * Copyright 2020 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 + * + * https://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 org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.repository.support.Repositories; +import org.springframework.data.repository.support.RepositoryInvokerFactory; +import org.springframework.data.rest.core.config.RepositoryRestConfiguration; +import org.springframework.data.rest.core.mapping.RepositoryResourceMappings; +import org.springframework.data.rest.webmvc.alps.AlpsController; +import org.springframework.data.rest.webmvc.json.JsonSchema; +import org.springframework.data.rest.webmvc.json.PersistentEntityToJsonSchemaConverter; +import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks; +import org.springframework.data.web.PagedResourcesAssembler; +import org.springframework.hateoas.server.EntityLinks; + +/** + * Configuration class registering required {@link org.springframework.stereotype.Component components} that declare + * request mappings as {@link Bean beans}. + * + * @author Christoph Strobl + * @since 3.4 + */ +@Configuration(proxyBeanMethods = false) +public class RestControllerConfiguration { + + private final RepositoryRestConfiguration restConfiguration; + private final RepositoryResourceMappings resourceMappings; + private final PagedResourcesAssembler resourcesAssembler; + private final Repositories repositories; + + RestControllerConfiguration(RepositoryRestConfiguration restConfiguration, + RepositoryResourceMappings resourceMappings, PagedResourcesAssembler resourcesAssembler, + Repositories repositories) { + + this.restConfiguration = restConfiguration; + this.resourceMappings = resourceMappings; + this.resourcesAssembler = resourcesAssembler; + this.repositories = repositories; + } + + /** + * The controller for the root resource exposing links to the repository resources. + * + * @param entityLinks the accessor to links pointing to controllers backing an entity type. Must not be + * {@literal null}. + * @return never {@literal null}. + */ + @Bean + protected RepositoryController repositoryController(EntityLinks entityLinks) { + return new RepositoryController(resourcesAssembler, repositories, entityLinks, resourceMappings); + } + + /** + * The root controller for entities reachable via {@code /{repository}}. + * + * @param entityLinks the accessor to links pointing to controllers backing an entity type. Must not be * + * {@literal null}. + * @param headersPreparer must not be {@literal null}. + * @return never {@literal null}. + */ + @Bean + protected RepositoryEntityController repositoryEntityController(RepositoryEntityLinks entityLinks, + HttpHeadersPreparer headersPreparer) { + return new RepositoryEntityController(repositories, restConfiguration, entityLinks, resourcesAssembler, + headersPreparer); + } + + /** + * The controller to access referenced properties via {@code /{repository}/{id}/{property}}. + * + * @param repositoryInvokerFactory must not be {@literal null}. + * @return never {@literal null}. + */ + @Bean + protected RepositoryPropertyReferenceController repositoryPropertyReferenceController( + RepositoryInvokerFactory repositoryInvokerFactory) { + return new RepositoryPropertyReferenceController(repositories, repositoryInvokerFactory, resourcesAssembler); + } + + /** + * The controller that performs lookups and executes searches. + * + * @param entityLinks he accessor to links pointing to controllers backing an entity type. Must not be * + * {@literal null}. + * @param headersPreparer must not be {@literal null}. + * @return never {@literal null}. + */ + @Bean + protected RepositorySearchController repositorySearchController(RepositoryEntityLinks entityLinks, + HttpHeadersPreparer headersPreparer) { + return new RepositorySearchController(resourcesAssembler, entityLinks, resourceMappings, headersPreparer); + } + + /** + * The controller that exposes the JSON schema via {@code /repository/schema}. + * + * @param jsonSchemaConverter the converter to create the {@link JsonSchema}. Must not be {@literal null}. + * @return never {@literal null}. + */ + @Bean + protected RepositorySchemaController repositorySchemaController( + PersistentEntityToJsonSchemaConverter jsonSchemaConverter) { + return new RepositorySchemaController(jsonSchemaConverter); + } + + /** + * The controller that exposes semantic documentation in the ALPS (Application Level + * Profile Semantics) format. + * + * @return never {@literal null}. + */ + @Bean + protected AlpsController alpsController() { + return new AlpsController(restConfiguration); + } + + /** + * Profile-based controller exposing multiple forms of metadata via {@code /profile}. + * + * @return never {@literal null}. + */ + @Bean + protected ProfileController profileController() { + return new ProfileController(restConfiguration, resourceMappings, repositories); + } +} 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 72356dcfa..40f44d2a4 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 @@ -32,8 +32,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportResource; @@ -144,13 +142,13 @@ import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; * @author Jon Brisbin * @author Greg Turnquist * @author Mark Paluch + * @author Christoph Strobl */ @Configuration(proxyBeanMethods = false) @EnableHypermediaSupport(type = HypermediaType.HAL) -@ComponentScan(basePackageClasses = RepositoryRestController.class, - includeFilters = @Filter(BasePathAwareController.class), useDefaultFilters = false) @ImportResource("classpath*:META-INF/spring-data-rest/**/*.xml") -@Import({ SpringDataJacksonConfiguration.class, EnableSpringDataWebSupport.QuerydslActivator.class }) +@Import({ RestControllerImportSelector.class, SpringDataJacksonConfiguration.class, + EnableSpringDataWebSupport.QuerydslActivator.class }) public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebConfiguration implements BeanClassLoaderAware { diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RestControllerImportSelector.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RestControllerImportSelector.java new file mode 100644 index 000000000..a289b9af7 --- /dev/null +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RestControllerImportSelector.java @@ -0,0 +1,48 @@ +/* + * Copyright 2020 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 + * + * https://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.config; + +import org.springframework.context.annotation.ImportSelector; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.data.rest.webmvc.RestControllerConfiguration; +import org.springframework.util.ClassUtils; + +/** + * {@link ImportSelector} choosing additional controller configuration (eg. for the {@litearl Hal Explorer}) when + * present. + * + * @author Christoph Strobl + * @since 3.4 + */ +class RestControllerImportSelector implements ImportSelector { + + /* + * (non-Javadoc) + * @see org.springframework.context.annotation.ImportSelector#selectImports(org.springframework.core.type.AnnotationMetadata) + */ + @Override + public String[] selectImports(AnnotationMetadata importingClassMetadata) { + + if (ClassUtils.isPresent("org.springframework.data.rest.webmvc.halexplorer.HalExplorerConfiguration", + importingClassMetadata.getClass().getClassLoader())) { + + return new String[] { RestControllerConfiguration.class.getName(), + "org.springframework.data.rest.webmvc.halexplorer.HalExplorerConfiguration" }; + } + + return new String[] { RestControllerConfiguration.class.getName() }; + } +}