diff --git a/src/integration/java/org/springframework/data/couchbase/repository/base/RepositoryBaseTest.java b/src/integration/java/org/springframework/data/couchbase/repository/base/RepositoryBaseTest.java new file mode 100644 index 00000000..8d3b6878 --- /dev/null +++ b/src/integration/java/org/springframework/data/couchbase/repository/base/RepositoryBaseTest.java @@ -0,0 +1,114 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.couchbase.repository.base; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.List; + +import com.couchbase.client.java.cluster.ClusterInfo; +import com.couchbase.client.java.util.features.CouchbaseFeature; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.annotation.Id; +import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration; +import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.couchbase.repository.base.impl.MyRepository; +import org.springframework.data.couchbase.repository.base.impl.MyRepositoryImpl; +import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * This tests custom implementation of base repository. + * + * @author Simon Baslé + */ +@SuppressWarnings("SpringJavaAutowiringInspection") +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class RepositoryBaseTest { + + private static CouchbaseOperations mockOpsA; + + @BeforeClass + public static void initMocks() { + ClusterInfo info = mock(ClusterInfo.class); + when(info.checkAvailable(any(CouchbaseFeature.class))).thenReturn(true); + + mockOpsA = mock(CouchbaseOperations.class); + when(mockOpsA.getCouchbaseClusterInfo()).thenReturn(info); + when(mockOpsA.exists(any(String.class))).thenReturn(true); + } + + @Autowired + ItemRepository repositoryA; + + public interface ItemRepository extends MyRepository { + // + } + + @Configuration + @EnableCouchbaseRepositories(considerNestedRepositories = true, repositoryBaseClass = MyRepositoryImpl.class) + static class Config extends AbstractCouchbaseConfiguration { + + @Override + protected List getBootstrapHosts() { + return Arrays.asList("127.0.0.1"); + } + + @Override + protected String getBucketName() { + return "default"; + } + + @Override + protected String getBucketPassword() { + return ""; + } + + @Bean + public CouchbaseOperations couchbaseOperations() { + return mockOpsA; + } + } + + @Test + public void testRepositoryBaseIsChanged() { + assertNotNull(repositoryA); + + assertEquals(4, repositoryA.sharedCustomMethod("toto")); + assertEquals(4000, repositoryA.sharedCustomMethod("anna")); + } + + private static class Item { + @Id + public String id; + + public String value; + } + +} diff --git a/src/integration/java/org/springframework/data/couchbase/repository/base/impl/MyRepository.java b/src/integration/java/org/springframework/data/couchbase/repository/base/impl/MyRepository.java new file mode 100644 index 00000000..fabf896d --- /dev/null +++ b/src/integration/java/org/springframework/data/couchbase/repository/base/impl/MyRepository.java @@ -0,0 +1,12 @@ +package org.springframework.data.couchbase.repository.base.impl; + +import java.io.Serializable; + +import org.springframework.data.couchbase.repository.CouchbaseRepository; +import org.springframework.data.repository.NoRepositoryBean; + +@NoRepositoryBean +public interface MyRepository extends CouchbaseRepository { + + int sharedCustomMethod(ID id); +} diff --git a/src/integration/java/org/springframework/data/couchbase/repository/base/impl/MyRepositoryImpl.java b/src/integration/java/org/springframework/data/couchbase/repository/base/impl/MyRepositoryImpl.java new file mode 100644 index 00000000..91eb3d5d --- /dev/null +++ b/src/integration/java/org/springframework/data/couchbase/repository/base/impl/MyRepositoryImpl.java @@ -0,0 +1,24 @@ +package org.springframework.data.couchbase.repository.base.impl; + +import java.io.Serializable; + +import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation; +import org.springframework.data.couchbase.repository.support.N1qlCouchbaseRepository; + +public class MyRepositoryImpl + extends N1qlCouchbaseRepository + implements MyRepository { + + public MyRepositoryImpl(CouchbaseEntityInformation metadata, CouchbaseOperations couchbaseOperations) { + super(metadata, couchbaseOperations); + } + + @Override + public int sharedCustomMethod(ID id) { + String key = String.valueOf(id); + if (key.startsWith("a")) + return key.length() * 1000; + return key.length(); + } +} diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactory.java b/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactory.java index 4cdc8be1..2096eb3c 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactory.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactory.java @@ -31,6 +31,7 @@ import org.springframework.data.couchbase.core.query.N1qlSecondaryIndexed; import org.springframework.data.couchbase.core.query.Query; import org.springframework.data.couchbase.core.query.View; import org.springframework.data.couchbase.core.query.ViewIndexed; +import org.springframework.data.couchbase.repository.CouchbaseRepository; import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping; import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation; import org.springframework.data.couchbase.repository.query.CouchbaseQueryMethod; @@ -118,14 +119,18 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport { } /** - * Returns a new Repository based on the metadata. + * Returns a new Repository based on the metadata. Two categories of repositories can be instantiated: + * {@link SimpleCouchbaseRepository} and {@link N1qlCouchbaseRepository}. + * + * This method performs feature checks to decide which of the two categories can be instantiated (eg. is N1QL available?). + * Instantiation is done via reflection, see {@link #getRepositoryBaseClass(RepositoryMetadata)}. * * @param metadata the repository metadata. * * @return a new created repository. */ @Override - protected Object getTargetRepository(final RepositoryInformation metadata) { + protected final Object getTargetRepository(final RepositoryInformation metadata) { CouchbaseOperations couchbaseOperations = couchbaseOperationsMapping.resolve(metadata.getRepositoryInterface(), metadata.getDomainType()); boolean isN1qlAvailable = couchbaseOperations.getCouchbaseClusterInfo().checkAvailable(CouchbaseFeature.N1QL); @@ -138,16 +143,9 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport { indexManager.buildIndexes(metadata, viewIndexed, n1qlPrimaryIndexed, n1qlSecondaryIndexed, couchbaseOperations); CouchbaseEntityInformation entityInformation = getEntityInformation(metadata.getDomainType()); - if (isN1qlAvailable) { - //this implementation also conforms to PagingAndSortingRepository - N1qlCouchbaseRepository n1qlRepository = new N1qlCouchbaseRepository(entityInformation, couchbaseOperations); - n1qlRepository.setViewMetadataProvider(viewPostProcessor.getViewMetadataProvider()); - return n1qlRepository; - } else { - final SimpleCouchbaseRepository simpleCouchbaseRepository = new SimpleCouchbaseRepository(entityInformation, couchbaseOperations); - simpleCouchbaseRepository.setViewMetadataProvider(viewPostProcessor.getViewMetadataProvider()); - return simpleCouchbaseRepository; - } + SimpleCouchbaseRepository repo = getTargetRepositoryViaReflection(metadata, entityInformation, couchbaseOperations); + repo.setViewMetadataProvider(viewPostProcessor.getViewMetadataProvider()); + return repo; } private void checkFeatures(RepositoryInformation metadata, boolean isN1qlAvailable, @@ -175,20 +173,33 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport { } /** - * The base class for this repository. + * Returns the base class for the repository being constructed. Two categories of repositories can be produced by + * this factory: {@link SimpleCouchbaseRepository} and {@link N1qlCouchbaseRepository}. This method checks if N1QL + * is available to choose between the two, but the actual concrete class is determined respectively by + * {@link #getSimpleBaseClass(RepositoryMetadata)} and {@link #getN1qlBaseClass(RepositoryMetadata)}. + * + * Override these methods if you want to change the base class for all your repositories. * * @param repositoryMetadata metadata for the repository. * * @return the base class. */ @Override - protected Class getRepositoryBaseClass(final RepositoryMetadata repositoryMetadata) { + protected final Class getRepositoryBaseClass(final RepositoryMetadata repositoryMetadata) { CouchbaseOperations couchbaseOperations = couchbaseOperationsMapping.resolve(repositoryMetadata.getRepositoryInterface(), repositoryMetadata.getDomainType()); boolean isN1qlAvailable = couchbaseOperations.getCouchbaseClusterInfo().checkAvailable(CouchbaseFeature.N1QL); if (isN1qlAvailable) { - return N1qlCouchbaseRepository.class; + return getN1qlBaseClass(repositoryMetadata); } + return getSimpleBaseClass(repositoryMetadata); + } + + protected Class getN1qlBaseClass(final RepositoryMetadata repositoryMetadata) { + return N1qlCouchbaseRepository.class; + } + + protected Class getSimpleBaseClass(final RepositoryMetadata repositoryMetadata) { return SimpleCouchbaseRepository.class; } diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactoryBean.java b/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactoryBean.java index ec79e6ad..72c29fd9 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactoryBean.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactoryBean.java @@ -83,7 +83,7 @@ public class CouchbaseRepositoryFactoryBean, S, ID e * @param indexManager the reference to the {@link IndexManager}. * @return the factory instance. */ - private RepositoryFactorySupport getFactoryInstance(final RepositoryOperationsMapping operationsMapping, + protected CouchbaseRepositoryFactory getFactoryInstance(final RepositoryOperationsMapping operationsMapping, IndexManager indexManager) { return new CouchbaseRepositoryFactory(operationsMapping, indexManager); }