diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/UriDomainClassConverter.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/UriDomainClassConverter.java index d2a842110..8ab44de72 100644 --- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/UriDomainClassConverter.java +++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/UriDomainClassConverter.java @@ -1,59 +1,104 @@ +/* + * 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.HashSet; import java.util.Set; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.ConditionalGenericConverter; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.repository.support.DomainClassConverter; -import org.springframework.data.rest.repository.support.RepositoryInformationSupport; +import org.springframework.data.repository.support.Repositories; +import org.springframework.util.Assert; /** * A {@link ConditionalGenericConverter} that can convert a {@link URI} domain entity. * * @author Jon Brisbin + * @author Oliver Gierke */ -public class UriDomainClassConverter extends RepositoryInformationSupport implements ConditionalGenericConverter, - InitializingBean { +public class UriDomainClassConverter implements ConditionalGenericConverter { - private static TypeDescriptor STRING_TYPE = TypeDescriptor.valueOf(String.class); + private static final TypeDescriptor STRING_TYPE = TypeDescriptor.valueOf(String.class); - @Autowired private DomainClassConverter domainClassConverter; - private Set convertiblePairs = new HashSet(); + private final Repositories repositories; + private final DomainClassConverter domainClassConverter; + private final Set convertiblePairs; + + /** + * Creates a new {@link UriDomainClassConverter} using the given {@link Repositories} and {@link DomainClassConverter} + * . + * + * @param repositories must not be {@literal null}. + * @param domainClassConverter must not be {@literal null}. + */ + public UriDomainClassConverter(Repositories repositories, DomainClassConverter domainClassConverter) { + + Assert.notNull(repositories, "Repositories must not be null!"); + Assert.notNull(domainClassConverter, "DomainClassConverter must not be null!"); + + this.repositories = repositories; + this.domainClassConverter = domainClassConverter; + this.convertiblePairs = new HashSet(); - @Override - public void afterPropertiesSet() throws Exception { for (Class domainType : repositories) { convertiblePairs.add(new ConvertiblePair(URI.class, domainType)); } } + /* + * (non-Javadoc) + * @see org.springframework.core.convert.converter.ConditionalConverter#matches(org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor) + */ @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { + return URI.class.isAssignableFrom(sourceType.getType()) - && (null != repositories.getPersistentEntity(targetType.getType())); + && repositories.getPersistentEntity(targetType.getType()) != null; } + /* + * (non-Javadoc) + * @see org.springframework.core.convert.converter.GenericConverter#getConvertibleTypes() + */ @Override public Set getConvertibleTypes() { return convertiblePairs; } + /* + * (non-Javadoc) + * @see org.springframework.core.convert.converter.GenericConverter#convert(java.lang.Object, org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor) + */ @Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { + PersistentEntity entity = repositories.getPersistentEntity(targetType.getType()); - if (null == entity || !domainClassConverter.matches(STRING_TYPE, targetType)) { + + if (entity != null || !domainClassConverter.matches(STRING_TYPE, targetType)) { throw new ConversionFailedException(sourceType, targetType, source, new IllegalArgumentException( "No PersistentEntity information available for " + targetType.getType())); } URI uri = (URI) source; String[] parts = uri.getPath().split("/"); + if (parts.length < 2) { throw new ConversionFailedException(sourceType, targetType, source, new IllegalArgumentException( "Cannot resolve URI " + uri + ". Is it local or remote? Only local URIs are resolvable.")); diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/CrudRepositoryInvoker.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/CrudRepositoryInvoker.java new file mode 100644 index 000000000..a52ab8db7 --- /dev/null +++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/CrudRepositoryInvoker.java @@ -0,0 +1,173 @@ +/* + * 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.invoke; + +import java.io.Serializable; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.repository.CrudRepository; + +/** + * @author Oliver Gierke + */ +class CrudRepositoryInvoker implements RepositoryInvoker { + + private final CrudRepository repository; + + /** + * @param repository must not be {@literal null}. + */ + public CrudRepositoryInvoker(CrudRepository repository) { + this.repository = repository; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Sort) + */ + @Override + public Iterable findAll(Sort sort) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Pageable) + */ + @Override + public Page findAll(Pageable pageable) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object) + */ + @Override + public S save(S entity) { + return repository.save(entity); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable) + */ + @Override + public Iterable save(Iterable entities) { + return repository.save(entities); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.CrudRepository#findOne(java.io.Serializable) + */ + @Override + public Object findOne(Serializable id) { + return repository.findOne(id); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.CrudRepository#exists(java.io.Serializable) + */ + @Override + public boolean exists(Serializable id) { + return repository.exists(id); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.CrudRepository#findAll() + */ + @Override + public Iterable findAll() { + return repository.findAll(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable) + */ + @Override + public Iterable findAll(Iterable ids) { + return repository.findAll(ids); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.CrudRepository#count() + */ + @Override + public long count() { + return repository.count(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.CrudRepository#delete(java.io.Serializable) + */ + @Override + public void delete(Serializable id) { + repository.delete(id); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Object) + */ + @Override + public void delete(Object entity) { + repository.delete(entity); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable) + */ + @Override + public void delete(Iterable entities) { + repository.delete(entities); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.CrudRepository#deleteAll() + */ + @Override + public void deleteAll() { + repository.deleteAll(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.repository.invoke.RepositoryInvocationInformation#hasFindOne() + */ + @Override + public boolean hasFindOne() { + return true; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.repository.invoke.RepositoryInvocationInformation#hasFindAll() + */ + @Override + public boolean hasFindAll() { + return true; + } +} diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/PagingAndSortingRepositoryInvoker.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/PagingAndSortingRepositoryInvoker.java new file mode 100644 index 000000000..24ccd3ac1 --- /dev/null +++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/PagingAndSortingRepositoryInvoker.java @@ -0,0 +1,57 @@ +/* + * 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.invoke; + +import java.io.Serializable; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.repository.PagingAndSortingRepository; + +/** + * @author Oliver Gierke + */ +class PagingAndSortingRepositoryInvoker extends CrudRepositoryInvoker { + + private final PagingAndSortingRepository repository; + + /** + * @param repository must not be {@literal null}. + */ + public PagingAndSortingRepositoryInvoker(PagingAndSortingRepository repository) { + super(repository); + this.repository = repository; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.repository.invoke.CrudRepositoryInvoker#findAll(org.springframework.data.domain.Pageable) + */ + @Override + public Page findAll(Pageable pageable) { + return repository.findAll(pageable); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.repository.invoke.CrudRepositoryInvoker#findAll(org.springframework.data.domain.Sort) + */ + @Override + public Iterable findAll(Sort sort) { + return repository.findAll(sort); + } +} diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryInvocationInformation.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryInvocationInformation.java new file mode 100644 index 000000000..7e12352c4 --- /dev/null +++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryInvocationInformation.java @@ -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.invoke; + +/** + * @author Oliver Gierke + */ +public interface RepositoryInvocationInformation { + + boolean hasFindOne(); + + boolean hasFindAll(); +} diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryInvoker.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryInvoker.java new file mode 100644 index 000000000..23ba8773f --- /dev/null +++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryInvoker.java @@ -0,0 +1,28 @@ +/* + * 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.invoke; + +import java.io.Serializable; + +import org.springframework.data.repository.PagingAndSortingRepository; + +/** + * @author Oliver Gierke + */ +public interface RepositoryInvoker extends PagingAndSortingRepository, + RepositoryInvocationInformation { + +} diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryInvokerFactory.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryInvokerFactory.java new file mode 100644 index 000000000..58a895406 --- /dev/null +++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryInvokerFactory.java @@ -0,0 +1,68 @@ +/* + * 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.invoke; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.support.Repositories; + +/** + * @author Oliver Gierke + */ +public class RepositoryInvokerFactory { + + private final Repositories repositories; + private final Map, RepositoryInvoker> invokers; + + /** + * @param repositories + * @param invokers + */ + public RepositoryInvokerFactory(Repositories repositories) { + + this.repositories = repositories; + this.invokers = new HashMap, RepositoryInvoker>(); + prepareInvokers(repositories); + } + + @SuppressWarnings("unchecked") + private final void prepareInvokers(Repositories repositories) { + + for (Class domainType : repositories) { + + Object repository = repositories.getRepositoryFor(domainType); + RepositoryInvoker invoker = null; + + if (repository instanceof PagingAndSortingRepository) { + invoker = new PagingAndSortingRepositoryInvoker((PagingAndSortingRepository) repository); + } else if (repository instanceof CrudRepository) { + invoker = new CrudRepositoryInvoker((CrudRepository) repository); + } else { + invoker = new RepositoryMethodInvoker(repository, null, null); + } + + invokers.put(domainType, invoker); + } + } + + public RepositoryInvoker getInvokerFor(Class domainType) { + return invokers.get(domainType); + } +} diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryMethodInvoker.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryMethodInvoker.java index 28e97e6bd..abf85d55c 100644 --- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryMethodInvoker.java +++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryMethodInvoker.java @@ -13,7 +13,6 @@ import org.springframework.core.convert.ConversionService; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.rest.repository.support.ResourceMappingUtils; import org.springframework.util.ReflectionUtils.MethodCallback; @@ -22,7 +21,7 @@ import org.springframework.util.ReflectionUtils.MethodCallback; * @author Jon Brisbin */ @SuppressWarnings("deprecation") -public class RepositoryMethodInvoker implements PagingAndSortingRepository { +public class RepositoryMethodInvoker implements RepositoryInvoker { private final Object repository; private final Map queryMethods = new HashMap(); @@ -57,11 +56,11 @@ public class RepositoryMethodInvoker implements PagingAndSortingRepository paramType = (cardinality == 1 ? method.getParameterTypes()[0] : null); - boolean someMethod = (null != paramType && Iterable.class.isAssignableFrom(paramType)); - boolean byIdMethod = (null != paramType && paramType == Serializable.class); - boolean sortable = (null != paramType && Sort.class.isAssignableFrom(paramType)); - boolean pageable = (null != paramType && Pageable.class.isAssignableFrom(paramType)); + Class paramType = cardinality == 1 ? method.getParameterTypes()[0] : null; + boolean someMethod = null != paramType && Iterable.class.isAssignableFrom(paramType); + boolean byIdMethod = null != paramType && paramType == Serializable.class; + boolean sortable = null != paramType && Sort.class.isAssignableFrom(paramType); + boolean pageable = null != paramType && Pageable.class.isAssignableFrom(paramType); RepositoryMethod repoMethod = new RepositoryMethod(method); if ("save".equals(name) && someMethod) { diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/support/RepositoryInformationSupport.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/support/RepositoryInformationSupport.java deleted file mode 100644 index 169ba8027..000000000 --- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/support/RepositoryInformationSupport.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.springframework.data.rest.repository.support; - -import static org.springframework.data.rest.repository.support.ResourceMappingUtils.*; -import static org.springframework.util.ReflectionUtils.*; -import static org.springframework.util.StringUtils.*; - -import java.lang.reflect.Method; - -import org.springframework.beans.factory.annotation.Autowired; -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.invoke.RepositoryMethod; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; - -/** - * @author Jon Brisbin - */ -@SuppressWarnings("deprecation") -public abstract class RepositoryInformationSupport { - - protected Repositories repositories; - protected RepositoryRestConfiguration config; - protected MultiValueMap, RepositoryMethod> repositoryMethods = new LinkedMultiValueMap, RepositoryMethod>(); - - public Repositories getRepositories() { - return repositories; - } - - @Autowired - public void setRepositories(Repositories repositories) { - this.repositories = repositories; - for (Class domainType : repositories) { - final RepositoryInformation repoInfo = repositories.getRepositoryInformationFor(domainType); - doWithMethods(repoInfo.getRepositoryInterface(), new MethodCallback() { - @Override - public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { - repositoryMethods.add(repoInfo.getRepositoryInterface(), new RepositoryMethod(method)); - } - }); - } - } - - public RepositoryRestConfiguration getConfig() { - return config; - } - - @Autowired - public void setConfig(RepositoryRestConfiguration config) { - this.config = config; - } - - protected RepositoryInformation findRepositoryInfoFor(String pathSegment) { - if (!hasText(pathSegment)) { - return null; - } - for (Class domainType : repositories) { - RepositoryInformation repoInfo = findRepositoryInfoFor(domainType); - ResourceMapping mapping = getResourceMapping(config, repoInfo); - if (pathSegment.equals(mapping.getPath()) && mapping.isExported()) { - return repoInfo; - } - } - return null; - } - - protected RepositoryInformation findRepositoryInfoFor(Class domainType) { - PersistentEntity entity = repositories.getPersistentEntity(domainType); - if (null != entity) { - return repositories.getRepositoryInformationFor(domainType); - } - return null; - } - -} diff --git a/spring-data-rest-repository/src/test/java/org/springframework/data/rest/config/ResourceMappingUnitTests.java b/spring-data-rest-repository/src/test/java/org/springframework/data/rest/config/ResourceMappingUnitTests.java index 965bbb445..459a0422e 100644 --- a/spring-data-rest-repository/src/test/java/org/springframework/data/rest/config/ResourceMappingUnitTests.java +++ b/spring-data-rest-repository/src/test/java/org/springframework/data/rest/config/ResourceMappingUnitTests.java @@ -25,12 +25,8 @@ import org.junit.Test; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.repository.query.Param; -import org.springframework.data.rest.core.Path; import org.springframework.data.rest.repository.annotation.RestResource; -import org.springframework.data.rest.repository.domain.jpa.AnnotatedPersonRepository; import org.springframework.data.rest.repository.domain.jpa.Person; -import org.springframework.data.rest.repository.domain.jpa.PlainPersonRepository; -import org.springframework.data.rest.repository.mapping.RepositoryCollectionResourceMapping; import org.springframework.data.rest.repository.mapping.ResourceMapping; import org.springframework.hateoas.RelProvider; import org.springframework.hateoas.core.EvoInflectorRelProvider; @@ -44,27 +40,6 @@ import org.springframework.hateoas.core.EvoInflectorRelProvider; public class ResourceMappingUnitTests { RelProvider relProvider = new EvoInflectorRelProvider(); - - - @Test - public void shouldDetectDefaultRelAndPath() throws Exception { - - ResourceMapping newMapping = new RepositoryCollectionResourceMapping(PlainPersonRepository.class, relProvider); - - assertThat(newMapping.getRel(), is("persons")); - assertThat(newMapping.getPath(), is(new Path("person"))); - assertThat(newMapping.isExported(), is(true)); - } - - @Test - public void shouldDetectAnnotatedRelAndPath() throws Exception { - - ResourceMapping newMapping = new RepositoryCollectionResourceMapping(AnnotatedPersonRepository.class, relProvider); - - assertThat(newMapping.getRel(), is("people")); - assertThat(newMapping.getPath(), is(new Path("person"))); - assertThat(newMapping.isExported(), is(false)); - } @Test public void shouldDetectPathAndRemoveLeadingSlashIfAny() { diff --git a/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/RepositoryTestsConfig.java b/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/RepositoryTestsConfig.java index 613710462..9742a2cbb 100644 --- a/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/RepositoryTestsConfig.java +++ b/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/RepositoryTestsConfig.java @@ -56,6 +56,6 @@ public class RepositoryTestsConfig { @Bean public UriDomainClassConverter uriDomainClassConverter() { - return new UriDomainClassConverter(); + return new UriDomainClassConverter(repositories(), domainClassConverter()); } } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/AbstractRepositoryRestController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/AbstractRepositoryRestController.java index 54975e272..b07408791 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/AbstractRepositoryRestController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/AbstractRepositoryRestController.java @@ -33,8 +33,8 @@ import org.springframework.core.convert.ConversionFailedException; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.data.domain.Page; -import org.springframework.data.rest.config.ResourceMapping; import org.springframework.data.rest.repository.RepositoryConstraintViolationException; +import org.springframework.data.rest.repository.mapping.ResourceMetadata; import org.springframework.data.rest.webmvc.support.ExceptionMessage; import org.springframework.data.rest.webmvc.support.RepositoryConstraintViolationExceptionMessage; import org.springframework.data.rest.webmvc.support.ValidationExceptionHandler; @@ -196,8 +196,8 @@ class AbstractRepositoryRestController implements MessageSourceAware, Initializi } protected Link resourceLink(RepositoryRestRequest repoRequest, Resource resource) { - ResourceMapping repoMapping = repoRequest.getRepositoryResourceMapping(); - ResourceMapping entityMapping = repoRequest.getPersistentEntityResourceMapping(); + ResourceMetadata repoMapping = repoRequest.getRepositoryResourceMapping(); + ResourceMetadata entityMapping = repoRequest.getPersistentEntityResourceMapping(); Link selfLink = resource.getLink("self"); String rel = repoMapping.getRel() + "." + entityMapping.getRel(); @@ -205,11 +205,11 @@ class AbstractRepositoryRestController implements MessageSourceAware, Initializi } @SuppressWarnings({ "unchecked" }) - protected Resources resultToResources(Object result, Link baseLink) { + protected Resources resultToResources(Object result) { if (result instanceof Page) { Page page = (Page) result; - return entitiesToResources(page, baseLink, assembler); + return entitiesToResources(page, assembler); } else if (result instanceof Iterable) { return entitiesToResources((Iterable) result); } else if (null == result) { @@ -220,10 +220,10 @@ class AbstractRepositoryRestController implements MessageSourceAware, Initializi } } - protected Resources> entitiesToResources(Page page, Link baseLink, - final PagedResourcesAssembler assembler) { + protected Resources> entitiesToResources(Page page, + PagedResourcesAssembler assembler) { - return assembler.toResource(page, perAssembler, baseLink); + return assembler.toResource(page, perAssembler); } protected Resources> entitiesToResources(Iterable entities) { diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryEntityController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryEntityController.java index 08d15f487..d8131c6d8 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryEntityController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryEntityController.java @@ -44,6 +44,7 @@ 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.mapping.ResourceMetadata; import org.springframework.data.rest.repository.support.DomainObjectMerger; import org.springframework.data.web.PagedResourcesAssembler; import org.springframework.hateoas.EntityLinks; @@ -134,14 +135,13 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem throw new ResourceNotFoundException(); } - ResourceMapping repoMapping = request.getRepositoryResourceMapping(); + ResourceMetadata repoMapping = request.getRepositoryResourceMapping(); if (!repoMethodInvoker.getQueryMethods().isEmpty()) { links.add(entityLinks.linkForSingleResource(request.getPersistentEntity().getType(), "search").withRel( repoMapping.getRel() + ".search")); } - Link baseLink = request.getRepositoryLink(); - Resources resources = resultToResources(results, baseLink); + Resources resources = resultToResources(results); resources.add(links); return resources; } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryInvokerHandlerMethodArgumentResolver.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryInvokerHandlerMethodArgumentResolver.java new file mode 100644 index 000000000..67de5ceee --- /dev/null +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryInvokerHandlerMethodArgumentResolver.java @@ -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.webmvc; + +import org.springframework.core.MethodParameter; +import org.springframework.data.rest.repository.invoke.RepositoryInvoker; +import org.springframework.data.rest.repository.invoke.RepositoryInvokerFactory; +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; + +/** + * @author Oliver Gierke + */ +public class RepositoryInvokerHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { + + private final RepositoryRestRequestHandlerMethodArgumentResolver requestResolver; + private final RepositoryInvokerFactory invokerFactory; + + /** + * @param requestResolver + * @param invokerFactory + */ + private RepositoryInvokerHandlerMethodArgumentResolver( + RepositoryRestRequestHandlerMethodArgumentResolver requestResolver, RepositoryInvokerFactory invokerFactory) { + this.requestResolver = requestResolver; + this.invokerFactory = invokerFactory; + } + + /* + * (non-Javadoc) + * @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter) + */ + @Override + public boolean supportsParameter(MethodParameter parameter) { + return RepositoryInvoker.class.isAssignableFrom(parameter.getParameterType()); + } + + /* (non-Javadoc) + * @see org.springframework.web.method.support.HandlerMethodArgumentResolver#resolveArgument(org.springframework.core.MethodParameter, org.springframework.web.method.support.ModelAndViewContainer, org.springframework.web.context.request.NativeWebRequest, org.springframework.web.bind.support.WebDataBinderFactory) + */ + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + + RepositoryRestRequest request = requestResolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); + return invokerFactory.getInvokerFor(request.getPersistentEntity().getType()); + } +} diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestRequest.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestRequest.java index af942ffc4..37504583c 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestRequest.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestRequest.java @@ -15,19 +15,16 @@ */ 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 javax.servlet.http.HttpServletRequest; 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.invoke.RepositoryMethodInvoker; +import org.springframework.data.rest.repository.mapping.ResourceMetadata; import org.springframework.hateoas.Link; /** @@ -39,30 +36,22 @@ class RepositoryRestRequest { private final HttpServletRequest request; private final URI baseUri; - private final ResourceMapping repoMapping; + private final ResourceMetadata resourceMetadata; private final Link repoLink; - private final Object repository; private final RepositoryMethodInvoker repoMethodInvoker; private final PersistentEntity persistentEntity; - private final ResourceMapping entityMapping; public RepositoryRestRequest(RepositoryRestConfiguration config, Repositories repositories, - HttpServletRequest request, URI baseUri, RepositoryInformation repoInfo, ConversionService conversionService) { + HttpServletRequest request, URI baseUri, ResourceMetadata repoInfo, ConversionService conversionService) { this.request = request; this.baseUri = baseUri; - this.repoMapping = getResourceMapping(config, repoInfo); - if (null == repoMapping || !repoMapping.isExported()) { + this.resourceMetadata = repoInfo; + if (resourceMetadata == null || !resourceMetadata.isExported()) { this.repoLink = null; - this.repository = null; this.repoMethodInvoker = null; this.persistentEntity = null; - this.entityMapping = null; } else { - 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, conversionService); - this.entityMapping = getResourceMapping(config, persistentEntity); } } @@ -74,12 +63,8 @@ class RepositoryRestRequest { return baseUri; } - ResourceMapping getRepositoryResourceMapping() { - return repoMapping; - } - - Link getRepositoryLink() { - return repoLink; + ResourceMetadata getRepositoryResourceMapping() { + return resourceMetadata; } RepositoryMethodInvoker getRepositoryMethodInvoker() { @@ -89,8 +74,4 @@ class RepositoryRestRequest { PersistentEntity getPersistentEntity() { return persistentEntity; } - - ResourceMapping getPersistentEntityResourceMapping() { - return entityMapping; - } } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestRequestHandlerMethodArgumentResolver.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestRequestHandlerMethodArgumentResolver.java index ff6c9499b..fa763aa00 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestRequestHandlerMethodArgumentResolver.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestRequestHandlerMethodArgumentResolver.java @@ -16,14 +16,15 @@ 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.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.mapping.ResourceMetadata; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; @@ -39,25 +40,32 @@ public class RepositoryRestRequestHandlerMethodArgumentResolver implements Handl @Autowired private RepositoryRestConfiguration config; @Autowired private Repositories repositories; - @Autowired private RepositoryInformationHandlerMethodArgumentResolver repoInfoResolver; + @Autowired private ResourceMetadataHandlerMethodArgumentResolver repoInfoResolver; @Autowired private BaseUriMethodArgumentResolver baseUriResolver; public RepositoryRestRequestHandlerMethodArgumentResolver(ConversionService conversionService) { this.conversionService = conversionService; } + /* + * (non-Javadoc) + * @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter) + */ @Override public boolean supportsParameter(MethodParameter parameter) { return RepositoryRestRequest.class.isAssignableFrom(parameter.getParameterType()); } + /* + * (non-Javadoc) + * @see org.springframework.web.method.support.HandlerMethodArgumentResolver#resolveArgument(org.springframework.core.MethodParameter, org.springframework.web.method.support.ModelAndViewContainer, org.springframework.web.context.request.NativeWebRequest, org.springframework.web.bind.support.WebDataBinderFactory) + */ @Override - public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, + public RepositoryRestRequest resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { - URI baseUri = (URI) baseUriResolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); - RepositoryInformation repoInfo = repoInfoResolver.resolveArgument(parameter, mavContainer, webRequest, - binderFactory); + URI baseUri = baseUriResolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); + ResourceMetadata repoInfo = repoInfoResolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); return new RepositoryRestRequest(config, repositories, webRequest.getNativeRequest(HttpServletRequest.class), baseUri, repoInfo, conversionService); diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryInformationHandlerMethodArgumentResolver.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ResourceMetadataHandlerMethodArgumentResolver.java similarity index 50% rename from spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryInformationHandlerMethodArgumentResolver.java rename to spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ResourceMetadataHandlerMethodArgumentResolver.java index 3efbcb7fc..64acd14e0 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryInformationHandlerMethodArgumentResolver.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ResourceMetadataHandlerMethodArgumentResolver.java @@ -16,12 +16,16 @@ package org.springframework.data.rest.webmvc; import static org.springframework.util.ClassUtils.*; +import static org.springframework.util.StringUtils.*; import javax.servlet.http.HttpServletRequest; import org.springframework.core.MethodParameter; import org.springframework.data.repository.core.RepositoryInformation; -import org.springframework.data.rest.repository.support.RepositoryInformationSupport; +import org.springframework.data.repository.support.Repositories; +import org.springframework.data.rest.repository.mapping.ResourceMappings; +import org.springframework.data.rest.repository.mapping.ResourceMetadata; +import org.springframework.util.Assert; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; @@ -32,16 +36,39 @@ import org.springframework.web.util.UrlPathHelper; * @author Jon Brisbin * @author Oliver Gierke */ -public class RepositoryInformationHandlerMethodArgumentResolver extends RepositoryInformationSupport implements - HandlerMethodArgumentResolver { +public class ResourceMetadataHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { + private final Repositories repositories; + private final ResourceMappings mappings; + + /** + * @param repositories must not be {@literal null}. + * @param mappings must not be {@literal null}. + */ + public ResourceMetadataHandlerMethodArgumentResolver(Repositories repositories, ResourceMappings mappings) { + + Assert.notNull(repositories, "Repositories must not be null!"); + Assert.notNull(mappings, "ResourceMappings must not be null!"); + + this.repositories = repositories; + this.mappings = mappings; + } + + /* + * (non-Javadoc) + * @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter) + */ @Override public boolean supportsParameter(MethodParameter parameter) { return isAssignable(parameter.getParameterType(), RepositoryInformation.class); } + /* + * (non-Javadoc) + * @see org.springframework.web.method.support.HandlerMethodArgumentResolver#resolveArgument(org.springframework.core.MethodParameter, org.springframework.web.method.support.ModelAndViewContainer, org.springframework.web.context.request.NativeWebRequest, org.springframework.web.bind.support.WebDataBinderFactory) + */ @Override - public RepositoryInformation resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, + public ResourceMetadata resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); @@ -60,4 +87,20 @@ public class RepositoryInformationHandlerMethodArgumentResolver extends Reposito return findRepositoryInfoFor(parts[0]); } + + private ResourceMetadata findRepositoryInfoFor(String pathSegment) { + + if (!hasText(pathSegment)) { + return null; + } + + for (Class domainType : repositories) { + ResourceMetadata mapping = mappings.getMappingFor(domainType); + if (pathSegment.equals(mapping.getPath()) && mapping.isExported()) { + return mapping; + } + } + + return null; + } } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityToJsonSchemaConverter.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityToJsonSchemaConverter.java index 28465b8ca..7997555b1 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityToJsonSchemaConverter.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityToJsonSchemaConverter.java @@ -3,6 +3,7 @@ package org.springframework.data.rest.webmvc.json; 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; @@ -11,7 +12,6 @@ import java.util.Set; import javax.annotation.Nonnull; import javax.validation.constraints.NotNull; -import org.springframework.beans.factory.InitializingBean; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.ConditionalGenericConverter; import org.springframework.data.mapping.Association; @@ -19,40 +19,53 @@ 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.support.Repositories; 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; /** * @author Jon Brisbin */ -public class PersistentEntityToJsonSchemaConverter extends RepositoryInformationSupport implements - ConditionalGenericConverter, InitializingBean { +public class PersistentEntityToJsonSchemaConverter implements ConditionalGenericConverter { private static final TypeDescriptor STRING_TYPE = TypeDescriptor.valueOf(String.class); private static final TypeDescriptor SCHEMA_TYPE = TypeDescriptor.valueOf(JsonSchema.class); - private Set convertiblePairs = new HashSet(); - private ResourceMappings mappings; - @Override - public void afterPropertiesSet() throws Exception { + private final Set convertiblePairs = new HashSet(); + private final ResourceMappings mappings; + private final Repositories repositories; + + /** + * @param repositories must not be {@literal null}. + * @param mappings must not be {@literal null}. + */ + public PersistentEntityToJsonSchemaConverter(Repositories repositories, ResourceMappings mappings) { + + this.repositories = repositories; + this.mappings = mappings; for (Class domainType : repositories) { convertiblePairs.add(new ConvertiblePair(domainType, JsonSchema.class)); } - - this.mappings = new ResourceMappings(config, repositories); } + /* + * (non-Javadoc) + * @see org.springframework.core.convert.converter.ConditionalConverter#matches(org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor) + */ @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { - return (Class.class.isAssignableFrom(sourceType.getType()) && JsonSchema.class.isAssignableFrom(targetType - .getType())); + return Class.class.isAssignableFrom(sourceType.getType()) + && JsonSchema.class.isAssignableFrom(targetType.getType()); } + /* + * (non-Javadoc) + * @see org.springframework.core.convert.converter.GenericConverter#getConvertibleTypes() + */ @Override public Set getConvertibleTypes() { return convertiblePairs; @@ -68,8 +81,8 @@ public class PersistentEntityToJsonSchemaConverter extends RepositoryInformation PersistentEntity persistentEntity = repositories.getPersistentEntity((Class) source); final ResourceMetadata metadata = mappings.getMappingFor(persistentEntity.getClass()); - String entityDesc = persistentEntity.getType().isAnnotationPresent(Description.class) ? ((Description) persistentEntity - .getType().getAnnotation(Description.class)).value() : null; + String entityDesc = persistentEntity.getType().isAnnotationPresent(Description.class) ? persistentEntity.getType() + .getAnnotation(Description.class).value() : null; final JsonSchema jsonSchema = new JsonSchema(persistentEntity.getName(), entityDesc); persistentEntity.doWithProperties(new PropertyHandler() { @@ -77,10 +90,10 @@ public class PersistentEntityToJsonSchemaConverter extends RepositoryInformation public void doWithPersistentProperty(PersistentProperty persistentProperty) { Class propertyType = persistentProperty.getType(); String type = uncapitalize(propertyType.getSimpleName()); - boolean notNull = (persistentProperty.getField().isAnnotationPresent(Nonnull.class) || persistentProperty - .getGetter().isAnnotationPresent(Nonnull.class)) - || (persistentProperty.getField().isAnnotationPresent(NotNull.class) || persistentProperty.getGetter() - .isAnnotationPresent(NotNull.class)); + boolean notNull = persistentProperty.getField().isAnnotationPresent(Nonnull.class) + || persistentProperty.getGetter().isAnnotationPresent(Nonnull.class) + || persistentProperty.getField().isAnnotationPresent(NotNull.class) + || persistentProperty.getGetter().isAnnotationPresent(NotNull.class); String desc = persistentProperty.getField().isAnnotationPresent(Description.class) ? persistentProperty .getField().getAnnotation(Description.class).value() : persistentProperty.getGetter().isAnnotationPresent( Description.class) ? persistentProperty.getGetter().getAnnotation(Description.class).value() : null; @@ -98,14 +111,21 @@ public class PersistentEntityToJsonSchemaConverter extends RepositoryInformation final List links = new ArrayList(); persistentEntity.doWithAssociations(new AssociationHandler() { + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.AssociationHandler#doWithAssociation(org.springframework.data.mapping.Association) + */ @Override public void doWithAssociation(Association association) { + PersistentProperty persistentProperty = association.getInverse(); + if (!metadata.isMapped(persistentProperty)) { return; } - RepositoryLinkBuilder builder = new RepositoryLinkBuilder(metadata, config.getBaseUri()).slash("{id}"); + RepositoryLinkBuilder builder = new RepositoryLinkBuilder(metadata, URI.create("{id}")); maybeAddAssociationLink(builder, mappings, persistentProperty, links); } }); @@ -114,5 +134,4 @@ public class PersistentEntityToJsonSchemaConverter extends RepositoryInformation return jsonSchema; } - }