#973 - Explicit configuration options for web stacks.

@EnableHypermediaSupport now exposes a "stacks" attribute to limit the enablement of hypermedia functionality to a certain web stack.
This commit is contained in:
Oliver Drotbohm
2019-04-02 15:42:28 +02:00
parent 0e02d4f041
commit f27160d16f
4 changed files with 139 additions and 9 deletions

View File

@@ -13,3 +13,18 @@ To let the `RepresentationModel` subtypes be rendered according to the specifica
* By default, it enables `@EnableEntityLinks` (see <<fundamentals.obtaining-links.entity-links>>) and automatically picks up `EntityLinks` implementations and bundles them into a `DelegatingEntityLinks` instance that you can autowire.
* It automatically picks up all `RelProvider` implementations in the `ApplicationContext` and bundles them into a `DelegatingRelProvider` that you can autowire. It registers providers to consider `@Relation` on domain types as well as Spring MVC controllers. If the https://github.com/atteo/evo-inflector[EVO inflector] is on the classpath, collection `rel` values are derived by using the pluralizing algorithm implemented in the library (see <<spis.rel-provider>>).
[[configuration.at-enable.stacks]]
=== Explicitly enabling support for dedicated web stacks
By default, `@EnableHypermediaSupport` will reflectively detect the web application stack you're using and hook into the Spring components registered for those to enable support for hypermedia representations.
However, there are situations in which you'd only explicitly want to activate support for a particular stack.
E.g. if your Spring WebMVC based application uses WebFlux' `WebClient` to make outgoing requests and that one is not supposed to work with hypermedia elements, you can restrict the functionality to be enabled by explicitly declaring WebMvc in the configuration:
.Explicitly activating hypermedia support for a particular web stack
====
[source, java]
----
@EnableHypermediaSupport(…, stacks = WebStack.WEBMVC)
class MyHypermediaConfiguration { … }
----
====

View File

@@ -26,7 +26,9 @@ import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.support.WebStack;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Activates hypermedia support in the {@link ApplicationContext}. Will register infrastructure beans to support all
@@ -49,6 +51,15 @@ public @interface EnableHypermediaSupport {
*/
HypermediaType[] type();
/**
* Configures which {@link WebStack}s we're supposed to enable support for. By default we're activating it for all
* available ones if they happen to be in use. Configure this explicitly in case you're using WebFlux components like
* {@link WebClient} but don't want to use hypermedia operations with it.
*
* @return
*/
WebStack[] stacks() default { WebStack.WEBMVC, WebStack.WEBFLUX };
/**
* Hypermedia representation types supported.
*

View File

@@ -15,8 +15,10 @@
*/
package org.springframework.hateoas.config;
import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
@@ -29,23 +31,44 @@ import org.springframework.hateoas.support.WebStack;
*/
class WebStackImportSelector implements ImportSelector {
private static final String WEB_STACK_MISSING = "At least one web stack has to be selected in @EnableHypermediaSupport on %s!";
static Map<WebStack, String> CONFIGS;
static {
Map<WebStack, String> configs = new HashMap<>();
configs.put(WebStack.WEBMVC, "org.springframework.hateoas.config.WebMvcHateoasConfiguration");
configs.put(WebStack.WEBFLUX, "org.springframework.hateoas.config.WebFluxHateoasConfiguration");
CONFIGS = Collections.unmodifiableMap(configs);
}
/*
* (non-Javadoc)
* @see org.springframework.context.annotation.ImportSelector#selectImports(org.springframework.core.type.AnnotationMetadata)
*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
public String[] selectImports(AnnotationMetadata metadata) {
List<String> imports = new ArrayList<>();
Map<String, Object> attributes = metadata.getAnnotationAttributes(EnableHypermediaSupport.class.getName());
if (WebStack.WEBMVC.isAvailable()) {
imports.add("org.springframework.hateoas.config.WebMvcHateoasConfiguration");
// Configuration class imported but not through @EnableHypermediaSupport
if (attributes == null) {
return new String[0];
}
if (WebStack.WEBFLUX.isAvailable()) {
imports.add("org.springframework.hateoas.config.WebFluxHateoasConfiguration");
WebStack[] stacks = (WebStack[]) attributes.get("stacks");
if (stacks.length == 0) {
throw new IllegalStateException(String.format(WEB_STACK_MISSING, metadata.getClassName()));
}
return imports.toArray(new String[0]);
return Arrays.stream(stacks) //
.filter(WebStack::isAvailable) //
.map(CONFIGS::get) //
.toArray(String[]::new);
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2019 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.hateoas.config;
import static org.assertj.core.api.Assertions.*;
import org.junit.Test;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.hateoas.support.WebStack;
/**
* Unit tests for {@link WebStackImportSelector}.
*
* @author Oliver Drotbohm
*/
public class WebStackImportSelectorUnitTest {
WebStackImportSelector selector = new WebStackImportSelector();
@Test // #973
public void activatesAllWebStacksByDefault() {
AnnotationMetadata metadata = new StandardAnnotationMetadata(DefaultHypermedia.class);
assertThat(selector.selectImports(metadata))
.containsExactlyInAnyOrderElementsOf(WebStackImportSelector.CONFIGS.values());
}
@Test // #973
public void activatesWebMvcOnlyifConfigured() {
AnnotationMetadata metadata = new StandardAnnotationMetadata(WebMvcHypermedia.class);
assertThat(selector.selectImports(metadata)).containsExactly(WebStackImportSelector.CONFIGS.get(WebStack.WEBMVC));
}
@Test // #973
public void activatesWebFluxOnlyIfConfigured() {
AnnotationMetadata metadata = new StandardAnnotationMetadata(WebFluxHypermedia.class);
assertThat(selector.selectImports(metadata)).containsExactly(WebStackImportSelector.CONFIGS.get(WebStack.WEBFLUX));
}
@Test // #973
public void rejectsNoStacksSelected() {
AnnotationMetadata metadata = new StandardAnnotationMetadata(NoStacksHypermedia.class);
assertThatExceptionOfType(IllegalStateException.class) //
.isThrownBy(() -> selector.selectImports(metadata)) //
.withMessageContaining(NoStacksHypermedia.class.getName());
}
@EnableHypermediaSupport(type = HypermediaType.HAL)
static class DefaultHypermedia {}
@EnableHypermediaSupport(type = HypermediaType.HAL, stacks = WebStack.WEBMVC)
static class WebMvcHypermedia {}
@EnableHypermediaSupport(type = HypermediaType.HAL, stacks = WebStack.WEBFLUX)
static class WebFluxHypermedia {}
@EnableHypermediaSupport(type = HypermediaType.HAL, stacks = {})
static class NoStacksHypermedia {}
}