Merge branch 'master' of github.com:SpringSource/spring-data-rest

This commit is contained in:
Jon Brisbin
2012-08-23 09:59:25 -05:00
5 changed files with 267 additions and 244 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
.DS_Store
.gradle/
bin/
build/
*.i*
.classpath

View File

@@ -16,7 +16,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
*
* @author Jon Brisbin
*/
public class RepositoryRestHandlerAdapter extends ResourcePostProcessorInvokingHandlerAdapter {
public class RepositoryRestHandlerAdapter extends ResourceEnricherInvokingHandlerAdapter {
@Autowired
private ResourcesReturnValueHandler resourcesReturnValueHandler;

View File

@@ -16,25 +16,22 @@
package org.springframework.data.rest.webmvc;
import static org.springframework.data.util.ClassTypeInformation.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceProcessor;
import org.springframework.hateoas.ResourceEnricher;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.ResourcesProcessor;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
@@ -42,58 +39,53 @@ import org.springframework.web.method.support.ModelAndViewContainer;
/**
* {@link HandlerMethodReturnValueHandler} to post-process the objects returned from controller methods using the
* configured {@link ResourceProcessor}s / {@link ResourcesProcessor}s.
* configured {@link ResourceEnricher}s.
*
* @author Oliver Gierke
*/
public class ResourceProcessingHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
public class ResourceEnricherHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
private static final Set<Class<?>> RESOURCE_TYPES = new HashSet<Class<?>>(2, 1);
private static final Set<Class<?>> PARAMETER_TYPES = new HashSet<Class<?>>(3, 1);
static {
RESOURCE_TYPES.add(Resource.class);
RESOURCE_TYPES.add(Resources.class);
PARAMETER_TYPES.addAll(RESOURCE_TYPES);
PARAMETER_TYPES.add(HttpEntity.class);
}
private static final TypeInformation<?> RESOURCE_TYPE = from(Resource.class);
private static final TypeInformation<?> RESOURCES_TYPE = from(Resources.class);
private final HandlerMethodReturnValueHandler delegate;
private final List<PostProcessorWrapper> processorWrapper;
private final List<EnricherWrapper> enrichers;
/**
* Creates a new {@link ResourceProcessingHandlerMethodReturnValueHandler} using the given delegate to eventually
* Creates a new {@link ResourceEnricherHandlerMethodReturnValueHandler} using the given delegate to eventually
* delegate calls to {@link #handleReturnValue(Object, MethodParameter, ModelAndViewContainer, NativeWebRequest)} to.
* Will consider the given {@link ResourceProcessor} / {@link ResourcesProcessor} to post-process the controller
* methods return value to before invoking the delegate.
* Will consider the given {@link ResourceEnricher} to post-process the controller methods return value to before
* invoking the delegate.
*
* @param delegate the {@link HandlerMethodReturnValueHandler} to evenually delegate calls to, must not be
* {@literal null}.
* @param resourceProcessors the {@link ResourceProcessor}s to be considered, must not be {@literal null}.
* @param resourcesProcessors the {@link ResourcesProcessor}s to be considered, must not be {@literal null}.
* @param enrichers the {@link ResourceEnricher}s to be considered, must not be {@literal null}.
*/
@Autowired
public ResourceProcessingHandlerMethodReturnValueHandler(HandlerMethodReturnValueHandler delegate,
List<ResourceProcessor<?>> resourceProcessors, List<ResourcesProcessor<?>> resourcesProcessors) {
public ResourceEnricherHandlerMethodReturnValueHandler(HandlerMethodReturnValueHandler delegate,
List<ResourceEnricher<?>> enrichers) {
Assert.notNull(delegate, "Delegate must not be null!");
Assert.notNull(resourceProcessors, "ResourceProcessors must not be null!");
Assert.notNull(resourcesProcessors, "ResourcesProcessors must not be null!");
Assert.notNull(enrichers, "ResourceEnrichers must not be null!");
this.delegate = delegate;
this.enrichers = new ArrayList<EnricherWrapper>();
this.processorWrapper = new ArrayList<PostProcessorWrapper>();
for (ResourceEnricher<?> enricher : enrichers) {
for (ResourceProcessor<? extends Resource<?>> processor : resourceProcessors) {
this.processorWrapper.add(new ResourceProcessorWrapper(processor));
TypeInformation<?> componentType = from(enricher.getClass()).getSuperTypeInformation(ResourceEnricher.class)
.getComponentType();
Class<?> rawType = componentType.getType();
if (Resource.class.isAssignableFrom(rawType)) {
this.enrichers.add(new ResourceEnricherWrapper(enricher));
} else if (Resources.class.isAssignableFrom(rawType)) {
this.enrichers.add(new ResourcesEnricherWrapper(enricher));
} else {
this.enrichers.add(new DefaultEnricherWrapper(enricher));
}
}
for (ResourcesProcessor<? extends Resources<?>> processor : resourcesProcessors) {
this.processorWrapper.add(new ResourcesProcessorWrapper(processor));
}
Collections.sort(this.processorWrapper, AnnotationAwareOrderComparator.INSTANCE);
Collections.sort(this.enrichers, AnnotationAwareOrderComparator.INSTANCE);
}
/*
@@ -125,7 +117,7 @@ public class ResourceProcessingHandlerMethodReturnValueHandler implements Handle
return;
}
// We have a Resource or Resources - find suitable processors
// We have a Resource or Resources - find suitable enrichers
TypeInformation<?> targetType = ClassTypeInformation.fromReturnTypeOf(returnType.getMethod());
// Unbox HttpEntity
@@ -139,15 +131,35 @@ public class ResourceProcessingHandlerMethodReturnValueHandler implements Handle
targetType = returnValueTypeInformation;
}
Object processedValue = value;
// For Resources implementations, process elements first
if (RESOURCES_TYPE.isAssignableFrom(targetType)) {
for (PostProcessorWrapper wrapper : this.processorWrapper) {
if (wrapper.supports(targetType, processedValue)) {
processedValue = wrapper.invokeProcessor(processedValue);
Resources<?> resources = (Resources<?>) value;
TypeInformation<?> elementTargetType = targetType.getSuperTypeInformation(Resources.class).getComponentType();
for (Object element : resources) {
TypeInformation<?> elementTypeInformation = from(element.getClass());
if (!elementTargetType.getType().equals(elementTypeInformation.getType())) {
elementTargetType = elementTypeInformation;
}
for (EnricherWrapper wrapper : this.enrichers) {
if (wrapper.supports(elementTargetType, element)) {
wrapper.invokeEnricher(element);
}
}
}
}
delegate.handleReturnValue(rewrapResult(processedValue, returnValue), returnType, mavContainer, webRequest);
// Process actual value
for (EnricherWrapper wrapper : this.enrichers) {
if (wrapper.supports(targetType, value)) {
wrapper.invokeEnricher(value);
}
}
delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
/**
@@ -157,117 +169,76 @@ public class ResourceProcessingHandlerMethodReturnValueHandler implements Handle
* @return
*/
private boolean isResourceType(Object value) {
if (value == null) {
return false;
}
for (Class<?> type : RESOURCE_TYPES) {
if (type.isAssignableFrom(value.getClass())) {
return true;
}
}
return false;
return value instanceof ResourceSupport;
}
/**
* Re-wraps the result of the post-processing work into an {@link HttpEntity} or {@link ResponseEntity} if the
* original value was one of those two types. Copies headers and status code from the original value but uses the new
* body.
*
* @param newBody the post-processed value.
* @param originalValue the original input value.
* @return
*/
static Object rewrapResult(Object newBody, Object originalValue) {
if (!(originalValue instanceof HttpEntity)) {
return newBody;
}
if (originalValue instanceof ResponseEntity) {
ResponseEntity<?> source = (ResponseEntity<?>) originalValue;
return new ResponseEntity<Object>(newBody, source.getHeaders(), source.getStatusCode());
} else {
HttpEntity<?> source = (HttpEntity<?>) originalValue;
return new HttpEntity<Object>(newBody, source.getHeaders());
}
}
/**
* Interface to unify interaction with {@link ResourceProcessor} and {@link ResourcesProcessor}. The {@link Ordered}
* rank should be determined by the underlying processor.
* Interface to unify interaction with {@link ResourceEnricher}s. The {@link Ordered} rank should be determined by the
* underlying processor.
*
* @author Oliver Gierke
*/
private interface PostProcessorWrapper extends Ordered {
private interface EnricherWrapper extends Ordered {
/**
* Returns whether the underlying processor supports the given {@link TypeInformation}. It might also aditionally
* inspect the object that would eventually be handed to the processor.
* inspect the object that would eventually be handed to the enricher.
*
* @param typeInformation the type of object to be post processed, will never be {@literal null}.
* @param value the object that would be passed into the processor eventually, can be {@literal null}.
* @param value the object that would be passed into the enricher eventually, can be {@literal null}.
* @return
*/
boolean supports(TypeInformation<?> typeInformation, Object value);
/**
* Performs the actual invocation of the processor. Implementations can be sure
* Performs the actual invocation of the enricher. Implementations can be sure
* {@link #supports(TypeInformation, Object)} has been called before and returned {@literal true}.
*
* @param object
* @return
*/
Object invokeProcessor(Object object);
void invokeEnricher(Object object);
}
/**
* {@link PostProcessorWrapper} to invoke {@link ResourceProcessor} instances.
* Default implementation of {@link EnricherWrapper} to generically deal with {@link ResourceSupport} types.
*
* @author Oliver Gierke
*/
private static class ResourceProcessorWrapper implements PostProcessorWrapper {
private static class DefaultEnricherWrapper implements EnricherWrapper {
private final ResourceProcessor<? extends Resource<?>> processor;
private final ResourceEnricher<?> enricher;
private final TypeInformation<?> targetType;
/**
* Creates a new {@link ResourceProcessorWrapper} for the given {@link ResourceProcessor}.
* Creates a ne {@link DefaultEnricherWrapper} with the given {@link ResourceEnricher}.
*
* @param processor must not be {@literal null}.
* @param enricher must not be {@literal null}.
*/
public ResourceProcessorWrapper(ResourceProcessor<? extends Resource<?>> processor) {
public DefaultEnricherWrapper(ResourceEnricher<?> enricher) {
Assert.notNull(processor);
Assert.notNull(enricher);
this.processor = processor;
this.targetType = from(processor.getClass()).getSuperTypeInformation(ResourceProcessor.class).getComponentType();
this.enricher = enricher;
this.targetType = from(enricher.getClass()).getSuperTypeInformation(ResourceEnricher.class).getComponentType();
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.rest.webmvc.ResourceProcessingHandlerMethodReturnValueHandler.PostProcessorWrapper#supports(org.springframework.data.util.TypeInformation, java.lang.Object)
* @see org.springframework.data.rest.webmvc.ResourceEnricherHandlerMethodReturnValueHandler.PostProcessorWrapper#supports(org.springframework.data.util.TypeInformation, java.lang.Object)
*/
@Override
public boolean supports(TypeInformation<?> typeInformation, Object value) {
if (value == null || !Resource.class.isAssignableFrom(value.getClass())) {
return false;
}
return targetType.isAssignableFrom(typeInformation) || isValueTypeMatch((Resource<?>) value, targetType);
return targetType.isAssignableFrom(typeInformation);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.rest.webmvc.ResourceProcessingHandlerMethodReturnValueHandler.PostProcessorWrapper#invokeProcessor(java.lang.Object)
* @see org.springframework.data.rest.webmvc.ResourceEnricherHandlerMethodReturnValueHandler.PostProcessorWrapper#invokeProcessor(java.lang.Object)
*/
@Override
@SuppressWarnings("unchecked")
public Object invokeProcessor(Object object) {
return ((ResourceProcessor<Resource<?>>) processor).process((Resource<?>) object);
public void invokeEnricher(Object object) {
((ResourceEnricher<ResourceSupport>) enricher).enrich((ResourceSupport) object);
}
/*
@@ -276,7 +247,48 @@ public class ResourceProcessingHandlerMethodReturnValueHandler implements Handle
*/
@Override
public int getOrder() {
return CustomOrderAwareComparator.INSTANCE.getOrder(processor);
return CustomOrderAwareComparator.INSTANCE.getOrder(enricher);
}
/**
* Returns the target type the underlying {@link ResourceEnricher} wants to get invoked for.
*
* @return the targetType
*/
public TypeInformation<?> getTargetType() {
return targetType;
}
}
/**
* {@link EnricherWrapper} to deal with {@link ResourceEnricher}s for {@link Resource}s. Will fall back to peeking
* into the {@link Resource}'s content for type resolution.
*
* @author Oliver Gierke
*/
private static class ResourceEnricherWrapper extends DefaultEnricherWrapper {
/**
* Creates a new {@link ResourceEnricherWrapper} for the given {@link ResourceEnricher}.
*
* @param processor must not be {@literal null}.
*/
public ResourceEnricherWrapper(ResourceEnricher<?> processor) {
super(processor);
}
/*
* (non-Javadoc)
* @see org.springframework.data.rest.webmvc.ResourceEnricherHandlerMethodReturnValueHandler.PostProcessorWrapper#supports(org.springframework.data.util.TypeInformation, java.lang.Object)
*/
@Override
public boolean supports(TypeInformation<?> typeInformation, Object value) {
if (!RESOURCE_TYPE.isAssignableFrom(typeInformation)) {
return false;
}
return super.supports(typeInformation, value) || isValueTypeMatch((Resource<?>) value, getTargetType());
}
/**
@@ -305,58 +317,34 @@ public class ResourceProcessingHandlerMethodReturnValueHandler implements Handle
}
/**
* {@link PostProcessorWrapper} for {@link ResourcesProcessor}s.
* {@link EnricherWrapper} for {@link ResourceEnricher}s targeting {@link Resources}. Will peek into the content of
* the {@link Resources} for type matching decisions if needed.
*
* @author Oliver Gierke
*/
private static class ResourcesProcessorWrapper implements PostProcessorWrapper {
private final ResourcesProcessor<? extends Resources<?>> processor;
private final TypeInformation<?> targetType;
private static class ResourcesEnricherWrapper extends DefaultEnricherWrapper {
/**
* Creates a new {@link ResourcesProcessorWrapper} for the given {@link ResourcesProcessor}.
* Creates a new {@link ResourcesEnricherWrapper} for the given {@link ResourceEnricher}.
*
* @param processor must not be {@literal null}.
* @param enricher must not be {@literal null}.
*/
public ResourcesProcessorWrapper(ResourcesProcessor<? extends Resources<?>> processor) {
Assert.notNull(processor);
this.processor = processor;
this.targetType = from(processor.getClass()).getSuperTypeInformation(ResourcesProcessor.class).getComponentType();
public ResourcesEnricherWrapper(ResourceEnricher<?> enricher) {
super(enricher);
}
/* (non-Javadoc)
/*
* (non-Javadoc)
* @see org.springframework.data.rest.webmvc.PostProcessorWrapper#supports(org.springframework.data.util.TypeInformation, java.lang.Object)
*/
@Override
public boolean supports(TypeInformation<?> typeInformation, Object value) {
if (value == null || !Resources.class.isAssignableFrom(value.getClass())) {
if (!RESOURCES_TYPE.isAssignableFrom(typeInformation)) {
return false;
}
return targetType.isAssignableFrom(typeInformation) || isValueTypeMatch((Resources<?>) value, targetType);
}
/*
* (non-Javadoc)
* @see org.springframework.data.rest.webmvc.ResourceProcessingHandlerMethodReturnValueHandler.PostProcessorWrapper#invokeProcessor(java.lang.Object)
*/
@Override
@SuppressWarnings("unchecked")
public Object invokeProcessor(Object object) {
return ((ResourcesProcessor<Resources<?>>) processor).process((Resources<?>) object);
}
/*
* (non-Javadoc)
* @see org.springframework.core.Ordered#getOrder()
*/
@Override
public int getOrder() {
return CustomOrderAwareComparator.INSTANCE.getOrder(processor);
return super.supports(typeInformation, value) || isValueTypeMatch((Resources<?>) value, getTargetType());
}
/**
@@ -386,7 +374,7 @@ public class ResourceProcessingHandlerMethodReturnValueHandler implements Handle
}
TypeInformation<?> resourceTypeInformation = target.getSuperTypeInformation(Resources.class).getComponentType();
return ResourceProcessorWrapper.isValueTypeMatch((Resource<?>) element, resourceTypeInformation);
return ResourceEnricherWrapper.isValueTypeMatch((Resource<?>) element, resourceTypeInformation);
}
}

View File

@@ -19,32 +19,53 @@ import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceProcessor;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.ResourcesProcessor;
import org.springframework.hateoas.ResourceEnricher;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.web.bind.support.WebBindingInitializer;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
/**
* Special {@link RequestMappingHandlerAdapter} that tweaks the {@link HandlerMethodReturnValueHandlerComposite} to be
* proxied by a {@link ResourceProcessingHandlerMethodReturnValueHandler} which will invoke the
* {@link ResourceProcessor}s/{@link ResourcesProcessor}s found in the application context and eventually delegate to
* the originally configured {@link HandlerMethodReturnValueHandler}.
* proxied by a {@link ResourceEnricherHandlerMethodReturnValueHandler} which will invoke the {@link ResourceEnricher}s
* found in the application context and eventually delegate to the originally configured
* {@link HandlerMethodReturnValueHandler}.
* <p>
* This is a separate component as it might make sense to deploy it in a standalone SpringMVC application to enable post
* processing. It would actually make most sense in Spring HATEOAS project.
*
* @author Oliver Gierke
*/
public class ResourcePostProcessorInvokingHandlerAdapter extends RequestMappingHandlerAdapter {
public class ResourceEnricherInvokingHandlerAdapter extends RequestMappingHandlerAdapter {
@Autowired(required = false)
private List<ResourceProcessor<? extends Resource<?>>> resourceProcessors = new ArrayList<ResourceProcessor<? extends Resource<?>>>();
private List<ResourceEnricher<?>> resourcesEnrichers = new ArrayList<ResourceEnricher<?>>();
@Autowired(required = false)
private List<ResourcesProcessor<? extends Resources<?>>> resourcesProcessors = new ArrayList<ResourcesProcessor<? extends Resources<?>>>();
/**
* Empty constructor to setup a {@link ResourceEnricherInvokingHandlerAdapter}.
*/
public ResourceEnricherInvokingHandlerAdapter() {
}
/**
* Copy constructor to copy configuration of {@link HttpMessageConverter}s, {@link WebBindingInitializer}, custom
* {@link HandlerMethodArgumentResolver}s and custom {@link HandlerMethodReturnValueHandler}s.
*
* @param original must not be {@literal null}.
*/
public ResourceEnricherInvokingHandlerAdapter(RequestMappingHandlerAdapter original) {
Assert.notNull(original);
setMessageConverters(original.getMessageConverters());
setWebBindingInitializer(original.getWebBindingInitializer());
setCustomArgumentResolvers(original.getCustomArgumentResolvers());
setCustomReturnValueHandlers(original.getCustomReturnValueHandlers());
}
/*
* (non-Javadoc)
@@ -60,8 +81,7 @@ public class ResourcePostProcessorInvokingHandlerAdapter extends RequestMappingH
// Set up ResourceProcessingHandlerMethodResolver to delegate to originally configured ones
List<HandlerMethodReturnValueHandler> newHandlers = new ArrayList<HandlerMethodReturnValueHandler>();
newHandlers.add(new ResourceProcessingHandlerMethodReturnValueHandler(oldHandlers, resourceProcessors,
resourcesProcessors));
newHandlers.add(new ResourceEnricherHandlerMethodReturnValueHandler(oldHandlers, resourcesEnrichers));
// Configure the new handler to be used
this.setReturnValueHandlers(newHandlers);

View File

@@ -17,6 +17,7 @@ package org.springframework.data.rest.test.webmvc;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.lang.reflect.Method;
@@ -32,11 +33,11 @@ import org.mockito.Mockito;
import org.mockito.internal.matchers.Equals;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.MethodParameter;
import org.springframework.data.rest.webmvc.ResourceProcessingHandlerMethodReturnValueHandler;
import org.springframework.data.rest.webmvc.ResourceEnricherHandlerMethodReturnValueHandler;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceProcessor;
import org.springframework.hateoas.ResourceEnricher;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.ResourcesProcessor;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -45,10 +46,12 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* Unit tests for {@link ResourceEnricherHandlerMethodReturnValueHandler}.
*
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class ResourceProcessingHandlerMethodReturnValueHandlerUnitTests {
public class ResourceEnricherHandlerMethodReturnValueHandlerUnitTests {
@Mock
HandlerMethodReturnValueHandler delegate;
@@ -56,16 +59,20 @@ public class ResourceProcessingHandlerMethodReturnValueHandlerUnitTests {
@Mock
MethodParameter parameter;
List<ResourceProcessor<?>> resourceProcessors;
List<ResourcesProcessor<?>> resourcesProcessors;
List<ResourceEnricher<?>> resourceEnrichers;
Resource<String> source = new Resource<String>("foo");
Resource<String> result = StringResourceProcessor.RESULT;
Resource<String> source;
Resource<String> result;
@Before
public void setUp() {
resourceProcessors = new ArrayList<ResourceProcessor<?>>();
resourcesProcessors = new ArrayList<ResourcesProcessor<?>>();
resourceEnrichers = new ArrayList<ResourceEnricher<?>>();
resetResources();
}
private void resetResources() {
source = new Resource<String>("foo");
result = new Resource<String>("foo", StringResourceEnricher.LINK);
}
@Test
@@ -81,8 +88,8 @@ public class ResourceProcessingHandlerMethodReturnValueHandlerUnitTests {
private void assertSupport(boolean value) {
when(delegate.supportsReturnType(Mockito.any(MethodParameter.class))).thenReturn(value);
HandlerMethodReturnValueHandler handler = new ResourceProcessingHandlerMethodReturnValueHandler(delegate,
resourceProcessors, resourcesProcessors);
HandlerMethodReturnValueHandler handler = new ResourceEnricherHandlerMethodReturnValueHandler(delegate,
resourceEnrichers);
assertThat(handler.supportsReturnType(parameter), is(value));
}
@@ -90,65 +97,81 @@ public class ResourceProcessingHandlerMethodReturnValueHandlerUnitTests {
@Test
public void invokesStringPostProcessorForSimpleStringResource() throws Exception {
resourceProcessors.add(new StringResourceProcessor());
resourceProcessors.add(new LongResourceProcessor());
resourceEnrichers.add(new StringResourceEnricher());
resourceEnrichers.add(new LongResourceEnricher());
HttpEntity<Resource<String>> input = new HttpEntity<Resource<String>>(source);
HttpEntity<Resource<String>> output = new HttpEntity<Resource<String>>(result);
assertProcessorInvokedForMethod("stringResourceEntity", input, output);
resetResources();
input = new HttpEntity<Resource<String>>(source);
assertProcessorInvokedForMethod("resourceEntity", input, output);
}
@Test
public void invokesStringPostProcessorForSimpleStringResourceInResponseEntity() throws Exception {
resourceProcessors.add(new StringResourceProcessor());
resourceProcessors.add(new LongResourceProcessor());
resourceEnrichers.add(new StringResourceEnricher());
resourceEnrichers.add(new LongResourceEnricher());
ResponseEntity<Resource<String>> input = new ResponseEntity<Resource<String>>(source, HttpStatus.OK);
ResponseEntity<Resource<String>> output = new ResponseEntity<Resource<String>>(result, HttpStatus.OK);
assertProcessorInvokedForMethod("stringResourceEntity", input, output);
resetResources();
input = new ResponseEntity<Resource<String>>(source, HttpStatus.OK);
assertProcessorInvokedForMethod("resourceEntity", input, output);
}
@Test
public void invokesStringPostProcessorForSimpleStringResources() throws Exception {
resourceProcessors.add(new StringResourceProcessor());
resourceProcessors.add(new LongResourceProcessor());
resourcesProcessors.add(new StringResourcesProcessor());
resourceEnrichers.add(new StringResourceEnricher());
resourceEnrichers.add(new LongResourceEnricher());
resourceEnrichers.add(new StringResourcesEnricher());
Resources<Resource<String>> sources = new Resources<Resource<String>>(Collections.singleton(source));
HttpEntity<Resources<Resource<String>>> input = new HttpEntity<Resources<Resource<String>>>(sources);
HttpEntity<Resources<Resource<String>>> output = new HttpEntity<Resources<Resource<String>>>(
StringResourcesProcessor.RESULT);
new Resources<Resource<String>>(Collections.singleton(result), StringResourcesEnricher.LINK));
assertProcessorInvokedForMethod("stringResourceEntity", input, output);
resetResources();
input = new HttpEntity<Resources<Resource<String>>>(new Resources<Resource<String>>(Collections.singleton(source)));
output = new HttpEntity<Resources<Resource<String>>>(
new Resources<Resource<String>>(Collections.singleton(result), StringResourcesEnricher.LINK));
assertProcessorInvokedForMethod("resourceEntity", input, output);
}
@Test
public void invokesStringPostProcessorForSpecializedStringResource() throws Exception {
resourceProcessors.add(new StringResourceProcessor());
resourceProcessors.add(new LongResourceProcessor());
resourceEnrichers.add(new StringResourceEnricher());
resourceEnrichers.add(new LongResourceEnricher());
HttpEntity<Resource<String>> stringOutput = new HttpEntity<Resource<String>>(result);
HttpEntity<StringResource> specializedInput = new HttpEntity<StringResource>(new StringResource("foo"));
HttpEntity<StringResource> stringOutput = new HttpEntity<StringResource>(new StringResource("foo",
StringResourceEnricher.LINK));
assertProcessorInvokedForMethod("stringResourceEntity", specializedInput, stringOutput);
specializedInput = new HttpEntity<StringResource>(new StringResource("foo"));
assertProcessorInvokedForMethod("resourceEntity", specializedInput, stringOutput);
specializedInput = new HttpEntity<StringResource>(new StringResource("foo"));
assertProcessorInvokedForMethod("specializedStringResourceEntity", specializedInput, stringOutput);
}
@Test
public void doesNotInvokeSpecializedStringPostProcessorForSimpleStringResource() throws Exception {
resourceProcessors.add(new SpecializedStringResourceProcessor());
resourceProcessors.add(new LongResourceProcessor());
resourceEnrichers.add(new SpecializedStringResourceEnricher());
resourceEnrichers.add(new LongResourceEnricher());
HttpEntity<Resource<String>> input = new HttpEntity<Resource<String>>(source);
@@ -158,11 +181,12 @@ public class ResourceProcessingHandlerMethodReturnValueHandlerUnitTests {
@Test
public void invokesSpecializedStringPostProcessor() throws Exception {
resourceProcessors.add(new SpecializedStringResourceProcessor());
resourceProcessors.add(new LongResourceProcessor());
resourceEnrichers.add(new SpecializedStringResourceEnricher());
resourceEnrichers.add(new LongResourceEnricher());
HttpEntity<StringResource> input = new HttpEntity<StringResource>(new StringResource("foo"));
HttpEntity<StringResource> output = new HttpEntity<StringResource>(SpecializedStringResourceProcessor.RESULT);
HttpEntity<StringResource> output = new HttpEntity<StringResource>(new StringResource("foo",
SpecializedStringResourceEnricher.LINK));
assertProcessorInvokedForMethod("specializedStringResourceEntity", input, output);
}
@@ -170,22 +194,23 @@ public class ResourceProcessingHandlerMethodReturnValueHandlerUnitTests {
@Test
public void invokesLongPostProcessorForLongResource() throws Exception {
resourceProcessors.add(new StringResourceProcessor());
resourceProcessors.add(new LongResourceProcessor());
resourceEnrichers.add(new StringResourceEnricher());
resourceEnrichers.add(new LongResourceEnricher());
HttpEntity<Resource<Long>> input = new HttpEntity<Resource<Long>>(new Resource<Long>(50L));
HttpEntity<LongResource> specializedInput = new HttpEntity<LongResource>(new LongResource(50L));
HttpEntity<Resource<Long>> output = new HttpEntity<Resource<Long>>(LongResourceProcessor.RESULT);
assertProcessorInvokedForMethod("resourceEntity", specializedInput, output);
assertProcessorInvokedForMethod("numberResourceEntity", input, output);
assertProcessorInvokedForMethod("resourceEntity", specializedInput, new HttpEntity<LongResource>(new LongResource(
50L, LongResourceEnricher.LINK)));
assertProcessorInvokedForMethod("numberResourceEntity", input, new HttpEntity<Resource<Long>>(new Resource<Long>(
50L, LongResourceEnricher.LINK)));
}
private void assertProcessorInvokedForMethod(String methodName, Object returnValue, Object processedValue)
throws Exception {
HandlerMethodReturnValueHandler handler = new ResourceProcessingHandlerMethodReturnValueHandler(delegate,
resourceProcessors, resourcesProcessors);
HandlerMethodReturnValueHandler handler = new ResourceEnricherHandlerMethodReturnValueHandler(delegate,
resourceEnrichers);
Method method = Controller.class.getMethod(methodName);
MethodParameter returnType = new MethodParameter(method, -1);
@@ -267,69 +292,58 @@ public class ResourceProcessingHandlerMethodReturnValueHandlerUnitTests {
ResponseEntity<Resources<?>> resourcesResponseEntity();
}
/**
* {@link ResourceProcessor} to process {@link String}s.
*
* @author Oliver Gierke
*/
static class StringResourceProcessor implements ResourceProcessor<Resource<String>> {
static final Resource<String> RESULT = new Resource<String>("bar");
@Override
public Resource<String> process(Resource<String> resource) {
return RESULT;
}
}
static class StringResourcesProcessor implements ResourcesProcessor<Resources<Resource<String>>> {
static final Resources<Resource<String>> RESULT = new Resources<Resource<String>>(
Collections.singleton(StringResourceProcessor.RESULT));
@Override
public Resources<?> process(Resources<Resource<String>> resources) {
return RESULT;
}
}
/**
* {@link ResourceProcessor} to process {@link Long} values.
*
* @author Oliver Gierke
*/
static class LongResourceProcessor implements ResourceProcessor<Resource<Long>> {
static final Resource<Long> RESULT = new Resource<Long>(10L);
@Override
public Resource<?> process(Resource<Long> resource) {
return RESULT;
}
}
static class StringResource extends Resource<String> {
public StringResource(String value) {
super(value);
public StringResource(String value, Link... links) {
super(value, links);
}
}
static class LongResource extends Resource<Long> {
public LongResource(Long value) {
super(value);
public LongResource(Long value, Link... links) {
super(value, links);
}
}
static class SpecializedStringResourceProcessor implements ResourceProcessor<StringResource> {
static class StringResourceEnricher implements ResourceEnricher<Resource<String>> {
static final StringResource RESULT = new StringResource("foobar");
static final Link LINK = new Link("string-resource");
@Override
public Resource<?> process(StringResource resource) {
return RESULT;
public void enrich(Resource<String> resource) {
resource.add(LINK);
}
}
static class StringResourcesEnricher implements ResourceEnricher<Resources<Resource<String>>> {
static final Link LINK = new Link("string-resources");
@Override
public void enrich(Resources<Resource<String>> resource) {
resource.add(LINK);
}
}
static class LongResourceEnricher implements ResourceEnricher<Resource<Long>> {
static final Link LINK = new Link("long-resource");
@Override
public void enrich(Resource<Long> resource) {
resource.add(LINK);
}
}
static class SpecializedStringResourceEnricher implements ResourceEnricher<StringResource> {
static final Link LINK = new Link("specialized-string");
@Override
public void enrich(StringResource resource) {
resource.add(LINK);
}
}
}