DATAREST-93 - Quite a lot of refactoring.
Integrated pagination resolving and closer integration of Spring Data Commons configuration.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,3 +7,4 @@ build/
|
||||
.project
|
||||
.settings
|
||||
*.log
|
||||
vf.gf.*
|
||||
@@ -10,7 +10,7 @@ ext {
|
||||
|
||||
// Spring
|
||||
springVersion = "3.2.3.RELEASE"
|
||||
hateoasVersion = "0.6.0.RELEASE"
|
||||
hateoasVersion = "0.7.0.BUILD-SNAPSHOT"
|
||||
springPluginVersion = "0.8.0.RELEASE"
|
||||
springSecurityVersion = "3.1.3.RELEASE"
|
||||
sdCommonsVersion = "1.6.0.BUILD-SNAPSHOT"
|
||||
@@ -67,11 +67,11 @@ configure(allprojects) {
|
||||
sourceSets.test.resources.srcDirs = ["src/test/resources", "src/test/java"]
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
maven { url "http://oss.sonatype.org/content/repositories/releases" }
|
||||
maven { url "http://m2.neo4j.org/releases" }
|
||||
mavenLocal()
|
||||
maven { url "http://repo.springsource.org/libs-snapshot" }
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.config;
|
||||
|
||||
import java.net.URI;
|
||||
@@ -10,7 +25,9 @@ import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class RepositoryRestConfiguration {
|
||||
|
||||
private URI baseUri = null;
|
||||
@@ -340,5 +357,4 @@ public class RepositoryRestConfiguration {
|
||||
Collections.addAll(exposeIdsFor, domainTypes);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.config;
|
||||
|
||||
import static org.springframework.data.rest.repository.support.ResourceMappingUtils.*;
|
||||
@@ -8,6 +23,7 @@ import java.util.Map;
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@Deprecated
|
||||
public class ResourceMapping {
|
||||
|
||||
private String rel;
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.config;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -9,6 +24,7 @@ import java.util.Map;
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ResourceMappingConfiguration {
|
||||
|
||||
private final Map<Class<?>, ResourceMapping> resourceMappings = new HashMap<Class<?>, ResourceMapping>();
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
package org.springframework.data.rest.repository;
|
||||
|
||||
import static org.springframework.data.rest.core.util.UriUtils.*;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Resource;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class BaseUriAwareResource<T> extends Resource<T> {
|
||||
|
||||
@JsonIgnore
|
||||
private URI baseUri;
|
||||
|
||||
public BaseUriAwareResource() {
|
||||
}
|
||||
|
||||
public BaseUriAwareResource(T content, Link... links) {
|
||||
super(content, links);
|
||||
}
|
||||
|
||||
public BaseUriAwareResource(T content, Iterable<Link> links) {
|
||||
super(content, links);
|
||||
}
|
||||
|
||||
public URI getBaseUri() {
|
||||
return baseUri;
|
||||
}
|
||||
|
||||
public BaseUriAwareResource<T> setBaseUri(URI baseUri) {
|
||||
this.baseUri = baseUri;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public List<Link> getLinks() {
|
||||
String baseUriStr = baseUri.toString();
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
for(Link l : super.getLinks()) {
|
||||
if(!l.getHref().startsWith(baseUriStr)
|
||||
&& !l.getHref().startsWith("http")) {
|
||||
links.add(new Link(buildUri(baseUri, l.getHref()).toString(), l.getRel()));
|
||||
} else {
|
||||
links.add(l);
|
||||
}
|
||||
}
|
||||
return links;
|
||||
}
|
||||
|
||||
@Override public Link getLink(String rel) {
|
||||
Link l = super.getLink(rel);
|
||||
if(null == l) {
|
||||
return null;
|
||||
}
|
||||
if(!l.getHref().startsWith(baseUri.toString())
|
||||
&& !l.getHref().startsWith("http")) {
|
||||
return new Link(buildUri(baseUri, l.getHref()).toString(), l.getRel());
|
||||
} else {
|
||||
return l;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package org.springframework.data.rest.repository;
|
||||
|
||||
import static org.springframework.data.rest.core.util.UriUtils.*;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.Resources;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class BaseUriAwareResources extends Resources<Resource<?>> {
|
||||
|
||||
@JsonIgnore
|
||||
private URI baseUri;
|
||||
|
||||
public BaseUriAwareResources() {
|
||||
}
|
||||
|
||||
public BaseUriAwareResources(Iterable<Resource<?>> content, Link... links) {
|
||||
super(content, links);
|
||||
}
|
||||
|
||||
public BaseUriAwareResources(Iterable<Resource<?>> content, Iterable<Link> links) {
|
||||
super(content, links);
|
||||
}
|
||||
|
||||
public URI getBaseUri() {
|
||||
return baseUri;
|
||||
}
|
||||
|
||||
public BaseUriAwareResources setBaseUri(URI baseUri) {
|
||||
this.baseUri = baseUri;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public Collection<Resource<?>> getContent() {
|
||||
List<Resource<?>> resources = new ArrayList<Resource<?>>();
|
||||
for(Resource<?> resource : super.getContent()) {
|
||||
if(resource instanceof BaseUriAwareResource) {
|
||||
resources.add(((BaseUriAwareResource<?>) resource).setBaseUri(baseUri));
|
||||
} else {
|
||||
resources.add(new BaseUriAwareResource<Object>(resource.getContent(), resource.getLinks()).setBaseUri(baseUri));
|
||||
}
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
|
||||
@Override public Iterator<Resource<?>> iterator() {
|
||||
return getContent().iterator();
|
||||
}
|
||||
|
||||
@Override public List<Link> getLinks() {
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
for(Link l : super.getLinks()) {
|
||||
links.add(new Link(buildUri(baseUri, l.getHref()).toString(), l.getRel()));
|
||||
}
|
||||
return links;
|
||||
}
|
||||
|
||||
@Override public Link getLink(String rel) {
|
||||
Link l = super.getLink(rel);
|
||||
return new Link(buildUri(baseUri, l.getHref()).toString(), l.getRel());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package org.springframework.data.rest.repository;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* Implementation of {@link Pageable} that is URL-aware.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class PagingAndSorting implements Pageable {
|
||||
|
||||
private final RepositoryRestConfiguration config;
|
||||
private final PageRequest pageRequest;
|
||||
|
||||
public PagingAndSorting(RepositoryRestConfiguration config,
|
||||
PageRequest pageRequest) {
|
||||
this.config = config;
|
||||
this.pageRequest = pageRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the current sort parameters to the URI.
|
||||
*
|
||||
* @param urib
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public PagingAndSorting addSortParameters(UriComponentsBuilder urib) {
|
||||
Sort sort = pageRequest.getSort();
|
||||
if(null != sort) {
|
||||
Iterator<Sort.Order> iter = sort.iterator();
|
||||
while(iter.hasNext()) {
|
||||
Sort.Order order = iter.next();
|
||||
urib.queryParam(config.getSortParamName(), order.getProperty());
|
||||
try {
|
||||
urib.queryParam(URLEncoder.encode(order.getProperty() + ".dir", "ISO-8859-1"),
|
||||
order.getDirection().toString().toLowerCase());
|
||||
} catch(UnsupportedEncodingException ignored) {
|
||||
// this should never happen
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public int getPageNumber() {
|
||||
return pageRequest.getPageNumber();
|
||||
}
|
||||
|
||||
@Override public int getPageSize() {
|
||||
return pageRequest.getPageSize();
|
||||
}
|
||||
|
||||
@Override public int getOffset() {
|
||||
return pageRequest.getOffset();
|
||||
}
|
||||
|
||||
@Override public Sort getSort() {
|
||||
return pageRequest.getSort();
|
||||
}
|
||||
|
||||
@Override public Pageable first() {
|
||||
return pageRequest.first();
|
||||
}
|
||||
|
||||
@Override public Pageable next() {
|
||||
return pageRequest.next();
|
||||
}
|
||||
|
||||
@Override public boolean hasPrevious() {
|
||||
return pageRequest.hasPrevious();
|
||||
}
|
||||
|
||||
@Override public Pageable previousOrFirst() {
|
||||
return pageRequest.previousOrFirst();
|
||||
}
|
||||
}
|
||||
@@ -1,50 +1,51 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Resource;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
/**
|
||||
* A Spring HATEOAS {@link Resource} subclass that holds a reference to the entity's {@link PersistentEntity} metadata.
|
||||
*
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class PersistentEntityResource<T> extends BaseUriAwareResource<T> {
|
||||
public class PersistentEntityResource<T> extends Resource<T> {
|
||||
|
||||
@JsonIgnore
|
||||
private final PersistentEntity<?, ?> persistentEntity;
|
||||
@JsonIgnore private final PersistentEntity<?, ?> entity;
|
||||
|
||||
public static <T> PersistentEntityResource<T> wrap(PersistentEntity<?, ?> persistentEntity,
|
||||
T obj,
|
||||
URI baseUri) {
|
||||
PersistentEntityResource<T> resource = new PersistentEntityResource<T>(persistentEntity, obj);
|
||||
resource.setBaseUri(baseUri);
|
||||
return resource;
|
||||
}
|
||||
public static <T> PersistentEntityResource<T> wrap(PersistentEntity<?, ?> entity, T obj) {
|
||||
return new PersistentEntityResource<T>(entity, obj);
|
||||
}
|
||||
|
||||
public PersistentEntityResource(PersistentEntity<?, ?> persistentEntity) {
|
||||
this.persistentEntity = persistentEntity;
|
||||
}
|
||||
public PersistentEntityResource(PersistentEntity<?, ?> entity, T content, Link... links) {
|
||||
this(entity, content, Arrays.asList(links));
|
||||
}
|
||||
|
||||
public PersistentEntityResource(PersistentEntity<?, ?> persistentEntity,
|
||||
T content,
|
||||
Link... links) {
|
||||
super(content, links);
|
||||
this.persistentEntity = persistentEntity;
|
||||
}
|
||||
|
||||
public PersistentEntityResource(PersistentEntity<?, ?> persistentEntity,
|
||||
T content,
|
||||
Iterable<Link> links) {
|
||||
super(content, links);
|
||||
this.persistentEntity = persistentEntity;
|
||||
}
|
||||
|
||||
public PersistentEntity<?, ?> getPersistentEntity() {
|
||||
return persistentEntity;
|
||||
}
|
||||
private PersistentEntityResource(PersistentEntity<?, ?> entity, T content, Iterable<Link> links) {
|
||||
super(content, links);
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
public PersistentEntity<?, ?> getPersistentEntity() {
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package org.springframework.data.rest.repository.context;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class RepositoriesFactoryBean implements FactoryBean<Repositories>,
|
||||
ApplicationContextAware {
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override public Repositories getObject() throws Exception {
|
||||
return new Repositories(applicationContext);
|
||||
}
|
||||
|
||||
@Override public Class<?> getObjectType() {
|
||||
return Repositories.class;
|
||||
}
|
||||
|
||||
@Override public boolean isSingleton() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
package org.springframework.data.rest.repository.invoke;
|
||||
|
||||
import static org.springframework.util.ReflectionUtils.doWithMethods;
|
||||
import static org.springframework.util.ReflectionUtils.invokeMethod;
|
||||
import static org.springframework.util.ReflectionUtils.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
@@ -19,10 +21,13 @@ import org.springframework.util.ReflectionUtils.MethodCallback;
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class RepositoryMethodInvoker implements PagingAndSortingRepository<Object, Serializable> {
|
||||
|
||||
private final Object repository;
|
||||
private final Map<String, RepositoryMethod> queryMethods = new HashMap<String, RepositoryMethod>();
|
||||
private final ConversionService conversionService;
|
||||
|
||||
private RepositoryMethod saveOne;
|
||||
private RepositoryMethod saveSome;
|
||||
private RepositoryMethod findOne;
|
||||
@@ -38,8 +43,11 @@ public class RepositoryMethodInvoker implements PagingAndSortingRepository<Objec
|
||||
private RepositoryMethod deleteAll;
|
||||
|
||||
public RepositoryMethodInvoker(Object repository,
|
||||
RepositoryInformation repoInfo) {
|
||||
RepositoryInformation repoInfo,
|
||||
ConversionService conversionService) {
|
||||
|
||||
this.repository = repository;
|
||||
this.conversionService = conversionService;
|
||||
Class<?> repoType = repoInfo.getRepositoryInterface();
|
||||
|
||||
doWithMethods(repoType, new MethodCallback() {
|
||||
@@ -219,5 +227,49 @@ public class RepositoryMethodInvoker implements PagingAndSortingRepository<Objec
|
||||
public Object invokeQueryMethod(RepositoryMethod method, Object... params) {
|
||||
return invokeMethod(method.getMethod(), repository, params);
|
||||
}
|
||||
|
||||
public Object invokeQueryMethod(RepositoryMethod method, Pageable pageable, Map<String, String[]> rawParameters) {
|
||||
return invokeQueryMethod(method, foo(method, pageable, rawParameters));
|
||||
}
|
||||
|
||||
private Object[] foo(RepositoryMethod repoMethod, Pageable pageable, Map<String, String[]> rawParameters) {
|
||||
|
||||
List<MethodParameter> methodParams = repoMethod.getParameters();
|
||||
|
||||
if (methodParams.isEmpty()) {
|
||||
return new Object[0];
|
||||
}
|
||||
|
||||
Object[] paramValues = new Object[methodParams.size()];
|
||||
|
||||
|
||||
for(int i = 0; i < paramValues.length; i++) {
|
||||
MethodParameter param = methodParams.get(i);
|
||||
Class<?> targetType = param.getParameterType();
|
||||
if(Pageable.class.isAssignableFrom(targetType)) {
|
||||
paramValues[i] = pageable;
|
||||
} else if(Sort.class.isAssignableFrom(targetType)) {
|
||||
paramValues[i] = pageable.getSort();
|
||||
} else {
|
||||
String paramName = repoMethod.getParameterNames().get(i);
|
||||
String[] queryParamVals = rawParameters.get(paramName);
|
||||
if(null == queryParamVals) {
|
||||
if(paramName.startsWith("arg")) {
|
||||
throw new IllegalArgumentException("No @Param annotation found on query method "
|
||||
+ repoMethod.getMethod().getName()
|
||||
+ " for parameter " + param.getParameterName());
|
||||
} else {
|
||||
throw new IllegalArgumentException("No query parameter specified for "
|
||||
+ repoMethod.getMethod().getName() + " param '"
|
||||
+ paramName + "'");
|
||||
}
|
||||
}
|
||||
paramValues[i] = conversionService.convert(queryParamVals, targetType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return paramValues;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.mapping;
|
||||
|
||||
/**
|
||||
* A custom resource mapping for collection resources.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface CollectionResourceMapping extends ResourceMapping {
|
||||
|
||||
String getSingleResourceRel();
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.mapping;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
class CollectionResourceMappingBuilder implements InternalMappingBuilder {
|
||||
|
||||
private final CollectionResourceMapping mapping;
|
||||
|
||||
public CollectionResourceMappingBuilder(CollectionResourceMapping mapping) {
|
||||
this.mapping = mapping;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.InternalMappingBuilder#withCollectionRel(java.lang.String)
|
||||
*/
|
||||
public CollectionResourceMappingBuilder withCollectionRel(String rel) {
|
||||
|
||||
SimpleCollectionResourceMapping newMapping = new SimpleCollectionResourceMapping(rel != null ? rel : mapping.getRel(),
|
||||
mapping.getSingleResourceRel(), mapping.getPath(), mapping.isExported());
|
||||
|
||||
return new CollectionResourceMappingBuilder(newMapping);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.InternalMappingBuilder#withSingleRel(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public CollectionResourceMappingBuilder withSingleRel(String rel) {
|
||||
|
||||
SimpleCollectionResourceMapping newMapping = new SimpleCollectionResourceMapping(mapping.getRel(),
|
||||
rel != null ? rel : mapping.getSingleResourceRel(), mapping.getPath(), mapping.isExported());
|
||||
|
||||
return new CollectionResourceMappingBuilder(newMapping);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.InternalMappingBuilder#withPath(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public CollectionResourceMappingBuilder withPath(String path) {
|
||||
|
||||
SimpleCollectionResourceMapping newMapping = new SimpleCollectionResourceMapping(mapping.getRel(),
|
||||
mapping.getSingleResourceRel(), path != null ? path : mapping.getPath(), mapping.isExported());
|
||||
|
||||
return new CollectionResourceMappingBuilder(newMapping);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.InternalMappingBuilder#withExposed(java.lang.Boolean)
|
||||
*/
|
||||
@Override
|
||||
public CollectionResourceMappingBuilder withExposed(Boolean exported) {
|
||||
|
||||
SimpleCollectionResourceMapping newMapping = new SimpleCollectionResourceMapping(mapping.getRel(),
|
||||
mapping.getSingleResourceRel(), mapping.getPath(), exported != null ? exported : mapping.isExported());
|
||||
|
||||
return new CollectionResourceMappingBuilder(newMapping);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.InternalMappingBuilder#merge(org.springframework.data.rest.repository.mapping.CollectionResourceMapping)
|
||||
*/
|
||||
@Override
|
||||
public InternalMappingBuilder merge(CollectionResourceMapping mapping) {
|
||||
|
||||
if (mapping == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return withCollectionRel(mapping.getRel()). //
|
||||
withSingleRel(mapping.getSingleResourceRel()). //
|
||||
withPath(mapping.getPath()). //
|
||||
withExposed(mapping.isExported());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.InternalMappingBuilder#getMapping()
|
||||
*/
|
||||
@Override
|
||||
public CollectionResourceMapping getMapping() {
|
||||
|
||||
Assert.hasText(mapping.getRel(), "Rel must not be null or empty!");
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the exported
|
||||
*/
|
||||
public Boolean isExported() {
|
||||
return mapping.isExported();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.mapping;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
interface InternalMappingBuilder extends MappingBuilder {
|
||||
|
||||
InternalMappingBuilder merge(CollectionResourceMapping mapping);
|
||||
|
||||
CollectionResourceMapping getMapping();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.mapping;
|
||||
|
||||
/**
|
||||
* SPI to allow users to register
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface MappingBuilder {
|
||||
|
||||
MappingBuilder withCollectionRel(String rel);
|
||||
|
||||
MappingBuilder withSingleRel(String rel);
|
||||
|
||||
MappingBuilder withPath(String path);
|
||||
|
||||
MappingBuilder withExposed(Boolean exposed);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.mapping;
|
||||
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class RepositoryAwareResourceInformation implements ResourceMetadata {
|
||||
|
||||
private final Repositories repositories;
|
||||
private final CollectionResourceMapping mapping;
|
||||
private final ResourceMetadataProvider provider;
|
||||
private final RepositoryInformation repositoryInterface;
|
||||
|
||||
/**
|
||||
* @param repositories must not be {@literal null}.
|
||||
* @param mapping must not be {@literal null}.
|
||||
* @param provider must not be {@literal null}.
|
||||
*/
|
||||
public RepositoryAwareResourceInformation(Repositories repositories, CollectionResourceMapping mapping,
|
||||
ResourceMetadataProvider provider, RepositoryInformation repositoryInterface) {
|
||||
|
||||
Assert.notNull(repositories, "Repositories must not be null!");
|
||||
Assert.notNull(mapping, "ResourceMapping must not be null!");
|
||||
Assert.notNull(provider, "ResourceMetadataProvider must not be null!");
|
||||
|
||||
this.repositories = repositories;
|
||||
this.mapping = mapping;
|
||||
this.provider = provider;
|
||||
this.repositoryInterface = repositoryInterface;
|
||||
}
|
||||
|
||||
public boolean isPrimary() {
|
||||
return AnnotationUtils.findAnnotation(repositoryInterface.getRepositoryInterface(), Primary.class) != null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.DelegatingResourceInformation#isManaged(org.springframework.data.mapping.PersistentProperty)
|
||||
*/
|
||||
@Override
|
||||
public boolean isManaged(PersistentProperty<?> property) {
|
||||
return repositories.hasRepositoryFor(property.getActualType());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.ResourceMetadataProvider#getMappingFor(org.springframework.data.mapping.PersistentProperty)
|
||||
*/
|
||||
@Override
|
||||
public ResourceMapping getMappingFor(PersistentProperty<?> property) {
|
||||
return provider.getMappingFor(property);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.ResourceMetadataProvider#hasMappingFor(org.springframework.data.mapping.PersistentProperty)
|
||||
*/
|
||||
@Override
|
||||
public boolean isMapped(PersistentProperty<?> property) {
|
||||
return provider.isMapped(property);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#isExported()
|
||||
*/
|
||||
@Override
|
||||
public Boolean isExported() {
|
||||
return mapping.isExported();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getCollectionRel()
|
||||
*/
|
||||
@Override
|
||||
public String getRel() {
|
||||
return mapping.getRel();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getSingleResourceRel()
|
||||
*/
|
||||
@Override
|
||||
public String getSingleResourceRel() {
|
||||
return mapping.getSingleResourceRel();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getPath()
|
||||
*/
|
||||
@Override
|
||||
public String getPath() {
|
||||
return mapping.getPath();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.mapping;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.rest.repository.annotation.RestResource;
|
||||
|
||||
/**
|
||||
* A {@link RepositoryMethodResourceMapping} created from a {@link Method}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class RepositoryMethodResourceMapping implements ResourceMapping {
|
||||
|
||||
private final boolean isExported;
|
||||
private final String rel;
|
||||
private final String path;
|
||||
|
||||
/**
|
||||
* Creates a new {@link RepositoryMethodResourceMapping} for the given {@link Method}.
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
*/
|
||||
public RepositoryMethodResourceMapping(Method method) {
|
||||
|
||||
RestResource annotation = AnnotationUtils.findAnnotation(method, RestResource.class);
|
||||
|
||||
this.isExported = annotation != null ? annotation.exported() : true;
|
||||
this.rel = annotation != null ? annotation.rel() : method.getName();
|
||||
this.path = annotation != null ? annotation.path() : method.getName();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.ResourceMapping#isExported()
|
||||
*/
|
||||
@Override
|
||||
public Boolean isExported() {
|
||||
return isExported;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.ResourceMapping#getRel()
|
||||
*/
|
||||
@Override
|
||||
public String getRel() {
|
||||
return rel;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.ResourceMapping#getPath()
|
||||
*/
|
||||
@Override
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.mapping;
|
||||
|
||||
/**
|
||||
* Mapping information for components to be exported as REST resources.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface ResourceMapping {
|
||||
|
||||
public static ResourceMapping NO_MAPPING = new ResourceMapping() {
|
||||
|
||||
@Override
|
||||
public Boolean isExported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether the component shall be exported at all.
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
Boolean isExported();
|
||||
|
||||
/**
|
||||
* Returns the relation for the resource exported.
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
String getRel();
|
||||
|
||||
/**
|
||||
* Returns the path the resource is exposed under.
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
String getPath();
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.mapping;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.rest.repository.annotation.RestResource;
|
||||
import org.springframework.data.rest.repository.support.RepositoriesUtils;
|
||||
import org.springframework.hateoas.RelProvider;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ResourceMappingFactory {
|
||||
|
||||
private final RelProvider relProvider;
|
||||
|
||||
public ResourceMappingFactory(RelProvider relProvider) {
|
||||
this.relProvider = relProvider;
|
||||
}
|
||||
|
||||
public CollectionResourceMapping getMappingForType(Class<?> type) {
|
||||
return getMappingForType(type, new CollectionResourceMapping[0]);
|
||||
}
|
||||
|
||||
public CollectionResourceMapping getMappingForType(Class<?> type, CollectionResourceMapping... manualMapping) {
|
||||
|
||||
Class<?> typeToInspect = getTypeToInspect(type);
|
||||
|
||||
InternalMappingBuilder mapping = getBaseMetadata(typeToInspect). //
|
||||
merge(discoverConfig(typeToInspect));
|
||||
|
||||
if (type != typeToInspect) {
|
||||
mapping = mapping.merge(discoverConfig(type));
|
||||
}
|
||||
|
||||
for (CollectionResourceMapping externalMapping : manualMapping) {
|
||||
mapping = mapping.merge(externalMapping);
|
||||
}
|
||||
|
||||
return mapping.getMapping();
|
||||
}
|
||||
|
||||
private static Class<?> getTypeToInspect(Class<?> type) {
|
||||
|
||||
if (!RepositoriesUtils.isRepositoryInterface(type)) {
|
||||
return type;
|
||||
}
|
||||
|
||||
return RepositoriesUtils.getDomainType(type);
|
||||
}
|
||||
|
||||
private InternalMappingBuilder getBaseMetadata(Class<?> domainType) {
|
||||
|
||||
String path = StringUtils.uncapitalize(domainType.getSimpleName());
|
||||
String defaultCollectionRel = relProvider.getCollectionResourceRelFor(domainType);
|
||||
String defaultSingleRel = relProvider.getSingleResourceRelFor(domainType);
|
||||
|
||||
CollectionResourceMapping mapping = new SimpleCollectionResourceMapping(defaultCollectionRel, defaultSingleRel, path, true);
|
||||
|
||||
return new CollectionResourceMappingBuilder(mapping);
|
||||
}
|
||||
|
||||
private static final CollectionResourceMapping discoverConfig(Class<?> type) {
|
||||
|
||||
RestResource resource = AnnotationUtils.findAnnotation(type, RestResource.class);
|
||||
|
||||
if (resource == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new AnnotationResourceMapping(resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link CollectionResourceMapping} based on an {@link RestResource} annotation.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class AnnotationResourceMapping implements CollectionResourceMapping {
|
||||
|
||||
private final RestResource annotation;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AnnotationResourceMapping} for the given {@link RestResource}.
|
||||
*
|
||||
* @param annotation must not be {@literal null}.
|
||||
*/
|
||||
public AnnotationResourceMapping(RestResource annotation) {
|
||||
Assert.notNull(annotation, "Annotation must not be null!");
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#isExported()
|
||||
*/
|
||||
@Override
|
||||
public Boolean isExported() {
|
||||
return annotation.exported();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getCollectionRel()
|
||||
*/
|
||||
@Override
|
||||
public String getRel() {
|
||||
return StringUtils.hasText(annotation.rel()) ? annotation.rel() : null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getSingleResourceRel()
|
||||
*/
|
||||
@Override
|
||||
public String getSingleResourceRel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getPath()
|
||||
*/
|
||||
@Override
|
||||
public String getPath() {
|
||||
return StringUtils.hasText(annotation.path()) ? annotation.path() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.mapping;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.repository.support.RepositoriesUtils;
|
||||
import org.springframework.data.rest.repository.support.SimpleRelProvider;
|
||||
import org.springframework.hateoas.RelProvider;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Central abstraction obtain {@link ResourceMetadata} and {@link ResourceMapping} instances for domain types and
|
||||
* repositories.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ResourceMappings implements ResourceMetadataProvider, Iterable<ResourceMetadata> {
|
||||
|
||||
private final RepositoryRestConfiguration config;
|
||||
private final ResourceMappingFactory factory;
|
||||
private final Repositories repositories;
|
||||
|
||||
private final Map<Class<?>, ResourceMetadata> cache = new HashMap<Class<?>, ResourceMetadata>();
|
||||
private final Map<Class<?>, Map<String, ResourceMapping>> searchCache = new HashMap<Class<?>, Map<String, ResourceMapping>>();
|
||||
|
||||
/**
|
||||
* Creates a new {@link ResourceMappings} using the given {@link RepositoryRestConfiguration} and {@link Repositories}
|
||||
* .
|
||||
*
|
||||
* @param config
|
||||
* @param repositories
|
||||
*/
|
||||
public ResourceMappings(RepositoryRestConfiguration config, Repositories repositories) {
|
||||
this(config, repositories, new SimpleRelProvider());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ResourceMappings} from the given {@link RepositoryRestConfiguration}, {@link Repositories} and
|
||||
* {@link RelProvider}.
|
||||
*
|
||||
* @param config must not be {@literal null}.
|
||||
* @param repositories must not be {@literal null}.
|
||||
* @param relProvider must not be {@literal null}.
|
||||
*/
|
||||
public ResourceMappings(RepositoryRestConfiguration config, Repositories repositories, RelProvider relProvider) {
|
||||
|
||||
Assert.notNull(config, "RepositoryRestConfiguration must not be null!");
|
||||
Assert.notNull(repositories, "Repositories must not be null!");
|
||||
Assert.notNull(relProvider, "RelProvider must not be null!");
|
||||
|
||||
this.config = config;
|
||||
this.repositories = repositories;
|
||||
this.factory = new ResourceMappingFactory(relProvider);
|
||||
|
||||
this.populateCache(repositories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link ResourceMetadata} for the given type if available.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public ResourceMetadata getMappingFor(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Type must not be null!");
|
||||
return cache.get(type);
|
||||
}
|
||||
|
||||
private final void populateCache(Repositories repositories) {
|
||||
|
||||
for (Class<?> type : repositories) {
|
||||
|
||||
RepositoryInformation repositoryInformation = repositories.getRepositoryInformationFor(type);
|
||||
Class<?> repositoryInterface = repositoryInformation.getRepositoryInterface();
|
||||
|
||||
CollectionResourceMapping mapping = factory.getMappingForType(repositoryInterface, fromConfig(type),
|
||||
fromConfig(repositoryInterface));
|
||||
|
||||
RepositoryAwareResourceInformation information = new RepositoryAwareResourceInformation(repositories, mapping,
|
||||
this, repositoryInformation);
|
||||
|
||||
cache.put(repositoryInterface, information);
|
||||
|
||||
if (!cache.containsKey(type) || information.isPrimary()) {
|
||||
cache.put(type, information);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ResourceMapping}s for the search resources of the given type.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public Map<String, ResourceMapping> getSearchResourceMappings(Class<?> type) {
|
||||
|
||||
if (searchCache.containsKey(type)) {
|
||||
return searchCache.get(type);
|
||||
}
|
||||
|
||||
Class<?> domainType = RepositoriesUtils.getDomainType(type);
|
||||
RepositoryInformation repositoryInformation = repositories.getRepositoryInformationFor(domainType);
|
||||
Map<String, ResourceMapping> mappings = new HashMap<String, ResourceMapping>();
|
||||
|
||||
for (Method queryMethod : repositoryInformation.getQueryMethods()) {
|
||||
mappings.put(queryMethod.getName(), new RepositoryMethodResourceMapping(queryMethod));
|
||||
}
|
||||
|
||||
searchCache.put(type, mappings);
|
||||
return mappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether we have a {@link ResourceMapping} for the given type and it is exported.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public boolean exportsMappingFor(Class<?> type) {
|
||||
|
||||
if (!hasMappingFor(type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResourceMetadata metadata = getMappingFor(type);
|
||||
return metadata.isExported();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether we have a {@link ResourceMapping} for the given type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public boolean hasMappingFor(Class<?> type) {
|
||||
|
||||
if (cache.containsKey(type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (repositories.hasRepositoryFor(type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (RepositoriesUtils.isRepositoryInterface(type) && hasMappingFor(RepositoriesUtils.getDomainType(type))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.ResourceMetadataProvider#getMappingFor(org.springframework.data.mapping.PersistentProperty)
|
||||
*/
|
||||
@Override
|
||||
public ResourceMapping getMappingFor(PersistentProperty<?> property) {
|
||||
return getMappingFor(property.getActualType());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.ResourceMetadataProvider#hasMappingFor(org.springframework.data.mapping.PersistentProperty)
|
||||
*/
|
||||
@Override
|
||||
public boolean isMapped(PersistentProperty<?> property) {
|
||||
|
||||
ResourceMapping metadata = getMappingFor(property);
|
||||
return metadata != null && metadata.isExported();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
@Override
|
||||
public Iterator<ResourceMetadata> iterator() {
|
||||
return cache.values().iterator();
|
||||
}
|
||||
|
||||
private CollectionResourceMapping fromConfig(Class<?> domainType) {
|
||||
|
||||
org.springframework.data.rest.config.ResourceMapping mapping = config.getResourceMappingForDomainType(domainType);
|
||||
|
||||
if (mapping == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SimpleCollectionResourceMapping(mapping.getRel(), null, mapping.getPath(), mapping.isExported());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.mapping;
|
||||
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface ResourceMetadata extends CollectionResourceMapping, ResourceMetadataProvider {
|
||||
|
||||
boolean isManaged(PersistentProperty<?> property);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.mapping;
|
||||
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface ResourceMetadataProvider {
|
||||
|
||||
boolean isMapped(PersistentProperty<?> property);
|
||||
|
||||
ResourceMapping getMappingFor(PersistentProperty<?> property);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.mapping;
|
||||
|
||||
|
||||
public class SimpleCollectionResourceMapping implements CollectionResourceMapping {
|
||||
|
||||
private final String collectionRel;
|
||||
private final String singleRel;
|
||||
private final String path;
|
||||
private final Boolean exported;
|
||||
|
||||
public SimpleCollectionResourceMapping(String relsAndPath) {
|
||||
this(relsAndPath, relsAndPath, relsAndPath, true);
|
||||
}
|
||||
|
||||
public SimpleCollectionResourceMapping(String collectionRel, String singleRel, String path, Boolean exported) {
|
||||
|
||||
this.collectionRel = collectionRel;
|
||||
this.singleRel = singleRel;
|
||||
this.path = path;
|
||||
this.exported = exported;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getCollectionRel()
|
||||
*/
|
||||
public String getRel() {
|
||||
return collectionRel;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getSingleResourceRel()
|
||||
*/
|
||||
public String getSingleResourceRel() {
|
||||
return singleRel;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getPath()
|
||||
*/
|
||||
@Override
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the exported
|
||||
*/
|
||||
public Boolean isExported() {
|
||||
return exported;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.support;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.RepositoryDefinition;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.AnnotationRepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class RepositoriesUtils {
|
||||
|
||||
public static Class<?> getDomainType(Class<?> repositoryType) {
|
||||
|
||||
if (!isRepositoryInterface(repositoryType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getMetadataFor(repositoryType).getDomainType();
|
||||
}
|
||||
|
||||
public static boolean isRepositoryInterface(Class<?> type) {
|
||||
return Repository.class.isAssignableFrom(type)
|
||||
|| AnnotationUtils.findAnnotation(type, RepositoryDefinition.class) != null;
|
||||
}
|
||||
|
||||
private static RepositoryMetadata getMetadataFor(Class<?> type) {
|
||||
return Repository.class.isAssignableFrom(type) ? new DefaultRepositoryMetadata(type)
|
||||
: new AnnotationRepositoryMetadata(type);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import org.springframework.util.MultiValueMap;
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public abstract class RepositoryInformationSupport {
|
||||
|
||||
protected Repositories repositories;
|
||||
@@ -57,7 +58,7 @@ public abstract class RepositoryInformationSupport {
|
||||
}
|
||||
for(Class<?> domainType : repositories) {
|
||||
RepositoryInformation repoInfo = findRepositoryInfoFor(domainType);
|
||||
ResourceMapping mapping = getResourceMapping(config, repoInfo);
|
||||
ResourceMapping mapping = getResourceMapping(config, repoInfo);
|
||||
if(pathSegment.equals(mapping.getPath()) && mapping.isExported()) {
|
||||
return repoInfo;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.support;
|
||||
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappings;
|
||||
import org.springframework.hateoas.RelProvider;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class RepositoryRelProvider implements RelProvider {
|
||||
|
||||
private final ResourceMappings mappings;
|
||||
|
||||
/**
|
||||
* @param repositories
|
||||
* @param config
|
||||
*/
|
||||
public RepositoryRelProvider(ResourceMappings mappings) {
|
||||
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.RelProvider#getCollectionResourceRelFor(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public String getCollectionResourceRelFor(Class<?> type) {
|
||||
return mappings.getMappingFor(type).getRel();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.RelProvider#getSingleResourceRelFor(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public String getSingleResourceRelFor(Class<?> type) {
|
||||
return mappings.getMappingFor(type).getSingleResourceRel();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> delimiter) {
|
||||
return mappings.hasMappingFor(delimiter);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import org.springframework.data.rest.repository.annotation.RestResource;
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class ResourceMappingUtils {
|
||||
|
||||
protected ResourceMappingUtils() {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.support;
|
||||
|
||||
import org.springframework.hateoas.RelProvider;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class SimpleRelProvider implements RelProvider {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> delimiter) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.RelProvider#getSingleResourceRelFor(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public String getSingleResourceRelFor(Class<?> type) {
|
||||
String collectionRel = getCollectionResourceRelFor(type);
|
||||
return String.format("%s.%s", collectionRel, collectionRel);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.RelProvider#getCollectionResourceRelFor(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public String getCollectionResourceRelFor(Class<?> type) {
|
||||
return StringUtils.uncapitalize(type.getSimpleName());
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,13 @@ package org.springframework.data.rest.config;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.springframework.data.rest.repository.support.ResourceMappingUtils.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.rest.repository.domain.jpa.AnnotatedPersonRepository;
|
||||
import org.springframework.data.rest.repository.domain.jpa.PersonRepository;
|
||||
import org.springframework.data.rest.repository.domain.jpa.PlainPersonRepository;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMapping;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappingFactory;
|
||||
import org.springframework.data.rest.repository.support.SimpleRelProvider;
|
||||
|
||||
/**
|
||||
* Ensure the {@link ResourceMapping} components convey the correct information.
|
||||
@@ -18,46 +16,26 @@ import org.springframework.data.rest.repository.domain.jpa.PlainPersonRepository
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class ResourceMappingUnitTests {
|
||||
|
||||
ResourceMappingFactory factory = new ResourceMappingFactory(new SimpleRelProvider());
|
||||
|
||||
@Test
|
||||
public void shouldDetectDefaultRelAndPath() throws Exception {
|
||||
ResourceMapping mapping = new ResourceMapping(
|
||||
findRel(PlainPersonRepository.class),
|
||||
findPath(PlainPersonRepository.class),
|
||||
findExported(PlainPersonRepository.class)
|
||||
);
|
||||
|
||||
assertThat(mapping.getRel(), is("plainPerson"));
|
||||
assertThat(mapping.getPath(), is("plainPerson"));
|
||||
assertThat(mapping.isExported(), is(true));
|
||||
|
||||
ResourceMapping newMapping = factory.getMappingForType(PlainPersonRepository.class);
|
||||
|
||||
assertThat(newMapping.getRel(), is("person"));
|
||||
assertThat(newMapping.getPath(), is("person"));
|
||||
assertThat(newMapping.isExported(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDetectAnnotatedRelAndPath() throws Exception {
|
||||
ResourceMapping mapping = new ResourceMapping(
|
||||
findRel(AnnotatedPersonRepository.class),
|
||||
findPath(AnnotatedPersonRepository.class),
|
||||
findExported(AnnotatedPersonRepository.class)
|
||||
);
|
||||
|
||||
assertThat(mapping.getRel(), is("people"));
|
||||
// The path is not set on the annotation so this should be the default from class name.
|
||||
assertThat(mapping.getPath(), is("annotatedPerson"));
|
||||
assertThat(mapping.isExported(), is(false));
|
||||
|
||||
ResourceMapping newMapping = factory.getMappingForType(AnnotatedPersonRepository.class);
|
||||
|
||||
assertThat(newMapping.getRel(), is("people"));
|
||||
assertThat(newMapping.getPath(), is("person"));
|
||||
assertThat(newMapping.isExported(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDetectAnnotatedRelAndPathOnMethod() throws Exception {
|
||||
Method method = PersonRepository.class.getMethod("findByFirstName", String.class, Pageable.class);
|
||||
ResourceMapping mapping = new ResourceMapping(
|
||||
findRel(method),
|
||||
findPath(method),
|
||||
findExported(method)
|
||||
);
|
||||
|
||||
assertThat(mapping.getRel(), is("firstname"));
|
||||
assertThat(mapping.getPath(), is("firstname"));
|
||||
assertThat(mapping.isExported(), is(true));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = RepositoryTestsConfig.class)
|
||||
@SuppressWarnings("deprecation")
|
||||
public class RepositoryRestConfigurationIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
@@ -26,7 +27,7 @@ public class RepositoryRestConfigurationIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void shouldProvideResourceMappingForConfiguredRepository() throws Exception {
|
||||
ResourceMapping mapping = config.getResourceMappingForRepository(ConfiguredPersonRepository.class);
|
||||
ResourceMapping mapping = config.getResourceMappingForRepository(ConfiguredPersonRepository.class);
|
||||
|
||||
assertThat(mapping, notNullValue());
|
||||
assertThat(mapping.getRel(), is("people"));
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package org.springframework.data.rest.repository;
|
||||
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -14,7 +12,6 @@ import org.springframework.data.rest.repository.domain.jpa.ConfiguredPersonRepos
|
||||
import org.springframework.data.rest.repository.domain.jpa.JpaRepositoryConfig;
|
||||
import org.springframework.data.rest.repository.domain.jpa.Person;
|
||||
import org.springframework.data.rest.repository.domain.jpa.PersonRepository;
|
||||
import org.springframework.data.rest.repository.json.PersistentEntityJackson2Module;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
|
||||
/**
|
||||
@@ -31,7 +28,8 @@ public class RepositoryTestsConfig {
|
||||
return new Repositories(appCtx);
|
||||
}
|
||||
|
||||
@Bean public RepositoryRestConfiguration config() {
|
||||
@SuppressWarnings("deprecation")
|
||||
@Bean public RepositoryRestConfiguration config() {
|
||||
RepositoryRestConfiguration config = new RepositoryRestConfiguration();
|
||||
|
||||
config.setResourceMappingForDomainType(Person.class)
|
||||
@@ -63,15 +61,4 @@ public class RepositoryTestsConfig {
|
||||
@Bean public UriDomainClassConverter uriDomainClassConverter() {
|
||||
return new UriDomainClassConverter();
|
||||
}
|
||||
|
||||
@Bean public Module persistentEntityModule() {
|
||||
return new PersistentEntityJackson2Module(defaultConversionService());
|
||||
}
|
||||
|
||||
@Bean public ObjectMapper objectMapper() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.registerModule(persistentEntityModule());
|
||||
return mapper;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package org.springframework.data.rest.repository.domain.jpa;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
|
||||
/**
|
||||
* A repository to manage {@link Person}s.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@NoRepositoryBean
|
||||
public interface ConfiguredPersonRepository extends CrudRepository<Person, Long> {
|
||||
}
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.domain.jpa;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
@@ -22,9 +37,10 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan(basePackageClasses = {JpaRepositoryConfig.class})
|
||||
@ComponentScan
|
||||
@EnableJpaRepositories
|
||||
@EnableTransactionManagement
|
||||
public class JpaRepositoryConfig {
|
||||
|
||||
@@ -11,7 +11,7 @@ import org.springframework.stereotype.Component;
|
||||
public class PersonLoader implements InitializingBean {
|
||||
|
||||
@Autowired
|
||||
private PlainPersonRepository people;
|
||||
private AnnotatedPersonRepository people;
|
||||
|
||||
@Override public void afterPropertiesSet() throws Exception {
|
||||
people.save(new Person("John", "Doe"));
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package org.springframework.data.rest.repository.domain.jpa;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
|
||||
/**
|
||||
* A repository to manage {@link Person}s.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@NoRepositoryBean
|
||||
public interface PlainPersonRepository extends CrudRepository<Person, Long> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.mapping;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.rest.repository.annotation.RestResource;
|
||||
import org.springframework.data.rest.repository.support.SimpleRelProvider;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class RepositoryAwareResourceMappingFactoryUnitTests {
|
||||
|
||||
ResourceMappingFactory factory = new ResourceMappingFactory(new SimpleRelProvider());
|
||||
|
||||
@Test
|
||||
public void foo() {
|
||||
|
||||
CollectionResourceMapping mapping = factory.getMappingForType(Person.class);
|
||||
assertThat(mapping.getPath(), is("person"));
|
||||
assertThat(mapping.getRel(), is("person"));
|
||||
assertThat(mapping.getSingleResourceRel(), is("person.person"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void honorsAnnotatedMapping() {
|
||||
|
||||
CollectionResourceMapping mapping = factory.getMappingForType(AnnotatedPerson.class);
|
||||
assertThat(mapping.getPath(), is("bar"));
|
||||
assertThat(mapping.getRel(), is("foo"));
|
||||
assertThat(mapping.getSingleResourceRel(), is("foo.foo"));
|
||||
assertThat(mapping.isExported(), is(false));
|
||||
}
|
||||
|
||||
static class Person {
|
||||
|
||||
}
|
||||
|
||||
@RestResource(path = "bar", rel = "foo", exported = false)
|
||||
static class AnnotatedPerson {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.mapping;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.rest.repository.annotation.RestResource;
|
||||
import org.springframework.data.rest.repository.support.SimpleRelProvider;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ResourceMappingFactoryUnitTests {
|
||||
|
||||
ResourceMappingFactory factory = new ResourceMappingFactory(new SimpleRelProvider());
|
||||
|
||||
@Test
|
||||
public void foo() {
|
||||
|
||||
CollectionResourceMapping mapping = factory.getMappingForType(Person.class);
|
||||
assertThat(mapping.getPath(), is("person"));
|
||||
assertThat(mapping.getRel(), is("person"));
|
||||
assertThat(mapping.getSingleResourceRel(), is("person.person"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void honorsAnnotatedMapping() {
|
||||
|
||||
CollectionResourceMapping mapping = factory.getMappingForType(AnnotatedPerson.class);
|
||||
assertThat(mapping.getPath(), is("bar"));
|
||||
assertThat(mapping.getRel(), is("foo"));
|
||||
assertThat(mapping.getSingleResourceRel(), is("foo.foo"));
|
||||
assertThat(mapping.isExported(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void honorsAnnotatedsMapping() {
|
||||
|
||||
CollectionResourceMapping mapping = factory.getMappingForType(PersonRepository.class);
|
||||
assertThat(mapping.getPath(), is("bar"));
|
||||
assertThat(mapping.getRel(), is("foo"));
|
||||
assertThat(mapping.getSingleResourceRel(), is("foo.foo"));
|
||||
assertThat(mapping.isExported(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void repositoryAnnotationTrumpsDomainTypeMapping() {
|
||||
|
||||
CollectionResourceMapping mapping = factory.getMappingForType(AnnotatedAnnotatedPersonRepository.class);
|
||||
assertThat(mapping.getPath(), is("trumpsAll"));
|
||||
assertThat(mapping.getRel(), is("foo"));
|
||||
assertThat(mapping.getSingleResourceRel(), is("foo.foo"));
|
||||
assertThat(mapping.isExported(), is(true));
|
||||
}
|
||||
|
||||
static class Person {
|
||||
|
||||
}
|
||||
|
||||
@RestResource(path = "bar", rel = "foo", exported = false)
|
||||
static class AnnotatedPerson {
|
||||
|
||||
}
|
||||
|
||||
interface PersonRepository extends Repository<AnnotatedPerson, Long> {
|
||||
|
||||
}
|
||||
|
||||
@RestResource(path = "trumpsAll")
|
||||
interface AnnotatedAnnotatedPersonRepository extends Repository<AnnotatedPerson, Long> {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.repository.mapping;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.repository.domain.jpa.JpaRepositoryConfig;
|
||||
import org.springframework.data.rest.repository.domain.jpa.Person;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link ResourceMappings}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = JpaRepositoryConfig.class)
|
||||
public class ResourceMappingsIntegrationTest {
|
||||
|
||||
@Autowired ListableBeanFactory factory;
|
||||
|
||||
ResourceMappings mappings;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
Repositories repositories = new Repositories(factory);
|
||||
this.mappings = new ResourceMappings(new RepositoryRestConfiguration(), repositories);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void foo() {
|
||||
|
||||
assertThat(mappings, is(Matchers.<ResourceMetadata> iterableWithSize(2)));
|
||||
assertThat(mappings.getMappingFor(Person.class).isExported(), is(false));
|
||||
}
|
||||
}
|
||||
16
spring-data-rest-repository/src/test/resources/logback.xml
Normal file
16
spring-data-rest-repository/src/test/resources/logback.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework.data" level="error" />
|
||||
|
||||
<root level="error">
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
@@ -1,137 +1,119 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import static org.springframework.data.rest.webmvc.ControllerUtils.*;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.OptimisticLockingFailureException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.support.DomainClassConverter;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.config.ResourceMapping;
|
||||
import org.springframework.data.rest.repository.BaseUriAwareResource;
|
||||
import org.springframework.data.rest.repository.PagingAndSorting;
|
||||
import org.springframework.data.rest.repository.PersistentEntityResource;
|
||||
import org.springframework.data.rest.repository.RepositoryConstraintViolationException;
|
||||
import org.springframework.data.rest.repository.invoke.MethodParameterConversionService;
|
||||
import org.springframework.data.rest.repository.support.ResourceMappingUtils;
|
||||
import org.springframework.data.rest.webmvc.support.BaseUriLinkBuilder;
|
||||
import org.springframework.data.rest.webmvc.support.ExceptionMessage;
|
||||
import org.springframework.data.rest.webmvc.support.RepositoryConstraintViolationExceptionMessage;
|
||||
import org.springframework.data.rest.webmvc.support.ValidationExceptionHandler;
|
||||
import org.springframework.hateoas.*;
|
||||
import org.springframework.data.web.PagedResourcesAssembler;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.Resources;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.util.*;
|
||||
|
||||
import static org.springframework.data.rest.core.util.UriUtils.buildUri;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes"})
|
||||
public class AbstractRepositoryRestController implements ApplicationContextAware,
|
||||
InitializingBean {
|
||||
@SuppressWarnings({ "rawtypes", "deprecation" })
|
||||
class AbstractRepositoryRestController implements MessageSourceAware, InitializingBean {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AbstractRepositoryRestController.class);
|
||||
|
||||
static final Resource<?> EMPTY_RESOURCE = new Resource<Object>(Collections.emptyList());
|
||||
static final Resources<Resource<?>> EMPTY_RESOURCES = new Resources<Resource<?>>(Collections.<Resource<?>>emptyList());
|
||||
static final Iterable<Resource<?>> EMPTY_RESOURCE_LIST = Collections.emptyList();
|
||||
static final TypeDescriptor STRING_TYPE = TypeDescriptor.valueOf(String.class);
|
||||
protected final Logger LOG = LoggerFactory.getLogger(getClass());
|
||||
protected final Repositories repositories;
|
||||
protected final RepositoryRestConfiguration config;
|
||||
protected final DomainClassConverter domainClassConverter;
|
||||
protected final ConversionService conversionService;
|
||||
protected final MethodParameterConversionService methodParameterConversionService;
|
||||
protected final EntityLinks entityLinks;
|
||||
protected ApplicationContext applicationContext;
|
||||
@Autowired(required = false)
|
||||
protected ValidationExceptionHandler handler;
|
||||
@Autowired(required = false)
|
||||
protected PlatformTransactionManager txMgr;
|
||||
protected TransactionTemplate txTmpl;
|
||||
private final PersistentEntityResourceAssembler<Object> perAssembler;
|
||||
|
||||
@Autowired
|
||||
public AbstractRepositoryRestController(Repositories repositories,
|
||||
RepositoryRestConfiguration config,
|
||||
DomainClassConverter domainClassConverter,
|
||||
ConversionService conversionService,
|
||||
EntityLinks entityLinks) {
|
||||
this.repositories = repositories;
|
||||
this.config = config;
|
||||
this.domainClassConverter = domainClassConverter;
|
||||
this.conversionService = conversionService;
|
||||
this.entityLinks = entityLinks;
|
||||
this.methodParameterConversionService = new MethodParameterConversionService(conversionService);
|
||||
@Autowired(required = false) private ValidationExceptionHandler handler;
|
||||
@Autowired(required = false) private PlatformTransactionManager txMgr;
|
||||
|
||||
private MessageSource messageSource;
|
||||
private PagedResourcesAssembler<Object> assembler;
|
||||
|
||||
public AbstractRepositoryRestController(PagedResourcesAssembler<Object> assembler, PersistentEntityResourceAssembler<Object> entityResourceAssembler) {
|
||||
|
||||
this.assembler = assembler;
|
||||
this.perAssembler = entityResourceAssembler;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.MessageSourceAware#setMessageSource(org.springframework.context.MessageSource)
|
||||
*/
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messageSource = messageSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (null != txMgr) {
|
||||
txTmpl = new TransactionTemplate(txMgr);
|
||||
txTmpl.afterPropertiesSet();
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
|
||||
// if (null != txMgr) {
|
||||
// txTmpl = new TransactionTemplate(txMgr);
|
||||
// txTmpl.afterPropertiesSet();
|
||||
// }
|
||||
}
|
||||
|
||||
@ExceptionHandler({
|
||||
NullPointerException.class
|
||||
})
|
||||
@ExceptionHandler({ NullPointerException.class })
|
||||
@ResponseBody
|
||||
public ResponseEntity<?> handleNPE(NullPointerException npe) {
|
||||
return errorResponse(npe, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
@ExceptionHandler({
|
||||
ResourceNotFoundException.class
|
||||
})
|
||||
@ExceptionHandler({ ResourceNotFoundException.class })
|
||||
@ResponseBody
|
||||
public ResponseEntity<?> handleNotFound() {
|
||||
return notFound();
|
||||
}
|
||||
|
||||
@ExceptionHandler({
|
||||
NoSuchMethodError.class,
|
||||
HttpRequestMethodNotSupportedException.class
|
||||
})
|
||||
@ExceptionHandler({ NoSuchMethodError.class, HttpRequestMethodNotSupportedException.class })
|
||||
@ResponseBody
|
||||
public ResponseEntity<?> handleNoSuchMethod() {
|
||||
return errorResponse(null, HttpStatus.METHOD_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
@ExceptionHandler({
|
||||
HttpMessageNotReadableException.class,
|
||||
HttpMessageNotWritableException.class
|
||||
})
|
||||
@ExceptionHandler({ HttpMessageNotReadableException.class, HttpMessageNotWritableException.class })
|
||||
@ResponseBody
|
||||
public ResponseEntity<ExceptionMessage> handleNotReadable(HttpMessageNotReadableException e) {
|
||||
return badRequest(e);
|
||||
@@ -139,16 +121,12 @@ public class AbstractRepositoryRestController implements ApplicationContextAware
|
||||
|
||||
/**
|
||||
* Handle failures commonly thrown from code tries to read incoming data and convert or cast it to the right type.
|
||||
*
|
||||
*
|
||||
* @param t
|
||||
* @return
|
||||
*/
|
||||
@ExceptionHandler({
|
||||
InvocationTargetException.class,
|
||||
IllegalArgumentException.class,
|
||||
ClassCastException.class,
|
||||
ConversionFailedException.class
|
||||
})
|
||||
@ExceptionHandler({ InvocationTargetException.class, IllegalArgumentException.class, ClassCastException.class,
|
||||
ConversionFailedException.class })
|
||||
@ResponseBody
|
||||
public ResponseEntity handleMiscFailures(Throwable t) {
|
||||
if (null != t.getCause() && t.getCause() instanceof ResourceNotFoundException) {
|
||||
@@ -157,48 +135,22 @@ public class AbstractRepositoryRestController implements ApplicationContextAware
|
||||
return badRequest(t);
|
||||
}
|
||||
|
||||
// @ExceptionHandler({
|
||||
// RuntimeException.class
|
||||
// })
|
||||
// @ResponseBody
|
||||
// public ResponseEntity maybeHandleValidationException(Locale locale,
|
||||
// RuntimeException ex) {
|
||||
// if(ResourceNotFoundException.class.isAssignableFrom(ex.getClass())) {
|
||||
// return handleNotFound();
|
||||
// }
|
||||
//
|
||||
// if(null != handler) {
|
||||
// return handler.handleValidationException(ex,
|
||||
// applicationContext,
|
||||
// locale);
|
||||
// } else {
|
||||
// return response(null,
|
||||
// ex,
|
||||
// HttpStatus.BAD_REQUEST);
|
||||
// }
|
||||
// }
|
||||
|
||||
@ExceptionHandler({
|
||||
RepositoryConstraintViolationException.class
|
||||
})
|
||||
@ExceptionHandler({ RepositoryConstraintViolationException.class })
|
||||
@ResponseBody
|
||||
public ResponseEntity handleRepositoryConstraintViolationException(Locale locale,
|
||||
RepositoryConstraintViolationException rcve) {
|
||||
return response(null,
|
||||
new RepositoryConstraintViolationExceptionMessage(rcve, applicationContext, locale),
|
||||
HttpStatus.BAD_REQUEST);
|
||||
RepositoryConstraintViolationException rcve) {
|
||||
|
||||
return response(null, new RepositoryConstraintViolationExceptionMessage(rcve, messageSource, locale),
|
||||
HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a 409 Conflict in case of concurrent modification.
|
||||
*
|
||||
*
|
||||
* @param ex
|
||||
* @return
|
||||
*/
|
||||
@ExceptionHandler({
|
||||
OptimisticLockingFailureException.class,
|
||||
DataIntegrityViolationException.class
|
||||
})
|
||||
@ExceptionHandler({ OptimisticLockingFailureException.class, DataIntegrityViolationException.class })
|
||||
@ResponseBody
|
||||
public ResponseEntity handleConflict(Exception ex) {
|
||||
return errorResponse(null, ex, HttpStatus.CONFLICT);
|
||||
@@ -220,14 +172,12 @@ public class AbstractRepositoryRestController implements ApplicationContextAware
|
||||
return errorResponse(headers, throwable, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
public <T extends Throwable> ResponseEntity<ExceptionMessage> errorResponse(T throwable,
|
||||
HttpStatus status) {
|
||||
public <T extends Throwable> ResponseEntity<ExceptionMessage> errorResponse(T throwable, HttpStatus status) {
|
||||
return errorResponse(null, throwable, status);
|
||||
}
|
||||
|
||||
public <T extends Throwable> ResponseEntity<ExceptionMessage> errorResponse(HttpHeaders headers,
|
||||
T throwable,
|
||||
HttpStatus status) {
|
||||
public <T extends Throwable> ResponseEntity<ExceptionMessage> errorResponse(HttpHeaders headers, T throwable,
|
||||
HttpStatus status) {
|
||||
if (null != throwable && null != throwable.getMessage()) {
|
||||
LOG.error(throwable.getMessage(), throwable);
|
||||
return response(headers, new ExceptionMessage(throwable), status);
|
||||
@@ -244,86 +194,6 @@ public class AbstractRepositoryRestController implements ApplicationContextAware
|
||||
return new ResponseEntity<T>(body, hdrs, status);
|
||||
}
|
||||
|
||||
public <R extends Resource<?>> ResponseEntity<Resource<?>> resourceResponse(HttpHeaders headers,
|
||||
R resource,
|
||||
HttpStatus status) {
|
||||
HttpHeaders hdrs = new HttpHeaders();
|
||||
if (null != headers) {
|
||||
hdrs.putAll(headers);
|
||||
}
|
||||
return new ResponseEntity<Resource<?>>(resource, hdrs, status);
|
||||
}
|
||||
|
||||
protected void addQueryParameters(HttpServletRequest request,
|
||||
UriComponentsBuilder builder) {
|
||||
for (Enumeration<String> names = request.getParameterNames(); names.hasMoreElements(); ) {
|
||||
String name = names.nextElement();
|
||||
String value = request.getParameter(name);
|
||||
if (name.equals(config.getPageParamName()) || name.equals(config.getLimitParamName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.queryParam(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected Link searchLink(RepositoryRestRequest repoRequest,
|
||||
int pageIncrement,
|
||||
String method,
|
||||
String rel) {
|
||||
PagingAndSorting pageSort = repoRequest.getPagingAndSorting();
|
||||
UriComponentsBuilder ucb = UriComponentsBuilder.fromUri(
|
||||
entityLinks.linkFor(repoRequest.getPersistentEntity().getType())
|
||||
.slash("search")
|
||||
.slash(method)
|
||||
.toUri()
|
||||
);
|
||||
ucb.queryParam(config.getPageParamName(), Math.max(pageSort.getPageNumber() + pageIncrement, 1))
|
||||
.queryParam(config.getLimitParamName(), pageSort.getPageSize());
|
||||
|
||||
addQueryParameters(repoRequest.getRequest(), ucb);
|
||||
|
||||
return new Link(ucb.build().toString(), rel);
|
||||
}
|
||||
|
||||
protected Link entitiesPageLink(RepositoryRestRequest repoRequest,
|
||||
int pageIncrement,
|
||||
String rel) {
|
||||
PagingAndSorting pageSort = repoRequest.getPagingAndSorting();
|
||||
UriComponentsBuilder ucb = UriComponentsBuilder.fromUri(
|
||||
entityLinks.linkFor(repoRequest.getPersistentEntity().getType())
|
||||
.toUri()
|
||||
);
|
||||
if (null != repoRequest.getRequest().getParameter(config.getPageParamName())) {
|
||||
ucb.queryParam(config.getPageParamName(), Math.max(pageSort.getPageNumber() + pageIncrement, 1))
|
||||
.queryParam(config.getLimitParamName(), pageSort.getPageSize());
|
||||
}
|
||||
|
||||
addQueryParameters(repoRequest.getRequest(), ucb);
|
||||
|
||||
return new Link(ucb.build().toString(), rel);
|
||||
}
|
||||
|
||||
protected List<Link> queryMethodLinks(URI baseUri, Class<?> domainType) {
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
RepositoryInformation repoInfo = repositories.getRepositoryInformationFor(domainType);
|
||||
ResourceMapping repoMapping = ResourceMappingUtils.merge(
|
||||
repoInfo.getRepositoryInterface(),
|
||||
config.getResourceMappingForRepository(repoInfo.getRepositoryInterface())
|
||||
);
|
||||
for (Method method : repoInfo.getQueryMethods()) {
|
||||
LinkBuilder linkBuilder = BaseUriLinkBuilder.create(buildUri(baseUri, repoMapping.getPath(), "search"));
|
||||
ResourceMapping methodMapping = ResourceMappingUtils.merge(method,
|
||||
repoMapping.getResourceMappingFor(method.getName()));
|
||||
if (!methodMapping.isExported()) {
|
||||
continue;
|
||||
}
|
||||
links.add(linkBuilder.slash(methodMapping.getPath())
|
||||
.withRel(repoMapping.getRel() + "." + methodMapping.getRel()));
|
||||
}
|
||||
return links;
|
||||
}
|
||||
|
||||
protected Link resourceLink(RepositoryRestRequest repoRequest, Resource resource) {
|
||||
ResourceMapping repoMapping = repoRequest.getRepositoryResourceMapping();
|
||||
ResourceMapping entityMapping = repoRequest.getPersistentEntityResourceMapping();
|
||||
@@ -333,89 +203,37 @@ public class AbstractRepositoryRestController implements ApplicationContextAware
|
||||
return new Link(selfLink.getHref(), rel);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
protected Resources resultToResources(RepositoryRestRequest repoRequest,
|
||||
Object result,
|
||||
List<Link> links,
|
||||
Link prevLink,
|
||||
Link nextLink) {
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
protected Resources resultToResources(Object result, Link baseLink) {
|
||||
|
||||
if (result instanceof Page) {
|
||||
Page page = (Page) result;
|
||||
PagedResources.PageMetadata pageMeta = pageMetadata(page);
|
||||
if (page.hasPreviousPage() && null != prevLink) {
|
||||
links.add(prevLink);
|
||||
}
|
||||
if (page.hasNextPage() && null != nextLink) {
|
||||
links.add(nextLink);
|
||||
}
|
||||
if (page.hasContent()) {
|
||||
return entitiesToResources(repoRequest, links, page);
|
||||
} else {
|
||||
return new PagedResources(Collections.emptyList(), pageMeta, links);
|
||||
}
|
||||
Page<Object> page = (Page<Object>) result;
|
||||
return entitiesToResources(page, baseLink, assembler);
|
||||
} else if (result instanceof Iterable) {
|
||||
return entitiesToResources(repoRequest, links, (Iterable) result);
|
||||
return entitiesToResources((Iterable<Object>) result);
|
||||
} else if (null == result) {
|
||||
return new Resources(EMPTY_RESOURCE_LIST);
|
||||
} else {
|
||||
PersistentEntityResource per = PersistentEntityResource.wrap(
|
||||
repoRequest.getPersistentEntity(),
|
||||
result,
|
||||
repoRequest.getBaseUri()
|
||||
);
|
||||
BeanWrapper wrapper = BeanWrapper.create(result, conversionService);
|
||||
Link selfLink = entityLinks.linkForSingleResource(
|
||||
result.getClass(),
|
||||
wrapper.getProperty(repoRequest.getPersistentEntity().getIdProperty())
|
||||
)
|
||||
.withSelfRel();
|
||||
per.add(selfLink);
|
||||
return new Resources(Collections.singletonList(per), links);
|
||||
Resource<Object> resource = perAssembler.toResource(result);
|
||||
return new Resources(Collections.singletonList(resource));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
protected Resources entitiesToResources(RepositoryRestRequest repoRequest, List<Link> links, Page page) {
|
||||
PagedResources.PageMetadata pageMeta = pageMetadata(page);
|
||||
Resources<Object> resource = (Resources<Object>) entitiesToResources(repoRequest, links, page.getContent());
|
||||
return new PagedResources<Object>(resource.getContent(), pageMeta, resource.getLinks());
|
||||
protected Resources<? extends Resource<Object>> entitiesToResources(Page<Object> page, Link baseLink,
|
||||
final PagedResourcesAssembler<Object> assembler) {
|
||||
|
||||
return assembler.toResource(page, perAssembler, baseLink);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
protected Resources entitiesToResources(RepositoryRestRequest repoRequest, List<Link> links, Iterable entities) {
|
||||
List<Resource<?>> resources = new ArrayList<Resource<?>>();
|
||||
|
||||
protected Resources<Resource<Object>> entitiesToResources(Iterable<Object> entities) {
|
||||
|
||||
List<Resource<Object>> resources = new ArrayList<Resource<Object>>();
|
||||
|
||||
for (Object obj : entities) {
|
||||
if (null == obj) {
|
||||
resources.add(null);
|
||||
continue;
|
||||
}
|
||||
|
||||
PersistentEntity persistentEntity = repositories.getPersistentEntity(obj.getClass());
|
||||
if (null == persistentEntity) {
|
||||
resources.add(new BaseUriAwareResource<Object>(obj)
|
||||
.setBaseUri(repoRequest.getBaseUri()));
|
||||
continue;
|
||||
}
|
||||
|
||||
BeanWrapper wrapper = BeanWrapper.create(obj, conversionService);
|
||||
PersistentEntityResource per = PersistentEntityResource.wrap(persistentEntity, obj, repoRequest.getBaseUri());
|
||||
Link selfLink = entityLinks.linkForSingleResource(persistentEntity.getType(),
|
||||
wrapper.getProperty(persistentEntity.getIdProperty()))
|
||||
.withSelfRel();
|
||||
per.add(selfLink);
|
||||
resources.add(per);
|
||||
resources.add(obj == null ? null : perAssembler.toResource(obj));
|
||||
}
|
||||
|
||||
return new Resources(resources, links);
|
||||
return new Resources<Resource<Object>>(resources);
|
||||
}
|
||||
|
||||
protected PagedResources.PageMetadata pageMetadata(Page page) {
|
||||
return new PagedResources.PageMetadata(
|
||||
page.getNumberOfElements(),
|
||||
page.getNumber() + 1,
|
||||
page.getTotalElements(),
|
||||
page.getTotalPages()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import java.net.URI;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.webmvc.annotation.BaseURI;
|
||||
@@ -15,14 +29,11 @@ import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class BaseUriMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
@Autowired
|
||||
private RepositoryRestConfiguration config;
|
||||
|
||||
public BaseUriMethodArgumentResolver() {
|
||||
}
|
||||
private final RepositoryRestConfiguration config;
|
||||
|
||||
public BaseUriMethodArgumentResolver(RepositoryRestConfiguration config) {
|
||||
this.config = config;
|
||||
@@ -34,7 +45,7 @@ public class BaseUriMethodArgumentResolver implements HandlerMethodArgumentResol
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
public URI resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
@@ -48,5 +59,4 @@ public class BaseUriMethodArgumentResolver implements HandlerMethodArgumentResol
|
||||
|
||||
return config.getBaseUri();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.Resources;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
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());
|
||||
public static final Iterable<Resource<?>> EMPTY_RESOURCE_LIST = Collections.emptyList();
|
||||
public static final TypeDescriptor STRING_TYPE = TypeDescriptor.valueOf(String.class);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.config.ResourceMapping;
|
||||
import org.springframework.data.rest.repository.PagingAndSorting;
|
||||
import org.springframework.data.web.PageableDefaults;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* {@link HandlerMethodArgumentResolver} implementation responsible for inspecting a request for page and sort
|
||||
* parameters for use by the repositories.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class PagingAndSortingMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
private static final int DEFAULT_PAGE = 1; // We're 1-based, not 0-based
|
||||
@Autowired
|
||||
private RepositoryRestConfiguration config;
|
||||
@Autowired
|
||||
private RepositoryInformationHandlerMethodArgumentResolver repoInfoResolver;
|
||||
|
||||
public PagingAndSortingMethodArgumentResolver() {
|
||||
}
|
||||
|
||||
public PagingAndSortingMethodArgumentResolver(RepositoryRestConfiguration config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public RepositoryRestConfiguration getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public RepositoryInformationHandlerMethodArgumentResolver getRepoInfoResolver() {
|
||||
return repoInfoResolver;
|
||||
}
|
||||
|
||||
public PagingAndSortingMethodArgumentResolver setRepoInfoResolver(RepositoryInformationHandlerMethodArgumentResolver repoInfoResolver) {
|
||||
this.repoInfoResolver = repoInfoResolver;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public boolean supportsParameter(MethodParameter parameter) {
|
||||
return ClassUtils.isAssignable(parameter.getParameterType(), PagingAndSorting.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
HttpServletRequest request = (HttpServletRequest)webRequest.getNativeRequest();
|
||||
|
||||
PageRequest pr = null;
|
||||
for(Annotation annotation : parameter.getParameterAnnotations()) {
|
||||
if(annotation instanceof PageableDefaults) {
|
||||
PageableDefaults defaults = (PageableDefaults)annotation;
|
||||
pr = new PageRequest(defaults.pageNumber(), defaults.value());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(null == pr) {
|
||||
int page = DEFAULT_PAGE;
|
||||
String sPage = request.getParameter(config.getPageParamName());
|
||||
if(StringUtils.hasText(sPage)) {
|
||||
try {
|
||||
page = Integer.parseInt(sPage);
|
||||
} catch(NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
int limit = config.getDefaultPageSize();
|
||||
String sLimit = request.getParameter(config.getLimitParamName());
|
||||
if(StringUtils.hasText(sLimit)) {
|
||||
try {
|
||||
limit = Math.min(Integer.parseInt(sLimit), config.getMaxPageSize());
|
||||
} catch(NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
Sort sort = null;
|
||||
List<Sort.Order> orders = new ArrayList<Sort.Order>();
|
||||
String[] orderValues = request.getParameterValues(config.getSortParamName());
|
||||
if(null != orderValues) {
|
||||
for(String orderParam : orderValues) {
|
||||
String name = nameForParam(parameter,
|
||||
mavContainer,
|
||||
webRequest,
|
||||
binderFactory,
|
||||
orderParam);
|
||||
String sortDir = request.getParameter(orderParam + ".dir");
|
||||
Sort.Direction dir = (null != sortDir ? Sort.Direction.valueOf(sortDir.toUpperCase()) : Sort.Direction.ASC);
|
||||
orders.add(new Sort.Order(dir, name));
|
||||
}
|
||||
if(!orders.isEmpty()) {
|
||||
sort = new Sort(orders);
|
||||
}
|
||||
}
|
||||
|
||||
if(null != sort) {
|
||||
pr = new PageRequest(page - 1, limit, sort);
|
||||
} else {
|
||||
pr = new PageRequest(page - 1, limit);
|
||||
}
|
||||
}
|
||||
|
||||
return new PagingAndSorting(config, pr);
|
||||
}
|
||||
|
||||
private String nameForParam(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory,
|
||||
String orderParam) throws Exception {
|
||||
if(null == repoInfoResolver) {
|
||||
return orderParam;
|
||||
}
|
||||
RepositoryInformation repoInfo = (RepositoryInformation)repoInfoResolver.resolveArgument(parameter,
|
||||
mavContainer,
|
||||
webRequest,
|
||||
binderFactory);
|
||||
ResourceMapping repoMapping = config.getResourceMappingForDomainType(repoInfo.getDomainType());
|
||||
if(null != repoMapping) {
|
||||
return repoMapping.getNameForPath(orderParam);
|
||||
}
|
||||
return orderParam;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.repository.PersistentEntityResource;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.Link;
|
||||
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}.
|
||||
*
|
||||
* @param repositories must not be {@literal null}.
|
||||
* @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;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.ResourceAssembler#toResource(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -49,8 +49,7 @@ public class PersistentEntityResourceHandlerMethodArgumentResolver implements Ha
|
||||
}
|
||||
|
||||
Object obj = converter.read(domainType, request);
|
||||
return new PersistentEntityResource<Object>(repoRequest.getPersistentEntity(),
|
||||
obj);
|
||||
return new PersistentEntityResource<Object>(repoRequest.getPersistentEntity(), obj);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,11 +1,25 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.repository.support.DomainClassConverter;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.config.ResourceMapping;
|
||||
import org.springframework.data.web.PagedResourcesAssembler;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
@@ -15,30 +29,29 @@ import static org.springframework.data.rest.repository.support.ResourceMappingUt
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RestController
|
||||
@SuppressWarnings("deprecation")
|
||||
public class RepositoryController extends AbstractRepositoryRestController {
|
||||
|
||||
private final Repositories repositories;
|
||||
private final RepositoryRestConfiguration config;
|
||||
private final EntityLinks entityLinks;
|
||||
|
||||
@Autowired
|
||||
public RepositoryController(Repositories repositories,
|
||||
RepositoryRestConfiguration config,
|
||||
DomainClassConverter<?> domainClassConverter,
|
||||
ConversionService conversionService,
|
||||
EntityLinks entityLinks) {
|
||||
super(repositories,
|
||||
config,
|
||||
domainClassConverter,
|
||||
conversionService,
|
||||
entityLinks);
|
||||
public RepositoryController(Repositories repositories, RepositoryRestConfiguration config, EntityLinks entityLinks,
|
||||
PagedResourcesAssembler<Object> assembler, PersistentEntityResourceAssembler<Object> perAssembler) {
|
||||
|
||||
super(assembler, perAssembler);
|
||||
|
||||
this.repositories = repositories;
|
||||
this.config = config;
|
||||
this.entityLinks = entityLinks;
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
value = "/",
|
||||
method = RequestMethod.GET,
|
||||
produces = {
|
||||
"application/json",
|
||||
"application/x-spring-data-compact+json"
|
||||
}
|
||||
)
|
||||
@RequestMapping(value = "/", method = RequestMethod.GET, //
|
||||
produces = { "application/json", "application/x-spring-data-compact+json" })
|
||||
@ResponseBody
|
||||
public RepositoryLinksResource listRepositories() throws ResourceNotFoundException {
|
||||
RepositoryLinksResource resource = new RepositoryLinksResource();
|
||||
@@ -50,5 +63,4 @@ public class RepositoryController extends AbstractRepositoryRestController {
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,34 +1,21 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.repository.support.DomainClassConverter;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.config.ResourceMapping;
|
||||
import org.springframework.data.rest.repository.PagingAndSorting;
|
||||
import org.springframework.data.rest.repository.PersistentEntityResource;
|
||||
import org.springframework.data.rest.repository.context.*;
|
||||
import org.springframework.data.rest.repository.invoke.RepositoryMethodInvoker;
|
||||
import org.springframework.data.rest.repository.json.JsonSchema;
|
||||
import org.springframework.data.rest.repository.json.PersistentEntityToJsonSchemaConverter;
|
||||
import org.springframework.data.rest.repository.support.DomainObjectMerger;
|
||||
import org.springframework.hateoas.*;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import static org.springframework.data.rest.webmvc.ControllerUtils.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
@@ -36,109 +23,147 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.repository.support.DomainClassConverter;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.config.ResourceMapping;
|
||||
import org.springframework.data.rest.repository.PersistentEntityResource;
|
||||
import org.springframework.data.rest.repository.context.AfterCreateEvent;
|
||||
import org.springframework.data.rest.repository.context.AfterDeleteEvent;
|
||||
import org.springframework.data.rest.repository.context.AfterSaveEvent;
|
||||
import org.springframework.data.rest.repository.context.BeforeCreateEvent;
|
||||
import org.springframework.data.rest.repository.context.BeforeDeleteEvent;
|
||||
import org.springframework.data.rest.repository.context.BeforeSaveEvent;
|
||||
import org.springframework.data.rest.repository.invoke.RepositoryMethodInvoker;
|
||||
import org.springframework.data.rest.repository.support.DomainObjectMerger;
|
||||
import org.springframework.data.rest.webmvc.json.JsonSchema;
|
||||
import org.springframework.data.rest.webmvc.json.PersistentEntityToJsonSchemaConverter;
|
||||
import org.springframework.data.web.PagedResourcesAssembler;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.PagedResources;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.Resources;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
import org.springframework.transaction.support.TransactionOperations;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes"})
|
||||
public class RepositoryEntityController extends AbstractRepositoryRestController {
|
||||
@RestController
|
||||
@SuppressWarnings("deprecation")
|
||||
class RepositoryEntityController extends AbstractRepositoryRestController implements
|
||||
ApplicationEventPublisherAware {
|
||||
|
||||
private static final String BASE_MAPPING = "/{repository}";
|
||||
|
||||
@Autowired
|
||||
private DomainObjectMerger domainObjectMerger;
|
||||
@Autowired
|
||||
private PersistentEntityToJsonSchemaConverter jsonSchemaConverter;
|
||||
|
||||
public RepositoryEntityController(Repositories repositories,
|
||||
RepositoryRestConfiguration config,
|
||||
DomainClassConverter domainClassConverter,
|
||||
ConversionService conversionService,
|
||||
EntityLinks entityLinks) {
|
||||
super(repositories,
|
||||
config,
|
||||
domainClassConverter,
|
||||
conversionService,
|
||||
entityLinks);
|
||||
private final EntityLinks entityLinks;
|
||||
private final PersistentEntityResourceAssembler<Object> perAssembler;
|
||||
private final RepositoryRestConfiguration config;
|
||||
private final DomainClassConverter<?> converter;
|
||||
private final ConversionService conversionService;
|
||||
|
||||
private final TransactionOperations txOperations;
|
||||
|
||||
private ApplicationEventPublisher publisher;
|
||||
@Autowired private DomainObjectMerger domainObjectMerger;
|
||||
@Autowired private PersistentEntityToJsonSchemaConverter jsonSchemaConverter;
|
||||
|
||||
@Autowired
|
||||
public RepositoryEntityController(Repositories repositories, RepositoryRestConfiguration config,
|
||||
EntityLinks entityLinks, PagedResourcesAssembler<Object> assembler,
|
||||
PersistentEntityResourceAssembler<Object> perAssembler, DomainClassConverter<?> converter,
|
||||
@Qualifier("defaultConversionService") ConversionService conversionService) {
|
||||
|
||||
super(assembler, perAssembler);
|
||||
|
||||
this.entityLinks = entityLinks;
|
||||
this.perAssembler = perAssembler;
|
||||
this.config = config;
|
||||
this.converter = converter;
|
||||
this.conversionService = conversionService;
|
||||
|
||||
this.txOperations = null;
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
value = BASE_MAPPING + "/schema",
|
||||
method = RequestMethod.GET,
|
||||
produces = {
|
||||
"application/schema+json"
|
||||
}
|
||||
)
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher)
|
||||
*/
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.publisher = applicationEventPublisher;
|
||||
|
||||
}
|
||||
|
||||
@RequestMapping(value = BASE_MAPPING + "/schema", method = RequestMethod.GET,
|
||||
produces = { "application/schema+json" })
|
||||
@ResponseBody
|
||||
public JsonSchema schema(RepositoryRestRequest repoRequest) {
|
||||
return jsonSchemaConverter.convert(repoRequest.getPersistentEntity().getType());
|
||||
}
|
||||
|
||||
@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 Resources listEntities(final RepositoryRestRequest repoRequest)
|
||||
public Resources<?> listEntities(final RepositoryRestRequest request, Pageable pageable)
|
||||
throws ResourceNotFoundException {
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
|
||||
Iterable<?> results;
|
||||
RepositoryMethodInvoker repoMethodInvoker = repoRequest.getRepositoryMethodInvoker();
|
||||
RepositoryMethodInvoker repoMethodInvoker = request.getRepositoryMethodInvoker();
|
||||
if (null == repoMethodInvoker) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
boolean hasSortParams = (null != repoRequest.getRequest().getParameter(config.getSortParamName()));
|
||||
|
||||
if (repoMethodInvoker.hasFindAllPageable()) {
|
||||
PagingAndSorting pageSort = repoRequest.getPagingAndSorting();
|
||||
results = repoMethodInvoker.findAll(new PageRequest(pageSort.getPageNumber(),
|
||||
pageSort.getPageSize(),
|
||||
pageSort.getSort()));
|
||||
} else if (repoMethodInvoker.hasFindAllSorted() && hasSortParams) {
|
||||
results = repoMethodInvoker.findAll(repoRequest.getPagingAndSorting().getSort());
|
||||
results = repoMethodInvoker.findAll(pageable);
|
||||
} else if (repoMethodInvoker.hasFindAllSorted()) {
|
||||
results = repoMethodInvoker.findAll(pageable.getSort());
|
||||
} else if (repoMethodInvoker.hasFindAll()) {
|
||||
results = repoMethodInvoker.findAll();
|
||||
} else {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
ResourceMapping repoMapping = repoRequest.getRepositoryResourceMapping();
|
||||
ResourceMapping repoMapping = request.getRepositoryResourceMapping();
|
||||
if (!repoMethodInvoker.getQueryMethods().isEmpty()) {
|
||||
links.add(entityLinks.linkForSingleResource(repoRequest.getPersistentEntity().getType(), "search")
|
||||
.withRel(repoMapping.getRel() + ".search"));
|
||||
links.add(entityLinks.linkForSingleResource(request.getPersistentEntity().getType(), "search").withRel(
|
||||
repoMapping.getRel() + ".search"));
|
||||
}
|
||||
|
||||
PagingAndSorting pageSort = repoRequest.getPagingAndSorting();
|
||||
Link prevLink = null;
|
||||
Link nextLink = null;
|
||||
if (results instanceof Page) {
|
||||
if (((Page) results).hasPreviousPage() && pageSort.getPageNumber() > 0) {
|
||||
prevLink = entitiesPageLink(repoRequest, 0, "page.previous");
|
||||
}
|
||||
if (((Page) results).hasNextPage()) {
|
||||
nextLink = entitiesPageLink(repoRequest, 1, "page.next");
|
||||
}
|
||||
}
|
||||
|
||||
return resultToResources(repoRequest, results, links, prevLink, nextLink);
|
||||
Link baseLink = request.getRepositoryLink();
|
||||
Resources<?> resources = resultToResources(results, baseLink);
|
||||
resources.add(links);
|
||||
return resources;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@RequestMapping(
|
||||
value = BASE_MAPPING,
|
||||
method = RequestMethod.GET,
|
||||
produces = {
|
||||
"application/x-spring-data-compact+json",
|
||||
"text/uri-list"
|
||||
}
|
||||
)
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET, produces = {
|
||||
"application/x-spring-data-compact+json", "text/uri-list" })
|
||||
@ResponseBody
|
||||
public Resources listEntitiesCompact(final RepositoryRestRequest repoRequest)
|
||||
public Resources<?> listEntitiesCompact(final RepositoryRestRequest repoRequest, Pageable pageable)
|
||||
throws ResourceNotFoundException {
|
||||
Resources resources = listEntities(repoRequest);
|
||||
Resources<?> resources = listEntities(repoRequest, pageable);
|
||||
List<Link> links = new ArrayList<Link>(resources.getLinks());
|
||||
|
||||
for (Resource<?> resource : ((Resources<Resource<?>>) resources).getContent()) {
|
||||
@@ -146,169 +171,115 @@ public class RepositoryEntityController extends AbstractRepositoryRestController
|
||||
links.add(resourceLink(repoRequest, persistentEntityResource));
|
||||
}
|
||||
if (resources instanceof PagedResources) {
|
||||
return new PagedResources(Collections.emptyList(), ((PagedResources) resources).getMetadata(), links);
|
||||
return new PagedResources<Object>(Collections.emptyList(), ((PagedResources<?>) resources).getMetadata(), links);
|
||||
} else {
|
||||
return new Resources(Collections.emptyList(), links);
|
||||
return new Resources<Object>(Collections.emptyList(), links);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@RequestMapping(
|
||||
value = BASE_MAPPING,
|
||||
method = RequestMethod.POST,
|
||||
consumes = {
|
||||
"application/json"
|
||||
},
|
||||
produces = {
|
||||
"application/json",
|
||||
"text/uri-list"
|
||||
}
|
||||
)
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.POST, consumes = { "application/json" }, produces = {
|
||||
"application/json", "text/uri-list" })
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource<?>> createNewEntity(RepositoryRestRequest repoRequest,
|
||||
PersistentEntityResource<?> incoming) {
|
||||
PersistentEntityResource<?> incoming) {
|
||||
RepositoryMethodInvoker repoMethodInvoker = repoRequest.getRepositoryMethodInvoker();
|
||||
if (null == repoMethodInvoker || !repoMethodInvoker.hasSaveOne()) {
|
||||
throw new NoSuchMethodError();
|
||||
}
|
||||
|
||||
applicationContext.publishEvent(new BeforeCreateEvent(incoming.getContent()));
|
||||
publisher.publishEvent(new BeforeCreateEvent(incoming.getContent()));
|
||||
Object obj = repoMethodInvoker.save(incoming.getContent());
|
||||
applicationContext.publishEvent(new AfterCreateEvent(obj));
|
||||
publisher.publishEvent(new AfterCreateEvent(obj));
|
||||
|
||||
BeanWrapper wrapper = BeanWrapper.create(obj, conversionService);
|
||||
Link selfLink = entityLinks.linkForSingleResource(
|
||||
repoRequest.getPersistentEntity().getType(),
|
||||
wrapper.getProperty(repoRequest.getPersistentEntity().getIdProperty())
|
||||
).withSelfRel();
|
||||
Link selfLink = perAssembler.getSelfLinkFor(obj);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setLocation(URI.create(selfLink.getHref()));
|
||||
|
||||
if (config.isReturnBodyOnCreate()) {
|
||||
return resourceResponse(
|
||||
headers,
|
||||
new PersistentEntityResource<Object>(
|
||||
repoRequest.getPersistentEntity(),
|
||||
obj,
|
||||
selfLink
|
||||
).setBaseUri(repoRequest.getBaseUri()),
|
||||
HttpStatus.CREATED
|
||||
);
|
||||
return ControllerUtils.toResponseEntity(headers, perAssembler.toResource(obj), HttpStatus.CREATED);
|
||||
} else {
|
||||
return resourceResponse(headers, null, HttpStatus.CREATED);
|
||||
return ControllerUtils.toResponseEntity(headers, null, HttpStatus.CREATED);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@RequestMapping(
|
||||
value = BASE_MAPPING + "/{id}",
|
||||
method = RequestMethod.GET,
|
||||
produces = {
|
||||
"application/json",
|
||||
"application/x-spring-data-compact+json",
|
||||
"text/uri-list"
|
||||
}
|
||||
)
|
||||
/**
|
||||
* {@code GET /{repository}/{id}}
|
||||
*
|
||||
* @param repoRequest
|
||||
* @param id
|
||||
* @return
|
||||
* @throws ResourceNotFoundException
|
||||
*/
|
||||
@RequestMapping(value = BASE_MAPPING + "/{id}", method = RequestMethod.GET, produces = { "application/json",
|
||||
"application/x-spring-data-compact+json", "text/uri-list" })
|
||||
@ResponseBody
|
||||
public Resource<?> getSingleEntity(RepositoryRestRequest repoRequest,
|
||||
@PathVariable String id) throws ResourceNotFoundException {
|
||||
public Resource<?> getSingleEntity(RepositoryRestRequest repoRequest, @PathVariable String id)
|
||||
throws ResourceNotFoundException {
|
||||
RepositoryMethodInvoker repoMethodInvoker = repoRequest.getRepositoryMethodInvoker();
|
||||
if (null == repoMethodInvoker || !repoMethodInvoker.hasFindOne()) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
Object domainObj = domainClassConverter.convert(
|
||||
id,
|
||||
STRING_TYPE,
|
||||
TypeDescriptor.valueOf(repoRequest.getPersistentEntity().getType())
|
||||
);
|
||||
Object domainObj = converter.convert(id, STRING_TYPE,
|
||||
TypeDescriptor.valueOf(repoRequest.getPersistentEntity().getType()));
|
||||
|
||||
if (null == domainObj) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
PersistentEntityResource per = PersistentEntityResource.wrap(repoRequest.getPersistentEntity(),
|
||||
domainObj,
|
||||
repoRequest.getBaseUri());
|
||||
BeanWrapper wrapper = BeanWrapper.create(domainObj, conversionService);
|
||||
Link selfLink = entityLinks.linkForSingleResource(
|
||||
repoRequest.getPersistentEntity().getType(),
|
||||
wrapper.getProperty(repoRequest.getPersistentEntity().getIdProperty())
|
||||
).withSelfRel();
|
||||
per.add(selfLink);
|
||||
return per;
|
||||
return perAssembler.toResource(domainObj);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@RequestMapping(
|
||||
value = BASE_MAPPING + "/{id}",
|
||||
method = RequestMethod.PUT,
|
||||
consumes = {
|
||||
"application/json"
|
||||
},
|
||||
produces = {
|
||||
"application/json",
|
||||
"text/uri-list"
|
||||
}
|
||||
)
|
||||
/**
|
||||
* {@code PUT /{repository}/{id}} - Updates an existing entity or creates one at exactly that place.
|
||||
*
|
||||
* @param repoRequest
|
||||
* @param incoming
|
||||
* @param id
|
||||
* @return
|
||||
* @throws ResourceNotFoundException
|
||||
*/
|
||||
@RequestMapping(value = BASE_MAPPING + "/{id}", method = RequestMethod.PUT, consumes = { "application/json" },
|
||||
produces = { "application/json", "text/uri-list" })
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource<?>> updateEntity(RepositoryRestRequest repoRequest,
|
||||
PersistentEntityResource<?> incoming,
|
||||
@PathVariable String id) throws ResourceNotFoundException {
|
||||
PersistentEntityResource<Object> incoming, @PathVariable String id) throws ResourceNotFoundException {
|
||||
|
||||
RepositoryMethodInvoker repoMethodInvoker = repoRequest.getRepositoryMethodInvoker();
|
||||
if (null == repoMethodInvoker || !repoMethodInvoker.hasSaveOne() || !repoMethodInvoker.hasFindOne()) {
|
||||
throw new NoSuchMethodError();
|
||||
}
|
||||
|
||||
Object domainObj = domainClassConverter.convert(
|
||||
id,
|
||||
STRING_TYPE,
|
||||
TypeDescriptor.valueOf(repoRequest.getPersistentEntity().getType())
|
||||
);
|
||||
Object domainObj = converter.convert(id, STRING_TYPE,
|
||||
TypeDescriptor.valueOf(repoRequest.getPersistentEntity().getType()));
|
||||
if (null == domainObj) {
|
||||
BeanWrapper incomingWrapper = BeanWrapper.create(incoming.getContent(), conversionService);
|
||||
PersistentProperty idProp = incoming.getPersistentEntity().getIdProperty();
|
||||
BeanWrapper<?, Object> incomingWrapper = BeanWrapper.create(incoming.getContent(), conversionService);
|
||||
PersistentProperty<?> idProp = incoming.getPersistentEntity().getIdProperty();
|
||||
incomingWrapper.setProperty(idProp, conversionService.convert(id, idProp.getType()));
|
||||
return createNewEntity(repoRequest, incoming);
|
||||
}
|
||||
|
||||
domainObjectMerger.merge(incoming.getContent(), domainObj);
|
||||
|
||||
applicationContext.publishEvent(new BeforeSaveEvent(incoming.getContent()));
|
||||
publisher.publishEvent(new BeforeSaveEvent(incoming.getContent()));
|
||||
Object obj = repoMethodInvoker.save(domainObj);
|
||||
applicationContext.publishEvent(new AfterSaveEvent(obj));
|
||||
publisher.publishEvent(new AfterSaveEvent(obj));
|
||||
|
||||
if (config.isReturnBodyOnUpdate()) {
|
||||
PersistentEntityResource per = PersistentEntityResource.wrap(repoRequest.getPersistentEntity(),
|
||||
obj,
|
||||
repoRequest.getBaseUri());
|
||||
BeanWrapper wrapper = BeanWrapper.create(obj, conversionService);
|
||||
Link selfLink = entityLinks.linkForSingleResource(repoRequest.getPersistentEntity().getType(),
|
||||
wrapper.getProperty(repoRequest.getPersistentEntity()
|
||||
.getIdProperty()))
|
||||
.withSelfRel();
|
||||
per.add(selfLink);
|
||||
return resourceResponse(null,
|
||||
per,
|
||||
HttpStatus.OK);
|
||||
return ControllerUtils.toResponseEntity(null, perAssembler.toResource(obj), HttpStatus.OK);
|
||||
} else {
|
||||
return resourceResponse(null,
|
||||
null,
|
||||
HttpStatus.NO_CONTENT);
|
||||
return ControllerUtils.toResponseEntity(null, null, HttpStatus.NO_CONTENT);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@RequestMapping(
|
||||
value = BASE_MAPPING + "/{id}",
|
||||
method = RequestMethod.DELETE
|
||||
)
|
||||
@RequestMapping(value = BASE_MAPPING + "/{id}", method = RequestMethod.DELETE)
|
||||
@ResponseBody
|
||||
public ResponseEntity<?> deleteEntity(final RepositoryRestRequest repoRequest,
|
||||
@PathVariable final String id)
|
||||
public ResponseEntity<?> deleteEntity(final RepositoryRestRequest repoRequest, @PathVariable final String id)
|
||||
throws ResourceNotFoundException, HttpRequestMethodNotSupportedException {
|
||||
final RepositoryMethodInvoker repoMethodInvoker = repoRequest.getRepositoryMethodInvoker();
|
||||
if (null == repoMethodInvoker || (!repoMethodInvoker.hasFindOne()
|
||||
&& !(repoMethodInvoker.hasDeleteOne() || repoMethodInvoker.hasDeleteOneById()))) {
|
||||
if (null == repoMethodInvoker
|
||||
|| (!repoMethodInvoker.hasFindOne() && !(repoMethodInvoker.hasDeleteOne() || repoMethodInvoker
|
||||
.hasDeleteOneById()))) {
|
||||
throw new HttpRequestMethodNotSupportedException("DELETE");
|
||||
}
|
||||
ResourceMapping methodMapping = repoRequest.getRepositoryResourceMapping().getResourceMappingFor("delete");
|
||||
@@ -316,22 +287,20 @@ public class RepositoryEntityController extends AbstractRepositoryRestController
|
||||
throw new HttpRequestMethodNotSupportedException("DELETE");
|
||||
}
|
||||
|
||||
final Object domainObj = domainClassConverter.convert(id,
|
||||
STRING_TYPE,
|
||||
TypeDescriptor.valueOf(repoRequest.getPersistentEntity()
|
||||
.getType()));
|
||||
final Object domainObj = converter.convert(id, STRING_TYPE,
|
||||
TypeDescriptor.valueOf(repoRequest.getPersistentEntity().getType()));
|
||||
if (null == domainObj) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
applicationContext.publishEvent(new BeforeDeleteEvent(domainObj));
|
||||
publisher.publishEvent(new BeforeDeleteEvent(domainObj));
|
||||
TransactionCallbackWithoutResult callback = new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
if (repoMethodInvoker.hasDeleteOneById()) {
|
||||
Class<? extends Serializable> idType = (Class<? extends Serializable>) repoRequest.getPersistentEntity()
|
||||
.getIdProperty()
|
||||
.getType();
|
||||
.getIdProperty().getType();
|
||||
final Serializable idVal = conversionService.convert(id, idType);
|
||||
repoMethodInvoker.delete(idVal);
|
||||
} else if (repoMethodInvoker.hasDeleteOne()) {
|
||||
@@ -339,12 +308,16 @@ public class RepositoryEntityController extends AbstractRepositoryRestController
|
||||
}
|
||||
}
|
||||
};
|
||||
if (null != txTmpl) {
|
||||
txTmpl.execute(callback);
|
||||
|
||||
// FIXME
|
||||
|
||||
if (txOperations != null) {
|
||||
txOperations.execute(callback);
|
||||
} else {
|
||||
callback.doInTransaction(null);
|
||||
}
|
||||
applicationContext.publishEvent(new AfterDeleteEvent(domainObj));
|
||||
|
||||
publisher.publishEvent(new AfterDeleteEvent(domainObj));
|
||||
|
||||
return new ResponseEntity<Object>(HttpStatus.NO_CONTENT);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import static org.springframework.util.ClassUtils.*;
|
||||
@@ -15,27 +30,31 @@ import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class RepositoryInformationHandlerMethodArgumentResolver
|
||||
extends RepositoryInformationSupport
|
||||
implements HandlerMethodArgumentResolver {
|
||||
public class RepositoryInformationHandlerMethodArgumentResolver extends RepositoryInformationSupport implements
|
||||
HandlerMethodArgumentResolver {
|
||||
|
||||
@Override public boolean supportsParameter(MethodParameter parameter) {
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return isAssignable(parameter.getParameterType(), RepositoryInformation.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
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);
|
||||
}
|
||||
|
||||
String[] parts = requestUri.split("/");
|
||||
|
||||
if(parts.length == 0) {
|
||||
// Root request
|
||||
return null;
|
||||
@@ -43,6 +62,4 @@ public class RepositoryInformationHandlerMethodArgumentResolver
|
||||
|
||||
return findRepositoryInfoFor(parts[0]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,23 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import static org.springframework.data.rest.core.util.UriUtils.*;
|
||||
import static org.springframework.data.rest.repository.support.ResourceMappingUtils.*;
|
||||
import static org.springframework.data.rest.webmvc.ControllerUtils.*;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
@@ -10,7 +26,9 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
@@ -26,7 +44,7 @@ import org.springframework.data.rest.repository.context.AfterLinkSaveEvent;
|
||||
import org.springframework.data.rest.repository.context.BeforeLinkDeleteEvent;
|
||||
import org.springframework.data.rest.repository.context.BeforeLinkSaveEvent;
|
||||
import org.springframework.data.rest.repository.invoke.RepositoryMethodInvoker;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.data.web.PagedResourcesAssembler;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -43,21 +61,39 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public class RepositoryPropertyReferenceController extends AbstractRepositoryRestController {
|
||||
@RestController
|
||||
@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;
|
||||
private final DomainClassConverter<?> converter;
|
||||
|
||||
public RepositoryPropertyReferenceController(Repositories repositories,
|
||||
RepositoryRestConfiguration config,
|
||||
DomainClassConverter<?> domainClassConverter,
|
||||
ConversionService conversionService,
|
||||
EntityLinks entityLinks) {
|
||||
super(repositories,
|
||||
config,
|
||||
domainClassConverter,
|
||||
conversionService,
|
||||
entityLinks);
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
@Autowired
|
||||
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)
|
||||
*/
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.publisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
@@ -76,51 +112,36 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
final HttpHeaders headers = new HttpHeaders();
|
||||
Function<ReferencedProperty, Resource<?>> handler = new Function<ReferencedProperty, Resource<?>>() {
|
||||
@Override public Resource<?> apply(ReferencedProperty prop) {
|
||||
|
||||
if(null == prop.propertyValue) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
if(prop.property.isCollectionLike()) {
|
||||
|
||||
List<Resource<?>> resources = new ArrayList<Resource<?>>();
|
||||
PersistentEntity entity = repositories.getPersistentEntity(prop.propertyType);
|
||||
for(Object obj : ((Iterable)prop.propertyValue)) {
|
||||
BeanWrapper wrapper = BeanWrapper.create(obj, conversionService);
|
||||
PersistentEntityResource per = PersistentEntityResource.wrap(entity, obj, repoRequest.getBaseUri());
|
||||
Link selfLink = entityLinks.linkForSingleResource(entity.getType(),
|
||||
wrapper.getProperty(entity.getIdProperty()))
|
||||
.withSelfRel();
|
||||
per.add(selfLink);
|
||||
resources.add(per);
|
||||
|
||||
for(Object obj : ((Iterable<Object>) prop.propertyValue)) {
|
||||
resources.add(perAssembler.toResource(obj));
|
||||
}
|
||||
|
||||
return new Resource<Object>(resources);
|
||||
|
||||
} else if(prop.property.isMap()) {
|
||||
|
||||
Map<Object, Resource<?>> resources = new HashMap<Object, Resource<?>>();
|
||||
PersistentEntity entity = repositories.getPersistentEntity(prop.propertyType);
|
||||
for(Map.Entry<Object, Object> entry : ((Map<Object, Object>)prop.propertyValue).entrySet()) {
|
||||
PersistentEntityResource per = PersistentEntityResource.wrap(entity,
|
||||
entry.getValue(),
|
||||
repoRequest.getBaseUri());
|
||||
BeanWrapper wrapper = BeanWrapper.create(entry.getValue(), conversionService);
|
||||
Link selfLink = entityLinks.linkForSingleResource(entity.getType(),
|
||||
wrapper.getProperty(entity.getIdProperty()))
|
||||
.withSelfRel();
|
||||
per.add(selfLink);
|
||||
resources.put(entry.getKey(), per);
|
||||
|
||||
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 per = PersistentEntityResource.wrap(repositories.getPersistentEntity(prop.propertyType),
|
||||
prop.propertyValue,
|
||||
repoRequest.getBaseUri());
|
||||
BeanWrapper wrapper = BeanWrapper.create(prop.propertyValue, conversionService);
|
||||
Link selfLink = entityLinks.linkForSingleResource(prop.propertyType,
|
||||
wrapper.getProperty(prop.entity.getIdProperty()))
|
||||
.withSelfRel();
|
||||
per.add(selfLink);
|
||||
headers.set("Content-Location", selfLink.getHref());
|
||||
|
||||
return new Resource<Object>(per);
|
||||
|
||||
PersistentEntityResource<Object> resource = perAssembler.toResource(prop.propertyValue);
|
||||
headers.set("Content-Location", resource.getId().getHref());
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -128,7 +149,7 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
id,
|
||||
property,
|
||||
handler);
|
||||
return resourceResponse(headers, responseResource, HttpStatus.OK);
|
||||
return ControllerUtils.toResponseEntity(headers, responseResource, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
@@ -158,9 +179,9 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
prop.wrapper.setProperty(prop.property, null);
|
||||
}
|
||||
|
||||
applicationContext.publishEvent(new BeforeLinkDeleteEvent(prop.wrapper.getBean(), prop.propertyValue));
|
||||
publisher.publishEvent(new BeforeLinkDeleteEvent(prop.wrapper.getBean(), prop.propertyValue));
|
||||
Object result = repoMethodInvoker.save(prop.wrapper.getBean());
|
||||
applicationContext.publishEvent(new AfterLinkDeleteEvent(result, prop.propertyValue));
|
||||
publisher.publishEvent(new AfterLinkDeleteEvent(result, prop.propertyValue));
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -175,7 +196,7 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
}
|
||||
}
|
||||
|
||||
return resourceResponse(null, EMPTY_RESOURCE, HttpStatus.NO_CONTENT);
|
||||
return ControllerUtils.toResponseEntity(null, EMPTY_RESOURCE, HttpStatus.NO_CONTENT);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
@@ -201,31 +222,29 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
if(prop.property.isCollectionLike()) {
|
||||
PersistentEntity entity = repositories.getPersistentEntity(prop.propertyType);
|
||||
for(Object obj : ((Iterable)prop.propertyValue)) {
|
||||
BeanWrapper propValWrapper = BeanWrapper.create(obj, conversionService);
|
||||
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)) {
|
||||
PersistentEntityResource per = PersistentEntityResource.wrap(entity, obj, repoRequest.getBaseUri());
|
||||
Link selfLink = entityLinks.linkForSingleResource(entity.getType(), sId).withSelfRel();
|
||||
per.add(selfLink);
|
||||
headers.set("Content-Location", selfLink.getHref());
|
||||
return per;
|
||||
|
||||
PersistentEntityResource<Object> resource = perAssembler.toResource(obj);
|
||||
headers.set("Content-Location", resource.getId().getHref());
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
} else if(prop.property.isMap()) {
|
||||
PersistentEntity entity = repositories.getPersistentEntity(prop.propertyType);
|
||||
for(Map.Entry<Object, Object> entry : ((Map<Object, Object>)prop.propertyValue).entrySet()) {
|
||||
BeanWrapper propValWrapper = BeanWrapper.create(entry.getValue(), conversionService);
|
||||
|
||||
BeanWrapper<?, Object> propValWrapper = BeanWrapper.create(entry.getValue(), null);
|
||||
String sId = propValWrapper.getProperty(prop.entity.getIdProperty()).toString();
|
||||
|
||||
if(propertyId.equals(sId)) {
|
||||
PersistentEntityResource per = PersistentEntityResource.wrap(entity,
|
||||
entry.getValue(),
|
||||
repoRequest.getBaseUri());
|
||||
Link selfLink = entityLinks.linkForSingleResource(entity.getType(), sId).withSelfRel();
|
||||
per.add(selfLink);
|
||||
headers.set("Content-Location", selfLink.getHref());
|
||||
return per;
|
||||
|
||||
PersistentEntityResource<Object> resource = perAssembler.toResource(entry.getValue());
|
||||
headers.set("Content-Location", resource.getId().getHref());
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -234,11 +253,9 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
throw new IllegalArgumentException(new ResourceNotFoundException());
|
||||
}
|
||||
};
|
||||
Resource<?> responseResource = doWithReferencedProperty(repoRequest,
|
||||
id,
|
||||
property,
|
||||
handler);
|
||||
return resourceResponse(headers, responseResource, HttpStatus.OK);
|
||||
|
||||
Resource<?> responseResource = doWithReferencedProperty(repoRequest, id, property, handler);
|
||||
return ControllerUtils.toResponseEntity(headers, responseResource, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
@@ -263,7 +280,7 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
ResourceMapping entityMapping = repoRequest.getPersistentEntityResourceMapping();
|
||||
String propName = entityMapping.getNameForPath(property);
|
||||
ResourceMapping propMapping = entityMapping.getResourceMappingFor(entityMapping.getNameForPath(property));
|
||||
PersistentProperty persistentProp = repoRequest.getPersistentEntity().getPersistentProperty(propName);
|
||||
PersistentProperty<?> persistentProp = repoRequest.getPersistentEntity().getPersistentProperty(propName);
|
||||
Class<?> propType = (persistentProp.isCollectionLike() || persistentProp.isMap()
|
||||
? persistentProp.getComponentType()
|
||||
: persistentProp.getType());
|
||||
@@ -290,15 +307,14 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
}
|
||||
} 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(), conversionService.convert(entry.getKey(),
|
||||
String.class));
|
||||
Link l = new Link(entry.getValue().getLink("self").getHref(), entry.getKey().toString());
|
||||
links.add(l);
|
||||
}
|
||||
} else {
|
||||
links.add(new Link(entityBaseUri.toString(), propRel));
|
||||
}
|
||||
|
||||
return resourceResponse(null, new Resource<Object>(EMPTY_RESOURCE_LIST, links), HttpStatus.OK);
|
||||
return ControllerUtils.toResponseEntity(null, new Resource<Object>(EMPTY_RESOURCE_LIST, links), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
@@ -326,9 +342,9 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
Function<ReferencedProperty, Resource<?>> handler = new Function<ReferencedProperty, Resource<?>>() {
|
||||
@Override public Resource<?> apply(ReferencedProperty prop) {
|
||||
if(prop.property.isCollectionLike()) {
|
||||
Collection coll = new ArrayList();
|
||||
Collection<Object> coll = new ArrayList<Object>();
|
||||
if("POST".equals(repoRequest.getRequest().getMethod())) {
|
||||
coll.addAll((Collection)prop.propertyValue);
|
||||
coll.addAll((Collection<Object>)prop.propertyValue);
|
||||
}
|
||||
for(Link l : incoming.getLinks()) {
|
||||
Object propVal = loadPropertyValue(prop.propertyType, l.getHref());
|
||||
@@ -336,9 +352,9 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
}
|
||||
prop.wrapper.setProperty(prop.property, coll);
|
||||
} else if(prop.property.isMap()) {
|
||||
Map m = new HashMap();
|
||||
Map<String, Object> m = new HashMap<String, Object>();
|
||||
if("POST".equals(repoRequest.getRequest().getMethod())) {
|
||||
m.putAll((Map)prop.propertyValue);
|
||||
m.putAll((Map<String, Object>)prop.propertyValue);
|
||||
}
|
||||
for(Link l : incoming.getLinks()) {
|
||||
Object propVal = loadPropertyValue(prop.propertyType, l.getHref());
|
||||
@@ -358,9 +374,9 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
prop.wrapper.setProperty(prop.property, propVal);
|
||||
}
|
||||
|
||||
applicationContext.publishEvent(new BeforeLinkSaveEvent(prop.wrapper.getBean(), prop.propertyValue));
|
||||
publisher.publishEvent(new BeforeLinkSaveEvent(prop.wrapper.getBean(), prop.propertyValue));
|
||||
Object result = repoMethodInvoker.save(prop.wrapper.getBean());
|
||||
applicationContext.publishEvent(new AfterLinkSaveEvent(result, prop.propertyValue));
|
||||
publisher.publishEvent(new AfterLinkSaveEvent(result, prop.propertyValue));
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -368,7 +384,7 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
id,
|
||||
property,
|
||||
handler);
|
||||
return resourceResponse(null, EMPTY_RESOURCE, HttpStatus.CREATED);
|
||||
return ControllerUtils.toResponseEntity(null, EMPTY_RESOURCE, HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
@@ -392,20 +408,20 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
return null;
|
||||
}
|
||||
if(prop.property.isCollectionLike()) {
|
||||
Collection coll = new ArrayList();
|
||||
for(Object obj : (Collection)prop.propertyValue) {
|
||||
BeanWrapper propValWrapper = BeanWrapper.create(obj, conversionService);
|
||||
String s = (String)propValWrapper.getProperty(prop.entity.getIdProperty(), String.class, false);
|
||||
Collection<Object> coll = new ArrayList<Object>();
|
||||
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)) {
|
||||
coll.add(obj);
|
||||
}
|
||||
}
|
||||
prop.wrapper.setProperty(prop.property, coll);
|
||||
} else if(prop.property.isMap()) {
|
||||
Map m = new HashMap();
|
||||
Map<Object, Object> m = new HashMap<Object, Object>();
|
||||
for(Map.Entry<Object, Object> entry : ((Map<Object, Object>)prop.propertyValue).entrySet()) {
|
||||
BeanWrapper propValWrapper = BeanWrapper.create(entry.getValue(), conversionService);
|
||||
String s = (String)propValWrapper.getProperty(prop.entity.getIdProperty(), String.class, false);
|
||||
BeanWrapper<?, Object> propValWrapper = BeanWrapper.create(entry.getValue(), null);
|
||||
String s = propValWrapper.getProperty(prop.entity.getIdProperty()).toString();
|
||||
if(!propertyId.equals(s)) {
|
||||
m.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
@@ -415,9 +431,9 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
prop.wrapper.setProperty(prop.property, null);
|
||||
}
|
||||
|
||||
applicationContext.publishEvent(new BeforeLinkDeleteEvent(prop.wrapper.getBean(), prop.propertyValue));
|
||||
publisher.publishEvent(new BeforeLinkDeleteEvent(prop.wrapper.getBean(), prop.propertyValue));
|
||||
Object result = repoMethodInvoker.save(prop.wrapper.getBean());
|
||||
applicationContext.publishEvent(new AfterLinkDeleteEvent(result, prop.propertyValue));
|
||||
publisher.publishEvent(new AfterLinkDeleteEvent(result, prop.propertyValue));
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -426,7 +442,7 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
property,
|
||||
handler);
|
||||
|
||||
return resourceResponse(null, EMPTY_RESOURCE, HttpStatus.NO_CONTENT);
|
||||
return ControllerUtils.toResponseEntity(null, EMPTY_RESOURCE, HttpStatus.NO_CONTENT);
|
||||
}
|
||||
|
||||
private Link propertyReferenceLink(Resource<?> resource,
|
||||
@@ -439,7 +455,7 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
|
||||
private Object loadPropertyValue(Class<?> type, String href) {
|
||||
String id = href.substring(href.lastIndexOf('/') + 1);
|
||||
return domainClassConverter.convert(id,
|
||||
return converter.convert(id,
|
||||
STRING_TYPE,
|
||||
TypeDescriptor.valueOf(type));
|
||||
}
|
||||
@@ -454,21 +470,20 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
throw new NoSuchMethodException();
|
||||
}
|
||||
|
||||
Object domainObj = domainClassConverter.convert(id,
|
||||
STRING_TYPE,
|
||||
TypeDescriptor.valueOf(repoRequest.getPersistentEntity()
|
||||
.getType()));
|
||||
Object domainObj = converter.convert(id, STRING_TYPE,
|
||||
TypeDescriptor.valueOf(repoRequest.getPersistentEntity().getType()));
|
||||
|
||||
if(null == domainObj) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
String propertyName = repoRequest.getPersistentEntityResourceMapping().getNameForPath(propertyPath);
|
||||
PersistentProperty prop = repoRequest.getPersistentEntity().getPersistentProperty(propertyName);
|
||||
PersistentProperty<?> prop = repoRequest.getPersistentEntity().getPersistentProperty(propertyName);
|
||||
if(null == prop) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
BeanWrapper wrapper = BeanWrapper.create(domainObj, conversionService);
|
||||
BeanWrapper<?, Object> wrapper = BeanWrapper.create(domainObj, null);
|
||||
Object propVal = wrapper.getProperty(prop);
|
||||
|
||||
return handler.apply(new ReferencedProperty(prop,
|
||||
@@ -477,15 +492,17 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
}
|
||||
|
||||
private class ReferencedProperty {
|
||||
final PersistentEntity entity;
|
||||
final PersistentProperty property;
|
||||
final Class<?> propertyType;
|
||||
final Object propertyValue;
|
||||
final BeanWrapper wrapper;
|
||||
|
||||
final PersistentEntity<?, ?> entity;
|
||||
final PersistentProperty<?> property;
|
||||
final Class<?> propertyType;
|
||||
final Object propertyValue;
|
||||
final BeanWrapper<?, ?>wrapper;
|
||||
|
||||
private ReferencedProperty(PersistentProperty property,
|
||||
private ReferencedProperty(PersistentProperty<?> property,
|
||||
Object propertyValue,
|
||||
BeanWrapper wrapper) {
|
||||
BeanWrapper<?, ?> wrapper) {
|
||||
|
||||
this.property = property;
|
||||
this.propertyValue = propertyValue;
|
||||
this.wrapper = wrapper;
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import static org.springframework.data.rest.repository.support.ResourceMappingUtils.*;
|
||||
import static org.springframework.util.StringUtils.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.config.ResourceMapping;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.webmvc.support.JpaHelper;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor;
|
||||
@@ -36,9 +36,12 @@ public class RepositoryRestHandlerMapping extends RequestMappingHandlerMapping {
|
||||
private RepositoryRestConfiguration config;
|
||||
@Autowired(required = false)
|
||||
private JpaHelper jpaHelper;
|
||||
|
||||
private final ResourceMappings mappings;
|
||||
|
||||
public RepositoryRestHandlerMapping() {
|
||||
public RepositoryRestHandlerMapping(ResourceMappings mappings) {
|
||||
setOrder(Ordered.LOWEST_PRECEDENCE);
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,11 +84,9 @@ public class RepositoryRestHandlerMapping extends RequestMappingHandlerMapping {
|
||||
// Root request
|
||||
return super.lookupHandlerMethod(lookupPath, request);
|
||||
}
|
||||
|
||||
|
||||
for(Class<?> domainType : repositories) {
|
||||
RepositoryInformation repoInfo = repositories.getRepositoryInformationFor(domainType);
|
||||
ResourceMapping mapping = getResourceMapping(config, repoInfo);
|
||||
if(mapping.getPath().equals(parts[0]) && mapping.isExported()) {
|
||||
if (mappings.exportsMappingFor(domainType)) {
|
||||
return super.lookupHandlerMethod(lookupPath, request);
|
||||
}
|
||||
}
|
||||
@@ -94,10 +95,7 @@ public class RepositoryRestHandlerMapping extends RequestMappingHandlerMapping {
|
||||
}
|
||||
|
||||
@Override protected boolean isHandler(Class<?> beanType) {
|
||||
return (RepositoryController.class.isAssignableFrom(beanType)
|
||||
|| RepositoryEntityController.class.isAssignableFrom(beanType)
|
||||
|| RepositoryPropertyReferenceController.class.isAssignableFrom(beanType)
|
||||
|| RepositorySearchController.class.isAssignableFrom(beanType));
|
||||
return AnnotationUtils.findAnnotation(beanType, RestController.class) != null;
|
||||
}
|
||||
|
||||
@Override protected void extendInterceptors(List<Object> interceptors) {
|
||||
|
||||
@@ -1,34 +1,44 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import static org.springframework.data.rest.core.util.UriUtils.*;
|
||||
import static org.springframework.data.rest.repository.support.ResourceMappingUtils.*;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.config.ResourceMapping;
|
||||
import org.springframework.data.rest.repository.PagingAndSorting;
|
||||
import org.springframework.data.rest.repository.invoke.RepositoryMethodInvoker;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
class RepositoryRestRequest {
|
||||
|
||||
private final RepositoryRestConfiguration config;
|
||||
private final HttpServletRequest request;
|
||||
private final PagingAndSorting pagingAndSorting;
|
||||
private final URI baseUri;
|
||||
private final RepositoryInformation repoInfo;
|
||||
private final ResourceMapping repoMapping;
|
||||
private final Link repoLink;
|
||||
private final Object repository;
|
||||
@@ -39,14 +49,11 @@ class RepositoryRestRequest {
|
||||
public RepositoryRestRequest(RepositoryRestConfiguration config,
|
||||
Repositories repositories,
|
||||
HttpServletRequest request,
|
||||
PagingAndSorting pagingAndSorting,
|
||||
URI baseUri,
|
||||
RepositoryInformation repoInfo) {
|
||||
this.config = config;
|
||||
RepositoryInformation repoInfo,
|
||||
ConversionService conversionService) {
|
||||
this.request = request;
|
||||
this.pagingAndSorting = pagingAndSorting;
|
||||
this.baseUri = baseUri;
|
||||
this.repoInfo = repoInfo;
|
||||
this.repoMapping = getResourceMapping(config, repoInfo);
|
||||
if(null == repoMapping || !repoMapping.isExported()) {
|
||||
this.repoLink = null;
|
||||
@@ -58,7 +65,7 @@ class RepositoryRestRequest {
|
||||
this.repoLink = new Link(buildUri(baseUri, repoMapping.getPath()).toString(), repoMapping.getRel());
|
||||
this.repository = repositories.getRepositoryFor(repoInfo.getDomainType());
|
||||
this.persistentEntity = repositories.getPersistentEntity(repoInfo.getDomainType());
|
||||
this.repoMethodInvoker = new RepositoryMethodInvoker(repository, repoInfo);
|
||||
this.repoMethodInvoker = new RepositoryMethodInvoker(repository, repoInfo, conversionService);
|
||||
this.entityMapping = getResourceMapping(config, persistentEntity);
|
||||
}
|
||||
}
|
||||
@@ -67,18 +74,10 @@ class RepositoryRestRequest {
|
||||
return request;
|
||||
}
|
||||
|
||||
PagingAndSorting getPagingAndSorting() {
|
||||
return pagingAndSorting;
|
||||
}
|
||||
|
||||
URI getBaseUri() {
|
||||
return baseUri;
|
||||
}
|
||||
|
||||
RepositoryInformation getRepositoryInformation() {
|
||||
return repoInfo;
|
||||
}
|
||||
|
||||
ResourceMapping getRepositoryResourceMapping() {
|
||||
return repoMapping;
|
||||
}
|
||||
@@ -87,10 +86,6 @@ class RepositoryRestRequest {
|
||||
return repoLink;
|
||||
}
|
||||
|
||||
Object getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
RepositoryMethodInvoker getRepositoryMethodInvoker() {
|
||||
return repoMethodInvoker;
|
||||
}
|
||||
@@ -102,40 +97,4 @@ class RepositoryRestRequest {
|
||||
ResourceMapping getPersistentEntityResourceMapping() {
|
||||
return entityMapping;
|
||||
}
|
||||
|
||||
void addNextLink(Page<?> page, List<Link> links) {
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromUri(baseUri);
|
||||
// Add existing query parameters
|
||||
addQueryParameters(request, builder);
|
||||
|
||||
builder.queryParam(config.getPageParamName(), page.getNumber() + 1)
|
||||
.queryParam(config.getLimitParamName(), pagingAndSorting.getPageSize());
|
||||
|
||||
links.add(new Link(builder.build().toString(), "page.next"));
|
||||
}
|
||||
|
||||
void addPrevLink(Page<?> page, List<Link> links) {
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromUri(baseUri);
|
||||
// Add existing query parameters
|
||||
addQueryParameters(request, builder);
|
||||
|
||||
builder.queryParam(config.getPageParamName(), page.getNumber() - 1)
|
||||
.queryParam(config.getLimitParamName(), pagingAndSorting.getPageSize());
|
||||
|
||||
links.add(new Link(builder.build().toString(), "page.previous"));
|
||||
}
|
||||
|
||||
private void addQueryParameters(HttpServletRequest request,
|
||||
UriComponentsBuilder builder) {
|
||||
for(Enumeration<String> names = request.getParameterNames(); names.hasMoreElements(); ) {
|
||||
String name = names.nextElement();
|
||||
String value = request.getParameter(name);
|
||||
if(name.equals(config.getPageParamName()) || name.equals(config.getLimitParamName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.queryParam(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import java.net.URI;
|
||||
@@ -5,10 +20,10 @@ import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.repository.PagingAndSorting;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
@@ -16,9 +31,12 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class RepositoryRestRequestHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
@Autowired
|
||||
private RepositoryRestConfiguration config;
|
||||
@Autowired
|
||||
@@ -26,11 +44,14 @@ public class RepositoryRestRequestHandlerMethodArgumentResolver implements Handl
|
||||
@Autowired
|
||||
private RepositoryInformationHandlerMethodArgumentResolver repoInfoResolver;
|
||||
@Autowired
|
||||
private PagingAndSortingMethodArgumentResolver pagingAndSortingResolver;
|
||||
@Autowired
|
||||
private BaseUriMethodArgumentResolver baseUriResolver;
|
||||
|
||||
public RepositoryRestRequestHandlerMethodArgumentResolver(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@Override public boolean supportsParameter(MethodParameter parameter) {
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return RepositoryRestRequest.class.isAssignableFrom(parameter.getParameterType());
|
||||
}
|
||||
|
||||
@@ -39,25 +60,11 @@ public class RepositoryRestRequestHandlerMethodArgumentResolver implements Handl
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
PagingAndSorting pagingAndSorting = (PagingAndSorting)pagingAndSortingResolver.resolveArgument(parameter,
|
||||
mavContainer,
|
||||
webRequest,
|
||||
binderFactory);
|
||||
RepositoryInformation repoInfo = (RepositoryInformation)repoInfoResolver.resolveArgument(parameter,
|
||||
mavContainer,
|
||||
webRequest,
|
||||
binderFactory);
|
||||
URI baseUri = (URI)baseUriResolver.resolveArgument(parameter,
|
||||
mavContainer,
|
||||
webRequest,
|
||||
binderFactory);
|
||||
|
||||
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),
|
||||
pagingAndSorting,
|
||||
baseUri,
|
||||
repoInfo);
|
||||
return new RepositoryRestRequest(config, repositories, webRequest.getNativeRequest(HttpServletRequest.class), baseUri, repoInfo, conversionService);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,26 +1,45 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import static org.springframework.data.rest.core.util.UriUtils.*;
|
||||
import static org.springframework.data.rest.repository.support.ResourceMappingUtils.*;
|
||||
import static org.springframework.data.rest.webmvc.ControllerUtils.*;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.repository.support.DomainClassConverter;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.config.ResourceMapping;
|
||||
import org.springframework.data.rest.repository.PagingAndSorting;
|
||||
import org.springframework.data.rest.repository.invoke.RepositoryMethod;
|
||||
import org.springframework.data.rest.repository.invoke.RepositoryMethodInvoker;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.data.rest.repository.support.ResourceMappingUtils;
|
||||
import org.springframework.data.rest.webmvc.support.BaseUriLinkBuilder;
|
||||
import org.springframework.data.web.PagedResourcesAssembler;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkBuilder;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.hateoas.Resources;
|
||||
@@ -31,21 +50,27 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class RepositorySearchController extends AbstractRepositoryRestController {
|
||||
@RestController
|
||||
@SuppressWarnings("deprecation")
|
||||
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,
|
||||
DomainClassConverter<?> domainClassConverter,
|
||||
ConversionService conversionService,
|
||||
EntityLinks entityLinks) {
|
||||
super(repositories,
|
||||
config,
|
||||
domainClassConverter,
|
||||
conversionService,
|
||||
entityLinks);
|
||||
PagedResourcesAssembler<Object> assembler,
|
||||
PersistentEntityResourceAssembler<Object> perAssembler) {
|
||||
|
||||
super(assembler, perAssembler);
|
||||
|
||||
this.repositories = repositories;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
@@ -66,6 +91,24 @@ public class RepositorySearchController extends AbstractRepositoryRestController
|
||||
}
|
||||
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);
|
||||
ResourceMapping repoMapping = ResourceMappingUtils.merge(repoInfo.getRepositoryInterface(),
|
||||
config.getResourceMappingForRepository(repoInfo.getRepositoryInterface()));
|
||||
for (Method method : repoInfo.getQueryMethods()) {
|
||||
LinkBuilder linkBuilder = BaseUriLinkBuilder.create(buildUri(baseUri, repoMapping.getPath(), "search"));
|
||||
ResourceMapping methodMapping = ResourceMappingUtils.merge(method,
|
||||
repoMapping.getResourceMappingFor(method.getName()));
|
||||
if (!methodMapping.isExported()) {
|
||||
continue;
|
||||
}
|
||||
links
|
||||
.add(linkBuilder.slash(methodMapping.getPath()).withRel(repoMapping.getRel() + "." + methodMapping.getRel()));
|
||||
}
|
||||
return links;
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
value = BASE_MAPPING + "/{method}",
|
||||
@@ -78,7 +121,7 @@ public class RepositorySearchController extends AbstractRepositoryRestController
|
||||
@ResponseBody
|
||||
public ResourceSupport query(final RepositoryRestRequest repoRequest,
|
||||
@PathVariable String repository,
|
||||
@PathVariable String method)
|
||||
@PathVariable String method, Pageable pageable)
|
||||
throws ResourceNotFoundException {
|
||||
RepositoryMethodInvoker repoMethodInvoker = repoRequest.getRepositoryMethodInvoker();
|
||||
if(repoMethodInvoker.getQueryMethods().isEmpty()) {
|
||||
@@ -100,50 +143,15 @@ public class RepositorySearchController extends AbstractRepositoryRestController
|
||||
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();
|
||||
|
||||
PagingAndSorting pageSort = repoRequest.getPagingAndSorting();
|
||||
List<MethodParameter> methodParams = repoMethod.getParameters();
|
||||
Object[] paramValues = new Object[methodParams.size()];
|
||||
if(!methodParams.isEmpty()) {
|
||||
for(int i = 0; i < paramValues.length; i++) {
|
||||
MethodParameter param = methodParams.get(i);
|
||||
if(Pageable.class.isAssignableFrom(param.getParameterType())) {
|
||||
paramValues[i] = new PageRequest(pageSort.getPageNumber(),
|
||||
pageSort.getPageSize(),
|
||||
pageSort.getSort());
|
||||
} else if(Sort.class.isAssignableFrom(param.getParameterType())) {
|
||||
paramValues[i] = pageSort.getSort();
|
||||
} else {
|
||||
String paramName = repoMethod.getParameterNames().get(i);
|
||||
String[] queryParamVals = repoRequest.getRequest().getParameterValues(paramName);
|
||||
if(null == queryParamVals) {
|
||||
if(paramName.startsWith("arg")) {
|
||||
throw new IllegalArgumentException("No @Param annotation found on query method "
|
||||
+ repoMethod.getMethod().getName()
|
||||
+ " for parameter " + param.getParameterName());
|
||||
} else {
|
||||
throw new IllegalArgumentException("No query parameter specified for "
|
||||
+ repoMethod.getMethod().getName() + " param '"
|
||||
+ paramName + "'");
|
||||
}
|
||||
}
|
||||
paramValues[i] = methodParameterConversionService.convert(queryParamVals, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object result = repoMethodInvoker.invokeQueryMethod(repoMethod, paramValues);
|
||||
Link prevLink = null;
|
||||
Link nextLink = null;
|
||||
if(result instanceof Page) {
|
||||
if(((Page<?>)result).hasPreviousPage() && pageSort.getPageNumber() > 0) {
|
||||
prevLink = searchLink(repoRequest, 0, method, "page.previous");
|
||||
}
|
||||
if(((Page<?>)result).hasNextPage()) {
|
||||
nextLink = searchLink(repoRequest, 1, method, "page.next");
|
||||
}
|
||||
}
|
||||
return resultToResources(repoRequest, result, new ArrayList<Link>(), prevLink, nextLink);
|
||||
return resultToResources(result, baseLink);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
@@ -156,11 +164,12 @@ public class RepositorySearchController extends AbstractRepositoryRestController
|
||||
@ResponseBody
|
||||
public ResourceSupport queryCompact(RepositoryRestRequest repoRequest,
|
||||
@PathVariable String repository,
|
||||
@PathVariable String method)
|
||||
@PathVariable String method,
|
||||
Pageable pageable)
|
||||
throws ResourceNotFoundException {
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
|
||||
ResourceSupport resource = query(repoRequest, repository, method);
|
||||
ResourceSupport resource = query(repoRequest, repository, method, pageable);
|
||||
links.addAll(resource.getLinks());
|
||||
|
||||
if(resource instanceof Resources && ((Resources<?>) resource).getContent() != null) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.ResourceProcessor;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.hateoas.Resources;
|
||||
import org.springframework.hateoas.mvc.HeaderLinksResponseEntity;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -58,6 +59,7 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
|
||||
private final HandlerMethodReturnValueHandler delegate;
|
||||
private final List<ProcessorWrapper> processors;
|
||||
private boolean rootLinksAsHeaders = false;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ResourceProcessorHandlerMethodReturnValueHandler} using the given delegate to eventually
|
||||
@@ -95,6 +97,13 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
|
||||
Collections.sort(this.processors, AnnotationAwareOrderComparator.INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rootLinksAsHeaders the rootLinksAsHeaders to set
|
||||
*/
|
||||
public void setRootLinksAsHeaders(boolean rootLinksAsHeaders) {
|
||||
this.rootLinksAsHeaders = rootLinksAsHeaders;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -159,14 +168,14 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
ReflectionUtils.setField(CONTENT_FIELD, resources, result);
|
||||
}
|
||||
|
||||
Object result = invokeProcessorsFor(value, targetType);
|
||||
ResourceSupport result = (ResourceSupport) invokeProcessorsFor(value, targetType);
|
||||
delegate.handleReturnValue(rewrapResult(result, returnValue), returnType, mavContainer, webRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes all registered {@link ResourceProcessor}s registered for the given {@link TypeInformation}.
|
||||
*
|
||||
* @param value the object to process
|
||||
* @param value the object to process
|
||||
* @param targetType
|
||||
* @return
|
||||
*/
|
||||
@@ -192,19 +201,28 @@ public class ResourceProcessorHandlerMethodReturnValueHandler implements Handler
|
||||
* @param originalValue the original input value.
|
||||
* @return
|
||||
*/
|
||||
static Object rewrapResult(Object newBody, Object originalValue) {
|
||||
Object rewrapResult(ResourceSupport newBody, Object originalValue) {
|
||||
|
||||
if (!(originalValue instanceof HttpEntity)) {
|
||||
return newBody;
|
||||
}
|
||||
|
||||
HttpEntity<ResourceSupport> entity = null;
|
||||
|
||||
if (originalValue instanceof ResponseEntity) {
|
||||
ResponseEntity<?> source = (ResponseEntity<?>) originalValue;
|
||||
return new ResponseEntity<Object>(newBody, source.getHeaders(), source.getStatusCode());
|
||||
entity = new ResponseEntity<ResourceSupport>(newBody, source.getHeaders(), source.getStatusCode());
|
||||
} else {
|
||||
HttpEntity<?> source = (HttpEntity<?>) originalValue;
|
||||
return new HttpEntity<Object>(newBody, source.getHeaders());
|
||||
entity = new HttpEntity<ResourceSupport>(newBody, source.getHeaders());
|
||||
}
|
||||
|
||||
return addLinksToHeaderWrapper(entity);
|
||||
}
|
||||
|
||||
private HttpEntity<?> addLinksToHeaderWrapper(HttpEntity<ResourceSupport> entity) {
|
||||
|
||||
return rootLinksAsHeaders ? HeaderLinksResponseEntity.wrap(entity) : entity;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Documented
|
||||
@Component
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
|
||||
public @interface RestController {
|
||||
|
||||
}
|
||||
@@ -1,47 +1,65 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportResource;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.convert.support.ConfigurableConversionService;
|
||||
import org.springframework.data.repository.support.DomainClassConverter;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.convert.ISO8601DateConverter;
|
||||
import org.springframework.data.rest.convert.UUIDConverter;
|
||||
import org.springframework.data.rest.repository.UriDomainClassConverter;
|
||||
import org.springframework.data.rest.repository.context.AnnotatedHandlerBeanPostProcessor;
|
||||
import org.springframework.data.rest.repository.context.RepositoriesFactoryBean;
|
||||
import org.springframework.data.rest.repository.context.ValidatingRepositoryEventListener;
|
||||
import org.springframework.data.rest.repository.json.Jackson2DatatypeHelper;
|
||||
import org.springframework.data.rest.repository.json.PersistentEntityJackson2Module;
|
||||
import org.springframework.data.rest.repository.json.PersistentEntityToJsonSchemaConverter;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.repository.support.DomainObjectMerger;
|
||||
import org.springframework.data.rest.webmvc.BaseUriMethodArgumentResolver;
|
||||
import org.springframework.data.rest.webmvc.PagingAndSortingMethodArgumentResolver;
|
||||
import org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler;
|
||||
import org.springframework.data.rest.webmvc.PersistentEntityResourceHandlerMethodArgumentResolver;
|
||||
import org.springframework.data.rest.webmvc.RepositoryController;
|
||||
import org.springframework.data.rest.webmvc.RepositoryEntityController;
|
||||
import org.springframework.data.rest.webmvc.RepositoryInformationHandlerMethodArgumentResolver;
|
||||
import org.springframework.data.rest.webmvc.RepositoryPropertyReferenceController;
|
||||
import org.springframework.data.rest.webmvc.RepositoryRestHandlerAdapter;
|
||||
import org.springframework.data.rest.webmvc.RepositoryRestHandlerMapping;
|
||||
import org.springframework.data.rest.webmvc.RepositoryRestRequestHandlerMethodArgumentResolver;
|
||||
import org.springframework.data.rest.webmvc.RepositorySearchController;
|
||||
import org.springframework.data.rest.webmvc.RestController;
|
||||
import org.springframework.data.rest.webmvc.ServerHttpRequestMethodArgumentResolver;
|
||||
import org.springframework.data.rest.webmvc.convert.UriListHttpMessageConverter;
|
||||
import org.springframework.data.rest.webmvc.json.Jackson2DatatypeHelper;
|
||||
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module;
|
||||
import org.springframework.data.rest.webmvc.json.PersistentEntityToJsonSchemaConverter;
|
||||
import org.springframework.data.rest.webmvc.support.JpaHelper;
|
||||
import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks;
|
||||
import org.springframework.data.rest.webmvc.support.ValidationExceptionHandler;
|
||||
import org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.RelProvider;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
@@ -51,33 +69,41 @@ import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExc
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
|
||||
/**
|
||||
* Main application configuration for Spring Data REST. To customize how the exporter works, subclass this and override
|
||||
* any of the {@literal configure*} methods.
|
||||
* <p/>
|
||||
* Any XML files located in the classpath under the {@literal META-INF/spring-data-rest/} path will be automatically
|
||||
* found and loaded into this {@link org.springframework.context.ApplicationContext}.
|
||||
*
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan(basePackageClasses = RestController.class, includeFilters = @Filter(RestController.class), useDefaultFilters = false)
|
||||
@ImportResource("classpath*:META-INF/spring-data-rest/**/*.xml")
|
||||
public class RepositoryRestMvcConfiguration {
|
||||
public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebConfiguration {
|
||||
|
||||
private static final boolean IS_JAVAX_VALIDATION_AVAILABLE = ClassUtils.isPresent(
|
||||
"javax.validation.ConstraintViolationException",
|
||||
RepositoryRestMvcConfiguration.class.getClassLoader()
|
||||
);
|
||||
private static final boolean IS_JPA_AVAILABLE = ClassUtils.isPresent(
|
||||
"javax.persistence.EntityManager",
|
||||
RepositoryRestMvcConfiguration.class.getClassLoader()
|
||||
);
|
||||
"javax.validation.ConstraintViolationException", RepositoryRestMvcConfiguration.class.getClassLoader());
|
||||
private static final boolean IS_JPA_AVAILABLE = ClassUtils.isPresent("javax.persistence.EntityManager",
|
||||
RepositoryRestMvcConfiguration.class.getClassLoader());
|
||||
|
||||
@Bean public RepositoriesFactoryBean repositories() {
|
||||
return new RepositoriesFactoryBean();
|
||||
@Autowired ListableBeanFactory beanFactory;
|
||||
|
||||
@Bean
|
||||
public Repositories repositories() {
|
||||
return new Repositories(beanFactory);
|
||||
}
|
||||
|
||||
@Bean public DefaultFormattingConversionService defaultConversionService() {
|
||||
@Bean
|
||||
@Qualifier
|
||||
public DefaultFormattingConversionService defaultConversionService() {
|
||||
|
||||
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
|
||||
conversionService.addConverter(UUIDConverter.INSTANCE);
|
||||
conversionService.addConverter(ISO8601DateConverter.INSTANCE);
|
||||
@@ -85,34 +111,41 @@ public class RepositoryRestMvcConfiguration {
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
@Bean public DomainClassConverter<?> domainClassConverter() {
|
||||
@Bean
|
||||
public DomainClassConverter<?> domainClassConverter() {
|
||||
return new DomainClassConverter<DefaultFormattingConversionService>(defaultConversionService());
|
||||
}
|
||||
|
||||
@Bean public UriDomainClassConverter uriDomainClassConverter() {
|
||||
@Bean
|
||||
public UriDomainClassConverter uriDomainClassConverter() {
|
||||
return new UriDomainClassConverter();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link org.springframework.context.ApplicationListener} implementation for invoking {@link
|
||||
* org.springframework.validation.Validator} instances assigned to specific domain types.
|
||||
* {@link org.springframework.context.ApplicationListener} implementation for invoking
|
||||
* {@link org.springframework.validation.Validator} instances assigned to specific domain types.
|
||||
*/
|
||||
@Bean public ValidatingRepositoryEventListener validatingRepositoryEventListener() {
|
||||
@Bean
|
||||
public ValidatingRepositoryEventListener validatingRepositoryEventListener() {
|
||||
ValidatingRepositoryEventListener listener = new ValidatingRepositoryEventListener();
|
||||
configureValidatingRepositoryEventListener(listener);
|
||||
return listener;
|
||||
}
|
||||
|
||||
@Bean @Lazy public ValidationExceptionHandler validationExceptionHandler() {
|
||||
if(IS_JAVAX_VALIDATION_AVAILABLE) {
|
||||
@Bean
|
||||
@Lazy
|
||||
public ValidationExceptionHandler validationExceptionHandler() {
|
||||
if (IS_JAVAX_VALIDATION_AVAILABLE) {
|
||||
return new ValidationExceptionHandler();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Bean @Lazy public JpaHelper jpaHelper() {
|
||||
if(IS_JPA_AVAILABLE) {
|
||||
@Bean
|
||||
@Lazy
|
||||
public JpaHelper jpaHelper() {
|
||||
if (IS_JPA_AVAILABLE) {
|
||||
return new JpaHelper();
|
||||
} else {
|
||||
return null;
|
||||
@@ -122,167 +155,95 @@ public class RepositoryRestMvcConfiguration {
|
||||
/**
|
||||
* Main configuration for the REST exporter.
|
||||
*/
|
||||
@Bean public RepositoryRestConfiguration config() {
|
||||
@Bean
|
||||
public RepositoryRestConfiguration config() {
|
||||
RepositoryRestConfiguration config = new RepositoryRestConfiguration();
|
||||
configureRepositoryRestConfiguration(config);
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.config.BeanPostProcessor} to turn beans annotated as {@link
|
||||
* org.springframework.data.rest.repository.annotation.RepositoryEventHandler}s.
|
||||
*
|
||||
* {@link org.springframework.beans.factory.config.BeanPostProcessor} to turn beans annotated as
|
||||
* {@link org.springframework.data.rest.repository.annotation.RepositoryEventHandler}s.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean public AnnotatedHandlerBeanPostProcessor annotatedHandlerBeanPostProcessor() {
|
||||
@Bean
|
||||
public AnnotatedHandlerBeanPostProcessor annotatedHandlerBeanPostProcessor() {
|
||||
return new AnnotatedHandlerBeanPostProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* For merging incoming objects materialized from JSON with existing domain objects loaded from the repository.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Bean public DomainObjectMerger domainObjectMerger() throws Exception {
|
||||
return new DomainObjectMerger(
|
||||
repositories().getObject(),
|
||||
defaultConversionService()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The controller that handles top-level requests for listing what repositories are available.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Bean public RepositoryController repositoryController() throws Exception {
|
||||
return new RepositoryController(
|
||||
repositories().getObject(),
|
||||
config(),
|
||||
domainClassConverter(),
|
||||
defaultConversionService(),
|
||||
entityLinks()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The controller responsible for handling requests to display or those that modify an entity.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Bean public RepositoryEntityController repositoryEntityController() throws Exception {
|
||||
return new RepositoryEntityController(
|
||||
repositories().getObject(),
|
||||
config(),
|
||||
domainClassConverter(),
|
||||
defaultConversionService(),
|
||||
entityLinks()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The controller responsible for managing links of property references.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Bean public RepositoryPropertyReferenceController propertyReferenceController() throws Exception {
|
||||
return new RepositoryPropertyReferenceController(
|
||||
repositories().getObject(),
|
||||
config(),
|
||||
domainClassConverter(),
|
||||
defaultConversionService(),
|
||||
entityLinks()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The controller responsible for performing searches.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Bean public RepositorySearchController repositorySearchController() throws Exception {
|
||||
return new RepositorySearchController(
|
||||
repositories().getObject(),
|
||||
config(),
|
||||
domainClassConverter(),
|
||||
defaultConversionService(),
|
||||
entityLinks()
|
||||
);
|
||||
@Bean
|
||||
public DomainObjectMerger domainObjectMerger() throws Exception {
|
||||
return new DomainObjectMerger(repositories(), defaultConversionService());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the base {@link java.net.URI} under which this application is configured.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean public BaseUriMethodArgumentResolver baseUriMethodArgumentResolver() {
|
||||
return new BaseUriMethodArgumentResolver();
|
||||
@Bean
|
||||
public BaseUriMethodArgumentResolver baseUriMethodArgumentResolver() {
|
||||
return new BaseUriMethodArgumentResolver(config());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the {@link org.springframework.data.repository.core.RepositoryInformation} for this request.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean public RepositoryInformationHandlerMethodArgumentResolver repoInfoMethodArgumentResolver() {
|
||||
@Bean
|
||||
public RepositoryInformationHandlerMethodArgumentResolver repoInfoMethodArgumentResolver() {
|
||||
return new RepositoryInformationHandlerMethodArgumentResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the paging and sorting information from the query parameters based on the current configuration settings.
|
||||
*
|
||||
* Turns an {@link javax.servlet.http.HttpServletRequest} into a
|
||||
* {@link org.springframework.http.server.ServerHttpRequest}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean public PagingAndSortingMethodArgumentResolver pagingAndSortingMethodArgumentResolver() {
|
||||
return new PagingAndSortingMethodArgumentResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns an {@link javax.servlet.http.HttpServletRequest} into a {@link org.springframework.http.server.ServerHttpRequest}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean public ServerHttpRequestMethodArgumentResolver serverHttpRequestMethodArgumentResolver() {
|
||||
@Bean
|
||||
public ServerHttpRequestMethodArgumentResolver serverHttpRequestMethodArgumentResolver() {
|
||||
return new ServerHttpRequestMethodArgumentResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience resolver that pulls together all the information needed to service a request.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean public RepositoryRestRequestHandlerMethodArgumentResolver repoRequestArgumentResolver() {
|
||||
return new RepositoryRestRequestHandlerMethodArgumentResolver();
|
||||
@Bean
|
||||
public RepositoryRestRequestHandlerMethodArgumentResolver repoRequestArgumentResolver() {
|
||||
return new RepositoryRestRequestHandlerMethodArgumentResolver(defaultConversionService());
|
||||
}
|
||||
|
||||
/**
|
||||
* A special {@link org.springframework.hateoas.EntityLinks} implementation that takes repository and current
|
||||
* configuration into account when generating links.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Bean public EntityLinks entityLinks() throws Exception {
|
||||
return new RepositoryEntityLinks(repositories().getObject(), config());
|
||||
@Bean
|
||||
public EntityLinks entityLinks() {
|
||||
return new RepositoryEntityLinks(repositories(), resourceMappings(), config());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads incoming JSON into an entity.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean public PersistentEntityResourceHandlerMethodArgumentResolver persistentEntityArgumentResolver() {
|
||||
@Bean
|
||||
public PersistentEntityResourceHandlerMethodArgumentResolver persistentEntityArgumentResolver() {
|
||||
List<HttpMessageConverter<?>> messageConverters = defaultMessageConverters();
|
||||
configureHttpMessageConverters(messageConverters);
|
||||
|
||||
@@ -290,20 +251,22 @@ public class RepositoryRestMvcConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a domain class into a {@link org.springframework.data.rest.repository.json.JsonSchema}.
|
||||
*
|
||||
* Turns a domain class into a {@link org.springframework.data.rest.webmvc.json.JsonSchema}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean public PersistentEntityToJsonSchemaConverter jsonSchemaConverter() {
|
||||
@Bean
|
||||
public PersistentEntityToJsonSchemaConverter jsonSchemaConverter() {
|
||||
return new PersistentEntityToJsonSchemaConverter();
|
||||
}
|
||||
|
||||
/**
|
||||
* The Jackson {@link ObjectMapper} used internally.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean public ObjectMapper objectMapper() {
|
||||
@Bean
|
||||
public ObjectMapper objectMapper() {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
|
||||
// Our special PersistentEntityResource Module
|
||||
@@ -317,37 +280,43 @@ public class RepositoryRestMvcConfiguration {
|
||||
|
||||
/**
|
||||
* The {@link HttpMessageConverter} used by Spring MVC to read and write JSON data.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean public MappingJackson2HttpMessageConverter jacksonHttpMessageConverter() {
|
||||
@Bean
|
||||
public MappingJackson2HttpMessageConverter jacksonHttpMessageConverter() {
|
||||
MappingJackson2HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter();
|
||||
jacksonConverter.setObjectMapper(objectMapper());
|
||||
jacksonConverter.setSupportedMediaTypes(Arrays.asList(
|
||||
MediaType.APPLICATION_JSON,
|
||||
MediaType.valueOf("application/schema+json"),
|
||||
MediaType.valueOf("application/x-spring-data-verbose+json"),
|
||||
MediaType.valueOf("application/x-spring-data-compact+json")
|
||||
));
|
||||
jacksonConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON,
|
||||
MediaType.valueOf("application/schema+json"), MediaType.valueOf("application/x-spring-data-verbose+json"),
|
||||
MediaType.valueOf("application/x-spring-data-compact+json")));
|
||||
return jacksonConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link HttpMessageConverter} used to create {@literal text/uri-list} responses.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean public UriListHttpMessageConverter uriListHttpMessageConverter() {
|
||||
@Bean
|
||||
public UriListHttpMessageConverter uriListHttpMessageConverter() {
|
||||
return new UriListHttpMessageConverter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PersistentEntityResourceAssembler<Object> persistentEntityResourceAssembler() {
|
||||
return new PersistentEntityResourceAssembler<Object>(repositories(), entityLinks());
|
||||
}
|
||||
|
||||
/**
|
||||
* Special {@link org.springframework.web.servlet.HandlerAdapter} that only recognizes handler methods defined in
|
||||
* the provided controller classes.
|
||||
*
|
||||
* Special {@link org.springframework.web.servlet.HandlerAdapter} that only recognizes handler methods defined in the
|
||||
* provided controller classes.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean public RequestMappingHandlerAdapter repositoryExporterHandlerAdapter() {
|
||||
@Bean
|
||||
public RequestMappingHandlerAdapter repositoryExporterHandlerAdapter() {
|
||||
|
||||
List<HttpMessageConverter<?>> messageConverters = defaultMessageConverters();
|
||||
configureHttpMessageConverters(messageConverters);
|
||||
|
||||
@@ -359,30 +328,47 @@ public class RepositoryRestMvcConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Special {@link org.springframework.web.servlet.HandlerMapping} that only recognizes handler methods defined in
|
||||
* the provided controller classes.
|
||||
*
|
||||
* Special {@link org.springframework.web.servlet.HandlerMapping} that only recognizes handler methods defined in the
|
||||
* provided controller classes.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean public RequestMappingHandlerMapping repositoryExporterHandlerMapping() {
|
||||
return new RepositoryRestHandlerMapping();
|
||||
@Bean
|
||||
public RequestMappingHandlerMapping repositoryExporterHandlerMapping() {
|
||||
return new RepositoryRestHandlerMapping(resourceMappings());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResourceMappings resourceMappings() {
|
||||
|
||||
Repositories repositories = repositories();
|
||||
RepositoryRestConfiguration config = config();
|
||||
|
||||
try {
|
||||
RelProvider relProvider = beanFactory.getBean(RelProvider.class);
|
||||
return new ResourceMappings(config, repositories, relProvider);
|
||||
} catch (NoSuchBeanDefinitionException e) {
|
||||
return new ResourceMappings(config, repositories);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Jackson module responsible for intelligently serializing and deserializing JSON that corresponds to an entity.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean public Module persistentEntityJackson2Module() {
|
||||
return new PersistentEntityJackson2Module(defaultConversionService());
|
||||
@Bean
|
||||
public Module persistentEntityJackson2Module() {
|
||||
return new PersistentEntityJackson2Module(resourceMappings());
|
||||
}
|
||||
|
||||
/**
|
||||
* Bean for looking up methods annotated with {@link org.springframework.web.bind.annotation.ExceptionHandler}.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean public ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver() {
|
||||
@Bean
|
||||
public ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver() {
|
||||
ExceptionHandlerExceptionResolver er = new ExceptionHandlerExceptionResolver();
|
||||
er.setCustomArgumentResolvers(defaultMethodArgumentResolvers());
|
||||
|
||||
@@ -403,67 +389,51 @@ public class RepositoryRestMvcConfiguration {
|
||||
}
|
||||
|
||||
private List<HandlerMethodArgumentResolver> defaultMethodArgumentResolvers() {
|
||||
return Arrays.asList(baseUriMethodArgumentResolver(),
|
||||
pagingAndSortingMethodArgumentResolver(),
|
||||
serverHttpRequestMethodArgumentResolver(),
|
||||
repoInfoMethodArgumentResolver(),
|
||||
repoRequestArgumentResolver(),
|
||||
persistentEntityArgumentResolver());
|
||||
return Arrays.asList(baseUriMethodArgumentResolver(), pageableResolver(), sortResolver(),
|
||||
serverHttpRequestMethodArgumentResolver(), repoInfoMethodArgumentResolver(), repoRequestArgumentResolver(),
|
||||
persistentEntityArgumentResolver());
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to add additional configuration.
|
||||
*
|
||||
* @param config
|
||||
* Main configuration bean.
|
||||
*
|
||||
* @param config Main configuration bean.
|
||||
*/
|
||||
protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
|
||||
}
|
||||
protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {}
|
||||
|
||||
/**
|
||||
* Override this method to add your own converters.
|
||||
*
|
||||
* @param conversionService
|
||||
* Default ConversionService bean.
|
||||
*
|
||||
* @param conversionService Default ConversionService bean.
|
||||
*/
|
||||
protected void configureConversionService(ConfigurableConversionService conversionService) {
|
||||
}
|
||||
protected void configureConversionService(ConfigurableConversionService conversionService) {}
|
||||
|
||||
/**
|
||||
* Override this method to add validators manually.
|
||||
*
|
||||
* @param validatingListener
|
||||
* The {@link org.springframework.context.ApplicationListener} responsible for invoking {@link
|
||||
* org.springframework.validation.Validator} instances.
|
||||
*
|
||||
* @param validatingListener The {@link org.springframework.context.ApplicationListener} responsible for invoking
|
||||
* {@link org.springframework.validation.Validator} instances.
|
||||
*/
|
||||
protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
|
||||
}
|
||||
protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {}
|
||||
|
||||
/**
|
||||
* Configure the {@link ExceptionHandlerExceptionResolver}.
|
||||
*
|
||||
* @param exceptionResolver
|
||||
* The default exception resolver on which you can add custom argument resolvers.
|
||||
*
|
||||
* @param exceptionResolver The default exception resolver on which you can add custom argument resolvers.
|
||||
*/
|
||||
protected void configureExceptionHandlerExceptionResolver(ExceptionHandlerExceptionResolver exceptionResolver) {
|
||||
}
|
||||
protected void configureExceptionHandlerExceptionResolver(ExceptionHandlerExceptionResolver exceptionResolver) {}
|
||||
|
||||
/**
|
||||
* Configure the available {@link HttpMessageConverter}s by adding your own.
|
||||
*
|
||||
* @param messageConverters
|
||||
* The converters to be used by the system.
|
||||
*
|
||||
* @param messageConverters The converters to be used by the system.
|
||||
*/
|
||||
protected void configureHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
|
||||
}
|
||||
protected void configureHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {}
|
||||
|
||||
/**
|
||||
* Configure the Jackson {@link ObjectMapper} directly.
|
||||
*
|
||||
* @param objectMapper
|
||||
* The {@literal ObjectMapper} to be used by the system.
|
||||
*
|
||||
* @param objectMapper The {@literal ObjectMapper} to be used by the system.
|
||||
*/
|
||||
protected void configureJacksonObjectMapper(ObjectMapper objectMapper) {
|
||||
}
|
||||
|
||||
protected void configureJacksonObjectMapper(ObjectMapper objectMapper) {}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.data.rest.repository.json;
|
||||
package org.springframework.data.rest.webmvc.json;
|
||||
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.data.rest.repository.json;
|
||||
package org.springframework.data.rest.webmvc.json;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -1,8 +1,6 @@
|
||||
package org.springframework.data.rest.repository.json;
|
||||
package org.springframework.data.rest.webmvc.json;
|
||||
|
||||
import static org.springframework.beans.BeanUtils.*;
|
||||
import static org.springframework.data.rest.core.util.UriUtils.*;
|
||||
import static org.springframework.data.rest.repository.support.ResourceMappingUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
@@ -15,6 +13,28 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.AssociationHandler;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.repository.PersistentEntityResource;
|
||||
import org.springframework.data.rest.repository.UriDomainClassConverter;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.webmvc.support.RepositoryLinkBuilder;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerationException;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
@@ -26,26 +46,6 @@ import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.AssociationHandler;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.config.ResourceMapping;
|
||||
import org.springframework.data.rest.repository.PersistentEntityResource;
|
||||
import org.springframework.data.rest.repository.UriDomainClassConverter;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
@@ -55,54 +55,45 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init
|
||||
private static final long serialVersionUID = -7289265674870906323L;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PersistentEntityJackson2Module.class);
|
||||
private static final TypeDescriptor URI_TYPE = TypeDescriptor.valueOf(URI.class);
|
||||
private final ConversionService conversionService;
|
||||
@Autowired
|
||||
private Repositories repositories;
|
||||
@Autowired
|
||||
private RepositoryRestConfiguration config;
|
||||
@Autowired
|
||||
private UriDomainClassConverter uriDomainClassConverter;
|
||||
private final ResourceMappings mappings;
|
||||
|
||||
public PersistentEntityJackson2Module(ConversionService conversionService) {
|
||||
public PersistentEntityJackson2Module(ResourceMappings resourceMappings) {
|
||||
|
||||
super(new Version(1, 1, 0, "BUILD-SNAPSHOT", "org.springframework.data.rest", "jackson-module"));
|
||||
this.conversionService = conversionService;
|
||||
|
||||
|
||||
this.mappings = resourceMappings;
|
||||
addSerializer(new ResourceSerializer());
|
||||
}
|
||||
|
||||
public static boolean maybeAddAssociationLink(Repositories repositories,
|
||||
RepositoryRestConfiguration config,
|
||||
URI baseEntityUri,
|
||||
RepositoryInformation repoInfo,
|
||||
ResourceMapping entityMapping,
|
||||
ResourceMapping propertyMapping,
|
||||
public static boolean maybeAddAssociationLink(RepositoryLinkBuilder builder,
|
||||
ResourceMappings mappings,
|
||||
PersistentProperty<?> persistentProperty,
|
||||
List<Link> links) {
|
||||
Class<?> propertyType = persistentProperty.getType();
|
||||
if(persistentProperty.isCollectionLike() || persistentProperty.isArray()) {
|
||||
propertyType = persistentProperty.getComponentType();
|
||||
}
|
||||
|
||||
String propertyPath = (null != propertyMapping
|
||||
? propertyMapping.getPath()
|
||||
: persistentProperty.getName());
|
||||
// In case a property mapping is specified but no path is set
|
||||
if(null == propertyPath) {
|
||||
propertyPath = persistentProperty.getName();
|
||||
}
|
||||
String propertyRel = formatRel(config, repoInfo, persistentProperty);
|
||||
if(repositories.hasRepositoryFor(propertyType)) {
|
||||
// This is a managed type, generate a Link
|
||||
RepositoryInformation linkedRepoInfo = repositories.getRepositoryInformationFor(propertyType);
|
||||
ResourceMapping linkedRepoMapping = getResourceMapping(config, linkedRepoInfo);
|
||||
if(linkedRepoMapping.isExported()) {
|
||||
URI uri = buildUri(baseEntityUri, propertyPath);
|
||||
Link l = new Link(uri.toString(), propertyRel);
|
||||
links.add(l);
|
||||
// This is an association. We added a Link.
|
||||
return true;
|
||||
}
|
||||
|
||||
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()) {
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -136,8 +127,9 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init
|
||||
DeserializationContext ctxt) throws IOException,
|
||||
JsonProcessingException {
|
||||
Object entity = instantiateClass(getValueClass());
|
||||
BeanWrapper<?, Object> wrapper = BeanWrapper.create(entity, conversionService);
|
||||
ResourceMapping domainMapping = config.getResourceMappingForDomainType(getValueClass());
|
||||
BeanWrapper<?, Object> wrapper = BeanWrapper.create(entity, null);
|
||||
|
||||
ResourceMetadata metadata = mappings.getMappingFor(getValueClass());
|
||||
|
||||
for(JsonToken tok = jp.nextToken(); tok != JsonToken.END_OBJECT; tok = jp.nextToken()) {
|
||||
String name = jp.getCurrentName();
|
||||
@@ -160,18 +152,7 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init
|
||||
|
||||
PersistentProperty<?> persistentProperty = persistentEntity.getPersistentProperty(name);
|
||||
if(null == persistentProperty) {
|
||||
String errMsg = "Property '" + name + "' not found for entity " + getValueClass().getName();
|
||||
if(null == domainMapping) {
|
||||
throw new HttpMessageNotReadableException(errMsg);
|
||||
}
|
||||
String propertyName = domainMapping.getNameForPath(name);
|
||||
if(null == propertyName) {
|
||||
throw new HttpMessageNotReadableException(errMsg);
|
||||
}
|
||||
persistentProperty = persistentEntity.getPersistentProperty(propertyName);
|
||||
if(null == persistentProperty) {
|
||||
throw new HttpMessageNotReadableException(errMsg);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Object val = null;
|
||||
@@ -199,7 +180,7 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init
|
||||
// The method of doing that varies based on the type of the property.
|
||||
if(persistentProperty.isCollectionLike()) {
|
||||
Class<? extends Collection<?>> ctype = (Class<? extends Collection<?>>) persistentProperty.getType();
|
||||
Collection<Object> c = (Collection<Object>) wrapper.getProperty(persistentProperty, ctype, false);
|
||||
Collection<Object> c = (Collection<Object>) wrapper.getProperty(persistentProperty);
|
||||
if(null == c || c == Collections.EMPTY_LIST || c == Collections.EMPTY_SET) {
|
||||
if(Collection.class.isAssignableFrom(ctype)) {
|
||||
c = new ArrayList<Object>();
|
||||
@@ -222,7 +203,7 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init
|
||||
}
|
||||
} else if(persistentProperty.isMap()) {
|
||||
Class<? extends Map<?, ?>> mtype = (Class<? extends Map<?, ?>>)persistentProperty.getType();
|
||||
Map<Object, Object> m = (Map<Object, Object>) wrapper.getProperty(persistentProperty, mtype, false);
|
||||
Map<Object, Object> m = (Map<Object, Object>) wrapper.getProperty(persistentProperty);
|
||||
if(null == m || m == Collections.EMPTY_MAP) {
|
||||
m = new HashMap<Object, Object>();
|
||||
}
|
||||
@@ -278,18 +259,13 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init
|
||||
|
||||
Object obj = resource.getContent();
|
||||
|
||||
final PersistentEntity persistentEntity = resource.getPersistentEntity();
|
||||
final ResourceMapping entityMapping = getResourceMapping(config, persistentEntity);
|
||||
final PersistentEntity entity = resource.getPersistentEntity();
|
||||
|
||||
final RepositoryInformation repoInfo = repositories.getRepositoryInformationFor(persistentEntity.getType());
|
||||
final ResourceMapping repoMapping = getResourceMapping(config, repoInfo);
|
||||
|
||||
final BeanWrapper wrapper = BeanWrapper.create(obj, conversionService);
|
||||
final Object entityId = wrapper.getProperty(persistentEntity.getIdProperty());
|
||||
|
||||
final URI baseEntityUri = buildUri(resource.getBaseUri(),
|
||||
repoMapping.getPath(),
|
||||
entityId.toString());
|
||||
final BeanWrapper wrapper = BeanWrapper.create(obj, null);
|
||||
final Object entityId = wrapper.getProperty(entity.getIdProperty());
|
||||
final ResourceMappings mappings = new ResourceMappings(config, repositories);
|
||||
final ResourceMetadata metadata = mappings.getMappingFor(entity.getType());
|
||||
final RepositoryLinkBuilder builder = new RepositoryLinkBuilder(metadata, config.getBaseUri()).slash(entityId);
|
||||
|
||||
final List<Link> links = new ArrayList<Link>();
|
||||
// Start with ResourceProcessor-added links
|
||||
@@ -297,32 +273,23 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init
|
||||
|
||||
jgen.writeStartObject();
|
||||
try {
|
||||
persistentEntity.doWithProperties(new PropertyHandler() {
|
||||
@Override public void doWithPersistentProperty(PersistentProperty persistentProperty) {
|
||||
if(persistentProperty.isIdProperty() && !config.isIdExposedFor(persistentEntity.getType())) {
|
||||
entity.doWithProperties(new PropertyHandler() {
|
||||
@Override public void doWithPersistentProperty(PersistentProperty property) {
|
||||
|
||||
boolean idAvailableAndShallNotBeExposed = property.isIdProperty() && !config.isIdExposedFor(entity.getType());
|
||||
|
||||
if(idAvailableAndShallNotBeExposed) {
|
||||
return;
|
||||
}
|
||||
ResourceMapping propertyMapping = entityMapping.getResourceMappingFor(persistentProperty.getName());
|
||||
if(null != propertyMapping && !propertyMapping.isExported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(persistentProperty.isEntity() && maybeAddAssociationLink(repositories,
|
||||
config,
|
||||
baseEntityUri,
|
||||
repoInfo,
|
||||
entityMapping,
|
||||
propertyMapping,
|
||||
persistentProperty,
|
||||
links)) {
|
||||
|
||||
if (property.isEntity() && maybeAddAssociationLink(builder, mappings, property, links)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Property is a normal or non-managed property.
|
||||
String propertyName = (null != propertyMapping ? propertyMapping.getPath() : persistentProperty.getName());
|
||||
Object propertyValue = wrapper.getProperty(persistentProperty);
|
||||
Object propertyValue = wrapper.getProperty(property);
|
||||
try {
|
||||
jgen.writeObjectField(propertyName, propertyValue);
|
||||
jgen.writeObjectField(property.getName(), propertyValue);
|
||||
} catch(IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
@@ -330,27 +297,22 @@ public class PersistentEntityJackson2Module extends SimpleModule implements Init
|
||||
});
|
||||
|
||||
// Add associations as links
|
||||
persistentEntity.doWithAssociations(new AssociationHandler() {
|
||||
entity.doWithAssociations(new AssociationHandler() {
|
||||
@Override public void doWithAssociation(Association association) {
|
||||
PersistentProperty persistentProperty = association.getInverse();
|
||||
ResourceMapping propertyMapping = entityMapping.getResourceMappingFor(persistentProperty.getName());
|
||||
if(null != propertyMapping && !propertyMapping.isExported()) {
|
||||
|
||||
PersistentProperty property = association.getInverse();
|
||||
|
||||
if(!mappings.isMapped(property)) {
|
||||
return;
|
||||
}
|
||||
if(maybeAddAssociationLink(repositories,
|
||||
config,
|
||||
baseEntityUri,
|
||||
repoInfo,
|
||||
entityMapping,
|
||||
propertyMapping,
|
||||
persistentProperty,
|
||||
links)) {
|
||||
|
||||
if (maybeAddAssociationLink(builder, mappings, property, links)) {
|
||||
return;
|
||||
}
|
||||
// Association Link was not added, probably because this isn't a managed type. Add value of property inline.
|
||||
Object propertyValue = wrapper.getProperty(persistentProperty);
|
||||
Object propertyValue = wrapper.getProperty(property);
|
||||
try {
|
||||
jgen.writeObjectField(persistentProperty.getName(), propertyValue);
|
||||
jgen.writeObjectField(property.getName(), propertyValue);
|
||||
} catch(IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
package org.springframework.data.rest.repository.json;
|
||||
package org.springframework.data.rest.webmvc.json;
|
||||
|
||||
import static org.springframework.data.rest.core.util.UriUtils.*;
|
||||
import static org.springframework.data.rest.repository.json.PersistentEntityJackson2Module.*;
|
||||
import static org.springframework.data.rest.repository.support.ResourceMappingUtils.*;
|
||||
import static org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module.*;
|
||||
import static org.springframework.util.StringUtils.*;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@@ -21,10 +19,11 @@ import org.springframework.data.mapping.AssociationHandler;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.rest.config.ResourceMapping;
|
||||
import org.springframework.data.rest.repository.annotation.Description;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.repository.support.RepositoryInformationSupport;
|
||||
import org.springframework.data.rest.webmvc.support.RepositoryLinkBuilder;
|
||||
import org.springframework.hateoas.Link;
|
||||
|
||||
/**
|
||||
@@ -38,11 +37,15 @@ public class PersistentEntityToJsonSchemaConverter
|
||||
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) {
|
||||
|
||||
for(Class<?> domainType : repositories) {
|
||||
convertiblePairs.add(new ConvertiblePair(domainType, JsonSchema.class));
|
||||
}
|
||||
|
||||
this.mappings = new ResourceMappings(config, repositories);
|
||||
}
|
||||
|
||||
@Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
@@ -60,15 +63,13 @@ public class PersistentEntityToJsonSchemaConverter
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
|
||||
PersistentEntity<?, ?> persistentEntity = repositories.getPersistentEntity((Class<?>)source);
|
||||
final RepositoryInformation repoInfo = repositories.getRepositoryInformationFor(persistentEntity.getType());
|
||||
final ResourceMapping repoMapping = getResourceMapping(config, repoInfo);
|
||||
final ResourceMapping entityMapping = getResourceMapping(config, persistentEntity);
|
||||
final URI baseEntityUri = buildUri(config.getBaseUri(), repoMapping.getPath(), "{id}");
|
||||
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) {
|
||||
@@ -95,23 +96,19 @@ public class PersistentEntityToJsonSchemaConverter
|
||||
});
|
||||
|
||||
final List<Link> links = new ArrayList<Link>();
|
||||
|
||||
persistentEntity.doWithAssociations(new AssociationHandler() {
|
||||
@Override public void doWithAssociation(Association association) {
|
||||
PersistentProperty persistentProperty = association.getInverse();
|
||||
ResourceMapping propertyMapping = entityMapping.getResourceMappingFor(persistentProperty.getName());
|
||||
if(null != propertyMapping && !propertyMapping.isExported()) {
|
||||
if(!metadata.isMapped(persistentProperty)) {
|
||||
return;
|
||||
}
|
||||
maybeAddAssociationLink(repositories,
|
||||
config,
|
||||
baseEntityUri,
|
||||
repoInfo,
|
||||
entityMapping,
|
||||
propertyMapping,
|
||||
persistentProperty,
|
||||
links);
|
||||
|
||||
RepositoryLinkBuilder builder = new RepositoryLinkBuilder(metadata, config.getBaseUri()).slash("{id}");
|
||||
maybeAddAssociationLink(builder, mappings, persistentProperty, links);
|
||||
}
|
||||
});
|
||||
|
||||
jsonSchema.add(links);
|
||||
|
||||
return jsonSchema;
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc.json;
|
||||
@@ -1,131 +1,83 @@
|
||||
package org.springframework.data.rest.webmvc.support;
|
||||
|
||||
import static org.springframework.data.rest.core.util.UriUtils.*;
|
||||
import static org.springframework.data.rest.repository.support.ResourceMappingUtils.*;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.config.ResourceMapping;
|
||||
import org.springframework.data.rest.webmvc.RepositoryController;
|
||||
import org.springframework.hateoas.Identifiable;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMetadata;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkBuilder;
|
||||
import org.springframework.hateoas.core.AbstractEntityLinks;
|
||||
import org.springframework.hateoas.mvc.ControllerLinkBuilder;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class RepositoryEntityLinks extends AbstractEntityLinks {
|
||||
|
||||
private final Repositories repositories;
|
||||
private final Repositories repositories;
|
||||
private final ResourceMappings mappings;
|
||||
private final RepositoryRestConfiguration config;
|
||||
|
||||
@Autowired
|
||||
public RepositoryEntityLinks(Repositories repositories,
|
||||
RepositoryRestConfiguration config) {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override public boolean supports(Class<?> delimiter) {
|
||||
PersistentEntity<?, ?> persistentEntity = repositories.getPersistentEntity(delimiter);
|
||||
return (null != persistentEntity);
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> delimiter) {
|
||||
return repositories.hasRepositoryFor(delimiter);
|
||||
}
|
||||
|
||||
@Override public LinkBuilder linkFor(Class<?> type) {
|
||||
RepositoryInformation repoInfo = repositories.getRepositoryInformationFor(type);
|
||||
if(null == repoInfo) {
|
||||
throw new IllegalArgumentException(type + " is not managed by any repository.");
|
||||
}
|
||||
PersistentEntity<?, ?> persistentEntity = repositories.getPersistentEntity(type);
|
||||
if(null == persistentEntity) {
|
||||
throw new IllegalArgumentException(type + " is not managed by any repository.");
|
||||
}
|
||||
return new PersistentEntityLinkBuilder(config.getBaseUri(), repoInfo, persistentEntity);
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EntityLinks#linkFor(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public LinkBuilder linkFor(Class<?> type) {
|
||||
|
||||
ResourceMetadata metadata = mappings.getMappingFor(type);
|
||||
return new RepositoryLinkBuilder(metadata, config.getBaseUri());
|
||||
}
|
||||
|
||||
@Override public LinkBuilder linkFor(Class<?> type, Object... parameters) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EntityLinks#linkFor(java.lang.Class, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
public LinkBuilder linkFor(Class<?> type, Object... parameters) {
|
||||
return linkFor(type);
|
||||
}
|
||||
|
||||
@Override public Link linkToCollectionResource(Class<?> type) {
|
||||
RepositoryInformation repoInfo = repositories.getRepositoryInformationFor(type);
|
||||
if(null == repoInfo) {
|
||||
throw new IllegalArgumentException(type + " is not managed by any repository.");
|
||||
}
|
||||
ResourceMapping mapping = getResourceMapping(config, repoInfo);
|
||||
return linkFor(type).withRel(mapping.getRel());
|
||||
}
|
||||
|
||||
@Override public Link linkToSingleResource(Class<?> type, Object id) {
|
||||
RepositoryInformation repoInfo = repositories.getRepositoryInformationFor(type);
|
||||
if(null == repoInfo) {
|
||||
throw new IllegalArgumentException(type + " is not managed by any repository.");
|
||||
}
|
||||
ResourceMapping repoMapping = getResourceMapping(config, repoInfo);
|
||||
PersistentEntity<?, ?> persistentEntity = repositories.getPersistentEntity(type);
|
||||
ResourceMapping entityMapping = getResourceMapping(config, persistentEntity);
|
||||
return linkFor(type).slash(id).withRel(repoMapping.getRel() + "." + entityMapping.getRel());
|
||||
}
|
||||
|
||||
private class PersistentEntityLinkBuilder implements LinkBuilder {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EntityLinks#linkToCollectionResource(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public Link linkToCollectionResource(Class<?> type) {
|
||||
|
||||
private final UriComponentsBuilder builder;
|
||||
private final ResourceMapping repoMapping;
|
||||
private final ResourceMapping entityMapping;
|
||||
|
||||
private PersistentEntityLinkBuilder(URI baseUri,
|
||||
RepositoryInformation repoInfo,
|
||||
PersistentEntity<?, ?> persistentEntity) {
|
||||
this.repoMapping = getResourceMapping(config, repoInfo);
|
||||
this.entityMapping = getResourceMapping(config, persistentEntity);
|
||||
if(null == baseUri) {
|
||||
URI u = ControllerLinkBuilder.linkTo(RepositoryController.class).toUri();
|
||||
if(u.toString().endsWith("/")) {
|
||||
String s = u.toString();
|
||||
baseUri = URI.create(s.substring(0, s.lastIndexOf('/')));
|
||||
} else {
|
||||
baseUri = u;
|
||||
}
|
||||
}
|
||||
this.builder = UriComponentsBuilder.fromUri(buildUri(baseUri, repoMapping.getPath()));
|
||||
}
|
||||
|
||||
@Override public LinkBuilder slash(Object object) {
|
||||
String path = String.format("%s", object);
|
||||
if(object instanceof PersistentProperty) {
|
||||
String propName = ((PersistentProperty<?>) object).getName();
|
||||
if(entityMapping.hasResourceMappingFor(propName)) {
|
||||
path = entityMapping.getResourceMappingFor(propName).getPath();
|
||||
}
|
||||
}
|
||||
builder.pathSegment(path);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public LinkBuilder slash(Identifiable<?> identifiable) {
|
||||
return slash(identifiable.getId());
|
||||
}
|
||||
|
||||
@Override public URI toUri() {
|
||||
return builder.build().toUri();
|
||||
}
|
||||
|
||||
@Override public Link withRel(String rel) {
|
||||
return new Link(builder.build().toUriString(), rel);
|
||||
}
|
||||
|
||||
@Override public Link withSelfRel() {
|
||||
return withRel("self");
|
||||
}
|
||||
ResourceMetadata metadata = mappings.getMappingFor(type);
|
||||
return linkFor(type).withRel(metadata.getRel());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EntityLinks#linkToSingleResource(java.lang.Class, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Link linkToSingleResource(Class<?> type, Object id) {
|
||||
|
||||
ResourceMetadata metadata = mappings.getMappingFor(type);
|
||||
return linkFor(type).slash(id).withRel(metadata.getSingleResourceRel());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc.support;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.webmvc.RepositoryController;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.core.LinkBuilderSupport;
|
||||
import org.springframework.hateoas.mvc.ControllerLinkBuilder;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
public class RepositoryLinkBuilder extends LinkBuilderSupport<RepositoryLinkBuilder> {
|
||||
|
||||
private final ResourceMetadata metadata;
|
||||
|
||||
public RepositoryLinkBuilder(ResourceMetadata metadata, URI baseUri) {
|
||||
this(metadata, prepareBuilder(baseUri, metadata));
|
||||
}
|
||||
|
||||
private RepositoryLinkBuilder(ResourceMetadata metadata, UriComponentsBuilder builder) {
|
||||
|
||||
super(builder);
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
private static UriComponentsBuilder prepareBuilder(URI baseUri, ResourceMetadata metadata) {
|
||||
|
||||
UriComponentsBuilder builder = baseUri != null ? UriComponentsBuilder.fromUri(baseUri) : ControllerLinkBuilder.linkTo(RepositoryController.class).toUriComponentsBuilder();
|
||||
return builder.path(metadata.getPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryLinkBuilder slash(Object object) {
|
||||
|
||||
if (object instanceof PersistentProperty) {
|
||||
return slash((PersistentProperty<?>) object);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.core.LinkBuilderSupport#createNewInstance(org.springframework.web.util.UriComponentsBuilder)
|
||||
*/
|
||||
@Override
|
||||
protected RepositoryLinkBuilder createNewInstance(UriComponentsBuilder builder) {
|
||||
return new RepositoryLinkBuilder(this.metadata, builder);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.core.LinkBuilderSupport#getThis()
|
||||
*/
|
||||
@Override
|
||||
protected RepositoryLinkBuilder getThis() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkDiscoverer;
|
||||
import org.springframework.hateoas.core.DefaultLinkDiscoverer;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@WebAppConfiguration
|
||||
@Transactional
|
||||
@ContextConfiguration(classes = RepositoryRestMvcConfiguration.class)
|
||||
public abstract class AbstractWebIntegrationTests {
|
||||
|
||||
@Autowired WebApplicationContext context;
|
||||
|
||||
protected MockMvc mvc;
|
||||
LinkDiscoverer links = new DefaultLinkDiscoverer();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mvc = MockMvcBuilders.webAppContextSetup(context).build();
|
||||
}
|
||||
|
||||
protected MockHttpServletResponse request(String href, MediaType contentType) throws Exception {
|
||||
return mvc.perform(get(href).accept(contentType)). //
|
||||
andExpect(status().isOk()). //
|
||||
andExpect(content().contentType(MediaType.APPLICATION_JSON)). //
|
||||
andReturn().getResponse();
|
||||
}
|
||||
|
||||
protected MockHttpServletResponse request(Link link) throws Exception {
|
||||
return request(link.getHref());
|
||||
}
|
||||
|
||||
protected MockHttpServletResponse request(String href) throws Exception {
|
||||
return request(href, MediaType.APPLICATION_JSON);
|
||||
}
|
||||
|
||||
protected Link assertHasLinkWithRel(String rel, MockHttpServletResponse response) throws Exception {
|
||||
|
||||
String content = response.getContentAsString();
|
||||
Link link = links.findLinkWithRel(rel, content);
|
||||
|
||||
assertThat("Expected to find link with rel " + rel + " but found none in " + content + "!", link,
|
||||
is(notNullValue()));
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
protected void assertDoesNotHaveLinkWithRel(String rel, MockHttpServletResponse response) throws Exception {
|
||||
|
||||
String content = response.getContentAsString();
|
||||
Link link = links.findLinkWithRel(rel, content);
|
||||
|
||||
assertThat("Expected not to find link with rel " + rel + " but found " + link + "!", link, is(nullValue()));
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
@@ -7,18 +22,25 @@ import java.net.URI;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.repository.PagingAndSorting;
|
||||
import org.springframework.data.rest.webmvc.annotation.BaseURI;
|
||||
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class CustomMethodArgumentResolverTests extends AbstractJMockTests {
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CustomMethodArgumentResolverTests {
|
||||
|
||||
static final MethodParameter BASE_URI;
|
||||
static final MethodParameter PAGE_SORT;
|
||||
@@ -30,7 +52,7 @@ public class CustomMethodArgumentResolverTests extends AbstractJMockTests {
|
||||
0
|
||||
);
|
||||
PAGE_SORT = MethodParameter.forMethodOrConstructor(
|
||||
Methods.class.getDeclaredMethod("pagingAndSorting", PagingAndSorting.class),
|
||||
Methods.class.getDeclaredMethod("pagingAndSorting", Pageable.class),
|
||||
0
|
||||
);
|
||||
} catch(NoSuchMethodException e) {
|
||||
@@ -43,15 +65,17 @@ public class CustomMethodArgumentResolverTests extends AbstractJMockTests {
|
||||
.setBaseUri(URI.create("http://localhost:8080"));
|
||||
private final BaseUriMethodArgumentResolver baseUriResolver =
|
||||
new BaseUriMethodArgumentResolver(config);
|
||||
private final PagingAndSortingMethodArgumentResolver pageSortResolver =
|
||||
new PagingAndSortingMethodArgumentResolver(config);
|
||||
private final PageableHandlerMethodArgumentResolver pageSortResolver =
|
||||
new PageableHandlerMethodArgumentResolver();
|
||||
private ModelAndViewContainer mavContainer;
|
||||
private WebDataBinderFactory webDataBinderFactory;
|
||||
@Mock WebDataBinderFactory webDataBinderFactory;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mavContainer = new ModelAndViewContainer();
|
||||
webDataBinderFactory = context.mock(WebDataBinderFactory.class);
|
||||
|
||||
pageSortResolver.setOneIndexedParameters(true);
|
||||
pageSortResolver.setFallbackPageable(new PageRequest(1, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -80,7 +104,7 @@ public class CustomMethodArgumentResolverTests extends AbstractJMockTests {
|
||||
is(true));
|
||||
|
||||
// Resolve Page and Sort information
|
||||
PagingAndSorting pageSort = (PagingAndSorting)pageSortResolver.resolveArgument(
|
||||
Pageable pageSort = pageSortResolver.resolveArgument(
|
||||
PAGE_SORT,
|
||||
mavContainer,
|
||||
new ServletWebRequest(Requests.PAGE_REQUEST),
|
||||
@@ -92,15 +116,14 @@ public class CustomMethodArgumentResolverTests extends AbstractJMockTests {
|
||||
is(1));
|
||||
assertThat("Finds limit parameter value",
|
||||
pageSort.getPageSize(),
|
||||
is(5));
|
||||
is(10));
|
||||
}
|
||||
|
||||
static class Methods {
|
||||
void baseUri(@BaseURI URI baseUri) {
|
||||
}
|
||||
|
||||
void pagingAndSorting(PagingAndSorting pageSort) {
|
||||
void pagingAndSorting(Pageable pageSort) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ public abstract class Requests {
|
||||
|
||||
static {
|
||||
PAGE_REQUEST.setParameter("page", "2");
|
||||
PAGE_REQUEST.setParameter("limit", "5");
|
||||
PAGE_REQUEST.setParameter("size", "10");
|
||||
}
|
||||
|
||||
private Requests() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.rest.webmvc;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.rest.webmvc.HttpEntityMatcher.*;
|
||||
import static org.springframework.util.ReflectionUtils.*;
|
||||
|
||||
@@ -28,13 +29,18 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.jmock.Expectations;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.ResourceProcessor;
|
||||
import org.springframework.hateoas.Resources;
|
||||
import org.springframework.hateoas.mvc.HeaderLinksResponseEntity;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -48,7 +54,8 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
* @author Oliver Gierke
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class ResourceProcessorHandlerMethodReturnValueHandlerUnitTests extends AbstractJMockTests {
|
||||
@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>>(
|
||||
@@ -90,12 +97,11 @@ public class ResourceProcessorHandlerMethodReturnValueHandlerUnitTests extends A
|
||||
});
|
||||
}
|
||||
|
||||
HandlerMethodReturnValueHandler delegate;
|
||||
@Mock HandlerMethodReturnValueHandler delegate;
|
||||
List<ResourceProcessor<?>> resourceProcessors;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
delegate = context.mock(HandlerMethodReturnValueHandler.class);
|
||||
resourceProcessors = new ArrayList<ResourceProcessor<?>>();
|
||||
}
|
||||
|
||||
@@ -188,6 +194,20 @@ public class ResourceProcessorHandlerMethodReturnValueHandlerUnitTests extends A
|
||||
|
||||
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,
|
||||
@@ -195,12 +215,9 @@ public class ResourceProcessorHandlerMethodReturnValueHandlerUnitTests extends A
|
||||
Object returnValue) throws Exception {
|
||||
final MethodParameter methodParam = METHOD_PARAMS.get(method);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(delegate).handleReturnValue(with(matcher),
|
||||
with(methodParam),
|
||||
with(aNull(ModelAndViewContainer.class)),
|
||||
with(aNull(NativeWebRequest.class)));
|
||||
}});
|
||||
if (methodParam == null) {
|
||||
throw new IllegalArgumentException("Invalid method!");
|
||||
}
|
||||
|
||||
HandlerMethodReturnValueHandler handler = new ResourceProcessorHandlerMethodReturnValueHandler(
|
||||
delegate,
|
||||
@@ -209,13 +226,10 @@ public class ResourceProcessorHandlerMethodReturnValueHandlerUnitTests extends A
|
||||
handler.handleReturnValue(returnValue, methodParam, null, null);
|
||||
}
|
||||
|
||||
private void assertSupport(final boolean value) {
|
||||
final MethodParameter parameter = context.mock(MethodParameter.class);
|
||||
private void assertSupport(boolean value) {
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(delegate).supportsReturnType(parameter);
|
||||
will(returnValue(value));
|
||||
}});
|
||||
final MethodParameter parameter = Mockito.mock(MethodParameter.class);
|
||||
when(delegate.supportsReturnType(Mockito.any(MethodParameter.class))).thenReturn(value);
|
||||
|
||||
HandlerMethodReturnValueHandler handler = new ResourceProcessorHandlerMethodReturnValueHandler(
|
||||
delegate,
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc.config;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
|
||||
import org.springframework.hateoas.core.DefaultRelProvider;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class RepositoryRestMvConfigurationIntegrationTests {
|
||||
|
||||
static AbstractApplicationContext context;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() {
|
||||
context = new AnnotationConfigApplicationContext(ExtendingConfiguration.class);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDown() {
|
||||
if (context != null) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void foo() {
|
||||
context.getBean(PageableHandlerMethodArgumentResolver.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ExtendingConfiguration extends RepositoryRestMvcConfiguration {
|
||||
|
||||
@Bean
|
||||
public DefaultRelProvider relProvider() {
|
||||
return new DefaultRelProvider();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc.jpa;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -9,20 +23,19 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||
import org.springframework.orm.jpa.JpaDialect;
|
||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.orm.jpa.vendor.Database;
|
||||
import org.springframework.orm.jpa.vendor.HibernateJpaDialect;
|
||||
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan(basePackageClasses = JpaRepositoryConfig.class)
|
||||
@ComponentScan
|
||||
@EnableJpaRepositories
|
||||
@EnableTransactionManagement
|
||||
public class JpaRepositoryConfig {
|
||||
@@ -32,7 +45,7 @@ public class JpaRepositoryConfig {
|
||||
return builder.setType(EmbeddedDatabaseType.HSQL).build();
|
||||
}
|
||||
|
||||
@Bean public EntityManagerFactory entityManagerFactory() {
|
||||
@Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
|
||||
vendorAdapter.setDatabase(Database.HSQL);
|
||||
vendorAdapter.setGenerateDdl(true);
|
||||
@@ -41,20 +54,12 @@ public class JpaRepositoryConfig {
|
||||
factory.setJpaVendorAdapter(vendorAdapter);
|
||||
factory.setPackagesToScan(getClass().getPackage().getName());
|
||||
factory.setDataSource(dataSource());
|
||||
|
||||
factory.afterPropertiesSet();
|
||||
|
||||
return factory.getObject();
|
||||
}
|
||||
|
||||
@Bean public JpaDialect jpaDialect() {
|
||||
return new HibernateJpaDialect();
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean public PlatformTransactionManager transactionManager() {
|
||||
JpaTransactionManager txManager = new JpaTransactionManager();
|
||||
txManager.setEntityManagerFactory(entityManagerFactory());
|
||||
return txManager;
|
||||
return new JpaTransactionManager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc.jpa;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.rest.webmvc.AbstractWebIntegrationTests;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
* Web integration tests specific to JPA.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@ContextConfiguration(classes = JpaRepositoryConfig.class)
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,37 @@
|
||||
package org.springframework.data.rest.repository.json;
|
||||
package org.springframework.data.rest.webmvc.json;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.repository.PersistentEntityResource;
|
||||
import org.springframework.data.rest.repository.RepositoryTestsConfig;
|
||||
import org.springframework.data.rest.repository.domain.jpa.Person;
|
||||
import org.springframework.data.rest.repository.domain.jpa.PersonRepository;
|
||||
import org.springframework.data.rest.webmvc.jpa.Person;
|
||||
import org.springframework.data.rest.webmvc.jpa.PersonRepository;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkDiscoverer;
|
||||
import org.springframework.hateoas.core.DefaultLinkDiscoverer;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.util.UriTemplate;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = RepositoryTestsConfig.class)
|
||||
@Transactional
|
||||
public class PersistentEntitySerializationTests {
|
||||
|
||||
private static final String PERSON_JSON_IN = "{\"firstName\": \"John\",\"lastName\": \"Doe\"}";
|
||||
@@ -38,22 +39,19 @@ public class PersistentEntitySerializationTests {
|
||||
@Autowired ObjectMapper mapper;
|
||||
@Autowired Repositories repositories;
|
||||
@Autowired PersonRepository people;
|
||||
|
||||
public static Matcher<Link> isLinkWithHref(final String href) {
|
||||
return new BaseMatcher<Link>() {
|
||||
@Override public boolean matches(Object item) {
|
||||
return (item instanceof Link && href.equals(((Link)item).getHref()));
|
||||
}
|
||||
|
||||
@Override public void describeTo(Description description) {
|
||||
description.appendText(href);
|
||||
}
|
||||
};
|
||||
|
||||
LinkDiscoverer linkDiscoverer;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
linkDiscoverer = new DefaultLinkDiscoverer();
|
||||
}
|
||||
|
||||
@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));
|
||||
@@ -62,13 +60,18 @@ public class PersistentEntitySerializationTests {
|
||||
@Test
|
||||
public void serializesPersonEntity() throws IOException, InterruptedException {
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
PersistentEntity<?, ?> persistentEntity = repositories.getPersistentEntity(Person.class);
|
||||
Person person = people.save(new Person("John", "Doe"));
|
||||
mapper.writeValue(out, PersistentEntityResource.wrap(persistentEntity, person, URI.create("http://localhost")));
|
||||
out.flush();
|
||||
String s = new String(out.toByteArray());
|
||||
|
||||
assertThat("Siblings Link looks correct", JsonPath.read(s, "$links[0].href").toString(), endsWith("/2/siblings"));
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
mapper.writeValue(writer, PersistentEntityResource.wrap(persistentEntity, person));
|
||||
|
||||
String s = writer.toString();
|
||||
|
||||
Link fatherLink = linkDiscoverer.findLinkWithRel("father", s);
|
||||
assertThat(fatherLink.getHref(), endsWith(new UriTemplate("/{id}/father").expand(person.getId()).toString()));
|
||||
|
||||
Link siblingLink = linkDiscoverer.findLinkWithRel("siblings", s);
|
||||
assertThat(siblingLink.getHref(), endsWith(new UriTemplate("/{id}/siblings").expand(person.getId()).toString()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package org.springframework.data.rest.webmvc.json;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.repository.support.DomainClassConverter;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.repository.UriDomainClassConverter;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.webmvc.jpa.JpaRepositoryConfig;
|
||||
import org.springframework.data.rest.webmvc.jpa.Person;
|
||||
import org.springframework.data.rest.webmvc.jpa.PersonRepository;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@Configuration
|
||||
@Import({JpaRepositoryConfig.class})
|
||||
@SuppressWarnings("deprecation")
|
||||
public class RepositoryTestsConfig {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext appCtx;
|
||||
|
||||
@Bean public Repositories repositories() {
|
||||
return new Repositories(appCtx);
|
||||
}
|
||||
|
||||
@Bean public RepositoryRestConfiguration config() {
|
||||
RepositoryRestConfiguration config = new RepositoryRestConfiguration();
|
||||
|
||||
config.setResourceMappingForDomainType(Person.class)
|
||||
.setRel("person");
|
||||
|
||||
// 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"));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@Bean public DefaultFormattingConversionService defaultConversionService() {
|
||||
return new DefaultFormattingConversionService();
|
||||
}
|
||||
|
||||
@Bean public DomainClassConverter<?> domainClassConverter() {
|
||||
return new DomainClassConverter<DefaultFormattingConversionService>(defaultConversionService());
|
||||
}
|
||||
|
||||
@Bean public UriDomainClassConverter uriDomainClassConverter() {
|
||||
return new UriDomainClassConverter();
|
||||
}
|
||||
|
||||
@Bean public Module persistentEntityModule() {
|
||||
return new PersistentEntityJackson2Module(new ResourceMappings(config(), repositories()));
|
||||
}
|
||||
|
||||
@Bean public ObjectMapper objectMapper() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.registerModule(persistentEntityModule());
|
||||
return mapper;
|
||||
}
|
||||
}
|
||||
16
spring-data-rest-webmvc/src/test/resources/logback.xml
Normal file
16
spring-data-rest-webmvc/src/test/resources/logback.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework" level="info" />
|
||||
|
||||
<root level="error">
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user