#831 - Polishing.
Significant rework of the nullability annotation introduction to not produce any warnings anymore. Added nullability annotations where still missing. Removed the ones that were added erroneously. Tightened nullability contracts in a couple of places, most prominently LinkRelationProvider, that now uses a LookupContext as plugin delimiter so that implementations can selectively be looked up for item or collection link relation lookup. Removed obsolete declaration of Findbugs dependency.
This commit is contained in:
@@ -20,21 +20,23 @@ import java.net.URI;
|
||||
import org.springframework.hateoas.IanaLinkRelations;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Builder to ease building {@link Link} instances.
|
||||
*
|
||||
* @author Ricardo Gladwell
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
public interface LinkBuilder {
|
||||
|
||||
/**
|
||||
* Adds the given object's {@link String} representation as sub-resource to the current URI.
|
||||
*
|
||||
* @param object
|
||||
* @param object can be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
LinkBuilder slash(Object object);
|
||||
LinkBuilder slash(@Nullable Object object);
|
||||
|
||||
/**
|
||||
* Creates a URI of the link built by the current builder instance.
|
||||
|
||||
@@ -15,15 +15,26 @@
|
||||
*/
|
||||
package org.springframework.hateoas.server;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.server.LinkRelationProvider.LookupContext;
|
||||
import org.springframework.hateoas.server.core.DelegatingLinkRelationProvider;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.plugin.core.Plugin;
|
||||
|
||||
/**
|
||||
* API to provide {@link LinkRelation}s for collections and items of the given type.
|
||||
* API to provide {@link LinkRelation}s for collections and items of the given type. Implementations can be selected
|
||||
* based on the {@link LookupContext}, for item resource relations, collection resource relations or both.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @see #supports(LookupContext)
|
||||
*/
|
||||
public interface LinkRelationProvider extends Plugin<Class<?>> {
|
||||
public interface LinkRelationProvider extends Plugin<LookupContext> {
|
||||
|
||||
/**
|
||||
* Returns the relation type to be used to point to an item resource of the given type.
|
||||
@@ -40,4 +51,106 @@ public interface LinkRelationProvider extends Plugin<Class<?>> {
|
||||
* @return
|
||||
*/
|
||||
LinkRelation getCollectionResourceRelFor(Class<?> type);
|
||||
|
||||
/**
|
||||
* Callback method to manually select {@link LinkRelationProvider} implementations based on a given
|
||||
* {@link LookupContext}. User code shouldn't need to call this method explicitly but rather use
|
||||
* {@link DelegatingLinkRelationProvider}, equip that with a set of {@link LinkRelationProvider} implementations as
|
||||
* that will perform the selection of the matching one on invocations of {@link #getItemResourceRelFor(Class)} and
|
||||
* {@link #getCollectionResourceRelFor(Class)} transparently.
|
||||
*
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
boolean supports(LookupContext delimiter);
|
||||
|
||||
/**
|
||||
* {@link LinkRelationProvider} selection context for item resource relation lookups
|
||||
* ({@link #forItemResourceRelLookup(Class)}, collection resource relation lookups
|
||||
* {@link #forCollectionResourceRelLookup(Class)} or both {@link #forType(Class)}.
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@RequiredArgsConstructor(staticName = "of", access = AccessLevel.PRIVATE)
|
||||
@EqualsAndHashCode
|
||||
static class LookupContext {
|
||||
|
||||
private enum ResourceType {
|
||||
ITEM, COLLECTION;
|
||||
}
|
||||
|
||||
private final @NonNull @Getter Class<?> type;
|
||||
private final @Nullable ResourceType resourceType;
|
||||
|
||||
/**
|
||||
* Creates a {@link LookupContext} for the type in general, i.e. both item and collection relation lookups.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static LookupContext forType(Class<?> type) {
|
||||
return new LookupContext(type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link LookupContext} to lookup the item resource relation for the given type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static LookupContext forItemResourceRelLookup(Class<?> type) {
|
||||
return new LookupContext(type, ResourceType.ITEM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link LookupContext} to lookup the collection resource relation for the given type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static LookupContext forCollectionResourceRelLookup(Class<?> type) {
|
||||
return new LookupContext(type, ResourceType.COLLECTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the current context includes the item relation lookup.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isItemRelationLookup() {
|
||||
return resourceType == null || ResourceType.ITEM.equals(resourceType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the current context includes the collection relation lookup.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isCollectionRelationLookup() {
|
||||
return resourceType == null || ResourceType.COLLECTION.equals(resourceType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the lookup is executed for the given type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public boolean handlesType(Class<?> type) {
|
||||
return this.type.equals(type);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
ResourceType resourceType = this.resourceType;
|
||||
|
||||
return String.format("LookupContext for %s for %s resource relations.", type.getName(),
|
||||
resourceType == null ? "ITEM & COLLECTION" : resourceType.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* 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.
|
||||
@@ -19,12 +19,15 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.server.LinkRelationProvider;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link LinkRelationProvider} that evaluates the {@link Relation} annotation on entity types.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Alexander Baetz
|
||||
* @author Greg Turnquist
|
||||
@@ -38,13 +41,12 @@ public class AnnotationLinkRelationProvider implements LinkRelationProvider, Ord
|
||||
* @see org.springframework.hateoas.server.LinkRelationProvider#getCollectionResourceRelFor(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public LinkRelation getCollectionResourceRelFor(Class<?> type) {
|
||||
|
||||
Relation annotation = lookupAnnotation(type);
|
||||
|
||||
if (annotation == null || Relation.NO_RELATION.equals(annotation.collectionRelation())) {
|
||||
return null;
|
||||
throw new IllegalArgumentException(String.format("No collection relation found for type %s!", type.getName()));
|
||||
}
|
||||
|
||||
return LinkRelation.of(annotation.collectionRelation());
|
||||
@@ -55,13 +57,14 @@ public class AnnotationLinkRelationProvider implements LinkRelationProvider, Ord
|
||||
* @see org.springframework.hateoas.server.LinkRelationProvider#getItemResourceRelFor(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public LinkRelation getItemResourceRelFor(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Type must not be null!");
|
||||
|
||||
Relation annotation = lookupAnnotation(type);
|
||||
|
||||
if (annotation == null || Relation.NO_RELATION.equals(annotation.value())) {
|
||||
return null;
|
||||
throw new IllegalStateException(String.format("Type %s is not supported!", type.getName()));
|
||||
}
|
||||
|
||||
return LinkRelation.of(annotation.value());
|
||||
@@ -81,12 +84,27 @@ public class AnnotationLinkRelationProvider implements LinkRelationProvider, Ord
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> delimiter) {
|
||||
return lookupAnnotation(delimiter) != null;
|
||||
public boolean supports(LookupContext context) {
|
||||
|
||||
Relation relation = lookupAnnotation(context.getType());
|
||||
|
||||
if (relation == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (context.isItemRelationLookup()) {
|
||||
return !relation.value().equals(Relation.NO_RELATION);
|
||||
}
|
||||
|
||||
if (context.isCollectionRelationLookup()) {
|
||||
return !relation.collectionRelation().equals(Relation.NO_RELATION);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Relation lookupAnnotation(Class<?> type) {
|
||||
return annotationCache.computeIfAbsent(type, key -> AnnotationUtils.getAnnotation(key, Relation.class));
|
||||
return annotationCache.computeIfAbsent(type, key -> AnnotatedElementUtils.getMergedAnnotation(key, Relation.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ public class CachingMappingDiscoverer implements MappingDiscoverer {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.core.MappingDiscoverer#getMapping(java.lang.Class)
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public String getMapping(Class<?> type) {
|
||||
|
||||
@@ -56,6 +57,7 @@ public class CachingMappingDiscoverer implements MappingDiscoverer {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.core.MappingDiscoverer#getMapping(java.lang.reflect.Method)
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public String getMapping(Method method) {
|
||||
|
||||
@@ -68,6 +70,7 @@ public class CachingMappingDiscoverer implements MappingDiscoverer {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.core.MappingDiscoverer#getMapping(java.lang.Class, java.lang.reflect.Method)
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public String getMapping(Class<?> type, Method method) {
|
||||
|
||||
|
||||
@@ -28,16 +28,17 @@ import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.hateoas.server.ExposesResourceFor;
|
||||
import org.springframework.hateoas.server.LinkBuilder;
|
||||
import org.springframework.hateoas.server.LinkBuilderFactory;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} implementation to create {@link ControllerEntityLinks} instances looking up controller classes
|
||||
* from an {@link ApplicationContext}. The controller types are identified by the annotation type configured.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ControllerEntityLinksFactoryBean extends AbstractFactoryBean<ControllerEntityLinks> implements
|
||||
ApplicationContextAware {
|
||||
public class ControllerEntityLinksFactoryBean extends AbstractFactoryBean<ControllerEntityLinks>
|
||||
implements ApplicationContextAware {
|
||||
|
||||
private Class<? extends Annotation> annotation;
|
||||
private LinkBuilderFactory<? extends LinkBuilder> linkBuilderFactory;
|
||||
@@ -45,7 +46,7 @@ public class ControllerEntityLinksFactoryBean extends AbstractFactoryBean<Contro
|
||||
|
||||
/**
|
||||
* Configures the annotation type to inspect the {@link ApplicationContext} for beans that carry the given annotation.
|
||||
*
|
||||
*
|
||||
* @param annotation must not be {@literal null}.
|
||||
*/
|
||||
public void setAnnotation(Class<? extends Annotation> annotation) {
|
||||
@@ -55,14 +56,14 @@ public class ControllerEntityLinksFactoryBean extends AbstractFactoryBean<Contro
|
||||
|
||||
/**
|
||||
* Configures the {@link LinkBuilderFactory} to be used to create {@link LinkBuilder} instances.
|
||||
*
|
||||
*
|
||||
* @param linkBuilderFactory the linkBuilderFactory to set
|
||||
*/
|
||||
public void setLinkBuilderFactory(LinkBuilderFactory<? extends LinkBuilder> linkBuilderFactory) {
|
||||
this.linkBuilderFactory = linkBuilderFactory;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
|
||||
*/
|
||||
@@ -71,16 +72,17 @@ public class ControllerEntityLinksFactoryBean extends AbstractFactoryBean<Contro
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#getObjectType()
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return ControllerEntityLinks.class;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance()
|
||||
*/
|
||||
@@ -98,7 +100,7 @@ public class ControllerEntityLinksFactoryBean extends AbstractFactoryBean<Contro
|
||||
return new ControllerEntityLinks(controllerTypes, linkBuilderFactory);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#afterPropertiesSet()
|
||||
*/
|
||||
|
||||
@@ -62,7 +62,7 @@ public class DefaultLinkRelationProvider implements LinkRelationProvider, Ordere
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> delimiter) {
|
||||
public boolean supports(LookupContext delimiter) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,16 @@ import org.springframework.plugin.core.PluginRegistry;
|
||||
@RequiredArgsConstructor
|
||||
public class DelegatingLinkRelationProvider implements LinkRelationProvider {
|
||||
|
||||
private final @NonNull PluginRegistry<LinkRelationProvider, Class<?>> providers;
|
||||
private final @NonNull PluginRegistry<LinkRelationProvider, LookupContext> providers;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultLinkRelationProvider} for the given {@link LinkRelationProvider}s.
|
||||
*
|
||||
* @param providers must not be {@literal null}.
|
||||
*/
|
||||
public DelegatingLinkRelationProvider(LinkRelationProvider... providers) {
|
||||
this(PluginRegistry.of(providers));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -36,7 +45,10 @@ public class DelegatingLinkRelationProvider implements LinkRelationProvider {
|
||||
*/
|
||||
@Override
|
||||
public LinkRelation getItemResourceRelFor(Class<?> type) {
|
||||
return providers.getRequiredPluginFor(type).getItemResourceRelFor(type);
|
||||
|
||||
LookupContext context = LookupContext.forItemResourceRelLookup(type);
|
||||
|
||||
return providers.getRequiredPluginFor(context).getItemResourceRelFor(type);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -45,7 +57,10 @@ public class DelegatingLinkRelationProvider implements LinkRelationProvider {
|
||||
*/
|
||||
@Override
|
||||
public LinkRelation getCollectionResourceRelFor(java.lang.Class<?> type) {
|
||||
return providers.getRequiredPluginFor(type).getCollectionResourceRelFor(type);
|
||||
|
||||
LookupContext context = LookupContext.forCollectionResourceRelLookup(type);
|
||||
|
||||
return providers.getRequiredPluginFor(context).getCollectionResourceRelFor(type);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -53,7 +68,7 @@ public class DelegatingLinkRelationProvider implements LinkRelationProvider {
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(java.lang.Class<?> delimiter) {
|
||||
public boolean supports(LookupContext delimiter) {
|
||||
return providers.hasPluginFor(delimiter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,17 +21,12 @@ import lombok.Value;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.target.EmptyTargetSource;
|
||||
import org.springframework.cglib.proxy.Enhancer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.objenesis.ObjenesisStd;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
@@ -41,9 +36,6 @@ import org.springframework.util.ReflectionUtils;
|
||||
*/
|
||||
public class DummyInvocationUtils {
|
||||
|
||||
private static final ObjenesisStd OBJENESIS = new ObjenesisStd();
|
||||
private static final Map<Class<?>, Class<?>> CLASS_CACHE = new ConcurrentReferenceHashMap<>(16, ReferenceType.WEAK);
|
||||
|
||||
/**
|
||||
* Method interceptor that records the last method invocation and creates a proxy for the return value that exposes
|
||||
* the method invocation.
|
||||
@@ -86,6 +78,7 @@ public class DummyInvocationUtils {
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("null")
|
||||
public Object invoke(org.aopalliance.intercept.MethodInvocation invocation) {
|
||||
|
||||
Method method = invocation.getMethod();
|
||||
@@ -164,30 +157,6 @@ public class DummyInvocationUtils {
|
||||
return (T) factory.getProxy(classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the already created proxy class for the given source type or creates a new one.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @param classLoader must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private static Class<?> getOrCreateEnhancedClass(Class<?> type, ClassLoader classLoader) {
|
||||
|
||||
Assert.notNull(type, "Source type must not be null!");
|
||||
Assert.notNull(classLoader, "ClassLoader must not be null!");
|
||||
|
||||
return CLASS_CACHE.computeIfAbsent(type, key -> {
|
||||
|
||||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setSuperclass(key);
|
||||
enhancer.setInterfaces(new Class<?>[] { LastInvocationAware.class });
|
||||
enhancer.setCallbackType(org.springframework.cglib.proxy.MethodInterceptor.class);
|
||||
enhancer.setClassLoader(classLoader);
|
||||
|
||||
return enhancer.createClass();
|
||||
});
|
||||
}
|
||||
|
||||
@Value
|
||||
private static class SimpleMethodInvocation implements MethodInvocation {
|
||||
|
||||
|
||||
@@ -20,8 +20,9 @@ import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.EntityModel;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -186,6 +187,7 @@ public class EmbeddedWrappers {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.hal.EmbeddedWrapper#getValue()
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return value;
|
||||
@@ -195,6 +197,7 @@ public class EmbeddedWrappers {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EmbeddedWrappers.AbstractElementWrapper#peek()
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
protected Object peek() {
|
||||
return getValue();
|
||||
@@ -283,6 +286,7 @@ public class EmbeddedWrappers {
|
||||
public EmptyCollectionEmbeddedWrapper(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Element type must not be null!");
|
||||
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@@ -308,6 +312,7 @@ public class EmbeddedWrappers {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.core.EmbeddedWrapper#getRelTargetType()
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<?> getRelTargetType() {
|
||||
return type;
|
||||
|
||||
@@ -17,7 +17,6 @@ package org.springframework.hateoas.server.core;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.util.UriUtils;
|
||||
|
||||
@@ -40,7 +39,7 @@ class EncodingUtils {
|
||||
* @param source must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static String encodePath(@Nullable Object source) {
|
||||
public static String encodePath(Object source) {
|
||||
|
||||
Assert.notNull(source, "Path value must not be null!");
|
||||
|
||||
@@ -57,7 +56,7 @@ class EncodingUtils {
|
||||
* @param source must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static String encodeParameter(@Nullable Object source) {
|
||||
public static String encodeParameter(Object source) {
|
||||
|
||||
Assert.notNull(source, "Request parameter value must not be null!");
|
||||
|
||||
|
||||
@@ -44,8 +44,11 @@ public class HeaderLinksResponseEntity<T extends RepresentationModel<?>> extends
|
||||
private HeaderLinksResponseEntity(ResponseEntity<T> entity) {
|
||||
|
||||
super(entity.getBody(), getHeadersWithLinks(entity), entity.getStatusCode());
|
||||
if (entity.getBody() != null) {
|
||||
entity.getBody().removeLinks();
|
||||
|
||||
T body = entity.getBody();
|
||||
|
||||
if (body != null) {
|
||||
body.removeLinks();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +103,9 @@ public class HeaderLinksResponseEntity<T extends RepresentationModel<?>> extends
|
||||
*/
|
||||
private static <T extends RepresentationModel<?>> HttpHeaders getHeadersWithLinks(ResponseEntity<T> entity) {
|
||||
|
||||
Links links = entity.getBody() != null ? entity.getBody().getLinks() : Links.NONE;
|
||||
T body = entity.getBody();
|
||||
|
||||
Links links = body != null ? body.getLinks() : Links.NONE;
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.putAll(entity.getHeaders());
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.hateoas.IanaLinkRelations;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.server.LinkBuilder;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
@@ -84,7 +85,7 @@ public abstract class LinkBuilderSupport<T extends LinkBuilder> implements LinkB
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.LinkBuilder#slash(java.lang.Object)
|
||||
*/
|
||||
public T slash(Object object) {
|
||||
public T slash(@Nullable Object object) {
|
||||
|
||||
object = object instanceof Optional ? ((Optional<?>) object).orElse(null) : object;
|
||||
|
||||
@@ -117,7 +118,7 @@ public abstract class LinkBuilderSupport<T extends LinkBuilder> implements LinkB
|
||||
|
||||
String fragment = components.getFragment();
|
||||
|
||||
if (StringUtils.hasText(fragment)) {
|
||||
if (fragment != null && !fragment.trim().isEmpty()) {
|
||||
builder.fragment(encoded ? fragment : encodeFragment(fragment));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.springframework.lang.Nullable;
|
||||
/**
|
||||
* Strategy interface to discover a URI mapping and related {@link org.springframework.hateoas.Affordance}s for either a
|
||||
* given type or method.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@@ -32,7 +32,7 @@ public interface MappingDiscoverer {
|
||||
|
||||
/**
|
||||
* Returns the mapping associated with the given type.
|
||||
*
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return the type-level mapping or {@literal null} in case none is present.
|
||||
*/
|
||||
@@ -41,16 +41,17 @@ public interface MappingDiscoverer {
|
||||
|
||||
/**
|
||||
* Returns the mapping associated with the given {@link Method}. This will include the type-level mapping.
|
||||
*
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
* @return the method mapping including the type-level one or {@literal null} if neither of them present.
|
||||
*/
|
||||
@Nullable
|
||||
String getMapping(Method method);
|
||||
|
||||
/**
|
||||
* Returns the mapping for the given {@link Method} invoked on the given type. This can be used to calculate the
|
||||
* mapping for a super type method being invoked on a sub-type with a type mapping.
|
||||
*
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @param method must not be {@literal null}.
|
||||
* @return the method mapping including the type-level one or {@literal null} if neither of them present.
|
||||
@@ -61,7 +62,7 @@ public interface MappingDiscoverer {
|
||||
/**
|
||||
* Returns the HTTP verbs for the given {@link Method} invoked on the given type. This can be used to build hypermedia
|
||||
* templates.
|
||||
*
|
||||
*
|
||||
* @param type
|
||||
* @param method
|
||||
* @return
|
||||
|
||||
@@ -33,7 +33,7 @@ import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
|
||||
/**
|
||||
* Value object to represent {@link MethodParameters} to allow to easily find the ones with a given annotation.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class MethodParameters {
|
||||
@@ -45,7 +45,7 @@ public class MethodParameters {
|
||||
|
||||
/**
|
||||
* Creates a new {@link MethodParameters} from the given {@link Method}.
|
||||
*
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
*/
|
||||
public MethodParameters(Method method) {
|
||||
@@ -55,7 +55,7 @@ public class MethodParameters {
|
||||
/**
|
||||
* Creates a new {@link MethodParameters} for the given {@link Method} and {@link AnnotationAttribute}. If the latter
|
||||
* is given, method parameter names will be looked up from the annotation attribute if present.
|
||||
*
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
* @param namingAnnotation can be {@literal null}.
|
||||
*/
|
||||
@@ -71,7 +71,7 @@ public class MethodParameters {
|
||||
|
||||
/**
|
||||
* Returns all {@link MethodParameter}s.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<MethodParameter> getParameters() {
|
||||
@@ -80,7 +80,7 @@ public class MethodParameters {
|
||||
|
||||
/**
|
||||
* Returns the {@link MethodParameter} with the given name or {@literal null} if none found.
|
||||
*
|
||||
*
|
||||
* @param name must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
@@ -95,7 +95,7 @@ public class MethodParameters {
|
||||
|
||||
/**
|
||||
* Returns all parameters of the given type.
|
||||
*
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
* @since 0.9
|
||||
@@ -111,7 +111,7 @@ public class MethodParameters {
|
||||
|
||||
/**
|
||||
* Returns all {@link MethodParameter}s annotated with the given annotation type.
|
||||
*
|
||||
*
|
||||
* @param annotation must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@@ -130,7 +130,7 @@ public class MethodParameters {
|
||||
/**
|
||||
* Custom {@link MethodParameter} extension that will favor the name configured in the {@link AnnotationAttribute} if
|
||||
* set over discovering it.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class AnnotationNamingMethodParameter extends SynthesizingMethodParameter {
|
||||
@@ -141,7 +141,7 @@ public class MethodParameters {
|
||||
/**
|
||||
* Creates a new {@link AnnotationNamingMethodParameter} for the given {@link Method}'s parameter with the given
|
||||
* index.
|
||||
*
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
* @param parameterIndex
|
||||
* @param attribute can be {@literal null}
|
||||
@@ -153,10 +153,11 @@ public class MethodParameters {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.MethodParameter#getParameterName()
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public String getParameterName() {
|
||||
|
||||
@@ -172,8 +173,7 @@ public class MethodParameters {
|
||||
}
|
||||
}
|
||||
|
||||
name = super.getParameterName();
|
||||
return name;
|
||||
return super.getParameterName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* 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.
|
||||
@@ -20,13 +20,14 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.hateoas.EntityModel;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.hateoas.CollectionModel;
|
||||
import org.springframework.hateoas.EntityModel;
|
||||
|
||||
/**
|
||||
* Annotation to configure the relation to be used when embedding objects in HAL representations of {@link EntityModel}s
|
||||
* and {@link CollectionModel}.
|
||||
*
|
||||
*
|
||||
* @author Alexander Baetz
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@@ -37,15 +38,24 @@ public @interface Relation {
|
||||
String NO_RELATION = "";
|
||||
|
||||
/**
|
||||
* Defines the relation to be used when referring to a single resource.
|
||||
*
|
||||
* Defines the relation to be used when referring to a single resource. Alias for {@link #itemRelation()}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@AliasFor("itemRelation")
|
||||
String value() default NO_RELATION;
|
||||
|
||||
/**
|
||||
* Defines the relation to be used when referring to a single resource. Alias of {@link #value()}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@AliasFor("value")
|
||||
String itemRelation() default NO_RELATION;
|
||||
|
||||
/**
|
||||
* Defines the relation to be used when referring to a collection of resources.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String collectionRelation() default NO_RELATION;
|
||||
|
||||
@@ -109,7 +109,7 @@ public class TypeReferences {
|
||||
* @see org.springframework.core.ParameterizedTypeReference#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
return this == obj || obj instanceof SyntheticParameterizedTypeReference
|
||||
&& this.type.equals(((SyntheticParameterizedTypeReference<?>) obj).type);
|
||||
}
|
||||
|
||||
@@ -104,6 +104,7 @@ public class WebHandler {
|
||||
|
||||
for (AnnotatedParametersParameterAccessor.BoundMethodParameter parameter : PATH_VARIABLE_ACCESSOR
|
||||
.getBoundParameters(invocation)) {
|
||||
|
||||
values.put(parameter.getVariableName(), encodePath(parameter.asString()));
|
||||
}
|
||||
|
||||
@@ -222,7 +223,7 @@ public class WebHandler {
|
||||
* @see org.springframework.hateoas.mvc.AnnotatedParametersParameterAccessor#createParameter(org.springframework.core.MethodParameter, java.lang.Object, org.springframework.hateoas.core.AnnotationAttribute)
|
||||
*/
|
||||
@Override
|
||||
protected BoundMethodParameter createParameter(final MethodParameter parameter, Object value,
|
||||
protected BoundMethodParameter createParameter(final MethodParameter parameter, @Nullable Object value,
|
||||
AnnotationAttribute attribute) {
|
||||
|
||||
return new BoundMethodParameter(parameter, value, attribute) {
|
||||
@@ -252,7 +253,7 @@ public class WebHandler {
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
protected Object verifyParameterValue(MethodParameter parameter, Object value) {
|
||||
protected Object verifyParameterValue(MethodParameter parameter, @Nullable Object value) {
|
||||
|
||||
RequestParam annotation = parameter.getParameterAnnotation(RequestParam.class);
|
||||
|
||||
@@ -334,7 +335,7 @@ public class WebHandler {
|
||||
* @return the verified value.
|
||||
*/
|
||||
@Nullable
|
||||
protected Object verifyParameterValue(MethodParameter parameter, Object value) {
|
||||
protected Object verifyParameterValue(MethodParameter parameter, @Nullable Object value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -414,12 +415,21 @@ public class WebHandler {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
public String asString() {
|
||||
|
||||
return value == null //
|
||||
? null //
|
||||
: (String) CONVERSION_SERVICE.convert(value, parameterTypeDescriptor, STRING_DESCRIPTOR);
|
||||
Object value = this.value;
|
||||
|
||||
if (value == null) {
|
||||
throw new IllegalStateException("Cannot turn null value into required String!");
|
||||
}
|
||||
|
||||
Object result = CONVERSION_SERVICE.convert(value, parameterTypeDescriptor, STRING_DESCRIPTOR);
|
||||
|
||||
if (result == null) {
|
||||
throw new IllegalStateException(String.format("Conversion of value %s resulted in null!", value));
|
||||
}
|
||||
|
||||
return (String) result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.hateoas.server.mvc;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.server.ExposesResourceFor;
|
||||
import org.springframework.hateoas.server.LinkRelationProvider;
|
||||
@@ -23,21 +23,34 @@ import org.springframework.plugin.core.PluginRegistry;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* {@link LinkRelationProvider} inspecting {@link ExposesResourceFor} annotations on controller classes.
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
public class ControllerLinkRelationProvider implements LinkRelationProvider {
|
||||
|
||||
private final Class<?> controllerType;
|
||||
private final Class<?> entityType;
|
||||
private final PluginRegistry<LinkRelationProvider, Class<?>> providers;
|
||||
private final PluginRegistry<LinkRelationProvider, LookupContext> providers;
|
||||
|
||||
public ControllerLinkRelationProvider(Class<?> controller, PluginRegistry<LinkRelationProvider, Class<?>> providers) {
|
||||
/**
|
||||
* Creates a new {@link ControllerLinkRelationProvider}
|
||||
*
|
||||
* @param controller must not be {@literal null}.
|
||||
* @param providers must not be {@literal null}.
|
||||
*/
|
||||
public ControllerLinkRelationProvider(Class<?> controller,
|
||||
PluginRegistry<LinkRelationProvider, LookupContext> providers) {
|
||||
|
||||
Assert.notNull(controller, "Controller must not be null!");
|
||||
Assert.notNull(providers, "LinkRelationProviders must not be null!");
|
||||
|
||||
ExposesResourceFor annotation = AnnotationUtils.findAnnotation(controller, ExposesResourceFor.class);
|
||||
ExposesResourceFor annotation = AnnotatedElementUtils.findMergedAnnotation(controller, ExposesResourceFor.class);
|
||||
|
||||
Assert.notNull(annotation, "Controller must be annotated with ExposesResourceFor!");
|
||||
if (annotation == null) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Controller %s must be annotated with @ExposesResourceFor(…)!", controller.getName()));
|
||||
}
|
||||
|
||||
this.controllerType = controller;
|
||||
this.entityType = annotation.value();
|
||||
@@ -50,7 +63,10 @@ public class ControllerLinkRelationProvider implements LinkRelationProvider {
|
||||
*/
|
||||
@Override
|
||||
public LinkRelation getItemResourceRelFor(Class<?> resource) {
|
||||
return providers.getRequiredPluginFor(entityType).getItemResourceRelFor(resource);
|
||||
|
||||
LookupContext context = LookupContext.forItemResourceRelLookup(entityType);
|
||||
|
||||
return providers.getRequiredPluginFor(context).getItemResourceRelFor(resource);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -59,7 +75,10 @@ public class ControllerLinkRelationProvider implements LinkRelationProvider {
|
||||
*/
|
||||
@Override
|
||||
public LinkRelation getCollectionResourceRelFor(Class<?> resource) {
|
||||
return providers.getRequiredPluginFor(entityType).getCollectionResourceRelFor(resource);
|
||||
|
||||
LookupContext context = LookupContext.forCollectionResourceRelLookup(entityType);
|
||||
|
||||
return providers.getRequiredPluginFor(context).getCollectionResourceRelFor(resource);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -67,7 +86,7 @@ public class ControllerLinkRelationProvider implements LinkRelationProvider {
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> delimiter) {
|
||||
return controllerType.equals(delimiter);
|
||||
public boolean supports(LookupContext context) {
|
||||
return context.handlesType(controllerType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ public class JacksonSerializers {
|
||||
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public MediaType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
return MediaType.parseMediaType(p.getText());
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.
|
||||
@@ -19,6 +19,7 @@ import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
@@ -29,6 +30,7 @@ import org.springframework.hateoas.server.RepresentationModelProcessor;
|
||||
import org.springframework.hateoas.server.core.HeaderLinksResponseEntity;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
@@ -84,8 +86,8 @@ public class RepresentationModelProcessorHandlerMethodReturnValueHandler impleme
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
|
||||
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
|
||||
|
||||
Object value = returnValue;
|
||||
|
||||
@@ -99,8 +101,14 @@ public class RepresentationModelProcessorHandlerMethodReturnValueHandler impleme
|
||||
return;
|
||||
}
|
||||
|
||||
Method method = returnType.getMethod();
|
||||
|
||||
if (method == null) {
|
||||
throw new IllegalStateException(String.format("Return type %s does not expose a method!", returnType));
|
||||
}
|
||||
|
||||
// We have a Resource or Resources - find suitable processors
|
||||
ResolvableType targetType = ResolvableType.forMethodReturnType(returnType.getMethod());
|
||||
ResolvableType targetType = ResolvableType.forMethodReturnType(method);
|
||||
|
||||
// Unbox HttpEntity
|
||||
if (HTTP_ENTITY_TYPE.isAssignableFrom(targetType)) {
|
||||
@@ -127,7 +135,7 @@ public class RepresentationModelProcessorHandlerMethodReturnValueHandler impleme
|
||||
* @param originalValue the original input value.
|
||||
* @return
|
||||
*/
|
||||
Object rewrapResult(RepresentationModel<?> newBody, Object originalValue) {
|
||||
Object rewrapResult(RepresentationModel<?> newBody, @Nullable Object originalValue) {
|
||||
|
||||
if (!(originalValue instanceof HttpEntity)) {
|
||||
return rootLinksAsHeaders ? HeaderLinksResponseEntity.wrap(newBody) : newBody;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
* Copyright 2016-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.
|
||||
@@ -107,8 +107,13 @@ public class RepresentationModelProcessorInvoker {
|
||||
if (RepresentationModelProcessorHandlerMethodReturnValueHandler.RESOURCES_TYPE.isAssignableFrom(referenceType)) {
|
||||
|
||||
CollectionModel<?> resources = (CollectionModel<?>) value;
|
||||
ResolvableType elementTargetType = ResolvableType.forClass(CollectionModel.class, referenceType.getRawClass())
|
||||
.getGeneric(0);
|
||||
Class<?> rawClass = referenceType.getRawClass();
|
||||
|
||||
if (rawClass == null) {
|
||||
throw new IllegalArgumentException(String.format("%s does not expose a raw type!", referenceType));
|
||||
}
|
||||
|
||||
ResolvableType elementTargetType = ResolvableType.forClass(CollectionModel.class, rawClass).getGeneric(0);
|
||||
List<Object> result = new ArrayList<>(resources.getContent().size());
|
||||
|
||||
for (Object element : resources) {
|
||||
@@ -421,7 +426,9 @@ public class RepresentationModelProcessorInvoker {
|
||||
*/
|
||||
private static ResolvableType getSuperType(ResolvableType source, Class<?> superType) {
|
||||
|
||||
if (source.getRawClass() != null && source.getRawClass().equals(superType)) {
|
||||
Class<?> rawType = source.getRawClass();
|
||||
|
||||
if (rawType != null && rawType.equals(superType)) {
|
||||
return source;
|
||||
}
|
||||
|
||||
@@ -452,7 +459,7 @@ public class RepresentationModelProcessorInvoker {
|
||||
public static RepresentationModelProcessorInvoker.CustomOrderAwareComparator INSTANCE = new CustomOrderAwareComparator();
|
||||
|
||||
@Override
|
||||
protected int getOrder(Object obj) {
|
||||
protected int getOrder(@Nullable Object obj) {
|
||||
return super.getOrder(obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-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.
|
||||
@@ -20,6 +20,7 @@ import java.util.List;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@@ -28,7 +29,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
* Extension of {@link MappingJackson2HttpMessageConverter} to constrain the ability to read and write HTTP message
|
||||
* based on the target type. Useful in case the {@link ObjectMapper} about to be configured has customizations that
|
||||
* shall only be applied to object trees of a certain base type.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class TypeConstrainedMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
|
||||
@@ -37,7 +38,7 @@ public class TypeConstrainedMappingJackson2HttpMessageConverter extends MappingJ
|
||||
|
||||
/**
|
||||
* Creates a new {@link TypeConstrainedMappingJackson2HttpMessageConverter} for the given type.
|
||||
*
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
*/
|
||||
public TypeConstrainedMappingJackson2HttpMessageConverter(Class<?> type) {
|
||||
@@ -53,38 +54,39 @@ public class TypeConstrainedMappingJackson2HttpMessageConverter extends MappingJ
|
||||
* @param supportedMediaTypes
|
||||
* @param objectMapper
|
||||
*/
|
||||
public TypeConstrainedMappingJackson2HttpMessageConverter(Class<?> type, List<MediaType> supportedMediaTypes, ObjectMapper objectMapper) {
|
||||
public TypeConstrainedMappingJackson2HttpMessageConverter(Class<?> type, List<MediaType> supportedMediaTypes,
|
||||
ObjectMapper objectMapper) {
|
||||
|
||||
this(type);
|
||||
setSupportedMediaTypes(supportedMediaTypes);
|
||||
setObjectMapper(objectMapper);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#canRead(java.lang.Class, org.springframework.http.MediaType)
|
||||
*/
|
||||
@Override
|
||||
public boolean canRead(Class<?> clazz, MediaType mediaType) {
|
||||
public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
|
||||
return type.isAssignableFrom(clazz) && super.canRead(clazz, mediaType);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#canRead(java.lang.reflect.Type, java.lang.Class, org.springframework.http.MediaType)
|
||||
*/
|
||||
@Override
|
||||
public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
|
||||
public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
|
||||
return this.type.isAssignableFrom(getJavaType(type, contextClass).getRawClass())
|
||||
&& super.canRead(type, contextClass, mediaType);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#canWrite(java.lang.Class, org.springframework.http.MediaType)
|
||||
*/
|
||||
@Override
|
||||
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
|
||||
public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
|
||||
return type.isAssignableFrom(clazz) && super.canWrite(clazz, mediaType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,10 @@ class UriComponentsBuilderFactory {
|
||||
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
|
||||
if (requestAttributes == null) {
|
||||
throw new IllegalStateException("Could not look up RequestAttributes!");
|
||||
}
|
||||
|
||||
Assert.state(requestAttributes != null, REQUEST_ATTRIBUTES_MISSING);
|
||||
Assert.isInstanceOf(ServletRequestAttributes.class, requestAttributes);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.hateoas.server.mvc;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.hateoas.server.MethodLinkBuilderFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
@@ -24,7 +25,7 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
* SPI callback to enhance a {@link UriComponentsBuilder} when referring to a method through a dummy method invocation.
|
||||
* Will usually be implemented in implementations of {@link HandlerMethodArgumentResolver} as they represent exactly the
|
||||
* same functionality inverted.
|
||||
*
|
||||
*
|
||||
* @see MethodLinkBuilderFactory#linkTo(Object)
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@@ -32,7 +33,7 @@ public interface UriComponentsContributor {
|
||||
|
||||
/**
|
||||
* Returns whether the {@link UriComponentsBuilder} supports the given {@link MethodParameter}.
|
||||
*
|
||||
*
|
||||
* @param parameter will never be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@@ -40,10 +41,10 @@ public interface UriComponentsContributor {
|
||||
|
||||
/**
|
||||
* Enhance the given {@link UriComponentsBuilder} with the given value.
|
||||
*
|
||||
*
|
||||
* @param builder will never be {@literal null}.
|
||||
* @param parameter will never be {@literal null}.
|
||||
* @param parameter can be {@literal null}.
|
||||
* @param value can be {@literal null}.
|
||||
*/
|
||||
void enhance(UriComponentsBuilder builder, MethodParameter parameter, Object value);
|
||||
void enhance(UriComponentsBuilder builder, @Nullable MethodParameter parameter, @Nullable Object value);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.springframework.web.util.UriTemplate;
|
||||
* @author Oliver Trosien
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class WebMvcLinkBuilder extends TemplateVariableAwareLinkBuilderSupport<WebMvcLinkBuilder> {
|
||||
|
||||
private static final MappingDiscoverer DISCOVERER = CachingMappingDiscoverer
|
||||
|
||||
Reference in New Issue
Block a user