DATAREST-93 - Fixed formatting in Spring Data REST.
Added formatter to be used within Eclipse going forward.
This commit is contained in:
@@ -58,7 +58,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "deprecation" })
|
||||
class AbstractRepositoryRestController implements MessageSourceAware, InitializingBean {
|
||||
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AbstractRepositoryRestController.class);
|
||||
|
||||
private final PersistentEntityResourceAssembler<Object> perAssembler;
|
||||
@@ -69,8 +69,9 @@ class AbstractRepositoryRestController implements MessageSourceAware, Initializi
|
||||
private MessageSource messageSource;
|
||||
private PagedResourcesAssembler<Object> assembler;
|
||||
|
||||
public AbstractRepositoryRestController(PagedResourcesAssembler<Object> assembler, PersistentEntityResourceAssembler<Object> entityResourceAssembler) {
|
||||
|
||||
public AbstractRepositoryRestController(PagedResourcesAssembler<Object> assembler,
|
||||
PersistentEntityResourceAssembler<Object> entityResourceAssembler) {
|
||||
|
||||
this.assembler = assembler;
|
||||
this.perAssembler = entityResourceAssembler;
|
||||
}
|
||||
@@ -86,13 +87,13 @@ class AbstractRepositoryRestController implements MessageSourceAware, Initializi
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
// FIXME:
|
||||
|
||||
// if (null != txMgr) {
|
||||
// txTmpl = new TransactionTemplate(txMgr);
|
||||
// txTmpl.afterPropertiesSet();
|
||||
// }
|
||||
|
||||
// FIXME:
|
||||
|
||||
// if (null != txMgr) {
|
||||
// txTmpl = new TransactionTemplate(txMgr);
|
||||
// txTmpl.afterPropertiesSet();
|
||||
// }
|
||||
}
|
||||
|
||||
@ExceptionHandler({ NullPointerException.class })
|
||||
@@ -139,7 +140,7 @@ class AbstractRepositoryRestController implements MessageSourceAware, Initializi
|
||||
@ResponseBody
|
||||
public ResponseEntity handleRepositoryConstraintViolationException(Locale locale,
|
||||
RepositoryConstraintViolationException rcve) {
|
||||
|
||||
|
||||
return response(null, new RepositoryConstraintViolationExceptionMessage(rcve, messageSource, locale),
|
||||
HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
@@ -205,7 +206,7 @@ class AbstractRepositoryRestController implements MessageSourceAware, Initializi
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
protected Resources resultToResources(Object result, Link baseLink) {
|
||||
|
||||
|
||||
if (result instanceof Page) {
|
||||
Page<Object> page = (Page<Object>) result;
|
||||
return entitiesToResources(page, baseLink, assembler);
|
||||
@@ -225,7 +226,6 @@ class AbstractRepositoryRestController implements MessageSourceAware, Initializi
|
||||
return assembler.toResource(page, perAssembler, baseLink);
|
||||
}
|
||||
|
||||
|
||||
protected Resources<Resource<Object>> entitiesToResources(Iterable<Object> entities) {
|
||||
|
||||
List<Resource<Object>> resources = new ArrayList<Resource<Object>>();
|
||||
|
||||
@@ -33,30 +33,28 @@ import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
||||
*/
|
||||
public class BaseUriMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
private final RepositoryRestConfiguration config;
|
||||
private final RepositoryRestConfiguration config;
|
||||
|
||||
public BaseUriMethodArgumentResolver(RepositoryRestConfiguration config) {
|
||||
this.config = config;
|
||||
}
|
||||
public BaseUriMethodArgumentResolver(RepositoryRestConfiguration config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override public boolean supportsParameter(MethodParameter parameter) {
|
||||
return (null != parameter.getParameterAnnotation(BaseURI.class)
|
||||
&& parameter.getParameterType() == URI.class);
|
||||
}
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return (null != parameter.getParameterAnnotation(BaseURI.class) && parameter.getParameterType() == URI.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
@Override
|
||||
public URI resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
|
||||
// Use configured URI if there is one or set the current one as the default if not.
|
||||
if(null == config.getBaseUri()) {
|
||||
URI baseUri = ServletUriComponentsBuilder.fromServletMapping(servletRequest).build().toUri();
|
||||
config.setBaseUri(baseUri);
|
||||
}
|
||||
// Use configured URI if there is one or set the current one as the default if not.
|
||||
if (null == config.getBaseUri()) {
|
||||
URI baseUri = ServletUriComponentsBuilder.fromServletMapping(servletRequest).build().toUri();
|
||||
config.setBaseUri(baseUri);
|
||||
}
|
||||
|
||||
return config.getBaseUri();
|
||||
}
|
||||
return config.getBaseUri();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,11 +25,10 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ControllerUtils {
|
||||
|
||||
|
||||
public static final Resource<?> EMPTY_RESOURCE = new Resource<Object>(Collections.emptyList());
|
||||
public static final Resources<Resource<?>> EMPTY_RESOURCES = new Resources<Resource<?>>(
|
||||
Collections.<Resource<?>> emptyList());
|
||||
@@ -38,12 +37,12 @@ public class ControllerUtils {
|
||||
|
||||
public static <R extends Resource<?>> ResponseEntity<Resource<?>> toResponseEntity(HttpHeaders headers, R resource,
|
||||
HttpStatus status) {
|
||||
|
||||
|
||||
HttpHeaders hdrs = new HttpHeaders();
|
||||
if (null != headers) {
|
||||
hdrs.putAll(headers);
|
||||
}
|
||||
|
||||
|
||||
return new ResponseEntity<Resource<?>>(resource, hdrs, status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,14 +25,13 @@ import org.springframework.hateoas.ResourceAssembler;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class PersistentEntityResourceAssembler<T> implements ResourceAssembler<T, PersistentEntityResource<T>> {
|
||||
|
||||
private final Repositories repositories;
|
||||
private final EntityLinks entityLinks;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link PersistentEntityResourceAssembler}.
|
||||
*
|
||||
@@ -40,10 +39,10 @@ public class PersistentEntityResourceAssembler<T> implements ResourceAssembler<T
|
||||
* @param entityLinks must not be {@literal null}.
|
||||
*/
|
||||
public PersistentEntityResourceAssembler(Repositories repositories, EntityLinks entityLinks) {
|
||||
|
||||
|
||||
Assert.notNull(repositories, "Repositories must not be null!");
|
||||
Assert.notNull(entityLinks, "EntityLinks must not be null!");
|
||||
|
||||
|
||||
this.repositories = repositories;
|
||||
this.entityLinks = entityLinks;
|
||||
}
|
||||
@@ -56,19 +55,19 @@ public class PersistentEntityResourceAssembler<T> implements ResourceAssembler<T
|
||||
public PersistentEntityResource<T> toResource(T instance) {
|
||||
|
||||
PersistentEntity<?, ?> entity = repositories.getPersistentEntity(instance.getClass());
|
||||
|
||||
|
||||
PersistentEntityResource<T> resource = PersistentEntityResource.wrap(entity, instance);
|
||||
resource.add(getSelfLinkFor(instance));
|
||||
return resource;
|
||||
}
|
||||
|
||||
|
||||
public Link getSelfLinkFor(Object instance) {
|
||||
|
||||
|
||||
PersistentEntity<?, ?> entity = repositories.getPersistentEntity(instance.getClass());
|
||||
|
||||
|
||||
BeanWrapper<?, Object> wrapper = BeanWrapper.create(instance, null);
|
||||
Object id = wrapper.getProperty(entity.getIdProperty());
|
||||
|
||||
|
||||
return entityLinks.linkForSingleResource(entity.getType(), id).withSelfRel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,41 +18,38 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
*/
|
||||
public class PersistentEntityResourceHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
@Autowired
|
||||
private RepositoryRestRequestHandlerMethodArgumentResolver repoRequestResolver;
|
||||
private final List<HttpMessageConverter<?>> messageConverters;
|
||||
@Autowired private RepositoryRestRequestHandlerMethodArgumentResolver repoRequestResolver;
|
||||
private final List<HttpMessageConverter<?>> messageConverters;
|
||||
|
||||
public PersistentEntityResourceHandlerMethodArgumentResolver(List<HttpMessageConverter<?>> messageConverters) {
|
||||
this.messageConverters = messageConverters;
|
||||
}
|
||||
public PersistentEntityResourceHandlerMethodArgumentResolver(List<HttpMessageConverter<?>> messageConverters) {
|
||||
this.messageConverters = messageConverters;
|
||||
}
|
||||
|
||||
@Override public boolean supportsParameter(MethodParameter parameter) {
|
||||
return PersistentEntityResource.class.isAssignableFrom(parameter.getParameterType());
|
||||
}
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return PersistentEntityResource.class.isAssignableFrom(parameter.getParameterType());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
RepositoryRestRequest repoRequest = (RepositoryRestRequest)repoRequestResolver.resolveArgument(parameter,
|
||||
mavContainer,
|
||||
webRequest,
|
||||
binderFactory);
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||
RepositoryRestRequest repoRequest = (RepositoryRestRequest) repoRequestResolver.resolveArgument(parameter,
|
||||
mavContainer, webRequest, binderFactory);
|
||||
|
||||
final ServletServerHttpRequest request = new ServletServerHttpRequest(webRequest.getNativeRequest(HttpServletRequest.class));
|
||||
for(HttpMessageConverter converter : messageConverters) {
|
||||
Class<?> domainType = repoRequest.getPersistentEntity().getType();
|
||||
if(!converter.canRead(domainType, request.getHeaders().getContentType())) {
|
||||
continue;
|
||||
}
|
||||
final ServletServerHttpRequest request = new ServletServerHttpRequest(
|
||||
webRequest.getNativeRequest(HttpServletRequest.class));
|
||||
for (HttpMessageConverter converter : messageConverters) {
|
||||
Class<?> domainType = repoRequest.getPersistentEntity().getType();
|
||||
if (!converter.canRead(domainType, request.getHeaders().getContentType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object obj = converter.read(domainType, request);
|
||||
Object obj = converter.read(domainType, request);
|
||||
return new PersistentEntityResource<Object>(repoRequest.getPersistentEntity(), obj);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class RepositoryController extends AbstractRepositoryRestController {
|
||||
@Autowired
|
||||
public RepositoryController(Repositories repositories, RepositoryRestConfiguration config, EntityLinks entityLinks,
|
||||
PagedResourcesAssembler<Object> assembler, PersistentEntityResourceAssembler<Object> perAssembler) {
|
||||
|
||||
|
||||
super(assembler, perAssembler);
|
||||
|
||||
this.repositories = repositories;
|
||||
|
||||
@@ -71,8 +71,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
*/
|
||||
@RestController
|
||||
@SuppressWarnings("deprecation")
|
||||
class RepositoryEntityController extends AbstractRepositoryRestController implements
|
||||
ApplicationEventPublisherAware {
|
||||
class RepositoryEntityController extends AbstractRepositoryRestController implements ApplicationEventPublisherAware {
|
||||
|
||||
private static final String BASE_MAPPING = "/{repository}";
|
||||
|
||||
@@ -81,7 +80,7 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
|
||||
private final RepositoryRestConfiguration config;
|
||||
private final DomainClassConverter<?> converter;
|
||||
private final ConversionService conversionService;
|
||||
|
||||
|
||||
private final TransactionOperations txOperations;
|
||||
|
||||
private ApplicationEventPublisher publisher;
|
||||
@@ -91,7 +90,7 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
|
||||
@Autowired
|
||||
public RepositoryEntityController(Repositories repositories, RepositoryRestConfiguration config,
|
||||
EntityLinks entityLinks, PagedResourcesAssembler<Object> assembler,
|
||||
PersistentEntityResourceAssembler<Object> perAssembler, DomainClassConverter<?> converter,
|
||||
PersistentEntityResourceAssembler<Object> perAssembler, DomainClassConverter<?> converter,
|
||||
@Qualifier("defaultConversionService") ConversionService conversionService) {
|
||||
|
||||
super(assembler, perAssembler);
|
||||
@@ -101,7 +100,7 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
|
||||
this.config = config;
|
||||
this.converter = converter;
|
||||
this.conversionService = conversionService;
|
||||
|
||||
|
||||
this.txOperations = null;
|
||||
}
|
||||
|
||||
@@ -203,7 +202,7 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code GET /{repository}/{id}}
|
||||
* {@code GET / repository}/{id}}
|
||||
*
|
||||
* @param repoRequest
|
||||
* @param id
|
||||
@@ -231,7 +230,7 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code PUT /{repository}/{id}} - Updates an existing entity or creates one at exactly that place.
|
||||
* {@code PUT / repository}/{id}} - Updates an existing entity or creates one at exactly that place.
|
||||
*
|
||||
* @param repoRequest
|
||||
* @param incoming
|
||||
@@ -244,7 +243,7 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource<?>> updateEntity(RepositoryRestRequest repoRequest,
|
||||
PersistentEntityResource<Object> incoming, @PathVariable String id) throws ResourceNotFoundException {
|
||||
|
||||
|
||||
RepositoryMethodInvoker repoMethodInvoker = repoRequest.getRepositoryMethodInvoker();
|
||||
if (null == repoMethodInvoker || !repoMethodInvoker.hasSaveOne() || !repoMethodInvoker.hasFindOne()) {
|
||||
throw new NoSuchMethodError();
|
||||
@@ -308,15 +307,15 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// FIXME
|
||||
|
||||
|
||||
if (txOperations != null) {
|
||||
txOperations.execute(callback);
|
||||
} else {
|
||||
callback.doInTransaction(null);
|
||||
}
|
||||
|
||||
|
||||
publisher.publishEvent(new AfterDeleteEvent(domainObj));
|
||||
|
||||
return new ResponseEntity<Object>(HttpStatus.NO_CONTENT);
|
||||
|
||||
@@ -35,31 +35,29 @@ import org.springframework.web.util.UrlPathHelper;
|
||||
public class RepositoryInformationHandlerMethodArgumentResolver extends RepositoryInformationSupport implements
|
||||
HandlerMethodArgumentResolver {
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return isAssignable(parameter.getParameterType(), RepositoryInformation.class);
|
||||
}
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return isAssignable(parameter.getParameterType(), RepositoryInformation.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryInformation resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
|
||||
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
String requestUri = new UrlPathHelper().getLookupPathForRequest(request);
|
||||
|
||||
if(requestUri.startsWith("/")) {
|
||||
requestUri = requestUri.substring(1);
|
||||
}
|
||||
@Override
|
||||
public RepositoryInformation resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||
|
||||
String[] parts = requestUri.split("/");
|
||||
|
||||
if(parts.length == 0) {
|
||||
// Root request
|
||||
return null;
|
||||
}
|
||||
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
String requestUri = new UrlPathHelper().getLookupPathForRequest(request);
|
||||
|
||||
return findRepositoryInfoFor(parts[0]);
|
||||
}
|
||||
if (requestUri.startsWith("/")) {
|
||||
requestUri = requestUri.substring(1);
|
||||
}
|
||||
|
||||
String[] parts = requestUri.split("/");
|
||||
|
||||
if (parts.length == 0) {
|
||||
// Root request
|
||||
return null;
|
||||
}
|
||||
|
||||
return findRepositoryInfoFor(parts[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,11 +62,12 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RestController
|
||||
@SuppressWarnings({"unchecked", "deprecation"})
|
||||
public class RepositoryPropertyReferenceController extends AbstractRepositoryRestController implements ApplicationEventPublisherAware {
|
||||
|
||||
@SuppressWarnings({ "unchecked", "deprecation" })
|
||||
public class RepositoryPropertyReferenceController extends AbstractRepositoryRestController implements
|
||||
ApplicationEventPublisherAware {
|
||||
|
||||
private static final String BASE_MAPPING = "/{repository}/{id}/{property}";
|
||||
|
||||
|
||||
private final Repositories repositories;
|
||||
private final RepositoryRestConfiguration config;
|
||||
private final PersistentEntityResourceAssembler<Object> perAssembler;
|
||||
@@ -78,15 +79,15 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
public RepositoryPropertyReferenceController(Repositories repositories, RepositoryRestConfiguration config,
|
||||
DomainClassConverter<?> domainClassConverter, PagedResourcesAssembler<Object> assembler,
|
||||
PersistentEntityResourceAssembler<Object> perAssembler) {
|
||||
|
||||
|
||||
super(assembler, perAssembler);
|
||||
|
||||
|
||||
this.repositories = repositories;
|
||||
this.perAssembler = perAssembler;
|
||||
this.config = config;
|
||||
this.converter = domainClassConverter;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher)
|
||||
@@ -96,84 +97,71 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
this.publisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
value = BASE_MAPPING,
|
||||
method = RequestMethod.GET,
|
||||
produces = {
|
||||
"application/json",
|
||||
"application/x-spring-data-verbose+json"
|
||||
}
|
||||
)
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET, produces = { "application/json",
|
||||
"application/x-spring-data-verbose+json" })
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource<?>> followPropertyReference(final RepositoryRestRequest repoRequest,
|
||||
@PathVariable String id,
|
||||
@PathVariable String property)
|
||||
throws ResourceNotFoundException, NoSuchMethodException {
|
||||
@PathVariable String id, @PathVariable String property) throws ResourceNotFoundException, NoSuchMethodException {
|
||||
final HttpHeaders headers = new HttpHeaders();
|
||||
Function<ReferencedProperty, Resource<?>> handler = new Function<ReferencedProperty, Resource<?>>() {
|
||||
@Override public Resource<?> apply(ReferencedProperty prop) {
|
||||
|
||||
if(null == prop.propertyValue) {
|
||||
@Override
|
||||
public Resource<?> apply(ReferencedProperty prop) {
|
||||
|
||||
if (null == prop.propertyValue) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
if(prop.property.isCollectionLike()) {
|
||||
|
||||
|
||||
if (prop.property.isCollectionLike()) {
|
||||
|
||||
List<Resource<?>> resources = new ArrayList<Resource<?>>();
|
||||
|
||||
for(Object obj : ((Iterable<Object>) prop.propertyValue)) {
|
||||
|
||||
for (Object obj : ((Iterable<Object>) prop.propertyValue)) {
|
||||
resources.add(perAssembler.toResource(obj));
|
||||
}
|
||||
|
||||
return new Resource<Object>(resources);
|
||||
|
||||
} else if(prop.property.isMap()) {
|
||||
|
||||
|
||||
} else if (prop.property.isMap()) {
|
||||
|
||||
Map<Object, Resource<?>> resources = new HashMap<Object, Resource<?>>();
|
||||
|
||||
for(Map.Entry<Object, Object> entry : ((Map<Object, Object>) prop.propertyValue).entrySet()) {
|
||||
|
||||
for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) prop.propertyValue).entrySet()) {
|
||||
resources.put(entry.getKey(), perAssembler.toResource(entry.getValue()));
|
||||
}
|
||||
|
||||
return new Resource<Object>(resources);
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
PersistentEntityResource<Object> resource = perAssembler.toResource(prop.propertyValue);
|
||||
headers.set("Content-Location", resource.getId().getHref());
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
};
|
||||
Resource<?> responseResource = doWithReferencedProperty(repoRequest,
|
||||
id,
|
||||
property,
|
||||
handler);
|
||||
Resource<?> responseResource = doWithReferencedProperty(repoRequest, id, property, handler);
|
||||
return ControllerUtils.toResponseEntity(headers, responseResource, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
value = BASE_MAPPING,
|
||||
method = RequestMethod.DELETE
|
||||
)
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.DELETE)
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource<?>> deletePropertyReference(final RepositoryRestRequest repoRequest,
|
||||
@PathVariable String id,
|
||||
@PathVariable String property)
|
||||
throws ResourceNotFoundException, NoSuchMethodException, HttpRequestMethodNotSupportedException {
|
||||
@PathVariable String id, @PathVariable String property) throws ResourceNotFoundException, NoSuchMethodException,
|
||||
HttpRequestMethodNotSupportedException {
|
||||
final RepositoryMethodInvoker repoMethodInvoker = repoRequest.getRepositoryMethodInvoker();
|
||||
if(!repoMethodInvoker.hasDeleteOne()) {
|
||||
if (!repoMethodInvoker.hasDeleteOne()) {
|
||||
throw new NoSuchMethodException();
|
||||
}
|
||||
|
||||
Function<ReferencedProperty, Resource<?>> handler = new Function<ReferencedProperty, Resource<?>>() {
|
||||
@Override public Resource<?> apply(ReferencedProperty prop) {
|
||||
if(null == prop.propertyValue) {
|
||||
@Override
|
||||
public Resource<?> apply(ReferencedProperty prop) {
|
||||
if (null == prop.propertyValue) {
|
||||
return null;
|
||||
}
|
||||
if(prop.property.isCollectionLike()) {
|
||||
if (prop.property.isCollectionLike()) {
|
||||
throw new IllegalArgumentException(new HttpRequestMethodNotSupportedException("DELETE"));
|
||||
} else if(prop.property.isMap()) {
|
||||
} else if (prop.property.isMap()) {
|
||||
throw new IllegalArgumentException(new HttpRequestMethodNotSupportedException("DELETE"));
|
||||
} else {
|
||||
prop.wrapper.setProperty(prop.property, null);
|
||||
@@ -186,62 +174,50 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
}
|
||||
};
|
||||
try {
|
||||
doWithReferencedProperty(repoRequest,
|
||||
id,
|
||||
property,
|
||||
handler);
|
||||
} catch(IllegalArgumentException iae) {
|
||||
if(iae.getCause() instanceof HttpRequestMethodNotSupportedException) {
|
||||
throw (HttpRequestMethodNotSupportedException)iae.getCause();
|
||||
doWithReferencedProperty(repoRequest, id, property, handler);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
if (iae.getCause() instanceof HttpRequestMethodNotSupportedException) {
|
||||
throw (HttpRequestMethodNotSupportedException) iae.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
return ControllerUtils.toResponseEntity(null, EMPTY_RESOURCE, HttpStatus.NO_CONTENT);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
value = BASE_MAPPING + "/{propertyId}",
|
||||
method = RequestMethod.GET,
|
||||
produces = {
|
||||
"application/json",
|
||||
"application/x-spring-data-verbose+json",
|
||||
"application/x-spring-data-compact+json",
|
||||
"text/uri-list"
|
||||
}
|
||||
)
|
||||
@RequestMapping(value = BASE_MAPPING + "/{propertyId}", method = RequestMethod.GET, produces = { "application/json",
|
||||
"application/x-spring-data-verbose+json", "application/x-spring-data-compact+json", "text/uri-list" })
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource<?>> followPropertyReference(final RepositoryRestRequest repoRequest,
|
||||
@PathVariable String id,
|
||||
@PathVariable String property,
|
||||
final @PathVariable String propertyId)
|
||||
@PathVariable String id, @PathVariable String property, final @PathVariable String propertyId)
|
||||
throws ResourceNotFoundException, NoSuchMethodException {
|
||||
final HttpHeaders headers = new HttpHeaders();
|
||||
Function<ReferencedProperty, Resource<?>> handler = new Function<ReferencedProperty, Resource<?>>() {
|
||||
@Override public Resource<?> apply(ReferencedProperty prop) {
|
||||
if(null == prop.propertyValue) {
|
||||
@Override
|
||||
public Resource<?> apply(ReferencedProperty prop) {
|
||||
if (null == prop.propertyValue) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
if(prop.property.isCollectionLike()) {
|
||||
for(Object obj : ((Iterable<?>)prop.propertyValue)) {
|
||||
|
||||
if (prop.property.isCollectionLike()) {
|
||||
for (Object obj : ((Iterable<?>) prop.propertyValue)) {
|
||||
|
||||
BeanWrapper<?, Object> propValWrapper = BeanWrapper.create(obj, null);
|
||||
String sId = propValWrapper.getProperty(prop.entity.getIdProperty()).toString();
|
||||
|
||||
if(propertyId.equals(sId)) {
|
||||
|
||||
|
||||
if (propertyId.equals(sId)) {
|
||||
|
||||
PersistentEntityResource<Object> resource = perAssembler.toResource(obj);
|
||||
headers.set("Content-Location", resource.getId().getHref());
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
} else if(prop.property.isMap()) {
|
||||
for(Map.Entry<Object, Object> entry : ((Map<Object, Object>)prop.propertyValue).entrySet()) {
|
||||
|
||||
} else if (prop.property.isMap()) {
|
||||
for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) prop.propertyValue).entrySet()) {
|
||||
|
||||
BeanWrapper<?, Object> propValWrapper = BeanWrapper.create(entry.getValue(), null);
|
||||
String sId = propValWrapper.getProperty(prop.entity.getIdProperty()).toString();
|
||||
|
||||
if(propertyId.equals(sId)) {
|
||||
|
||||
|
||||
if (propertyId.equals(sId)) {
|
||||
|
||||
PersistentEntityResource<Object> resource = perAssembler.toResource(entry.getValue());
|
||||
headers.set("Content-Location", resource.getId().getHref());
|
||||
return resource;
|
||||
@@ -253,26 +229,18 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
throw new IllegalArgumentException(new ResourceNotFoundException());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Resource<?> responseResource = doWithReferencedProperty(repoRequest, id, property, handler);
|
||||
return ControllerUtils.toResponseEntity(headers, responseResource, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
value = BASE_MAPPING,
|
||||
method = RequestMethod.GET,
|
||||
produces = {
|
||||
"application/x-spring-data-compact+json",
|
||||
"text/uri-list"
|
||||
}
|
||||
)
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET, produces = {
|
||||
"application/x-spring-data-compact+json", "text/uri-list" })
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource<?>> followPropertyReferenceCompact(RepositoryRestRequest repoRequest,
|
||||
@PathVariable String id,
|
||||
@PathVariable String property)
|
||||
throws ResourceNotFoundException, NoSuchMethodException {
|
||||
@PathVariable String id, @PathVariable String property) throws ResourceNotFoundException, NoSuchMethodException {
|
||||
ResponseEntity<Resource<?>> response = followPropertyReference(repoRequest, id, property);
|
||||
if(response.getStatusCode() != HttpStatus.OK) {
|
||||
if (response.getStatusCode() != HttpStatus.OK) {
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -281,32 +249,25 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
String propName = entityMapping.getNameForPath(property);
|
||||
ResourceMapping propMapping = entityMapping.getResourceMappingFor(entityMapping.getNameForPath(property));
|
||||
PersistentProperty<?> persistentProp = repoRequest.getPersistentEntity().getPersistentProperty(propName);
|
||||
Class<?> propType = (persistentProp.isCollectionLike() || persistentProp.isMap()
|
||||
? persistentProp.getComponentType()
|
||||
: persistentProp.getType());
|
||||
Class<?> propType = (persistentProp.isCollectionLike() || persistentProp.isMap() ? persistentProp
|
||||
.getComponentType() : persistentProp.getType());
|
||||
ResourceMapping propRepoMapping = getResourceMapping(config, repositories.getRepositoryInformationFor(propType));
|
||||
String propRel = String.format("%s.%s.%s.%s",
|
||||
repoMapping.getRel(),
|
||||
entityMapping.getRel(),
|
||||
(null != propMapping ? propMapping.getRel() : property),
|
||||
propRepoMapping.getRel());
|
||||
String propRel = String.format("%s.%s.%s.%s", repoMapping.getRel(), entityMapping.getRel(),
|
||||
(null != propMapping ? propMapping.getRel() : property), propRepoMapping.getRel());
|
||||
|
||||
Resource<?> resource = response.getBody();
|
||||
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
|
||||
URI entityBaseUri = buildUri(repoRequest.getBaseUri(),
|
||||
repoMapping.getPath(),
|
||||
id,
|
||||
property);
|
||||
URI entityBaseUri = buildUri(repoRequest.getBaseUri(), repoMapping.getPath(), id, property);
|
||||
|
||||
if(resource.getContent() instanceof Iterable) {
|
||||
for(Resource<?> res : (Iterable<Resource<?>>)resource.getContent()) {
|
||||
if (resource.getContent() instanceof Iterable) {
|
||||
for (Resource<?> res : (Iterable<Resource<?>>) resource.getContent()) {
|
||||
Link propLink = propertyReferenceLink(res, entityBaseUri, propRel);
|
||||
links.add(propLink);
|
||||
}
|
||||
} else if(resource.getContent() instanceof Map) {
|
||||
for(Map.Entry<Object, Resource<?>> entry : ((Map<Object, Resource<?>>)resource.getContent()).entrySet()) {
|
||||
} else if (resource.getContent() instanceof Map) {
|
||||
for (Map.Entry<Object, Resource<?>> entry : ((Map<Object, Resource<?>>) resource.getContent()).entrySet()) {
|
||||
Link l = new Link(entry.getValue().getLink("self").getHref(), entry.getKey().toString());
|
||||
links.add(l);
|
||||
}
|
||||
@@ -317,56 +278,45 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
return ControllerUtils.toResponseEntity(null, new Resource<Object>(EMPTY_RESOURCE_LIST, links), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
value = BASE_MAPPING,
|
||||
method = {
|
||||
RequestMethod.POST,
|
||||
RequestMethod.PUT
|
||||
},
|
||||
consumes = {
|
||||
"application/json",
|
||||
"application/x-spring-data-compact+json",
|
||||
"text/uri-list"
|
||||
}
|
||||
)
|
||||
@RequestMapping(value = BASE_MAPPING, method = { RequestMethod.POST, RequestMethod.PUT }, consumes = {
|
||||
"application/json", "application/x-spring-data-compact+json", "text/uri-list" })
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource<?>> createPropertyReference(final RepositoryRestRequest repoRequest,
|
||||
final @RequestBody Resource<Object> incoming,
|
||||
@PathVariable String id,
|
||||
@PathVariable String property)
|
||||
final @RequestBody Resource<Object> incoming, @PathVariable String id, @PathVariable String property)
|
||||
throws ResourceNotFoundException, NoSuchMethodException {
|
||||
final RepositoryMethodInvoker repoMethodInvoker = repoRequest.getRepositoryMethodInvoker();
|
||||
if(!repoMethodInvoker.hasSaveOne()) {
|
||||
if (!repoMethodInvoker.hasSaveOne()) {
|
||||
throw new NoSuchMethodException();
|
||||
}
|
||||
Function<ReferencedProperty, Resource<?>> handler = new Function<ReferencedProperty, Resource<?>>() {
|
||||
@Override public Resource<?> apply(ReferencedProperty prop) {
|
||||
if(prop.property.isCollectionLike()) {
|
||||
@Override
|
||||
public Resource<?> apply(ReferencedProperty prop) {
|
||||
if (prop.property.isCollectionLike()) {
|
||||
Collection<Object> coll = new ArrayList<Object>();
|
||||
if("POST".equals(repoRequest.getRequest().getMethod())) {
|
||||
coll.addAll((Collection<Object>)prop.propertyValue);
|
||||
if ("POST".equals(repoRequest.getRequest().getMethod())) {
|
||||
coll.addAll((Collection<Object>) prop.propertyValue);
|
||||
}
|
||||
for(Link l : incoming.getLinks()) {
|
||||
for (Link l : incoming.getLinks()) {
|
||||
Object propVal = loadPropertyValue(prop.propertyType, l.getHref());
|
||||
coll.add(propVal);
|
||||
}
|
||||
prop.wrapper.setProperty(prop.property, coll);
|
||||
} else if(prop.property.isMap()) {
|
||||
} else if (prop.property.isMap()) {
|
||||
Map<String, Object> m = new HashMap<String, Object>();
|
||||
if("POST".equals(repoRequest.getRequest().getMethod())) {
|
||||
m.putAll((Map<String, Object>)prop.propertyValue);
|
||||
if ("POST".equals(repoRequest.getRequest().getMethod())) {
|
||||
m.putAll((Map<String, Object>) prop.propertyValue);
|
||||
}
|
||||
for(Link l : incoming.getLinks()) {
|
||||
for (Link l : incoming.getLinks()) {
|
||||
Object propVal = loadPropertyValue(prop.propertyType, l.getHref());
|
||||
m.put(l.getRel(), propVal);
|
||||
}
|
||||
prop.wrapper.setProperty(prop.property, m);
|
||||
} else {
|
||||
if("POST".equals(repoRequest.getRequest().getMethod())) {
|
||||
if ("POST".equals(repoRequest.getRequest().getMethod())) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot POST a reference to this singular property since the property type is not a List or a Map.");
|
||||
}
|
||||
if(incoming.getLinks().size() != 1) {
|
||||
if (incoming.getLinks().size() != 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"Must send only 1 link to update a property reference that isn't a List or a Map.");
|
||||
}
|
||||
@@ -380,49 +330,42 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
return null;
|
||||
}
|
||||
};
|
||||
doWithReferencedProperty(repoRequest,
|
||||
id,
|
||||
property,
|
||||
handler);
|
||||
doWithReferencedProperty(repoRequest, id, property, handler);
|
||||
return ControllerUtils.toResponseEntity(null, EMPTY_RESOURCE, HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
value = BASE_MAPPING + "/{propertyId}",
|
||||
method = RequestMethod.DELETE
|
||||
)
|
||||
@RequestMapping(value = BASE_MAPPING + "/{propertyId}", method = RequestMethod.DELETE)
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource<?>> deletePropertyReferenceId(final RepositoryRestRequest repoRequest,
|
||||
@PathVariable String id,
|
||||
@PathVariable String property,
|
||||
final @PathVariable String propertyId)
|
||||
@PathVariable String id, @PathVariable String property, final @PathVariable String propertyId)
|
||||
throws ResourceNotFoundException, NoSuchMethodException {
|
||||
final RepositoryMethodInvoker repoMethodInvoker = repoRequest.getRepositoryMethodInvoker();
|
||||
if(!repoMethodInvoker.hasDeleteOne()) {
|
||||
if (!repoMethodInvoker.hasDeleteOne()) {
|
||||
throw new NoSuchMethodException();
|
||||
}
|
||||
|
||||
Function<ReferencedProperty, Resource<?>> handler = new Function<ReferencedProperty, Resource<?>>() {
|
||||
@Override public Resource<?> apply(ReferencedProperty prop) {
|
||||
if(null == prop.propertyValue) {
|
||||
@Override
|
||||
public Resource<?> apply(ReferencedProperty prop) {
|
||||
if (null == prop.propertyValue) {
|
||||
return null;
|
||||
}
|
||||
if(prop.property.isCollectionLike()) {
|
||||
if (prop.property.isCollectionLike()) {
|
||||
Collection<Object> coll = new ArrayList<Object>();
|
||||
for(Object obj : (Collection<Object>) prop.propertyValue) {
|
||||
for (Object obj : (Collection<Object>) prop.propertyValue) {
|
||||
BeanWrapper<?, Object> propValWrapper = BeanWrapper.create(obj, null);
|
||||
String s = propValWrapper.getProperty(prop.entity.getIdProperty()).toString();
|
||||
if(!propertyId.equals(s)) {
|
||||
if (!propertyId.equals(s)) {
|
||||
coll.add(obj);
|
||||
}
|
||||
}
|
||||
prop.wrapper.setProperty(prop.property, coll);
|
||||
} else if(prop.property.isMap()) {
|
||||
} else if (prop.property.isMap()) {
|
||||
Map<Object, Object> m = new HashMap<Object, Object>();
|
||||
for(Map.Entry<Object, Object> entry : ((Map<Object, Object>)prop.propertyValue).entrySet()) {
|
||||
for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) prop.propertyValue).entrySet()) {
|
||||
BeanWrapper<?, Object> propValWrapper = BeanWrapper.create(entry.getValue(), null);
|
||||
String s = propValWrapper.getProperty(prop.entity.getIdProperty()).toString();
|
||||
if(!propertyId.equals(s)) {
|
||||
if (!propertyId.equals(s)) {
|
||||
m.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
@@ -437,17 +380,12 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
return null;
|
||||
}
|
||||
};
|
||||
doWithReferencedProperty(repoRequest,
|
||||
id,
|
||||
property,
|
||||
handler);
|
||||
doWithReferencedProperty(repoRequest, id, property, handler);
|
||||
|
||||
return ControllerUtils.toResponseEntity(null, EMPTY_RESOURCE, HttpStatus.NO_CONTENT);
|
||||
}
|
||||
|
||||
private Link propertyReferenceLink(Resource<?> resource,
|
||||
URI baseUri,
|
||||
String rel) {
|
||||
private Link propertyReferenceLink(Resource<?> resource, URI baseUri, String rel) {
|
||||
Link selfLink = resource.getLink("self");
|
||||
String objId = selfLink.getHref().substring(selfLink.getHref().lastIndexOf('/') + 1);
|
||||
return new Link(buildUri(baseUri, objId).toString(), rel);
|
||||
@@ -455,60 +393,51 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
|
||||
private Object loadPropertyValue(Class<?> type, String href) {
|
||||
String id = href.substring(href.lastIndexOf('/') + 1);
|
||||
return converter.convert(id,
|
||||
STRING_TYPE,
|
||||
TypeDescriptor.valueOf(type));
|
||||
return converter.convert(id, STRING_TYPE, TypeDescriptor.valueOf(type));
|
||||
}
|
||||
|
||||
private Resource<?> doWithReferencedProperty(RepositoryRestRequest repoRequest,
|
||||
String id,
|
||||
String propertyPath,
|
||||
Function<ReferencedProperty, Resource<?>> handler)
|
||||
throws ResourceNotFoundException, NoSuchMethodException {
|
||||
private Resource<?> doWithReferencedProperty(RepositoryRestRequest repoRequest, String id, String propertyPath,
|
||||
Function<ReferencedProperty, Resource<?>> handler) throws ResourceNotFoundException, NoSuchMethodException {
|
||||
RepositoryMethodInvoker repoMethodInvoker = repoRequest.getRepositoryMethodInvoker();
|
||||
if(!repoMethodInvoker.hasFindOne()) {
|
||||
if (!repoMethodInvoker.hasFindOne()) {
|
||||
throw new NoSuchMethodException();
|
||||
}
|
||||
|
||||
Object domainObj = converter.convert(id, STRING_TYPE,
|
||||
TypeDescriptor.valueOf(repoRequest.getPersistentEntity().getType()));
|
||||
|
||||
if(null == domainObj) {
|
||||
|
||||
if (null == domainObj) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
String propertyName = repoRequest.getPersistentEntityResourceMapping().getNameForPath(propertyPath);
|
||||
PersistentProperty<?> prop = repoRequest.getPersistentEntity().getPersistentProperty(propertyName);
|
||||
if(null == prop) {
|
||||
if (null == prop) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
BeanWrapper<?, Object> wrapper = BeanWrapper.create(domainObj, null);
|
||||
Object propVal = wrapper.getProperty(prop);
|
||||
|
||||
return handler.apply(new ReferencedProperty(prop,
|
||||
propVal,
|
||||
wrapper));
|
||||
return handler.apply(new ReferencedProperty(prop, propVal, wrapper));
|
||||
}
|
||||
|
||||
private class ReferencedProperty {
|
||||
|
||||
|
||||
final PersistentEntity<?, ?> entity;
|
||||
final PersistentProperty<?> property;
|
||||
final Class<?> propertyType;
|
||||
final Object propertyValue;
|
||||
final BeanWrapper<?, ?>wrapper;
|
||||
final BeanWrapper<?, ?> wrapper;
|
||||
|
||||
private ReferencedProperty(PersistentProperty<?> property, Object propertyValue, BeanWrapper<?, ?> wrapper) {
|
||||
|
||||
private ReferencedProperty(PersistentProperty<?> property,
|
||||
Object propertyValue,
|
||||
BeanWrapper<?, ?> wrapper) {
|
||||
|
||||
this.property = property;
|
||||
this.propertyValue = propertyValue;
|
||||
this.wrapper = wrapper;
|
||||
if(property.isCollectionLike()) {
|
||||
if (property.isCollectionLike()) {
|
||||
this.propertyType = property.getComponentType();
|
||||
} else if(property.isMap()) {
|
||||
} else if (property.isMap()) {
|
||||
this.propertyType = property.getMapValueType();
|
||||
} else {
|
||||
this.propertyType = property.getType();
|
||||
|
||||
@@ -7,12 +7,11 @@ import org.springframework.web.servlet.DispatcherServlet;
|
||||
|
||||
/**
|
||||
* Special {@link DispatcherServlet} subclass that certain exporter components can recognize.
|
||||
*
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class RepositoryRestDispatcherServlet extends DispatcherServlet {
|
||||
|
||||
|
||||
private static final long serialVersionUID = 5761346441984290240L;
|
||||
|
||||
public RepositoryRestDispatcherServlet() {
|
||||
|
||||
@@ -10,32 +10,34 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
|
||||
|
||||
/**
|
||||
* {@link RequestMappingHandlerAdapter} implementation that adds a couple argument resolvers for controller method
|
||||
* parameters used in the REST exporter controller. Also only looks for handler methods in the Spring Data REST
|
||||
* provided controller classes to help isolate this handler adapter from other handler adapters the user might have
|
||||
* configured in their Spring MVC context.
|
||||
*
|
||||
* parameters used in the REST exporter controller. Also only looks for handler methods in the Spring Data REST provided
|
||||
* controller classes to help isolate this handler adapter from other handler adapters the user might have configured in
|
||||
* their Spring MVC context.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class RepositoryRestHandlerAdapter extends ResourceProcessorInvokingHandlerAdapter {
|
||||
|
||||
@Autowired
|
||||
private List<HandlerMethodArgumentResolver> argumentResolvers;
|
||||
@Autowired private List<HandlerMethodArgumentResolver> argumentResolvers;
|
||||
|
||||
@Override public void afterPropertiesSet() {
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
setCustomArgumentResolvers(argumentResolvers);
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Override public int getOrder() {
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.HIGHEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
@Override protected boolean supportsInternal(HandlerMethod handlerMethod) {
|
||||
@Override
|
||||
protected boolean supportsInternal(HandlerMethod handlerMethod) {
|
||||
Class<?> controllerType = handlerMethod.getBeanType();
|
||||
return (RepositoryController.class.isAssignableFrom(controllerType)
|
||||
|| RepositoryEntityController.class.isAssignableFrom(controllerType)
|
||||
|| RepositoryPropertyReferenceController.class.isAssignableFrom(controllerType)
|
||||
|| RepositorySearchController.class.isAssignableFrom(controllerType));
|
||||
|| RepositoryPropertyReferenceController.class.isAssignableFrom(controllerType) || RepositorySearchController.class
|
||||
.isAssignableFrom(controllerType));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,22 +21,19 @@ import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
|
||||
/**
|
||||
* {@link RequestMappingHandlerMapping} implementation that will only find a handler method if a {@link
|
||||
* org.springframework.data.repository.Repository} is exported under that URL path segment. Also ensures the {@link
|
||||
* OpenEntityManagerInViewInterceptor} is registered in the application context. The OEMIVI is required for the REST
|
||||
* exporter to function properly.
|
||||
*
|
||||
* {@link RequestMappingHandlerMapping} implementation that will only find a handler method if a
|
||||
* {@link org.springframework.data.repository.Repository} is exported under that URL path segment. Also ensures the
|
||||
* {@link OpenEntityManagerInViewInterceptor} is registered in the application context. The OEMIVI is required for the
|
||||
* REST exporter to function properly.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class RepositoryRestHandlerMapping extends RequestMappingHandlerMapping {
|
||||
|
||||
@Autowired
|
||||
private Repositories repositories;
|
||||
@Autowired
|
||||
private RepositoryRestConfiguration config;
|
||||
@Autowired(required = false)
|
||||
private JpaHelper jpaHelper;
|
||||
|
||||
@Autowired private Repositories repositories;
|
||||
@Autowired private RepositoryRestConfiguration config;
|
||||
@Autowired(required = false) private JpaHelper jpaHelper;
|
||||
|
||||
private final ResourceMappings mappings;
|
||||
|
||||
public RepositoryRestHandlerMapping(ResourceMappings mappings) {
|
||||
@@ -45,26 +42,25 @@ public class RepositoryRestHandlerMapping extends RequestMappingHandlerMapping {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HandlerMethod lookupHandlerMethod(String lookupPath,
|
||||
HttpServletRequest origRequest) throws Exception {
|
||||
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest origRequest) throws Exception {
|
||||
String acceptType = origRequest.getHeader("Accept");
|
||||
if(null == acceptType) {
|
||||
if (null == acceptType) {
|
||||
acceptType = config.getDefaultMediaType().toString();
|
||||
}
|
||||
List<MediaType> acceptHeaderTypes = MediaType.parseMediaTypes(acceptType);
|
||||
List<MediaType> acceptableTypes = new ArrayList<MediaType>();
|
||||
for(MediaType mt : acceptHeaderTypes) {
|
||||
if(("*".equals(mt.getType()) && ("*".equals(mt.getSubtype()))
|
||||
|| ("application".equals(mt.getType()) && "*".equals(mt.getSubtype())))) {
|
||||
for (MediaType mt : acceptHeaderTypes) {
|
||||
if (("*".equals(mt.getType()) && ("*".equals(mt.getSubtype())) || ("application".equals(mt.getType()) && "*"
|
||||
.equals(mt.getSubtype())))) {
|
||||
mt = config.getDefaultMediaType();
|
||||
}
|
||||
if(!acceptableTypes.contains(mt)) {
|
||||
if (!acceptableTypes.contains(mt)) {
|
||||
acceptableTypes.add(mt);
|
||||
}
|
||||
}
|
||||
if(acceptableTypes.size() > 1) {
|
||||
if (acceptableTypes.size() > 1) {
|
||||
acceptType = collectionToDelimitedString(acceptableTypes, ",");
|
||||
} else if(acceptableTypes.size() == 1) {
|
||||
} else if (acceptableTypes.size() == 1) {
|
||||
acceptType = acceptableTypes.get(0).toString();
|
||||
} else {
|
||||
acceptType = config.getDefaultMediaType().toString();
|
||||
@@ -73,19 +69,19 @@ public class RepositoryRestHandlerMapping extends RequestMappingHandlerMapping {
|
||||
HttpServletRequest request = new DefaultAcceptTypeHttpServletRequest(origRequest, acceptType);
|
||||
|
||||
String requestUri = lookupPath;
|
||||
if(requestUri.startsWith("/")) {
|
||||
if (requestUri.startsWith("/")) {
|
||||
requestUri = requestUri.substring(1);
|
||||
}
|
||||
if(!hasText(requestUri)) {
|
||||
if (!hasText(requestUri)) {
|
||||
return super.lookupHandlerMethod(lookupPath, request);
|
||||
}
|
||||
String[] parts = requestUri.split("/");
|
||||
if(parts.length == 0) {
|
||||
if (parts.length == 0) {
|
||||
// Root request
|
||||
return super.lookupHandlerMethod(lookupPath, request);
|
||||
}
|
||||
|
||||
for(Class<?> domainType : repositories) {
|
||||
|
||||
for (Class<?> domainType : repositories) {
|
||||
if (mappings.exportsMappingFor(domainType)) {
|
||||
return super.lookupHandlerMethod(lookupPath, request);
|
||||
}
|
||||
@@ -94,13 +90,15 @@ public class RepositoryRestHandlerMapping extends RequestMappingHandlerMapping {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override protected boolean isHandler(Class<?> beanType) {
|
||||
@Override
|
||||
protected boolean isHandler(Class<?> beanType) {
|
||||
return AnnotationUtils.findAnnotation(beanType, RestController.class) != null;
|
||||
}
|
||||
|
||||
@Override protected void extendInterceptors(List<Object> interceptors) {
|
||||
if(null != jpaHelper) {
|
||||
for(Object o : jpaHelper.getInterceptors()) {
|
||||
@Override
|
||||
protected void extendInterceptors(List<Object> interceptors) {
|
||||
if (null != jpaHelper) {
|
||||
for (Object o : jpaHelper.getInterceptors()) {
|
||||
interceptors.add(o);
|
||||
}
|
||||
}
|
||||
@@ -109,14 +107,14 @@ public class RepositoryRestHandlerMapping extends RequestMappingHandlerMapping {
|
||||
private static class DefaultAcceptTypeHttpServletRequest extends HttpServletRequestWrapper {
|
||||
private final String defaultAcceptType;
|
||||
|
||||
private DefaultAcceptTypeHttpServletRequest(HttpServletRequest request,
|
||||
String defaultAcceptType) {
|
||||
private DefaultAcceptTypeHttpServletRequest(HttpServletRequest request, String defaultAcceptType) {
|
||||
super(request);
|
||||
this.defaultAcceptType = defaultAcceptType;
|
||||
}
|
||||
|
||||
@Override public String getHeader(String name) {
|
||||
if("accept".equals(name.toLowerCase())) {
|
||||
@Override
|
||||
public String getHeader(String name) {
|
||||
if ("accept".equals(name.toLowerCase())) {
|
||||
return defaultAcceptType;
|
||||
} else {
|
||||
return super.getHeader(name);
|
||||
|
||||
@@ -37,25 +37,21 @@ import org.springframework.hateoas.Link;
|
||||
@SuppressWarnings("deprecation")
|
||||
class RepositoryRestRequest {
|
||||
|
||||
private final HttpServletRequest request;
|
||||
private final URI baseUri;
|
||||
private final ResourceMapping repoMapping;
|
||||
private final Link repoLink;
|
||||
private final Object repository;
|
||||
private final RepositoryMethodInvoker repoMethodInvoker;
|
||||
private final PersistentEntity<?, ?> persistentEntity;
|
||||
private final ResourceMapping entityMapping;
|
||||
private final HttpServletRequest request;
|
||||
private final URI baseUri;
|
||||
private final ResourceMapping repoMapping;
|
||||
private final Link repoLink;
|
||||
private final Object repository;
|
||||
private final RepositoryMethodInvoker repoMethodInvoker;
|
||||
private final PersistentEntity<?, ?> persistentEntity;
|
||||
private final ResourceMapping entityMapping;
|
||||
|
||||
public RepositoryRestRequest(RepositoryRestConfiguration config,
|
||||
Repositories repositories,
|
||||
HttpServletRequest request,
|
||||
URI baseUri,
|
||||
RepositoryInformation repoInfo,
|
||||
ConversionService conversionService) {
|
||||
public RepositoryRestRequest(RepositoryRestConfiguration config, Repositories repositories,
|
||||
HttpServletRequest request, URI baseUri, RepositoryInformation repoInfo, ConversionService conversionService) {
|
||||
this.request = request;
|
||||
this.baseUri = baseUri;
|
||||
this.repoMapping = getResourceMapping(config, repoInfo);
|
||||
if(null == repoMapping || !repoMapping.isExported()) {
|
||||
if (null == repoMapping || !repoMapping.isExported()) {
|
||||
this.repoLink = null;
|
||||
this.repository = null;
|
||||
this.repoMethodInvoker = null;
|
||||
|
||||
@@ -34,37 +34,32 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class RepositoryRestRequestHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
@Autowired
|
||||
private RepositoryRestConfiguration config;
|
||||
@Autowired
|
||||
private Repositories repositories;
|
||||
@Autowired
|
||||
private RepositoryInformationHandlerMethodArgumentResolver repoInfoResolver;
|
||||
@Autowired
|
||||
private BaseUriMethodArgumentResolver baseUriResolver;
|
||||
|
||||
public RepositoryRestRequestHandlerMethodArgumentResolver(ConversionService conversionService) {
|
||||
|
||||
@Autowired private RepositoryRestConfiguration config;
|
||||
@Autowired private Repositories repositories;
|
||||
@Autowired private RepositoryInformationHandlerMethodArgumentResolver repoInfoResolver;
|
||||
@Autowired private BaseUriMethodArgumentResolver baseUriResolver;
|
||||
|
||||
public RepositoryRestRequestHandlerMethodArgumentResolver(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return RepositoryRestRequest.class.isAssignableFrom(parameter.getParameterType());
|
||||
}
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return RepositoryRestRequest.class.isAssignableFrom(parameter.getParameterType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
|
||||
URI baseUri = (URI) baseUriResolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
|
||||
RepositoryInformation repoInfo = repoInfoResolver.resolveArgument(parameter, mavContainer, webRequest,
|
||||
binderFactory);
|
||||
|
||||
return new RepositoryRestRequest(config, repositories, webRequest.getNativeRequest(HttpServletRequest.class), baseUri, repoInfo, conversionService);
|
||||
}
|
||||
return new RepositoryRestRequest(config, repositories, webRequest.getNativeRequest(HttpServletRequest.class),
|
||||
baseUri, repoInfo, conversionService);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,41 +57,32 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
class RepositorySearchController extends AbstractRepositoryRestController {
|
||||
|
||||
private static final String BASE_MAPPING = "/{repository}/search";
|
||||
|
||||
|
||||
private final Repositories repositories;
|
||||
private final RepositoryRestConfiguration config;
|
||||
|
||||
|
||||
@Autowired
|
||||
public RepositorySearchController(Repositories repositories,
|
||||
RepositoryRestConfiguration config,
|
||||
PagedResourcesAssembler<Object> assembler,
|
||||
PersistentEntityResourceAssembler<Object> perAssembler) {
|
||||
|
||||
public RepositorySearchController(Repositories repositories, RepositoryRestConfiguration config,
|
||||
PagedResourcesAssembler<Object> assembler, PersistentEntityResourceAssembler<Object> perAssembler) {
|
||||
|
||||
super(assembler, perAssembler);
|
||||
|
||||
this.repositories = repositories;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
value = BASE_MAPPING,
|
||||
method = RequestMethod.GET,
|
||||
produces = {
|
||||
"application/json",
|
||||
"application/x-spring-data-compact+json"
|
||||
}
|
||||
)
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET, produces = { "application/json",
|
||||
"application/x-spring-data-compact+json" })
|
||||
@ResponseBody
|
||||
public Resource<?> list(RepositoryRestRequest repoRequest) throws ResourceNotFoundException {
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
links.addAll(queryMethodLinks(repoRequest.getBaseUri(),
|
||||
repoRequest.getPersistentEntity().getType()));
|
||||
if(links.isEmpty()) {
|
||||
links.addAll(queryMethodLinks(repoRequest.getBaseUri(), repoRequest.getPersistentEntity().getType()));
|
||||
if (links.isEmpty()) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
return new Resource<Object>(Collections.emptyList(), links);
|
||||
}
|
||||
|
||||
|
||||
protected List<Link> queryMethodLinks(URI baseUri, Class<?> domainType) {
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
RepositoryInformation repoInfo = repositories.getRepositoryInformationFor(domainType);
|
||||
@@ -110,81 +101,63 @@ class RepositorySearchController extends AbstractRepositoryRestController {
|
||||
return links;
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
value = BASE_MAPPING + "/{method}",
|
||||
method = RequestMethod.GET,
|
||||
produces = {
|
||||
"application/json",
|
||||
"application/x-spring-data-verbose+json"
|
||||
}
|
||||
)
|
||||
@RequestMapping(value = BASE_MAPPING + "/{method}", method = RequestMethod.GET, produces = { "application/json",
|
||||
"application/x-spring-data-verbose+json" })
|
||||
@ResponseBody
|
||||
public ResourceSupport query(final RepositoryRestRequest repoRequest,
|
||||
@PathVariable String repository,
|
||||
@PathVariable String method, Pageable pageable)
|
||||
throws ResourceNotFoundException {
|
||||
public ResourceSupport query(final RepositoryRestRequest repoRequest, @PathVariable String repository,
|
||||
@PathVariable String method, Pageable pageable) throws ResourceNotFoundException {
|
||||
RepositoryMethodInvoker repoMethodInvoker = repoRequest.getRepositoryMethodInvoker();
|
||||
if(repoMethodInvoker.getQueryMethods().isEmpty()) {
|
||||
if (repoMethodInvoker.getQueryMethods().isEmpty()) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
ResourceMapping repoMapping = repoRequest.getRepositoryResourceMapping();
|
||||
String methodName = repoMapping.getNameForPath(method);
|
||||
RepositoryMethod repoMethod = repoMethodInvoker.getQueryMethods().get(methodName);
|
||||
if(null == repoMethod) {
|
||||
for(RepositoryMethod queryMethod : repoMethodInvoker.getQueryMethods().values()) {
|
||||
if (null == repoMethod) {
|
||||
for (RepositoryMethod queryMethod : repoMethodInvoker.getQueryMethods().values()) {
|
||||
String path = findPath(queryMethod.getMethod());
|
||||
if(path.equals(method)) {
|
||||
if (path.equals(method)) {
|
||||
repoMethod = queryMethod;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(null == repoMethod) {
|
||||
if (null == repoMethod) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Map<String, String[]> rawParameters = repoRequest.getRequest().getParameterMap();
|
||||
Object result = repoMethodInvoker.invokeQueryMethod(repoMethod, pageable, rawParameters);
|
||||
|
||||
|
||||
Link baseLink = linkTo(methodOn(RepositorySearchController.class). //
|
||||
queryCompact(repoRequest, repository, methodName, pageable)).withSelfRel();
|
||||
|
||||
return resultToResources(result, baseLink);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
value = BASE_MAPPING +"/{method}",
|
||||
method = RequestMethod.GET,
|
||||
produces = {
|
||||
"application/x-spring-data-compact+json"
|
||||
}
|
||||
)
|
||||
@RequestMapping(value = BASE_MAPPING + "/{method}", method = RequestMethod.GET,
|
||||
produces = { "application/x-spring-data-compact+json" })
|
||||
@ResponseBody
|
||||
public ResourceSupport queryCompact(RepositoryRestRequest repoRequest,
|
||||
@PathVariable String repository,
|
||||
@PathVariable String method,
|
||||
Pageable pageable)
|
||||
throws ResourceNotFoundException {
|
||||
public ResourceSupport queryCompact(RepositoryRestRequest repoRequest, @PathVariable String repository,
|
||||
@PathVariable String method, Pageable pageable) throws ResourceNotFoundException {
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
|
||||
ResourceSupport resource = query(repoRequest, repository, method, pageable);
|
||||
links.addAll(resource.getLinks());
|
||||
|
||||
if(resource instanceof Resources && ((Resources<?>) resource).getContent() != null) {
|
||||
for(Object obj : ((Resources<?>) resource).getContent()) {
|
||||
if(null != obj && obj instanceof Resource) {
|
||||
Resource<?> res = (Resource<?>)obj;
|
||||
if (resource instanceof Resources && ((Resources<?>) resource).getContent() != null) {
|
||||
for (Object obj : ((Resources<?>) resource).getContent()) {
|
||||
if (null != obj && obj instanceof Resource) {
|
||||
Resource<?> res = (Resource<?>) obj;
|
||||
links.add(resourceLink(repoRequest, res));
|
||||
}
|
||||
}
|
||||
} else if(resource instanceof Resource) {
|
||||
} else if (resource instanceof Resource) {
|
||||
Resource<?> res = (Resource<?>) resource;
|
||||
links.add(resourceLink(repoRequest, res));
|
||||
}
|
||||
|
||||
|
||||
return new Resource<Object>(EMPTY_RESOURCE_LIST, links);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.springframework.data.rest.webmvc;
|
||||
|
||||
/**
|
||||
* Indicates a resource was not found.
|
||||
*
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class ResourceNotFoundException extends RuntimeException {
|
||||
@@ -10,14 +10,14 @@ public class ResourceNotFoundException extends RuntimeException {
|
||||
private static final long serialVersionUID = 7992904489502842099L;
|
||||
|
||||
public ResourceNotFoundException() {
|
||||
super("Resource not found");
|
||||
}
|
||||
super("Resource not found");
|
||||
}
|
||||
|
||||
public ResourceNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
public ResourceNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ResourceNotFoundException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
public ResourceNotFoundException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,21 +44,21 @@ import static org.springframework.data.util.ClassTypeInformation.from;
|
||||
/**
|
||||
* {@link HandlerMethodReturnValueHandler} to post-process the objects returned from controller methods using the
|
||||
* configured {@link ResourceProcessor}s.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ResourceProcessorHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
|
||||
|
||||
private static final TypeInformation<?> RESOURCE_TYPE = from(Resource.class);
|
||||
private static final TypeInformation<?> RESOURCE_TYPE = from(Resource.class);
|
||||
private static final TypeInformation<?> RESOURCES_TYPE = from(Resources.class);
|
||||
private static final Field CONTENT_FIELD = ReflectionUtils.findField(Resources.class, "content");
|
||||
private static final Field CONTENT_FIELD = ReflectionUtils.findField(Resources.class, "content");
|
||||
|
||||
static {
|
||||
ReflectionUtils.makeAccessible(CONTENT_FIELD);
|
||||
}
|
||||
|
||||
private final HandlerMethodReturnValueHandler delegate;
|
||||
private final List<ProcessorWrapper> processors;
|
||||
private final List<ProcessorWrapper> processors;
|
||||
private boolean rootLinksAsHeaders = false;
|
||||
|
||||
/**
|
||||
@@ -66,13 +66,13 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
* delegate calls to {@link #handleReturnValue(Object, MethodParameter, ModelAndViewContainer, NativeWebRequest)} to.
|
||||
* Will consider the given {@link ResourceProcessor} 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 delegate the {@link HandlerMethodReturnValueHandler} to evenually delegate calls to, must not be
|
||||
* {@literal null}.
|
||||
* @param processors the {@link ResourceProcessor}s to be considered, must not be {@literal null}.
|
||||
*/
|
||||
public ResourceProcessorHandlerMethodReturnValueHandler(HandlerMethodReturnValueHandler delegate,
|
||||
List<ResourceProcessor<?>> processors) {
|
||||
List<ResourceProcessor<?>> processors) {
|
||||
|
||||
Assert.notNull(delegate, "Delegate must not be null!");
|
||||
Assert.notNull(processors, "ResourceProcessors must not be null!");
|
||||
@@ -97,7 +97,7 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
|
||||
Collections.sort(this.processors, AnnotationAwareOrderComparator.INSTANCE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param rootLinksAsHeaders the rootLinksAsHeaders to set
|
||||
*/
|
||||
@@ -120,7 +120,7 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
*/
|
||||
@Override
|
||||
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
|
||||
Object value = returnValue;
|
||||
|
||||
@@ -174,7 +174,7 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
|
||||
/**
|
||||
* Invokes all registered {@link ResourceProcessor}s registered for the given {@link TypeInformation}.
|
||||
*
|
||||
*
|
||||
* @param value the object to process
|
||||
* @param targetType
|
||||
* @return
|
||||
@@ -194,10 +194,11 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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
|
||||
*/
|
||||
@@ -206,7 +207,7 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
if (!(originalValue instanceof HttpEntity)) {
|
||||
return newBody;
|
||||
}
|
||||
|
||||
|
||||
HttpEntity<ResourceSupport> entity = null;
|
||||
|
||||
if (originalValue instanceof ResponseEntity) {
|
||||
@@ -216,18 +217,18 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
HttpEntity<?> source = (HttpEntity<?>) originalValue;
|
||||
entity = new HttpEntity<ResourceSupport>(newBody, source.getHeaders());
|
||||
}
|
||||
|
||||
|
||||
return addLinksToHeaderWrapper(entity);
|
||||
}
|
||||
|
||||
|
||||
private HttpEntity<?> addLinksToHeaderWrapper(HttpEntity<ResourceSupport> entity) {
|
||||
|
||||
|
||||
return rootLinksAsHeaders ? HeaderLinksResponseEntity.wrap(entity) : entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given value is a resource (i.e. implements {@link Resource) or {@link Resources}).
|
||||
*
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
@@ -236,9 +237,9 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface to unify interaction with {@link ResourceProcessor}s. The {@link Ordered} rank should be determined by the
|
||||
* underlying processor.
|
||||
*
|
||||
* Interface to unify interaction with {@link ResourceProcessor}s. The {@link Ordered} rank should be determined by
|
||||
* the underlying processor.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private interface ProcessorWrapper extends Ordered {
|
||||
@@ -246,17 +247,17 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* @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 processor eventually, can be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
boolean supports(TypeInformation<?> typeInformation, Object value);
|
||||
|
||||
/**
|
||||
* Performs the actual invocation of the processor. Implementations can be sure {@link #supports(TypeInformation,
|
||||
* Object)} has been called before and returned {@literal true}.
|
||||
*
|
||||
* Performs the actual invocation of the processor. Implementations can be sure
|
||||
* {@link #supports(TypeInformation, Object)} has been called before and returned {@literal true}.
|
||||
*
|
||||
* @param object
|
||||
*/
|
||||
Object invokeProcessor(Object object);
|
||||
@@ -264,17 +265,17 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ProcessorWrapper} to generically deal with {@link ResourceSupport} types.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class DefaultProcessorWrapper implements ProcessorWrapper {
|
||||
|
||||
private final ResourceProcessor<?> processor;
|
||||
private final TypeInformation<?> targetType;
|
||||
private final TypeInformation<?> targetType;
|
||||
|
||||
/**
|
||||
* Creates a ne {@link DefaultProcessorWrapper} with the given {@link ResourceProcessor}.
|
||||
*
|
||||
*
|
||||
* @param processor must not be {@literal null}.
|
||||
*/
|
||||
public DefaultProcessorWrapper(ResourceProcessor<?> processor) {
|
||||
@@ -315,7 +316,7 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
|
||||
/**
|
||||
* Returns the target type the underlying {@link ResourceProcessor} wants to get invoked for.
|
||||
*
|
||||
*
|
||||
* @return the targetType
|
||||
*/
|
||||
public TypeInformation<?> getTargetType() {
|
||||
@@ -326,14 +327,14 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
/**
|
||||
* {@link ProcessorWrapper} to deal with {@link ResourceProcessor}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 ResourceProcessorWrapper extends DefaultProcessorWrapper {
|
||||
|
||||
/**
|
||||
* Creates a new {@link ResourceProcessorWrapper} for the given {@link ResourceProcessor}.
|
||||
*
|
||||
*
|
||||
* @param processor must not be {@literal null}.
|
||||
*/
|
||||
public ResourceProcessorWrapper(ResourceProcessor<?> processor) {
|
||||
@@ -355,11 +356,11 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link Resource} matches the given target {@link TypeInformation}. We inspect the {@link
|
||||
* Resource}'s value to determine the match.
|
||||
*
|
||||
* Returns whether the given {@link Resource} matches the given target {@link TypeInformation}. We inspect the
|
||||
* {@link Resource}'s value to determine the match.
|
||||
*
|
||||
* @param resource
|
||||
* @param target must not be {@literal null}.
|
||||
* @param target must not be {@literal null}.
|
||||
* @return whether the given {@link Resource} can be assigned to the given target {@link TypeInformation}
|
||||
*/
|
||||
private static boolean isValueTypeMatch(Resource<?> resource, TypeInformation<?> target) {
|
||||
@@ -382,14 +383,14 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
/**
|
||||
* {@link ProcessorWrapper} for {@link ResourceProcessor}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 extends DefaultProcessorWrapper {
|
||||
|
||||
/**
|
||||
* Creates a new {@link ResourcesProcessorWrapper} for the given {@link ResourceProcessor}.
|
||||
*
|
||||
*
|
||||
* @param processor must not be {@literal null}.
|
||||
*/
|
||||
public ResourcesProcessorWrapper(ResourceProcessor<?> processor) {
|
||||
@@ -411,11 +412,11 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link Resources} instance matches the given {@link TypeInformation}. We predict this by
|
||||
* inspecting the first element of the content of the {@link Resources}.
|
||||
*
|
||||
* Returns whether the given {@link Resources} instance matches the given {@link TypeInformation}. We predict this
|
||||
* by inspecting the first element of the content of the {@link Resources}.
|
||||
*
|
||||
* @param resources the {@link Resources} to inspect.
|
||||
* @param target that target {@link TypeInformation}.
|
||||
* @param target that target {@link TypeInformation}.
|
||||
* @return
|
||||
*/
|
||||
private static boolean isValueTypeMatch(Resources<?> resources, TypeInformation<?> target) {
|
||||
@@ -444,7 +445,7 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
/**
|
||||
* Helper extension of {@link AnnotationAwareOrderComparator} to make {@link #getOrder(Object)} public to allow it
|
||||
* being used in a standalone fashion.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class CustomOrderAwareComparator extends AnnotationAwareOrderComparator {
|
||||
|
||||
@@ -30,63 +30,59 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
|
||||
|
||||
/**
|
||||
* Special {@link RequestMappingHandlerAdapter} that tweaks the {@link HandlerMethodReturnValueHandlerComposite} to be
|
||||
* proxied by a {@link ResourceProcessorHandlerMethodReturnValueHandler} which will invoke the {@link
|
||||
* ResourceProcessor}s
|
||||
* found in the application context and eventually delegate to the originally configured
|
||||
* proxied by a {@link ResourceProcessorHandlerMethodReturnValueHandler} which will invoke the {@link ResourceProcessor}
|
||||
* 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
|
||||
* 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 ResourceProcessorInvokingHandlerAdapter extends RequestMappingHandlerAdapter {
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<ResourceProcessor<?>> resourcesProcessors = new ArrayList<ResourceProcessor<?>>();
|
||||
@Autowired(required = false) private List<ResourceProcessor<?>> resourcesProcessors = new ArrayList<ResourceProcessor<?>>();
|
||||
|
||||
/**
|
||||
* Empty constructor to setup a {@link ResourceProcessorInvokingHandlerAdapter}.
|
||||
*/
|
||||
public ResourceProcessorInvokingHandlerAdapter() {
|
||||
/**
|
||||
* Empty constructor to setup a {@link ResourceProcessorInvokingHandlerAdapter}.
|
||||
*/
|
||||
public ResourceProcessorInvokingHandlerAdapter() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ResourceProcessorInvokingHandlerAdapter(RequestMappingHandlerAdapter original) {
|
||||
/**
|
||||
* 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 ResourceProcessorInvokingHandlerAdapter(RequestMappingHandlerAdapter original) {
|
||||
|
||||
Assert.notNull(original);
|
||||
Assert.notNull(original);
|
||||
|
||||
setMessageConverters(original.getMessageConverters());
|
||||
setWebBindingInitializer(original.getWebBindingInitializer());
|
||||
setCustomArgumentResolvers(original.getCustomArgumentResolvers());
|
||||
setCustomReturnValueHandlers(original.getCustomReturnValueHandlers());
|
||||
}
|
||||
setMessageConverters(original.getMessageConverters());
|
||||
setWebBindingInitializer(original.getWebBindingInitializer());
|
||||
setCustomArgumentResolvers(original.getCustomArgumentResolvers());
|
||||
setCustomReturnValueHandlers(original.getCustomReturnValueHandlers());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
super.afterPropertiesSet();
|
||||
super.afterPropertiesSet();
|
||||
|
||||
// Retrieve actual handlers to use as delegate
|
||||
HandlerMethodReturnValueHandlerComposite oldHandlers = getReturnValueHandlers();
|
||||
// Retrieve actual handlers to use as delegate
|
||||
HandlerMethodReturnValueHandlerComposite oldHandlers = getReturnValueHandlers();
|
||||
|
||||
// Set up ResourceProcessingHandlerMethodResolver to delegate to originally configured ones
|
||||
List<HandlerMethodReturnValueHandler> newHandlers = new ArrayList<HandlerMethodReturnValueHandler>();
|
||||
newHandlers.add(new ResourceProcessorHandlerMethodReturnValueHandler(oldHandlers, resourcesProcessors));
|
||||
// Set up ResourceProcessingHandlerMethodResolver to delegate to originally configured ones
|
||||
List<HandlerMethodReturnValueHandler> newHandlers = new ArrayList<HandlerMethodReturnValueHandler>();
|
||||
newHandlers.add(new ResourceProcessorHandlerMethodReturnValueHandler(oldHandlers, resourcesProcessors));
|
||||
|
||||
// Configure the new handler to be used
|
||||
this.setReturnValueHandlers(newHandlers);
|
||||
}
|
||||
// Configure the new handler to be used
|
||||
this.setReturnValueHandlers(newHandlers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,15 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
*/
|
||||
public class ServerHttpRequestMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
@Override public boolean supportsParameter(MethodParameter parameter) {
|
||||
return ClassUtils.isAssignable(parameter.getParameterType(), ServletServerHttpRequest.class);
|
||||
}
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return ClassUtils.isAssignable(parameter.getParameterType(), ServletServerHttpRequest.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
return new ServletServerHttpRequest((HttpServletRequest)webRequest.getNativeRequest());
|
||||
}
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||
return new ServletServerHttpRequest((HttpServletRequest) webRequest.getNativeRequest());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marker annotation to denote which {@link java.net.URI} parameter should be resolved to the request base URI.
|
||||
*
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@Target({ElementType.PARAMETER})
|
||||
@Target({ ElementType.PARAMETER })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface BaseURI {
|
||||
}
|
||||
|
||||
@@ -84,7 +84,8 @@ import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan(basePackageClasses = RestController.class, includeFilters = @Filter(RestController.class), useDefaultFilters = false)
|
||||
@ComponentScan(basePackageClasses = RestController.class, includeFilters = @Filter(RestController.class),
|
||||
useDefaultFilters = false)
|
||||
@ImportResource("classpath*:META-INF/spring-data-rest/**/*.xml")
|
||||
public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebConfiguration {
|
||||
|
||||
@@ -103,7 +104,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
@Bean
|
||||
@Qualifier
|
||||
public DefaultFormattingConversionService defaultConversionService() {
|
||||
|
||||
|
||||
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
|
||||
conversionService.addConverter(UUIDConverter.INSTANCE);
|
||||
conversionService.addConverter(ISO8601DateConverter.INSTANCE);
|
||||
|
||||
@@ -23,51 +23,51 @@ import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
*/
|
||||
public class UriListHttpMessageConverter implements HttpMessageConverter<Resource<?>> {
|
||||
|
||||
private static final List<MediaType> MEDIA_TYPES = new ArrayList<MediaType>();
|
||||
private static final List<MediaType> MEDIA_TYPES = new ArrayList<MediaType>();
|
||||
|
||||
static {
|
||||
MEDIA_TYPES.add(MediaType.parseMediaType("text/uri-list"));
|
||||
}
|
||||
static {
|
||||
MEDIA_TYPES.add(MediaType.parseMediaType("text/uri-list"));
|
||||
}
|
||||
|
||||
@Override public boolean canRead(Class<?> clazz, MediaType mediaType) {
|
||||
if(null == mediaType) {
|
||||
return false;
|
||||
}
|
||||
return Resource.class.isAssignableFrom(clazz) && mediaType.getSubtype().contains("uri-list");
|
||||
}
|
||||
@Override
|
||||
public boolean canRead(Class<?> clazz, MediaType mediaType) {
|
||||
if (null == mediaType) {
|
||||
return false;
|
||||
}
|
||||
return Resource.class.isAssignableFrom(clazz) && mediaType.getSubtype().contains("uri-list");
|
||||
}
|
||||
|
||||
@Override public boolean canWrite(Class<?> clazz, MediaType mediaType) {
|
||||
return canRead(clazz, mediaType);
|
||||
}
|
||||
@Override
|
||||
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
|
||||
return canRead(clazz, mediaType);
|
||||
}
|
||||
|
||||
@Override public List<MediaType> getSupportedMediaTypes() {
|
||||
return MEDIA_TYPES;
|
||||
}
|
||||
@Override
|
||||
public List<MediaType> getSupportedMediaTypes() {
|
||||
return MEDIA_TYPES;
|
||||
}
|
||||
|
||||
@Override public Resource<?> read(Class<? extends Resource<?>> clazz,
|
||||
HttpInputMessage inputMessage)
|
||||
throws IOException,
|
||||
HttpMessageNotReadableException {
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputMessage.getBody()));
|
||||
String line;
|
||||
while(null != (line = reader.readLine())) {
|
||||
links.add(new Link(line));
|
||||
}
|
||||
return new Resource<Object>(Collections.emptyList(), links);
|
||||
}
|
||||
@Override
|
||||
public Resource<?> read(Class<? extends Resource<?>> clazz, HttpInputMessage inputMessage) throws IOException,
|
||||
HttpMessageNotReadableException {
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputMessage.getBody()));
|
||||
String line;
|
||||
while (null != (line = reader.readLine())) {
|
||||
links.add(new Link(line));
|
||||
}
|
||||
return new Resource<Object>(Collections.emptyList(), links);
|
||||
}
|
||||
|
||||
@Override public void write(Resource<?> resource,
|
||||
MediaType contentType,
|
||||
HttpOutputMessage outputMessage)
|
||||
throws IOException,
|
||||
HttpMessageNotWritableException {
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputMessage.getBody()));
|
||||
for(Link link : resource.getLinks()) {
|
||||
writer.write(link.getHref());
|
||||
writer.newLine();
|
||||
}
|
||||
writer.flush();
|
||||
}
|
||||
@Override
|
||||
public void write(Resource<?> resource, MediaType contentType, HttpOutputMessage outputMessage) throws IOException,
|
||||
HttpMessageNotWritableException {
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputMessage.getBody()));
|
||||
for (Link link : resource.getLinks()) {
|
||||
writer.write(link.getHref());
|
||||
writer.newLine();
|
||||
}
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,40 +8,35 @@ import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Helper class to register datatype modules based on their presence in the classpath.
|
||||
*
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class Jackson2DatatypeHelper {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Jackson2DatatypeHelper.class);
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Jackson2DatatypeHelper.class);
|
||||
private static final boolean IS_HIBERNATE4_MODULE_AVAILABLE = ClassUtils.isPresent(
|
||||
"com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module",
|
||||
Jackson2DatatypeHelper.class.getClassLoader()
|
||||
);
|
||||
private static final boolean IS_JODA_MODULE_AVAILABLE = ClassUtils.isPresent(
|
||||
"com.fasterxml.jackson.datatype.joda.JodaModule",
|
||||
Jackson2DatatypeHelper.class.getClassLoader()
|
||||
);
|
||||
"com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module", Jackson2DatatypeHelper.class.getClassLoader());
|
||||
private static final boolean IS_JODA_MODULE_AVAILABLE = ClassUtils.isPresent(
|
||||
"com.fasterxml.jackson.datatype.joda.JodaModule", Jackson2DatatypeHelper.class.getClassLoader());
|
||||
|
||||
public static void configureObjectMapper(ObjectMapper mapper) {
|
||||
// Hibernate types
|
||||
if(IS_HIBERNATE4_MODULE_AVAILABLE) {
|
||||
if (IS_HIBERNATE4_MODULE_AVAILABLE) {
|
||||
try {
|
||||
mapper.registerModule((Module)Class.forName("com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module")
|
||||
.newInstance());
|
||||
} catch(Throwable t) {
|
||||
if(LOG.isDebugEnabled()) {
|
||||
mapper.registerModule((Module) Class.forName("com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module")
|
||||
.newInstance());
|
||||
} catch (Throwable t) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
// JODA time
|
||||
if(IS_JODA_MODULE_AVAILABLE) {
|
||||
if (IS_JODA_MODULE_AVAILABLE) {
|
||||
try {
|
||||
mapper.registerModule((Module)Class.forName("com.fasterxml.jackson.datatype.joda.JodaModule")
|
||||
.newInstance());
|
||||
} catch(Throwable t) {
|
||||
if(LOG.isDebugEnabled()) {
|
||||
mapper.registerModule((Module) Class.forName("com.fasterxml.jackson.datatype.joda.JodaModule").newInstance());
|
||||
} catch (Throwable t) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,84 +13,82 @@ import org.springframework.hateoas.Resource;
|
||||
*/
|
||||
public class JsonSchema extends Resource<Map<String, JsonSchema.Property>> {
|
||||
|
||||
private final String name;
|
||||
@SuppressWarnings("unused")
|
||||
private final String description;
|
||||
private final String name;
|
||||
@SuppressWarnings("unused") private final String description;
|
||||
|
||||
public JsonSchema(String name, String description) {
|
||||
super(new HashMap<String, Property>());
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
public JsonSchema(String name, String description) {
|
||||
super(new HashMap<String, Property>());
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@JsonProperty("properties")
|
||||
@Override public Map<String, JsonSchema.Property> getContent() {
|
||||
return super.getContent();
|
||||
}
|
||||
@JsonProperty("properties")
|
||||
@Override
|
||||
public Map<String, JsonSchema.Property> getContent() {
|
||||
return super.getContent();
|
||||
}
|
||||
|
||||
public JsonSchema addProperty(String name, Property property) {
|
||||
getContent().put(name, property);
|
||||
return this;
|
||||
}
|
||||
public JsonSchema addProperty(String name, Property property) {
|
||||
getContent().put(name, property);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isArrayProperty(String name) {
|
||||
return (getContent().containsKey(name) && getContent().get(name) instanceof ArrayProperty);
|
||||
}
|
||||
public boolean isArrayProperty(String name) {
|
||||
return (getContent().containsKey(name) && getContent().get(name) instanceof ArrayProperty);
|
||||
}
|
||||
|
||||
public ArrayProperty getArrayProperty(String name) {
|
||||
return (ArrayProperty)getContent().get(name);
|
||||
}
|
||||
public ArrayProperty getArrayProperty(String name) {
|
||||
return (ArrayProperty) getContent().get(name);
|
||||
}
|
||||
|
||||
public static class Property {
|
||||
private final String type;
|
||||
private final String description;
|
||||
private final boolean required;
|
||||
public static class Property {
|
||||
private final String type;
|
||||
private final String description;
|
||||
private final boolean required;
|
||||
|
||||
public Property(String type, String description, boolean required) {
|
||||
this.type = type;
|
||||
this.description = description;
|
||||
this.required = required;
|
||||
}
|
||||
public Property(String type, String description, boolean required) {
|
||||
this.type = type;
|
||||
this.description = description;
|
||||
this.required = required;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public boolean isRequired() {
|
||||
return required;
|
||||
}
|
||||
}
|
||||
public boolean isRequired() {
|
||||
return required;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ArrayProperty extends Property {
|
||||
private List<Property> items = new ArrayList<Property>();
|
||||
public static class ArrayProperty extends Property {
|
||||
private List<Property> items = new ArrayList<Property>();
|
||||
|
||||
public ArrayProperty(String type,
|
||||
String description,
|
||||
boolean required) {
|
||||
super(type, description, required);
|
||||
}
|
||||
public ArrayProperty(String type, String description, boolean required) {
|
||||
super(type, description, required);
|
||||
}
|
||||
|
||||
public List<? extends Property> getItems() {
|
||||
return items;
|
||||
}
|
||||
public List<? extends Property> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public ArrayProperty setItems(List<Property> items) {
|
||||
this.items = items;
|
||||
return this;
|
||||
}
|
||||
public ArrayProperty setItems(List<Property> items) {
|
||||
this.items = items;
|
||||
return this;
|
||||
}
|
||||
|
||||
public <P extends Property> ArrayProperty addItem(P item) {
|
||||
this.items.add(item);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
public <P extends Property> ArrayProperty addItem(P item) {
|
||||
this.items.add(item);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,57 +53,53 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
public class PersistentEntityJackson2Module extends SimpleModule implements InitializingBean {
|
||||
|
||||
private static final long serialVersionUID = -7289265674870906323L;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PersistentEntityJackson2Module.class);
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PersistentEntityJackson2Module.class);
|
||||
private static final TypeDescriptor URI_TYPE = TypeDescriptor.valueOf(URI.class);
|
||||
@Autowired
|
||||
private Repositories repositories;
|
||||
@Autowired
|
||||
private RepositoryRestConfiguration config;
|
||||
@Autowired
|
||||
private UriDomainClassConverter uriDomainClassConverter;
|
||||
@Autowired private Repositories repositories;
|
||||
@Autowired private RepositoryRestConfiguration config;
|
||||
@Autowired private UriDomainClassConverter uriDomainClassConverter;
|
||||
private final ResourceMappings mappings;
|
||||
|
||||
public PersistentEntityJackson2Module(ResourceMappings resourceMappings) {
|
||||
|
||||
|
||||
super(new Version(1, 1, 0, "BUILD-SNAPSHOT", "org.springframework.data.rest", "jackson-module"));
|
||||
|
||||
|
||||
this.mappings = resourceMappings;
|
||||
addSerializer(new ResourceSerializer());
|
||||
}
|
||||
|
||||
public static boolean maybeAddAssociationLink(RepositoryLinkBuilder builder,
|
||||
ResourceMappings mappings,
|
||||
PersistentProperty<?> persistentProperty,
|
||||
List<Link> links) {
|
||||
|
||||
public static boolean maybeAddAssociationLink(RepositoryLinkBuilder builder, ResourceMappings mappings,
|
||||
PersistentProperty<?> persistentProperty, List<Link> links) {
|
||||
|
||||
Assert.isTrue(persistentProperty.isAssociation(), "PersistentProperty must be an association!");
|
||||
ResourceMetadata metadata = mappings.getMappingFor(persistentProperty.getOwner().getType());
|
||||
|
||||
|
||||
if (!metadata.isManaged(persistentProperty)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
metadata = mappings.getMappingFor(persistentProperty.getActualType());
|
||||
|
||||
if(metadata.isExported()) {
|
||||
|
||||
|
||||
if (metadata.isExported()) {
|
||||
|
||||
String propertyRel = String.format("%s.%s", metadata.getSingleResourceRel(), persistentProperty.getName());
|
||||
links.add(builder.slash(persistentProperty.getName()).withRel(propertyRel));
|
||||
|
||||
|
||||
// This is an association. We added a Link.
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// This is not an association. No Link was added.
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Override public void afterPropertiesSet() throws Exception {
|
||||
for(Class<?> domainType : repositories) {
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
for (Class<?> domainType : repositories) {
|
||||
PersistentEntity<?, ?> pe = repositories.getPersistentEntity(domainType);
|
||||
if(null == pe) {
|
||||
if(LOG.isWarnEnabled()) {
|
||||
if (null == pe) {
|
||||
if (LOG.isWarnEnabled()) {
|
||||
LOG.warn("The domain class {} does not have PersistentEntity metadata.", domainType.getName());
|
||||
}
|
||||
} else {
|
||||
@@ -122,47 +118,46 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init
|
||||
this.persistentEntity = persistentEntity;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "incomplete-switch", "null", "unused"})
|
||||
@Override public T deserialize(JsonParser jp,
|
||||
DeserializationContext ctxt) throws IOException,
|
||||
JsonProcessingException {
|
||||
@SuppressWarnings({ "unchecked", "incomplete-switch", "null", "unused" })
|
||||
@Override
|
||||
public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
|
||||
Object entity = instantiateClass(getValueClass());
|
||||
BeanWrapper<?, Object> wrapper = BeanWrapper.create(entity, null);
|
||||
|
||||
BeanWrapper<?, Object> wrapper = BeanWrapper.create(entity, null);
|
||||
|
||||
ResourceMetadata metadata = mappings.getMappingFor(getValueClass());
|
||||
|
||||
for(JsonToken tok = jp.nextToken(); tok != JsonToken.END_OBJECT; tok = jp.nextToken()) {
|
||||
for (JsonToken tok = jp.nextToken(); tok != JsonToken.END_OBJECT; tok = jp.nextToken()) {
|
||||
String name = jp.getCurrentName();
|
||||
switch(tok) {
|
||||
switch (tok) {
|
||||
case FIELD_NAME: {
|
||||
if("href".equals(name)) {
|
||||
if ("href".equals(name)) {
|
||||
URI uri = URI.create(jp.nextTextValue());
|
||||
TypeDescriptor entityType = TypeDescriptor.forObject(entity);
|
||||
if(uriDomainClassConverter.matches(URI_TYPE, entityType)) {
|
||||
if (uriDomainClassConverter.matches(URI_TYPE, entityType)) {
|
||||
entity = uriDomainClassConverter.convert(uri, URI_TYPE, entityType);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if("rel".equals(name)) {
|
||||
if ("rel".equals(name)) {
|
||||
// rel is currently ignored
|
||||
continue;
|
||||
}
|
||||
|
||||
PersistentProperty<?> persistentProperty = persistentEntity.getPersistentProperty(name);
|
||||
if(null == persistentProperty) {
|
||||
if (null == persistentProperty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object val = null;
|
||||
|
||||
if("links".equals(name)) {
|
||||
if((tok = jp.nextToken()) == JsonToken.START_ARRAY) {
|
||||
while((tok = jp.nextToken()) != JsonToken.END_ARRAY) {
|
||||
if ("links".equals(name)) {
|
||||
if ((tok = jp.nextToken()) == JsonToken.START_ARRAY) {
|
||||
while ((tok = jp.nextToken()) != JsonToken.END_ARRAY) {
|
||||
// Advance past the links
|
||||
}
|
||||
} else if(tok == JsonToken.VALUE_NULL) {
|
||||
} else if (tok == JsonToken.VALUE_NULL) {
|
||||
// skip null value
|
||||
} else {
|
||||
throw new HttpMessageNotReadableException(
|
||||
@@ -171,44 +166,44 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init
|
||||
continue;
|
||||
}
|
||||
|
||||
if(null == persistentProperty) {
|
||||
if (null == persistentProperty) {
|
||||
// do nothing
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try and read the value of this attribute.
|
||||
// The method of doing that varies based on the type of the property.
|
||||
if(persistentProperty.isCollectionLike()) {
|
||||
if (persistentProperty.isCollectionLike()) {
|
||||
Class<? extends Collection<?>> ctype = (Class<? extends Collection<?>>) persistentProperty.getType();
|
||||
Collection<Object> c = (Collection<Object>) wrapper.getProperty(persistentProperty);
|
||||
if(null == c || c == Collections.EMPTY_LIST || c == Collections.EMPTY_SET) {
|
||||
if(Collection.class.isAssignableFrom(ctype)) {
|
||||
if (null == c || c == Collections.EMPTY_LIST || c == Collections.EMPTY_SET) {
|
||||
if (Collection.class.isAssignableFrom(ctype)) {
|
||||
c = new ArrayList<Object>();
|
||||
} else if(Set.class.isAssignableFrom(ctype)) {
|
||||
} else if (Set.class.isAssignableFrom(ctype)) {
|
||||
c = new HashSet<Object>();
|
||||
}
|
||||
}
|
||||
|
||||
if((tok = jp.nextToken()) == JsonToken.START_ARRAY) {
|
||||
while((tok = jp.nextToken()) != JsonToken.END_ARRAY) {
|
||||
if ((tok = jp.nextToken()) == JsonToken.START_ARRAY) {
|
||||
while ((tok = jp.nextToken()) != JsonToken.END_ARRAY) {
|
||||
Object cval = jp.readValueAs(persistentProperty.getComponentType());
|
||||
c.add(cval);
|
||||
}
|
||||
|
||||
val = c;
|
||||
} else if(tok == JsonToken.VALUE_NULL) {
|
||||
} else if (tok == JsonToken.VALUE_NULL) {
|
||||
val = null;
|
||||
} else {
|
||||
throw new HttpMessageNotReadableException("Cannot read a JSON " + tok + " as a Collection.");
|
||||
}
|
||||
} else if(persistentProperty.isMap()) {
|
||||
Class<? extends Map<?, ?>> mtype = (Class<? extends Map<?, ?>>)persistentProperty.getType();
|
||||
} else if (persistentProperty.isMap()) {
|
||||
Class<? extends Map<?, ?>> mtype = (Class<? extends Map<?, ?>>) persistentProperty.getType();
|
||||
Map<Object, Object> m = (Map<Object, Object>) wrapper.getProperty(persistentProperty);
|
||||
if(null == m || m == Collections.EMPTY_MAP) {
|
||||
if (null == m || m == Collections.EMPTY_MAP) {
|
||||
m = new HashMap<Object, Object>();
|
||||
}
|
||||
|
||||
if((tok = jp.nextToken()) == JsonToken.START_OBJECT) {
|
||||
if ((tok = jp.nextToken()) == JsonToken.START_OBJECT) {
|
||||
do {
|
||||
name = jp.getCurrentName();
|
||||
// TODO resolve domain object from URI
|
||||
@@ -216,16 +211,16 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init
|
||||
Object mval = jp.readValueAs(persistentProperty.getMapValueType());
|
||||
|
||||
m.put(name, mval);
|
||||
} while((tok = jp.nextToken()) != JsonToken.END_OBJECT);
|
||||
} while ((tok = jp.nextToken()) != JsonToken.END_OBJECT);
|
||||
|
||||
val = m;
|
||||
} else if(tok == JsonToken.VALUE_NULL) {
|
||||
} else if (tok == JsonToken.VALUE_NULL) {
|
||||
val = null;
|
||||
} else {
|
||||
throw new HttpMessageNotReadableException("Cannot read a JSON " + tok + " as a Map.");
|
||||
}
|
||||
} else {
|
||||
if((tok = jp.nextToken()) != JsonToken.VALUE_NULL) {
|
||||
if ((tok = jp.nextToken()) != JsonToken.VALUE_NULL) {
|
||||
val = jp.readValueAs(persistentProperty.getType());
|
||||
}
|
||||
}
|
||||
@@ -237,7 +232,7 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init
|
||||
}
|
||||
}
|
||||
|
||||
return (T)entity;
|
||||
return (T) entity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,12 +243,11 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init
|
||||
super(PersistentEntityResource.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@Override public void serialize(final PersistentEntityResource resource,
|
||||
final JsonGenerator jgen,
|
||||
final SerializerProvider provider) throws IOException,
|
||||
JsonGenerationException {
|
||||
if(LOG.isDebugEnabled()) {
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
@Override
|
||||
public void serialize(final PersistentEntityResource resource, final JsonGenerator jgen,
|
||||
final SerializerProvider provider) throws IOException, JsonGenerationException {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Serializing PersistentEntity " + resource.getPersistentEntity());
|
||||
}
|
||||
|
||||
@@ -274,14 +268,16 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init
|
||||
jgen.writeStartObject();
|
||||
try {
|
||||
entity.doWithProperties(new PropertyHandler() {
|
||||
@Override public void doWithPersistentProperty(PersistentProperty property) {
|
||||
|
||||
boolean idAvailableAndShallNotBeExposed = property.isIdProperty() && !config.isIdExposedFor(entity.getType());
|
||||
|
||||
if(idAvailableAndShallNotBeExposed) {
|
||||
@Override
|
||||
public void doWithPersistentProperty(PersistentProperty property) {
|
||||
|
||||
boolean idAvailableAndShallNotBeExposed = property.isIdProperty()
|
||||
&& !config.isIdExposedFor(entity.getType());
|
||||
|
||||
if (idAvailableAndShallNotBeExposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (property.isEntity() && maybeAddAssociationLink(builder, mappings, property, links)) {
|
||||
return;
|
||||
}
|
||||
@@ -290,7 +286,7 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init
|
||||
Object propertyValue = wrapper.getProperty(property);
|
||||
try {
|
||||
jgen.writeObjectField(property.getName(), propertyValue);
|
||||
} catch(IOException e) {
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
@@ -298,14 +294,15 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init
|
||||
|
||||
// Add associations as links
|
||||
entity.doWithAssociations(new AssociationHandler() {
|
||||
@Override public void doWithAssociation(Association association) {
|
||||
|
||||
@Override
|
||||
public void doWithAssociation(Association association) {
|
||||
|
||||
PersistentProperty property = association.getInverse();
|
||||
|
||||
if(!mappings.isMapped(property)) {
|
||||
|
||||
if (!mappings.isMapped(property)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (maybeAddAssociationLink(builder, mappings, property, links)) {
|
||||
return;
|
||||
}
|
||||
@@ -313,20 +310,20 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init
|
||||
Object propertyValue = wrapper.getProperty(property);
|
||||
try {
|
||||
jgen.writeObjectField(property.getName(), propertyValue);
|
||||
} catch(IOException e) {
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
jgen.writeArrayFieldStart("links");
|
||||
for(Link l : links) {
|
||||
for (Link l : links) {
|
||||
jgen.writeObject(l);
|
||||
}
|
||||
jgen.writeEndArray();
|
||||
|
||||
} catch(IllegalStateException e) {
|
||||
throw (IOException)e.getCause();
|
||||
} catch (IllegalStateException e) {
|
||||
throw (IOException) e.getCause();
|
||||
} finally {
|
||||
jgen.writeEndObject();
|
||||
}
|
||||
|
||||
@@ -29,89 +29,90 @@ import org.springframework.hateoas.Link;
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class PersistentEntityToJsonSchemaConverter
|
||||
extends RepositoryInformationSupport
|
||||
implements ConditionalGenericConverter,
|
||||
InitializingBean {
|
||||
public class PersistentEntityToJsonSchemaConverter extends RepositoryInformationSupport implements
|
||||
ConditionalGenericConverter, InitializingBean {
|
||||
|
||||
private static final TypeDescriptor STRING_TYPE = TypeDescriptor.valueOf(String.class);
|
||||
private static final TypeDescriptor SCHEMA_TYPE = TypeDescriptor.valueOf(JsonSchema.class);
|
||||
private Set<ConvertiblePair> convertiblePairs = new HashSet<ConvertiblePair>();
|
||||
private ResourceMappings mappings;
|
||||
private static final TypeDescriptor STRING_TYPE = TypeDescriptor.valueOf(String.class);
|
||||
private static final TypeDescriptor SCHEMA_TYPE = TypeDescriptor.valueOf(JsonSchema.class);
|
||||
private Set<ConvertiblePair> convertiblePairs = new HashSet<ConvertiblePair>();
|
||||
private ResourceMappings mappings;
|
||||
|
||||
@Override public void afterPropertiesSet() throws Exception {
|
||||
|
||||
for(Class<?> domainType : repositories) {
|
||||
convertiblePairs.add(new ConvertiblePair(domainType, JsonSchema.class));
|
||||
}
|
||||
|
||||
this.mappings = new ResourceMappings(config, repositories);
|
||||
}
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
@Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return (Class.class.isAssignableFrom(sourceType.getType()) && JsonSchema.class.isAssignableFrom(targetType.getType()));
|
||||
}
|
||||
for (Class<?> domainType : repositories) {
|
||||
convertiblePairs.add(new ConvertiblePair(domainType, JsonSchema.class));
|
||||
}
|
||||
|
||||
this.mappings = new ResourceMappings(config, repositories);
|
||||
}
|
||||
|
||||
@Override public Set<ConvertiblePair> getConvertibleTypes() {
|
||||
return convertiblePairs;
|
||||
}
|
||||
@Override
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return (Class.class.isAssignableFrom(sourceType.getType()) && JsonSchema.class.isAssignableFrom(targetType
|
||||
.getType()));
|
||||
}
|
||||
|
||||
public JsonSchema convert(Class<?> domainType) {
|
||||
return (JsonSchema)convert(domainType, STRING_TYPE, SCHEMA_TYPE);
|
||||
}
|
||||
@Override
|
||||
public Set<ConvertiblePair> getConvertibleTypes() {
|
||||
return convertiblePairs;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
|
||||
PersistentEntity<?, ?> persistentEntity = repositories.getPersistentEntity((Class<?>)source);
|
||||
final ResourceMetadata metadata = mappings.getMappingFor(persistentEntity.getClass());
|
||||
String entityDesc = persistentEntity.getType().isAnnotationPresent(Description.class)
|
||||
? ((Description)persistentEntity.getType().getAnnotation(Description.class)).value()
|
||||
: null;
|
||||
|
||||
final JsonSchema jsonSchema = new JsonSchema(persistentEntity.getName(), entityDesc);
|
||||
persistentEntity.doWithProperties(new PropertyHandler() {
|
||||
@Override public void doWithPersistentProperty(PersistentProperty persistentProperty) {
|
||||
Class<?> propertyType = persistentProperty.getType();
|
||||
String type = uncapitalize(propertyType.getSimpleName());
|
||||
boolean notNull = (persistentProperty.getField().isAnnotationPresent(Nonnull.class)
|
||||
|| persistentProperty.getGetter().isAnnotationPresent(Nonnull.class))
|
||||
|| (persistentProperty.getField().isAnnotationPresent(NotNull.class)
|
||||
|| persistentProperty.getGetter().isAnnotationPresent(NotNull.class));
|
||||
String desc = persistentProperty.getField().isAnnotationPresent(Description.class)
|
||||
? persistentProperty.getField().getAnnotation(Description.class).value()
|
||||
: persistentProperty.getGetter().isAnnotationPresent(Description.class)
|
||||
? persistentProperty.getGetter().getAnnotation(Description.class).value()
|
||||
: null;
|
||||
public JsonSchema convert(Class<?> domainType) {
|
||||
return (JsonSchema) convert(domainType, STRING_TYPE, SCHEMA_TYPE);
|
||||
}
|
||||
|
||||
JsonSchema.Property property;
|
||||
if(persistentProperty.isCollectionLike()) {
|
||||
property = new JsonSchema.ArrayProperty("array", desc, notNull);
|
||||
} else {
|
||||
property = new JsonSchema.Property(type, desc, notNull);
|
||||
}
|
||||
jsonSchema.addProperty(persistentProperty.getName(), property);
|
||||
}
|
||||
});
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
|
||||
final List<Link> links = new ArrayList<Link>();
|
||||
|
||||
persistentEntity.doWithAssociations(new AssociationHandler() {
|
||||
@Override public void doWithAssociation(Association association) {
|
||||
PersistentProperty persistentProperty = association.getInverse();
|
||||
if(!metadata.isMapped(persistentProperty)) {
|
||||
return;
|
||||
}
|
||||
|
||||
RepositoryLinkBuilder builder = new RepositoryLinkBuilder(metadata, config.getBaseUri()).slash("{id}");
|
||||
PersistentEntity<?, ?> persistentEntity = repositories.getPersistentEntity((Class<?>) source);
|
||||
final ResourceMetadata metadata = mappings.getMappingFor(persistentEntity.getClass());
|
||||
String entityDesc = persistentEntity.getType().isAnnotationPresent(Description.class) ? ((Description) persistentEntity
|
||||
.getType().getAnnotation(Description.class)).value() : null;
|
||||
|
||||
final JsonSchema jsonSchema = new JsonSchema(persistentEntity.getName(), entityDesc);
|
||||
persistentEntity.doWithProperties(new PropertyHandler() {
|
||||
@Override
|
||||
public void doWithPersistentProperty(PersistentProperty persistentProperty) {
|
||||
Class<?> propertyType = persistentProperty.getType();
|
||||
String type = uncapitalize(propertyType.getSimpleName());
|
||||
boolean notNull = (persistentProperty.getField().isAnnotationPresent(Nonnull.class) || persistentProperty
|
||||
.getGetter().isAnnotationPresent(Nonnull.class))
|
||||
|| (persistentProperty.getField().isAnnotationPresent(NotNull.class) || persistentProperty.getGetter()
|
||||
.isAnnotationPresent(NotNull.class));
|
||||
String desc = persistentProperty.getField().isAnnotationPresent(Description.class) ? persistentProperty
|
||||
.getField().getAnnotation(Description.class).value() : persistentProperty.getGetter().isAnnotationPresent(
|
||||
Description.class) ? persistentProperty.getGetter().getAnnotation(Description.class).value() : null;
|
||||
|
||||
JsonSchema.Property property;
|
||||
if (persistentProperty.isCollectionLike()) {
|
||||
property = new JsonSchema.ArrayProperty("array", desc, notNull);
|
||||
} else {
|
||||
property = new JsonSchema.Property(type, desc, notNull);
|
||||
}
|
||||
jsonSchema.addProperty(persistentProperty.getName(), property);
|
||||
}
|
||||
});
|
||||
|
||||
final List<Link> links = new ArrayList<Link>();
|
||||
|
||||
persistentEntity.doWithAssociations(new AssociationHandler() {
|
||||
@Override
|
||||
public void doWithAssociation(Association association) {
|
||||
PersistentProperty persistentProperty = association.getInverse();
|
||||
if (!metadata.isMapped(persistentProperty)) {
|
||||
return;
|
||||
}
|
||||
|
||||
RepositoryLinkBuilder builder = new RepositoryLinkBuilder(metadata, config.getBaseUri()).slash("{id}");
|
||||
maybeAddAssociationLink(builder, mappings, persistentProperty, links);
|
||||
}
|
||||
});
|
||||
|
||||
jsonSchema.add(links);
|
||||
}
|
||||
});
|
||||
|
||||
return jsonSchema;
|
||||
}
|
||||
jsonSchema.add(links);
|
||||
|
||||
return jsonSchema;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,20 +10,22 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
*/
|
||||
public class BaseUriLinkBuilder extends LinkBuilderSupport<BaseUriLinkBuilder> {
|
||||
|
||||
public BaseUriLinkBuilder(UriComponentsBuilder builder) {
|
||||
super(builder);
|
||||
}
|
||||
public BaseUriLinkBuilder(UriComponentsBuilder builder) {
|
||||
super(builder);
|
||||
}
|
||||
|
||||
public static BaseUriLinkBuilder create(URI baseUri) {
|
||||
return new BaseUriLinkBuilder(UriComponentsBuilder.fromUri(baseUri));
|
||||
}
|
||||
public static BaseUriLinkBuilder create(URI baseUri) {
|
||||
return new BaseUriLinkBuilder(UriComponentsBuilder.fromUri(baseUri));
|
||||
}
|
||||
|
||||
@Override protected BaseUriLinkBuilder getThis() {
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
protected BaseUriLinkBuilder getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override protected BaseUriLinkBuilder createNewInstance(UriComponentsBuilder builder) {
|
||||
return new BaseUriLinkBuilder(builder);
|
||||
}
|
||||
@Override
|
||||
protected BaseUriLinkBuilder createNewInstance(UriComponentsBuilder builder) {
|
||||
return new BaseUriLinkBuilder(builder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,26 +14,24 @@ import org.springframework.context.MessageSource;
|
||||
*/
|
||||
public class ConstraintViolationExceptionMessage {
|
||||
|
||||
private final ConstraintViolationException cve;
|
||||
private final List<ConstraintViolationMessage> messages = new ArrayList<ConstraintViolationMessage>();
|
||||
private final ConstraintViolationException cve;
|
||||
private final List<ConstraintViolationMessage> messages = new ArrayList<ConstraintViolationMessage>();
|
||||
|
||||
public ConstraintViolationExceptionMessage(ConstraintViolationException cve,
|
||||
MessageSource msgSrc,
|
||||
Locale locale) {
|
||||
this.cve = cve;
|
||||
for(ConstraintViolation<?> cv : cve.getConstraintViolations()) {
|
||||
messages.add(new ConstraintViolationMessage(cv, msgSrc, locale));
|
||||
}
|
||||
}
|
||||
public ConstraintViolationExceptionMessage(ConstraintViolationException cve, MessageSource msgSrc, Locale locale) {
|
||||
this.cve = cve;
|
||||
for (ConstraintViolation<?> cv : cve.getConstraintViolations()) {
|
||||
messages.add(new ConstraintViolationMessage(cv, msgSrc, locale));
|
||||
}
|
||||
}
|
||||
|
||||
@JsonProperty("cause")
|
||||
public String getCause() {
|
||||
return cve.getMessage();
|
||||
}
|
||||
@JsonProperty("cause")
|
||||
public String getCause() {
|
||||
return cve.getMessage();
|
||||
}
|
||||
|
||||
@JsonProperty("messages")
|
||||
public List<ConstraintViolationMessage> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
@JsonProperty("messages")
|
||||
public List<ConstraintViolationMessage> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,26 +10,19 @@ import org.springframework.context.MessageSource;
|
||||
|
||||
/**
|
||||
* A helper class to encapsulate {@link ConstraintViolation} errors.
|
||||
*
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class ConstraintViolationMessage {
|
||||
|
||||
private final ConstraintViolation<?> violation;
|
||||
private final String message;
|
||||
private final String message;
|
||||
|
||||
public ConstraintViolationMessage(ConstraintViolation<?> violation,
|
||||
MessageSource msgSrc,
|
||||
Locale locale) {
|
||||
public ConstraintViolationMessage(ConstraintViolation<?> violation, MessageSource msgSrc, Locale locale) {
|
||||
this.violation = violation;
|
||||
this.message = msgSrc.getMessage(violation.getMessageTemplate(),
|
||||
new Object[]{
|
||||
violation.getLeafBean().getClass().getSimpleName(),
|
||||
violation.getPropertyPath().toString(),
|
||||
violation.getInvalidValue()
|
||||
},
|
||||
violation.getMessage(),
|
||||
locale);
|
||||
new Object[] { violation.getLeafBean().getClass().getSimpleName(), violation.getPropertyPath().toString(),
|
||||
violation.getInvalidValue() }, violation.getMessage(), locale);
|
||||
}
|
||||
|
||||
@JsonProperty("entity")
|
||||
|
||||
@@ -4,29 +4,28 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* A helper that renders an {@link Exception} JSON-friendly.
|
||||
*
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class ExceptionMessage {
|
||||
|
||||
private final Throwable exception;
|
||||
private final Throwable exception;
|
||||
|
||||
public ExceptionMessage(Throwable exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
public ExceptionMessage(Throwable exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
@JsonProperty("message")
|
||||
public String getMessage() {
|
||||
return exception.getMessage();
|
||||
}
|
||||
@JsonProperty("message")
|
||||
public String getMessage() {
|
||||
return exception.getMessage();
|
||||
}
|
||||
|
||||
@JsonProperty("cause")
|
||||
public ExceptionMessage getCause() {
|
||||
if(null != exception.getCause()) {
|
||||
return new ExceptionMessage(exception.getCause());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@JsonProperty("cause")
|
||||
public ExceptionMessage getCause() {
|
||||
if (null != exception.getCause()) {
|
||||
return new ExceptionMessage(exception.getCause());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -7,37 +7,33 @@ import org.springframework.data.rest.webmvc.RepositoryRestDispatcherServlet;
|
||||
|
||||
/**
|
||||
* Helper class to HttpServletRequest helpers.
|
||||
*
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public abstract class HttpRequestUtils {
|
||||
|
||||
/**
|
||||
* Strip a servlet registration mapping from the request URI.
|
||||
*
|
||||
* @param requestUri
|
||||
* The request URI to strip.
|
||||
* @param ctx
|
||||
* The servlet context in which to search for registration mappings.
|
||||
*
|
||||
* @return The stripped request URI.
|
||||
*/
|
||||
public static String stripRegistrationMapping(String requestUri,
|
||||
ServletContext ctx) {
|
||||
for(ServletRegistration reg : ctx.getServletRegistrations().values()) {
|
||||
if(reg.getClassName().equals(RepositoryRestDispatcherServlet.class.getName())
|
||||
|| reg.getName().equals("rest-exporter")) {
|
||||
for(String mapping : reg.getMappings()) {
|
||||
if(mapping.contains("*")) {
|
||||
mapping = mapping.substring(0, mapping.indexOf('*'));
|
||||
}
|
||||
if(requestUri.startsWith(mapping)) {
|
||||
return requestUri.replaceAll(mapping, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return requestUri;
|
||||
}
|
||||
/**
|
||||
* Strip a servlet registration mapping from the request URI.
|
||||
*
|
||||
* @param requestUri The request URI to strip.
|
||||
* @param ctx The servlet context in which to search for registration mappings.
|
||||
* @return The stripped request URI.
|
||||
*/
|
||||
public static String stripRegistrationMapping(String requestUri, ServletContext ctx) {
|
||||
for (ServletRegistration reg : ctx.getServletRegistrations().values()) {
|
||||
if (reg.getClassName().equals(RepositoryRestDispatcherServlet.class.getName())
|
||||
|| reg.getName().equals("rest-exporter")) {
|
||||
for (String mapping : reg.getMappings()) {
|
||||
if (mapping.contains("*")) {
|
||||
mapping = mapping.substring(0, mapping.indexOf('*'));
|
||||
}
|
||||
if (requestUri.startsWith(mapping)) {
|
||||
return requestUri.replaceAll(mapping, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return requestUri;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,13 +19,12 @@ public class JpaHelper implements BeanFactoryAware {
|
||||
|
||||
private List<WebRequestInterceptor> interceptor = new ArrayList<WebRequestInterceptor>();
|
||||
|
||||
@Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
|
||||
(ListableBeanFactory)beanFactory,
|
||||
EntityManagerFactory.class
|
||||
);
|
||||
for(String s : beanNames) {
|
||||
EntityManagerFactory emf = (EntityManagerFactory)beanFactory.getBean(s);
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors((ListableBeanFactory) beanFactory,
|
||||
EntityManagerFactory.class);
|
||||
for (String s : beanNames) {
|
||||
EntityManagerFactory emf = (EntityManagerFactory) beanFactory.getBean(s);
|
||||
OpenEntityManagerInViewInterceptor omivi = new OpenEntityManagerInViewInterceptor();
|
||||
omivi.setEntityManagerFactory(emf);
|
||||
interceptor.add(omivi);
|
||||
|
||||
@@ -17,28 +17,22 @@ public class RepositoryConstraintViolationExceptionMessage {
|
||||
private final List<ValidationError> errors = new ArrayList<ValidationError>();
|
||||
|
||||
public RepositoryConstraintViolationExceptionMessage(RepositoryConstraintViolationException violationException,
|
||||
MessageSource msgSrc,
|
||||
Locale locale) {
|
||||
MessageSource msgSrc, Locale locale) {
|
||||
|
||||
for(FieldError fe : violationException.getErrors().getFieldErrors()) {
|
||||
for (FieldError fe : violationException.getErrors().getFieldErrors()) {
|
||||
List<Object> args = new ArrayList<Object>();
|
||||
args.add(fe.getObjectName());
|
||||
args.add(fe.getField());
|
||||
args.add(fe.getRejectedValue());
|
||||
if(null != fe.getArguments()) {
|
||||
for(Object o : fe.getArguments()) {
|
||||
if (null != fe.getArguments()) {
|
||||
for (Object o : fe.getArguments()) {
|
||||
args.add(o);
|
||||
}
|
||||
}
|
||||
|
||||
String msg = msgSrc.getMessage(fe.getCode(),
|
||||
args.toArray(),
|
||||
fe.getDefaultMessage(),
|
||||
locale);
|
||||
this.errors.add(new ValidationError(fe.getObjectName(),
|
||||
msg,
|
||||
String.format("%s", fe.getRejectedValue()),
|
||||
fe.getField()));
|
||||
String msg = msgSrc.getMessage(fe.getCode(), args.toArray(), fe.getDefaultMessage(), locale);
|
||||
this.errors.add(new ValidationError(fe.getObjectName(), msg, String.format("%s", fe.getRejectedValue()), fe
|
||||
.getField()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ public class RepositoryEntityLinks extends AbstractEntityLinks {
|
||||
|
||||
@Autowired
|
||||
public RepositoryEntityLinks(Repositories repositories, ResourceMappings mappings, RepositoryRestConfiguration config) {
|
||||
|
||||
|
||||
Assert.notNull(repositories, "Repositories must not be null!");
|
||||
|
||||
|
||||
this.repositories = repositories;
|
||||
this.mappings = mappings;
|
||||
this.config = config;
|
||||
@@ -45,7 +45,7 @@ public class RepositoryEntityLinks extends AbstractEntityLinks {
|
||||
*/
|
||||
@Override
|
||||
public LinkBuilder linkFor(Class<?> type) {
|
||||
|
||||
|
||||
ResourceMetadata metadata = mappings.getMappingFor(type);
|
||||
return new RepositoryLinkBuilder(metadata, config.getBaseUri());
|
||||
}
|
||||
@@ -65,7 +65,7 @@ public class RepositoryEntityLinks extends AbstractEntityLinks {
|
||||
*/
|
||||
@Override
|
||||
public Link linkToCollectionResource(Class<?> type) {
|
||||
|
||||
|
||||
ResourceMetadata metadata = mappings.getMappingFor(type);
|
||||
return linkFor(type).withRel(metadata.getRel());
|
||||
}
|
||||
@@ -76,7 +76,7 @@ public class RepositoryEntityLinks extends AbstractEntityLinks {
|
||||
*/
|
||||
@Override
|
||||
public Link linkToSingleResource(Class<?> type, Object id) {
|
||||
|
||||
|
||||
ResourceMetadata metadata = mappings.getMappingFor(type);
|
||||
return linkFor(type).slash(id).withRel(metadata.getSingleResourceRel());
|
||||
}
|
||||
|
||||
@@ -40,8 +40,9 @@ public class RepositoryLinkBuilder extends LinkBuilderSupport<RepositoryLinkBuil
|
||||
}
|
||||
|
||||
private static UriComponentsBuilder prepareBuilder(URI baseUri, ResourceMetadata metadata) {
|
||||
|
||||
UriComponentsBuilder builder = baseUri != null ? UriComponentsBuilder.fromUri(baseUri) : ControllerLinkBuilder.linkTo(RepositoryController.class).toUriComponentsBuilder();
|
||||
|
||||
UriComponentsBuilder builder = baseUri != null ? UriComponentsBuilder.fromUri(baseUri) : ControllerLinkBuilder
|
||||
.linkTo(RepositoryController.class).toUriComponentsBuilder();
|
||||
return builder.path(metadata.getPath());
|
||||
}
|
||||
|
||||
@@ -54,18 +55,18 @@ public class RepositoryLinkBuilder extends LinkBuilderSupport<RepositoryLinkBuil
|
||||
|
||||
return super.slash(object);
|
||||
}
|
||||
|
||||
|
||||
public RepositoryLinkBuilder slash(PersistentProperty<?> property) {
|
||||
|
||||
|
||||
String propName = property.getName();
|
||||
|
||||
|
||||
if (metadata.isManaged(property)) {
|
||||
return slash(metadata.getMappingFor(property).getPath());
|
||||
} else {
|
||||
return slash(propName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Link withResourceRel() {
|
||||
return withRel(metadata.getRel());
|
||||
}
|
||||
@@ -78,7 +79,7 @@ public class RepositoryLinkBuilder extends LinkBuilderSupport<RepositoryLinkBuil
|
||||
protected RepositoryLinkBuilder createNewInstance(UriComponentsBuilder builder) {
|
||||
return new RepositoryLinkBuilder(this.metadata, builder);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.core.LinkBuilderSupport#getThis()
|
||||
@@ -87,4 +88,4 @@ public class RepositoryLinkBuilder extends LinkBuilderSupport<RepositoryLinkBuil
|
||||
protected RepositoryLinkBuilder getThis() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,15 +13,10 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class ValidationExceptionHandler {
|
||||
|
||||
public ResponseEntity<?> handleValidationException(RuntimeException ex,
|
||||
MessageSource msgsrc,
|
||||
Locale locale) {
|
||||
public ResponseEntity<?> handleValidationException(RuntimeException ex, MessageSource msgsrc, Locale locale) {
|
||||
Assert.isAssignable(ConstraintViolationException.class, ex.getClass());
|
||||
return new ResponseEntity<ConstraintViolationExceptionMessage>(
|
||||
new ConstraintViolationExceptionMessage((ConstraintViolationException)ex,
|
||||
msgsrc,
|
||||
locale),
|
||||
HttpStatus.BAD_REQUEST
|
||||
return new ResponseEntity<ConstraintViolationExceptionMessage>(new ConstraintViolationExceptionMessage(
|
||||
(ConstraintViolationException) ex, msgsrc, locale), HttpStatus.BAD_REQUEST
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,39 +74,33 @@ public abstract class AbstractWebIntegrationTests {
|
||||
protected MockHttpServletResponse request(String href) throws Exception {
|
||||
return request(href, MediaType.APPLICATION_JSON);
|
||||
}
|
||||
|
||||
|
||||
protected ResultActions follow(Link link) throws Exception {
|
||||
return mvc.perform(get(link.getHref()));
|
||||
}
|
||||
|
||||
return mvc.perform(get(link.getHref()));
|
||||
}
|
||||
|
||||
protected List<Link> discover(String rel) throws Exception {
|
||||
return discover(new Link("/"), rel);
|
||||
}
|
||||
|
||||
return discover(new Link("/"), rel);
|
||||
}
|
||||
|
||||
protected Link discoverUnique(String rel) throws Exception {
|
||||
|
||||
|
||||
List<Link> discover = discover(rel);
|
||||
assertThat(discover, hasSize(1));
|
||||
return discover.get(0);
|
||||
}
|
||||
|
||||
protected List<Link> discover(Link root, String rel) throws Exception {
|
||||
String s = mvc
|
||||
.perform(get(root.getHref()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(hasLinkWithRel(rel))
|
||||
.andReturn().getResponse().getContentAsString();
|
||||
return links.findLinksWithRel(rel, s);
|
||||
}
|
||||
|
||||
protected Link discoverUnique(Link root, String rel) throws Exception {
|
||||
String s = mvc
|
||||
.perform(get(root.getHref()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(hasLinkWithRel(rel))
|
||||
.andReturn().getResponse().getContentAsString();
|
||||
return links.findLinkWithRel(rel, s);
|
||||
}
|
||||
protected List<Link> discover(Link root, String rel) throws Exception {
|
||||
String s = mvc.perform(get(root.getHref())).andExpect(status().isOk()).andExpect(hasLinkWithRel(rel)).andReturn()
|
||||
.getResponse().getContentAsString();
|
||||
return links.findLinksWithRel(rel, s);
|
||||
}
|
||||
|
||||
protected Link discoverUnique(Link root, String rel) throws Exception {
|
||||
String s = mvc.perform(get(root.getHref())).andExpect(status().isOk()).andExpect(hasLinkWithRel(rel)).andReturn()
|
||||
.getResponse().getContentAsString();
|
||||
return links.findLinkWithRel(rel, s);
|
||||
}
|
||||
|
||||
protected Link assertHasLinkWithRel(String rel, MockHttpServletResponse response) throws Exception {
|
||||
|
||||
@@ -140,13 +134,13 @@ public abstract class AbstractWebIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void exposesRootResource() throws Exception {
|
||||
|
||||
|
||||
ResultActions actions = mvc.perform(get("/")).andExpect(status().isOk());
|
||||
|
||||
|
||||
for (String rel : expectedRootLinkRels()) {
|
||||
actions.andExpect(hasLinkWithRel(rel));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected abstract Iterable<String> expectedRootLinkRels();
|
||||
}
|
||||
|
||||
@@ -47,83 +47,55 @@ public class CustomMethodArgumentResolverTests {
|
||||
|
||||
static {
|
||||
try {
|
||||
BASE_URI = MethodParameter.forMethodOrConstructor(
|
||||
Methods.class.getDeclaredMethod("baseUri", URI.class),
|
||||
0
|
||||
);
|
||||
BASE_URI = MethodParameter.forMethodOrConstructor(Methods.class.getDeclaredMethod("baseUri", URI.class), 0);
|
||||
PAGE_SORT = MethodParameter.forMethodOrConstructor(
|
||||
Methods.class.getDeclaredMethod("pagingAndSorting", Pageable.class),
|
||||
0
|
||||
);
|
||||
} catch(NoSuchMethodException e) {
|
||||
Methods.class.getDeclaredMethod("pagingAndSorting", Pageable.class), 0);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final RepositoryRestConfiguration config =
|
||||
new RepositoryRestConfiguration()
|
||||
.setBaseUri(URI.create("http://localhost:8080"));
|
||||
private final BaseUriMethodArgumentResolver baseUriResolver =
|
||||
new BaseUriMethodArgumentResolver(config);
|
||||
private final PageableHandlerMethodArgumentResolver pageSortResolver =
|
||||
new PageableHandlerMethodArgumentResolver();
|
||||
private final RepositoryRestConfiguration config = new RepositoryRestConfiguration().setBaseUri(URI
|
||||
.create("http://localhost:8080"));
|
||||
private final BaseUriMethodArgumentResolver baseUriResolver = new BaseUriMethodArgumentResolver(config);
|
||||
private final PageableHandlerMethodArgumentResolver pageSortResolver = new PageableHandlerMethodArgumentResolver();
|
||||
private ModelAndViewContainer mavContainer;
|
||||
@Mock WebDataBinderFactory webDataBinderFactory;
|
||||
@Mock WebDataBinderFactory webDataBinderFactory;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mavContainer = new ModelAndViewContainer();
|
||||
|
||||
|
||||
pageSortResolver.setOneIndexedParameters(true);
|
||||
pageSortResolver.setFallbackPageable(new PageRequest(1, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void baseUriMethodArgumentResolver() throws Exception {
|
||||
assertThat("Finds @BaseURI-annotated java.net.URI parameter",
|
||||
baseUriResolver.supportsParameter(BASE_URI),
|
||||
is(true));
|
||||
assertThat("Finds @BaseURI-annotated java.net.URI parameter", baseUriResolver.supportsParameter(BASE_URI), is(true));
|
||||
|
||||
// Resolve the base URI
|
||||
URI baseUri = (URI)baseUriResolver.resolveArgument(
|
||||
BASE_URI,
|
||||
mavContainer,
|
||||
new ServletWebRequest(Requests.ROOT_REQUEST),
|
||||
webDataBinderFactory
|
||||
);
|
||||
URI baseUri = (URI) baseUriResolver.resolveArgument(BASE_URI, mavContainer, new ServletWebRequest(
|
||||
Requests.ROOT_REQUEST), webDataBinderFactory);
|
||||
|
||||
assertThat("Base URI should be 'http://localhost:8080'",
|
||||
baseUri.toString(),
|
||||
is("http://localhost:8080"));
|
||||
assertThat("Base URI should be 'http://localhost:8080'", baseUri.toString(), is("http://localhost:8080"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pagingAndSortingMethodArgumentResolver() throws Exception {
|
||||
assertThat("Finds PagingAndSorting parameter",
|
||||
pageSortResolver.supportsParameter(PAGE_SORT),
|
||||
is(true));
|
||||
assertThat("Finds PagingAndSorting parameter", pageSortResolver.supportsParameter(PAGE_SORT), is(true));
|
||||
|
||||
// Resolve Page and Sort information
|
||||
Pageable pageSort = pageSortResolver.resolveArgument(
|
||||
PAGE_SORT,
|
||||
mavContainer,
|
||||
new ServletWebRequest(Requests.PAGE_REQUEST),
|
||||
webDataBinderFactory
|
||||
);
|
||||
Pageable pageSort = pageSortResolver.resolveArgument(PAGE_SORT, mavContainer, new ServletWebRequest(
|
||||
Requests.PAGE_REQUEST), webDataBinderFactory);
|
||||
|
||||
assertThat("Finds page parameter value",
|
||||
pageSort.getPageNumber(),
|
||||
is(1));
|
||||
assertThat("Finds limit parameter value",
|
||||
pageSort.getPageSize(),
|
||||
is(10));
|
||||
assertThat("Finds page parameter value", pageSort.getPageNumber(), is(1));
|
||||
assertThat("Finds limit parameter value", pageSort.getPageSize(), is(10));
|
||||
}
|
||||
|
||||
static class Methods {
|
||||
void baseUri(@BaseURI URI baseUri) {
|
||||
}
|
||||
void baseUri(@BaseURI URI baseUri) {}
|
||||
|
||||
void pagingAndSorting(Pageable pageSort) {
|
||||
}
|
||||
void pagingAndSorting(Pageable pageSort) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,40 +11,41 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
class HttpEntityMatcher<T> extends BaseMatcher<HttpEntity<T>> {
|
||||
|
||||
private final HttpEntity<T> expected;
|
||||
private final HttpEntity<T> expected;
|
||||
|
||||
public HttpEntityMatcher(HttpEntity<T> expected) {
|
||||
Assert.notNull(expected, "HttpEntity cannot be null");
|
||||
this.expected = expected;
|
||||
}
|
||||
public HttpEntityMatcher(HttpEntity<T> expected) {
|
||||
Assert.notNull(expected, "HttpEntity cannot be null");
|
||||
this.expected = expected;
|
||||
}
|
||||
|
||||
public static <T> HttpEntityMatcher<T> httpEntity(HttpEntity<T> httpEntity) {
|
||||
return new HttpEntityMatcher<T>(httpEntity);
|
||||
}
|
||||
public static <T> HttpEntityMatcher<T> httpEntity(HttpEntity<T> httpEntity) {
|
||||
return new HttpEntityMatcher<T>(httpEntity);
|
||||
}
|
||||
|
||||
@Override public boolean matches(Object item) {
|
||||
if(!(item instanceof HttpEntity)) {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean matches(Object item) {
|
||||
if (!(item instanceof HttpEntity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(item instanceof ResponseEntity && expected instanceof ResponseEntity) {
|
||||
ResponseEntity<?> left = (ResponseEntity<?>)expected;
|
||||
ResponseEntity<?> right = (ResponseEntity<?>)item;
|
||||
if (item instanceof ResponseEntity && expected instanceof ResponseEntity) {
|
||||
ResponseEntity<?> left = (ResponseEntity<?>) expected;
|
||||
ResponseEntity<?> right = (ResponseEntity<?>) item;
|
||||
|
||||
if(!left.getStatusCode().equals(right.getStatusCode())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!left.getStatusCode().equals(right.getStatusCode())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
HttpEntity<?> left = expected;
|
||||
HttpEntity<?> right = (HttpEntity<?>)item;
|
||||
HttpEntity<?> left = expected;
|
||||
HttpEntity<?> right = (HttpEntity<?>) item;
|
||||
|
||||
return left.getBody().equals(right.getBody())
|
||||
&& left.getHeaders().equals(right.getHeaders());
|
||||
}
|
||||
return left.getBody().equals(right.getBody()) && left.getHeaders().equals(right.getHeaders());
|
||||
}
|
||||
|
||||
@Override public void describeTo(Description description) {
|
||||
description.appendText(expected.toString());
|
||||
}
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText(expected.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,10 +11,5 @@ import org.springframework.data.rest.webmvc.mongodb.MongoDbRepositoryConfig;
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@Configuration
|
||||
@Import({
|
||||
JpaRepositoryConfig.class,
|
||||
MongoDbRepositoryConfig.class,
|
||||
GemfireRepositoryConfig.class
|
||||
})
|
||||
public class RepositoryRestMvcTestConfig extends RepositoryRestMvcConfiguration {
|
||||
}
|
||||
@Import({ JpaRepositoryConfig.class, MongoDbRepositoryConfig.class, GemfireRepositoryConfig.class })
|
||||
public class RepositoryRestMvcTestConfig extends RepositoryRestMvcConfiguration {}
|
||||
|
||||
@@ -7,15 +7,14 @@ import org.springframework.mock.web.MockHttpServletRequest;
|
||||
*/
|
||||
public abstract class Requests {
|
||||
|
||||
public static MockHttpServletRequest ROOT_REQUEST = new MockHttpServletRequest("GET", "http://localhost:8080/");
|
||||
public static MockHttpServletRequest PAGE_REQUEST = new MockHttpServletRequest("GET", "http://localhost:8080/");
|
||||
public static MockHttpServletRequest ROOT_REQUEST = new MockHttpServletRequest("GET", "http://localhost:8080/");
|
||||
public static MockHttpServletRequest PAGE_REQUEST = new MockHttpServletRequest("GET", "http://localhost:8080/");
|
||||
|
||||
static {
|
||||
PAGE_REQUEST.setParameter("page", "2");
|
||||
PAGE_REQUEST.setParameter("size", "10");
|
||||
}
|
||||
static {
|
||||
PAGE_REQUEST.setParameter("page", "2");
|
||||
PAGE_REQUEST.setParameter("size", "10");
|
||||
}
|
||||
|
||||
private Requests() {
|
||||
}
|
||||
private Requests() {}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,278 +50,269 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link org.springframework.data.rest.webmvc.ResourceProcessorHandlerMethodReturnValueHandler}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ResourceProcessorHandlerMethodReturnValueHandlerUnitTests {
|
||||
|
||||
static final Resource<String> FOO = new Resource<String>("foo");
|
||||
static final Resources<Resource<String>> FOOS = new Resources<Resource<String>>(
|
||||
Collections.singletonList(FOO)
|
||||
);
|
||||
static final StringResource FOO_RES = new StringResource("foo");
|
||||
static final HttpEntity<Resource<String>> FOO_ENTITY = new HttpEntity<Resource<String>>(FOO);
|
||||
static final ResponseEntity<Resource<String>> FOO_RESP_ENTITY = new ResponseEntity<Resource<String>>(
|
||||
FOO,
|
||||
HttpStatus.OK
|
||||
);
|
||||
static final HttpEntity<StringResource> FOO_RES_ENTITY = new HttpEntity<StringResource>(FOO_RES);
|
||||
static final Resource<String> BAR = new Resource<String>("bar");
|
||||
static final Resources<Resource<String>> BARS = new Resources<Resource<String>>(
|
||||
Collections.singletonList(BAR)
|
||||
);
|
||||
static final StringResource BAR_RES = new StringResource("bar");
|
||||
static final HttpEntity<Resource<String>> BAR_ENTITY = new HttpEntity<Resource<String>>(BAR);
|
||||
static final ResponseEntity<Resource<String>> BAR_RESP_ENTITY = new ResponseEntity<Resource<String>>(
|
||||
BAR,
|
||||
HttpStatus.OK
|
||||
);
|
||||
static final HttpEntity<StringResource> BAR_RES_ENTITY = new HttpEntity<StringResource>(BAR_RES);
|
||||
static final Resource<Long> LONG_10 = new Resource<Long>(10L);
|
||||
static final Resource<Long> LONG_20 = new Resource<Long>(20L);
|
||||
static final LongResource LONG_10_RES = new LongResource(10L);
|
||||
static final LongResource LONG_20_RES = new LongResource(20L);
|
||||
static final HttpEntity<Resource<Long>> LONG_10_ENTITY = new HttpEntity<Resource<Long>>(LONG_10);
|
||||
static final HttpEntity<LongResource> LONG_10_RES_ENTITY = new HttpEntity<LongResource>(LONG_10_RES);
|
||||
static final HttpEntity<Resource<Long>> LONG_20_ENTITY = new HttpEntity<Resource<Long>>(LONG_20);
|
||||
static final HttpEntity<LongResource> LONG_20_RES_ENTITY = new HttpEntity<LongResource>(LONG_20_RES);
|
||||
static final Map<String, MethodParameter> METHOD_PARAMS = new HashMap<String, MethodParameter>();
|
||||
static final Resource<String> FOO = new Resource<String>("foo");
|
||||
static final Resources<Resource<String>> FOOS = new Resources<Resource<String>>(Collections.singletonList(FOO));
|
||||
static final StringResource FOO_RES = new StringResource("foo");
|
||||
static final HttpEntity<Resource<String>> FOO_ENTITY = new HttpEntity<Resource<String>>(FOO);
|
||||
static final ResponseEntity<Resource<String>> FOO_RESP_ENTITY = new ResponseEntity<Resource<String>>(FOO,
|
||||
HttpStatus.OK);
|
||||
static final HttpEntity<StringResource> FOO_RES_ENTITY = new HttpEntity<StringResource>(FOO_RES);
|
||||
static final Resource<String> BAR = new Resource<String>("bar");
|
||||
static final Resources<Resource<String>> BARS = new Resources<Resource<String>>(Collections.singletonList(BAR));
|
||||
static final StringResource BAR_RES = new StringResource("bar");
|
||||
static final HttpEntity<Resource<String>> BAR_ENTITY = new HttpEntity<Resource<String>>(BAR);
|
||||
static final ResponseEntity<Resource<String>> BAR_RESP_ENTITY = new ResponseEntity<Resource<String>>(BAR,
|
||||
HttpStatus.OK);
|
||||
static final HttpEntity<StringResource> BAR_RES_ENTITY = new HttpEntity<StringResource>(BAR_RES);
|
||||
static final Resource<Long> LONG_10 = new Resource<Long>(10L);
|
||||
static final Resource<Long> LONG_20 = new Resource<Long>(20L);
|
||||
static final LongResource LONG_10_RES = new LongResource(10L);
|
||||
static final LongResource LONG_20_RES = new LongResource(20L);
|
||||
static final HttpEntity<Resource<Long>> LONG_10_ENTITY = new HttpEntity<Resource<Long>>(LONG_10);
|
||||
static final HttpEntity<LongResource> LONG_10_RES_ENTITY = new HttpEntity<LongResource>(LONG_10_RES);
|
||||
static final HttpEntity<Resource<Long>> LONG_20_ENTITY = new HttpEntity<Resource<Long>>(LONG_20);
|
||||
static final HttpEntity<LongResource> LONG_20_RES_ENTITY = new HttpEntity<LongResource>(LONG_20_RES);
|
||||
static final Map<String, MethodParameter> METHOD_PARAMS = new HashMap<String, MethodParameter>();
|
||||
|
||||
static {
|
||||
doWithMethods(Controller.class, new MethodCallback() {
|
||||
@Override public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
METHOD_PARAMS.put(method.getName(), new MethodParameter(method, -1));
|
||||
}
|
||||
});
|
||||
}
|
||||
static {
|
||||
doWithMethods(Controller.class, new MethodCallback() {
|
||||
@Override
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
METHOD_PARAMS.put(method.getName(), new MethodParameter(method, -1));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Mock HandlerMethodReturnValueHandler delegate;
|
||||
List<ResourceProcessor<?>> resourceProcessors;
|
||||
@Mock HandlerMethodReturnValueHandler delegate;
|
||||
List<ResourceProcessor<?>> resourceProcessors;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
resourceProcessors = new ArrayList<ResourceProcessor<?>>();
|
||||
}
|
||||
@Before
|
||||
public void setUp() {
|
||||
resourceProcessors = new ArrayList<ResourceProcessor<?>>();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsIfDelegateSupports() {
|
||||
assertSupport(true);
|
||||
}
|
||||
@Test
|
||||
public void supportsIfDelegateSupports() {
|
||||
assertSupport(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotSupportIfDelegateDoesNot() {
|
||||
assertSupport(false);
|
||||
}
|
||||
@Test
|
||||
public void doesNotSupportIfDelegateDoesNot() {
|
||||
assertSupport(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessesStringResource() throws Exception {
|
||||
resourceProcessors.add(StringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(LongResourceProcessor.INSTANCE);
|
||||
@Test
|
||||
public void postProcessesStringResource() throws Exception {
|
||||
resourceProcessors.add(StringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(LongResourceProcessor.INSTANCE);
|
||||
|
||||
invokeReturnValueHandler("stringResourceEntity", is(BAR), FOO);
|
||||
}
|
||||
invokeReturnValueHandler("stringResourceEntity", is(BAR), FOO);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessesStringResourceInResponseEntity() throws Exception {
|
||||
resourceProcessors.add(StringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(LongResourceProcessor.INSTANCE);
|
||||
@Test
|
||||
public void postProcessesStringResourceInResponseEntity() throws Exception {
|
||||
resourceProcessors.add(StringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(LongResourceProcessor.INSTANCE);
|
||||
|
||||
invokeReturnValueHandler("stringResourceEntity", httpEntity(BAR_RESP_ENTITY), FOO_RESP_ENTITY);
|
||||
}
|
||||
invokeReturnValueHandler("stringResourceEntity", httpEntity(BAR_RESP_ENTITY), FOO_RESP_ENTITY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessesStringResourceInWildcardResponseEntity() throws Exception {
|
||||
resourceProcessors.add(StringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(LongResourceProcessor.INSTANCE);
|
||||
@Test
|
||||
public void postProcessesStringResourceInWildcardResponseEntity() throws Exception {
|
||||
resourceProcessors.add(StringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(LongResourceProcessor.INSTANCE);
|
||||
|
||||
invokeReturnValueHandler("resourceEntity", httpEntity(BAR_RESP_ENTITY), FOO_RESP_ENTITY);
|
||||
}
|
||||
invokeReturnValueHandler("resourceEntity", httpEntity(BAR_RESP_ENTITY), FOO_RESP_ENTITY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessesStringResources() throws Exception {
|
||||
resourceProcessors.add(StringResourcesProcessor.INSTANCE);
|
||||
resourceProcessors.add(LongResourceProcessor.INSTANCE);
|
||||
@Test
|
||||
public void postProcessesStringResources() throws Exception {
|
||||
resourceProcessors.add(StringResourcesProcessor.INSTANCE);
|
||||
resourceProcessors.add(LongResourceProcessor.INSTANCE);
|
||||
|
||||
invokeReturnValueHandler("resources", is(BARS), FOOS);
|
||||
}
|
||||
invokeReturnValueHandler("resources", is(BARS), FOOS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessesSpecializedStringResource() throws Exception {
|
||||
resourceProcessors.add(SpecializedStringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(LongResourceProcessor.INSTANCE);
|
||||
@Test
|
||||
public void postProcessesSpecializedStringResource() throws Exception {
|
||||
resourceProcessors.add(SpecializedStringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(LongResourceProcessor.INSTANCE);
|
||||
|
||||
invokeReturnValueHandler("stringResourceEntity", httpEntity(BAR_RES_ENTITY), FOO_RES_ENTITY);
|
||||
}
|
||||
invokeReturnValueHandler("stringResourceEntity", httpEntity(BAR_RES_ENTITY), FOO_RES_ENTITY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessesSpecializedStringUsingStringResourceProcessor() throws Exception {
|
||||
resourceProcessors.add(StringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(LongResourceProcessor.INSTANCE);
|
||||
@Test
|
||||
public void postProcessesSpecializedStringUsingStringResourceProcessor() throws Exception {
|
||||
resourceProcessors.add(StringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(LongResourceProcessor.INSTANCE);
|
||||
|
||||
invokeReturnValueHandler("specializedStringResourceEntity", httpEntity(BAR_ENTITY), FOO_RES_ENTITY);
|
||||
}
|
||||
invokeReturnValueHandler("specializedStringResourceEntity", httpEntity(BAR_ENTITY), FOO_RES_ENTITY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessesLongResource() throws Exception {
|
||||
resourceProcessors.add(StringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(LongResourceProcessor.INSTANCE);
|
||||
@Test
|
||||
public void postProcessesLongResource() throws Exception {
|
||||
resourceProcessors.add(StringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(LongResourceProcessor.INSTANCE);
|
||||
|
||||
invokeReturnValueHandler("longResource", is(LONG_20), LONG_10);
|
||||
}
|
||||
invokeReturnValueHandler("longResource", is(LONG_20), LONG_10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessesSpecializedLongResource() throws Exception {
|
||||
resourceProcessors.add(StringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(SpecializedLongResourceProcessor.INSTANCE);
|
||||
@Test
|
||||
public void postProcessesSpecializedLongResource() throws Exception {
|
||||
resourceProcessors.add(StringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(SpecializedLongResourceProcessor.INSTANCE);
|
||||
|
||||
invokeReturnValueHandler("specializedLongResourceEntity", httpEntity(LONG_20_RES_ENTITY), LONG_10_RES_ENTITY);
|
||||
}
|
||||
invokeReturnValueHandler("specializedLongResourceEntity", httpEntity(LONG_20_RES_ENTITY), LONG_10_RES_ENTITY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotPostProcesseLongResourceWithSpecializedLongResourceProcessor() throws Exception {
|
||||
resourceProcessors.add(StringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(SpecializedLongResourceProcessor.INSTANCE);
|
||||
@Test
|
||||
public void doesNotPostProcesseLongResourceWithSpecializedLongResourceProcessor() throws Exception {
|
||||
resourceProcessors.add(StringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(SpecializedLongResourceProcessor.INSTANCE);
|
||||
|
||||
invokeReturnValueHandler("numberResourceEntity", httpEntity(LONG_10_ENTITY), LONG_10_ENTITY);
|
||||
}
|
||||
invokeReturnValueHandler("numberResourceEntity", httpEntity(LONG_10_ENTITY), LONG_10_ENTITY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessesSpecializedLongResourceUsingLongResourceProcessor() throws Exception {
|
||||
resourceProcessors.add(StringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(LongResourceProcessor.INSTANCE);
|
||||
@Test
|
||||
public void postProcessesSpecializedLongResourceUsingLongResourceProcessor() throws Exception {
|
||||
resourceProcessors.add(StringResourceProcessor.INSTANCE);
|
||||
resourceProcessors.add(LongResourceProcessor.INSTANCE);
|
||||
|
||||
invokeReturnValueHandler("resourceEntity", is(LONG_20), LONG_10_RES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesHeaderLinksResponseEntityIfConfigured() throws Exception {
|
||||
|
||||
Resource<String> resource = new Resource<String>("foo", new Link("href", "rel"));
|
||||
MethodParameter parameter = METHOD_PARAMS.get("resource");
|
||||
|
||||
ResourceProcessorHandlerMethodReturnValueHandler handler = new ResourceProcessorHandlerMethodReturnValueHandler(
|
||||
delegate, resourceProcessors);
|
||||
handler.setRootLinksAsHeaders(true);
|
||||
handler.handleReturnValue(resource, parameter, null, null);
|
||||
|
||||
invokeReturnValueHandler("resourceEntity", is(LONG_20), LONG_10_RES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesHeaderLinksResponseEntityIfConfigured() throws Exception {
|
||||
|
||||
Resource<String> resource = new Resource<String>("foo", new Link("href", "rel"));
|
||||
MethodParameter parameter = METHOD_PARAMS.get("resource");
|
||||
|
||||
ResourceProcessorHandlerMethodReturnValueHandler handler = new ResourceProcessorHandlerMethodReturnValueHandler(delegate, resourceProcessors);
|
||||
handler.setRootLinksAsHeaders(true);
|
||||
handler.handleReturnValue(resource, parameter, null, null);
|
||||
|
||||
verify(delegate, times(1)).handleReturnValue(Mockito.any(HeaderLinksResponseEntity.class), eq(parameter),
|
||||
Mockito.any(ModelAndViewContainer.class), Mockito.any(NativeWebRequest.class));
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers ---------------------------------------------------------//
|
||||
private void invokeReturnValueHandler(String method,
|
||||
final Matcher<?> matcher,
|
||||
Object returnValue) throws Exception {
|
||||
final MethodParameter methodParam = METHOD_PARAMS.get(method);
|
||||
// Helpers ---------------------------------------------------------//
|
||||
private void invokeReturnValueHandler(String method, final Matcher<?> matcher, Object returnValue) throws Exception {
|
||||
final MethodParameter methodParam = METHOD_PARAMS.get(method);
|
||||
|
||||
if (methodParam == null) {
|
||||
throw new IllegalArgumentException("Invalid method!");
|
||||
}
|
||||
if (methodParam == null) {
|
||||
throw new IllegalArgumentException("Invalid method!");
|
||||
}
|
||||
|
||||
HandlerMethodReturnValueHandler handler = new ResourceProcessorHandlerMethodReturnValueHandler(
|
||||
delegate,
|
||||
resourceProcessors
|
||||
);
|
||||
handler.handleReturnValue(returnValue, methodParam, null, null);
|
||||
}
|
||||
HandlerMethodReturnValueHandler handler = new ResourceProcessorHandlerMethodReturnValueHandler(delegate,
|
||||
resourceProcessors);
|
||||
handler.handleReturnValue(returnValue, methodParam, null, null);
|
||||
}
|
||||
|
||||
private void assertSupport(boolean value) {
|
||||
private void assertSupport(boolean value) {
|
||||
|
||||
final MethodParameter parameter = Mockito.mock(MethodParameter.class);
|
||||
when(delegate.supportsReturnType(Mockito.any(MethodParameter.class))).thenReturn(value);
|
||||
final MethodParameter parameter = Mockito.mock(MethodParameter.class);
|
||||
when(delegate.supportsReturnType(Mockito.any(MethodParameter.class))).thenReturn(value);
|
||||
|
||||
HandlerMethodReturnValueHandler handler = new ResourceProcessorHandlerMethodReturnValueHandler(
|
||||
delegate,
|
||||
resourceProcessors
|
||||
);
|
||||
HandlerMethodReturnValueHandler handler = new ResourceProcessorHandlerMethodReturnValueHandler(delegate,
|
||||
resourceProcessors);
|
||||
|
||||
assertThat(handler.supportsReturnType(parameter), is(value));
|
||||
}
|
||||
assertThat(handler.supportsReturnType(parameter), is(value));
|
||||
}
|
||||
|
||||
enum StringResourceProcessor implements ResourceProcessor<Resource<String>> {
|
||||
INSTANCE;
|
||||
enum StringResourceProcessor implements ResourceProcessor<Resource<String>> {
|
||||
INSTANCE;
|
||||
|
||||
@Override public Resource<String> process(Resource<String> resource) {
|
||||
return BAR;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Resource<String> process(Resource<String> resource) {
|
||||
return BAR;
|
||||
}
|
||||
}
|
||||
|
||||
enum LongResourceProcessor implements ResourceProcessor<Resource<Long>> {
|
||||
INSTANCE;
|
||||
enum LongResourceProcessor implements ResourceProcessor<Resource<Long>> {
|
||||
INSTANCE;
|
||||
|
||||
@Override public Resource<Long> process(Resource<Long> resource) {
|
||||
return LONG_20;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Resource<Long> process(Resource<Long> resource) {
|
||||
return LONG_20;
|
||||
}
|
||||
}
|
||||
|
||||
enum StringResourcesProcessor implements ResourceProcessor<Resources<Resource<String>>> {
|
||||
INSTANCE;
|
||||
enum StringResourcesProcessor implements ResourceProcessor<Resources<Resource<String>>> {
|
||||
INSTANCE;
|
||||
|
||||
@Override public Resources<Resource<String>> process(Resources<Resource<String>> resource) {
|
||||
return BARS;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Resources<Resource<String>> process(Resources<Resource<String>> resource) {
|
||||
return BARS;
|
||||
}
|
||||
}
|
||||
|
||||
enum SpecializedStringResourceProcessor implements ResourceProcessor<StringResource> {
|
||||
INSTANCE;
|
||||
enum SpecializedStringResourceProcessor implements ResourceProcessor<StringResource> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public StringResource process(StringResource resource) {
|
||||
return BAR_RES;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public StringResource process(StringResource resource) {
|
||||
return BAR_RES;
|
||||
}
|
||||
}
|
||||
|
||||
enum SpecializedLongResourceProcessor implements ResourceProcessor<LongResource> {
|
||||
INSTANCE;
|
||||
enum SpecializedLongResourceProcessor implements ResourceProcessor<LongResource> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public LongResource process(LongResource resource) {
|
||||
return LONG_20_RES;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public LongResource process(LongResource resource) {
|
||||
return LONG_20_RES;
|
||||
}
|
||||
}
|
||||
|
||||
static interface Controller {
|
||||
static interface Controller {
|
||||
|
||||
Resources<Resource<String>> resources();
|
||||
Resources<Resource<String>> resources();
|
||||
|
||||
Resource<String> resource();
|
||||
Resource<String> resource();
|
||||
|
||||
Resource<Long> longResource();
|
||||
Resource<Long> longResource();
|
||||
|
||||
StringResource specializedResource();
|
||||
StringResource specializedResource();
|
||||
|
||||
Object object();
|
||||
Object object();
|
||||
|
||||
HttpEntity<Resource<?>> resourceEntity();
|
||||
HttpEntity<Resource<?>> resourceEntity();
|
||||
|
||||
HttpEntity<Resources<?>> resourcesEntity();
|
||||
HttpEntity<Resources<?>> resourcesEntity();
|
||||
|
||||
HttpEntity<Object> objectEntity();
|
||||
HttpEntity<Object> objectEntity();
|
||||
|
||||
HttpEntity<Resource<String>> stringResourceEntity();
|
||||
HttpEntity<Resource<String>> stringResourceEntity();
|
||||
|
||||
HttpEntity<Resource<? extends Number>> numberResourceEntity();
|
||||
HttpEntity<Resource<? extends Number>> numberResourceEntity();
|
||||
|
||||
HttpEntity<StringResource> specializedStringResourceEntity();
|
||||
HttpEntity<StringResource> specializedStringResourceEntity();
|
||||
|
||||
HttpEntity<LongResource> specializedLongResourceEntity();
|
||||
HttpEntity<LongResource> specializedLongResourceEntity();
|
||||
|
||||
ResponseEntity<Resource<?>> resourceResponseEntity();
|
||||
ResponseEntity<Resource<?>> resourceResponseEntity();
|
||||
|
||||
ResponseEntity<Resources<?>> resourcesResponseEntity();
|
||||
}
|
||||
ResponseEntity<Resources<?>> resourcesResponseEntity();
|
||||
}
|
||||
|
||||
static class StringResource extends Resource<String> {
|
||||
public StringResource(String value) {
|
||||
super(value);
|
||||
}
|
||||
}
|
||||
static class StringResource extends Resource<String> {
|
||||
public StringResource(String value) {
|
||||
super(value);
|
||||
}
|
||||
}
|
||||
|
||||
static class LongResource extends Resource<Long> {
|
||||
public LongResource(Long value) {
|
||||
super(value);
|
||||
}
|
||||
}
|
||||
static class LongResource extends Resource<Long> {
|
||||
public LongResource(Long value) {
|
||||
super(value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public class RepositoryRestMvConfigurationIntegrationTests {
|
||||
public static void setUp() {
|
||||
context = new AnnotationConfigApplicationContext(ExtendingConfiguration.class);
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void tearDown() {
|
||||
if (context != null) {
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.springframework.data.gemfire.repository.config.EnableGemfireRepositor
|
||||
|
||||
/**
|
||||
* Spring JavaConfig configuration class to setup a Spring container and infrastructure components.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author David Turanski
|
||||
*/
|
||||
|
||||
@@ -25,8 +25,7 @@ import org.springframework.data.annotation.Id;
|
||||
*/
|
||||
public class AbstractPersistentEntity {
|
||||
|
||||
@Id
|
||||
private final Long id;
|
||||
@Id private final Long id;
|
||||
|
||||
/**
|
||||
* Returns the identifier of the entity.
|
||||
@@ -36,15 +35,14 @@ public class AbstractPersistentEntity {
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
protected AbstractPersistentEntity(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
protected AbstractPersistentEntity() {
|
||||
this.id = null;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
|
||||
@@ -28,7 +28,7 @@ public class Address {
|
||||
|
||||
/**
|
||||
* Creates a new {@link Address} from the given street, city and country.
|
||||
*
|
||||
*
|
||||
* @param street must not be {@literal null} or empty.
|
||||
* @param city must not be {@literal null} or empty.
|
||||
* @param country must not be {@literal null} or empty.
|
||||
|
||||
@@ -22,117 +22,110 @@ import java.util.Set;
|
||||
import org.springframework.data.gemfire.mapping.Region;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* A customer.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author David Turanski
|
||||
*/
|
||||
@Region
|
||||
public class Customer extends AbstractPersistentEntity {
|
||||
private EmailAddress emailAddress;
|
||||
private String firstname, lastname;
|
||||
private Set<Address> addresses = new HashSet<Address>();
|
||||
private EmailAddress emailAddress;
|
||||
private String firstname, lastname;
|
||||
private Set<Address> addresses = new HashSet<Address>();
|
||||
|
||||
/**
|
||||
* Creates a new {@link Customer} from the given parameters.
|
||||
*
|
||||
* @param id
|
||||
* the unique id;
|
||||
* @param emailAddress
|
||||
* must not be {@literal null} or empty.
|
||||
* @param firstname
|
||||
* must not be {@literal null} or empty.
|
||||
* @param lastname
|
||||
* must not be {@literal null} or empty.
|
||||
*/
|
||||
public Customer(Long id, EmailAddress emailAddress, String firstname, String lastname) {
|
||||
super(id);
|
||||
Assert.hasText(firstname);
|
||||
Assert.hasText(lastname);
|
||||
Assert.notNull(emailAddress);
|
||||
/**
|
||||
* Creates a new {@link Customer} from the given parameters.
|
||||
*
|
||||
* @param id the unique id;
|
||||
* @param emailAddress must not be {@literal null} or empty.
|
||||
* @param firstname must not be {@literal null} or empty.
|
||||
* @param lastname must not be {@literal null} or empty.
|
||||
*/
|
||||
public Customer(Long id, EmailAddress emailAddress, String firstname, String lastname) {
|
||||
super(id);
|
||||
Assert.hasText(firstname);
|
||||
Assert.hasText(lastname);
|
||||
Assert.notNull(emailAddress);
|
||||
|
||||
this.firstname = firstname;
|
||||
this.lastname = lastname;
|
||||
this.emailAddress = emailAddress;
|
||||
}
|
||||
this.firstname = firstname;
|
||||
this.lastname = lastname;
|
||||
this.emailAddress = emailAddress;
|
||||
}
|
||||
|
||||
protected Customer() {
|
||||
}
|
||||
protected Customer() {}
|
||||
|
||||
/**
|
||||
* Adds the given {@link Address} to the {@link Customer}.
|
||||
*
|
||||
* @param address
|
||||
* must not be {@literal null}.
|
||||
*/
|
||||
public void add(Address address) {
|
||||
/**
|
||||
* Adds the given {@link Address} to the {@link Customer}.
|
||||
*
|
||||
* @param address must not be {@literal null}.
|
||||
*/
|
||||
public void add(Address address) {
|
||||
|
||||
Assert.notNull(address);
|
||||
this.addresses.add(address);
|
||||
}
|
||||
Assert.notNull(address);
|
||||
this.addresses.add(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the firstname of the {@link Customer}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getFirstname() {
|
||||
return firstname;
|
||||
}
|
||||
/**
|
||||
* Returns the firstname of the {@link Customer}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getFirstname() {
|
||||
return firstname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the firstname of the {@link Customer}.
|
||||
*
|
||||
* @param firstname
|
||||
*/
|
||||
public void setFirstname(String firstname) {
|
||||
this.firstname = firstname;
|
||||
}
|
||||
/**
|
||||
* Sets the firstname of the {@link Customer}.
|
||||
*
|
||||
* @param firstname
|
||||
*/
|
||||
public void setFirstname(String firstname) {
|
||||
this.firstname = firstname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lastname of the {@link Customer}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getLastname() {
|
||||
return lastname;
|
||||
}
|
||||
/**
|
||||
* Returns the lastname of the {@link Customer}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getLastname() {
|
||||
return lastname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the lastname of the {@link Customer}.
|
||||
*
|
||||
* @param lastname
|
||||
*/
|
||||
public void setLastname(String lastname) {
|
||||
this.lastname = lastname;
|
||||
}
|
||||
/**
|
||||
* Sets the lastname of the {@link Customer}.
|
||||
*
|
||||
* @param lastname
|
||||
*/
|
||||
public void setLastname(String lastname) {
|
||||
this.lastname = lastname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link EmailAddress} of the {@link Customer}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public EmailAddress getEmailAddress() {
|
||||
return emailAddress;
|
||||
}
|
||||
/**
|
||||
* Returns the {@link EmailAddress} of the {@link Customer}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public EmailAddress getEmailAddress() {
|
||||
return emailAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the emailAddress of the {@link Customer}.
|
||||
*
|
||||
* @param emailAddress
|
||||
*/
|
||||
public void setEmailAddress(EmailAddress emailAddress) {
|
||||
this.emailAddress = emailAddress;
|
||||
}
|
||||
/**
|
||||
* Sets the emailAddress of the {@link Customer}.
|
||||
*
|
||||
* @param emailAddress
|
||||
*/
|
||||
public void setEmailAddress(EmailAddress emailAddress) {
|
||||
this.emailAddress = emailAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link Customer}'s addresses.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Set<Address> getAddresses() {
|
||||
return Collections.unmodifiableSet(addresses);
|
||||
}
|
||||
/**
|
||||
* Return the {@link Customer}'s addresses.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Set<Address> getAddresses() {
|
||||
return Collections.unmodifiableSet(addresses);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,29 +22,27 @@ import org.springframework.data.repository.query.Param;
|
||||
|
||||
/**
|
||||
* Repository interface to access {@link Customer}s.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author David Turanski
|
||||
*/
|
||||
|
||||
public interface CustomerRepository extends CrudRepository<Customer, Long> {
|
||||
|
||||
/**
|
||||
* Finds all {@link Customer}s with the given lastname.
|
||||
*
|
||||
* @param lastname
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<Customer> findByLastname(@Param("lastname") String lastname);
|
||||
/**
|
||||
* Finds all {@link Customer}s with the given lastname.
|
||||
*
|
||||
* @param lastname
|
||||
* @return
|
||||
*/
|
||||
List<Customer> findByLastname(@Param("lastname") String lastname);
|
||||
|
||||
/**
|
||||
* Finds the Customer with the given {@link EmailAddress}.
|
||||
*
|
||||
* @param emailAddress
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Customer findByEmailAddress(@Param("email") EmailAddress emailAddress);
|
||||
/**
|
||||
* Finds the Customer with the given {@link EmailAddress}.
|
||||
*
|
||||
* @param emailAddress
|
||||
* @return
|
||||
*/
|
||||
Customer findByEmailAddress(@Param("email") EmailAddress emailAddress);
|
||||
|
||||
}
|
||||
|
||||
@@ -27,100 +27,97 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Value object to represent email addresses.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
public final class EmailAddress {
|
||||
|
||||
private static final String EMAIL_REGEX = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
|
||||
private static final Pattern PATTERN = Pattern.compile(EMAIL_REGEX);
|
||||
private final String value;
|
||||
private static final String EMAIL_REGEX = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
|
||||
private static final Pattern PATTERN = Pattern.compile(EMAIL_REGEX);
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* Creates a new {@link EmailAddress} from the given {@link String} representation.
|
||||
*
|
||||
* @param emailAddress
|
||||
* must not be {@literal null} or empty.
|
||||
*/
|
||||
@JsonCreator
|
||||
public EmailAddress(String emailAddress) {
|
||||
Assert.isTrue(isValid(emailAddress), "Invalid email address!");
|
||||
this.value = emailAddress;
|
||||
}
|
||||
/**
|
||||
* Creates a new {@link EmailAddress} from the given {@link String} representation.
|
||||
*
|
||||
* @param emailAddress must not be {@literal null} or empty.
|
||||
*/
|
||||
@JsonCreator
|
||||
public EmailAddress(String emailAddress) {
|
||||
Assert.isTrue(isValid(emailAddress), "Invalid email address!");
|
||||
this.value = emailAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link String} is a valid {@link EmailAddress} which means you can safely instantiate
|
||||
* the
|
||||
* class.
|
||||
*
|
||||
* @param candidate
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean isValid(String candidate) {
|
||||
return candidate == null ? false : PATTERN.matcher(candidate).matches();
|
||||
}
|
||||
/**
|
||||
* Returns whether the given {@link String} is a valid {@link EmailAddress} which means you can safely instantiate the
|
||||
* class.
|
||||
*
|
||||
* @param candidate
|
||||
* @return
|
||||
*/
|
||||
public static boolean isValid(String candidate) {
|
||||
return candidate == null ? false : PATTERN.matcher(candidate).matches();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if(this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!(obj instanceof EmailAddress)) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof EmailAddress)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EmailAddress that = (EmailAddress)obj;
|
||||
return this.value.equals(that.value);
|
||||
}
|
||||
EmailAddress that = (EmailAddress) obj;
|
||||
return this.value.equals(that.value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return value.hashCode();
|
||||
}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Component
|
||||
static class EmailAddressToStringConverter implements Converter<EmailAddress, String> {
|
||||
@Component
|
||||
static class EmailAddressToStringConverter implements Converter<EmailAddress, String> {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public String convert(EmailAddress source) {
|
||||
return source == null ? null : source.value;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public String convert(EmailAddress source) {
|
||||
return source == null ? null : source.value;
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
static class StringToEmailAddressConverter implements Converter<String, EmailAddress> {
|
||||
@Component
|
||||
static class StringToEmailAddressConverter implements Converter<String, EmailAddress> {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
public EmailAddress convert(String source) {
|
||||
return StringUtils.hasText(source) ? new EmailAddress(source) : null;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
public EmailAddress convert(String source) {
|
||||
return StringUtils.hasText(source) ? new EmailAddress(source) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,110 +24,101 @@ import org.springframework.data.annotation.PersistenceConstructor;
|
||||
import org.springframework.data.gemfire.mapping.Region;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* A product.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author David Turanski
|
||||
*/
|
||||
@Region
|
||||
public class Product extends AbstractPersistentEntity {
|
||||
|
||||
private String name, description;
|
||||
private BigDecimal price;
|
||||
private Map<String, String> attributes = new HashMap<String, String>();
|
||||
private String name, description;
|
||||
private BigDecimal price;
|
||||
private Map<String, String> attributes = new HashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Creates a new {@link Product} with the given name.
|
||||
*
|
||||
* @param id
|
||||
* a unique Id
|
||||
* @param name
|
||||
* must not be {@literal null} or empty.
|
||||
* @param price
|
||||
* must not be {@literal null} or less than or equal to zero.
|
||||
*/
|
||||
public Product(Long id, String name, BigDecimal price) {
|
||||
this(id, name, price, null);
|
||||
}
|
||||
/**
|
||||
* Creates a new {@link Product} with the given name.
|
||||
*
|
||||
* @param id a unique Id
|
||||
* @param name must not be {@literal null} or empty.
|
||||
* @param price must not be {@literal null} or less than or equal to zero.
|
||||
*/
|
||||
public Product(Long id, String name, BigDecimal price) {
|
||||
this(id, name, price, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Product} from the given name and description.
|
||||
*
|
||||
* @param id
|
||||
* a unique Id
|
||||
* @param name
|
||||
* must not be {@literal null} or empty.
|
||||
* @param price
|
||||
* must not be {@literal null} or less than or equal to zero.
|
||||
* @param description
|
||||
*/
|
||||
@PersistenceConstructor
|
||||
public Product(Long id, String name, BigDecimal price, String description) {
|
||||
super(id);
|
||||
Assert.hasText(name, "Name must not be null or empty!");
|
||||
Assert.isTrue(BigDecimal.ZERO.compareTo(price) < 0, "Price must be greater than zero!");
|
||||
/**
|
||||
* Creates a new {@link Product} from the given name and description.
|
||||
*
|
||||
* @param id a unique Id
|
||||
* @param name must not be {@literal null} or empty.
|
||||
* @param price must not be {@literal null} or less than or equal to zero.
|
||||
* @param description
|
||||
*/
|
||||
@PersistenceConstructor
|
||||
public Product(Long id, String name, BigDecimal price, String description) {
|
||||
super(id);
|
||||
Assert.hasText(name, "Name must not be null or empty!");
|
||||
Assert.isTrue(BigDecimal.ZERO.compareTo(price) < 0, "Price must be greater than zero!");
|
||||
|
||||
this.name = name;
|
||||
this.price = price;
|
||||
this.description = description;
|
||||
}
|
||||
this.name = name;
|
||||
this.price = price;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
protected Product() {
|
||||
}
|
||||
protected Product() {}
|
||||
|
||||
/**
|
||||
* Sets the attribute with the given name to the given value.
|
||||
*
|
||||
* @param name
|
||||
* must not be {@literal null} or empty.
|
||||
* @param value
|
||||
*/
|
||||
public void setAttribute(String name, String value) {
|
||||
/**
|
||||
* Sets the attribute with the given name to the given value.
|
||||
*
|
||||
* @param name must not be {@literal null} or empty.
|
||||
* @param value
|
||||
*/
|
||||
public void setAttribute(String name, String value) {
|
||||
|
||||
Assert.hasText(name);
|
||||
Assert.hasText(name);
|
||||
|
||||
if(value == null) {
|
||||
this.attributes.remove(value);
|
||||
} else {
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
}
|
||||
if (value == null) {
|
||||
this.attributes.remove(value);
|
||||
} else {
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Product}'s name.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
/**
|
||||
* Returns the {@link Product}'s name.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Product}'s description.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
/**
|
||||
* Returns the {@link Product}'s description.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the custom attributes of the {@link Product}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Map<String, String> getAttributes() {
|
||||
return Collections.unmodifiableMap(attributes);
|
||||
}
|
||||
/**
|
||||
* Returns all the custom attributes of the {@link Product}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Map<String, String> getAttributes() {
|
||||
return Collections.unmodifiableMap(attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the price of the {@link Product}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public BigDecimal getPrice() {
|
||||
return price;
|
||||
}
|
||||
/**
|
||||
* Returns the price of the {@link Product}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public BigDecimal getPrice() {
|
||||
return price;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.util.List;
|
||||
import org.springframework.data.gemfire.repository.Query;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
|
||||
/**
|
||||
* Repository interface to access {@link Product}s.
|
||||
*
|
||||
@@ -31,20 +30,22 @@ public interface ProductRepository extends CrudRepository<Product, Long> {
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Product}s having a description which contains the given snippet.
|
||||
*
|
||||
* @param the search string
|
||||
* @return
|
||||
*/
|
||||
|
||||
|
||||
List<Product> findByDescriptionContaining(String description);
|
||||
|
||||
/**
|
||||
* Returns all {@link Product}s having the given attribute value.
|
||||
*
|
||||
* @param attribute
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
@Query("SELECT * FROM /Product where attributes[$1] = $2")
|
||||
List<Product> findByAttributes(String key, String value);
|
||||
|
||||
|
||||
List<Product> findByName(String name);
|
||||
}
|
||||
|
||||
@@ -25,72 +25,69 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class LineItem {
|
||||
|
||||
private BigDecimal price;
|
||||
private int amount;
|
||||
private Long productId;
|
||||
private BigDecimal price;
|
||||
private int amount;
|
||||
private Long productId;
|
||||
|
||||
/**
|
||||
* Creates a new {@link LineItem} for the given {@link Product}.
|
||||
*
|
||||
* @param product
|
||||
* must not be {@literal null}.
|
||||
*/
|
||||
public LineItem(Product product) {
|
||||
this(product, 1);
|
||||
}
|
||||
/**
|
||||
* Creates a new {@link LineItem} for the given {@link Product}.
|
||||
*
|
||||
* @param product must not be {@literal null}.
|
||||
*/
|
||||
public LineItem(Product product) {
|
||||
this(product, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link LineItem} for the given {@link Product} and amount.
|
||||
*
|
||||
* @param product
|
||||
* must not be {@literal null}.
|
||||
* @param amount
|
||||
*/
|
||||
public LineItem(Product product, int amount) {
|
||||
Assert.notNull(product, "The given Product must not be null!");
|
||||
Assert.isTrue(amount > 0, "The amount of Products to be bought must be greater than 0!");
|
||||
/**
|
||||
* Creates a new {@link LineItem} for the given {@link Product} and amount.
|
||||
*
|
||||
* @param product must not be {@literal null}.
|
||||
* @param amount
|
||||
*/
|
||||
public LineItem(Product product, int amount) {
|
||||
Assert.notNull(product, "The given Product must not be null!");
|
||||
Assert.isTrue(amount > 0, "The amount of Products to be bought must be greater than 0!");
|
||||
|
||||
this.productId = product.getId();
|
||||
this.amount = amount;
|
||||
this.price = product.getPrice();
|
||||
}
|
||||
this.productId = product.getId();
|
||||
this.amount = amount;
|
||||
this.price = product.getPrice();
|
||||
}
|
||||
|
||||
protected LineItem() {
|
||||
}
|
||||
protected LineItem() {}
|
||||
|
||||
/**
|
||||
* Returns the id of the {@link Product} the {@link LineItem} refers to.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Long getProductId() {
|
||||
return productId;
|
||||
}
|
||||
/**
|
||||
* Returns the id of the {@link Product} the {@link LineItem} refers to.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Long getProductId() {
|
||||
return productId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of {@link Product}s to be ordered.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getAmount() {
|
||||
return amount;
|
||||
}
|
||||
/**
|
||||
* Returns the amount of {@link Product}s to be ordered.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the price a single unit of the {@link LineItem}'s product.
|
||||
*
|
||||
* @return the price
|
||||
*/
|
||||
public BigDecimal getUnitPrice() {
|
||||
return price;
|
||||
}
|
||||
/**
|
||||
* Returns the price a single unit of the {@link LineItem}'s product.
|
||||
*
|
||||
* @return the price
|
||||
*/
|
||||
public BigDecimal getUnitPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total for the {@link LineItem}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public BigDecimal getTotal() {
|
||||
return price.multiply(BigDecimal.valueOf(amount));
|
||||
}
|
||||
/**
|
||||
* Returns the total for the {@link LineItem}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public BigDecimal getTotal() {
|
||||
return price.multiply(BigDecimal.valueOf(amount));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,92 +32,88 @@ import org.springframework.util.Assert;
|
||||
@Region
|
||||
public class Order extends AbstractPersistentEntity {
|
||||
|
||||
private Long customerId;
|
||||
private Address billingAddress;
|
||||
private Address shippingAddress;
|
||||
private Set<LineItem> lineItems = new HashSet<LineItem>();
|
||||
private Long customerId;
|
||||
private Address billingAddress;
|
||||
private Address shippingAddress;
|
||||
private Set<LineItem> lineItems = new HashSet<LineItem>();
|
||||
|
||||
/**
|
||||
* Creates a new {@link Order} for the given {@link org.springframework.data.rest.webmvc.gemfire.core.Customer}.
|
||||
*
|
||||
* @param id
|
||||
* order ID
|
||||
* @param customerId
|
||||
* must not be {@literal null}.
|
||||
* @param shippingAddress
|
||||
* must not be {@literal null}.
|
||||
*/
|
||||
public Order(Long id, Long customerId, Address shippingAddress) {
|
||||
super(id);
|
||||
Assert.notNull(customerId);
|
||||
Assert.notNull(shippingAddress);
|
||||
/**
|
||||
* Creates a new {@link Order} for the given {@link org.springframework.data.rest.webmvc.gemfire.core.Customer}.
|
||||
*
|
||||
* @param id order ID
|
||||
* @param customerId must not be {@literal null}.
|
||||
* @param shippingAddress must not be {@literal null}.
|
||||
*/
|
||||
public Order(Long id, Long customerId, Address shippingAddress) {
|
||||
super(id);
|
||||
Assert.notNull(customerId);
|
||||
Assert.notNull(shippingAddress);
|
||||
|
||||
this.customerId = customerId;
|
||||
this.shippingAddress = shippingAddress;
|
||||
}
|
||||
this.customerId = customerId;
|
||||
this.shippingAddress = shippingAddress;
|
||||
}
|
||||
|
||||
protected Order() {
|
||||
}
|
||||
protected Order() {}
|
||||
|
||||
/**
|
||||
* Adds the given {@link LineItem} to the {@link Order}.
|
||||
*
|
||||
* @param lineItem
|
||||
*/
|
||||
public void add(LineItem lineItem) {
|
||||
this.lineItems.add(lineItem);
|
||||
}
|
||||
/**
|
||||
* Adds the given {@link LineItem} to the {@link Order}.
|
||||
*
|
||||
* @param lineItem
|
||||
*/
|
||||
public void add(LineItem lineItem) {
|
||||
this.lineItems.add(lineItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the {@link org.springframework.data.rest.webmvc.gemfire.core.Customer} who placed the {@link
|
||||
* Order}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
/**
|
||||
* Returns the id of the {@link org.springframework.data.rest.webmvc.gemfire.core.Customer} who placed the
|
||||
* {@link Order}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the billing {@link Address} for this order.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Address getBillingAddress() {
|
||||
return billingAddress != null ? billingAddress : shippingAddress;
|
||||
}
|
||||
/**
|
||||
* Returns the billing {@link Address} for this order.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Address getBillingAddress() {
|
||||
return billingAddress != null ? billingAddress : shippingAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shipping {@link Address} for this order;
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Address getShippingAddress() {
|
||||
return shippingAddress;
|
||||
}
|
||||
/**
|
||||
* Returns the shipping {@link Address} for this order;
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Address getShippingAddress() {
|
||||
return shippingAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all {@link LineItem}s currently belonging to the {@link Order}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Set<LineItem> getLineItems() {
|
||||
return Collections.unmodifiableSet(lineItems);
|
||||
}
|
||||
/**
|
||||
* Returns all {@link LineItem}s currently belonging to the {@link Order}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Set<LineItem> getLineItems() {
|
||||
return Collections.unmodifiableSet(lineItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total of the {@link Order}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public BigDecimal getTotal() {
|
||||
/**
|
||||
* Returns the total of the {@link Order}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public BigDecimal getTotal() {
|
||||
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
|
||||
for(LineItem item : lineItems) {
|
||||
total = total.add(item.getTotal());
|
||||
}
|
||||
for (LineItem item : lineItems) {
|
||||
total = total.add(item.getTotal());
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,26 +40,29 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
@EnableTransactionManagement
|
||||
public class JpaRepositoryConfig {
|
||||
|
||||
@Bean public DataSource dataSource() {
|
||||
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
|
||||
return builder.setType(EmbeddedDatabaseType.HSQL).build();
|
||||
}
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
|
||||
return builder.setType(EmbeddedDatabaseType.HSQL).build();
|
||||
}
|
||||
|
||||
@Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
|
||||
vendorAdapter.setDatabase(Database.HSQL);
|
||||
vendorAdapter.setGenerateDdl(true);
|
||||
@Bean
|
||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
|
||||
vendorAdapter.setDatabase(Database.HSQL);
|
||||
vendorAdapter.setGenerateDdl(true);
|
||||
|
||||
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
|
||||
factory.setJpaVendorAdapter(vendorAdapter);
|
||||
factory.setPackagesToScan(getClass().getPackage().getName());
|
||||
factory.setDataSource(dataSource());
|
||||
factory.afterPropertiesSet();
|
||||
|
||||
return factory;
|
||||
}
|
||||
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
|
||||
factory.setJpaVendorAdapter(vendorAdapter);
|
||||
factory.setPackagesToScan(getClass().getPackage().getName());
|
||||
factory.setDataSource(dataSource());
|
||||
factory.afterPropertiesSet();
|
||||
|
||||
@Bean public PlatformTransactionManager transactionManager() {
|
||||
return new JpaTransactionManager();
|
||||
}
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PlatformTransactionManager transactionManager() {
|
||||
return new JpaTransactionManager();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@ContextConfiguration(classes = JpaRepositoryConfig.class)
|
||||
@Transactional
|
||||
public class JpaWebTests extends AbstractWebIntegrationTests {
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.webmvc.AbstractWebIntegrationTests#expectedRootLinkRels()
|
||||
@@ -44,16 +44,16 @@ public class JpaWebTests extends AbstractWebIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void accessPersons() throws Exception {
|
||||
|
||||
|
||||
MockHttpServletResponse response = request("/people?page=0&size=1");
|
||||
|
||||
|
||||
Link nextLink = assertHasLinkWithRel(Link.REL_NEXT, response);
|
||||
assertDoesNotHaveLinkWithRel(Link.REL_PREVIOUS, response);
|
||||
|
||||
|
||||
response = request(nextLink);
|
||||
assertHasLinkWithRel(Link.REL_PREVIOUS, response);
|
||||
nextLink = assertHasLinkWithRel(Link.REL_NEXT, response);
|
||||
|
||||
|
||||
response = request(nextLink);
|
||||
assertHasLinkWithRel(Link.REL_PREVIOUS, response);
|
||||
assertDoesNotHaveLinkWithRel(Link.REL_NEXT, response);
|
||||
|
||||
@@ -17,90 +17,88 @@ import org.springframework.data.rest.repository.annotation.Description;
|
||||
|
||||
/**
|
||||
* An entity that represents a person.
|
||||
*
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@Entity
|
||||
public class Person {
|
||||
|
||||
private Long id;
|
||||
@Description("A person's first name")
|
||||
private String firstName;
|
||||
@Description("A person's last name")
|
||||
private String lastName;
|
||||
@Description("A person's siblings")
|
||||
private List<Person> siblings = Collections.emptyList();
|
||||
private Person father;
|
||||
@Description("Timestamp this person object was created")
|
||||
private Date created;
|
||||
private Long id;
|
||||
@Description("A person's first name") private String firstName;
|
||||
@Description("A person's last name") private String lastName;
|
||||
@Description("A person's siblings") private List<Person> siblings = Collections.emptyList();
|
||||
private Person father;
|
||||
@Description("Timestamp this person object was created") private Date created;
|
||||
|
||||
public Person() {
|
||||
}
|
||||
public Person() {}
|
||||
|
||||
public Person(String firstName, String lastName) {
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
}
|
||||
public Person(String firstName, String lastName) {
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
@Id @GeneratedValue public Long getId() {
|
||||
return id;
|
||||
}
|
||||
@Id
|
||||
@GeneratedValue
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
@NotNull
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public Person addSibling(Person p) {
|
||||
if(siblings == Collections.EMPTY_LIST) {
|
||||
siblings = new ArrayList<Person>();
|
||||
}
|
||||
siblings.add(p);
|
||||
return this;
|
||||
}
|
||||
public Person addSibling(Person p) {
|
||||
if (siblings == Collections.EMPTY_LIST) {
|
||||
siblings = new ArrayList<Person>();
|
||||
}
|
||||
siblings.add(p);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ManyToMany public List<Person> getSiblings() {
|
||||
return siblings;
|
||||
}
|
||||
@ManyToMany
|
||||
public List<Person> getSiblings() {
|
||||
return siblings;
|
||||
}
|
||||
|
||||
public void setSiblings(List<Person> siblings) {
|
||||
this.siblings = siblings;
|
||||
}
|
||||
public void setSiblings(List<Person> siblings) {
|
||||
this.siblings = siblings;
|
||||
}
|
||||
|
||||
@ManyToOne public Person getFather() {
|
||||
return father;
|
||||
}
|
||||
@ManyToOne
|
||||
public Person getFather() {
|
||||
return father;
|
||||
}
|
||||
|
||||
public void setFather(Person father) {
|
||||
this.father = father;
|
||||
}
|
||||
public void setFather(Person father) {
|
||||
this.father = father;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
}
|
||||
public void setCreated(Date created) {}
|
||||
|
||||
@PrePersist
|
||||
private void prePersist() {
|
||||
this.created = Calendar.getInstance().getTime();
|
||||
}
|
||||
@PrePersist
|
||||
private void prePersist() {
|
||||
this.created = Calendar.getInstance().getTime();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,20 +12,20 @@ import org.springframework.stereotype.Component;
|
||||
@Component
|
||||
public class PersonLoader implements InitializingBean {
|
||||
|
||||
@Autowired
|
||||
PersonRepository people;
|
||||
@Autowired PersonRepository people;
|
||||
|
||||
@Override public void afterPropertiesSet() throws Exception {
|
||||
Person billyBob = people.save(new Person("Billy Bob", "Thornton"));
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Person billyBob = people.save(new Person("Billy Bob", "Thornton"));
|
||||
|
||||
Person john = new Person("John", "Doe");
|
||||
Person jane = new Person("Jane", "Doe");
|
||||
john.addSibling(jane);
|
||||
john.setFather(billyBob);
|
||||
jane.addSibling(john);
|
||||
jane.setFather(billyBob);
|
||||
Person john = new Person("John", "Doe");
|
||||
Person jane = new Person("Jane", "Doe");
|
||||
john.addSibling(jane);
|
||||
john.setFather(billyBob);
|
||||
jane.addSibling(john);
|
||||
jane.setFather(billyBob);
|
||||
|
||||
people.save(Arrays.asList(john, jane));
|
||||
}
|
||||
people.save(Arrays.asList(john, jane));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,24 +13,21 @@ import org.springframework.data.rest.repository.annotation.RestResource;
|
||||
|
||||
/**
|
||||
* A repository to manage {@link Person}s.
|
||||
*
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@RestResource(rel = "people", path = "people")
|
||||
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
|
||||
|
||||
@RestResource(rel = "firstname", path = "firstname")
|
||||
public Page<Person> findByFirstName(@Param("firstName") String firstName, Pageable pageable);
|
||||
@RestResource(rel = "firstname", path = "firstname")
|
||||
public Page<Person> findByFirstName(@Param("firstName") String firstName, Pageable pageable);
|
||||
|
||||
public Person findFirstPersonByFirstName(@Param("firstName") String firstName);
|
||||
public Person findFirstPersonByFirstName(@Param("firstName") String firstName);
|
||||
|
||||
public Page<Person> findByCreatedGreaterThan(@Param("date") Date date, Pageable pageable);
|
||||
public Page<Person> findByCreatedGreaterThan(@Param("date") Date date, Pageable pageable);
|
||||
|
||||
@Query("select p from Person p where p.created > :date")
|
||||
public Page<Person> findByCreatedUsingISO8601Date(@Param("date")
|
||||
@ConvertWith(
|
||||
ISO8601DateConverter.class)
|
||||
Date date,
|
||||
Pageable pageable);
|
||||
@Query("select p from Person p where p.created > :date")
|
||||
public Page<Person> findByCreatedUsingISO8601Date(@Param("date") @ConvertWith(ISO8601DateConverter.class) Date date,
|
||||
Pageable pageable);
|
||||
|
||||
}
|
||||
|
||||
@@ -34,14 +34,14 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@Transactional
|
||||
public class PersistentEntitySerializationTests {
|
||||
|
||||
private static final String PERSON_JSON_IN = "{\"firstName\": \"John\",\"lastName\": \"Doe\"}";
|
||||
private static final String PERSON_JSON_IN = "{\"firstName\": \"John\",\"lastName\": \"Doe\"}";
|
||||
|
||||
@Autowired ObjectMapper mapper;
|
||||
@Autowired Repositories repositories;
|
||||
@Autowired PersonRepository people;
|
||||
|
||||
|
||||
LinkDiscoverer linkDiscoverer;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
linkDiscoverer = new DefaultLinkDiscoverer();
|
||||
@@ -49,9 +49,9 @@ public class PersistentEntitySerializationTests {
|
||||
|
||||
@Test
|
||||
public void deserializesPersonEntity() throws IOException {
|
||||
|
||||
|
||||
Person p = mapper.readValue(PERSON_JSON_IN, Person.class);
|
||||
|
||||
|
||||
assertThat(p.getFirstName(), is("John"));
|
||||
assertThat(p.getLastName(), is("Doe"));
|
||||
assertThat(p.getSiblings(), is(Collections.EMPTY_LIST));
|
||||
@@ -59,18 +59,18 @@ public class PersistentEntitySerializationTests {
|
||||
|
||||
@Test
|
||||
public void serializesPersonEntity() throws IOException, InterruptedException {
|
||||
|
||||
|
||||
PersistentEntity<?, ?> persistentEntity = repositories.getPersistentEntity(Person.class);
|
||||
Person person = people.save(new Person("John", "Doe"));
|
||||
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
mapper.writeValue(writer, PersistentEntityResource.wrap(persistentEntity, person));
|
||||
|
||||
|
||||
String s = writer.toString();
|
||||
|
||||
|
||||
Link fatherLink = linkDiscoverer.findLinkWithRel("people.people.father", s);
|
||||
assertThat(fatherLink.getHref(), endsWith(new UriTemplate("/{id}/father").expand(person.getId()).toString()));
|
||||
|
||||
|
||||
Link siblingLink = linkDiscoverer.findLinkWithRel("people.people.siblings", s);
|
||||
assertThat(siblingLink.getHref(), endsWith(new UriTemplate("/{id}/siblings").expand(person.getId()).toString()));
|
||||
}
|
||||
|
||||
@@ -24,59 +24,60 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@Configuration
|
||||
@Import({JpaRepositoryConfig.class})
|
||||
@Import({ JpaRepositoryConfig.class })
|
||||
@SuppressWarnings("deprecation")
|
||||
public class RepositoryTestsConfig {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext appCtx;
|
||||
@Autowired private ApplicationContext appCtx;
|
||||
|
||||
@Bean public Repositories repositories() {
|
||||
return new Repositories(appCtx);
|
||||
}
|
||||
@Bean
|
||||
public Repositories repositories() {
|
||||
return new Repositories(appCtx);
|
||||
}
|
||||
|
||||
@Bean public RepositoryRestConfiguration config() {
|
||||
RepositoryRestConfiguration config = new RepositoryRestConfiguration();
|
||||
@Bean
|
||||
public RepositoryRestConfiguration config() {
|
||||
RepositoryRestConfiguration config = new RepositoryRestConfiguration();
|
||||
|
||||
config.setResourceMappingForDomainType(Person.class)
|
||||
.setRel("person");
|
||||
config.setResourceMappingForDomainType(Person.class).setRel("person");
|
||||
|
||||
// config.setResourceMappingForRepository(ConfiguredPersonRepository.class)
|
||||
// .setRel("people")
|
||||
// .setPath("people")
|
||||
// .setExported(false);
|
||||
// config.setResourceMappingForRepository(ConfiguredPersonRepository.class)
|
||||
// .setRel("people")
|
||||
// .setPath("people")
|
||||
// .setExported(false);
|
||||
|
||||
config.setResourceMappingForRepository(PersonRepository.class)
|
||||
.setRel("people")
|
||||
.setPath("people")
|
||||
.addResourceMappingFor("findByFirstName")
|
||||
.setRel("firstname")
|
||||
.setPath("firstname");
|
||||
|
||||
config.setBaseUri(URI.create("http://localhost:8080"));
|
||||
config.setResourceMappingForRepository(PersonRepository.class).setRel("people").setPath("people")
|
||||
.addResourceMappingFor("findByFirstName").setRel("firstname").setPath("firstname");
|
||||
|
||||
return config;
|
||||
}
|
||||
config.setBaseUri(URI.create("http://localhost:8080"));
|
||||
|
||||
@Bean public DefaultFormattingConversionService defaultConversionService() {
|
||||
return new DefaultFormattingConversionService();
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
@Bean public DomainClassConverter<?> domainClassConverter() {
|
||||
return new DomainClassConverter<DefaultFormattingConversionService>(defaultConversionService());
|
||||
}
|
||||
@Bean
|
||||
public DefaultFormattingConversionService defaultConversionService() {
|
||||
return new DefaultFormattingConversionService();
|
||||
}
|
||||
|
||||
@Bean public UriDomainClassConverter uriDomainClassConverter() {
|
||||
return new UriDomainClassConverter();
|
||||
}
|
||||
@Bean
|
||||
public DomainClassConverter<?> domainClassConverter() {
|
||||
return new DomainClassConverter<DefaultFormattingConversionService>(defaultConversionService());
|
||||
}
|
||||
|
||||
@Bean public Module persistentEntityModule() {
|
||||
return new PersistentEntityJackson2Module(new ResourceMappings(config(), repositories()));
|
||||
}
|
||||
@Bean
|
||||
public UriDomainClassConverter uriDomainClassConverter() {
|
||||
return new UriDomainClassConverter();
|
||||
}
|
||||
|
||||
@Bean public ObjectMapper objectMapper() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.registerModule(persistentEntityModule());
|
||||
return mapper;
|
||||
}
|
||||
@Bean
|
||||
public Module persistentEntityModule() {
|
||||
return new PersistentEntityJackson2Module(new ResourceMappings(config(), repositories()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ObjectMapper objectMapper() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.registerModule(persistentEntityModule());
|
||||
return mapper;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,11 +34,13 @@ import org.springframework.data.mongodb.repository.config.EnableMongoRepositorie
|
||||
@EnableMongoRepositories
|
||||
public class MongoDbRepositoryConfig {
|
||||
|
||||
@Bean public MongoDbFactory mongoDbFactory() throws UnknownHostException {
|
||||
return new SimpleMongoDbFactory(new Mongo("localhost"), "spring-data-rest-example");
|
||||
}
|
||||
@Bean
|
||||
public MongoDbFactory mongoDbFactory() throws UnknownHostException {
|
||||
return new SimpleMongoDbFactory(new Mongo("localhost"), "spring-data-rest-example");
|
||||
}
|
||||
|
||||
@Bean public MongoTemplate mongoTemplate() throws UnknownHostException {
|
||||
return new MongoTemplate(mongoDbFactory());
|
||||
}
|
||||
@Bean
|
||||
public MongoTemplate mongoTemplate() throws UnknownHostException {
|
||||
return new MongoTemplate(mongoDbFactory());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,33 +29,32 @@ import org.springframework.hateoas.Link;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@ContextConfiguration(classes = MongoDbRepositoryConfig.class)
|
||||
public class MongoWebTests extends AbstractWebIntegrationTests {
|
||||
|
||||
@Autowired ProfileRepository repository;
|
||||
|
||||
|
||||
@Before
|
||||
public void populateProfiles() {
|
||||
|
||||
|
||||
Profile twitter = new Profile();
|
||||
twitter.setPerson(1L);
|
||||
twitter.setType("Twitter");
|
||||
|
||||
|
||||
Profile linkedIn = new Profile();
|
||||
linkedIn.setPerson(1L);
|
||||
linkedIn.setType("LinkedIn");
|
||||
|
||||
|
||||
repository.save(Arrays.asList(twitter, linkedIn));
|
||||
}
|
||||
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
repository.deleteAll();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.webmvc.AbstractWebIntegrationTests#expectedRootLinkRels()
|
||||
@@ -64,11 +63,11 @@ public class MongoWebTests extends AbstractWebIntegrationTests {
|
||||
protected Iterable<String> expectedRootLinkRels() {
|
||||
return Arrays.asList("profile");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void foo() throws Exception {
|
||||
|
||||
|
||||
Link profileLink = discoverUnique("profile");
|
||||
follow(profileLink).andExpect(jsonPath("$.content").value(hasSize(2)));
|
||||
follow(profileLink).andExpect(jsonPath("$.content").value(hasSize(2)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,36 +9,35 @@ import org.springframework.data.mongodb.core.mapping.Document;
|
||||
@Document
|
||||
public class Profile {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
private Long person;
|
||||
private String type;
|
||||
@Id private String id;
|
||||
private Long person;
|
||||
private String type;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Profile setId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
public Profile setId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Long getPerson() {
|
||||
return person;
|
||||
}
|
||||
public Long getPerson() {
|
||||
return person;
|
||||
}
|
||||
|
||||
public Profile setPerson(Long person) {
|
||||
this.person = person;
|
||||
return this;
|
||||
}
|
||||
public Profile setPerson(Long person) {
|
||||
this.person = person;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Profile setType(String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
public Profile setType(String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,5 +5,4 @@ import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public interface ProfileRepository extends PagingAndSortingRepository<Profile, String> {
|
||||
}
|
||||
public interface ProfileRepository extends PagingAndSortingRepository<Profile, String> {}
|
||||
|
||||
Reference in New Issue
Block a user