diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/DomainClassConverter.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/DomainClassConverter.java index c1d3a2d9d..c307c88f8 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/DomainClassConverter.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/DomainClassConverter.java @@ -16,13 +16,9 @@ package org.springframework.data.repository.support; import java.io.Serializable; -import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import java.util.Set; -import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.convert.ConversionService; @@ -31,7 +27,6 @@ import org.springframework.core.convert.converter.ConditionalGenericConverter; import org.springframework.core.convert.converter.ConverterRegistry; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.core.EntityInformation; -import org.springframework.data.repository.core.support.RepositoryFactoryInformation; /** * {@link org.springframework.core.convert.converter.Converter} to convert arbitrary input into domain classes managed @@ -44,8 +39,8 @@ import org.springframework.data.repository.core.support.RepositoryFactoryInforma public class DomainClassConverter implements ConditionalGenericConverter, ApplicationContextAware { - private final Map, CrudRepository> repositories = new HashMap, CrudRepository>(); private final T conversionService; + private Repositories repositories = Repositories.NONE; public DomainClassConverter(T conversionService) { this.conversionService = conversionService; @@ -65,9 +60,9 @@ public class DomainClassConverter info = getRepositoryForDomainType(targetType.getType()); + EntityInformation info = repositories.getEntityInformationFor(targetType.getType()); - CrudRepository repository = repositories.get(info); + CrudRepository repository = repositories.getRepositoryFor(info); Serializable id = conversionService.convert(source, info.getIdType()); return repository.findOne(id); } @@ -78,47 +73,21 @@ public class DomainClassConverter info = getRepositoryForDomainType(targetType.getType()); - - if (info == null) { + if (!repositories.hasRepositoryFor(targetType.getType())) { return false; } - return conversionService.canConvert(sourceType.getType(), info.getIdType()); - } - - private EntityInformation getRepositoryForDomainType(Class domainType) { - - for (EntityInformation information : repositories.keySet()) { - - if (domainType.equals(information.getJavaType())) { - return information; - } - } - - return null; + return conversionService.canConvert(sourceType.getType(), repositories + .getEntityInformationFor(targetType.getType()).getIdType()); } /* * (non-Javadoc) * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) */ - @SuppressWarnings({ "unchecked", "rawtypes" }) public void setApplicationContext(ApplicationContext context) { - Collection providers = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, - RepositoryFactoryInformation.class).values(); - - for (RepositoryFactoryInformation entry : providers) { - - EntityInformation metadata = entry.getEntityInformation(); - Class> objectType = entry.getRepositoryInterface(); - CrudRepository repository = BeanFactoryUtils.beanOfTypeIncludingAncestors(context, - objectType); - - this.repositories.put(metadata, repository); - } - + this.repositories = new Repositories(context); this.conversionService.addConverter(this); } } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/DomainClassPropertyEditorRegistrar.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/DomainClassPropertyEditorRegistrar.java index 290a08692..19faa6dc9 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/DomainClassPropertyEditorRegistrar.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/DomainClassPropertyEditorRegistrar.java @@ -16,19 +16,14 @@ package org.springframework.data.repository.support; import java.io.Serializable; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; import java.util.Map.Entry; import org.springframework.beans.PropertyEditorRegistrar; import org.springframework.beans.PropertyEditorRegistry; -import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.core.EntityInformation; -import org.springframework.data.repository.core.support.RepositoryFactoryInformation; /** * Simple helper class to use Hades DAOs to provide {@link java.beans.PropertyEditor}s for domain classes. To get this @@ -52,50 +47,31 @@ import org.springframework.data.repository.core.support.RepositoryFactoryInforma */ public class DomainClassPropertyEditorRegistrar implements PropertyEditorRegistrar, ApplicationContextAware { - private final Map, CrudRepository> repositories = new HashMap, CrudRepository>(); + private Repositories repositories = Repositories.NONE; /* - * (non-Javadoc) - * - * @see - * org.springframework.beans.PropertyEditorRegistrar#registerCustomEditors - * (org.springframework.beans.PropertyEditorRegistry) - */ + * (non-Javadoc) + * @see org.springframework.beans.PropertyEditorRegistrar#registerCustomEditors(org.springframework.beans.PropertyEditorRegistry) + */ public void registerCustomEditors(PropertyEditorRegistry registry) { - for (Entry, CrudRepository> entry : repositories - .entrySet()) { + for (Entry, CrudRepository> entry : repositories) { - EntityInformation metadata = entry.getKey(); + EntityInformation entityInformation = entry.getKey(); CrudRepository repository = entry.getValue(); DomainClassPropertyEditor editor = new DomainClassPropertyEditor( - repository, metadata, registry); + repository, entityInformation, registry); - registry.registerCustomEditor(metadata.getJavaType(), editor); + registry.registerCustomEditor(entityInformation.getJavaType(), editor); } } /* - * (non-Javadoc) - * - * @see - * org.springframework.context.ApplicationContextAware#setApplicationContext - * (org.springframework.context.ApplicationContext) - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) + * (non-Javadoc) + * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) + */ public void setApplicationContext(ApplicationContext context) { - - Collection providers = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, - RepositoryFactoryInformation.class).values(); - - for (RepositoryFactoryInformation information : providers) { - - EntityInformation metadata = information.getEntityInformation(); - Class> objectType = information.getRepositoryInterface(); - CrudRepository repository = BeanFactoryUtils.beanOfType(context, objectType); - - this.repositories.put(metadata, repository); - } + this.repositories = new Repositories(context); } } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/Repositories.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/Repositories.java new file mode 100644 index 000000000..5ae71023e --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/Repositories.java @@ -0,0 +1,138 @@ +/* + * Copyright 2012 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.repository.support; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.core.EntityInformation; +import org.springframework.data.repository.core.support.RepositoryFactoryInformation; +import org.springframework.util.Assert; + +/** + * Wrapper class to access repository instances obtained from a {@link ListableBeanFactory}. + * + * @author Oliver Gierke + */ +public class Repositories implements + Iterable, CrudRepository>> { + + static final Repositories NONE = new Repositories(); + + private final Map, CrudRepository> repositories = new HashMap, CrudRepository>(); + + /** + * Constructor to create the {@link #NONE} instance. + */ + private Repositories() { + + } + + /** + * Creates a new {@link Repositories} instance by looking up the repository instances and meta information from the + * given {@link ListableBeanFactory}. + * + * @param factory must not be {@literal null}. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Repositories(ListableBeanFactory factory) { + + Assert.notNull(factory); + + Collection providers = BeanFactoryUtils.beansOfTypeIncludingAncestors(factory, + RepositoryFactoryInformation.class).values(); + + for (RepositoryFactoryInformation entry : providers) { + + EntityInformation metadata = entry.getEntityInformation(); + Class repositoryInterface = entry.getRepositoryInterface(); + + if (CrudRepository.class.isAssignableFrom(repositoryInterface)) { + Class> objectType = repositoryInterface; + CrudRepository repository = BeanFactoryUtils.beanOfTypeIncludingAncestors(factory, + objectType); + + this.repositories.put(metadata, repository); + } + } + } + + /** + * Returns whether we have a repository instance registered to manage instances of the given domain class. + * + * @param domainClass must not be {@literal null}. + * @return + */ + public boolean hasRepositoryFor(Class domainClass) { + return repositories.containsKey(getEntityInformationFor(domainClass)); + } + + /** + * Returns the repository managing the given domain class. + * + * @param domainClass + * @return + */ + @SuppressWarnings("unchecked") + public CrudRepository getRepositoryFor(Class domainClass) { + return (CrudRepository) repositories.get(getEntityInformationFor(domainClass)); + } + + /** + * Returns the repository for the given {@link EntityInformation}. + * + * @param entityInformation + * @return the repository for the given {@link EntityInformation}. + */ + @SuppressWarnings("unchecked") + public CrudRepository getRepositoryFor(EntityInformation entityInformation) { + return (CrudRepository) repositories.get(entityInformation); + } + + /** + * Returns the {@link EntityInformation} for the given domain class. + * + * @param domainClass must not be {@literal null}. + * @return the {@link EntityInformation} for the given domain class or {@literal null} if no repository registered for + * this domain class. + */ + @SuppressWarnings("unchecked") + public EntityInformation getEntityInformationFor(Class domainClass) { + + for (EntityInformation information : repositories.keySet()) { + if (domainClass.equals(information.getJavaType())) { + return (EntityInformation) information; + } + } + + return null; + } + + /* + * (non-Javadoc) + * @see java.lang.Iterable#iterator() + */ + public Iterator, CrudRepository>> iterator() { + return repositories.entrySet().iterator(); + } +} diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/DomainClassPropertyEditorRegistrarUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/DomainClassPropertyEditorRegistrarUnitTests.java index 7c682b867..553ea1816 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/DomainClassPropertyEditorRegistrarUnitTests.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/DomainClassPropertyEditorRegistrarUnitTests.java @@ -16,8 +16,7 @@ package org.springframework.data.repository.support; import static org.hamcrest.Matchers.*; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import java.io.Serializable; @@ -101,7 +100,7 @@ public class DomainClassPropertyEditorRegistrarUnitTests { private Map getBeanAsMap(T bean) { Map beanMap = new HashMap(); - beanMap.put(bean.getClass().getName(), bean); + beanMap.put(bean.toString(), bean); return beanMap; } diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/RepositoriesUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/RepositoriesUnitTests.java new file mode 100644 index 000000000..6f14a0423 --- /dev/null +++ b/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/RepositoriesUnitTests.java @@ -0,0 +1,153 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except +import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; + 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.repository.support; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +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.context.ApplicationContext; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.Repository; +import org.springframework.data.repository.core.EntityInformation; +import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; +import org.springframework.data.repository.core.support.RepositoryFactoryInformation; + +/** + * Unit tests for {@link Repositories}. + * + * @author Oliver Gierke + */ +@RunWith(MockitoJUnitRunner.class) +public class RepositoriesUnitTests { + + @Mock + PersonRepository personRepository; + @Mock + AddressRepository addressRepository; + @Mock + ApplicationContext context; + + @Before + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void setUp() { + + Map factoryInformations = getBeanAsMap(new SampleRepoFactoryInformation(AddressRepository.class), + new SampleRepoFactoryInformation(PersonRepository.class)); + Map personRepositories = getBeanAsMap(personRepository); + Map addressRepositories = getBeanAsMap(addressRepository); + + when(context.getBeansOfType(RepositoryFactoryInformation.class)).thenReturn(factoryInformations); + when(context.getBeansOfType(PersonRepository.class)).thenReturn(personRepositories); + when(context.getBeansOfType(AddressRepository.class)).thenReturn(addressRepositories); + } + + @Test + public void considersCrudRepositoriesOnly() { + + Repositories repositories = new Repositories(context); + + assertThat(repositories.hasRepositoryFor(Person.class), is(true)); + assertThat(repositories.hasRepositoryFor(Address.class), is(false)); + } + + @Test + public void doesNotFindInformationForNonManagedDomainClass() { + Repositories repositories = new Repositories(context); + assertThat(repositories.hasRepositoryFor(String.class), is(false)); + assertThat(repositories.getRepositoryFor(String.class), is(nullValue())); + } + + @Test(expected = IllegalArgumentException.class) + public void rejectsNullBeanFactory() { + new Repositories(null); + } + + class Person { + + } + + class Address { + + } + + interface PersonRepository extends CrudRepository { + + } + + interface AddressRepository extends Repository { + + } + + static class SampleRepoFactoryInformation implements RepositoryFactoryInformation { + + private final RepositoryMetadata repositoryMetadata; + + public SampleRepoFactoryInformation(Class repositoryInterface) { + this.repositoryMetadata = new DefaultRepositoryMetadata(repositoryInterface); + } + + @SuppressWarnings({ "unchecked" }) + public EntityInformation getEntityInformation() { + + return new EntityInformation() { + + public Class getJavaType() { + return (Class) repositoryMetadata.getDomainClass(); + } + + public boolean isNew(T entity) { + return false; + } + + public S getId(T entity) { + return null; + } + + public Class getIdType() { + return (Class) repositoryMetadata.getIdClass(); + } + }; + } + + @SuppressWarnings("unchecked") + public Class> getRepositoryInterface() { + return (Class>) repositoryMetadata.getRepositoryInterface(); + } + } + + private static Map getBeanAsMap(T... beans) { + + Map beanMap = new HashMap(); + + for (T bean : beans) { + beanMap.put(bean.toString(), bean); + } + return beanMap; + } +}