diff --git a/src/main/java/org/springframework/hateoas/config/EnableHypermediaSupport.java b/src/main/java/org/springframework/hateoas/config/EnableHypermediaSupport.java index 58e2867d..77f81571 100644 --- a/src/main/java/org/springframework/hateoas/config/EnableHypermediaSupport.java +++ b/src/main/java/org/springframework/hateoas/config/EnableHypermediaSupport.java @@ -20,8 +20,6 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.util.EnumSet; -import java.util.Set; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Import; @@ -37,7 +35,7 @@ import org.springframework.hateoas.LinkDiscoverer; *
  • {@link LinkDiscoverer}
  • *
  • a Jackson 2 module to correctly marshal the resource model classes into the appropriate representation. * - * + * * @see LinkDiscoverer * @see EntityLinks * @author Oliver Gierke @@ -47,19 +45,19 @@ import org.springframework.hateoas.LinkDiscoverer; @Target(ElementType.TYPE) @Documented @EnableEntityLinks -@Import({ HypermediaSupportBeanDefinitionRegistrar.class, HateoasConfiguration.class }) +@Import({ HypermediaSupportBeanDefinitionRegistrar.class, HateoasConfiguration.class, WebStackImportSelector.class }) public @interface EnableHypermediaSupport { /** * The hypermedia type to be supported. - * + * * @return */ HypermediaType[] type(); /** * Hypermedia representation types supported. - * + * * @author Oliver Gierke * @author Greg Turnquist */ @@ -67,7 +65,7 @@ public @interface EnableHypermediaSupport { /** * HAL - Hypermedia Application Language. - * + * * @see http://stateless.co/hal_specification.html * @see http://tools.ietf.org/html/draft-kelly-json-hal-05 */ @@ -75,7 +73,7 @@ public @interface EnableHypermediaSupport { /** * HAL-FORMS - Independent, backward-compatible extension of the HAL designed to add runtime FORM support - * + * * @see https://rwcbook.github.io/hal-forms/ */ HAL_FORMS, @@ -93,7 +91,5 @@ public @interface EnableHypermediaSupport { * @see http://uberhypermedia.org/ */ UBER; - - private static Set HAL_BASED_MEDIATYPES = EnumSet.of(HAL, HAL_FORMS); } } diff --git a/src/main/java/org/springframework/hateoas/config/HypermediaSupportBeanDefinitionRegistrar.java b/src/main/java/org/springframework/hateoas/config/HypermediaSupportBeanDefinitionRegistrar.java index e75e85fd..925a3daf 100644 --- a/src/main/java/org/springframework/hateoas/config/HypermediaSupportBeanDefinitionRegistrar.java +++ b/src/main/java/org/springframework/hateoas/config/HypermediaSupportBeanDefinitionRegistrar.java @@ -35,11 +35,8 @@ import org.springframework.hateoas.EntityLinks; import org.springframework.hateoas.LinkDiscoverer; import org.springframework.hateoas.collectionjson.CollectionJsonLinkDiscoverer; import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType; -import org.springframework.hateoas.config.mvc.WebMvcHateoasConfiguration; -import org.springframework.hateoas.config.reactive.WebFluxHateoasConfiguration; import org.springframework.hateoas.hal.HalLinkDiscoverer; import org.springframework.hateoas.hal.forms.HalFormsLinkDiscoverer; -import org.springframework.hateoas.support.WebStack; import org.springframework.hateoas.uber.UberLinkDiscoverer; import org.springframework.util.ClassUtils; @@ -47,7 +44,7 @@ import org.springframework.util.ClassUtils; * {@link ImportBeanDefinitionRegistrar} implementation to activate hypermedia support based on the configured * hypermedia type. Activates {@link EntityLinks} support as well (essentially as if {@link EnableEntityLinks} was * activated as well). - * + * * @author Oliver Gierke * @author Greg Turnquist */ @@ -70,7 +67,7 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe * collection using the application context. */ for (HypermediaType type : types) { - + BeanDefinitionBuilder hypermediaTypeBeanDefinition = genericBeanDefinition(HypermediaType.class, () -> type); registerSourcedBeanDefinition(hypermediaTypeBeanDefinition, metadata, registry); } @@ -87,30 +84,11 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe BeanDefinitionReaderUtils.generateBeanName(linkDiscovererBeanDefinition, registry)), registry); } } - - /* - * Register a Spring MVC-specific HATEOAS configuration. - */ - if (WebStack.WEBMVC.isAvailable()) { - - BeanDefinitionBuilder webMvcHateosConfiguration = rootBeanDefinition(WebMvcHateoasConfiguration.class); - registerSourcedBeanDefinition(webMvcHateosConfiguration, metadata, registry); - } - - /* - * Register a Spring WebFlux-specific HATEOAS configuration. - */ - if (WebStack.WEBFLUX.isAvailable()) { - - BeanDefinitionBuilder webFluxHateoasConfiguration = rootBeanDefinition(WebFluxHateoasConfiguration.class); - registerSourcedBeanDefinition(webFluxHateoasConfiguration, metadata, registry); - } - } /** * Returns a {@link LinkDiscoverer} {@link BeanDefinition} suitable for the given {@link HypermediaType}. - * + * * @param type * @return */ diff --git a/src/main/java/org/springframework/hateoas/config/WebStackImportSelector.java b/src/main/java/org/springframework/hateoas/config/WebStackImportSelector.java new file mode 100644 index 00000000..93c05e82 --- /dev/null +++ b/src/main/java/org/springframework/hateoas/config/WebStackImportSelector.java @@ -0,0 +1,51 @@ +/* + * 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 java.util.ArrayList; +import java.util.List; + +import org.springframework.context.annotation.ImportSelector; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.hateoas.support.WebStack; + +/** + * {@link ImportSelector} to include web stack specific configuration. + * + * @author Oliver Drotbohm + */ +class WebStackImportSelector implements ImportSelector { + + /* + * (non-Javadoc) + * @see org.springframework.context.annotation.ImportSelector#selectImports(org.springframework.core.type.AnnotationMetadata) + */ + @Override + public String[] selectImports(AnnotationMetadata importingClassMetadata) { + + List imports = new ArrayList<>(); + + if (WebStack.WEBMVC.isAvailable()) { + imports.add("org.springframework.hateoas.config.mvc.WebMvcHateoasConfiguration"); + } + + if (WebStack.WEBFLUX.isAvailable()) { + imports.add("org.springframework.hateoas.config.reactive.WebFluxHateoasConfiguration"); + } + + return imports.toArray(new String[imports.size()]); + } +} diff --git a/src/main/java/org/springframework/hateoas/config/mvc/HypermediaWebMvcConfigurer.java b/src/main/java/org/springframework/hateoas/config/mvc/HypermediaWebMvcConfigurer.java index b677642d..6d74947c 100644 --- a/src/main/java/org/springframework/hateoas/config/mvc/HypermediaWebMvcConfigurer.java +++ b/src/main/java/org/springframework/hateoas/config/mvc/HypermediaWebMvcConfigurer.java @@ -22,6 +22,7 @@ import lombok.RequiredArgsConstructor; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import org.springframework.beans.BeansException; @@ -101,10 +102,10 @@ public class HypermediaWebMvcConfigurer implements WebMvcConfigurer, BeanFactory if (this.hypermediaTypes.contains(HypermediaType.HAL_FORMS)) { - converters.add(0, - new TypeConstrainedMappingJackson2HttpMessageConverter(ResourceSupport.class, - Collections.singletonList(HAL_FORMS_JSON), createHalFormsObjectMapper(this.mapper, this.curieProvider, - this.relProvider, linkRelationMessageSource, this.halFormsConfiguration))); + converters.add(0, new TypeConstrainedMappingJackson2HttpMessageConverter( + ResourceSupport.class, Collections.singletonList(HAL_FORMS_JSON), + createHalFormsObjectMapper(this.mapper, this.curieProvider, this.relProvider, linkRelationMessageSource, + this.halFormsConfiguration))); } if (this.hypermediaTypes.contains(HypermediaType.COLLECTION_JSON)) { @@ -115,8 +116,8 @@ public class HypermediaWebMvcConfigurer implements WebMvcConfigurer, BeanFactory if (this.hypermediaTypes.contains(HypermediaType.UBER)) { - converters.add(0, new TypeConstrainedMappingJackson2HttpMessageConverter(ResourceSupport.class, - Collections.singletonList(UBER_JSON), createUberObjectMapper(this.mapper))); + converters.add(0, new TypeConstrainedMappingJackson2HttpMessageConverter( + ResourceSupport.class, Collections.singletonList(UBER_JSON), createUberObjectMapper(this.mapper))); } } } diff --git a/src/main/java/org/springframework/hateoas/config/mvc/WebMvcHateoasConfiguration.java b/src/main/java/org/springframework/hateoas/config/mvc/WebMvcHateoasConfiguration.java index c3afa0db..98908657 100644 --- a/src/main/java/org/springframework/hateoas/config/mvc/WebMvcHateoasConfiguration.java +++ b/src/main/java/org/springframework/hateoas/config/mvc/WebMvcHateoasConfiguration.java @@ -30,7 +30,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; /** * Spring MVC HATEOAS Configuration - * + * * @author Greg Turnquist */ @Configuration @@ -38,19 +38,13 @@ public class WebMvcHateoasConfiguration { @Bean HypermediaWebMvcConfigurer hypermediaWebMvcConfigurer(ObjectProvider mapper, - DelegatingRelProvider relProvider, - ObjectProvider curieProvider, - ObjectProvider halConfiguration, - ObjectProvider halFormsConfiguration, - Collection hypermediaTypes) { - - return new HypermediaWebMvcConfigurer( - mapper.getIfAvailable(ObjectMapper::new), - relProvider, - curieProvider.getIfAvailable(), - halConfiguration.getIfAvailable(HalConfiguration::new), - halFormsConfiguration.getIfAvailable(HalFormsConfiguration::new), - hypermediaTypes); + DelegatingRelProvider relProvider, ObjectProvider curieProvider, + ObjectProvider halConfiguration, ObjectProvider halFormsConfiguration, + Collection hypermediaTypes) { + + return new HypermediaWebMvcConfigurer(mapper.getIfAvailable(ObjectMapper::new), relProvider, + curieProvider.getIfAvailable(), halConfiguration.getIfAvailable(HalConfiguration::new), + halFormsConfiguration.getIfAvailable(HalFormsConfiguration::new), hypermediaTypes); } @Bean diff --git a/src/main/java/org/springframework/hateoas/config/reactive/WebFluxHateoasConfiguration.java b/src/main/java/org/springframework/hateoas/config/reactive/WebFluxHateoasConfiguration.java index 8c1e6d0f..9f82f4ee 100644 --- a/src/main/java/org/springframework/hateoas/config/reactive/WebFluxHateoasConfiguration.java +++ b/src/main/java/org/springframework/hateoas/config/reactive/WebFluxHateoasConfiguration.java @@ -31,7 +31,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; /** * Spring WebFlux HATEOAS configuration. - * + * * @author Greg Turnquist * @since 1.0 */ @@ -39,45 +39,34 @@ import com.fasterxml.jackson.databind.ObjectMapper; public class WebFluxHateoasConfiguration { @Bean - WebClientConfigurer webClientConfigurer(ObjectProvider mapper, - DelegatingRelProvider relProvider, - ObjectProvider curieProvider, - ObjectProvider halConfiguration, - ObjectProvider halFormsConfiguration, - Collection hypermediaTypes) { - - return new WebClientConfigurer( - mapper.getIfAvailable(ObjectMapper::new), - relProvider, - curieProvider.getIfAvailable(), - halConfiguration.getIfAvailable(HalConfiguration::new), - halFormsConfiguration.getIfAvailable(HalFormsConfiguration::new), - hypermediaTypes); + WebClientConfigurer webClientConfigurer(ObjectProvider mapper, DelegatingRelProvider relProvider, + ObjectProvider curieProvider, ObjectProvider halConfiguration, + ObjectProvider halFormsConfiguration, Collection hypermediaTypes) { + + return new WebClientConfigurer(mapper.getIfAvailable(ObjectMapper::new), relProvider, + curieProvider.getIfAvailable(), halConfiguration.getIfAvailable(HalConfiguration::new), + halFormsConfiguration.getIfAvailable(HalFormsConfiguration::new), hypermediaTypes); } @Bean HypermediaWebClientBeanPostProcessor webClientBeanPostProcessor(WebClientConfigurer configurer) { return new HypermediaWebClientBeanPostProcessor(configurer); } - + @Bean HypermediaWebFluxConfigurer hypermediaWebFluxConfigurer(ObjectProvider mapper, - DelegatingRelProvider relProvider, - ObjectProvider curieProvider, - ObjectProvider halConfiguration, - ObjectProvider halFormsConfiguration, - Collection hypermediaTypes) { + DelegatingRelProvider relProvider, ObjectProvider curieProvider, + ObjectProvider halConfiguration, ObjectProvider halFormsConfiguration, + Collection hypermediaTypes) { - return new HypermediaWebFluxConfigurer( - mapper.getIfAvailable(ObjectMapper::new), - relProvider, - curieProvider.getIfAvailable(), - halConfiguration.getIfAvailable(HalConfiguration::new), - halFormsConfiguration.getIfAvailable(HalFormsConfiguration::new), hypermediaTypes); + return new HypermediaWebFluxConfigurer(mapper.getIfAvailable(ObjectMapper::new), relProvider, + curieProvider.getIfAvailable(), halConfiguration.getIfAvailable(HalConfiguration::new), + halFormsConfiguration.getIfAvailable(HalFormsConfiguration::new), hypermediaTypes); } /** - * TODO: Replace with Spring Framework filter when https://github.com/spring-projects/spring-framework/issues/21746 is completed. + * TODO: Replace with Spring Framework filter when https://github.com/spring-projects/spring-framework/issues/21746 is + * completed. */ @Bean HypermediaWebFilter hypermediaWebFilter() { diff --git a/src/main/java/org/springframework/hateoas/core/DummyInvocationUtils.java b/src/main/java/org/springframework/hateoas/core/DummyInvocationUtils.java index 7057a37d..e60181e5 100644 --- a/src/main/java/org/springframework/hateoas/core/DummyInvocationUtils.java +++ b/src/main/java/org/springframework/hateoas/core/DummyInvocationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-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. diff --git a/src/main/java/org/springframework/hateoas/core/LinkBuilderSupport.java b/src/main/java/org/springframework/hateoas/core/LinkBuilderSupport.java index 0e8a24a9..74eed704 100644 --- a/src/main/java/org/springframework/hateoas/core/LinkBuilderSupport.java +++ b/src/main/java/org/springframework/hateoas/core/LinkBuilderSupport.java @@ -16,15 +16,16 @@ package org.springframework.hateoas.core; import static org.springframework.hateoas.core.EncodingUtils.*; -import static org.springframework.web.util.UriComponentsBuilder.*; import lombok.Getter; import java.net.URI; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.function.Function; import org.springframework.hateoas.Affordance; import org.springframework.hateoas.IanaLinkRelations; @@ -48,32 +49,36 @@ import org.springframework.web.util.UriComponentsBuilder; */ public abstract class LinkBuilderSupport implements LinkBuilder { - private final UriComponents uriComponents; - - private @Getter final List affordances; - - /** - * Creates a new {@link LinkBuilderSupport} using the given {@link UriComponentsBuilder}. - * - * @param builder must not be {@literal null}. - */ - public LinkBuilderSupport(UriComponentsBuilder builder) { - - Assert.notNull(builder, "UriComponentsBuilder must not be null!"); - this.uriComponents = builder.build(); - this.affordances = new ArrayList<>(); - } + private final UriComponentsBuilder builder; + private final @Getter List affordances; /** * Creates a new {@link LinkBuilderSupport} using the given {@link UriComponents}. * - * @param uriComponents must not be {@literal null}. + * @param builder must not be {@literal null}. */ - public LinkBuilderSupport(UriComponents uriComponents) { + protected LinkBuilderSupport(UriComponentsBuilder builder) { + this(builder, Collections.emptyList()); + } - Assert.notNull(uriComponents, "UriComponents must not be null!"); - this.uriComponents = uriComponents; - this.affordances = new ArrayList<>(); + protected LinkBuilderSupport(UriComponentsBuilder builder, List affordances) { + + Assert.notNull(builder, "UriComponents must not be null!"); + Assert.notNull(affordances, "Affordances must not be null!"); + + this.builder = builder.cloneBuilder(); + this.affordances = affordances; + } + + protected LinkBuilderSupport(UriComponents components, List affordances) { + + String uriString = components.toUriString(); + UriComponentsBuilder builder = uriString.isEmpty() // + ? UriComponentsBuilder.fromUri(components.toUri()) // + : UriComponentsBuilder.fromUriString(uriString); + + this.builder = builder; + this.affordances = affordances; } /* @@ -109,20 +114,20 @@ public abstract class LinkBuilderSupport implements LinkB protected T slash(UriComponents components, boolean encoded) { - String uriString = uriComponents.toUriString(); - UriComponentsBuilder builder = uriString.isEmpty() ? fromUri(uriComponents.toUri()) : fromUriString(uriString); + return withFreshBuilder(builder -> { - for (String pathSegment : components.getPathSegments()) { - builder.pathSegment(encoded ? pathSegment : encodePath(pathSegment)); - } + for (String pathSegment : components.getPathSegments()) { + builder.pathSegment(encoded ? pathSegment : encodePath(pathSegment)); + } - String fragment = components.getFragment(); + String fragment = components.getFragment(); - if (StringUtils.hasText(fragment)) { - builder.fragment(encoded ? fragment : encodeFragment(fragment)); - } + if (StringUtils.hasText(fragment)) { + builder.fragment(encoded ? fragment : encodeFragment(fragment)); + } - return createNewInstance(builder.query(components.getQuery())); + return createNewInstance(builder.query(components.getQuery()), affordances); + }); } /* @@ -143,13 +148,16 @@ public abstract class LinkBuilderSupport implements LinkB * @see org.springframework.hateoas.LinkBuilder#toUri() */ public URI toUri() { - return uriComponents.encode().toUri().normalize(); + return builder.build().toUri().normalize(); } public T addAffordances(Collection affordances) { - this.affordances.addAll(affordances); - return getThis(); + List newAffordances = new ArrayList<>(); + newAffordances.addAll(this.affordances); + newAffordances.addAll(affordances); + + return createNewInstance(builder, newAffordances); } /* @@ -176,7 +184,20 @@ public abstract class LinkBuilderSupport implements LinkB */ @Override public String toString() { - return uriComponents.toUriString(); + return builder.build().toUriString(); + } + + /** + * Executes the given {@link Function} using a freshly cloned {@link UriComponentsBuilder}. + * + * @param function must not be {@literal null}. + * @return + */ + protected S withFreshBuilder(Function function) { + + Assert.notNull(function, "Function must not be null!"); + + return function.apply(builder.cloneBuilder()); } /** @@ -192,5 +213,5 @@ public abstract class LinkBuilderSupport implements LinkB * @param builder will never be {@literal null}. * @return */ - protected abstract T createNewInstance(UriComponentsBuilder builder); + protected abstract T createNewInstance(UriComponentsBuilder builder, List affordances); } diff --git a/src/main/java/org/springframework/hateoas/mvc/SpringMvcAffordanceBuilder.java b/src/main/java/org/springframework/hateoas/core/SpringAffordanceBuilder.java similarity index 88% rename from src/main/java/org/springframework/hateoas/mvc/SpringMvcAffordanceBuilder.java rename to src/main/java/org/springframework/hateoas/core/SpringAffordanceBuilder.java index 00947875..52ab1c49 100644 --- a/src/main/java/org/springframework/hateoas/mvc/SpringMvcAffordanceBuilder.java +++ b/src/main/java/org/springframework/hateoas/core/SpringAffordanceBuilder.java @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.hateoas.mvc; +package org.springframework.hateoas.core; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.stream.Collectors; @@ -26,9 +25,6 @@ import org.springframework.hateoas.AffordanceModelFactory; import org.springframework.hateoas.Link; import org.springframework.hateoas.LinkRelation; import org.springframework.hateoas.QueryParameter; -import org.springframework.hateoas.core.MappingDiscoverer; -import org.springframework.hateoas.core.MethodInvocation; -import org.springframework.hateoas.core.MethodParameters; import org.springframework.http.HttpMethod; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; @@ -38,8 +34,9 @@ import org.springframework.web.util.UriComponents; * Extract information needed to assemble an {@link Affordance} from a Spring MVC web method. * * @author Greg Turnquist + * @author Oliver Drotbohm */ -class SpringMvcAffordanceBuilder { +public class SpringAffordanceBuilder { /** * Use the attributes of the current method call along with a collection of {@link AffordanceModelFactory}'s to create @@ -50,7 +47,7 @@ class SpringMvcAffordanceBuilder { * @param components * @return */ - public static Collection create(MethodInvocation invocation, MappingDiscoverer discoverer, + public static List create(MethodInvocation invocation, MappingDiscoverer discoverer, UriComponents components) { List affordances = new ArrayList<>(); diff --git a/src/main/java/org/springframework/hateoas/core/TemplateVariableAwareLinkBuilderSupport.java b/src/main/java/org/springframework/hateoas/core/TemplateVariableAwareLinkBuilderSupport.java new file mode 100644 index 00000000..7a8ce1a8 --- /dev/null +++ b/src/main/java/org/springframework/hateoas/core/TemplateVariableAwareLinkBuilderSupport.java @@ -0,0 +1,83 @@ +/* + * 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.core; + +import java.util.List; + +import org.springframework.hateoas.Affordance; +import org.springframework.hateoas.TemplateVariables; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * A {@link LinkBuilderSupport} extension that can keep a list of {@link TemplateVariables} around. + * + * @author Oliver Gierke + */ +public abstract class TemplateVariableAwareLinkBuilderSupport> + extends LinkBuilderSupport { + + private final TemplateVariables variables; + + protected TemplateVariableAwareLinkBuilderSupport(UriComponentsBuilder builder, TemplateVariables variables, + List affordances) { + + super(builder, affordances); + + this.variables = variables; + } + + protected TemplateVariableAwareLinkBuilderSupport(UriComponents components, TemplateVariables variables, + List affordances) { + + super(components, affordances); + + this.variables = variables; + } + + /* + * (non-Javadoc) + * @see org.springframework.hateoas.core.LinkBuilderSupport#createNewInstance(org.springframework.web.util.UriComponentsBuilder, java.util.List) + */ + @Override + protected final T createNewInstance(UriComponentsBuilder builder, List affordances) { + return createNewInstance(builder, affordances, variables); + } + + protected abstract T createNewInstance(UriComponentsBuilder builder, List affordances, + TemplateVariables variables); + + /* + * (non-Javadoc) + * @see org.springframework.hateoas.core.LinkBuilderSupport#toString() + */ + @Override + public String toString() { + + String result = super.toString(); + + if (variables == TemplateVariables.NONE) { + return result; + } + + if (!result.contains("#")) { + return result.concat(variables.toString()); + } + + String[] parts = result.split("#"); + return parts[0].concat(variables.toString()).concat("#").concat(parts[0]); + } +} diff --git a/src/main/java/org/springframework/hateoas/WebHandler.java b/src/main/java/org/springframework/hateoas/core/WebHandler.java similarity index 88% rename from src/main/java/org/springframework/hateoas/WebHandler.java rename to src/main/java/org/springframework/hateoas/core/WebHandler.java index 11d4a526..2016043b 100644 --- a/src/main/java/org/springframework/hateoas/WebHandler.java +++ b/src/main/java/org/springframework/hateoas/core/WebHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.hateoas; +package org.springframework.hateoas.core; import static org.springframework.hateoas.TemplateVariable.VariableType.*; import static org.springframework.hateoas.TemplateVariables.*; @@ -38,12 +38,10 @@ import org.springframework.core.MethodParameter; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; import org.springframework.format.support.DefaultFormattingConversionService; -import org.springframework.hateoas.core.AnnotationAttribute; -import org.springframework.hateoas.core.AnnotationMappingDiscoverer; -import org.springframework.hateoas.core.LastInvocationAware; -import org.springframework.hateoas.core.MappingDiscoverer; -import org.springframework.hateoas.core.MethodInvocation; -import org.springframework.hateoas.core.MethodParameters; +import org.springframework.hateoas.Affordance; +import org.springframework.hateoas.LinkBuilder; +import org.springframework.hateoas.TemplateVariable; +import org.springframework.hateoas.TemplateVariables; import org.springframework.hateoas.mvc.ControllerLinkBuilder; import org.springframework.util.Assert; import org.springframework.util.ConcurrentReferenceHashMap; @@ -59,19 +57,29 @@ import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriTemplate; /** - * Utility for taking a a method invocation and extracting a {@link ControllerLinkBuilder}. + * Utility for taking a method invocation and extracting a {@link ControllerLinkBuilder}. * * @author Greg Turnquist */ public class WebHandler { private static final MappingDiscoverer DISCOVERER = new AnnotationMappingDiscoverer(RequestMapping.class); - private static final AnnotatedParametersParameterAccessor PATH_VARIABLE_ACCESSOR = new AnnotatedParametersParameterAccessor( - new AnnotationAttribute(PathVariable.class)); - private static final AnnotatedParametersParameterAccessor REQUEST_PARAM_ACCESSOR = new RequestParamParameterAccessor(); + private static final AnnotatedParametersParameterAccessor PATH_VARIABLE_ACCESSOR // + = new AnnotatedParametersParameterAccessor(new AnnotationAttribute(PathVariable.class)); + private static final AnnotatedParametersParameterAccessor REQUEST_PARAM_ACCESSOR // + = new RequestParamParameterAccessor(); - public static ControllerLinkBuilder linkTo(Object invocationValue, - Function mappingToUriComponentsBuilder, + public interface LinkBuilderCreator { + T createBuilder(UriComponents components, TemplateVariables variables, List affordances); + } + + public static T linkTo(Object invocationValue, + Function mappingToUriComponentsBuilder, LinkBuilderCreator creator) { + return linkTo(invocationValue, mappingToUriComponentsBuilder, creator, null); + } + + public static T linkTo(Object invocationValue, + Function mappingToUriComponentsBuilder, LinkBuilderCreator creator, BiFunction additionalUriHandler) { Assert.isInstanceOf(LastInvocationAware.class, invocationValue); @@ -92,13 +100,15 @@ public class WebHandler { values.put(names.next(), encodePath(classMappingParameters.next())); } - for (AnnotatedParametersParameterAccessor.BoundMethodParameter parameter : PATH_VARIABLE_ACCESSOR.getBoundParameters(invocation)) { + for (AnnotatedParametersParameterAccessor.BoundMethodParameter parameter : PATH_VARIABLE_ACCESSOR + .getBoundParameters(invocation)) { values.put(parameter.getVariableName(), encodePath(parameter.asString())); } List optionalEmptyParameters = new ArrayList<>(); - for (AnnotatedParametersParameterAccessor.BoundMethodParameter parameter : REQUEST_PARAM_ACCESSOR.getBoundParameters(invocation)) { + for (AnnotatedParametersParameterAccessor.BoundMethodParameter parameter : REQUEST_PARAM_ACCESSOR + .getBoundParameters(invocation)) { bindRequestParameters(builder, parameter); @@ -132,7 +142,9 @@ public class WebHandler { variables = variables.concat(variable); } - return new ControllerLinkBuilder(components, variables, invocation); + List affordances = SpringAffordanceBuilder.create(invocation, DISCOVERER, components); + + return creator.createBuilder(components, variables, affordances); } /** @@ -143,7 +155,8 @@ public class WebHandler { * @param parameter must not be {@literal null}. */ @SuppressWarnings("unchecked") - private static void bindRequestParameters(UriComponentsBuilder builder, AnnotatedParametersParameterAccessor.BoundMethodParameter parameter) { + private static void bindRequestParameters(UriComponentsBuilder builder, + AnnotatedParametersParameterAccessor.BoundMethodParameter parameter) { Object value = parameter.getValue(); String key = parameter.getVariableName(); @@ -248,15 +261,16 @@ public class WebHandler { } /** - * Value object to allow accessing {@link MethodInvocation} parameters with the configured {@link AnnotationAttribute}. + * Value object to allow accessing {@link MethodInvocation} parameters with the configured + * {@link AnnotationAttribute}. * * @author Oliver Gierke */ @RequiredArgsConstructor private static class AnnotatedParametersParameterAccessor { - private static final Map METHOD_PARAMETERS_CACHE = new ConcurrentReferenceHashMap<>( - 16, ConcurrentReferenceHashMap.ReferenceType.WEAK); + private static final Map METHOD_PARAMETERS_CACHE = new ConcurrentReferenceHashMap<>(16, + ConcurrentReferenceHashMap.ReferenceType.WEAK); private final @NonNull AnnotationAttribute attribute; @@ -297,7 +311,7 @@ public class WebHandler { * @return */ protected BoundMethodParameter createParameter(MethodParameter parameter, Object value, - AnnotationAttribute attribute) { + AnnotationAttribute attribute) { return new BoundMethodParameter(parameter, value, attribute); } diff --git a/src/main/java/org/springframework/hateoas/mvc/BasicLinkBuilder.java b/src/main/java/org/springframework/hateoas/mvc/BasicLinkBuilder.java deleted file mode 100644 index 9913a5d5..00000000 --- a/src/main/java/org/springframework/hateoas/mvc/BasicLinkBuilder.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2012 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.mvc; - -import org.springframework.hateoas.LinkBuilder; -import org.springframework.hateoas.core.LinkBuilderSupport; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import org.springframework.web.util.UriComponentsBuilder; - -/** - * Simples {@link LinkBuilder} implementation possible. Exposes a link to the current servlet mapping only. - * - * @author Oliver Gierke - */ -public class BasicLinkBuilder extends LinkBuilderSupport { - - /** - * Creates a new {@link BasicLinkBuilder} using the given {@link UriComponentsBuilder}. - * - * @param builder must not be {@literal null}. - */ - private BasicLinkBuilder(UriComponentsBuilder builder) { - super(builder); - } - - /** - * Creates a new {@link BasicLinkBuilder} to link to the current servlet mapping. - * - * @return - */ - public static BasicLinkBuilder linkToCurrentMapping() { - return new BasicLinkBuilder(ServletUriComponentsBuilder.fromCurrentServletMapping()); - } - - /* - * (non-Javadoc) - * @see org.springframework.hateoas.mvc.LinkBuilderSupport#createNewInstance(org.springframework.web.util.UriComponentsBuilder) - */ - @Override - protected BasicLinkBuilder createNewInstance(UriComponentsBuilder builder) { - return new BasicLinkBuilder(builder); - } - - /* - * (non-Javadoc) - * @see org.springframework.hateoas.mvc.LinkBuilderSupport#getThis() - */ - @Override - protected BasicLinkBuilder getThis() { - return this; - } -} diff --git a/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilder.java b/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilder.java index 21224490..ffabd33a 100755 --- a/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilder.java +++ b/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilder.java @@ -17,7 +17,8 @@ package org.springframework.hateoas.mvc; import java.lang.reflect.Method; import java.net.URI; -import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Map; import org.springframework.hateoas.Affordance; @@ -26,9 +27,8 @@ import org.springframework.hateoas.TemplateVariables; import org.springframework.hateoas.core.AnnotationMappingDiscoverer; import org.springframework.hateoas.core.CachingMappingDiscoverer; import org.springframework.hateoas.core.DummyInvocationUtils; -import org.springframework.hateoas.core.LinkBuilderSupport; import org.springframework.hateoas.core.MappingDiscoverer; -import org.springframework.hateoas.core.MethodInvocation; +import org.springframework.hateoas.core.TemplateVariableAwareLinkBuilderSupport; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.context.request.RequestContextHolder; @@ -48,42 +48,28 @@ import org.springframework.web.util.UriTemplate; * @author Oliver Trosien * @author Greg Turnquist */ -public class ControllerLinkBuilder extends LinkBuilderSupport { +public class ControllerLinkBuilder extends TemplateVariableAwareLinkBuilderSupport { private static final MappingDiscoverer DISCOVERER = CachingMappingDiscoverer .of(new AnnotationMappingDiscoverer(RequestMapping.class)); private static final ControllerLinkBuilderFactory FACTORY = new ControllerLinkBuilderFactory(); private static final CustomUriTemplateHandler HANDLER = new CustomUriTemplateHandler(); - private final TemplateVariables variables; - /** * Creates a new {@link ControllerLinkBuilder} using the given {@link UriComponentsBuilder}. * * @param builder must not be {@literal null}. */ ControllerLinkBuilder(UriComponentsBuilder builder) { - - super(builder); - - this.variables = TemplateVariables.NONE; + this(builder, TemplateVariables.NONE, Collections.emptyList()); } - /** - * Creates a new {@link ControllerLinkBuilder} using the given {@link UriComponents}. - * - * @param uriComponents must not be {@literal null}. - */ - ControllerLinkBuilder(UriComponents uriComponents) { - this(uriComponents, TemplateVariables.NONE, null); + ControllerLinkBuilder(UriComponentsBuilder builder, TemplateVariables variables, List affordances) { + super(builder, variables, affordances); } - public ControllerLinkBuilder(UriComponents uriComponents, TemplateVariables variables, MethodInvocation invocation) { - - super(uriComponents); - - this.variables = variables; - this.addAffordances(findAffordances(invocation, uriComponents)); + ControllerLinkBuilder(UriComponents uriComponents, TemplateVariables variables, List affordances) { + super(uriComponents, variables, affordances); } /** @@ -233,11 +219,12 @@ public class ControllerLinkBuilder extends LinkBuilderSupport affordances, + TemplateVariables variables) { + return new ControllerLinkBuilder(builder, variables, affordances); } /** @@ -249,27 +236,6 @@ public class ControllerLinkBuilder extends LinkBuilderSupport findAffordances(MethodInvocation invocation, UriComponents components) { - return SpringMvcAffordanceBuilder.create(invocation, DISCOVERER, components); - } - private static class CustomUriTemplateHandler extends DefaultUriTemplateHandler { public CustomUriTemplateHandler() { diff --git a/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilderFactory.java b/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilderFactory.java index 259b2205..a7197b68 100644 --- a/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilderFactory.java +++ b/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilderFactory.java @@ -28,7 +28,7 @@ import org.springframework.hateoas.Link; import org.springframework.hateoas.MethodLinkBuilderFactory; import org.springframework.hateoas.core.LinkBuilderSupport; import org.springframework.hateoas.core.MethodParameters; -import org.springframework.hateoas.WebHandler; +import org.springframework.hateoas.core.WebHandler; /** * Factory for {@link LinkBuilderSupport} instances based on the request mapping annotated on the given controller. @@ -45,12 +45,6 @@ import org.springframework.hateoas.WebHandler; */ public class ControllerLinkBuilderFactory implements MethodLinkBuilderFactory { - private static final MappingDiscoverer DISCOVERER = CachingMappingDiscoverer - .of(new AnnotationMappingDiscoverer(RequestMapping.class)); - private static final AnnotatedParametersParameterAccessor PATH_VARIABLE_ACCESSOR = new AnnotatedParametersParameterAccessor( - new AnnotationAttribute(PathVariable.class)); - private static final AnnotatedParametersParameterAccessor REQUEST_PARAM_ACCESSOR = new RequestParamParameterAccessor(); - private List uriComponentsContributors = new ArrayList<>(); /** @@ -107,26 +101,25 @@ public class ControllerLinkBuilderFactory implements MethodLinkBuilderFactory ControllerLinkBuilder.getBuilder().path(mapping), - (builder, invocation) -> { - - MethodParameters parameters = new MethodParameters(invocation.getMethod()); - Iterator parameterValues = Arrays.asList(invocation.getArguments()).iterator(); + return WebHandler.linkTo(invocationValue, mapping -> ControllerLinkBuilder.getBuilder().path(mapping), + ControllerLinkBuilder::new, (builder, invocation) -> { - for (MethodParameter parameter : parameters.getParameters()) { - Object parameterValue = parameterValues.next(); - - for (UriComponentsContributor contributor : this.uriComponentsContributors) { - - if (contributor.supportsParameter(parameter)) { - contributor.enhance(builder, parameter, parameterValue); + MethodParameters parameters = new MethodParameters(invocation.getMethod()); + Iterator parameterValues = Arrays.asList(invocation.getArguments()).iterator(); + + for (MethodParameter parameter : parameters.getParameters()) { + Object parameterValue = parameterValues.next(); + + for (UriComponentsContributor contributor : this.uriComponentsContributors) { + + if (contributor.supportsParameter(parameter)) { + contributor.enhance(builder, parameter, parameterValue); + } } } - } - return builder; - }); + return builder; + }); } /* diff --git a/src/main/java/org/springframework/hateoas/reactive/ReactiveLinkBuilder.java b/src/main/java/org/springframework/hateoas/reactive/ReactiveLinkBuilder.java index 2b481e70..1b44d7f1 100644 --- a/src/main/java/org/springframework/hateoas/reactive/ReactiveLinkBuilder.java +++ b/src/main/java/org/springframework/hateoas/reactive/ReactiveLinkBuilder.java @@ -19,10 +19,15 @@ import static org.springframework.hateoas.reactive.HypermediaWebFilter.*; import reactor.core.publisher.Mono; +import java.util.List; + +import org.springframework.hateoas.Affordance; import org.springframework.hateoas.Link; -import org.springframework.hateoas.WebHandler; -import org.springframework.hateoas.mvc.ControllerLinkBuilder; +import org.springframework.hateoas.TemplateVariables; +import org.springframework.hateoas.core.TemplateVariableAwareLinkBuilderSupport; +import org.springframework.hateoas.core.WebHandler; import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; /** @@ -31,12 +36,14 @@ import org.springframework.web.util.UriComponentsBuilder; * @author Greg Turnquist * @since 1.0 */ -public class ReactiveLinkBuilder { +public class ReactiveLinkBuilder extends TemplateVariableAwareLinkBuilderSupport { - private final ControllerLinkBuilder controllerLinkBuilder; + private ReactiveLinkBuilder(UriComponentsBuilder builder, TemplateVariables variables, List affordances) { + super(builder, variables, affordances); + } - private ReactiveLinkBuilder(ControllerLinkBuilder controllerLinkBuilder) { - this.controllerLinkBuilder = controllerLinkBuilder; + private ReactiveLinkBuilder(UriComponents components, TemplateVariables variables, List affordances) { + super(components, variables, affordances); } /** @@ -61,26 +68,9 @@ public class ReactiveLinkBuilder { */ public static ReactiveLinkBuilder linkTo(Object invocationValue, ServerWebExchange exchange) { - ControllerLinkBuilder controllerLinkBuilder = WebHandler // - .linkTo(invocationValue, path -> getBuilder(exchange).replacePath(path == null ? "/" : path), null); - - return new ReactiveLinkBuilder(controllerLinkBuilder); - } - - /** - * Utility method to transform {@link ReactiveLinkBuilder} into a {@link Link}. - */ - public Link withSelfRel() { - return this.controllerLinkBuilder.withSelfRel(); - } - - /** - * Utility method to transform {@link ReactiveLinkBuilder} into a {@link Link}. - * - * @param rel - */ - public Link withRel(String rel) { - return this.controllerLinkBuilder.withRel(rel); + return WebHandler.linkTo(invocationValue, // + path -> getBuilder(exchange).replacePath(path == null ? "/" : path), // + ReactiveLinkBuilder::new); } /** @@ -94,4 +84,23 @@ public class ReactiveLinkBuilder { ? UriComponentsBuilder.fromPath("/") // : UriComponentsBuilder.fromHttpRequest(exchange.getRequest()); } + + /* + * (non-Javadoc) + * @see org.springframework.hateoas.core.TemplateVariableAwareLinkBuilderSupport#createNewInstance(org.springframework.web.util.UriComponentsBuilder, java.util.List, org.springframework.hateoas.TemplateVariables) + */ + @Override + protected ReactiveLinkBuilder createNewInstance(UriComponentsBuilder builder, List affordances, + TemplateVariables variables) { + return new ReactiveLinkBuilder(builder, variables, affordances); + } + + /* + * (non-Javadoc) + * @see org.springframework.hateoas.core.LinkBuilderSupport#getThis() + */ + @Override + protected ReactiveLinkBuilder getThis() { + return this; + } } diff --git a/src/test/java/org/springframework/hateoas/config/EnableHypermediaSupportIntegrationTest.java b/src/test/java/org/springframework/hateoas/config/EnableHypermediaSupportIntegrationTest.java index a2aa590c..6d345d82 100755 --- a/src/test/java/org/springframework/hateoas/config/EnableHypermediaSupportIntegrationTest.java +++ b/src/test/java/org/springframework/hateoas/config/EnableHypermediaSupportIntegrationTest.java @@ -17,7 +17,7 @@ package org.springframework.hateoas.config; import static org.assertj.core.api.Assertions.*; import static org.springframework.hateoas.hal.HalConfiguration.RenderSingleLinks.*; -import static org.springframework.hateoas.support.ContextTester.withServletContext; +import static org.springframework.hateoas.support.ContextTester.*; import java.lang.reflect.Method; import java.util.List; @@ -52,12 +52,9 @@ import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.mock.web.MockServletContext; import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.test.web.servlet.MockMvc; import org.springframework.util.ReflectionUtils; import org.springframework.web.client.RestTemplate; -import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite; import org.springframework.web.servlet.config.annotation.EnableWebMvc; diff --git a/src/test/java/org/springframework/hateoas/config/mvc/HypermediaRestTemplateBeanPostProcessorTest.java b/src/test/java/org/springframework/hateoas/config/mvc/HypermediaRestTemplateBeanPostProcessorTest.java index de0b6cc1..6a979d31 100644 --- a/src/test/java/org/springframework/hateoas/config/mvc/HypermediaRestTemplateBeanPostProcessorTest.java +++ b/src/test/java/org/springframework/hateoas/config/mvc/HypermediaRestTemplateBeanPostProcessorTest.java @@ -35,64 +35,72 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert import org.springframework.web.client.RestTemplate; /** + * Tests the registration of media types by the {@link HypermediaRestTemplateBeanPostProcessor}. + * * @author Greg Turnquist */ public class HypermediaRestTemplateBeanPostProcessorTest { + /** + * @see #728 + */ @Test public void shouldRegisterJustHal() { withContext(HalConfig.class, context -> { - assertThat(lookupSupportedHypermediaTypes(context.getBean(RestTemplate.class))) - .containsExactlyInAnyOrder( - MediaTypes.HAL_JSON, - MediaTypes.HAL_JSON_UTF8, - MediaType.APPLICATION_JSON, - MediaType.parseMediaType("application/*+json")); + assertThat(lookupSupportedHypermediaTypes(context.getBean(RestTemplate.class))) // + .containsExactlyInAnyOrder( // + MediaTypes.HAL_JSON, // + MediaTypes.HAL_JSON_UTF8, // + MediaType.APPLICATION_JSON, // + MediaType.parseMediaType("application/*+json")); }); } + /** + * @see #728 + */ @Test public void shouldRegisterHalAndCollectionJsonMessageConverters() { withContext(HalAndCollectionJsonConfig.class, context -> { - assertThat(lookupSupportedHypermediaTypes(context.getBean(RestTemplate.class))) - .containsExactlyInAnyOrder( - MediaTypes.HAL_JSON, - MediaTypes.HAL_JSON_UTF8, - MediaTypes.COLLECTION_JSON, - MediaType.APPLICATION_JSON, - MediaType.parseMediaType("application/*+json")); + assertThat(lookupSupportedHypermediaTypes(context.getBean(RestTemplate.class))) // + .containsExactlyInAnyOrder( // + MediaTypes.HAL_JSON, // + MediaTypes.HAL_JSON_UTF8, // + MediaTypes.COLLECTION_JSON, // + MediaType.APPLICATION_JSON, // + MediaType.parseMediaType("application/*+json")); }); } + /** + * @see #728 + */ @Test public void shouldRegisterHypermediaMessageConverters() { withContext(AllHypermediaConfig.class, context -> { - assertThat(lookupSupportedHypermediaTypes(context.getBean(RestTemplate.class))) - .containsExactlyInAnyOrder( - MediaTypes.HAL_JSON, - MediaTypes.HAL_JSON_UTF8, - MediaTypes.HAL_FORMS_JSON, - MediaTypes.COLLECTION_JSON, - MediaTypes.UBER_JSON, - MediaType.APPLICATION_JSON, - MediaType.parseMediaType("application/*+json")); + assertThat(lookupSupportedHypermediaTypes(context.getBean(RestTemplate.class))) // + .containsExactlyInAnyOrder( // + MediaTypes.HAL_JSON, // + MediaTypes.HAL_JSON_UTF8, // + MediaTypes.HAL_FORMS_JSON, // + MediaTypes.COLLECTION_JSON, // + MediaTypes.UBER_JSON, // + MediaType.APPLICATION_JSON, // + MediaType.parseMediaType("application/*+json")); }); } private List lookupSupportedHypermediaTypes(RestTemplate restTemplate) { - - return restTemplate.getMessageConverters().stream() - .filter(MappingJackson2HttpMessageConverter.class::isInstance) - .map(AbstractJackson2HttpMessageConverter.class::cast) - .map(AbstractHttpMessageConverter::getSupportedMediaTypes) - .flatMap(Collection::stream) - .collect(Collectors.toList()); + + return restTemplate.getMessageConverters().stream().filter(MappingJackson2HttpMessageConverter.class::isInstance) + .map(AbstractJackson2HttpMessageConverter.class::cast).map(AbstractHttpMessageConverter::getSupportedMediaTypes) + .flatMap(Collection::stream).collect(Collectors.toList()); } static class BaseConfig { @@ -105,16 +113,14 @@ public class HypermediaRestTemplateBeanPostProcessorTest { @Configuration @EnableHypermediaSupport(type = HypermediaType.HAL) - static class HalConfig extends BaseConfig { - } + static class HalConfig extends BaseConfig {} @Configuration - @EnableHypermediaSupport(type = {HypermediaType.HAL, HypermediaType.COLLECTION_JSON}) - static class HalAndCollectionJsonConfig extends BaseConfig { - } + @EnableHypermediaSupport(type = { HypermediaType.HAL, HypermediaType.COLLECTION_JSON }) + static class HalAndCollectionJsonConfig extends BaseConfig {} @Configuration - @EnableHypermediaSupport(type = {HypermediaType.HAL, HypermediaType.HAL_FORMS, HypermediaType.COLLECTION_JSON, HypermediaType.UBER}) - static class AllHypermediaConfig extends BaseConfig { - } -} \ No newline at end of file + @EnableHypermediaSupport( + type = { HypermediaType.HAL, HypermediaType.HAL_FORMS, HypermediaType.COLLECTION_JSON, HypermediaType.UBER }) + static class AllHypermediaConfig extends BaseConfig {} +} diff --git a/src/test/java/org/springframework/hateoas/config/reactive/HypermediaWebClientBeanPostProcessorTest.java b/src/test/java/org/springframework/hateoas/config/reactive/HypermediaWebClientBeanPostProcessorTest.java index ac1d7954..36abd55b 100644 --- a/src/test/java/org/springframework/hateoas/config/reactive/HypermediaWebClientBeanPostProcessorTest.java +++ b/src/test/java/org/springframework/hateoas/config/reactive/HypermediaWebClientBeanPostProcessorTest.java @@ -20,7 +20,6 @@ import static org.springframework.hateoas.support.ContextTester.*; import reactor.test.StepVerifier; -import java.io.IOException; import java.net.URI; import org.junit.After; @@ -42,6 +41,8 @@ import org.springframework.hateoas.mvc.TypeReferences.ResourceType; import org.springframework.web.reactive.function.client.WebClient; /** + * Tests registration of proper decoders by the {@link HypermediaWebClientBeanPostProcessor}. + * * @author Greg Turnquist */ public class HypermediaWebClientBeanPostProcessorTest { @@ -75,6 +76,9 @@ public class HypermediaWebClientBeanPostProcessorTest { } } + /** + * @see #728 + */ @Test public void shouldHandleRootHalDocument() { @@ -96,6 +100,9 @@ public class HypermediaWebClientBeanPostProcessorTest { }); } + /** + * @see #728 + */ @Test public void shouldHandleNavigatingToAResourceObject() { diff --git a/src/test/java/org/springframework/hateoas/core/ControllerEntityLinksUnitTest.java b/src/test/java/org/springframework/hateoas/core/ControllerEntityLinksUnitTest.java index 232b8f57..d3a59145 100755 --- a/src/test/java/org/springframework/hateoas/core/ControllerEntityLinksUnitTest.java +++ b/src/test/java/org/springframework/hateoas/core/ControllerEntityLinksUnitTest.java @@ -15,9 +15,9 @@ */ package org.springframework.hateoas.core; +import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*; @@ -49,7 +49,7 @@ public class ControllerEntityLinksUnitTest extends TestUtils { public void rejectsUnannotatedController() { assertThatExceptionOfType(IllegalArgumentException.class) // - .isThrownBy(() -> new ControllerEntityLinks(Arrays.asList(InvalidController.class), linkBuilderFactory)) // + .isThrownBy(() -> new ControllerEntityLinks(singletonList(InvalidController.class), linkBuilderFactory)) // .withMessageContaining(InvalidController.class.getName()); } @@ -63,14 +63,14 @@ public class ControllerEntityLinksUnitTest extends TestUtils { public void rejectsNullLinkBuilderFactory() { assertThatExceptionOfType(IllegalArgumentException.class) // - .isThrownBy(() -> new ControllerEntityLinks(Arrays.asList(SampleController.class), null)); + .isThrownBy(() -> new ControllerEntityLinks(singletonList(SampleController.class), null)); } @Test public void registersControllerForEntity() { when(linkBuilderFactory.linkTo(SampleController.class, new Object[0])).thenReturn(linkTo(SampleController.class)); - EntityLinks links = new ControllerEntityLinks(Arrays.asList(SampleController.class), linkBuilderFactory); + EntityLinks links = new ControllerEntityLinks(singletonList(SampleController.class), linkBuilderFactory); assertThat(links.supports(Person.class)).isTrue(); assertThat(links.linkFor(Person.class)).isNotNull(); @@ -85,7 +85,7 @@ public class ControllerEntityLinksUnitTest extends TestUtils { when(linkBuilderFactory.linkTo(eq(ControllerWithParameters.class), (Object[]) any())) // .thenReturn(linkTo(ControllerWithParameters.class, "1")); - ControllerEntityLinks links = new ControllerEntityLinks(Arrays.asList(ControllerWithParameters.class), + ControllerEntityLinks links = new ControllerEntityLinks(singletonList(ControllerWithParameters.class), linkBuilderFactory); LinkBuilder builder = links.linkFor(Order.class, "1"); diff --git a/src/test/java/org/springframework/hateoas/core/LinkBuilderSupportUnitTest.java b/src/test/java/org/springframework/hateoas/core/LinkBuilderSupportUnitTest.java index 67f58664..dc252848 100755 --- a/src/test/java/org/springframework/hateoas/core/LinkBuilderSupportUnitTest.java +++ b/src/test/java/org/springframework/hateoas/core/LinkBuilderSupportUnitTest.java @@ -17,13 +17,17 @@ package org.springframework.hateoas.core; import static org.assertj.core.api.Assertions.*; +import java.util.Collections; +import java.util.List; + import org.junit.Test; +import org.springframework.hateoas.Affordance; import org.springframework.hateoas.TestUtils; import org.springframework.web.util.UriComponentsBuilder; /** * Unit tests for {@link LinkBuilderSupport}. - * + * * @author Oliver Gierke * @author Kamill Sokol */ @@ -32,14 +36,14 @@ public class LinkBuilderSupportUnitTest extends TestUtils { @Test public void callingSlashWithEmptyStringIsNoOp() { - SampleLinkBuilder builder = new SampleLinkBuilder(UriComponentsBuilder.newInstance()); + SampleLinkBuilder builder = new SampleLinkBuilder(UriComponentsBuilder.newInstance(), Collections.emptyList()); assertThat(builder.slash("")).isEqualTo(builder); } @Test public void appendsFragmentCorrectly() { - SampleLinkBuilder builder = new SampleLinkBuilder(UriComponentsBuilder.newInstance()); + SampleLinkBuilder builder = new SampleLinkBuilder(UriComponentsBuilder.newInstance(), Collections.emptyList()); builder = builder.slash("foo#bar"); assertThat(builder.toString()).endsWith("foo#bar"); builder = builder.slash("bar"); @@ -58,7 +62,7 @@ public class LinkBuilderSupportUnitTest extends TestUtils { @Test public void appendsPathContainingColonsCorrectly() { - SampleLinkBuilder builder = new SampleLinkBuilder(UriComponentsBuilder.newInstance()); + SampleLinkBuilder builder = new SampleLinkBuilder(UriComponentsBuilder.newInstance(), Collections.emptyList()); builder = builder.slash("47:11"); @@ -67,8 +71,8 @@ public class LinkBuilderSupportUnitTest extends TestUtils { static class SampleLinkBuilder extends LinkBuilderSupport { - public SampleLinkBuilder(UriComponentsBuilder builder) { - super(builder); + public SampleLinkBuilder(UriComponentsBuilder builder, List afforances) { + super(builder, afforances); } @Override @@ -76,9 +80,13 @@ public class LinkBuilderSupportUnitTest extends TestUtils { return this; } + /* + * (non-Javadoc) + * @see org.springframework.hateoas.core.LinkBuilderSupport#createNewInstance(org.springframework.web.util.UriComponentsBuilder, java.util.List) + */ @Override - protected SampleLinkBuilder createNewInstance(UriComponentsBuilder builder) { - return new SampleLinkBuilder(builder); + protected SampleLinkBuilder createNewInstance(UriComponentsBuilder builder, List affordances) { + return new SampleLinkBuilder(builder, affordances); } } } diff --git a/src/test/java/org/springframework/hateoas/reactive/HypermediaWebFilterTest.java b/src/test/java/org/springframework/hateoas/reactive/HypermediaWebFilterTest.java index 0e8fc43b..99eb54f7 100644 --- a/src/test/java/org/springframework/hateoas/reactive/HypermediaWebFilterTest.java +++ b/src/test/java/org/springframework/hateoas/reactive/HypermediaWebFilterTest.java @@ -60,6 +60,9 @@ public class HypermediaWebFilterTest { .exchangeStrategies(webClientConfigurer.hypermediaExchangeStrategies()).build(); } + /** + * @see #728 + */ @Test public void webFilterShouldEmbedExchangeIntoContext() { diff --git a/src/test/java/org/springframework/hateoas/reactive/ReactiveLinkBuilderTest.java b/src/test/java/org/springframework/hateoas/reactive/ReactiveLinkBuilderTest.java index aadeb777..d45787db 100644 --- a/src/test/java/org/springframework/hateoas/reactive/ReactiveLinkBuilderTest.java +++ b/src/test/java/org/springframework/hateoas/reactive/ReactiveLinkBuilderTest.java @@ -50,6 +50,9 @@ public class ReactiveLinkBuilderTest { @Mock ServerWebExchange exchange; @Mock ServerHttpRequest request; + /** + * @see #728 + */ @Test public void linkAtSameLevelAsExplicitServerExchangeShouldWork() throws URISyntaxException { @@ -64,6 +67,9 @@ public class ReactiveLinkBuilderTest { assertThat(link.getHref()).isEqualTo("http://localhost:8080/api"); } + /** + * @see #728 + */ @Test public void linkAtSameLevelAsContextProvidedServerExchangeShouldWork() throws URISyntaxException { @@ -83,6 +89,9 @@ public class ReactiveLinkBuilderTest { }).verifyComplete(); } + /** + * @see #728 + */ @Test public void shallowLinkFromDeepExplicitServerExchangeShouldWork() throws URISyntaxException { @@ -97,6 +106,9 @@ public class ReactiveLinkBuilderTest { assertThat(link.getHref()).isEqualTo("http://localhost:8080/api"); } + /** + * @see #728 + */ @Test public void shallowLinkFromDeepContextProvidedServerExchangeShouldWork() throws URISyntaxException { @@ -116,6 +128,9 @@ public class ReactiveLinkBuilderTest { }).verifyComplete(); } + /** + * @see #728 + */ @Test public void deepLinkFromShallowExplicitServerExchangeShouldWork() throws URISyntaxException { @@ -130,6 +145,9 @@ public class ReactiveLinkBuilderTest { assertThat(link.getHref()).isEqualTo("http://localhost:8080/api/employees"); } + /** + * @see #728 + */ @Test public void deepLinkFromShallowContextProvidedServerExchangeShouldWork() throws URISyntaxException { @@ -149,6 +167,9 @@ public class ReactiveLinkBuilderTest { }).verifyComplete(); } + /** + * @see #728 + */ @Test public void linkToRouteWithNoMappingShouldWork() throws URISyntaxException { @@ -168,6 +189,9 @@ public class ReactiveLinkBuilderTest { }).verifyComplete(); } + /** + * @see #728 + */ @Test public void linkToRouteWithNoExchangeInTheContextShouldFallbackToRelativeUris() throws URISyntaxException { @@ -183,6 +207,9 @@ public class ReactiveLinkBuilderTest { }).verifyComplete(); } + /** + * @see #728 + */ @Test public void linkToRouteWithExplictExchangeBeingNullShouldFallbackToRelativeUris() throws URISyntaxException { diff --git a/src/test/java/org/springframework/hateoas/reactive/ReactiveResourceAssemblerUnitTest.java b/src/test/java/org/springframework/hateoas/reactive/ReactiveResourceAssemblerUnitTest.java index 90354258..d872b384 100644 --- a/src/test/java/org/springframework/hateoas/reactive/ReactiveResourceAssemblerUnitTest.java +++ b/src/test/java/org/springframework/hateoas/reactive/ReactiveResourceAssemblerUnitTest.java @@ -57,6 +57,9 @@ public class ReactiveResourceAssemblerUnitTest { this.exchange = mock(ServerWebExchange.class); } + /** + * @see #728 + */ @Test public void simpleConversionShouldWork() { @@ -71,6 +74,9 @@ public class ReactiveResourceAssemblerUnitTest { }).verifyComplete(); } + /** + * @see #728 + */ @Test public void defaultResourcesConversionShouldWork() { @@ -90,6 +96,9 @@ public class ReactiveResourceAssemblerUnitTest { }).verifyComplete(); } + /** + * @see #728 + */ @Test public void customResourcesShouldWork() { @@ -151,5 +160,4 @@ public class ReactiveResourceAssemblerUnitTest { class EmployeeResource extends ResourceSupport { private Employee employee; } - } diff --git a/src/test/java/org/springframework/hateoas/reactive/SimpleReactiveResourceAssemblerTest.java b/src/test/java/org/springframework/hateoas/reactive/SimpleReactiveResourceAssemblerTest.java index 3ca6e330..06c5041f 100644 --- a/src/test/java/org/springframework/hateoas/reactive/SimpleReactiveResourceAssemblerTest.java +++ b/src/test/java/org/springframework/hateoas/reactive/SimpleReactiveResourceAssemblerTest.java @@ -48,7 +48,7 @@ public class SimpleReactiveResourceAssemblerTest { } /** - * @see # + * @see #728 */ @Test public void convertingToResourceShouldWork() { @@ -65,7 +65,7 @@ public class SimpleReactiveResourceAssemblerTest { } /** - * @see # + * @see #728 */ @Test public void convertingToResourcesShouldWork() { @@ -82,7 +82,7 @@ public class SimpleReactiveResourceAssemblerTest { } /** - * @see # + * @see #728 */ @Test public void convertingToResourceWithCustomLinksShouldWork() { @@ -100,7 +100,7 @@ public class SimpleReactiveResourceAssemblerTest { } /** - * @see # + * @see #728 */ @Test public void convertingToResourcesWithCustomLinksShouldWork() {