#833 - Introduce SpringFactories-based SPI for custom media types.

We now expose a MediaTypeConfigurationProvider SPI interface that media type implementations can register an implementation class for in META-INF/spring.factories. The implementation class then has to provide a root Spring configuration class to be included into the bootstrap and expose whether it's supposed to be included for a given set of media types.

Configuration classes are supposed to implement HypermediaMappingInformation to expose either a Jackson module or code to configure the Jackson ObjectMapper to be used in the mapping infrastructure of the supported web stacks (WebMVC and WebFlux).
This commit is contained in:
Oliver Drotbohm
2019-02-22 17:59:08 +01:00
parent 8c366e1687
commit 7733f39796
43 changed files with 817 additions and 700 deletions

View File

@@ -20,7 +20,7 @@ import java.util.List;
/**
* Interface for components that convert a domain type into a {@link ResourceSupport}.
*
*
* @author Oliver Gierke
* @author Greg Turnquist
*/
@@ -28,21 +28,21 @@ public interface ResourceAssembler<T, D extends ResourceSupport> {
/**
* Converts the given entity into a {@code D}, which extends {@link ResourceSupport}.
*
*
* @param entity
* @return
*/
D toResource(T entity);
/**
* Converts an {@link Iterable} or {@code T}s into an {@link Iterable} of {@link ResourceSupport} and wraps
* them in a {@link Resources} instance.
*
* Converts an {@link Iterable} or {@code T}s into an {@link Iterable} of {@link ResourceSupport} and wraps them in a
* {@link Resources} instance.
*
* @param entities must not be {@literal null}.
* @return {@link Resources} containing {@code D}.
*/
default Resources<D> toResources(Iterable<? extends T> entities) {
List<D> resources = new ArrayList<>();
for (T entity : entities) {
resources.add(toResource(entity));

View File

@@ -0,0 +1,47 @@
/*
* 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.collectionjson;
import java.util.Collection;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.config.HypermediaMappingInformation;
import org.springframework.hateoas.config.MediaTypeConfigurationProvider;
import org.springframework.http.MediaType;
/**
* @author Oliver Gierke
*/
class CollectionJsonConfigurationProvider implements MediaTypeConfigurationProvider {
/*
* (non-Javadoc)
* @see org.springframework.hateoas.config.MediaTypeConfigurationProvider#getConfiguration()
*/
@Override
public Class<? extends HypermediaMappingInformation> getConfiguration() {
return CollectionJsonMediaTypeConfiguration.class;
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.config.MediaTypeConfigurationProvider#supportsAny(java.util.Collection)
*/
@Override
public boolean supportsAny(Collection<MediaType> mediaTypes) {
return mediaTypes.contains(MediaTypes.COLLECTION_JSON);
}
}

View File

@@ -21,43 +21,40 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.LinkDiscoverer;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.hateoas.config.Hypermedia;
import org.springframework.hateoas.config.HypermediaMappingInformation;
import org.springframework.http.MediaType;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.Module;
/**
* Configuration setup for Collection/JSON.
*
* @author Greg Turnquist
* @author Oliver Drotbohm
*/
@Configuration
public class CollectionJsonConfigurer {
@Bean
Hypermedia collectionJsonBean() {
return new Hypermedia() {
@Override
public List<MediaType> getMediaTypes() {
return HypermediaType.COLLECTION_JSON.getMediaTypes();
}
@Override
public ObjectMapper createObjectMapper(ObjectMapper mapper) {
ObjectMapper mapper1 = mapper.copy();
mapper1.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper1.registerModule(new Jackson2CollectionJsonModule());
return mapper1;
}
};
}
class CollectionJsonMediaTypeConfiguration implements HypermediaMappingInformation {
@Bean
LinkDiscoverer linkDiscoverer() {
return new CollectionJsonLinkDiscoverer();
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.config.HypermediaMappingInformation#getMediaTypes()
*/
@Override
public List<MediaType> getMediaTypes() {
return HypermediaType.COLLECTION_JSON.getMediaTypes();
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.config.HypermediaMappingInformation#getJacksonModule()
*/
@Override
public Module getJacksonModule() {
return new Jackson2CollectionJsonModule();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-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.
@@ -63,8 +63,9 @@ import com.fasterxml.jackson.databind.type.TypeFactory;
* in Collection+JSON compatible JSON.
*
* @author Greg Turnquist
* @author Oliver Drotbohm
*/
public class Jackson2CollectionJsonModule extends SimpleModule {
class Jackson2CollectionJsonModule extends SimpleModule {
private static final long serialVersionUID = -6540574644565592709L;

View File

@@ -22,20 +22,15 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.collectionjson.CollectionJsonConfigurer;
import org.springframework.hateoas.hal.HalConfigurer;
import org.springframework.hateoas.hal.forms.HalFormsConfigurer;
import org.springframework.hateoas.uber.UberConfigurer;
import org.springframework.http.MediaType;
/**
* Activates hypermedia support in the {@link ApplicationContext}. Will register infrastructure beans to support all
* appropriate web stacks based on selected {@link Hypermedia}-type as well as the classpath.
* appropriate web stacks based on selected {@link HypermediaMappingInformation}-type as well as the classpath.
*
* @author Oliver Gierke
* @author Greg Turnquist
@@ -44,7 +39,7 @@ import org.springframework.http.MediaType;
@Target(ElementType.TYPE)
@Documented
@EnableEntityLinks
@Import({ HypermediaSupportBeanDefinitionRegistrar.class, HateoasConfiguration.class, WebStackImportSelector.class })
@Import({ HypermediaConfigurationImportSelector.class, HateoasConfiguration.class, WebStackImportSelector.class })
public @interface EnableHypermediaSupport {
/**
@@ -60,7 +55,7 @@ public @interface EnableHypermediaSupport {
* @author Oliver Gierke
* @author Greg Turnquist
*/
enum HypermediaType implements Hypermedia {
enum HypermediaType {
/**
* HAL - Hypermedia Application Language.
@@ -68,47 +63,37 @@ public @interface EnableHypermediaSupport {
* @see http://stateless.co/hal_specification.html
* @see http://tools.ietf.org/html/draft-kelly-json-hal-05
*/
HAL(HalConfigurer.class, MediaTypes.HAL_JSON, MediaTypes.HAL_JSON_UTF8),
HAL(MediaTypes.HAL_JSON, MediaTypes.HAL_JSON_UTF8),
/**
* HAL-FORMS - Independent, backward-compatible extension of the HAL designed to add runtime FORM support
*
* @see https://rwcbook.github.io/hal-forms/
*/
HAL_FORMS(HalFormsConfigurer.class, MediaTypes.HAL_FORMS_JSON),
HAL_FORMS(MediaTypes.HAL_FORMS_JSON),
/**
* Collection+JSON
*
* @see http://amundsen.com/media-types/collection/format/
*/
COLLECTION_JSON(CollectionJsonConfigurer.class, MediaTypes.COLLECTION_JSON),
COLLECTION_JSON(MediaTypes.COLLECTION_JSON),
/**
* UBER Hypermedia
*
* @see http://uberhypermedia.org/
*/
UBER(UberConfigurer.class, MediaTypes.UBER_JSON);
UBER(MediaTypes.UBER_JSON);
private final Class<?> configurer;
private final List<MediaType> mediaTypes;
HypermediaType(Class<?> configurer, MediaType... mediaTypes) {
this.configurer = configurer;
HypermediaType(MediaType... mediaTypes) {
this.mediaTypes = Arrays.asList(mediaTypes);
}
@Override
public List<MediaType> getMediaTypes() {
return this.mediaTypes;
}
@Override
public Optional<Class<?>> configurer() {
return Optional.ofNullable(this.configurer);
}
}
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.hateoas.config;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
@@ -54,17 +55,12 @@ class EntityLinksConfiguration {
}
@Bean
ControllerEntityLinksFactoryBean controllerEntityLinks(WebMvcLinkBuilderFactory controllerLinkBuilderFactory) {
ControllerEntityLinksFactoryBean controllerEntityLinks(ObjectProvider<WebMvcLinkBuilderFactory> linkBuilderFactory) {
ControllerEntityLinksFactoryBean factory = new ControllerEntityLinksFactoryBean();
factory.setAnnotation(Controller.class);
factory.setLinkBuilderFactory(controllerLinkBuilderFactory);
factory.setLinkBuilderFactory(linkBuilderFactory.getIfAvailable(WebMvcLinkBuilderFactory::new));
return factory;
}
@Bean
WebMvcLinkBuilderFactory webMvcLinkBuilderFactoryBean() {
return new WebMvcLinkBuilderFactory();
}
}

View File

@@ -1,55 +0,0 @@
/*
* 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.List;
import java.util.Optional;
import org.springframework.http.MediaType;
import org.springframework.util.MimeType;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Interface for registering custom hypermedia handlers.
*
* @author Greg Turnquist
*/
public interface Hypermedia {
/**
* {@link MediaType}s this hypermedia can handle.
*/
List<MediaType> getMediaTypes();
/**
* Convert list of {@link MediaType}s into an array of {@link MimeType}s to support various Spring constructors.
*/
default MimeType[] getMimeTypes() {
return getMediaTypes().toArray(new MimeType[0]);
}
/**
* Create an {@link ObjectMapper} and register custom serializers and deserializers for the supported media types.
*/
default ObjectMapper createObjectMapper(ObjectMapper mapper) {
return mapper.copy();
}
default Optional<Class<?>> configurer() {
return Optional.empty();
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.http.MediaType;
/**
* {@link ImportSelector} that looks up configuration classes from all {@link MediaTypeConfigurationProvider}
* implementations listed in {@code META-INF/spring.factories}.
*
* @author Oliver Drotbohm
*/
class HypermediaConfigurationImportSelector implements ImportSelector {
/*
* (non-Javadoc)
* @see org.springframework.context.annotation.ImportSelector#selectImports(org.springframework.core.type.AnnotationMetadata)
*/
@Override
public String[] selectImports(AnnotationMetadata metadata) {
Map<String, Object> attributes = metadata.getAnnotationAttributes(EnableHypermediaSupport.class.getName());
Collection<MediaType> types = Arrays.stream((HypermediaType[]) attributes.get("type")) //
.flatMap(it -> it.getMediaTypes().stream()) //
.collect(Collectors.toList());
Collection<MediaTypeConfigurationProvider> configurationProviders = SpringFactoriesLoader.loadFactories(
MediaTypeConfigurationProvider.class, HypermediaConfigurationImportSelector.class.getClassLoader());
// No additional filtering needed.
if (types.isEmpty()) {
return configurationProviders.toArray(new String[configurationProviders.size()]);
}
// Filter the ones supporting the given media types
return configurationProviders.stream() //
.filter(it -> it.supportsAny(types)) //
.map(MediaTypeConfigurationProvider::getConfiguration) //
.map(Class::getName) //
.toArray(String[]::new);
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.List;
import java.util.Optional;
import org.springframework.http.MediaType;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Interface for registering custom hypermedia handlers.
*
* @author Greg Turnquist
* @author Oliver Drotbohm
*/
public interface HypermediaMappingInformation {
/**
* All {@link MediaType}s this hypermedia can handle.
*
* @return
*/
List<MediaType> getMediaTypes();
/**
* Configure an {@link ObjectMapper} and register custom serializers and deserializers for the supported media types.
* If all you want to do is register a Jackson {@link Module}, prefer implementing {@link #getJacksonModule()}.
*
* @return
* @see #getJacksonModule()
*/
default ObjectMapper configureObjectMapper(ObjectMapper mapper) {
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
Optional.ofNullable(getJacksonModule()).ifPresent(mapper::registerModule);
return mapper;
}
/**
* Optionally return the Jackson {@link Module} to be used to customize the serialization of representation models.
* Override this if there's nothing but the module to be done to setup the {@link ObjectMapper}. For more advanced
* needs, see {@link #configureObjectMapper(ObjectMapper)}.
*
* @return
* @see #configureObjectMapper(ObjectMapper)
*/
default Module getJacksonModule() {
return null;
}
}

View File

@@ -1,96 +0,0 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.hateoas.config;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
import static org.springframework.beans.factory.support.BeanDefinitionReaderUtils.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.hateoas.EntityLinks;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.util.Assert;
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
*/
class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
private static final boolean JSONPATH_PRESENT = ClassUtils.isPresent("com.jayway.jsonpath.JsonPath", null);
/*
* (non-Javadoc)
* @see org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
Map<String, Object> attributes = metadata.getAnnotationAttributes(EnableHypermediaSupport.class.getName());
Collection<HypermediaType> types = Arrays.asList((HypermediaType[]) attributes.get("type"));
/*
* Register all the {@link HypermediaType}s as individual beans, so they can be gathered as needed into a
* collection using the application context.
*/
for (HypermediaType type : types) {
type.configurer().ifPresent(clazz -> {
Assert.isTrue(clazz.isAnnotationPresent(Configuration.class),
clazz + " must have Spring's @Configuration annotation!");
BeanDefinitionBuilder hypermediaTypeBeanDefinition = genericBeanDefinition(clazz);
registerSourcedBeanDefinition(hypermediaTypeBeanDefinition, metadata, registry);
});
}
}
private static String registerSourcedBeanDefinition(BeanDefinitionBuilder builder, AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
String generateBeanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, registry);
return registerSourcedBeanDefinition(builder, metadata, registry, generateBeanName);
}
private static String registerSourcedBeanDefinition(BeanDefinitionBuilder builder, AnnotationMetadata metadata,
BeanDefinitionRegistry registry, String name) {
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
beanDefinition.setSource(metadata);
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, name);
registerBeanDefinition(holder, registry);
return name;
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.Collection;
import org.springframework.http.MediaType;
/**
* SPI to register a media type configuration provider.
*
* @author Oliver Drotbohm
* @see HypermediaMappingInformation
*/
public interface MediaTypeConfigurationProvider {
/**
* Returns the primary Spring configuration class to be bootstrapped for the given media type.
*
* @return
*/
Class<? extends HypermediaMappingInformation> getConfiguration();
/**
* Returns whether the provider supports any of the given {@link MediaType}s. Used to select the providers to be
* included into a configuration setup in case the media types to be enabled are explicitly defined.
*
* @param mediaTypes will never be {@literal null}.
* @return
*/
boolean supportsAny(Collection<MediaType> mediaTypes);
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2018-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.mvc;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.web.client.RestTemplate;
/**
* {@link BeanPostProcessor} to register hypermedia support with {@link RestTemplate} instances found in the
* application context.
*
* @author Oliver Gierke
* @author Greg Turnquist
*/
@RequiredArgsConstructor
public class HypermediaRestTemplateBeanPostProcessor implements BeanPostProcessor {
private final HypermediaWebMvcConfigurer configurer;
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof RestTemplate) {
this.configurer.extendMessageConverters(((RestTemplate) bean).getMessageConverters());
}
return bean;
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2018-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.mvc;
import lombok.RequiredArgsConstructor;
import java.util.Collection;
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.config.Hypermedia;
import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author Oliver Gierke
* @author Greg Turnquist
*/
@Configuration
@RequiredArgsConstructor
public class HypermediaWebMvcConfigurer implements WebMvcConfigurer {
private final ObjectMapper mapper;
private final Collection<Hypermedia> hypermediaTypes;
/*
* (non-Javadoc)
* @see org.springframework.web.servlet.config.annotation.WebMvcConfigurer#extendMessageConverters(java.util.List)
*/
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
this.hypermediaTypes.forEach(hypermedia -> {
converters.add(0, new TypeConstrainedMappingJackson2HttpMessageConverter(
ResourceSupport.class, hypermedia.getMediaTypes(), hypermedia.createObjectMapper(this.mapper)));
});
}
}

View File

@@ -15,16 +15,25 @@
*/
package org.springframework.hateoas.config.mvc;
import java.util.Collection;
import lombok.RequiredArgsConstructor;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.config.Hypermedia;
import org.springframework.hateoas.core.DelegatingRelProvider;
import org.springframework.hateoas.hal.CurieProvider;
import org.springframework.hateoas.hal.HalConfiguration;
import org.springframework.hateoas.hal.forms.HalFormsConfiguration;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.config.HypermediaMappingInformation;
import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
import org.springframework.hateoas.mvc.UriComponentsContributor;
import org.springframework.hateoas.mvc.WebMvcLinkBuilderFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -34,13 +43,11 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* @author Greg Turnquist
*/
@Configuration
public class WebMvcHateoasConfiguration {
class WebMvcHateoasConfiguration {
@Bean
HypermediaWebMvcConfigurer hypermediaWebMvcConfigurer(ObjectProvider<ObjectMapper> mapper,
// DelegatingRelProvider relProvider, ObjectProvider<CurieProvider> curieProvider,
// ObjectProvider<HalConfiguration> halConfiguration, ObjectProvider<HalFormsConfiguration> halFormsConfiguration,
Collection<Hypermedia> hypermediaTypes) {
Collection<HypermediaMappingInformation> hypermediaTypes) {
return new HypermediaWebMvcConfigurer(mapper.getIfAvailable(ObjectMapper::new), hypermediaTypes);
}
@@ -49,4 +56,67 @@ public class WebMvcHateoasConfiguration {
HypermediaRestTemplateBeanPostProcessor restTemplateBeanPostProcessor(HypermediaWebMvcConfigurer configurer) {
return new HypermediaRestTemplateBeanPostProcessor(configurer);
}
@Bean
WebMvcLinkBuilderFactory webMvcLinkBuilderFactory(ObjectProvider<UriComponentsContributor> contributors) {
WebMvcLinkBuilderFactory factory = new WebMvcLinkBuilderFactory();
factory.setUriComponentsContributors(contributors.stream().collect(Collectors.toList()));
return factory;
}
/**
* @author Oliver Gierke
* @author Greg Turnquist
*/
@RequiredArgsConstructor
static class HypermediaWebMvcConfigurer implements WebMvcConfigurer {
private final ObjectMapper mapper;
private final Collection<HypermediaMappingInformation> hypermediaTypes;
/*
* (non-Javadoc)
* @see org.springframework.web.servlet.config.annotation.WebMvcConfigurer#extendMessageConverters(java.util.List)
*/
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
this.hypermediaTypes.forEach(hypermedia -> {
converters.add(0, new TypeConstrainedMappingJackson2HttpMessageConverter(ResourceSupport.class,
hypermedia.getMediaTypes(), hypermedia.configureObjectMapper(mapper.copy())));
});
}
}
/**
* {@link BeanPostProcessor} to register hypermedia support with {@link RestTemplate} instances found in the
* application context.
*
* @author Oliver Gierke
* @author Greg Turnquist
*/
@RequiredArgsConstructor
static class HypermediaRestTemplateBeanPostProcessor implements BeanPostProcessor {
private final HypermediaWebMvcConfigurer configurer;
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!RestTemplate.class.isInstance(bean)) {
return bean;
}
configurer.extendMessageConverters(((RestTemplate) bean).getMessageConverters());
return bean;
}
}
}

View File

@@ -1,45 +0,0 @@
/*
* 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.reactive;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.web.reactive.function.client.WebClient;
/**
* {@link BeanPostProcessor} to register the proper handlers in {@link WebClient} instances found in the
* application context.
*
* @author Greg Turnquist
* @since 1.0
*/
@RequiredArgsConstructor
public class HypermediaWebClientBeanPostProcessor implements BeanPostProcessor {
private final WebClientConfigurer configurer;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebClient) {
return this.configurer.registerHypermediaTypes((WebClient) bean);
}
return bean;
}
}

View File

@@ -1,72 +0,0 @@
/*
* 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.reactive;
import lombok.RequiredArgsConstructor;
import java.util.Collection;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.codec.CharSequenceEncoder;
import org.springframework.core.codec.StringDecoder;
import org.springframework.hateoas.config.Hypermedia;
import org.springframework.http.codec.CodecConfigurer;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* {@link WebFluxConfigurer} to register hypermedia-aware {@link org.springframework.core.codec.Encoder}s and
* {@link org.springframework.core.codec.Decoder}s that will render hypermedia for WebFlux controllers.
*
* @author Greg Turnquist
* @since 1.0
*/
@Configuration
@RequiredArgsConstructor
public class HypermediaWebFluxConfigurer implements WebFluxConfigurer {
private final ObjectMapper mapper;
private final Collection<Hypermedia> hypermediaTypes;
/**
* Configure custom HTTP message readers and writers or override built-in ones.
* <p>
* The configured readers and writers will be used for both annotated controllers and functional endpoints.
*
* @param configurer the configurer to use
*/
@Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
CodecConfigurer.CustomCodecs customCodecs = configurer.customCodecs();
this.hypermediaTypes.forEach(hypermedia -> {
ObjectMapper objectMapper = hypermedia.createObjectMapper(this.mapper);
customCodecs.encoder(new Jackson2JsonEncoder(objectMapper, hypermedia.getMimeTypes()));
customCodecs.decoder(new Jackson2JsonDecoder(objectMapper, hypermedia.getMimeTypes()));
});
customCodecs.encoder(CharSequenceEncoder.allMimeTypes());
customCodecs.decoder(StringDecoder.allMimeTypes());
configurer.registerDefaults(false);
}
}

View File

@@ -1,18 +1,3 @@
/*
* 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.reactive;
import lombok.RequiredArgsConstructor;
@@ -27,7 +12,7 @@ import org.springframework.core.codec.Decoder;
import org.springframework.core.codec.Encoder;
import org.springframework.core.codec.StringDecoder;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.hateoas.config.Hypermedia;
import org.springframework.hateoas.config.HypermediaMappingInformation;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.util.MimeType;
@@ -47,11 +32,11 @@ import com.fasterxml.jackson.databind.ObjectMapper;
public class WebClientConfigurer {
private final ObjectMapper mapper;
private final Collection<Hypermedia> hypermediaTypes;
private final Collection<HypermediaMappingInformation> hypermediaTypes;
/**
* Return a set of {@link ExchangeStrategies} driven by registered {@link HypermediaType}s.
*
*
* @return a collection of {@link Encoder}s and {@link Decoder} assembled into a {@link ExchangeStrategies}.
*/
public ExchangeStrategies hypermediaExchangeStrategies() {
@@ -61,10 +46,11 @@ public class WebClientConfigurer {
this.hypermediaTypes.forEach(hypermedia -> {
ObjectMapper objectMapper = hypermedia.createObjectMapper(this.mapper);
ObjectMapper objectMapper = hypermedia.configureObjectMapper(this.mapper.copy());
MimeType[] mimeTypes = hypermedia.getMediaTypes().toArray(new MimeType[0]);
encoders.add(new Jackson2JsonEncoder(objectMapper, hypermedia.getMediaTypes().toArray(new MimeType[]{})));
decoders.add(new Jackson2JsonDecoder(objectMapper, hypermedia.getMediaTypes().toArray(new MimeType[]{})));
encoders.add(new Jackson2JsonEncoder(objectMapper, mimeTypes));
decoders.add(new Jackson2JsonDecoder(objectMapper, mimeTypes));
});
encoders.add(CharSequenceEncoder.allMimeTypes());
@@ -86,7 +72,6 @@ public class WebClientConfigurer {
* @return mutated webClient with hypermedia support.
*/
public WebClient registerHypermediaTypes(WebClient webClient) {
return webClient.mutate().exchangeStrategies(hypermediaExchangeStrategies()).build();
}
}

View File

@@ -15,17 +15,26 @@
*/
package org.springframework.hateoas.config.reactive;
import lombok.RequiredArgsConstructor;
import java.util.Collection;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.config.Hypermedia;
import org.springframework.hateoas.core.DelegatingRelProvider;
import org.springframework.hateoas.hal.CurieProvider;
import org.springframework.hateoas.hal.HalConfiguration;
import org.springframework.hateoas.hal.forms.HalFormsConfiguration;
import org.springframework.core.codec.CharSequenceEncoder;
import org.springframework.core.codec.StringDecoder;
import org.springframework.hateoas.config.HypermediaMappingInformation;
import org.springframework.hateoas.reactive.HypermediaWebFilter;
import org.springframework.http.codec.CodecConfigurer;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.util.MimeType;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.function.client.WebClient;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -36,12 +45,11 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* @since 1.0
*/
@Configuration
public class WebFluxHateoasConfiguration {
class WebFluxHateoasConfiguration {
@Bean
WebClientConfigurer webClientConfigurer(ObjectProvider<ObjectMapper> mapper,
Collection<Hypermedia> hypermediaTypes) {
Collection<HypermediaMappingInformation> hypermediaTypes) {
return new WebClientConfigurer(mapper.getIfAvailable(ObjectMapper::new), hypermediaTypes);
}
@@ -52,7 +60,7 @@ public class WebFluxHateoasConfiguration {
@Bean
HypermediaWebFluxConfigurer hypermediaWebFluxConfigurer(ObjectProvider<ObjectMapper> mapper,
Collection<Hypermedia> hypermediaTypes) {
Collection<HypermediaMappingInformation> hypermediaTypes) {
return new HypermediaWebFluxConfigurer(mapper.getIfAvailable(ObjectMapper::new), hypermediaTypes);
}
@@ -65,4 +73,73 @@ public class WebFluxHateoasConfiguration {
HypermediaWebFilter hypermediaWebFilter() {
return new HypermediaWebFilter();
}
/**
* {@link BeanPostProcessor} to register the proper handlers in {@link WebClient} instances found in the application
* context.
*
* @author Greg Turnquist
* @since 1.0
*/
@RequiredArgsConstructor
static class HypermediaWebClientBeanPostProcessor implements BeanPostProcessor {
private final WebClientConfigurer configurer;
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebClient) {
return this.configurer.registerHypermediaTypes((WebClient) bean);
}
return bean;
}
}
/**
* {@link WebFluxConfigurer} to register hypermedia-aware {@link org.springframework.core.codec.Encoder}s and
* {@link org.springframework.core.codec.Decoder}s that will render hypermedia for WebFlux controllers.
*
* @author Greg Turnquist
* @since 1.0
*/
@RequiredArgsConstructor
static class HypermediaWebFluxConfigurer implements WebFluxConfigurer {
private final ObjectMapper mapper;
private final Collection<HypermediaMappingInformation> hypermediaTypes;
/**
* Configure custom HTTP message readers and writers or override built-in ones.
* <p>
* The configured readers and writers will be used for both annotated controllers and functional endpoints.
*
* @param configurer the configurer to use
*/
@Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
CodecConfigurer.CustomCodecs customCodecs = configurer.customCodecs();
this.hypermediaTypes.forEach(hypermedia -> {
MimeType[] mimeTypes = hypermedia.getMediaTypes().toArray(new MimeType[0]);
ObjectMapper objectMapper = hypermedia.configureObjectMapper(this.mapper.copy());
customCodecs.encoder(new Jackson2JsonEncoder(objectMapper, mimeTypes));
customCodecs.decoder(new Jackson2JsonDecoder(objectMapper, mimeTypes));
});
customCodecs.encoder(CharSequenceEncoder.allMimeTypes());
customCodecs.decoder(StringDecoder.allMimeTypes());
configurer.registerDefaults(false);
}
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.hal;
import java.util.Collection;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.config.HypermediaMappingInformation;
import org.springframework.hateoas.config.MediaTypeConfigurationProvider;
import org.springframework.http.MediaType;
/**
* {@link MediaTypeConfigurationProvider} for HAL.
*
* @author Oliver Drotbohm
*/
class HalConfigurationProvider implements MediaTypeConfigurationProvider {
/*
* (non-Javadoc)
* @see org.springframework.hateoas.config.HyperMediaTypeProvider#getConfiguration()
*/
@Override
public Class<? extends HypermediaMappingInformation> getConfiguration() {
return HalMediaTypeConfiguration.class;
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.config.HyperMediaTypeProvider#supportsAny(java.util.Collection)
*/
@Override
public boolean supportsAny(Collection<MediaType> mediaTypes) {
return mediaTypes.contains(MediaTypes.HAL_JSON);
}
}

View File

@@ -1,71 +0,0 @@
/*
* 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.hal;
import java.util.List;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.hateoas.LinkDiscoverer;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.hateoas.config.Hypermedia;
import org.springframework.hateoas.core.DelegatingRelProvider;
import org.springframework.http.MediaType;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author Greg Turnquist
*/
@Configuration
public class HalConfigurer {
@Bean
Hypermedia halBean(DelegatingRelProvider relProvider,
ObjectProvider<CurieProvider> curieProvider,
ObjectProvider<HalConfiguration> halConfiguration,
MessageSourceAccessor messageSourceAccessor) {
return new Hypermedia() {
@Override
public List<MediaType> getMediaTypes() {
return HypermediaType.HAL.getMediaTypes();
}
@Override
public ObjectMapper createObjectMapper(ObjectMapper mapper) {
ObjectMapper mapper1 = mapper.copy();
mapper1.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper1.registerModule(new Jackson2HalModule());
mapper1.setHandlerInstantiator(
new Jackson2HalModule.HalHandlerInstantiator(relProvider, curieProvider.getIfAvailable(), messageSourceAccessor, halConfiguration.getIfAvailable(HalConfiguration::new)));
return mapper1;
}
};
}
@Bean
LinkDiscoverer linkDiscoverer() {
return new HalLinkDiscoverer();
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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.hal;
import lombok.RequiredArgsConstructor;
import java.util.List;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.hateoas.LinkDiscoverer;
import org.springframework.hateoas.RelProvider;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.hateoas.config.HypermediaMappingInformation;
import org.springframework.http.MediaType;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Spring configuration to set up HAL support.
*
* @author Greg Turnquist
* @author Oliver Drotbohm
*/
@Configuration
@RequiredArgsConstructor
public class HalMediaTypeConfiguration implements HypermediaMappingInformation {
private final RelProvider relProvider;
private final ObjectProvider<CurieProvider> curieProvider;
private final ObjectProvider<HalConfiguration> halConfiguration;
private final MessageSourceAccessor messageSourceAccessor;
@Bean
LinkDiscoverer linkDiscoverer() {
return new HalLinkDiscoverer();
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.config.HypermediaMappingInformation#getMediaTypes()
*/
@Override
public List<MediaType> getMediaTypes() {
return HypermediaType.HAL.getMediaTypes();
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.config.HypermediaMappingInformation#configureObjectMapper(com.fasterxml.jackson.databind.ObjectMapper)
*/
@Override
public ObjectMapper configureObjectMapper(ObjectMapper mapper) {
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.registerModule(new Jackson2HalModule());
mapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider,
curieProvider.getIfAvailable(), messageSourceAccessor, halConfiguration.getIfAvailable(HalConfiguration::new)));
return mapper;
}
}

View File

@@ -428,8 +428,8 @@ public class Jackson2HalModule extends SimpleModule {
HalLink halLink = HalLink.class.cast(firstElement);
if (list.size() == 1
&& halConfiguration.getSingleLinkRenderModeFor(halLink.getLink().getRel()).equals(RenderSingleLinks.AS_SINGLE)) {
if (list.size() == 1 && halConfiguration.getSingleLinkRenderModeFor(halLink.getLink().getRel())
.equals(RenderSingleLinks.AS_SINGLE)) {
serializeContents(halLink, jgen, provider);

View File

@@ -29,7 +29,7 @@ import org.springframework.hateoas.hal.HalConfiguration;
*/
@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class HalFormsConfiguration {
class HalFormsConfiguration {
private @Wither @Getter RenderSingleLinks renderSingleLinks = RenderSingleLinks.AS_SINGLE;
@@ -48,7 +48,7 @@ public class HalFormsConfiguration {
/**
* Translate a {@link HalFormsConfiguration} into a {@link HalConfiguration}.
*
*
* @return
*/
public HalConfiguration toHalConfiguration() {

View File

@@ -0,0 +1,49 @@
/*
* 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.hal.forms;
import java.util.Collection;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.config.HypermediaMappingInformation;
import org.springframework.hateoas.config.MediaTypeConfigurationProvider;
import org.springframework.http.MediaType;
/**
* {@link MediaTypeConfigurationProvider} for HAL Forms.
*
* @author Oliver Drotbohm
*/
class HalFormsConfigurationProvider implements MediaTypeConfigurationProvider {
/*
* (non-Javadoc)
* @see org.springframework.hateoas.config.MediaTypeConfigurationProvider#getConfiguration()
*/
@Override
public Class<? extends HypermediaMappingInformation> getConfiguration() {
return HalFormsMediaTypeConfiguration.class;
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.config.MediaTypeConfigurationProvider#supportsAny(java.util.Collection)
*/
@Override
public boolean supportsAny(Collection<MediaType> mediaTypes) {
return mediaTypes.contains(MediaTypes.HAL_FORMS_JSON);
}
}

View File

@@ -1,71 +0,0 @@
/*
* 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.hal.forms;
import java.util.List;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.hateoas.LinkDiscoverer;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.hateoas.config.Hypermedia;
import org.springframework.hateoas.core.DelegatingRelProvider;
import org.springframework.hateoas.hal.CurieProvider;
import org.springframework.http.MediaType;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author Greg Turnquist
*/
@Configuration
public class HalFormsConfigurer {
@Bean
Hypermedia halFormsBean(DelegatingRelProvider relProvider,
ObjectProvider<CurieProvider> curieProvider,
ObjectProvider<HalFormsConfiguration> halFormsConfiguration,
MessageSourceAccessor messageSourceAccessor) {
return new Hypermedia() {
@Override
public List<MediaType> getMediaTypes() {
return HypermediaType.HAL_FORMS.getMediaTypes();
}
@Override
public ObjectMapper createObjectMapper(ObjectMapper mapper) {
ObjectMapper mapper1 = mapper.copy();
mapper1.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper1.registerModule(new Jackson2HalFormsModule());
mapper1.setHandlerInstantiator(new Jackson2HalFormsModule.HalFormsHandlerInstantiator(relProvider, curieProvider.getIfAvailable(), messageSourceAccessor,
true, halFormsConfiguration.getIfAvailable(HalFormsConfiguration::new)));
return mapper1;
}
};
}
@Bean LinkDiscoverer linkDiscoverer() {
return new HalFormsLinkDiscoverer();
}
}

View File

@@ -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.hal.forms;
import lombok.RequiredArgsConstructor;
import java.util.List;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.hateoas.LinkDiscoverer;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.hateoas.config.HypermediaMappingInformation;
import org.springframework.hateoas.core.DelegatingRelProvider;
import org.springframework.hateoas.hal.CurieProvider;
import org.springframework.http.MediaType;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Spring configuration for HAL Forms support.
*
* @author Greg Turnquist
* @author Oliver Drotbohm
*/
@Configuration
@RequiredArgsConstructor
class HalFormsMediaTypeConfiguration implements HypermediaMappingInformation {
private final DelegatingRelProvider relProvider;
private final ObjectProvider<CurieProvider> curieProvider;
private final ObjectProvider<HalFormsConfiguration> halFormsConfiguration;
private final MessageSourceAccessor messageSourceAccessor;
@Bean
LinkDiscoverer linkDiscoverer() {
return new HalFormsLinkDiscoverer();
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.config.HypermediaMappingInformation#getMediaTypes()
*/
@Override
public List<MediaType> getMediaTypes() {
return HypermediaType.HAL_FORMS.getMediaTypes();
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.config.HypermediaMappingInformation#configureObjectMapper(com.fasterxml.jackson.databind.ObjectMapper)
*/
@Override
public ObjectMapper configureObjectMapper(ObjectMapper mapper) {
ObjectMapper mapper1 = mapper.copy();
mapper1.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper1.registerModule(new Jackson2HalFormsModule());
mapper1.setHandlerInstantiator(
new Jackson2HalFormsModule.HalFormsHandlerInstantiator(relProvider, curieProvider.getIfAvailable(),
messageSourceAccessor, true, halFormsConfiguration.getIfAvailable(HalFormsConfiguration::new)));
return mapper1;
}
}

View File

@@ -71,7 +71,7 @@ import com.fasterxml.jackson.databind.type.TypeFactory;
* @author Greg Turnquist
* @author Oliver Gierke
*/
public class Jackson2HalFormsModule extends SimpleModule {
class Jackson2HalFormsModule extends SimpleModule {
private static final long serialVersionUID = -4496351128468451196L;

View File

@@ -34,7 +34,7 @@ import org.springframework.http.MediaType;
* @author Greg Turnquist
* @author Oliver Drotbohm
*/
public class UberAffordanceModelFactory implements AffordanceModelFactory {
class UberAffordanceModelFactory implements AffordanceModelFactory {
private final @Getter MediaType mediaType = MediaTypes.UBER_JSON;

View File

@@ -21,43 +21,40 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.LinkDiscoverer;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.hateoas.config.Hypermedia;
import org.springframework.hateoas.config.HypermediaMappingInformation;
import org.springframework.http.MediaType;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.Module;
/**
* Spring configuration for Uber media type support.
*
* @author Greg Turnquist
* @author Oliver Drotbohm
*/
@Configuration
public class UberConfigurer {
@Bean
Hypermedia uberBean() {
return new Hypermedia() {
@Override
public List<MediaType> getMediaTypes() {
return HypermediaType.UBER.getMediaTypes();
}
@Override
public ObjectMapper createObjectMapper(ObjectMapper mapper) {
ObjectMapper mapper1 = mapper.copy();
mapper1.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper1.registerModule(new Jackson2UberModule());
return mapper1;
}
};
}
class UberMediaTypeConfiguration implements HypermediaMappingInformation {
@Bean
LinkDiscoverer linkDiscoverer() {
return new UberLinkDiscoverer();
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.config.HypermediaMappingInformation#getMediaTypes()
*/
@Override
public List<MediaType> getMediaTypes() {
return HypermediaType.UBER.getMediaTypes();
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.config.HypermediaMappingInformation#getJacksonModule()
*/
@Override
public Module getJacksonModule() {
return new Jackson2UberModule();
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.uber;
import java.util.Collection;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.config.HypermediaMappingInformation;
import org.springframework.hateoas.config.MediaTypeConfigurationProvider;
import org.springframework.http.MediaType;
/**
* {@link MediaTypeConfigurationProvider} for Uber.
*
* @author Oliver Drotbohm
*/
class UberMediaTypeConfigurationProvider implements MediaTypeConfigurationProvider {
/*
* (non-Javadoc)
* @see org.springframework.hateoas.config.MediaTypeConfigurationProvider#getConfiguration()
*/
@Override
public Class<? extends HypermediaMappingInformation> getConfiguration() {
return UberMediaTypeConfiguration.class;
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.config.MediaTypeConfigurationProvider#supportsAny(java.util.Collection)
*/
@Override
public boolean supportsAny(Collection<MediaType> mediaTypes) {
return mediaTypes.contains(MediaTypes.UBER_JSON);
}
}