DATAREST-93 - Fixed formatting in Spring Data REST.

Added formatter to be used within Eclipse going forward.
This commit is contained in:
Oliver Gierke
2013-06-18 16:16:48 +02:00
parent eec52471d7
commit 5c61632ec2
162 changed files with 3775 additions and 3991 deletions

View File

@@ -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>>();

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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]);
}
}

View File

@@ -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();

View File

@@ -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() {

View File

@@ -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));
}
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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 {

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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 {
}

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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")

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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()));
}
}

View File

@@ -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());
}

View File

@@ -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;
}
}
}

View File

@@ -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
);
}

View File

@@ -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();
}

View File

@@ -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) {}
}
}

View File

@@ -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());
}
}

View File

@@ -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 {}

View File

@@ -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() {}
}

View File

@@ -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);
}
}
}

View File

@@ -36,7 +36,7 @@ public class RepositoryRestMvConfigurationIntegrationTests {
public static void setUp() {
context = new AnnotationConfigApplicationContext(ExtendingConfiguration.class);
}
@AfterClass
public static void tearDown() {
if (context != null) {

View File

@@ -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
*/

View File

@@ -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)

View File

@@ -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.

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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);

View File

@@ -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();
}
}
}

View File

@@ -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));
}
}

View File

@@ -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);
}

View File

@@ -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()));
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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)));
}
}

View File

@@ -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;
}
}

View File

@@ -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> {}