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