From 1c175ef59b32ba9e730fee2ca98e7d51739425b6 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Tue, 31 Mar 2015 16:30:39 +0200 Subject: [PATCH] DATACMNS-673 - Enhanced RepositoryMetadata to allow returning alternative domain types. In case a RepositoryMetadata implementation alters the domain type the repository is actually handling, we still have to be able to find the repository based on the type originally declared in the repository. The newly introduced getAlternativeDomainTypes() is now used by Repositories to register a repository for all types returned by that method, too, so that this reverse lookup still works. --- .../repository/core/RepositoryMetadata.java | 16 +++- .../support/AbstractRepositoryMetadata.java | 11 +++ .../support/DefaultRepositoryInformation.java | 11 ++- .../data/repository/support/Repositories.java | 30 ++++++-- .../support/DummyRepositoryInformation.java | 11 ++- .../support/RepositoriesUnitTests.java | 76 ++++++++++++++++++- 6 files changed, 143 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/springframework/data/repository/core/RepositoryMetadata.java b/src/main/java/org/springframework/data/repository/core/RepositoryMetadata.java index 3b283340b..1d2c72d07 100644 --- a/src/main/java/org/springframework/data/repository/core/RepositoryMetadata.java +++ b/src/main/java/org/springframework/data/repository/core/RepositoryMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-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. @@ -18,6 +18,9 @@ package org.springframework.data.repository.core; import java.io.Serializable; import java.lang.reflect.Method; import java.util.Collection; +import java.util.Set; + +import org.springframework.data.repository.support.Repositories; /** * Metadata for repository interfaces. @@ -69,4 +72,15 @@ public interface RepositoryMetadata { * @return */ boolean isPagingRepository(); + + /** + * Returns the set of types the repository shall be discoverable for when trying to look up a repository by domain + * type. + * + * @see Repositories#getRepositoryFor(Class) + * @return the set of types the repository shall be discoverable for when trying to look up a repository by domain + * type, must not be {@literal null}. + * @since 1.11 + */ + Set> getAlternativeDomainTypes(); } diff --git a/src/main/java/org/springframework/data/repository/core/support/AbstractRepositoryMetadata.java b/src/main/java/org/springframework/data/repository/core/support/AbstractRepositoryMetadata.java index 46d32815d..190dc129d 100644 --- a/src/main/java/org/springframework/data/repository/core/support/AbstractRepositoryMetadata.java +++ b/src/main/java/org/springframework/data/repository/core/support/AbstractRepositoryMetadata.java @@ -17,6 +17,8 @@ package org.springframework.data.repository.core.support; import java.lang.reflect.Method; import java.util.Arrays; +import java.util.Collections; +import java.util.Set; import org.springframework.data.domain.Pageable; import org.springframework.data.repository.Repository; @@ -115,6 +117,15 @@ public abstract class AbstractRepositoryMetadata implements RepositoryMetadata { return Arrays.asList(findAllMethod.getParameterTypes()).contains(Pageable.class); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.RepositoryMetadata#getAlternativeDomainTypes() + */ + @Override + public Set> getAlternativeDomainTypes() { + return Collections.emptySet(); + } + /** * Recursively unwraps well known wrapper types from the given {@link TypeInformation}. * diff --git a/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java b/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java index c06fa6561..26f35da51 100644 --- a/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java +++ b/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-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. @@ -315,6 +315,15 @@ class DefaultRepositoryInformation implements RepositoryInformation { return metadata.isPagingRepository(); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.RepositoryMetadata#getAlternativeDomainTypes() + */ + @Override + public Set> getAlternativeDomainTypes() { + return metadata.getAlternativeDomainTypes(); + } + /** * Checks the given method's parameters to match the ones of the given base class method. Matches generic arguments * agains the ones bound in the given repository interface. diff --git a/src/main/java/org/springframework/data/repository/support/Repositories.java b/src/main/java/org/springframework/data/repository/support/Repositories.java index 199a52ace..3ed318879 100644 --- a/src/main/java/org/springframework/data/repository/support/Repositories.java +++ b/src/main/java/org/springframework/data/repository/support/Repositories.java @@ -18,9 +18,11 @@ package org.springframework.data.repository.support; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryUtils; @@ -79,19 +81,33 @@ public class Repositories implements Iterable> { populateRepositoryFactoryInformation(factory); } - @SuppressWarnings({ "rawtypes", "unchecked" }) private void populateRepositoryFactoryInformation(ListableBeanFactory factory) { for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(factory, RepositoryFactoryInformation.class, false, false)) { + cacheRepositoryFactory(name); + } + } - RepositoryFactoryInformation repositoryFactoryInformation = beanFactory.getBean(name, - RepositoryFactoryInformation.class); - Class userDomainType = ClassUtils.getUserClass(repositoryFactoryInformation.getRepositoryInformation() - .getDomainType()); + @SuppressWarnings({ "rawtypes", "unchecked" }) + private synchronized void cacheRepositoryFactory(String name) { - this.repositoryFactoryInfos.put(userDomainType, repositoryFactoryInformation); - this.repositoryBeanNames.put(userDomainType, BeanFactoryUtils.transformedBeanName(name)); + RepositoryFactoryInformation repositoryFactoryInformation = beanFactory.getBean(name, + RepositoryFactoryInformation.class); + Class domainType = ClassUtils.getUserClass(repositoryFactoryInformation.getRepositoryInformation() + .getDomainType()); + + RepositoryInformation information = repositoryFactoryInformation.getRepositoryInformation(); + Set> alternativeDomainTypes = information.getAlternativeDomainTypes(); + String beanName = BeanFactoryUtils.transformedBeanName(name); + + Set> typesToRegister = new HashSet>(alternativeDomainTypes.size() + 1); + typesToRegister.add(domainType); + typesToRegister.addAll(alternativeDomainTypes); + + for (Class type : typesToRegister) { + this.repositoryFactoryInfos.put(type, repositoryFactoryInformation); + this.repositoryBeanNames.put(type, beanName); } } diff --git a/src/test/java/org/springframework/data/repository/core/support/DummyRepositoryInformation.java b/src/test/java/org/springframework/data/repository/core/support/DummyRepositoryInformation.java index a2d497a1a..bea708e63 100644 --- a/src/test/java/org/springframework/data/repository/core/support/DummyRepositoryInformation.java +++ b/src/test/java/org/springframework/data/repository/core/support/DummyRepositoryInformation.java @@ -29,7 +29,11 @@ public final class DummyRepositoryInformation implements RepositoryInformation { private final RepositoryMetadata metadata; public DummyRepositoryInformation(Class repositoryInterface) { - this.metadata = new DefaultRepositoryMetadata(repositoryInterface); + this(new DefaultRepositoryMetadata(repositoryInterface)); + } + + public DummyRepositoryInformation(RepositoryMetadata metadata) { + this.metadata = metadata; } public Class getIdType() { @@ -84,4 +88,9 @@ public final class DummyRepositoryInformation implements RepositoryInformation { public boolean isPagingRepository() { return false; } + + @Override + public Set> getAlternativeDomainTypes() { + return metadata.getAlternativeDomainTypes(); + } } diff --git a/src/test/java/org/springframework/data/repository/support/RepositoriesUnitTests.java b/src/test/java/org/springframework/data/repository/support/RepositoriesUnitTests.java index 1e8af754d..643fd630c 100644 --- a/src/test/java/org/springframework/data/repository/support/RepositoriesUnitTests.java +++ b/src/test/java/org/springframework/data/repository/support/RepositoriesUnitTests.java @@ -23,6 +23,7 @@ import static org.junit.Assert.*; import java.io.Serializable; import java.util.Collections; import java.util.List; +import java.util.Set; import org.junit.Before; import org.junit.Test; @@ -45,6 +46,7 @@ import org.springframework.data.repository.core.support.DummyRepositoryFactoryBe import org.springframework.data.repository.core.support.DummyRepositoryInformation; import org.springframework.data.repository.core.support.RepositoryFactoryInformation; import org.springframework.data.repository.query.QueryMethod; +import org.springframework.util.ClassUtils; /** * Unit tests for {@link Repositories}. @@ -116,6 +118,27 @@ public class RepositoriesUnitTests { assertThat(new Repositories(context).getPersistentEntity(AdvancedAddress.class), is(notNullValue())); } + /** + * @see DATACMNS-673 + */ + @Test + public void discoversRepositoryForAlternativeDomainType() { + + RepositoryMetadata metadata = new CustomRepositoryMetadata(SampleRepository.class); + RepositoryFactoryInformation information = new SampleRepoFactoryInformation(metadata); + + GenericApplicationContext context = new GenericApplicationContext(); + context.getBeanFactory().registerSingleton("foo", information); + context.refresh(); + + Repositories repositories = new Repositories(context); + + assertThat(repositories.getRepositoryFor(Sample.class), is(notNullValue())); + assertThat(repositories.getRepositoryFor(SampleEntity.class), is(notNullValue())); + + context.close(); + } + class Person {} class Address {} @@ -132,7 +155,11 @@ public class RepositoriesUnitTests { private final SampleMappingContext mappingContext; public SampleRepoFactoryInformation(Class repositoryInterface) { - this.repositoryMetadata = new DefaultRepositoryMetadata(repositoryInterface); + this(new DefaultRepositoryMetadata(repositoryInterface)); + } + + public SampleRepoFactoryInformation(RepositoryMetadata metadata) { + this.repositoryMetadata = metadata; this.mappingContext = new SampleMappingContext(); } @@ -142,7 +169,7 @@ public class RepositoriesUnitTests { } public RepositoryInformation getRepositoryInformation() { - return new DummyRepositoryInformation(repositoryMetadata.getRepositoryInterface()); + return new DummyRepositoryInformation(repositoryMetadata); } public PersistentEntity getPersistentEntity() { @@ -153,4 +180,49 @@ public class RepositoriesUnitTests { return Collections.emptyList(); } } + + static class CustomRepositoryMetadata extends DefaultRepositoryMetadata { + + private final Class domainType; + + /** + * @param repositoryInterface + */ + public CustomRepositoryMetadata(Class repositoryInterface) { + + super(repositoryInterface); + + String domainType = super.getDomainType().getName().concat("Entity"); + + try { + this.domainType = ClassUtils.forName(domainType, CustomRepositoryMetadata.class.getClassLoader()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.support.DefaultRepositoryMetadata#getDomainType() + */ + @Override + public Class getDomainType() { + return this.domainType; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.support.AbstractRepositoryMetadata#getAlternativeDomainTypes() + */ + @Override + public Set> getAlternativeDomainTypes() { + return Collections.> singleton(super.getDomainType()); + } + } + + interface Sample {} + + static class SampleEntity implements Sample {} + + interface SampleRepository extends Repository {} }