diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/MappingResourceMetadata.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/MappingResourceMetadata.java index b52455b04..d21aac9c9 100644 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/MappingResourceMetadata.java +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/MappingResourceMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,32 +19,44 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import org.springframework.data.mapping.Association; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.SimpleAssociationHandler; +import org.springframework.data.mapping.SimplePropertyHandler; import org.springframework.data.rest.core.mapping.SupportedHttpMethods.NoSupportedMethods; +import org.springframework.util.Assert; /** - * {@link ResourceMetadata} based on a {@link PersistentEntity}. + * {@link RootResourceMetadata} based on a {@link PersistentEntity}. * * @author Oliver Gierke * @since 2.1 */ -public class MappingResourceMetadata extends TypeBasedCollectionResourceMapping implements ResourceMetadata { +class MappingResourceMetadata extends TypeBasedCollectionResourceMapping implements ResourceMetadata { private final PersistentEntity entity; - private final Map, ResourceMapping> propertyMappings; + private final PropertyMappings propertyMappings; /** * Creates a new {@link MappingResourceMetadata} for the given {@link PersistentEntity}. * * @param entity must not be {@literal null}. */ - public MappingResourceMetadata(PersistentEntity entity) { + public MappingResourceMetadata(PersistentEntity entity, ResourceMappings resourceMappings) { super(entity.getType()); this.entity = entity; - this.propertyMappings = new HashMap, ResourceMapping>(); + this.propertyMappings = new PropertyMappings(resourceMappings); + } + + public MappingResourceMetadata init() { + + this.entity.doWithAssociations(propertyMappings); + this.entity.doWithProperties(propertyMappings); + + return this; } /* @@ -56,15 +68,6 @@ public class MappingResourceMetadata extends TypeBasedCollectionResourceMapping return entity.getType(); } - /* - * (non-Javadoc) - * @see org.springframework.data.rest.core.mapping.ResourceMetadata#isManagedResource(org.springframework.data.mapping.PersistentProperty) - */ - @Override - public boolean isManagedResource(PersistentProperty property) { - return property.isAssociation(); - } - /* * (non-Javadoc) * @see org.springframework.data.rest.core.mapping.ResourceMetadata#isExported(org.springframework.data.mapping.PersistentProperty) @@ -80,17 +83,7 @@ public class MappingResourceMetadata extends TypeBasedCollectionResourceMapping */ @Override public ResourceMapping getMappingFor(PersistentProperty property) { - - ResourceMapping propertyMapping = propertyMappings.get(property); - - if (propertyMapping != null) { - return propertyMapping; - } - - propertyMapping = new RepositoryResourceMappings.PersistentPropertyResourceMapping(property, this, this); - propertyMappings.put(property, propertyMapping); - - return propertyMapping; + return propertyMappings.getMappingFor(property); } /* @@ -110,4 +103,89 @@ public class MappingResourceMetadata extends TypeBasedCollectionResourceMapping public SupportedHttpMethods getSupportedHttpMethods() { return NoSupportedMethods.INSTANCE; } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.core.mapping.RootResourceMetadata#getProperty(java.lang.String) + */ + @Override + public PropertyAwareResourceMapping getProperty(String mappedPath) { + return propertyMappings.getMappingFor(mappedPath); + } + + /** + * Value object for {@link ResourceMapping}s for {@link PersistentProperty} instances. + * + * @author Oliver Gierke + * @see 2.1 + */ + private static class PropertyMappings implements SimpleAssociationHandler, SimplePropertyHandler { + + private final ResourceMappings resourceMappings; + private final Map, PropertyAwareResourceMapping> propertyMappings; + + /** + * Creates a new {@link PropertyMappings} instance for the given {@link ResourceMappings}. + * + * @param resourceMappings + */ + public PropertyMappings(ResourceMappings resourceMappings) { + + Assert.notNull(resourceMappings, "ResourceMappings must not be null!"); + + this.resourceMappings = resourceMappings; + this.propertyMappings = new HashMap, PropertyAwareResourceMapping>(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.SimpleAssociationHandler#doWithAssociation(org.springframework.data.mapping.Association) + */ + @Override + public void doWithAssociation(Association> association) { + doWithPersistentProperty(association.getInverse()); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.SimplePropertyHandler#doWithPersistentProperty(org.springframework.data.mapping.PersistentProperty) + */ + @Override + public void doWithPersistentProperty(PersistentProperty property) { + + Assert.notNull(property, "PersistentProperty must not be null!"); + + this.propertyMappings.put(property, new PersistentPropertyResourceMapping(property, resourceMappings)); + + } + + /** + * Returns the {@link PropertyAwareResourceMapping} for the given mapped path. + * + * @param mappedPath must not be {@literal null} or empty. + * @return the {@link PropertyAwareResourceMapping} if found, {@literal null} otherwise. + */ + public PropertyAwareResourceMapping getMappingFor(String mappedPath) { + + Assert.hasText(mappedPath, "Mapped path must not be null or empty!"); + + for (PropertyAwareResourceMapping mapping : propertyMappings.values()) { + if (mapping.getPath().matches(mappedPath)) { + return mapping; + } + } + + return null; + } + + /** + * Returns the {@link ResourceMapping} for the given {@link PersistentProperty}. + * + * @param property must not be {@literal null}. + * @return + */ + public ResourceMapping getMappingFor(PersistentProperty property) { + return propertyMappings.get(property); + } + } } diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/PersistentEntitiesResourceMappings.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/PersistentEntitiesResourceMappings.java new file mode 100644 index 000000000..a5a371018 --- /dev/null +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/PersistentEntitiesResourceMappings.java @@ -0,0 +1,225 @@ +/* + * Copyright 2015 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.core.mapping; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.context.PersistentEntities; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; + +/** + * {@link ResourceMappings} for {@link PersistentEntities}. + * + * @author Oliver Gierke + */ +public class PersistentEntitiesResourceMappings implements ResourceMappings { + + private final PersistentEntities entities; + private final SearchResourceMappings searchResourceMappings = new SearchResourceMappings( + Collections. emptyList()); + + private final Map, ResourceMetadata> cache = new HashMap, ResourceMetadata>(); + private final Map, MappingResourceMetadata> mappingCache = new HashMap, MappingResourceMetadata>(); + private final Map, ResourceMapping> propertyCache = new HashMap, ResourceMapping>(); + + /** + * Creates a new {@link PersistentEntitiesResourceMappings} from the given {@link PersistentEntities}. + * + * @param entities must not be {@literal null}. + */ + public PersistentEntitiesResourceMappings(PersistentEntities entities) { + this.entities = entities; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.core.mapping.ResourceMappings#getMappingFor(java.lang.Class) + */ + @Override + public ResourceMetadata getMetadataFor(Class type) { + + Assert.notNull(type, "Type must not be null!"); + + type = ClassUtils.getUserClass(type); + + if (cache.containsKey(type)) { + return cache.get(type); + } + + MappingResourceMetadata metadata = getMappingMetadataFor(type); + cache.put(type, metadata); + + return metadata; + } + + /** + * Returns the {@link MappingResourceMetadata} for the given type. + * + * @param type must not be {@literal null}. + * @return the {@link MappingResourceMetadata} if the given type is a {@link PersistentEntity}, {@literal null} + * otherwise. + */ + MappingResourceMetadata getMappingMetadataFor(Class type) { + + Assert.notNull(type, "Type must not be null!"); + type = ClassUtils.getUserClass(type); + + MappingResourceMetadata mappingMetadata = mappingCache.get(type); + + if (mappingMetadata != null) { + return mappingMetadata; + } + + PersistentEntity entity = entities.getPersistentEntity(type); + + if (entity == null) { + return null; + } + + mappingMetadata = new MappingResourceMetadata(entity, this); + mappingCache.put(type, mappingMetadata); + mappingMetadata.init(); + + return mappingMetadata; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.core.mapping.ResourceMappings#getSearchResourceMappings(java.lang.Class) + */ + @Override + public SearchResourceMappings getSearchResourceMappings(Class domainType) { + return searchResourceMappings; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.core.mapping.ResourceMappings#exportsMappingFor(java.lang.Class) + */ + @Override + public boolean exportsMappingFor(Class type) { + + if (!hasMappingFor(type)) { + return false; + } + + ResourceMetadata metadata = getMetadataFor(type); + return metadata.isExported(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.core.mapping.ResourceMappings#exportsTopLevelResourceFor(java.lang.String) + */ + @Override + public boolean exportsTopLevelResourceFor(String path) { + + Assert.hasText(path); + + for (ResourceMetadata metadata : this) { + if (metadata.getPath().matches(path)) { + return metadata.isExported(); + } + } + + return false; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.core.mapping.ResourceMappings#hasMappingFor(java.lang.Class) + */ + @Override + public boolean hasMappingFor(Class type) { + + if (cache.containsKey(type)) { + return true; + } + + return false; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.core.mapping.ResourceMetadataProvider#getMappingFor(org.springframework.data.mapping.PersistentProperty) + */ + public ResourceMapping getMappingFor(PersistentProperty property) { + + ResourceMapping propertyMapping = propertyCache.get(property); + + if (propertyMapping != null) { + return propertyMapping; + } + + propertyMapping = new PersistentPropertyResourceMapping(property, this); + + propertyCache.put(property, propertyMapping); + + return propertyMapping; + } + + public boolean isMapped(PersistentProperty property) { + + ResourceMapping metadata = getMappingFor(property); + return metadata != null && metadata.isExported(); + } + + /* + * (non-Javadoc) + * @see java.lang.Iterable#iterator() + */ + @Override + public Iterator iterator() { + + Set metadata = new HashSet(); + + for (ResourceMetadata candidate : cache.values()) { + if (candidate != null) { + metadata.add(candidate); + } + } + + return metadata.iterator(); + } + + /** + * Adds the given {@link ResourceMetadata} to the cache. + * + * @param type must not be {@literal null}. + * @param metadata can be {@literal null}. + */ + protected final void addToCache(Class type, ResourceMetadata metadata) { + cache.put(type, metadata); + } + + /** + * Returns whether we currently already have {@link ResourceMetadata} for the given type. + * + * @param type must not be {@literal null}. + * @return + */ + protected final boolean hasMetadataFor(Class type) { + return cache.containsKey(type); + } +} diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/PersistentPropertyResourceMapping.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/PersistentPropertyResourceMapping.java new file mode 100644 index 000000000..2b4e78dfe --- /dev/null +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/PersistentPropertyResourceMapping.java @@ -0,0 +1,121 @@ +/* + * Copyright 2015 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.core.mapping; + +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.rest.core.Path; +import org.springframework.data.rest.core.annotation.Description; +import org.springframework.data.rest.core.annotation.RestResource; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Special resource mapping for {@link PersistentProperty} instances. + * + * @author Oliver Gierke + */ +class PersistentPropertyResourceMapping implements PropertyAwareResourceMapping { + + private final PersistentProperty property; + private final ResourceMappings mappings; + private final RestResource annotation; + private final Description description; + + /** + * Creates a new {@link RootPropertyResourceMapping}. + * + * @param property must not be {@literal null}. + * @param exported whether the property is exported or not. + */ + public PersistentPropertyResourceMapping(PersistentProperty property, ResourceMappings mappings) { + + Assert.notNull(property, "PersistentProperty must not be null!"); + + this.property = property; + this.mappings = mappings; + this.annotation = property.isAssociation() ? property.findAnnotation(RestResource.class) : null; + this.description = property.findAnnotation(Description.class); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.core.mapping.ResourceMapping#getPath() + */ + @Override + public Path getPath() { + return annotation != null && StringUtils.hasText(annotation.path()) ? new Path(annotation.path()) : new Path( + property.getName()); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.core.mapping.ResourceMapping#getRel() + */ + @Override + public String getRel() { + return annotation != null && StringUtils.hasText(annotation.rel()) ? annotation.rel() : property.getName(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.core.mapping.ResourceMapping#isExported() + */ + @Override + public boolean isExported() { + + ResourceMapping typeMapping = mappings.getMetadataFor(property.getActualType()); + return !typeMapping.isExported() ? false : annotation == null ? true : annotation.exported(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.core.mapping.ResourceMapping#isPagingResource() + */ + @Override + public boolean isPagingResource() { + return false; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.core.mapping.ResourceMapping#getDescription() + */ + @Override + public ResourceDescription getDescription() { + + CollectionResourceMapping ownerTypeMapping = mappings.getMetadataFor(property.getOwner().getType()); + ResourceDescription fallback = TypedResourceDescription.defaultFor(ownerTypeMapping.getItemResourceRel(), property); + + if (description != null) { + return new AnnotationBasedResourceDescription(description, fallback); + } + + if (annotation != null) { + return new AnnotationBasedResourceDescription(annotation.description(), fallback); + } + + return fallback; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.core.mapping.PropertyAwareResourceMapping#getProperty() + */ + @Override + public PersistentProperty getProperty() { + return property; + } +} diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/PropertyAwareResourceMapping.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/PropertyAwareResourceMapping.java new file mode 100644 index 000000000..ffc2b6c89 --- /dev/null +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/PropertyAwareResourceMapping.java @@ -0,0 +1,26 @@ +/* + * Copyright 2015 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.core.mapping; + +import org.springframework.data.mapping.PersistentProperty; + +/** + * @author Oliver Gierke + */ +public interface PropertyAwareResourceMapping extends ResourceMapping { + + PersistentProperty getProperty(); +} diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryAwareResourceInformation.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryAwareResourceMetadata.java similarity index 76% rename from spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryAwareResourceInformation.java rename to spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryAwareResourceMetadata.java index c73f5ebba..14fffa29e 100644 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryAwareResourceInformation.java +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryAwareResourceMetadata.java @@ -17,9 +17,9 @@ package org.springframework.data.rest.core.mapping; import org.springframework.context.annotation.Primary; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.repository.core.RepositoryMetadata; -import org.springframework.data.repository.support.Repositories; import org.springframework.data.rest.core.Path; import org.springframework.util.Assert; @@ -28,46 +28,46 @@ import org.springframework.util.Assert; * * @author Oliver Gierke */ -class RepositoryAwareResourceInformation implements ResourceMetadata { +class RepositoryAwareResourceMetadata implements ResourceMetadata { - private final Repositories repositories; private final CollectionResourceMapping mapping; - private final RepositoryResourceMappings provider; - private final RepositoryMetadata repositoryInterface; + private final PersistentEntitiesResourceMappings provider; + private final RepositoryMetadata repositoryMetadata; private final SupportedHttpMethods crudMethodsSupportedHttpMethods; + private MappingResourceMetadata mappingMetadata; + /** - * Creates a new {@link RepositoryAwareResourceInformation} for the given {@link Repositories}, - * {@link CollectionResourceMapping}, {@link ResourceMappings} and {@link RepositoryMetadata}. + * Creates a new {@link RepositoryAwareResourceMetadata} for the given {@link CollectionResourceMapping}, + * {@link ResourceMappings} and {@link RepositoryMetadata}. * - * @param repositories must not be {@literal null}. + * @param entity must not be {@literal null}. * @param mapping must not be {@literal null}. * @param provider must not be {@literal null}. * @param repositoryMetadata must not be {@literal null}. */ - public RepositoryAwareResourceInformation(Repositories repositories, CollectionResourceMapping mapping, + public RepositoryAwareResourceMetadata(PersistentEntity entity, CollectionResourceMapping mapping, RepositoryResourceMappings provider, RepositoryMetadata repositoryMetadata) { - Assert.notNull(repositories, "Repositories must not be null!"); + Assert.notNull(entity, "PersistentEntity must not be null!"); Assert.notNull(mapping, "CollectionResourceMapping must not be null!"); Assert.notNull(provider, "ResourceMetadataProvider must not be null!"); Assert.notNull(repositoryMetadata, "RepositoryMetadata must not be null!"); - this.repositories = repositories; this.mapping = mapping; this.provider = provider; - this.repositoryInterface = repositoryMetadata; + this.repositoryMetadata = repositoryMetadata; this.crudMethodsSupportedHttpMethods = new CrudMethodsSupportedHttpMethods(repositoryMetadata.getCrudMethods()); } /** - * Returns whether the current {@link ResourceMetadata} instance for the repository is the primary one to be used. + * Returns whether the current {@link RootResourceMetadata} instance for the repository is the primary one to be used. * Reflects to the primary state of the bean definition. * * @return */ public boolean isPrimary() { - return AnnotationUtils.findAnnotation(repositoryInterface.getRepositoryInterface(), Primary.class) != null; + return AnnotationUtils.findAnnotation(repositoryMetadata.getRepositoryInterface(), Primary.class) != null; } /* @@ -76,18 +76,21 @@ class RepositoryAwareResourceInformation implements ResourceMetadata { */ @Override public Class getDomainType() { - return repositoryInterface.getDomainType(); + return repositoryMetadata.getDomainType(); } /* * (non-Javadoc) - * @see org.springframework.data.rest.core.mapping.DelegatingResourceInformation#isManaged(org.springframework.data.mapping.PersistentProperty) + * @see org.springframework.data.rest.core.mapping.RootResourceMetadata#getProperty(java.lang.String) */ @Override - public boolean isManagedResource(PersistentProperty property) { + public PropertyAwareResourceMapping getProperty(String mappedPath) { - Assert.notNull(property, "PersistentProperty must not be null!"); - return repositories.hasRepositoryFor(property.getActualType()); + if (this.mappingMetadata == null) { + this.mappingMetadata = provider.getMappingMetadataFor(getDomainType()); + } + + return mappingMetadata.getProperty(mappedPath); } /* @@ -186,7 +189,7 @@ class RepositoryAwareResourceInformation implements ResourceMetadata { */ @Override public SearchResourceMappings getSearchResourceMappings() { - return provider.getSearchResourceMappings(repositoryInterface.getDomainType()); + return provider.getSearchResourceMappings(repositoryMetadata.getDomainType()); } /* diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryResourceMappings.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryResourceMappings.java index c1d156e66..5dfaa81fc 100644 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryResourceMappings.java +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryResourceMappings.java @@ -18,22 +18,18 @@ package org.springframework.data.rest.core.mapping; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; +import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.context.PersistentEntities; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.support.Repositories; -import org.springframework.data.rest.core.Path; -import org.springframework.data.rest.core.annotation.Description; -import org.springframework.data.rest.core.annotation.RestResource; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.hateoas.RelProvider; import org.springframework.hateoas.core.EvoInflectorRelProvider; import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; /** * Central abstraction obtain {@link ResourceMetadata} and {@link ResourceMapping} instances for domain types and @@ -41,73 +37,56 @@ import org.springframework.util.StringUtils; * * @author Oliver Gierke */ -public class RepositoryResourceMappings implements ResourceMappings { +public class RepositoryResourceMappings extends PersistentEntitiesResourceMappings { private final Repositories repositories; - private final RelProvider relProvider; - - private final Map, ResourceMetadata> cache = new HashMap, ResourceMetadata>(); private final Map, SearchResourceMappings> searchCache = new HashMap, SearchResourceMappings>(); - private final Map, ResourceMapping> propertyCache = new HashMap, ResourceMapping>(); /** - * Creates a new {@link RepositoryResourceMappings} using the given {@link RepositoryRestConfiguration} and - * {@link Repositories} . + * Creates a new {@link RepositoryResourceMappings} using the given {@link Repositories} and + * {@link PersistentEntities}. * - * @param config - * @param repositories + * @param repositories must not be {@literal null}. + * @param entities must not be {@literal null}. */ - public RepositoryResourceMappings(RepositoryRestConfiguration config, Repositories repositories) { - this(config, repositories, new EvoInflectorRelProvider()); + public RepositoryResourceMappings(Repositories repositories, PersistentEntities entities) { + this(repositories, entities, new EvoInflectorRelProvider()); } /** * Creates a new {@link RepositoryResourceMappings} 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 entities must not be {@literal null}. * @param relProvider must not be {@literal null}. */ - public RepositoryResourceMappings(RepositoryRestConfiguration config, Repositories repositories, - RelProvider relProvider) { + public RepositoryResourceMappings(Repositories repositories, PersistentEntities entities, RelProvider relProvider) { + + super(entities); Assert.notNull(repositories, "Repositories must not be null!"); - Assert.notNull(relProvider, "RelProvider must not be null!"); this.repositories = repositories; - this.relProvider = relProvider; - - this.populateCache(repositories); + this.populateCache(repositories, relProvider); } - /* - * (non-Javadoc) - * @see org.springframework.data.rest.core.mapping.ResourceMappings#getMappingFor(java.lang.Class) - */ - @Override - public ResourceMetadata getMappingFor(Class type) { - - Assert.notNull(type, "Type must not be null!"); - return cache.get(ClassUtils.getUserClass(type)); - } - - private final void populateCache(Repositories repositories) { + private final void populateCache(Repositories repositories, RelProvider provider) { for (Class type : repositories) { RepositoryInformation repositoryInformation = repositories.getRepositoryInformationFor(type); Class repositoryInterface = repositoryInformation.getRepositoryInterface(); + PersistentEntity entity = repositories.getPersistentEntity(type); - CollectionResourceMapping mapping = new RepositoryCollectionResourceMapping(repositoryInformation, relProvider); + CollectionResourceMapping mapping = new RepositoryCollectionResourceMapping(repositoryInformation, provider); + RepositoryAwareResourceMetadata information = new RepositoryAwareResourceMetadata(entity, mapping, this, + repositoryInformation); - RepositoryAwareResourceInformation information = new RepositoryAwareResourceInformation(repositories, mapping, - this, repositoryInformation); + addToCache(repositoryInterface, information); - cache.put(repositoryInterface, information); - - if (!cache.containsKey(type) || information.isPrimary()) { - cache.put(type, information); + if (!hasMetadataFor(type) || information.isPrimary()) { + addToCache(type, information); } } } @@ -127,7 +106,7 @@ public class RepositoryResourceMappings implements ResourceMappings { RepositoryInformation repositoryInformation = repositories.getRepositoryInformationFor(domainType); List mappings = new ArrayList(); - ResourceMetadata resourceMapping = getMappingFor(domainType); + ResourceMetadata resourceMapping = getMetadataFor(domainType); if (resourceMapping.isExported()) { for (Method queryMethod : repositoryInformation.getQueryMethods()) { @@ -144,39 +123,6 @@ public class RepositoryResourceMappings implements ResourceMappings { return searchResourceMappings; } - /* - * (non-Javadoc) - * @see org.springframework.data.rest.core.mapping.ResourceMappings#exportsMappingFor(java.lang.Class) - */ - @Override - public boolean exportsMappingFor(Class type) { - - if (!hasMappingFor(type)) { - return false; - } - - ResourceMetadata metadata = getMappingFor(type); - return metadata.isExported(); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.core.mapping.ResourceMappings#exportsTopLevelResourceFor(java.lang.String) - */ - @Override - public boolean exportsTopLevelResourceFor(String path) { - - Assert.hasText(path); - - for (ResourceMetadata metadata : cache.values()) { - if (metadata.getPath().matches(path)) { - return metadata.isExported(); - } - } - - return false; - } - /* * (non-Javadoc) * @see org.springframework.data.rest.core.mapping.ResourceMappings#hasMappingFor(java.lang.Class) @@ -184,7 +130,7 @@ public class RepositoryResourceMappings implements ResourceMappings { @Override public boolean hasMappingFor(Class type) { - if (cache.containsKey(type)) { + if (super.hasMappingFor(type)) { return true; } @@ -197,132 +143,10 @@ public class RepositoryResourceMappings implements ResourceMappings { /* * (non-Javadoc) - * @see org.springframework.data.rest.core.mapping.ResourceMetadataProvider#getMappingFor(org.springframework.data.mapping.PersistentProperty) - */ - public ResourceMapping getMappingFor(PersistentProperty property) { - - ResourceMapping propertyMapping = propertyCache.get(property); - - if (propertyMapping != null) { - return propertyMapping; - } - - ResourceMetadata propertyTypeMapping = getMappingFor(property.getActualType()); - ResourceMetadata ownerTypeMapping = getMappingFor(property.getOwner().getType()); - propertyMapping = new PersistentPropertyResourceMapping(property, propertyTypeMapping, ownerTypeMapping); - - propertyCache.put(property, propertyMapping); - - return propertyMapping; - } - - public boolean isMapped(PersistentProperty property) { - - ResourceMapping metadata = getMappingFor(property); - return metadata != null && metadata.isExported(); - } - - /* - * (non-Javadoc) - * @see java.lang.Iterable#iterator() + * @see org.springframework.data.rest.core.mapping.PersistentEntitiesResourceMappings#isMapped(org.springframework.data.mapping.PersistentProperty) */ @Override - public Iterator iterator() { - return cache.values().iterator(); - } - - /** - * Special resource mapping for {@link PersistentProperty} instances. - * - * @author Oliver Gierke - */ - static class PersistentPropertyResourceMapping implements ResourceMapping { - - private final PersistentProperty property; - private final ResourceMapping typeMapping; - private final CollectionResourceMapping ownerTypeMapping; - private final RestResource annotation; - private final Description description; - - /** - * Creates a new {@link PersistentPropertyResourceMapping}. - * - * @param property must not be {@literal null}. - * @param exported whether the property is exported or not. - */ - public PersistentPropertyResourceMapping(PersistentProperty property, ResourceMapping typeMapping, - CollectionResourceMapping ownerTypeMapping) { - - Assert.notNull(property, "PersistentProperty must not be null!"); - - this.property = property; - this.typeMapping = typeMapping; - this.ownerTypeMapping = ownerTypeMapping; - this.annotation = property.isAssociation() ? property.findAnnotation(RestResource.class) : null; - this.description = property.findAnnotation(Description.class); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.core.mapping.ResourceMapping#getPath() - */ - @Override - public Path getPath() { - return annotation != null && StringUtils.hasText(annotation.path()) ? new Path(annotation.path()) : new Path( - property.getName()); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.core.mapping.ResourceMapping#getRel() - */ - @Override - public String getRel() { - return annotation != null && StringUtils.hasText(annotation.rel()) ? annotation.rel() : property.getName(); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.core.mapping.ResourceMapping#isExported() - */ - @Override - public boolean isExported() { - - if (typeMapping == null) { - return false; - } - - return !typeMapping.isExported() ? false : annotation == null ? true : annotation.exported(); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.core.mapping.ResourceMapping#isPagingResource() - */ - @Override - public boolean isPagingResource() { - return false; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.core.mapping.ResourceMapping#getDescription() - */ - @Override - public ResourceDescription getDescription() { - - ResourceDescription fallback = TypedResourceDescription.defaultFor(ownerTypeMapping.getItemResourceRel(), - property); - - if (description != null) { - return new AnnotationBasedResourceDescription(description, fallback); - } - - if (annotation != null) { - return new AnnotationBasedResourceDescription(annotation.description(), fallback); - } - - return fallback; - } + public boolean isMapped(PersistentProperty property) { + return repositories.hasRepositoryFor(property.getActualType()) && super.isMapped(property); } } diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java index 05e38b4f0..814fa72e2 100644 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java @@ -15,7 +15,6 @@ */ package org.springframework.data.rest.core.mapping; - /** * @author Oliver Gierke */ @@ -25,15 +24,15 @@ public interface ResourceMappings extends Iterable { * Returns a {@link ResourceMetadata} for the given type if available. * * @param type must not be {@literal null}. - * @return + * @return the {@link ResourceMetadata} if available or {@literal null} otherwise. */ - ResourceMetadata getMappingFor(Class type); + ResourceMetadata getMetadataFor(Class type); /** * Returns the {@link ResourceMapping}s for the search resources of the given type. * * @param type must not be {@literal null}. - * @return + * @return will never be {@literal null}. */ SearchResourceMappings getSearchResourceMappings(Class type); diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMetadata.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMetadata.java index 0b94565e1..fa10c8783 100644 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMetadata.java +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2013-2015 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. @@ -32,14 +32,6 @@ public interface ResourceMetadata extends CollectionResourceMapping { */ Class getDomainType(); - /** - * Returns whether the type of the given {@link PersistentProperty} is exposed as resource itself. - * - * @param property must not be {@literal null}. - * @return - */ - boolean isManagedResource(PersistentProperty property); - /** * Returns whether the given {@link PersistentProperty} is a managed resource and in fact exported. * @@ -48,6 +40,14 @@ public interface ResourceMetadata extends CollectionResourceMapping { */ boolean isExported(PersistentProperty property); + /** + * Returns the {@link PropertyAwareResourceMapping} for the given mapped path. + * + * @param mappedPath must not be {@literal null} or empty. + * @return the {@link PropertyAwareResourceMapping} for the given path or {@literal null} if none found. + */ + PropertyAwareResourceMapping getProperty(String mappedPath); + /** * Returns the {@link ResourceMapping} for the given {@link PersistentProperty} or {@literal null} if not managed. * diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/support/RepositoryRelProvider.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/support/RepositoryRelProvider.java index e15c910dc..9cf52ae70 100644 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/support/RepositoryRelProvider.java +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/support/RepositoryRelProvider.java @@ -49,7 +49,7 @@ public class RepositoryRelProvider implements RelProvider { */ @Override public String getCollectionResourceRelFor(Class type) { - return mappings.getObject().getMappingFor(type).getRel(); + return mappings.getObject().getMetadataFor(type).getRel(); } /* @@ -58,7 +58,7 @@ public class RepositoryRelProvider implements RelProvider { */ @Override public String getItemResourceRelFor(Class type) { - return mappings.getObject().getMappingFor(type).getItemResourceRel(); + return mappings.getObject().getMetadataFor(type).getItemResourceRel(); } /* diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/domain/jpa/Person.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/domain/jpa/Person.java index 208b28593..44a7597b0 100644 --- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/domain/jpa/Person.java +++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/domain/jpa/Person.java @@ -24,9 +24,12 @@ import java.util.List; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.PrePersist; +import org.springframework.data.rest.core.annotation.RestResource; + /** * An entity that represents a person. * @@ -40,6 +43,7 @@ public class Person { private String firstName; private String lastName; @OneToMany private List siblings = Collections.emptyList(); + private @RestResource(path = "father-mapped") @ManyToOne Person father; private Date created; public Person() {} diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/MappingResourceMetadataUnitTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/MappingResourceMetadataUnitTests.java new file mode 100644 index 000000000..918ec175e --- /dev/null +++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/MappingResourceMetadataUnitTests.java @@ -0,0 +1,66 @@ +/* + * Copyright 2015 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.core.mapping; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.util.Arrays; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.mapping.context.PersistentEntities; +import org.springframework.data.mongodb.core.mapping.DBRef; +import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; +import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; +import org.springframework.data.rest.core.annotation.RestResource; + +/** + * @author Oliver Gierke + */ +@RunWith(MockitoJUnitRunner.class) +public class MappingResourceMetadataUnitTests { + + MongoMappingContext context = new MongoMappingContext(); + + @Test + public void allowsLookupOfPropertyByMappedName() { + + ResourceMappings resourceMappings = new PersistentEntitiesResourceMappings(new PersistentEntities( + Arrays.asList(context))); + + MongoPersistentEntity entity = context.getPersistentEntity(Entity.class); + MongoPersistentProperty property = entity.getPersistentProperty("related"); + + MappingResourceMetadata metadata = new MappingResourceMetadata(entity, resourceMappings).init(); + + PropertyAwareResourceMapping propertyMapping = metadata.getProperty("foo"); + + assertThat(propertyMapping, is(notNullValue())); + assertThat(propertyMapping.getProperty(), is((Object) property)); + assertThat(metadata.getMappingFor(property).getPath().matches("foo"), is(true)); + } + + static class Entity { + @DBRef @RestResource(rel = "foo", path = "foo") private Related related; + } + + static class Related { + + } +} diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/PersistentPropertyResourceMappingUnitTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/PersistentPropertyResourceMappingUnitTests.java index d98269fd9..c81dd562c 100644 --- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/PersistentPropertyResourceMappingUnitTests.java +++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/PersistentPropertyResourceMappingUnitTests.java @@ -18,11 +18,13 @@ package org.springframework.data.rest.core.mapping; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import java.util.Arrays; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.mapping.context.PersistentEntities; import org.springframework.data.mongodb.core.mapping.DBRef; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; @@ -30,7 +32,6 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.rest.core.Path; import org.springframework.data.rest.core.annotation.Description; import org.springframework.data.rest.core.annotation.RestResource; -import org.springframework.data.rest.core.mapping.RepositoryResourceMappings.PersistentPropertyResourceMapping; /** * Unit tests for {@link PersistentPropertyResourceMapping}. @@ -113,10 +114,10 @@ public class PersistentPropertyResourceMappingUnitTests { MongoPersistentEntity persistentEntity = mappingContext.getPersistentEntity(entity); MongoPersistentProperty property = persistentEntity.getPersistentProperty(propertyName); - CollectionResourceMapping entityResourceMapping = new TypeBasedCollectionResourceMapping(entity); - ResourceMapping propertyTypeMapping = new TypeBasedCollectionResourceMapping(property.getType()); + ResourceMappings resourceMappings = new PersistentEntitiesResourceMappings(new PersistentEntities( + Arrays.asList(mappingContext))); - return new PersistentPropertyResourceMapping(property, propertyTypeMapping, entityResourceMapping); + return new PersistentPropertyResourceMapping(property, resourceMappings); } public static class Entity { diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/ResourceMappingsIntegrationTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryResourceMappingsIntegrationTests.java similarity index 78% rename from spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/ResourceMappingsIntegrationTests.java rename to spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryResourceMappingsIntegrationTests.java index 6cecb7e12..cbb6f3474 100644 --- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/ResourceMappingsIntegrationTests.java +++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryResourceMappingsIntegrationTests.java @@ -19,6 +19,7 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.hamcrest.Matchers; @@ -27,11 +28,12 @@ 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.jpa.mapping.JpaMetamodelMappingContext; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.context.PersistentEntities; import org.springframework.data.repository.support.Repositories; import org.springframework.data.rest.core.Path; -import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.core.domain.jpa.Author; import org.springframework.data.rest.core.domain.jpa.CreditCard; import org.springframework.data.rest.core.domain.jpa.JpaRepositoryConfig; @@ -41,7 +43,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; /** - * Integration tests for {@link ResourceMappings}. + * Integration tests for {@link RepositoryResourceMappings}. * * @author Oliver Gierke * @author Greg Trunquist @@ -49,9 +51,10 @@ import org.springframework.transaction.annotation.Transactional; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = JpaRepositoryConfig.class) @Transactional -public class ResourceMappingsIntegrationTests { +public class RepositoryResourceMappingsIntegrationTests { @Autowired ListableBeanFactory factory; + @Autowired JpaMetamodelMappingContext mappingContext; ResourceMappings mappings; @@ -59,18 +62,18 @@ public class ResourceMappingsIntegrationTests { public void setUp() { Repositories repositories = new Repositories(factory); - this.mappings = new RepositoryResourceMappings(new RepositoryRestConfiguration(), repositories); + this.mappings = new RepositoryResourceMappings(repositories, new PersistentEntities(Arrays.asList(mappingContext))); } @Test public void detectsAllMappings() { - assertThat(mappings, is(Matchers. iterableWithSize(8))); + assertThat(mappings, is(Matchers. iterableWithSize(4))); } @Test public void exportsResourceAndSearchesForPersons() { - ResourceMetadata personMappings = mappings.getMappingFor(Person.class); + ResourceMetadata personMappings = mappings.getMetadataFor(Person.class); assertThat(personMappings.isExported(), is(true)); assertThat(personMappings.getSearchResourceMappings().isExported(), is(true)); @@ -79,7 +82,7 @@ public class ResourceMappingsIntegrationTests { @Test public void doesNotExportAnyMappingsForHiddenRepository() { - ResourceMetadata creditCardMapping = mappings.getMappingFor(CreditCard.class); + ResourceMetadata creditCardMapping = mappings.getMetadataFor(CreditCard.class); assertThat(creditCardMapping.isExported(), is(false)); assertThat(creditCardMapping.getSearchResourceMappings().isExported(), is(false)); @@ -95,7 +98,7 @@ public class ResourceMappingsIntegrationTests { PersistentEntity entity = repositories.getPersistentEntity(Person.class); PersistentProperty property = entity.getPersistentProperty("siblings"); - ResourceMetadata metadata = mappings.getMappingFor(Person.class); + ResourceMetadata metadata = mappings.getMetadataFor(Person.class); ResourceMapping mapping = metadata.getMappingFor(property); assertThat(mapping.getRel(), is("siblings")); @@ -112,7 +115,7 @@ public class ResourceMappingsIntegrationTests { assertThat(mappings.exportsTopLevelResourceFor("people"), is(true)); assertThat(mappings.exportsTopLevelResourceFor("orders"), is(true)); - ResourceMetadata creditCardMapping = mappings.getMappingFor(CreditCard.class); + ResourceMetadata creditCardMapping = mappings.getMetadataFor(CreditCard.class); assertThat(creditCardMapping, is(notNullValue())); assertThat(creditCardMapping.getPath(), is(new Path("creditCards"))); assertThat(creditCardMapping.isExported(), is(false)); @@ -125,12 +128,12 @@ public class ResourceMappingsIntegrationTests { @Test public void skipsSearchMethodsNotExported() { - ResourceMetadata creditCardMetadata = mappings.getMappingFor(CreditCard.class); + ResourceMetadata creditCardMetadata = mappings.getMetadataFor(CreditCard.class); SearchResourceMappings searchResourceMappings = creditCardMetadata.getSearchResourceMappings(); assertThat(searchResourceMappings, is(Matchers. iterableWithSize(0))); - ResourceMetadata personMetadata = mappings.getMappingFor(Person.class); + ResourceMetadata personMetadata = mappings.getMetadataFor(Person.class); List methodNames = new ArrayList(); for (MethodResourceMapping method : personMetadata.getSearchResourceMappings()) { @@ -147,7 +150,7 @@ public class ResourceMappingsIntegrationTests { @Test public void exposesMethodResourceMappingInPackageProtectedButExportedRepo() { - ResourceMetadata metadata = mappings.getMappingFor(Author.class); + ResourceMetadata metadata = mappings.getMetadataFor(Author.class); assertThat(metadata.isExported(), is(true)); SearchResourceMappings searchMappings = metadata.getSearchResourceMappings(); @@ -161,4 +164,15 @@ public class ResourceMappingsIntegrationTests { assertThat(methodMapping.isExported(), is(true)); } } + + @Test + public void testname() { + + ResourceMetadata metadata = mappings.getMetadataFor(Person.class); + + PropertyAwareResourceMapping propertyMapping = metadata.getProperty("father-mapped"); + + assertThat(propertyMapping.getRel(), is("father")); + assertThat(propertyMapping.getPath(), is(new Path("father-mapped"))); + } } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PersistentEntityResourceAssembler.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PersistentEntityResourceAssembler.java index 6795a4c3c..3e147fcff 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PersistentEntityResourceAssembler.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PersistentEntityResourceAssembler.java @@ -124,7 +124,7 @@ public class PersistentEntityResourceAssembler implements ResourceAssembler associationProjections = new ArrayList(); final PersistentPropertyAccessor accessor = entity.getPropertyAccessor(instance); final AssociationLinks associationLinks = new AssociationLinks(mappings); - final ResourceMetadata metadata = mappings.getMappingFor(entity.getType()); + final ResourceMetadata metadata = mappings.getMetadataFor(entity.getType()); entity.doWithAssociations(new SimpleAssociationHandler() { diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryController.java index 36cea5f3e..0518ad610 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryController.java @@ -109,7 +109,7 @@ public class RepositoryController extends AbstractRepositoryRestController { for (Class domainType : repositories) { - ResourceMetadata metadata = mappings.getMappingFor(domainType); + ResourceMetadata metadata = mappings.getMetadataFor(domainType); if (metadata.isExported()) { resource.add(entityLinks.linkToCollectionResource(domainType)); } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceController.java index 4751fbd38..da85404e9 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceController.java @@ -45,6 +45,7 @@ import org.springframework.data.rest.core.event.AfterLinkDeleteEvent; import org.springframework.data.rest.core.event.AfterLinkSaveEvent; import org.springframework.data.rest.core.event.BeforeLinkDeleteEvent; import org.springframework.data.rest.core.event.BeforeLinkSaveEvent; +import org.springframework.data.rest.core.mapping.PropertyAwareResourceMapping; import org.springframework.data.rest.core.mapping.ResourceMapping; import org.springframework.data.rest.core.mapping.ResourceMetadata; import org.springframework.data.rest.core.mapping.ResourceType; @@ -445,32 +446,31 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro return conversionService.convert(id, type); } - private ResourceSupport doWithReferencedProperty(RootResourceInformation repoRequest, Serializable id, + private ResourceSupport doWithReferencedProperty(RootResourceInformation resourceInformation, Serializable id, String propertyPath, Function handler, HttpMethod method) throws Exception { - RepositoryInvoker invoker = repoRequest.getInvoker(); + RepositoryInvoker invoker = resourceInformation.getInvoker(); - if (!repoRequest.getSupportedMethods().supports(method, ResourceType.ITEM)) { + if (!resourceInformation.getSupportedMethods().supports(method, ResourceType.ITEM)) { throw new HttpRequestMethodNotSupportedException(method.name()); } + ResourceMetadata metadata = resourceInformation.getResourceMetadata(); + PropertyAwareResourceMapping mapping = metadata.getProperty(propertyPath); + + if (mapping == null || !mapping.isExported()) { + throw new ResourceNotFoundException(); + } + Object domainObj = invoker.invokeFindOne(id); if (null == domainObj) { throw new ResourceNotFoundException(); } - PersistentEntity persistentEntity = repoRequest.getPersistentEntity(); - PersistentProperty prop = persistentEntity.getPersistentProperty(propertyPath); - - if (null == prop) { - throw new ResourceNotFoundException(); - } - - PersistentPropertyAccessor accessor = persistentEntity.getPropertyAccessor(domainObj); - Object propVal = accessor.getProperty(prop); - - return handler.apply(new ReferencedProperty(prop, propVal, accessor)); + PersistentProperty property = mapping.getProperty(); + PersistentPropertyAccessor accessor = property.getOwner().getPropertyAccessor(domainObj); + return handler.apply(new ReferencedProperty(property, accessor.getProperty(property), accessor)); } private class ReferencedProperty { @@ -490,4 +490,5 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro this.entity = repositories.getPersistentEntity(propertyType); } } + } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositorySearchController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositorySearchController.java index 857319a52..2db4bbb6a 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositorySearchController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositorySearchController.java @@ -302,7 +302,7 @@ class RepositorySearchController extends AbstractRepositoryRestController { continue; } - ResourceMetadata metadata = mappings.getMappingFor(parameter.getParameterType()); + ResourceMetadata metadata = mappings.getMetadataFor(parameter.getParameterType()); if (metadata != null && metadata.isExported()) { result.put(parameter.getParameterName(), prepareUris(entry.getValue())); diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/alps/AlpsController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/alps/AlpsController.java index 685c7f520..3837b485b 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/alps/AlpsController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/alps/AlpsController.java @@ -108,7 +108,7 @@ public class AlpsController { for (Class domainType : repositories) { - ResourceMetadata mapping = mappings.getMappingFor(domainType); + ResourceMetadata mapping = mappings.getMetadataFor(domainType); if (mapping.isExported()) { diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/alps/RootResourceInformationToAlpsDescriptorConverter.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/alps/RootResourceInformationToAlpsDescriptorConverter.java index 2069c6691..43de05ab1 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/alps/RootResourceInformationToAlpsDescriptorConverter.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/alps/RootResourceInformationToAlpsDescriptorConverter.java @@ -51,7 +51,6 @@ import org.springframework.data.rest.core.mapping.SupportedHttpMethods; import org.springframework.data.rest.webmvc.RootResourceInformation; import org.springframework.data.rest.webmvc.json.JacksonMetadata; import org.springframework.data.rest.webmvc.mapping.AssociationLinks; -import org.springframework.data.rest.webmvc.mapping.PropertyMappings; import org.springframework.hateoas.EntityLinks; import org.springframework.hateoas.Link; import org.springframework.hateoas.TemplateVariable; @@ -146,7 +145,7 @@ public class RootResourceInformationToAlpsDescriptorConverter { private Descriptor buildRepresentationDescriptor(Class type) { - ResourceMetadata metadata = mappings.getMappingFor(type); + ResourceMetadata metadata = mappings.getMetadataFor(type); return descriptor().// id(metadata.getItemResourceRel().concat("-representation")).// @@ -159,7 +158,7 @@ public class RootResourceInformationToAlpsDescriptorConverter { private Descriptor buildCollectionResourceDescriptor(Class type, RootResourceInformation resourceInformation, Descriptor representationDescriptor, HttpMethod method) { - ResourceMetadata metadata = mappings.getMappingFor(type); + ResourceMetadata metadata = mappings.getMetadataFor(type); List nestedDescriptors = new ArrayList(); nestedDescriptors.addAll(getPaginationDescriptors(type, method)); @@ -241,7 +240,7 @@ public class RootResourceInformationToAlpsDescriptorConverter { Descriptor representationDescriptor, HttpMethod method) { PersistentEntity entity = resourceInformation.getPersistentEntity(); - ResourceMetadata metadata = mappings.getMappingFor(entity.getType()); + ResourceMetadata metadata = mappings.getMetadataFor(entity.getType()); return descriptor().// id(prefix(method).concat(metadata.getItemResourceRel())).// @@ -262,7 +261,7 @@ public class RootResourceInformationToAlpsDescriptorConverter { ProjectionDefinitionConfiguration projectionConfiguration = configuration.projectionConfiguration(); return projectionConfiguration.hasProjectionFor(type) ? Arrays.asList(buildProjectionDescriptor(mappings - .getMappingFor(type))) : Collections. emptyList(); + .getMetadataFor(type))) : Collections. emptyList(); } /** @@ -310,8 +309,8 @@ public class RootResourceInformationToAlpsDescriptorConverter { final PersistentEntity entity = persistentEntities.getPersistentEntity(type); final List propertyDescriptors = new ArrayList(); final JacksonMetadata jackson = new JacksonMetadata(mapper, type); - final PropertyMappings propertyMappings = new PropertyMappings(mappings); final AssociationLinks associationLinks = new AssociationLinks(mappings); + final ResourceMetadata metadata = mappings.getMetadataFor(entity.getType()); entity.doWithProperties(new SimplePropertyHandler() { @@ -319,7 +318,7 @@ public class RootResourceInformationToAlpsDescriptorConverter { public void doWithPersistentProperty(PersistentProperty property) { BeanPropertyDefinition propertyDefinition = jackson.getDefinitionFor(property); - ResourceMapping propertyMapping = propertyMappings.getMappingFor(property); + ResourceMapping propertyMapping = metadata.getMappingFor(property); if (propertyDefinition != null) { propertyDescriptors.add(// @@ -343,12 +342,12 @@ public class RootResourceInformationToAlpsDescriptorConverter { return; } - ResourceMapping mapping = propertyMappings.getMappingFor(property); + ResourceMapping mapping = metadata.getMappingFor(property); DescriptorBuilder builder = descriptor().// name(mapping.getRel()).doc(getDocFor(mapping.getDescription())); - ResourceMetadata targetTypeMapping = mappings.getMappingFor(property.getActualType()); + ResourceMetadata targetTypeMapping = mappings.getMetadataFor(property.getActualType()); String localPath = targetTypeMapping.getRel().concat("#").concat(targetTypeMapping.getItemResourceRel()); Link link = ControllerLinkBuilder.linkTo(AlpsController.class).slash(localPath).withSelfRel(); @@ -365,7 +364,7 @@ public class RootResourceInformationToAlpsDescriptorConverter { private Collection buildSearchResourceDescriptors(PersistentEntity entity) { - ResourceMetadata metadata = mappings.getMappingFor(entity.getType()); + ResourceMetadata metadata = mappings.getMetadataFor(entity.getType()); List descriptors = new ArrayList(); for (MethodResourceMapping methodMapping : metadata.getSearchResourceMappings()) { diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java index d1d4acc2d..210ad7a7f 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java @@ -525,11 +525,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon @Bean public ResourceMappings resourceMappings() { - - Repositories repositories = repositories(); - RepositoryRestConfiguration config = config(); - - return new RepositoryResourceMappings(config, repositories); + return new RepositoryResourceMappings(repositories(), persistentEntities()); } /** diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/ResourceMetadataHandlerMethodArgumentResolver.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/ResourceMetadataHandlerMethodArgumentResolver.java index c85b9a138..30eddd777 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/ResourceMetadataHandlerMethodArgumentResolver.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/ResourceMetadataHandlerMethodArgumentResolver.java @@ -88,7 +88,7 @@ public class ResourceMetadataHandlerMethodArgumentResolver implements HandlerMet } for (Class domainType : repositories) { - ResourceMetadata mapping = mappings.getMappingFor(domainType); + ResourceMetadata mapping = mappings.getMetadataFor(domainType); if (mapping.getPath().matches(repositoryKey) && mapping.isExported()) { return mapping; } 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 bf7c49a6c..e495645d3 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 @@ -33,7 +33,6 @@ import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.context.PersistentEntities; import org.springframework.data.rest.core.config.JsonSchemaFormat; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; -import org.springframework.data.rest.core.mapping.MappingResourceMetadata; import org.springframework.data.rest.core.mapping.ResourceDescription; import org.springframework.data.rest.core.mapping.ResourceMapping; import org.springframework.data.rest.core.mapping.ResourceMappings; @@ -137,7 +136,7 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric public JsonSchema convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { final PersistentEntity persistentEntity = entities.getPersistentEntity((Class) source); - final ResourceMetadata metadata = mappings.getMappingFor(persistentEntity.getType()); + final ResourceMetadata metadata = mappings.getMetadataFor(persistentEntity.getType()); Descriptors descriptors = new Descriptors(); List propertiesFor = getPropertiesFor(persistentEntity.getType(), metadata, descriptors); @@ -228,11 +227,7 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric return Collections.emptyList(); } - Class actualType = property.getActualType(); - PersistentEntity propertyEntity = entities.getPersistentEntity(actualType); - MappingResourceMetadata propertyMetadata = new MappingResourceMetadata(propertyEntity); - - return getPropertiesFor(actualType, propertyMetadata, descriptors); + return getPropertiesFor(property.getActualType(), mappings.getMetadataFor(property.getActualType()), descriptors); } private Property getSchemaProperty(BeanPropertyDefinition definition, TypeInformation type, diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/mapping/AssociationLinks.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/mapping/AssociationLinks.java index dff8c6462..f22d3a173 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/mapping/AssociationLinks.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/mapping/AssociationLinks.java @@ -37,7 +37,6 @@ import org.springframework.util.Assert; public class AssociationLinks { private final ResourceMappings mappings; - private final PropertyMappings propertyMappings; /** * Creates a new {@link AssociationLinks} using the given {@link ResourceMappings}. @@ -48,7 +47,6 @@ public class AssociationLinks { Assert.notNull(mappings, "ResourceMappings must not be null!"); - this.propertyMappings = new PropertyMappings(mappings); this.mappings = mappings; } @@ -68,7 +66,8 @@ public class AssociationLinks { if (isLinkableAssociation(property)) { - ResourceMapping propertyMapping = propertyMappings.getMappingFor(property); + ResourceMetadata metadata = mappings.getMetadataFor(property.getOwner().getType()); + ResourceMapping propertyMapping = metadata.getMappingFor(property); String href = path.slash(propertyMapping.getPath()).toString(); String rel = propertyMapping.getRel(); @@ -91,13 +90,13 @@ public class AssociationLinks { return false; } - ResourceMetadata metadata = mappings.getMappingFor(property.getOwner().getType()); + ResourceMetadata metadata = mappings.getMetadataFor(property.getOwner().getType()); if (metadata != null && !metadata.isExported(property)) { return false; } - metadata = mappings.getMappingFor(property.getActualType()); + metadata = mappings.getMetadataFor(property.getActualType()); return metadata == null ? false : metadata.isExported(); } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/mapping/PropertyMappings.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/mapping/PropertyMappings.java deleted file mode 100644 index cd9ba5313..000000000 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/mapping/PropertyMappings.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2014 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.mapping; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.data.mapping.PersistentProperty; -import org.springframework.data.rest.core.mapping.MappingResourceMetadata; -import org.springframework.data.rest.core.mapping.ResourceMapping; -import org.springframework.data.rest.core.mapping.ResourceMappings; -import org.springframework.data.rest.core.mapping.ResourceMetadata; -import org.springframework.util.Assert; - -/** - * Value object for {@link ResourceMapping}s for {@link PersistentProperty} instances. - * - * @author Oliver Gierke - * @see 2.1 - */ -public class PropertyMappings { - - private final ResourceMappings resourceMappings; - private final Map, ResourceMetadata> resourceMetadata; - - /** - * Creates a new {@link PropertyMappings} instance for the given {@link ResourceMappings}. - * - * @param resourceMappings - */ - public PropertyMappings(ResourceMappings resourceMappings) { - - Assert.notNull(resourceMappings, "ResourceMappings must not be null!"); - - this.resourceMappings = resourceMappings; - this.resourceMetadata = new HashMap, ResourceMetadata>(); - } - - /** - * Returns the {@link ResourceMapping} for the given {@link PersistentProperty}. - * - * @param property can be {@literal null}. - * @return - */ - public ResourceMapping getMappingFor(PersistentProperty property) { - - Assert.notNull(property, "PersistentProperty must not be null!"); - - ResourceMetadata metadata = resourceMetadata.get(property); - - if (metadata != null) { - return metadata.getMappingFor(property); - } - - metadata = resourceMappings.getMappingFor(property.getOwner().getType()); - - if (metadata != null) { - return cacheAndReturn(metadata, property); - } - - return cacheAndReturn(new MappingResourceMetadata(property.getOwner()), property); - } - - private ResourceMapping cacheAndReturn(ResourceMetadata metadata, PersistentProperty property) { - - resourceMetadata.put(property, metadata); - return metadata.getMappingFor(property); - } -} diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/PersistentEntityProjector.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/PersistentEntityProjector.java index dacef1c7e..5c971c164 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/PersistentEntityProjector.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/PersistentEntityProjector.java @@ -79,7 +79,7 @@ public class PersistentEntityProjector implements Projector { Assert.notNull(source, "Projection source must not be null!"); - ResourceMetadata metadata = mappings.getMappingFor(source.getClass()); + ResourceMetadata metadata = mappings.getMetadataFor(source.getClass()); Class projection = metadata == null ? null : metadata.getExcerptProjection(); if (projection == null) { @@ -96,7 +96,7 @@ public class PersistentEntityProjector implements Projector { @Override public boolean hasExcerptProjection(Class type) { - ResourceMetadata metadata = mappings.getMappingFor(type); + ResourceMetadata metadata = mappings.getMetadataFor(type); return metadata == null ? false : metadata.getExcerptProjection() != null; } } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryEntityLinks.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryEntityLinks.java index d0fa1a75a..a67ddb43e 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryEntityLinks.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryEntityLinks.java @@ -108,7 +108,7 @@ public class RepositoryEntityLinks extends AbstractEntityLinks { @Override public LinkBuilder linkFor(Class type) { - ResourceMetadata metadata = mappings.getMappingFor(type); + ResourceMetadata metadata = mappings.getMetadataFor(type); return new RepositoryLinkBuilder(metadata, new BaseUri(config.getBaseUri())); } @@ -130,7 +130,7 @@ public class RepositoryEntityLinks extends AbstractEntityLinks { */ public Link linkToPagedResource(Class type, Pageable pageable) { - ResourceMetadata metadata = mappings.getMappingFor(type); + ResourceMetadata metadata = mappings.getMetadataFor(type); String href = linkFor(type).toString(); UriComponents components = prepareUri(href, metadata, pageable); @@ -158,7 +158,7 @@ public class RepositoryEntityLinks extends AbstractEntityLinks { Assert.isInstanceOf(Serializable.class, id, "Id must be assignable to Serializable!"); - ResourceMetadata metadata = mappings.getMappingFor(type); + ResourceMetadata metadata = mappings.getMetadataFor(type); String mappedId = idConverters.getPluginFor(type, DefaultIdConverter.INSTANCE).toRequestId((Serializable) id, type); Link link = linkFor(type).slash(mappedId).withRel(metadata.getItemResourceRel()); diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryLinkBuilder.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryLinkBuilder.java index 82f71ac26..ba34e1329 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryLinkBuilder.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryLinkBuilder.java @@ -74,14 +74,7 @@ public class RepositoryLinkBuilder extends LinkBuilderSupport property) { - - String propName = property.getName(); - - if (metadata.isManagedResource(property)) { - return slash(metadata.getMappingFor(property).getPath()); - } else { - return slash(propName); - } + return slash(metadata.getMappingFor(property).getPath()); } public Link withResourceRel() { diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractControllerIntegrationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractControllerIntegrationTests.java index 8ba1a0e5e..4edfc313a 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractControllerIntegrationTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractControllerIntegrationTests.java @@ -79,7 +79,7 @@ public abstract class AbstractControllerIntegrationTests { PersistentEntity entity = repositories.getPersistentEntity(domainType); - return new RootResourceInformation(mappings.getMappingFor(domainType), entity, + return new RootResourceInformation(mappings.getMetadataFor(domainType), entity, invokerFactory.getInvokerFor(domainType)); } @@ -95,7 +95,7 @@ public abstract class AbstractControllerIntegrationTests { } protected ResourceMetadata getMetadata(Class domainType) { - return mappings.getMappingFor(domainType); + return mappings.getMetadataFor(domainType); } private static enum StubProjector implements Projector { diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AssociationLinksUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AssociationLinksUnitTests.java index 05a5678e3..621fd59cd 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AssociationLinksUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AssociationLinksUnitTests.java @@ -17,23 +17,23 @@ package org.springframework.data.rest.webmvc; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; -import static org.mockito.Mockito.*; +import java.util.Arrays; import java.util.List; 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.data.annotation.Reference; import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.context.PersistentEntities; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.rest.core.Path; import org.springframework.data.rest.core.annotation.RestResource; -import org.springframework.data.rest.core.mapping.MappingResourceMetadata; +import org.springframework.data.rest.core.mapping.PersistentEntitiesResourceMappings; import org.springframework.data.rest.core.mapping.ResourceMappings; import org.springframework.data.rest.core.mapping.ResourceMetadata; import org.springframework.data.rest.webmvc.mapping.AssociationLinks; @@ -47,7 +47,7 @@ public class AssociationLinksUnitTests { AssociationLinks links; - @Mock ResourceMappings mappings; + ResourceMappings mappings; MongoMappingContext mappingContext; MongoPersistentEntity entity; @@ -56,13 +56,10 @@ public class AssociationLinksUnitTests { @Before public void setUp() { - this.links = new AssociationLinks(mappings); - this.mappingContext = new MongoMappingContext(); this.entity = mappingContext.getPersistentEntity(Sample.class); - this.sampleResourceMetadata = new MappingResourceMetadata(entity); - - when(mappings.getMappingFor(Sample.class)).thenReturn(sampleResourceMetadata); + this.mappings = new PersistentEntitiesResourceMappings(new PersistentEntities(Arrays.asList(mappingContext))); + this.links = new AssociationLinks(mappings); } /** @@ -81,20 +78,12 @@ public class AssociationLinksUnitTests { assertThat(links.isLinkableAssociation(null), is(false)); } - /** - * @see DATAREST-262 - */ - @Test - public void consideredExportedPropertyLinkable() { - assertThat(links.isLinkableAssociation(exposeProperty("property")), is(true)); - } - /** * @see DATAREST-262 */ @Test public void consideredHiddenPropertyUnlinkable() { - assertThat(links.isLinkableAssociation(exposeProperty("hiddenProperty")), is(false)); + assertThat(links.isLinkableAssociation(entity.getPersistentProperty("hiddenProperty")), is(false)); } /** @@ -113,7 +102,7 @@ public class AssociationLinksUnitTests { @Test public void createsLinkToAssociationProperty() { - PersistentProperty property = exposeProperty("property"); + PersistentProperty property = entity.getPersistentProperty("property"); List associationLinks = links.getLinksFor(property.getAssociation(), new Path("/base")); assertThat(associationLinks, hasSize(1)); @@ -126,42 +115,17 @@ public class AssociationLinksUnitTests { @Test public void doesNotCreateLinksForHiddenProperty() { - PersistentProperty property = exposeProperty("hiddenProperty"); + PersistentProperty property = entity.getPersistentProperty("hiddenProperty"); assertThat(links.getLinksFor(property.getAssociation(), new Path("/sample")), hasSize(0)); } - /** - * @see DATAREST-262 - */ - @Test - public void doesNotCreateLinksForUnexportedProperty() { - - PersistentProperty property = entity.getPersistentProperty("unexportedProperty"); - assertThat(links.getLinksFor(property.getAssociation(), new Path("/sample")), hasSize(0)); - } - - private PersistentProperty exposeProperty(String name) { - - MongoPersistentProperty property = entity.getPersistentProperty(name); - ResourceMetadata metadata = new MappingResourceMetadata(mappingContext.getPersistentEntity(property)); - - when(mappings.getMappingFor(property.getActualType())).thenReturn(metadata); - - return property; - } - public static class Sample { @Reference Property property; @RestResource(exported = false) @Reference Property hiddenProperty; - @Reference UnexportedProperty unexportedProperty; } public static class Property { } - - public static class UnexportedProperty { - - } } diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceControllerIntegrationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceControllerIntegrationTests.java new file mode 100644 index 000000000..c8480e016 --- /dev/null +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceControllerIntegrationTests.java @@ -0,0 +1,63 @@ +/* + * Copyright 2015 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.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.rest.webmvc.jpa.Book; +import org.springframework.data.rest.webmvc.jpa.JpaRepositoryConfig; +import org.springframework.data.rest.webmvc.jpa.TestDataPopulator; +import org.springframework.http.HttpStatus; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author Oliver Gierke + */ +@ContextConfiguration(classes = JpaRepositoryConfig.class) +@Transactional +public class RepositoryPropertyReferenceControllerIntegrationTests extends AbstractControllerIntegrationTests { + + @Autowired RepositoryPropertyReferenceController controller; + @Autowired TestDataPopulator populator; + + PersistentEntityResourceAssembler assembler; + RootResourceInformation information; + + @Before + public void setUp() { + + this.assembler = mock(PersistentEntityResourceAssembler.class); + this.information = getResourceInformation(Book.class); + this.populator.populateRepositories(); + } + + @Test + public void exposesResourceForCustomizedPropertyResourcePath() throws Exception { + assertThat(controller.followPropertyReference(information, 1L, "creators", assembler).getStatusCode(), + is(HttpStatus.OK)); + } + + @Test(expected = ResourceNotFoundException.class) + public void doesNotExposeOriginalPathIfPropertyResourcePathIsCustomized() throws Exception { + controller.followPropertyReference(information, 1L, "authors", assembler); + } +} diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/Book.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/Book.java index 9e29e1dc8..86bf1680b 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/Book.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/Book.java @@ -24,6 +24,8 @@ import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToMany; +import org.springframework.data.rest.core.annotation.RestResource; + /** * @author Oliver Gierke */ @@ -34,7 +36,7 @@ public class Book { public String isbn; @ManyToMany(cascade = { CascadeType.MERGE })// - public Set authors; + @RestResource(path = "creators") public Set authors; public String title; diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaWebTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaWebTests.java index b9ffd6966..93e0e1cd3 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaWebTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaWebTests.java @@ -120,7 +120,7 @@ public class JpaWebTests extends CommonWebTests { mvc.perform(get("/")). // andExpect(status().isOk()). // - andExpect(doesNotHaveLinkWithRel(mappings.getMappingFor(CreditCard.class).getRel())); + andExpect(doesNotHaveLinkWithRel(mappings.getMetadataFor(CreditCard.class).getRel())); } @Test diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/RepositoryTestsConfig.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/RepositoryTestsConfig.java index 72b63cf8d..1d792d93e 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/RepositoryTestsConfig.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/RepositoryTestsConfig.java @@ -94,7 +94,7 @@ public class RepositoryTestsConfig { @Bean public Module persistentEntityModule() { - return new PersistentEntityJackson2Module(new RepositoryResourceMappings(config(), repositories()), + return new PersistentEntityJackson2Module(new RepositoryResourceMappings(repositories(), persistentEntities()), persistentEntities(), config(), new UriToEntityConverter(persistentEntities(), defaultConversionService())); } diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/RepositoryLinkBuildUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/RepositoryLinkBuildUnitTests.java index 81963beba..e4be423d2 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/RepositoryLinkBuildUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/RepositoryLinkBuildUnitTests.java @@ -18,11 +18,13 @@ package org.springframework.data.rest.webmvc.support; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import java.util.Arrays; + import org.junit.Test; +import org.springframework.data.mapping.context.PersistentEntities; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.rest.core.mapping.MappingResourceMetadata; -import org.springframework.data.rest.core.mapping.ResourceMetadata; +import org.springframework.data.rest.core.mapping.PersistentEntitiesResourceMappings; +import org.springframework.data.rest.core.mapping.ResourceMappings; import org.springframework.data.rest.webmvc.BaseUri; import org.springframework.data.rest.webmvc.TestMvcClient; import org.springframework.data.rest.webmvc.mongodb.Profile; @@ -58,10 +60,11 @@ public class RepositoryLinkBuildUnitTests { private void assertRootUriFor(String baseUri, String expectedUri) { - MongoPersistentEntity entity = context.getPersistentEntity(Profile.class); - ResourceMetadata metadata = new MappingResourceMetadata(entity); + context.getPersistentEntity(Profile.class); + ResourceMappings mappings = new PersistentEntitiesResourceMappings(new PersistentEntities(Arrays.asList(context))); - RepositoryLinkBuilder builder = new RepositoryLinkBuilder(metadata, new BaseUri(baseUri)); + RepositoryLinkBuilder builder = new RepositoryLinkBuilder(mappings.getMetadataFor(Profile.class), new BaseUri( + baseUri)); Link link = builder.withSelfRel(); assertThat(link.getHref(), is(expectedUri));