From e157f380bb2109b7a6fe45a909c495b67e7b6ed3 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Sun, 27 Feb 2011 18:33:34 +0100 Subject: [PATCH] DATADOC-34 - Infrastructure for callbacks on RepositoryQuery creation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduced QueryCreationListener that allows plugging in functionality after a query was created. Heavily refactored QueryMethod and RepositoryQuery subsystem. Splitted up simple EntityMetadata (domain class and potentially other stuff) from the more advanced methods like getId(Object) and isNew(…). QueryMethod now carries a type enum that allows switching over it to determine the execution. Beyond that QueryMethod now returns a EntityMetadata rather than a plain class. RepositoryQuery in turn exposes the query method. PartTree allows iterating over all contained Parts as well now. Introduced AbstractEntityInformation that regards an entity as new if its id is null. Simplified handling of RepositoryProxyPostProcessors and aligned them to QueryCreationListener handling. --- .../org/springframework/data/domain/Sort.java | 18 +++ .../data/repository/query/QueryMethod.java | 61 +++++++--- .../repository/query/RepositoryQuery.java | 10 ++ .../repository/query/parser/PartTree.java | 20 +++ ...ta.java => AbstractEntityInformation.java} | 12 +- .../repository/support/EntityInformation.java | 42 +++++++ .../repository/support/EntityMetadata.java | 18 --- ...java => PersistableEntityInformation.java} | 8 +- .../support/QueryCreationListener.java | 35 ++++++ .../support/RepositoryFactoryBeanSupport.java | 20 --- .../support/RepositoryFactorySupport.java | 47 ++++++- ...sactionalRepositoryFactoryBeanSupport.java | 29 +++-- ...> AbstractEntityInformationUnitTests.java} | 16 +-- ...ersistableEntityInformationUnitTests.java} | 10 +- .../RepositoryFactorySupportUnitTests.java | 115 ++++++++++++++++++ 15 files changed, 367 insertions(+), 94 deletions(-) rename spring-data-commons-core/src/main/java/org/springframework/data/repository/support/{AbstractEntityMetadata.java => AbstractEntityInformation.java} (77%) create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/repository/support/EntityInformation.java rename spring-data-commons-core/src/main/java/org/springframework/data/repository/support/{PersistableEntityMetadata.java => PersistableEntityInformation.java} (86%) create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/repository/support/QueryCreationListener.java rename spring-data-commons-core/src/test/java/org/springframework/data/repository/support/{AbstractEntityMetadataUnitTests.java => AbstractEntityInformationUnitTests.java} (76%) rename spring-data-commons-core/src/test/java/org/springframework/data/repository/support/{PersistableEntityMetadataUnitTests.java => PersistableEntityInformationUnitTests.java} (86%) create mode 100644 spring-data-commons-core/src/test/java/org/springframework/data/repository/support/RepositoryFactorySupportUnitTests.java diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/domain/Sort.java b/spring-data-commons-core/src/main/java/org/springframework/data/domain/Sort.java index 8d733568f..d300a618a 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/domain/Sort.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/domain/Sort.java @@ -111,6 +111,24 @@ public class Sort implements } + /** + * Returns the order registered for the given property. + * + * @param property + * @return + */ + public Order getOrderFor(String property) { + + for (Order order : this) { + if (order.getProperty().equals(property)) { + return order; + } + } + + return null; + } + + /* * (non-Javadoc) * diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/QueryMethod.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/QueryMethod.java index 80fa9241b..f05f5e720 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/QueryMethod.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/QueryMethod.java @@ -23,6 +23,8 @@ import java.util.List; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.data.repository.support.EntityMetadata; +import org.springframework.data.repository.util.ClassUtils; import org.springframework.util.Assert; @@ -35,6 +37,11 @@ import org.springframework.util.Assert; */ public class QueryMethod { + public static enum Type { + + SINGLE_ENTITY, PAGING, COLLECTION, MODIFYING; + } + private final Method method; private final Parameters parameters; @@ -82,27 +89,21 @@ public class QueryMethod { } - /** - * Returns whether the given - * - * @param number - * @return - */ - public boolean isCorrectNumberOfParameters(int number) { + public EntityMetadata getEntityMetadata() { - return number == parameters.getBindableParameters() - .getNumberOfParameters(); + return new EntityMetadata() { + + public Class getJavaType() { + + return getDomainClass(); + } + }; } - /** - * Returns the domain class for this method. - * - * @return - */ - public Class getDomainClass() { + protected Class getDomainClass() { - return getReturnedDomainClass(method); + return ClassUtils.getReturnedDomainClass(method); } @@ -112,7 +113,7 @@ public class QueryMethod { * * @return */ - public boolean isCollectionQuery() { + protected boolean isCollectionQuery() { Class returnType = method.getReturnType(); return org.springframework.util.ClassUtils.isAssignable(List.class, @@ -125,7 +126,7 @@ public class QueryMethod { * * @return */ - public boolean isPageQuery() { + protected boolean isPageQuery() { Class returnType = method.getReturnType(); return org.springframework.util.ClassUtils.isAssignable(Page.class, @@ -133,6 +134,30 @@ public class QueryMethod { } + public Type getType() { + + if (isModifyingQuery()) { + return Type.MODIFYING; + } + + if (isPageQuery()) { + return Type.PAGING; + } + + if (isCollectionQuery()) { + return Type.COLLECTION; + } + + return Type.SINGLE_ENTITY; + } + + + protected boolean isModifyingQuery() { + + return false; + } + + /** * Returns the {@link Parameters} wrapper to gain additional information * about {@link Method} parameters. diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/RepositoryQuery.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/RepositoryQuery.java index 860750b24..69bf0f0f1 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/RepositoryQuery.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/RepositoryQuery.java @@ -15,6 +15,8 @@ */ package org.springframework.data.repository.query; + + /** * Interface for a query abstraction. * @@ -30,4 +32,12 @@ public interface RepositoryQuery { * @return */ public Object execute(Object[] parameters); + + + /** + * Returns the + * + * @return + */ + public QueryMethod getQueryMethod(); } \ No newline at end of file diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/PartTree.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/PartTree.java index 826799ac5..a9f644c67 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/PartTree.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/query/parser/PartTree.java @@ -125,6 +125,26 @@ public class PartTree implements Iterable { } + /** + * Returns an {@link Iterable} of all parts contained in the + * {@link PartTree}. + * + * @return + */ + public Iterable getParts() { + + List result = new ArrayList(); + + for (OrPart orPart : this) { + for (Part part : orPart) { + result.add(part); + } + } + + return result; + } + + /** * Splits the given text at the given keywords. Expects camelcase style to * only match concrete keywords and not derivatives of it. diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/AbstractEntityMetadata.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/AbstractEntityInformation.java similarity index 77% rename from spring-data-commons-core/src/main/java/org/springframework/data/repository/support/AbstractEntityMetadata.java rename to spring-data-commons-core/src/main/java/org/springframework/data/repository/support/AbstractEntityInformation.java index 43f73433b..fd715e23e 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/AbstractEntityMetadata.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/AbstractEntityInformation.java @@ -19,22 +19,24 @@ import org.springframework.util.Assert; /** - * Base class for implementations of {@link EntityMetadata}. Considers an entity - * to be new whenever {@link #getId(Object)} returns {@literal null}. + * Base class for implementations of {@link EntityInformation}. Considers an + * entity to be new whenever {@link #getId(Object)} returns {@literal null}. * * @author Oliver Gierke */ -public abstract class AbstractEntityMetadata implements EntityMetadata { +public abstract class AbstractEntityInformation implements + EntityInformation { private final Class domainClass; /** - * Creates a new {@link AbstractEntityMetadata} from the given domain class. + * Creates a new {@link AbstractEntityInformation} from the given domain + * class. * * @param domainClass */ - public AbstractEntityMetadata(Class domainClass) { + public AbstractEntityInformation(Class domainClass) { Assert.notNull(domainClass); this.domainClass = domainClass; diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/EntityInformation.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/EntityInformation.java new file mode 100644 index 000000000..33797d33f --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/EntityInformation.java @@ -0,0 +1,42 @@ +/* + * Copyright 2011 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; + +/** + * Extension of {@link EntityMetadata} to add functionality to query information + * of entity instances. + * + * @author Oliver Gierke + */ +public interface EntityInformation extends EntityMetadata { + + /** + * Returns whether the given entity is considered to be new. + * + * @param entity must never be {@literal null} + * @return + */ + boolean isNew(T entity); + + + /** + * Returns the id of the given entity. + * + * @param entity must never be {@literal null} + * @return + */ + Object getId(T entity); +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/EntityMetadata.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/EntityMetadata.java index e62845cd6..ffc93e070 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/EntityMetadata.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/EntityMetadata.java @@ -22,24 +22,6 @@ package org.springframework.data.repository.support; */ public interface EntityMetadata { - /** - * Returns whether the given entity is considered to be new. - * - * @param entity must never be {@literal null} - * @return - */ - boolean isNew(T entity); - - - /** - * Returns the id of the given entity. - * - * @param entity must never be {@literal null} - * @return - */ - Object getId(T entity); - - /** * Returns the actual domain class type. * diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/PersistableEntityMetadata.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/PersistableEntityInformation.java similarity index 86% rename from spring-data-commons-core/src/main/java/org/springframework/data/repository/support/PersistableEntityMetadata.java rename to spring-data-commons-core/src/main/java/org/springframework/data/repository/support/PersistableEntityInformation.java index bd2c16313..ffb873a39 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/PersistableEntityMetadata.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/PersistableEntityInformation.java @@ -26,15 +26,15 @@ import org.springframework.data.domain.Persistable; * @author Oliver Gierke */ @SuppressWarnings("rawtypes") -public class PersistableEntityMetadata extends - AbstractEntityMetadata { +public class PersistableEntityInformation extends + AbstractEntityInformation { /** - * Creates a new {@link PersistableEntityMetadata}. + * Creates a new {@link PersistableEntityInformation}. * * @param domainClass */ - public PersistableEntityMetadata(Class domainClass) { + public PersistableEntityInformation(Class domainClass) { super(domainClass); } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/QueryCreationListener.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/QueryCreationListener.java new file mode 100644 index 000000000..b704dfa78 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/QueryCreationListener.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011 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 org.springframework.data.repository.query.RepositoryQuery; + + +/** + * Callback for listeners that want to execute functionality on + * {@link RepositoryQuery} creation. + * + * @author Oliver Gierke + */ +public interface QueryCreationListener { + + /** + * Will be invoked just after the {@link RepositoryQuery} was created. + * + * @param query + */ + void onCreation(T query); +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/RepositoryFactoryBeanSupport.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/RepositoryFactoryBeanSupport.java index 0ce0d5b05..00ab4b49b 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/RepositoryFactoryBeanSupport.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/RepositoryFactoryBeanSupport.java @@ -15,9 +15,6 @@ */ package org.springframework.data.repository.support; -import java.util.Collections; -import java.util.List; - import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Required; @@ -124,23 +121,6 @@ public abstract class RepositoryFactoryBeanSupport> this.factory = createRepositoryFactory(); this.factory.setQueryLookupStrategyKey(queryLookupStrategyKey); - - for (RepositoryProxyPostProcessor processor : getRepositoryPostProcessors()) { - this.factory.addRepositoryProxyPostProcessor(processor); - } - } - - - /** - * Returns all {@link RepositoryProxyPostProcessor} to be added to the - * repository factory to be created. Default implementation will return an - * empty list. - * - * @return - */ - protected List getRepositoryPostProcessors() { - - return Collections.emptyList(); } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/RepositoryFactorySupport.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/RepositoryFactorySupport.java index 5fa125449..ecb978219 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/RepositoryFactorySupport.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/RepositoryFactorySupport.java @@ -26,6 +26,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.framework.ProxyFactory; +import org.springframework.core.GenericTypeResolver; import org.springframework.data.repository.Repository; import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.QueryLookupStrategy.Key; @@ -49,6 +50,8 @@ public abstract class RepositoryFactorySupport { private final List postProcessors = new ArrayList(); private QueryLookupStrategy.Key queryLookupStrategyKey; + private List> queryPostProcessors = + new ArrayList>(); /** @@ -62,6 +65,20 @@ public abstract class RepositoryFactorySupport { } + /** + * Adds a {@link QueryCreationListener} to the factory to plug in + * functionality triggered right after creation of {@link RepositoryQuery} + * instances. + * + * @param listener + */ + public void addQueryCreationListener(QueryCreationListener listener) { + + Assert.notNull(listener); + this.queryPostProcessors.add(listener); + } + + /** * Adds {@link RepositoryProxyPostProcessor}s to the factory to allow * manipulation of the {@link ProxyFactory} before the proxy gets created. @@ -71,7 +88,7 @@ public abstract class RepositoryFactorySupport { * * @param processor */ - protected void addRepositoryProxyPostProcessor( + public void addRepositoryProxyPostProcessor( RepositoryProxyPostProcessor processor) { Assert.notNull(processor); @@ -205,10 +222,10 @@ public abstract class RepositoryFactorySupport { * interface methods. */ public QueryExecuterMethodInterceptor( - RepositoryMetadata repositoryInterface, + RepositoryMetadata repositoryMetadata, Object customImplementation, Object target) { - this.metadata = repositoryInterface; + this.metadata = repositoryMetadata; this.customImplementation = customImplementation; this.target = target; @@ -216,10 +233,28 @@ public abstract class RepositoryFactorySupport { getQueryLookupStrategy(queryLookupStrategyKey); for (Method method : metadata.getQueryMethods()) { - queries.put( - method, + RepositoryQuery query = lookupStrategy.resolveQuery(method, - repositoryInterface.getDomainClass())); + repositoryMetadata.getDomainClass()); + invokeListeners(query, metadata); + queries.put(method, query); + } + } + + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void invokeListeners(RepositoryQuery query, + RepositoryMetadata metadata) { + + for (QueryCreationListener listener : queryPostProcessors) { + Class typeArgument = + GenericTypeResolver.resolveTypeArgument( + listener.getClass(), + QueryCreationListener.class); + if (typeArgument != null + && typeArgument.isAssignableFrom(query.getClass())) { + listener.onCreation(query); + } } } diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/TransactionalRepositoryFactoryBeanSupport.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/TransactionalRepositoryFactoryBeanSupport.java index 4f2df71b2..4a189163f 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/TransactionalRepositoryFactoryBeanSupport.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/support/TransactionalRepositoryFactoryBeanSupport.java @@ -15,9 +15,6 @@ */ package org.springframework.data.repository.support; -import java.util.Arrays; -import java.util.List; - import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.ListableBeanFactory; @@ -59,20 +56,32 @@ public abstract class TransactionalRepositoryFactoryBeanSupport getRepositoryPostProcessors() { + protected final RepositoryFactorySupport createRepositoryFactory() { - return Arrays.asList(txPostProcessor); + RepositoryFactorySupport factory = doCreateRepositoryFactory(); + factory.addRepositoryProxyPostProcessor(txPostProcessor); + return factory; } + /** + * Creates the actual {@link RepositoryFactorySupport} instance. + * + * @return + */ + protected abstract RepositoryFactorySupport doCreateRepositoryFactory(); + + /* * (non-Javadoc) * diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/AbstractEntityMetadataUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/AbstractEntityInformationUnitTests.java similarity index 76% rename from spring-data-commons-core/src/test/java/org/springframework/data/repository/support/AbstractEntityMetadataUnitTests.java rename to spring-data-commons-core/src/test/java/org/springframework/data/repository/support/AbstractEntityInformationUnitTests.java index c8afa7aba..9b114f0b2 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/AbstractEntityMetadataUnitTests.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/AbstractEntityInformationUnitTests.java @@ -22,32 +22,32 @@ import org.junit.Test; /** - * Unit tests for {@link AbstractEntityMetadata}. + * Unit tests for {@link AbstractEntityInformation}. * * @author Oliver Gierke */ -public class AbstractEntityMetadataUnitTests { +public class AbstractEntityInformationUnitTests { @Test(expected = IllegalArgumentException.class) public void rejectsNullDomainClass() throws Exception { - new DummyAbstractEntityMetadata(null); + new DummyAbstractEntityInformation(null); } @Test public void considersEntityNewIfGetIdReturnsNull() throws Exception { - EntityMetadata metadata = - new DummyAbstractEntityMetadata(Object.class); + EntityInformation metadata = + new DummyAbstractEntityInformation(Object.class); assertThat(metadata.isNew(null), is(true)); assertThat(metadata.isNew(new Object()), is(false)); } - private static class DummyAbstractEntityMetadata extends - AbstractEntityMetadata { + private static class DummyAbstractEntityInformation extends + AbstractEntityInformation { - public DummyAbstractEntityMetadata(Class domainClass) { + public DummyAbstractEntityInformation(Class domainClass) { super(domainClass); } diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/PersistableEntityMetadataUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/PersistableEntityInformationUnitTests.java similarity index 86% rename from spring-data-commons-core/src/test/java/org/springframework/data/repository/support/PersistableEntityMetadataUnitTests.java rename to spring-data-commons-core/src/test/java/org/springframework/data/repository/support/PersistableEntityInformationUnitTests.java index a376376d9..e14b917fd 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/PersistableEntityMetadataUnitTests.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/PersistableEntityInformationUnitTests.java @@ -32,11 +32,11 @@ import org.springframework.data.domain.Persistable; * @author Oliver Gierke */ @RunWith(MockitoJUnitRunner.class) -public class PersistableEntityMetadataUnitTests { +public class PersistableEntityInformationUnitTests { @SuppressWarnings("rawtypes") - static final PersistableEntityMetadata metadata = - new PersistableEntityMetadata(Persistable.class); + static final PersistableEntityInformation metadata = + new PersistableEntityInformation(Persistable.class); @Mock Persistable persistable; @@ -64,8 +64,8 @@ public class PersistableEntityMetadataUnitTests { @Test public void returnsGivenClassAsEntityType() throws Exception { - PersistableEntityMetadata info = - new PersistableEntityMetadata( + PersistableEntityInformation info = + new PersistableEntityInformation( PersistableEntity.class); assertEquals(PersistableEntity.class, info.getJavaType()); diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/RepositoryFactorySupportUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/RepositoryFactorySupportUnitTests.java new file mode 100644 index 000000000..8043fe941 --- /dev/null +++ b/spring-data-commons-core/src/test/java/org/springframework/data/repository/support/RepositoryFactorySupportUnitTests.java @@ -0,0 +1,115 @@ +/* + * Copyright 2011 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 static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import java.io.Serializable; +import java.lang.reflect.Method; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.repository.Repository; +import org.springframework.data.repository.query.QueryLookupStrategy; +import org.springframework.data.repository.query.QueryLookupStrategy.Key; +import org.springframework.data.repository.query.RepositoryQuery; + + +/** + * Unit tests for {@link RepositoryFactorySupport}. + * + * @author Oliver Gierke + */ +@RunWith(MockitoJUnitRunner.class) +public class RepositoryFactorySupportUnitTests { + + RepositoryFactorySupport factory = new DummyRepositoryFactory(); + + @Mock + MyQueryCreationListener listener; + @Mock + PlainQueryCreationListener otherListener; + + + @Test + public void invokesCustomQueryCreationListenerForSpecialRepositoryQueryOnly() + throws Exception { + + factory.addQueryCreationListener(listener); + factory.addQueryCreationListener(otherListener); + + factory.getRepository(ObjectRepository.class); + + verify(listener, times(1)).onCreation(any(MyRepositoryQuery.class)); + verify(otherListener, times(2)).onCreation(any(RepositoryQuery.class)); + + } + + class DummyRepositoryFactory extends RepositoryFactorySupport { + + @Override + protected Object getTargetRepository(RepositoryMetadata metadata) { + + return new Object(); + } + + + @Override + protected Class getRepositoryBaseClass(Class repositoryInterface) { + + return Object.class; + } + + + @Override + protected QueryLookupStrategy getQueryLookupStrategy(Key key) { + + MyRepositoryQuery queryOne = mock(MyRepositoryQuery.class); + RepositoryQuery queryTwo = mock(RepositoryQuery.class); + + QueryLookupStrategy strategy = mock(QueryLookupStrategy.class); + when(strategy.resolveQuery(any(Method.class), any(Class.class))) + .thenReturn(queryOne, queryTwo); + + return strategy; + } + } + + interface ObjectRepository extends Repository { + + Object findByClass(Class clazz); + + + Object findByFoo(); + } + + interface PlainQueryCreationListener extends + QueryCreationListener { + + } + + interface MyQueryCreationListener extends + QueryCreationListener { + + } + + interface MyRepositoryQuery extends RepositoryQuery { + + } +}