This is an automated email from the ASF dual-hosted git repository. alexpl pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ignite-extensions.git
The following commit(s) were added to refs/heads/master by this push: new 52aac6f IGNITE-13722 Adds correct closing of Spring Data Ignite resources - Fixes #31. 52aac6f is described below commit 52aac6fc94f7228e7fa0b3bd643809f768672707 Author: Mikhail Petrov <pmgheap....@gmail.com> AuthorDate: Mon Dec 14 18:17:10 2020 +0300 IGNITE-13722 Adds correct closing of Spring Data Ignite resources - Fixes #31. Signed-off-by: Aleksey Plekhanov <plehanov.a...@gmail.com> --- .../IgniteRepositoryConfigurationExtension.java | 31 +++ .../repository/support/IgniteProxyFactory.java | 140 ++++++++++++ .../support/IgniteRepositoryFactory.java | 167 ++++----------- .../support/IgniteRepositoryFactoryBean.java | 2 +- .../repository/support/IgniteRepositoryImpl.java | 2 +- .../IgniteClientSpringDataCompoundKeyTest.java | 6 + .../IgniteClientSpringDataCrudSelfTest.java | 16 -- .../IgniteSpringDataCompoundKeyTest.java | 7 +- ...gniteSpringDataConnectionConfigurationTest.java | 236 +++++++++++++++++++++ .../misc/IgniteClientApplicationConfiguration.java | 16 +- .../testsuites/IgniteSpringData2TestSuite.java | 2 + .../config/EnableIgniteRepositories.java | 1 - .../IgniteRepositoryConfigurationExtension.java | 31 +++ .../repository/support/IgniteProxyFactory.java | 140 ++++++++++++ .../support/IgniteRepositoryFactory.java | 167 ++++----------- .../support/IgniteRepositoryFactoryBean.java | 2 +- .../repository/support/IgniteRepositoryImpl.java | 2 +- .../IgniteClientSpringDataCompoundKeyTest.java | 6 + .../IgniteClientSpringDataCrudSelfTest.java | 16 -- .../IgniteSpringDataCompoundKeyTest.java | 7 +- ...gniteSpringDataConnectionConfigurationTest.java | 236 +++++++++++++++++++++ .../misc/IgniteClientApplicationConfiguration.java | 16 +- .../testsuites/IgniteSpringData22TestSuite.java | 2 + .../proxy/ClosableIgniteClientProxy.java} | 24 ++- .../springdata/proxy/ClosableIgniteProxyImpl.java} | 24 ++- .../ignite/springdata/proxy/IgniteCacheProxy.java | 2 +- .../ignite/springdata/proxy/IgniteClientProxy.java | 19 +- .../ignite/springdata/proxy/IgniteProxy.java | 36 +++- .../ignite/springdata/proxy/IgniteProxyImpl.java | 24 ++- .../IgniteRepositoryConfigurationExtension.java | 16 ++ .../repository/support/IgniteProxyFactory.java | 87 ++++++++ .../support/IgniteRepositoryFactory.java | 88 ++------ .../support/IgniteRepositoryFactoryBean.java | 57 +---- .../IgniteClientSpringDataCrudSelfTest.java | 31 --- ...gniteSpringDataConnectionConfigurationTest.java | 217 +++++++++++++++++++ .../misc/IgniteClientApplicationConfiguration.java | 3 + .../testsuites/IgniteSpringDataTestSuite.java | 2 + 37 files changed, 1396 insertions(+), 485 deletions(-) diff --git a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/IgniteRepositoryConfigurationExtension.java b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/IgniteRepositoryConfigurationExtension.java index 354e35b..de8cf0b 100644 --- a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/IgniteRepositoryConfigurationExtension.java +++ b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/config/IgniteRepositoryConfigurationExtension.java @@ -18,15 +18,28 @@ package org.apache.ignite.springdata20.repository.config; import java.util.Collection; import java.util.Collections; +import org.apache.ignite.springdata.proxy.IgniteProxy; import org.apache.ignite.springdata20.repository.IgniteRepository; +import org.apache.ignite.springdata20.repository.support.IgniteProxyFactory; import org.apache.ignite.springdata20.repository.support.IgniteRepositoryFactoryBean; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.data.repository.config.RepositoryConfigurationExtension; import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport; +import org.springframework.data.repository.config.RepositoryConfigurationSource; + +import static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE; /** * Apache Ignite specific implementation of {@link RepositoryConfigurationExtension}. */ public class IgniteRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport { + /** Name of the auto-registered Ignite proxy factory bean. */ + private static final String IGNITE_PROXY_FACTORY_BEAN_NAME = "igniteProxyFactory"; + + /** Name of the auto-registered Ignite proxy bean prototype. */ + private static final String IGNITE_PROXY_BEAN_NAME = "igniteProxy"; + /** {@inheritDoc} */ @Override public String getModuleName() { return "Apache Ignite"; @@ -46,4 +59,22 @@ public class IgniteRepositoryConfigurationExtension extends RepositoryConfigurat @Override protected Collection<Class<?>> getIdentifyingTypes() { return Collections.singleton(IgniteRepository.class); } + + /** {@inheritDoc} */ + @Override public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource cfg) { + registerIfNotAlreadyRegistered( + BeanDefinitionBuilder.genericBeanDefinition(IgniteProxyFactory.class).getBeanDefinition(), + registry, + IGNITE_PROXY_FACTORY_BEAN_NAME, + cfg); + + registerIfNotAlreadyRegistered( + BeanDefinitionBuilder.genericBeanDefinition(IgniteProxy.class) + .setScope(SCOPE_PROTOTYPE) + .setFactoryMethodOnBean("igniteProxy", IGNITE_PROXY_FACTORY_BEAN_NAME) + .getBeanDefinition(), + registry, + IGNITE_PROXY_BEAN_NAME, + cfg); + } } diff --git a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteProxyFactory.java b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteProxyFactory.java new file mode 100644 index 0000000..d571cc2 --- /dev/null +++ b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteProxyFactory.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.ignite.springdata20.repository.support; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.ignite.springdata.proxy.IgniteProxy; +import org.apache.ignite.springdata20.repository.config.RepositoryConfig; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.config.BeanExpressionContext; +import org.springframework.beans.factory.config.BeanExpressionResolver; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.expression.StandardBeanExpressionResolver; + +import static org.apache.ignite.springdata20.repository.support.IgniteRepositoryFactory.getRepositoryConfiguration; + +/** + * Represents factory for obtaining instances of {@link IgniteProxy} that provide client-independent connection to the + * Ignite cluster. + */ +public class IgniteProxyFactory implements ApplicationContextAware, DisposableBean { + /** Spring application expression resolver. */ + private final BeanExpressionResolver expressionResolver = new StandardBeanExpressionResolver(); + + /** Repositories associated with Ignite proxy. */ + private final Map<Class<?>, IgniteProxy> igniteProxies = new ConcurrentHashMap<>(); + + /** Spring application context. */ + private ApplicationContext ctx; + + /** Spring application bean expression context. */ + private BeanExpressionContext beanExpressionCtx; + + /** + * @param repoInterface The repository interface class for which {@link IgniteProxy} will be created. + * @return {@link IgniteProxy} instance. + */ + public IgniteProxy igniteProxy(Class<?> repoInterface) { + return igniteProxies.computeIfAbsent(repoInterface, k -> createIgniteProxy(repoInterface)); + } + + /** {@inheritDoc} */ + @Override public void setApplicationContext(ApplicationContext ctx) throws BeansException { + this.ctx = ctx; + + beanExpressionCtx = new BeanExpressionContext( + new DefaultListableBeanFactory(ctx.getAutowireCapableBeanFactory()), + null); + } + + /** {@inheritDoc} */ + @Override public void destroy() throws Exception { + Set<IgniteProxy> proxies = new HashSet<>(igniteProxies.values()); + + Exception destroyE = null; + + for (IgniteProxy proxy : proxies) { + if (proxy instanceof AutoCloseable) { + try { + ((AutoCloseable)proxy).close(); + } + catch (Exception e) { + if (destroyE == null) + destroyE = e; + else + destroyE.addSuppressed(e); + } + } + } + + if (destroyE != null) + throw destroyE; + } + + /** + * Creates {@link IgniteProxy} to be used for providing access to the Ignite cluster for specified repository. + * + * @param repoInterface {@link Class} instance of the repository interface. + * @return Instance of {@link IgniteProxy} associated with specified repository. + * + * @see RepositoryConfig + */ + private IgniteProxy createIgniteProxy(Class<?> repoInterface) { + RepositoryConfig repoCfg = getRepositoryConfiguration(repoInterface); + + Object connCfg; + + try { + connCfg = ctx.getBean(evaluateExpression(repoCfg.igniteInstance())); + } + catch (BeansException ex) { + try { + connCfg = ctx.getBean(evaluateExpression(repoCfg.igniteCfg())); + } + catch (BeansException ex2) { + try { + connCfg = ctx.getBean(evaluateExpression(repoCfg.igniteSpringCfgPath()), String.class); + } + catch (BeansException ex3) { + throw new IllegalArgumentException("Invalid configuration for repository " + + repoInterface.getName() + ". No beans were found that provide connection configuration to the" + + " Ignite cluster. Check \"igniteInstance\", \"igniteCfg\", \"igniteSpringCfgPath\" parameters" + + " of " + RepositoryConfig.class.getName() + " repository annotation."); + } + } + } + + return IgniteProxy.of(connCfg); + } + + /** + * Evaluates the SpEL expression. + * + * @param spelExpression SpEL expression + * @return The result of evaluation of the SpEL expression. + */ + private String evaluateExpression(String spelExpression) { + return (String)expressionResolver.evaluate(spelExpression, beanExpressionCtx); + } +} diff --git a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryFactory.java b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryFactory.java index dacb3e4..2096277 100644 --- a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryFactory.java +++ b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryFactory.java @@ -16,27 +16,15 @@ */ package org.apache.ignite.springdata20.repository.support; -import java.util.HashMap; -import java.util.Map; import java.util.Optional; -import org.apache.ignite.Ignite; -import org.apache.ignite.IgniteException; -import org.apache.ignite.Ignition; -import org.apache.ignite.client.IgniteClient; -import org.apache.ignite.configuration.ClientConfiguration; -import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.springdata.proxy.IgniteCacheProxy; import org.apache.ignite.springdata.proxy.IgniteProxy; -import org.apache.ignite.springdata.proxy.IgniteProxyImpl; -import org.apache.ignite.springdata.proxy.IgniteClientProxy; -import org.apache.ignite.springdata20.repository.IgniteRepository; import org.apache.ignite.springdata20.repository.config.DynamicQueryConfig; import org.apache.ignite.springdata20.repository.config.Query; import org.apache.ignite.springdata20.repository.config.RepositoryConfig; import org.apache.ignite.springdata20.repository.query.IgniteQuery; import org.apache.ignite.springdata20.repository.query.IgniteQueryGenerator; import org.apache.ignite.springdata20.repository.query.IgniteRepositoryQuery; -import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanExpressionContext; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ApplicationContext; @@ -62,85 +50,44 @@ import org.springframework.util.StringUtils; * @author Manuel Núñez (manuel.nu...@hawkore.com) */ public class IgniteRepositoryFactory extends RepositoryFactorySupport { - /** Spring application context */ - private final ApplicationContext ctx; - - /** Spring application bean factory */ - private final DefaultListableBeanFactory beanFactory; - /** Spring application expression resolver */ private final StandardBeanExpressionResolver resolver = new StandardBeanExpressionResolver(); /** Spring application bean expression context */ private final BeanExpressionContext beanExpressionContext; - /** Mapping of a repository to a cache. */ - private final Map<Class<?>, String> repoToCache = new HashMap<>(); + /** Ignite cache proxy instance associated with the current repository. */ + private final IgniteCacheProxy<?, ?> cache; - /** Mapping of a repository to a ignite instance. */ - private final Map<Class<?>, IgniteProxy> repoToIgnite = new HashMap<>(); + /** Ignite proxy instance associated with the current repository. */ + private final IgniteProxy ignite; /** - * Creates the factory with initialized {@link Ignite} instance. - * - * @param ctx the ctx + * @param ctx Spring Application context. + * @param repoInterface Repository interface. */ - public IgniteRepositoryFactory(ApplicationContext ctx) { - this.ctx = ctx; - - beanFactory = new DefaultListableBeanFactory(ctx.getAutowireCapableBeanFactory()); - - beanExpressionContext = new BeanExpressionContext(beanFactory, null); - } + public IgniteRepositoryFactory(ApplicationContext ctx, Class<?> repoInterface) { + ignite = ctx.getBean(IgniteProxy.class, repoInterface); - /** */ - private IgniteProxy igniteForRepoConfig(RepositoryConfig config) { - try { - Object igniteInstanceBean = ctx.getBean(evaluateExpression(config.igniteInstance())); + beanExpressionContext = new BeanExpressionContext( + new DefaultListableBeanFactory(ctx.getAutowireCapableBeanFactory()), + null); - if (igniteInstanceBean instanceof Ignite) - return new IgniteProxyImpl((Ignite)igniteInstanceBean); - else if (igniteInstanceBean instanceof IgniteClient) - return new IgniteClientProxy((IgniteClient)igniteInstanceBean); + RepositoryConfig cfg = getRepositoryConfiguration(repoInterface); - throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to the" + - " \"igniteInstance\" property of repository configuration must be one of the following types: " + - Ignite.class.getName() + ", " + IgniteClient.class.getName()); - } - catch (BeansException ex) { - try { - Object igniteCfgBean = ctx.getBean(evaluateExpression(config.igniteCfg())); + String cacheName = evaluateExpression(cfg.cacheName()); - if (igniteCfgBean instanceof IgniteConfiguration) { - try { - // first try to attach to existing ignite instance - return new IgniteProxyImpl(Ignition.ignite(((IgniteConfiguration)igniteCfgBean).getIgniteInstanceName())); - } - catch (Exception ignored) { - // nop - } - return new IgniteProxyImpl(Ignition.start((IgniteConfiguration)igniteCfgBean)); - } - else if (igniteCfgBean instanceof ClientConfiguration) - return new IgniteClientProxy(Ignition.startClient((ClientConfiguration)igniteCfgBean)); + Assert.hasText(cacheName, "Invalid configuration for repository " + repoInterface.getName() + + ". Set a name of an Apache Ignite cache using " + RepositoryConfig.class.getName() + + " annotation to map this repository to the underlying cache."); - throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to" + - " the \"igniteCfg\" property of repository configuration must be one of the following types: [" + - IgniteConfiguration.class.getName() + ", " + ClientConfiguration.class.getName() + ']'); + cache = cfg.autoCreateCache() ? ignite.getOrCreateCache(cacheName) : ignite.cache(cacheName); - } - catch (BeansException ex2) { - try { - String igniteSpringCfgPath = evaluateExpression(config.igniteSpringCfgPath()); - String path = (String)ctx.getBean(igniteSpringCfgPath); - return new IgniteProxyImpl(Ignition.start(path)); - } - catch (BeansException ex3) { - throw new IgniteException("Failed to initialize Ignite repository factory. No beans required for" + - " repository configuration were found. Check \"igniteInstance\", \"igniteCfg\"," + - " \"igniteSpringCfgPath\" parameters of " + RepositoryConfig.class.getName() + "class."); - } - } + if (cache == null) { + throw new IllegalArgumentException( + "Cache '" + cacheName + "' not found for repository interface " + repoInterface.getName() + + ". Please, add a cache configuration to ignite configuration" + + " or pass autoCreateCache=true to " + RepositoryConfig.class.getName() + " annotation."); } } @@ -164,28 +111,6 @@ public class IgniteRepositoryFactory extends RepositoryFactorySupport { return IgniteRepositoryImpl.class; } - /** {@inheritDoc} */ - @Override protected synchronized RepositoryMetadata getRepositoryMetadata(Class<?> repoItf) { - Assert.notNull(repoItf, "Repository interface must be set."); - Assert.isAssignable(IgniteRepository.class, repoItf, "Repository must implement IgniteRepository interface."); - - RepositoryConfig annotation = repoItf.getAnnotation(RepositoryConfig.class); - - Assert.notNull(annotation, "Set a name of an Apache Ignite cache using @RepositoryConfig annotation to map " - + "this repository to the underlying cache."); - - Assert.hasText(annotation.cacheName(), "Set a name of an Apache Ignite cache using @RepositoryConfig " - + "annotation to map this repository to the underlying cache."); - - String cacheName = evaluateExpression(annotation.cacheName()); - - repoToCache.put(repoItf, cacheName); - - repoToIgnite.put(repoItf, igniteForRepoConfig(annotation)); - - return super.getRepositoryMetadata(repoItf); - } - /** * Evaluate the SpEL expression * @@ -196,33 +121,9 @@ public class IgniteRepositoryFactory extends RepositoryFactorySupport { return (String)resolver.evaluate(spelExpression, beanExpressionContext); } - /** Control underlying cache creation to avoid cache creation by mistake */ - private IgniteCacheProxy<?, ?> getRepositoryCache(Class<?> repoIf) { - IgniteProxy ignite = repoToIgnite.get(repoIf); - - RepositoryConfig config = repoIf.getAnnotation(RepositoryConfig.class); - - String cacheName = repoToCache.get(repoIf); - - IgniteCacheProxy<?, ?> c = config.autoCreateCache() ? ignite.getOrCreateCache(cacheName) : ignite.cache(cacheName); - - if (c == null) { - throw new IllegalStateException( - "Cache '" + cacheName + "' not found for repository interface " + repoIf.getName() - + ". Please, add a cache configuration to ignite configuration" - + " or pass autoCreateCache=true to org.apache.ignite.springdata20" - + ".repository.config.RepositoryConfig annotation."); - } - - return c; - } - /** {@inheritDoc} */ @Override protected Object getTargetRepository(RepositoryInformation metadata) { - IgniteProxy ignite = repoToIgnite.get(metadata.getRepositoryInterface()); - - return getTargetRepositoryViaReflection(metadata, ignite, - getRepositoryCache(metadata.getRepositoryInterface())); + return getTargetRepositoryViaReflection(metadata, ignite, cache); } /** {@inheritDoc} */ @@ -243,8 +144,7 @@ public class IgniteRepositoryFactory extends RepositoryFactorySupport { annotation.textQuery(), false, IgniteQueryGenerator.getOptions(mtd)) : null; if (key != QueryLookupStrategy.Key.CREATE) { - return new IgniteRepositoryQuery(metadata, query, mtd, factory, - getRepositoryCache(metadata.getRepositoryInterface()), + return new IgniteRepositoryQuery(metadata, query, mtd, factory, cache, annotatedIgniteQuery ? DynamicQueryConfig.fromQueryAnnotation(annotation) : null, evaluationContextProvider); } @@ -256,9 +156,8 @@ public class IgniteRepositoryFactory extends RepositoryFactorySupport { + ".config.Query annotation."); } - return new IgniteRepositoryQuery(metadata, IgniteQueryGenerator.generateSql(mtd, metadata), mtd, - factory, getRepositoryCache(metadata.getRepositoryInterface()), - DynamicQueryConfig.fromQueryAnnotation(annotation), evaluationContextProvider); + return new IgniteRepositoryQuery(metadata, IgniteQueryGenerator.generateSql(mtd, metadata), mtd, factory, + cache, DynamicQueryConfig.fromQueryAnnotation(annotation), evaluationContextProvider); }); } @@ -290,4 +189,18 @@ public class IgniteRepositoryFactory extends RepositoryFactorySupport { // insert qryUpperCase.matches("^\\s*INSERT\\b.*"); } + + /** + * @return Configuration of the specified repository. + * @throws IllegalArgumentException If no configuration is specified. + * @see RepositoryConfig + */ + static RepositoryConfig getRepositoryConfiguration(Class<?> repoInterface) { + RepositoryConfig cfg = repoInterface.getAnnotation(RepositoryConfig.class); + + Assert.notNull(cfg, "Invalid configuration for repository " + repoInterface.getName() + ". " + + RepositoryConfig.class.getName() + " annotation must be specified for each repository interface."); + + return cfg; + } } diff --git a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryFactoryBean.java b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryFactoryBean.java index 5b3d612..bce6dda 100644 --- a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryFactoryBean.java +++ b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryFactoryBean.java @@ -62,6 +62,6 @@ public class IgniteRepositoryFactoryBean<T extends Repository<V, K>, V, K extend /** {@inheritDoc} */ @Override protected RepositoryFactorySupport createRepositoryFactory() { - return new IgniteRepositoryFactory(ctx); + return new IgniteRepositoryFactory(ctx, getObjectType()); } } diff --git a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryImpl.java b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryImpl.java index b089c22..9410ec2 100644 --- a/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryImpl.java +++ b/modules/spring-data-2.0-ext/src/main/java/org/apache/ignite/springdata20/repository/support/IgniteRepositoryImpl.java @@ -49,7 +49,7 @@ import org.springframework.context.annotation.Conditional; */ @Conditional(ConditionFalse.class) public class IgniteRepositoryImpl<V, K extends Serializable> implements IgniteRepository<V, K> { - /** Error message indicating that operation is spported only if {@link Ignite} instance is used to access the cluster. */ + /** Error message indicating that operation is supported only if {@link Ignite} instance is used to access the cluster. */ private static final String UNSUPPORTED_ERR_MSG = "Current operation is supported only if Ignite node instance is" + " used to access the Ignite cluster. See " + RepositoryConfig.class.getName() + "#igniteInstance."; diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java index 6dcadb8..ec2e7ca 100644 --- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java +++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java @@ -17,6 +17,7 @@ package org.apache.ignite.springdata; +import org.apache.ignite.Ignite; import org.apache.ignite.springdata.compoundkey.CityRepository; import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -32,4 +33,9 @@ public class IgniteClientSpringDataCompoundKeyTest extends IgniteSpringDataCompo repo = ctx.getBean(CityRepository.class); } + + /** {@inheritDoc} */ + @Override protected Ignite ignite() { + return ctx.getBean("igniteServerNode", Ignite.class); + } } diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java index ae42434..226ee9b 100644 --- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java +++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java @@ -18,13 +18,9 @@ package org.apache.ignite.springdata; import org.apache.ignite.client.IgniteClient; -import org.apache.ignite.configuration.ClientConfiguration; import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration; import org.apache.ignite.springdata.misc.PersonRepository; -import org.apache.ignite.springdata.misc.IgniteClientConfigRepository; -import org.apache.ignite.springdata20.repository.support.IgniteRepositoryFactory; import org.apache.ignite.testframework.GridTestUtils; -import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** Tests Spring Data CRUD operation when thin client is used for accessing the Ignite cluster. */ @@ -47,16 +43,4 @@ public class IgniteClientSpringDataCrudSelfTest extends IgniteSpringDataCrudSelf " org.apache.ignite.springdata.misc.PersonRepository#textQueryByFirstNameWithProjectionNamedParameter" + " method configuration or use Ignite node instance to connect to the Ignite cluster."); } - - /** - * Tests repository configuration in case {@link ClientConfiguration} is used to provide access to Ignite cluster. - */ - @Test - public void testRepositoryWithClientConfiguration() { - IgniteRepositoryFactory factory = new IgniteRepositoryFactory(ctx); - - IgniteClientConfigRepository repo = factory.getRepository(IgniteClientConfigRepository.class); - - assertTrue(repo.count() > 0); - } } diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java index 06e0c28..3a65f29 100644 --- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java +++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java @@ -98,7 +98,7 @@ public class IgniteSpringDataCompoundKeyTest extends GridCommonAbstractTest { /** load data*/ public void loadData() throws Exception { - Ignite ignite = ctx.getBean(Ignite.class); + Ignite ignite = ignite(); if (ignite.cacheNames().contains(CACHE_NAME)) ignite.destroyCache(CACHE_NAME); @@ -124,4 +124,9 @@ public class IgniteSpringDataCompoundKeyTest extends GridCommonAbstractTest { assertEquals(AFG_COUNT, repo.findByCountryCode(AFG).size()); assertEquals(QUANDAHAR, repo.findById(QUANDAHAR_ID)); } + + /** */ + protected Ignite ignite() { + return ctx.getBean(Ignite.class); + } } diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataConnectionConfigurationTest.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataConnectionConfigurationTest.java new file mode 100644 index 0000000..4187e28 --- /dev/null +++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataConnectionConfigurationTest.java @@ -0,0 +1,236 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.ignite.springdata; + +import java.io.Serializable; +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.Ignition; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.ClientConfiguration; +import org.apache.ignite.configuration.ClientConnectorConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; +import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder; +import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder; +import org.apache.ignite.springdata20.repository.IgniteRepository; +import org.apache.ignite.springdata20.repository.config.EnableIgniteRepositories; +import org.apache.ignite.springdata20.repository.config.RepositoryConfig; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Configuration; + +import static org.apache.ignite.testframework.GridTestUtils.assertThrowsAnyCause; +import static org.springframework.context.annotation.FilterType.ASSIGNABLE_TYPE; + +/** Tests Spring Data repository cluster connection configurations. */ +public class IgniteSpringDataConnectionConfigurationTest extends GridCommonAbstractTest { + /** */ + private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true); + + /** */ + private static final String CACHE_NAME = "PersonCache"; + + /** */ + private static final int CLI_CONN_PORT = 10810; + + /** */ + private static final String CLI_NAME = "cli-node"; + + /** */ + private static final String SRV_NAME = "srv-node"; + + /** */ + private static final String LOCAL_HOST = "127.0.0.1"; + + /** Tests repository configuration in case {@link IgniteConfiguration} is used to access the Ignite cluster. */ + @Test + public void testRepositoryWithIgniteConfiguration() { + checkRepositoryConfiguration(IgniteConfigurationApplication.class, IgniteConfigRepository.class); + + assertClientNodeIsStopped(); + } + + /** Tests repository configuration in case {@link ClientConfiguration} is used to access the Ignite cluster. */ + @Test + public void testRepositoryWithClientConfiguration() { + checkRepositoryConfiguration(ClientConfigurationApplication.class, IgniteClientConfigRepository.class); + } + + /** + * Tests repository configuration in case {@link IgniteConfiguration} that refers to existing Ignite node instance + * used to access the Ignite cluster. + */ + @Test + public void testRepositoryWithExistingIgniteInstance() throws Exception { + try (Ignite ignored = startGrid(getIgniteConfiguration(CLI_NAME, true))) { + checkRepositoryConfiguration(IgniteConfigurationApplication.class, IgniteConfigRepository.class); + + assertNotNull(Ignition.ignite(CLI_NAME)); + } + } + + /** Tests repository configuration in case specified cache name is invalid. */ + @Test + public void testRepositoryWithInvalidCacheNameConfiguration() { + try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext()) { + ctx.register(InvalidCacheNameApplication.class); + + assertThrowsAnyCause(log, + () -> { + ctx.refresh(); + + return null; + }, + IllegalArgumentException.class, + "Cache 'invalidCache' not found for repository interface" + + " org.apache.ignite.springdata.IgniteSpringDataConnectionConfigurationTest$InvalidCacheNameRepository." + + " Please, add a cache configuration to ignite configuration or pass autoCreateCache=true to" + + " org.apache.ignite.springdata20.repository.config.RepositoryConfig annotation."); + } + + assertClientNodeIsStopped(); + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + super.afterTest(); + + grid(SRV_NAME).cache(CACHE_NAME).clear(); + } + + /** */ + private void assertClientNodeIsStopped() { + assertFalse(Ignition.allGrids().stream().map(Ignite::name).anyMatch(CLI_NAME::equals)); + } + + /** {@inheritDoc} */ + @Override protected void beforeTestsStarted() throws Exception { + super.beforeTestsStarted(); + + startGrid(getIgniteConfiguration(SRV_NAME, false)); + } + + /** + * Checks that repository created based on specified Spring application configuration is properly initialized and + * got access to the Ignite cluster. + */ + private void checkRepositoryConfiguration( + Class<?> cfgCls, + Class<? extends IgniteRepository<Object, Serializable>> repoCls + ) { + try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext()) { + ctx.register(cfgCls); + ctx.refresh(); + + IgniteRepository<Object, Serializable> repo = ctx.getBean(repoCls); + + IgniteCache<Object, Serializable> cache = grid(SRV_NAME).cache(CACHE_NAME); + + assertEquals(0, repo.count()); + assertEquals(0, cache.size()); + + int key = 0; + + repo.save(key, "1"); + + assertEquals(1, repo.count()); + assertNotNull(cache.get(key)); + } + } + + /** + * Spring Application configuration for repository testing in case {@link IgniteConfiguration} is used + * for accessing the cluster. + */ + @Configuration + @EnableIgniteRepositories( + considerNestedRepositories = true, + includeFilters = @Filter(type = ASSIGNABLE_TYPE, classes = InvalidCacheNameRepository.class)) + public static class InvalidCacheNameApplication { + /** Ignite configuration bean. */ + @Bean + public IgniteConfiguration igniteConfiguration() { + return getIgniteConfiguration(CLI_NAME, true); + } + } + + /** + * Spring Application configuration for repository testing in case {@link IgniteConfiguration} is used + * for accessing the cluster. + */ + @Configuration + @EnableIgniteRepositories( + considerNestedRepositories = true, + includeFilters = @Filter(type = ASSIGNABLE_TYPE, classes = IgniteConfigRepository.class)) + public static class IgniteConfigurationApplication { + /** Ignite configuration bean. */ + @Bean + public IgniteConfiguration igniteConfiguration() { + return getIgniteConfiguration(CLI_NAME, true); + } + } + + /** + * Spring Application configuration for repository testing in case {@link ClientConfiguration} is used + * for accessing the cluster. + */ + @Configuration + @EnableIgniteRepositories( + considerNestedRepositories = true, + includeFilters = @Filter(type = ASSIGNABLE_TYPE, classes = IgniteClientConfigRepository.class)) + public static class ClientConfigurationApplication { + /** Ignite client configuration bean. */ + @Bean + public ClientConfiguration clientConfiguration() { + return new ClientConfiguration().setAddresses(LOCAL_HOST + ':' + CLI_CONN_PORT); + } + } + + /** Repository for testing configuration approach through {@link IgniteConfiguration}. */ + @RepositoryConfig(cacheName = "PersonCache", igniteCfg = "igniteConfiguration") + public interface IgniteConfigRepository extends IgniteRepository<Object, Serializable> { + // No-op. + } + + /** Repository for testing repository configuration approach through {@link ClientConfiguration}. */ + @RepositoryConfig(cacheName = "PersonCache", igniteCfg = "clientConfiguration") + public interface IgniteClientConfigRepository extends IgniteRepository<Object, Serializable> { + // No-op. + } + + /** Repository for testing application behavior in case invalid cache is specified in the repository configuration. */ + @RepositoryConfig(cacheName = "invalidCache", igniteCfg = "igniteConfiguration") + public interface InvalidCacheNameRepository extends IgniteRepository<Object, Serializable> { + // No-op. + } + + /** */ + private static IgniteConfiguration getIgniteConfiguration(String name, boolean clientMode) { + return new IgniteConfiguration() + .setIgniteInstanceName(name) + .setClientMode(clientMode) + .setLocalHost(LOCAL_HOST) + .setDiscoverySpi(new TcpDiscoverySpi().setIpFinder(IP_FINDER)) + .setClientConnectorConfiguration(new ClientConnectorConfiguration().setPort(CLI_CONN_PORT)) + .setCacheConfiguration(new CacheConfiguration<>(CACHE_NAME)); + } +} diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java index 2122231..7b69075 100644 --- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java +++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java @@ -72,6 +72,12 @@ public class IgniteClientApplicationConfiguration { return Ignition.start(igniteConfiguration(IGNITE_INSTANCE_ONE, CLI_CONN_PORT)); } + /** */ + @Bean + public Ignite igniteSecondServerNode() { + return Ignition.start(igniteConfiguration(IGNITE_INSTANCE_TWO, 10801)); + } + /** Ignite client instance bean with default name. */ @Bean public IgniteClient igniteInstance() { @@ -82,18 +88,10 @@ public class IgniteClientApplicationConfiguration { /** Ignite client instance bean with non-default name. */ @Bean public IgniteClient igniteInstanceTWO() { - Ignition.start(igniteConfiguration(IGNITE_INSTANCE_TWO, 10801)); - return Ignition.startClient(new ClientConfiguration().setAddresses("127.0.0.1:10801")); } - /** Ignite client configuraition bean. */ - @Bean - public ClientConfiguration clientConfiguration() { - return new ClientConfiguration().setAddresses("127.0.0.1:" + CLI_CONN_PORT); - } - - /** Ingite configuration for server node. */ + /** */ private static IgniteConfiguration igniteConfiguration(String igniteInstanceName, int cliConnPort) { return new IgniteConfiguration() .setIgniteInstanceName(igniteInstanceName) diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData2TestSuite.java b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData2TestSuite.java index 6c26078..88879e2 100644 --- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData2TestSuite.java +++ b/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData2TestSuite.java @@ -21,6 +21,7 @@ import org.apache.ignite.springdata.IgniteClientSpringDataCompoundKeyTest; import org.apache.ignite.springdata.IgniteClientSpringDataCrudSelfTest; import org.apache.ignite.springdata.IgniteClientSpringDataQueriesSelfTest; import org.apache.ignite.springdata.IgniteSpringDataCompoundKeyTest; +import org.apache.ignite.springdata.IgniteSpringDataConnectionConfigurationTest; import org.apache.ignite.springdata.IgniteSpringDataCrudSelfExpressionTest; import org.apache.ignite.springdata.IgniteSpringDataCrudSelfTest; import org.apache.ignite.springdata.IgniteSpringDataQueriesSelfTest; @@ -36,6 +37,7 @@ import org.junit.runners.Suite; IgniteSpringDataQueriesSelfTest.class, IgniteSpringDataCrudSelfExpressionTest.class, IgniteSpringDataCompoundKeyTest.class, + IgniteSpringDataConnectionConfigurationTest.class, IgniteClientSpringDataCrudSelfTest.class, IgniteClientSpringDataQueriesSelfTest.class, IgniteClientSpringDataCompoundKeyTest.class diff --git a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/EnableIgniteRepositories.java b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/EnableIgniteRepositories.java index ad465c3..560ee2e 100644 --- a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/EnableIgniteRepositories.java +++ b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/EnableIgniteRepositories.java @@ -22,7 +22,6 @@ import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; - import org.apache.ignite.springdata22.repository.support.IgniteRepositoryFactoryBean; import org.apache.ignite.springdata22.repository.support.IgniteRepositoryImpl; import org.springframework.beans.factory.FactoryBean; diff --git a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/IgniteRepositoryConfigurationExtension.java b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/IgniteRepositoryConfigurationExtension.java index 4989196..cf6db05 100644 --- a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/IgniteRepositoryConfigurationExtension.java +++ b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/config/IgniteRepositoryConfigurationExtension.java @@ -18,15 +18,28 @@ package org.apache.ignite.springdata22.repository.config; import java.util.Collection; import java.util.Collections; +import org.apache.ignite.springdata.proxy.IgniteProxy; import org.apache.ignite.springdata22.repository.IgniteRepository; +import org.apache.ignite.springdata22.repository.support.IgniteProxyFactory; import org.apache.ignite.springdata22.repository.support.IgniteRepositoryFactoryBean; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.data.repository.config.RepositoryConfigurationExtension; import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport; +import org.springframework.data.repository.config.RepositoryConfigurationSource; + +import static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE; /** * Apache Ignite specific implementation of {@link RepositoryConfigurationExtension}. */ public class IgniteRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport { + /** Name of the auto-registered Ignite proxy factory bean. */ + private static final String IGNITE_PROXY_FACTORY_BEAN_NAME = "igniteProxyFactory"; + + /** Name of the auto-registered Ignite proxy bean prototype. */ + private static final String IGNITE_PROXY_BEAN_NAME = "igniteProxy"; + /** {@inheritDoc} */ @Override public String getModuleName() { return "Apache Ignite"; @@ -46,4 +59,22 @@ public class IgniteRepositoryConfigurationExtension extends RepositoryConfigurat @Override protected Collection<Class<?>> getIdentifyingTypes() { return Collections.singleton(IgniteRepository.class); } + + /** {@inheritDoc} */ + @Override public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource cfg) { + registerIfNotAlreadyRegistered( + () -> BeanDefinitionBuilder.genericBeanDefinition(IgniteProxyFactory.class).getBeanDefinition(), + registry, + IGNITE_PROXY_FACTORY_BEAN_NAME, + cfg); + + registerIfNotAlreadyRegistered( + () -> BeanDefinitionBuilder.genericBeanDefinition(IgniteProxy.class) + .setScope(SCOPE_PROTOTYPE) + .setFactoryMethodOnBean("igniteProxy", IGNITE_PROXY_FACTORY_BEAN_NAME) + .getBeanDefinition(), + registry, + IGNITE_PROXY_BEAN_NAME, + cfg); + } } diff --git a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteProxyFactory.java b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteProxyFactory.java new file mode 100644 index 0000000..ac56e00 --- /dev/null +++ b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteProxyFactory.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.ignite.springdata22.repository.support; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.ignite.springdata.proxy.IgniteProxy; +import org.apache.ignite.springdata22.repository.config.RepositoryConfig; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.config.BeanExpressionContext; +import org.springframework.beans.factory.config.BeanExpressionResolver; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.expression.StandardBeanExpressionResolver; + +import static org.apache.ignite.springdata22.repository.support.IgniteRepositoryFactory.getRepositoryConfiguration; + +/** + * Represents factory for obtaining instances of {@link IgniteProxy} that provide client-independent connection to the + * Ignite cluster. + */ +public class IgniteProxyFactory implements ApplicationContextAware, DisposableBean { + /** Spring application expression resolver. */ + private final BeanExpressionResolver expressionResolver = new StandardBeanExpressionResolver(); + + /** Repositories associated with Ignite proxy. */ + private final Map<Class<?>, IgniteProxy> igniteProxies = new ConcurrentHashMap<>(); + + /** Spring application context. */ + private ApplicationContext ctx; + + /** Spring application bean expression context. */ + private BeanExpressionContext beanExpressionCtx; + + /** + * @param repoInterface The repository interface class for which {@link IgniteProxy} will be created. + * @return {@link IgniteProxy} instance. + */ + public IgniteProxy igniteProxy(Class<?> repoInterface) { + return igniteProxies.computeIfAbsent(repoInterface, k -> createIgniteProxy(repoInterface)); + } + + /** {@inheritDoc} */ + @Override public void setApplicationContext(ApplicationContext ctx) throws BeansException { + this.ctx = ctx; + + beanExpressionCtx = new BeanExpressionContext( + new DefaultListableBeanFactory(ctx.getAutowireCapableBeanFactory()), + null); + } + + /** {@inheritDoc} */ + @Override public void destroy() throws Exception { + Set<IgniteProxy> proxies = new HashSet<>(igniteProxies.values()); + + Exception destroyE = null; + + for (IgniteProxy proxy : proxies) { + if (proxy instanceof AutoCloseable) { + try { + ((AutoCloseable)proxy).close(); + } + catch (Exception e) { + if (destroyE == null) + destroyE = e; + else + destroyE.addSuppressed(e); + } + } + } + + if (destroyE != null) + throw destroyE; + } + + /** + * Creates {@link IgniteProxy} to be used for providing access to the Ignite cluster for specified repository. + * + * @param repoInterface {@link Class} instance of the repository interface. + * @return Instance of {@link IgniteProxy} associated with specified repository. + * + * @see RepositoryConfig + */ + private IgniteProxy createIgniteProxy(Class<?> repoInterface) { + RepositoryConfig repoCfg = getRepositoryConfiguration(repoInterface); + + Object connCfg; + + try { + connCfg = ctx.getBean(evaluateExpression(repoCfg.igniteInstance())); + } + catch (BeansException ex) { + try { + connCfg = ctx.getBean(evaluateExpression(repoCfg.igniteCfg())); + } + catch (BeansException ex2) { + try { + connCfg = ctx.getBean(evaluateExpression(repoCfg.igniteSpringCfgPath()), String.class); + } + catch (BeansException ex3) { + throw new IllegalArgumentException("Invalid configuration for repository " + + repoInterface.getName() + ". No beans were found that provide connection configuration to the" + + " Ignite cluster. Check \"igniteInstance\", \"igniteCfg\", \"igniteSpringCfgPath\" parameters" + + " of " + RepositoryConfig.class.getName() + " repository annotation."); + } + } + } + + return IgniteProxy.of(connCfg); + } + + /** + * Evaluates the SpEL expression. + * + * @param spelExpression SpEL expression + * @return The result of evaluation of the SpEL expression. + */ + private String evaluateExpression(String spelExpression) { + return (String)expressionResolver.evaluate(spelExpression, beanExpressionCtx); + } +} diff --git a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryFactory.java b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryFactory.java index a9c7ad2..4494c14 100644 --- a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryFactory.java +++ b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryFactory.java @@ -16,27 +16,15 @@ */ package org.apache.ignite.springdata22.repository.support; -import java.util.HashMap; -import java.util.Map; import java.util.Optional; -import org.apache.ignite.Ignite; -import org.apache.ignite.IgniteException; -import org.apache.ignite.Ignition; -import org.apache.ignite.client.IgniteClient; -import org.apache.ignite.configuration.ClientConfiguration; -import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.springdata.proxy.IgniteCacheProxy; import org.apache.ignite.springdata.proxy.IgniteProxy; -import org.apache.ignite.springdata.proxy.IgniteProxyImpl; -import org.apache.ignite.springdata.proxy.IgniteClientProxy; -import org.apache.ignite.springdata22.repository.IgniteRepository; import org.apache.ignite.springdata22.repository.config.DynamicQueryConfig; import org.apache.ignite.springdata22.repository.config.Query; import org.apache.ignite.springdata22.repository.config.RepositoryConfig; import org.apache.ignite.springdata22.repository.query.IgniteQuery; import org.apache.ignite.springdata22.repository.query.IgniteQueryGenerator; import org.apache.ignite.springdata22.repository.query.IgniteRepositoryQuery; -import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanExpressionContext; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ApplicationContext; @@ -62,85 +50,44 @@ import org.springframework.util.StringUtils; * @author Manuel Núñez (manuel.nu...@hawkore.com) */ public class IgniteRepositoryFactory extends RepositoryFactorySupport { - /** Spring application context */ - private final ApplicationContext ctx; - - /** Spring application bean factory */ - private final DefaultListableBeanFactory beanFactory; - /** Spring application expression resolver */ private final StandardBeanExpressionResolver resolver = new StandardBeanExpressionResolver(); /** Spring application bean expression context */ private final BeanExpressionContext beanExpressionContext; - /** Mapping of a repository to a cache. */ - private final Map<Class<?>, String> repoToCache = new HashMap<>(); + /** Ignite cache proxy instance associated with the current repository. */ + private final IgniteCacheProxy<?, ?> cache; - /** Mapping of a repository to a ignite instance. */ - private final Map<Class<?>, IgniteProxy> repoToIgnite = new HashMap<>(); + /** Ignite proxy instance associated with the current repository. */ + private final IgniteProxy ignite; /** - * Creates the factory with initialized {@link Ignite} instance. - * - * @param ctx the ctx + * @param ctx Spring Application context. + * @param repoInterface Repository interface. */ - public IgniteRepositoryFactory(ApplicationContext ctx) { - this.ctx = ctx; - - beanFactory = new DefaultListableBeanFactory(ctx.getAutowireCapableBeanFactory()); - - beanExpressionContext = new BeanExpressionContext(beanFactory, null); - } + public IgniteRepositoryFactory(ApplicationContext ctx, Class<?> repoInterface) { + ignite = ctx.getBean(IgniteProxy.class, repoInterface); - /** */ - private IgniteProxy igniteForRepoConfig(RepositoryConfig config) { - try { - Object igniteInstanceBean = ctx.getBean(evaluateExpression(config.igniteInstance())); + beanExpressionContext = new BeanExpressionContext( + new DefaultListableBeanFactory(ctx.getAutowireCapableBeanFactory()), + null); - if (igniteInstanceBean instanceof Ignite) - return new IgniteProxyImpl((Ignite)igniteInstanceBean); - else if (igniteInstanceBean instanceof IgniteClient) - return new IgniteClientProxy((IgniteClient)igniteInstanceBean); + RepositoryConfig cfg = getRepositoryConfiguration(repoInterface); - throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to the" + - " \"igniteInstance\" property of repository configuration must be one of the following types: " + - Ignite.class.getName() + ", " + IgniteClient.class.getName()); - } - catch (BeansException ex) { - try { - Object igniteCfgBean = ctx.getBean(evaluateExpression(config.igniteCfg())); + String cacheName = evaluateExpression(cfg.cacheName()); - if (igniteCfgBean instanceof IgniteConfiguration) { - try { - // first try to attach to existing ignite instance - return new IgniteProxyImpl(Ignition.ignite(((IgniteConfiguration)igniteCfgBean).getIgniteInstanceName())); - } - catch (Exception ignored) { - // nop - } - return new IgniteProxyImpl(Ignition.start((IgniteConfiguration)igniteCfgBean)); - } - else if (igniteCfgBean instanceof ClientConfiguration) - return new IgniteClientProxy(Ignition.startClient((ClientConfiguration)igniteCfgBean)); + Assert.hasText(cacheName, "Invalid configuration for repository " + repoInterface.getName() + + ". Set a name of an Apache Ignite cache using " + RepositoryConfig.class.getName() + + " annotation to map this repository to the underlying cache."); - throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to" + - " the \"igniteCfg\" property of repository configuration must be one of the following types: [" + - IgniteConfiguration.class.getName() + ", " + ClientConfiguration.class.getName() + ']'); + cache = cfg.autoCreateCache() ? ignite.getOrCreateCache(cacheName) : ignite.cache(cacheName); - } - catch (BeansException ex2) { - try { - String igniteSpringCfgPath = evaluateExpression(config.igniteSpringCfgPath()); - String path = (String)ctx.getBean(igniteSpringCfgPath); - return new IgniteProxyImpl(Ignition.start(path)); - } - catch (BeansException ex3) { - throw new IgniteException("Failed to initialize Ignite repository factory. No beans required for" + - " repository configuration were found. Check \"igniteInstance\", \"igniteCfg\"," + - " \"igniteSpringCfgPath\" parameters of " + RepositoryConfig.class.getName() + "class."); - } - } + if (cache == null) { + throw new IllegalArgumentException( + "Cache '" + cacheName + "' not found for repository interface " + repoInterface.getName() + + ". Please, add a cache configuration to ignite configuration" + + " or pass autoCreateCache=true to " + RepositoryConfig.class.getName() + " annotation."); } } @@ -164,28 +111,6 @@ public class IgniteRepositoryFactory extends RepositoryFactorySupport { return IgniteRepositoryImpl.class; } - /** {@inheritDoc} */ - @Override protected synchronized RepositoryMetadata getRepositoryMetadata(Class<?> repoItf) { - Assert.notNull(repoItf, "Repository interface must be set."); - Assert.isAssignable(IgniteRepository.class, repoItf, "Repository must implement IgniteRepository interface."); - - RepositoryConfig annotation = repoItf.getAnnotation(RepositoryConfig.class); - - Assert.notNull(annotation, "Set a name of an Apache Ignite cache using @RepositoryConfig annotation to map " - + "this repository to the underlying cache."); - - Assert.hasText(annotation.cacheName(), "Set a name of an Apache Ignite cache using @RepositoryConfig " - + "annotation to map this repository to the underlying cache."); - - String cacheName = evaluateExpression(annotation.cacheName()); - - repoToCache.put(repoItf, cacheName); - - repoToIgnite.put(repoItf, igniteForRepoConfig(annotation)); - - return super.getRepositoryMetadata(repoItf); - } - /** * Evaluate the SpEL expression * @@ -196,33 +121,9 @@ public class IgniteRepositoryFactory extends RepositoryFactorySupport { return (String)resolver.evaluate(spelExpression, beanExpressionContext); } - /** Control underlying cache creation to avoid cache creation by mistake */ - private IgniteCacheProxy<?, ?> getRepositoryCache(Class<?> repoIf) { - IgniteProxy ignite = repoToIgnite.get(repoIf); - - RepositoryConfig config = repoIf.getAnnotation(RepositoryConfig.class); - - String cacheName = repoToCache.get(repoIf); - - IgniteCacheProxy<?, ?> c = config.autoCreateCache() ? ignite.getOrCreateCache(cacheName) : ignite.cache(cacheName); - - if (c == null) { - throw new IllegalStateException( - "Cache '" + cacheName + "' not found for repository interface " + repoIf.getName() - + ". Please, add a cache configuration to ignite configuration" - + " or pass autoCreateCache=true to org.apache.ignite.springdata22" - + ".repository.config.RepositoryConfig annotation."); - } - - return c; - } - /** {@inheritDoc} */ @Override protected Object getTargetRepository(RepositoryInformation metadata) { - IgniteProxy ignite = repoToIgnite.get(metadata.getRepositoryInterface()); - - return getTargetRepositoryViaReflection(metadata, ignite, - getRepositoryCache(metadata.getRepositoryInterface())); + return getTargetRepositoryViaReflection(metadata, ignite, cache); } /** {@inheritDoc} */ @@ -243,8 +144,7 @@ public class IgniteRepositoryFactory extends RepositoryFactorySupport { annotation.textQuery(), false, IgniteQueryGenerator.getOptions(mtd)) : null; if (key != QueryLookupStrategy.Key.CREATE) { - return new IgniteRepositoryQuery(metadata, query, mtd, factory, - getRepositoryCache(metadata.getRepositoryInterface()), + return new IgniteRepositoryQuery(metadata, query, mtd, factory, cache, annotatedIgniteQuery ? DynamicQueryConfig.fromQueryAnnotation(annotation) : null, evaluationContextProvider); } @@ -256,9 +156,8 @@ public class IgniteRepositoryFactory extends RepositoryFactorySupport { + ".config.Query annotation."); } - return new IgniteRepositoryQuery(metadata, IgniteQueryGenerator.generateSql(mtd, metadata), mtd, - factory, getRepositoryCache(metadata.getRepositoryInterface()), - DynamicQueryConfig.fromQueryAnnotation(annotation), evaluationContextProvider); + return new IgniteRepositoryQuery(metadata, IgniteQueryGenerator.generateSql(mtd, metadata), mtd, factory, + cache, DynamicQueryConfig.fromQueryAnnotation(annotation), evaluationContextProvider); }); } @@ -290,4 +189,18 @@ public class IgniteRepositoryFactory extends RepositoryFactorySupport { // insert qryUpperCase.matches("^\\s*INSERT\\b.*"); } + + /** + * @return Configuration of the specified repository. + * @throws IllegalArgumentException If no configuration is specified. + * @see RepositoryConfig + */ + static RepositoryConfig getRepositoryConfiguration(Class<?> repoInterface) { + RepositoryConfig cfg = repoInterface.getAnnotation(RepositoryConfig.class); + + Assert.notNull(cfg, "Invalid configuration for repository " + repoInterface.getName() + ". " + + RepositoryConfig.class.getName() + " annotation must be specified for each repository interface."); + + return cfg; + } } diff --git a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryFactoryBean.java b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryFactoryBean.java index ece9f31..9d6794d 100644 --- a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryFactoryBean.java +++ b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryFactoryBean.java @@ -62,6 +62,6 @@ public class IgniteRepositoryFactoryBean<T extends Repository<V, K>, V, K extend /** {@inheritDoc} */ @Override protected RepositoryFactorySupport createRepositoryFactory() { - return new IgniteRepositoryFactory(ctx); + return new IgniteRepositoryFactory(ctx, getObjectType()); } } diff --git a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryImpl.java b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryImpl.java index de0573b..d91da58 100644 --- a/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryImpl.java +++ b/modules/spring-data-2.2-ext/src/main/java/org/apache/ignite/springdata22/repository/support/IgniteRepositoryImpl.java @@ -49,7 +49,7 @@ import org.springframework.context.annotation.Conditional; */ @Conditional(ConditionFalse.class) public class IgniteRepositoryImpl<V, K extends Serializable> implements IgniteRepository<V, K> { - /** Error message indicating that operation is spported only if {@link Ignite} instance is used to access the cluster. */ + /** Error message indicating that operation is supported only if {@link Ignite} instance is used to access the cluster. */ private static final String UNSUPPORTED_ERR_MSG = "Current operation is supported only if Ignite node instance is" + " used to access the Ignite cluster. See " + RepositoryConfig.class.getName() + "#igniteInstance."; diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java index 6dcadb8..ec2e7ca 100644 --- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java +++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCompoundKeyTest.java @@ -17,6 +17,7 @@ package org.apache.ignite.springdata; +import org.apache.ignite.Ignite; import org.apache.ignite.springdata.compoundkey.CityRepository; import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -32,4 +33,9 @@ public class IgniteClientSpringDataCompoundKeyTest extends IgniteSpringDataCompo repo = ctx.getBean(CityRepository.class); } + + /** {@inheritDoc} */ + @Override protected Ignite ignite() { + return ctx.getBean("igniteServerNode", Ignite.class); + } } diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java index d1597d9..226ee9b 100644 --- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java +++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java @@ -18,13 +18,9 @@ package org.apache.ignite.springdata; import org.apache.ignite.client.IgniteClient; -import org.apache.ignite.configuration.ClientConfiguration; import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration; -import org.apache.ignite.springdata.misc.IgniteClientConfigRepository; import org.apache.ignite.springdata.misc.PersonRepository; -import org.apache.ignite.springdata22.repository.support.IgniteRepositoryFactory; import org.apache.ignite.testframework.GridTestUtils; -import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** Tests Spring Data CRUD operation when thin client is used for accessing the Ignite cluster. */ @@ -47,16 +43,4 @@ public class IgniteClientSpringDataCrudSelfTest extends IgniteSpringDataCrudSelf " org.apache.ignite.springdata.misc.PersonRepository#textQueryByFirstNameWithProjectionNamedParameter" + " method configuration or use Ignite node instance to connect to the Ignite cluster."); } - - /** - * Tests repository configuration in case {@link ClientConfiguration} is used to provide access to Ignite cluster. - */ - @Test - public void testRepositoryWithClientConfiguration() { - IgniteRepositoryFactory factory = new IgniteRepositoryFactory(ctx); - - IgniteClientConfigRepository repo = factory.getRepository(IgniteClientConfigRepository.class); - - assertTrue(repo.count() > 0); - } } diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java index 06e0c28..3a65f29 100644 --- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java +++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataCompoundKeyTest.java @@ -98,7 +98,7 @@ public class IgniteSpringDataCompoundKeyTest extends GridCommonAbstractTest { /** load data*/ public void loadData() throws Exception { - Ignite ignite = ctx.getBean(Ignite.class); + Ignite ignite = ignite(); if (ignite.cacheNames().contains(CACHE_NAME)) ignite.destroyCache(CACHE_NAME); @@ -124,4 +124,9 @@ public class IgniteSpringDataCompoundKeyTest extends GridCommonAbstractTest { assertEquals(AFG_COUNT, repo.findByCountryCode(AFG).size()); assertEquals(QUANDAHAR, repo.findById(QUANDAHAR_ID)); } + + /** */ + protected Ignite ignite() { + return ctx.getBean(Ignite.class); + } } diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataConnectionConfigurationTest.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataConnectionConfigurationTest.java new file mode 100644 index 0000000..68c421a --- /dev/null +++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataConnectionConfigurationTest.java @@ -0,0 +1,236 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.ignite.springdata; + +import java.io.Serializable; +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.Ignition; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.ClientConfiguration; +import org.apache.ignite.configuration.ClientConnectorConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; +import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder; +import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder; +import org.apache.ignite.springdata22.repository.IgniteRepository; +import org.apache.ignite.springdata22.repository.config.EnableIgniteRepositories; +import org.apache.ignite.springdata22.repository.config.RepositoryConfig; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Configuration; + +import static org.apache.ignite.testframework.GridTestUtils.assertThrowsAnyCause; +import static org.springframework.context.annotation.FilterType.ASSIGNABLE_TYPE; + +/** Tests Spring Data repository cluster connection configurations. */ +public class IgniteSpringDataConnectionConfigurationTest extends GridCommonAbstractTest { + /** */ + private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true); + + /** */ + private static final String CACHE_NAME = "PersonCache"; + + /** */ + private static final int CLI_CONN_PORT = 10810; + + /** */ + private static final String CLI_NAME = "cli-node"; + + /** */ + private static final String SRV_NAME = "srv-node"; + + /** */ + private static final String LOCAL_HOST = "127.0.0.1"; + + /** Tests repository configuration in case {@link IgniteConfiguration} is used to access the Ignite cluster. */ + @Test + public void testRepositoryWithIgniteConfiguration() { + checkRepositoryConfiguration(IgniteConfigurationApplication.class, IgniteConfigRepository.class); + + assertClientNodeIsStopped(); + } + + /** Tests repository configuration in case {@link ClientConfiguration} is used to access the Ignite cluster. */ + @Test + public void testRepositoryWithClientConfiguration() { + checkRepositoryConfiguration(ClientConfigurationApplication.class, IgniteClientConfigRepository.class); + } + + /** + * Tests repository configuration in case {@link IgniteConfiguration} that refers to existing Ignite node instance + * used to access the Ignite cluster. + */ + @Test + public void testRepositoryWithExistingIgniteInstance() throws Exception { + try (Ignite ignored = startGrid(getIgniteConfiguration(CLI_NAME, true))) { + checkRepositoryConfiguration(IgniteConfigurationApplication.class, IgniteConfigRepository.class); + + assertNotNull(Ignition.ignite(CLI_NAME)); + } + } + + /** Tests repository configuration in case specified cache name is invalid. */ + @Test + public void testRepositoryWithInvalidCacheNameConfiguration() { + try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext()) { + ctx.register(InvalidCacheNameApplication.class); + + assertThrowsAnyCause(log, + () -> { + ctx.refresh(); + + return null; + }, + IllegalArgumentException.class, + "Cache 'invalidCache' not found for repository interface" + + " org.apache.ignite.springdata.IgniteSpringDataConnectionConfigurationTest$InvalidCacheNameRepository." + + " Please, add a cache configuration to ignite configuration or pass autoCreateCache=true to" + + " org.apache.ignite.springdata22.repository.config.RepositoryConfig annotation."); + } + + assertClientNodeIsStopped(); + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + super.afterTest(); + + grid(SRV_NAME).cache(CACHE_NAME).clear(); + } + + /** */ + private void assertClientNodeIsStopped() { + assertFalse(Ignition.allGrids().stream().map(Ignite::name).anyMatch(CLI_NAME::equals)); + } + + /** {@inheritDoc} */ + @Override protected void beforeTestsStarted() throws Exception { + super.beforeTestsStarted(); + + startGrid(getIgniteConfiguration(SRV_NAME, false)); + } + + /** + * Checks that repository created based on specified Spring application configuration is properly initialized and + * got access to the Ignite cluster. + */ + private void checkRepositoryConfiguration( + Class<?> cfgCls, + Class<? extends IgniteRepository<Object, Serializable>> repoCls + ) { + try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext()) { + ctx.register(cfgCls); + ctx.refresh(); + + IgniteRepository<Object, Serializable> repo = ctx.getBean(repoCls); + + IgniteCache<Object, Serializable> cache = grid(SRV_NAME).cache(CACHE_NAME); + + assertEquals(0, repo.count()); + assertEquals(0, cache.size()); + + int key = 0; + + repo.save(key, "1"); + + assertEquals(1, repo.count()); + assertNotNull(cache.get(key)); + } + } + + /** + * Spring Application configuration for repository testing in case {@link IgniteConfiguration} is used + * for accessing the cluster. + */ + @Configuration + @EnableIgniteRepositories( + considerNestedRepositories = true, + includeFilters = @Filter(type = ASSIGNABLE_TYPE, classes = InvalidCacheNameRepository.class)) + public static class InvalidCacheNameApplication { + /** Ignite configuration bean. */ + @Bean + public IgniteConfiguration igniteConfiguration() { + return getIgniteConfiguration(CLI_NAME, true); + } + } + + /** + * Spring Application configuration for repository testing in case {@link IgniteConfiguration} is used + * for accessing the cluster. + */ + @Configuration + @EnableIgniteRepositories( + considerNestedRepositories = true, + includeFilters = @Filter(type = ASSIGNABLE_TYPE, classes = IgniteConfigRepository.class)) + public static class IgniteConfigurationApplication { + /** Ignite configuration bean. */ + @Bean + public IgniteConfiguration igniteConfiguration() { + return getIgniteConfiguration(CLI_NAME, true); + } + } + + /** + * Spring Application configuration for repository testing in case {@link ClientConfiguration} is used + * for accessing the cluster. + */ + @Configuration + @EnableIgniteRepositories( + considerNestedRepositories = true, + includeFilters = @Filter(type = ASSIGNABLE_TYPE, classes = IgniteClientConfigRepository.class)) + public static class ClientConfigurationApplication { + /** Ignite client configuration bean. */ + @Bean + public ClientConfiguration clientConfiguration() { + return new ClientConfiguration().setAddresses(LOCAL_HOST + ':' + CLI_CONN_PORT); + } + } + + /** Repository for testing configuration approach through {@link IgniteConfiguration}. */ + @RepositoryConfig(cacheName = "PersonCache", igniteCfg = "igniteConfiguration") + public interface IgniteConfigRepository extends IgniteRepository<Object, Serializable> { + // No-op. + } + + /** Repository for testing repository configuration approach through {@link ClientConfiguration}. */ + @RepositoryConfig(cacheName = "PersonCache", igniteCfg = "clientConfiguration") + public interface IgniteClientConfigRepository extends IgniteRepository<Object, Serializable> { + // No-op. + } + + /** Repository for testing application behavior in case invalid cache is specified in the repository configuration. */ + @RepositoryConfig(cacheName = "invalidCache", igniteCfg = "igniteConfiguration") + public interface InvalidCacheNameRepository extends IgniteRepository<Object, Serializable> { + // No-op. + } + + /** */ + private static IgniteConfiguration getIgniteConfiguration(String name, boolean clientMode) { + return new IgniteConfiguration() + .setIgniteInstanceName(name) + .setClientMode(clientMode) + .setLocalHost(LOCAL_HOST) + .setDiscoverySpi(new TcpDiscoverySpi().setIpFinder(IP_FINDER)) + .setClientConnectorConfiguration(new ClientConnectorConfiguration().setPort(CLI_CONN_PORT)) + .setCacheConfiguration(new CacheConfiguration<>(CACHE_NAME)); + } +} diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java index c5d159a..ad255d0 100644 --- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java +++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java @@ -72,6 +72,12 @@ public class IgniteClientApplicationConfiguration { return Ignition.start(igniteConfiguration(IGNITE_INSTANCE_ONE, CLI_CONN_PORT)); } + /** */ + @Bean + public Ignite igniteSecondServerNode() { + return Ignition.start(igniteConfiguration(IGNITE_INSTANCE_TWO, 10801)); + } + /** Ignite client instance bean with default name. */ @Bean public IgniteClient igniteInstance() { @@ -82,18 +88,10 @@ public class IgniteClientApplicationConfiguration { /** Ignite client instance bean with non-default name. */ @Bean public IgniteClient igniteInstanceTWO() { - Ignition.start(igniteConfiguration(IGNITE_INSTANCE_TWO, 10801)); - return Ignition.startClient(new ClientConfiguration().setAddresses("127.0.0.1:10801")); } - /** Ignite client configuraition bean. */ - @Bean - public ClientConfiguration clientConfiguration() { - return new ClientConfiguration().setAddresses("127.0.0.1:" + CLI_CONN_PORT); - } - - /** Ingite configuration for server node. */ + /** */ private static IgniteConfiguration igniteConfiguration(String igniteInstanceName, int cliConnPort) { return new IgniteConfiguration() .setIgniteInstanceName(igniteInstanceName) diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData22TestSuite.java b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData22TestSuite.java index 4363f74..899118b 100644 --- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData22TestSuite.java +++ b/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringData22TestSuite.java @@ -21,6 +21,7 @@ import org.apache.ignite.springdata.IgniteClientSpringDataCompoundKeyTest; import org.apache.ignite.springdata.IgniteClientSpringDataCrudSelfTest; import org.apache.ignite.springdata.IgniteClientSpringDataQueriesSelfTest; import org.apache.ignite.springdata.IgniteSpringDataCompoundKeyTest; +import org.apache.ignite.springdata.IgniteSpringDataConnectionConfigurationTest; import org.apache.ignite.springdata.IgniteSpringDataCrudSelfExpressionTest; import org.apache.ignite.springdata.IgniteSpringDataCrudSelfTest; import org.apache.ignite.springdata.IgniteSpringDataQueriesSelfTest; @@ -36,6 +37,7 @@ import org.junit.runners.Suite; IgniteSpringDataQueriesSelfTest.class, IgniteSpringDataCrudSelfExpressionTest.class, IgniteSpringDataCompoundKeyTest.class, + IgniteSpringDataConnectionConfigurationTest.class, IgniteClientSpringDataCrudSelfTest.class, IgniteClientSpringDataQueriesSelfTest.class, IgniteClientSpringDataCompoundKeyTest.class diff --git a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/ClosableIgniteClientProxy.java similarity index 59% rename from modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java rename to modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/ClosableIgniteClientProxy.java index ccbfa08..73e1681 100644 --- a/modules/spring-data-2.0-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java +++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/ClosableIgniteClientProxy.java @@ -15,15 +15,21 @@ * limitations under the License. */ -package org.apache.ignite.springdata.misc; +package org.apache.ignite.springdata.proxy; -import java.io.Serializable; -import org.apache.ignite.configuration.ClientConfiguration; -import org.apache.ignite.springdata20.repository.IgniteRepository; -import org.apache.ignite.springdata20.repository.config.RepositoryConfig; +import org.apache.ignite.client.IgniteClient; -/** Repository for testing repository configurion approach through {@link ClientConfiguration}. */ -@RepositoryConfig(cacheName = "PersonCache", igniteCfg = "clientConfiguration") -public interface IgniteClientConfigRepository extends IgniteRepository<Object, Serializable> { - // No-op. +/** Extends {@link IgniteClientProxy} with the ability to close underlying thin client instance. */ +public class ClosableIgniteClientProxy extends IgniteClientProxy implements AutoCloseable { + /** + * @param cli Ignite client instance. + */ + public ClosableIgniteClientProxy(IgniteClient cli) { + super(cli); + } + + /** {@inheritDoc} */ + @Override public void close() throws Exception { + cli.close(); + } } diff --git a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/ClosableIgniteProxyImpl.java similarity index 59% rename from modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java rename to modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/ClosableIgniteProxyImpl.java index 23a9d8e..79807e4 100644 --- a/modules/spring-data-2.2-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientConfigRepository.java +++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/ClosableIgniteProxyImpl.java @@ -15,15 +15,21 @@ * limitations under the License. */ -package org.apache.ignite.springdata.misc; +package org.apache.ignite.springdata.proxy; -import java.io.Serializable; -import org.apache.ignite.configuration.ClientConfiguration; -import org.apache.ignite.springdata22.repository.IgniteRepository; -import org.apache.ignite.springdata22.repository.config.RepositoryConfig; +import org.apache.ignite.Ignite; -/** Repository for testing repository configurion approach through {@link ClientConfiguration}. */ -@RepositoryConfig(cacheName = "PersonCache", igniteCfg = "clientConfiguration") -public interface IgniteClientConfigRepository extends IgniteRepository<Object, Serializable> { - // No-op. +/** Extends {@link IgniteProxyImpl} with the ability to close underlying Ignite instance. */ +public class ClosableIgniteProxyImpl extends IgniteProxyImpl implements AutoCloseable { + /** + * @param ignite Ignite instance. + */ + public ClosableIgniteProxyImpl(Ignite ignite) { + super(ignite); + } + + /** {@inheritDoc} */ + @Override public void close() throws Exception { + ignite.close(); + } } diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheProxy.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheProxy.java index ac29eb4..3c5c4d2 100644 --- a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheProxy.java +++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteCacheProxy.java @@ -26,7 +26,7 @@ import org.apache.ignite.cache.query.Query; import org.apache.ignite.cache.query.QueryCursor; import org.apache.ignite.client.ClientCache; -/** Reperesents Ignite cache operations required by Spring Data. */ +/** Represents Ignite cache operations required by Spring Data. */ public interface IgniteCacheProxy<K, V> extends Iterable<Cache.Entry<K, V>> { /** * Gets an entry from the cache. diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteClientProxy.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteClientProxy.java index ae50823..00dc42e 100644 --- a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteClientProxy.java +++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteClientProxy.java @@ -17,12 +17,13 @@ package org.apache.ignite.springdata.proxy; +import java.util.Objects; import org.apache.ignite.client.IgniteClient; /** Implementation of {@link IgniteProxy} that provides access to Ignite cluster through {@link IgniteClient} instance. */ public class IgniteClientProxy implements IgniteProxy { /** {@link IgniteClient} instance to which operations are delegated. */ - private final IgniteClient cli; + protected final IgniteClient cli; /** */ public IgniteClientProxy(IgniteClient cli) { @@ -38,4 +39,20 @@ public class IgniteClientProxy implements IgniteProxy { @Override public <K, V> IgniteCacheProxy<K, V> cache(String name) { return new IgniteCacheClientProxy<>(cli.cache(name)); } + + /** {@inheritDoc} */ + @Override public boolean equals(Object other) { + if (this == other) + return true; + + if (other == null || getClass() != other.getClass()) + return false; + + return Objects.equals(cli, ((IgniteClientProxy)other).cli); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + return cli.hashCode(); + } } diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxy.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxy.java index e798274..62e0d5c 100644 --- a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxy.java +++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxy.java @@ -17,7 +17,13 @@ package org.apache.ignite.springdata.proxy; -/** Reperesents Ignite cluster operations required by Spring Data. */ +import org.apache.ignite.Ignite; +import org.apache.ignite.Ignition; +import org.apache.ignite.client.IgniteClient; +import org.apache.ignite.configuration.ClientConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; + +/** Represents Ignite cluster operations required by Spring Data. */ public interface IgniteProxy { /** * Gets existing cache with the given name or creates new one. @@ -34,4 +40,32 @@ public interface IgniteProxy { * @return Cache proxy that provides access to cache with specified name or {@code null} if it doesn't exist. */ public <K, V> IgniteCacheProxy<K, V> cache(String name); + + /** + * @param connObj Object that will be used to obtain underlying Ignite client instance to access the Ignite cluster. + * @return Ignite proxy instance. + */ + public static IgniteProxy of(Object connObj) { + if (connObj instanceof Ignite) + return new IgniteProxyImpl((Ignite)connObj); + else if (connObj instanceof IgniteConfiguration) { + try { + return new IgniteProxyImpl(Ignition.ignite(((IgniteConfiguration)connObj).getIgniteInstanceName())); + } + catch (Exception ignored) { + // No-op. + } + + return new ClosableIgniteProxyImpl(Ignition.start((IgniteConfiguration)connObj)); + } + else if (connObj instanceof String) + return new ClosableIgniteProxyImpl(Ignition.start((String)connObj)); + else if (connObj instanceof IgniteClient) + return new IgniteClientProxy((IgniteClient)connObj); + else if (connObj instanceof ClientConfiguration) + return new ClosableIgniteClientProxy(Ignition.startClient((ClientConfiguration)connObj)); + + throw new IllegalArgumentException( + "Object of type " + connObj.getClass().getName() + " can not be used to connect to the Ignite cluster."); + } } diff --git a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxyImpl.java b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxyImpl.java index 96b46a7..85acceb 100644 --- a/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxyImpl.java +++ b/modules/spring-data-commons/src/main/java/org/apache/ignite/springdata/proxy/IgniteProxyImpl.java @@ -17,12 +17,14 @@ package org.apache.ignite.springdata.proxy; +import java.util.Objects; import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; /** Implementation of {@link IgniteProxy} that provides access to Ignite cluster through {@link Ignite} instance. */ public class IgniteProxyImpl implements IgniteProxy { /** {@link Ignite} instance to which operations are delegated. */ - private final Ignite ignite; + protected final Ignite ignite; /** */ public IgniteProxyImpl(Ignite ignite) { @@ -36,11 +38,29 @@ public class IgniteProxyImpl implements IgniteProxy { /** {@inheritDoc} */ @Override public <K, V> IgniteCacheProxy<K, V> cache(String name) { - return new IgniteCacheProxyImpl<>(ignite.cache(name)); + IgniteCache<K, V> cache = ignite.cache(name); + + return cache == null ? null : new IgniteCacheProxyImpl<>(cache); } /** @return {@link Ignite} instance to which operations are delegated. */ public Ignite delegate() { return ignite; } + + /** {@inheritDoc} */ + @Override public boolean equals(Object other) { + if (this == other) + return true; + + if (other == null || getClass() != other.getClass()) + return false; + + return Objects.equals(ignite, ((IgniteProxyImpl)other).ignite); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + return ignite.hashCode(); + } } diff --git a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/config/IgniteRepositoryConfigurationExtension.java b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/config/IgniteRepositoryConfigurationExtension.java index 630690a..6f21b59 100644 --- a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/config/IgniteRepositoryConfigurationExtension.java +++ b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/config/IgniteRepositoryConfigurationExtension.java @@ -19,14 +19,21 @@ package org.apache.ignite.springdata.repository.config; import java.util.Collection; import java.util.Collections; import org.apache.ignite.springdata.repository.IgniteRepository; +import org.apache.ignite.springdata.repository.support.IgniteProxyFactory; import org.apache.ignite.springdata.repository.support.IgniteRepositoryFactoryBean; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.data.repository.config.RepositoryConfigurationExtension; import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport; +import org.springframework.data.repository.config.RepositoryConfigurationSource; /** * Apache Ignite specific implementation of {@link RepositoryConfigurationExtension}. */ public class IgniteRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport { + /** Name of the auto-registered Ignite proxy factory bean. */ + private static final String IGNITE_PROXY_FACTORY_BEAN_NAME = "igniteProxyFactory"; + /** {@inheritDoc} */ @Override public String getModuleName() { return "Apache Ignite"; @@ -46,4 +53,13 @@ public class IgniteRepositoryConfigurationExtension extends RepositoryConfigurat @Override protected Collection<Class<?>> getIdentifyingTypes() { return Collections.<Class<?>>singleton(IgniteRepository.class); } + + /** {@inheritDoc} */ + @Override public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource cfg) { + registerIfNotAlreadyRegistered( + BeanDefinitionBuilder.genericBeanDefinition(IgniteProxyFactory.class).getBeanDefinition(), + registry, + IGNITE_PROXY_FACTORY_BEAN_NAME, + cfg); + } } diff --git a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteProxyFactory.java b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteProxyFactory.java new file mode 100644 index 0000000..3585287 --- /dev/null +++ b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteProxyFactory.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.ignite.springdata.repository.support; + +import org.apache.ignite.springdata.proxy.IgniteProxy; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.AbstractFactoryBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * Represents factory for obtaining instance of {@link IgniteProxy} that provide client-independent connection to the + * Ignite cluster. + */ +public class IgniteProxyFactory extends AbstractFactoryBean<IgniteProxy> implements ApplicationContextAware { + /** Name of the bean that stores Ignite client instance to access the Ignite cluster. */ + private static final String IGNITE_INSTANCE_BEAN_NAME = "igniteInstance"; + + /** Name of the bean that stores the configuration to instantiate the Ignite client. */ + private static final String IGNITE_CONFIG_BEAN_NAME = "igniteCfg"; + + /** Name of the bean that stores the Spring configuration path to instantiate the Ignite client. */ + private static final String IGNITE_SPRING_CONFIG_PATH_BEAN_NAME= "igniteSpringCfgPath"; + + /** Spring application context. */ + private ApplicationContext ctx; + + /** {@inheritDoc} */ + @Override public void setApplicationContext(ApplicationContext ctx) throws BeansException { + this.ctx = ctx; + } + + /** {@inheritDoc} */ + @Override public void destroy() throws Exception { + Object igniteProxy = getObject(); + + if (igniteProxy instanceof AutoCloseable) + ((AutoCloseable)igniteProxy).close(); + } + + /** {@inheritDoc} */ + @Override public Class<?> getObjectType() { + return IgniteProxy.class; + } + + /** {@inheritDoc} */ + @Override protected IgniteProxy createInstance() { + Object connCfg; + + try { + connCfg = ctx.getBean(IGNITE_INSTANCE_BEAN_NAME); + } + catch (BeansException ex) { + try { + connCfg = ctx.getBean(IGNITE_CONFIG_BEAN_NAME); + } + catch (BeansException ex2) { + try { + connCfg = ctx.getBean(IGNITE_SPRING_CONFIG_PATH_BEAN_NAME, String.class); + } + catch (BeansException ex3) { + throw new IllegalArgumentException("No beans were found that provide connection configuration to" + + " the Ignite cluster. One of the beans with the following names is required : \"" + + IGNITE_INSTANCE_BEAN_NAME + "\", \"" + IGNITE_CONFIG_BEAN_NAME + "\" or \"" + + IGNITE_SPRING_CONFIG_PATH_BEAN_NAME + "\"."); + } + } + } + + return IgniteProxy.of(connCfg); + } +} diff --git a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactory.java b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactory.java index 5fc0ba2..8649733 100644 --- a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactory.java +++ b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactory.java @@ -18,17 +18,8 @@ package org.apache.ignite.springdata.repository.support; import java.io.Serializable; import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; -import org.apache.ignite.Ignite; -import org.apache.ignite.Ignition; -import org.apache.ignite.client.IgniteClient; -import org.apache.ignite.configuration.ClientConfiguration; -import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.springdata.proxy.IgniteCacheProxy; import org.apache.ignite.springdata.proxy.IgniteProxy; -import org.apache.ignite.springdata.proxy.IgniteProxyImpl; -import org.apache.ignite.springdata.proxy.IgniteClientProxy; -import org.apache.ignite.springdata.repository.IgniteRepository; import org.apache.ignite.springdata.repository.config.Query; import org.apache.ignite.springdata.repository.config.RepositoryConfig; import org.apache.ignite.springdata.repository.query.IgniteQuery; @@ -51,52 +42,28 @@ import org.springframework.util.StringUtils; * Crucial for spring-data functionality class. Create proxies for repositories. */ public class IgniteRepositoryFactory extends RepositoryFactorySupport { - /** Ignite instance */ - private IgniteProxy ignite; - - /** Mapping of a repository to a cache. */ - private final Map<Class<?>, String> repoToCache = new HashMap<>(); + /** Ignite cache proxy instance associated with the current repository. */ + private final IgniteCacheProxy<?, ?> cache; /** - * Creates the factory with initialized {@link Ignite} instance. + * Creates the factory with initialized {@link IgniteProxy} instance. * - * @param ignite + * @param ignite Ignite proxy instance. + * @param repoInterface Repository interface. */ - public IgniteRepositoryFactory(Ignite ignite) { - this.ignite = new IgniteProxyImpl(ignite); - } + public IgniteRepositoryFactory(IgniteProxy ignite, Class<?> repoInterface) { + RepositoryConfig cfg = repoInterface.getAnnotation(RepositoryConfig.class); - /** Creates the factory with initialized {@link IgniteClient} instance. */ - public IgniteRepositoryFactory(IgniteClient cli) { - ignite = new IgniteClientProxy(cli); - } + Assert.notNull(cfg, "Invalid repository configuration [name=" + repoInterface.getName() + "]. " + + RepositoryConfig.class.getName() + " annotation must be specified for each repository interface."); - /** - * Initializes the factory with provided {@link IgniteConfiguration} that is used to start up an underlying - * {@link Ignite} instance. - * - * @param cfg Ignite configuration. - */ - public IgniteRepositoryFactory(IgniteConfiguration cfg) { - this.ignite = new IgniteProxyImpl(Ignition.start(cfg)); - } + String cacheName = cfg.cacheName(); - /** - * Initializes the factory with provided {@link ClientConfiguration} that is used to start up an underlying - * {@link IgniteClient} instance. - */ - public IgniteRepositoryFactory(ClientConfiguration cfg) { - this.ignite = new IgniteClientProxy(Ignition.startClient(cfg)); - } + Assert.hasText(cacheName, "Invalid repository configuration [name=" + repoInterface.getName() + "]." + + " Set a name of an Apache Ignite cache using " + RepositoryConfig.class.getName() + + " annotation to map this repository to the underlying cache."); - /** - * Initializes the factory with provided a configuration under {@code springCfgPath} that is used to start up - * an underlying {@link Ignite} instance. - * - * @param springCfgPath A path to Ignite configuration. - */ - public IgniteRepositoryFactory(String springCfgPath) { - this.ignite = new IgniteProxyImpl(Ignition.start(springCfgPath)); + cache = ignite.getOrCreateCache(cacheName); } /** {@inheritDoc} */ @@ -118,27 +85,8 @@ public class IgniteRepositoryFactory extends RepositoryFactorySupport { } /** {@inheritDoc} */ - @Override protected RepositoryMetadata getRepositoryMetadata(Class<?> repoItf) { - Assert.notNull(repoItf, "Repository interface must be set."); - Assert.isAssignable(IgniteRepository.class, repoItf, "Repository must implement IgniteRepository interface."); - - RepositoryConfig annotation = repoItf.getAnnotation(RepositoryConfig.class); - - Assert.notNull(annotation, "Set a name of an Apache Ignite cache using @RepositoryConfig annotation to map " + - "this repository to the underlying cache."); - - Assert.hasText(annotation.cacheName(), "Set a name of an Apache Ignite cache using @RepositoryConfig " + - "annotation to map this repository to the underlying cache."); - - repoToCache.put(repoItf, annotation.cacheName()); - - return super.getRepositoryMetadata(repoItf); - } - - /** {@inheritDoc} */ @Override protected Object getTargetRepository(RepositoryInformation metadata) { - return getTargetRepositoryViaReflection(metadata, - ignite.getOrCreateCache(repoToCache.get(metadata.getRepositoryInterface()))); + return getTargetRepositoryViaReflection(metadata, cache); } /** {@inheritDoc} */ @@ -157,7 +105,7 @@ public class IgniteRepositoryFactory extends RepositoryFactorySupport { if (key != Key.CREATE && StringUtils.hasText(qryStr)) return new IgniteRepositoryQuery(metadata, new IgniteQuery(qryStr, isFieldQuery(qryStr), IgniteQueryGenerator.getOptions(mtd)), - mtd, factory, ignite.getOrCreateCache(repoToCache.get(metadata.getRepositoryInterface()))); + mtd, factory, cache); } if (key == QueryLookupStrategy.Key.USE_DECLARED_QUERY) @@ -165,7 +113,7 @@ public class IgniteRepositoryFactory extends RepositoryFactorySupport { "a query string via org.apache.ignite.springdata.repository.config.Query annotation."); return new IgniteRepositoryQuery(metadata, IgniteQueryGenerator.generateSql(mtd, metadata), mtd, - factory, ignite.getOrCreateCache(repoToCache.get(metadata.getRepositoryInterface()))); + factory, cache); } }; } diff --git a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactoryBean.java b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactoryBean.java index 71200c2..3028cea 100644 --- a/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactoryBean.java +++ b/modules/spring-data-ext/src/main/java/org/apache/ignite/springdata/repository/support/IgniteRepositoryFactoryBean.java @@ -19,14 +19,12 @@ package org.apache.ignite.springdata.repository.support; import java.io.Serializable; import org.apache.ignite.Ignite; -import org.apache.ignite.IgniteException; import org.apache.ignite.client.IgniteClient; import org.apache.ignite.configuration.ClientConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.springdata.proxy.IgniteProxy; import org.apache.ignite.springdata.repository.IgniteRepository; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; import org.springframework.data.repository.core.support.RepositoryFactorySupport; @@ -47,9 +45,10 @@ import org.springframework.data.repository.core.support.RepositoryFactorySupport * @param <ID> Domain object key, super expects {@link Serializable}. */ public class IgniteRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> - extends RepositoryFactoryBeanSupport<T, S, ID> implements ApplicationContextAware { - /** Application context. */ - private ApplicationContext ctx; + extends RepositoryFactoryBeanSupport<T, S, ID> { + /** Ignite proxy. */ + @Autowired + private IgniteProxy ignite; /** * @param repositoryInterface Repository interface. @@ -59,50 +58,8 @@ public class IgniteRepositoryFactoryBean<T extends Repository<S, ID>, S, ID exte } /** {@inheritDoc} */ - @Override public void setApplicationContext(ApplicationContext context) throws BeansException { - this.ctx = context; - } - - /** {@inheritDoc} */ @Override protected RepositoryFactorySupport createRepositoryFactory() { - try { - Object igniteInstanceBean = ctx.getBean("igniteInstance"); - - if (igniteInstanceBean instanceof Ignite) - return new IgniteRepositoryFactory((Ignite)igniteInstanceBean); - else if (igniteInstanceBean instanceof IgniteClient) - return new IgniteRepositoryFactory((IgniteClient)igniteInstanceBean); - - throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to the" + - " \"igniteInstance\" property of repository configuration must be one of the following types: " + - Ignite.class.getName() + ", " + IgniteClient.class.getName()); - } - catch (BeansException ex) { - try { - Object igniteCfgBean = ctx.getBean("igniteCfg"); - - if (igniteCfgBean instanceof IgniteConfiguration) - return new IgniteRepositoryFactory((IgniteConfiguration)igniteCfgBean); - else if (igniteCfgBean instanceof ClientConfiguration) - return new IgniteRepositoryFactory((ClientConfiguration)igniteCfgBean); - - throw new IllegalStateException("Invalid repository configuration. The Spring Bean corresponding to" + - " the \"igniteCfg\" property of repository configuration must be one of the following types: [" + - IgniteConfiguration.class.getName() + ", " + ClientConfiguration.class.getName() + ']'); - } - catch (BeansException ex2) { - try { - String path = (String)ctx.getBean("igniteSpringCfgPath"); - - return new IgniteRepositoryFactory(path); - } - catch (BeansException ex3) { - throw new IgniteException("Failed to initialize Ignite repository factory. One of the following" + - " beans must be defined in application configuration: \"igniteInstance\", \"igniteCfg\"," + - " \"igniteSpringCfgPath\"."); - } - } - } + return new IgniteRepositoryFactory(ignite, getObjectType()); } } diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java index a887ad8..751eb09 100644 --- a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java +++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteClientSpringDataCrudSelfTest.java @@ -17,18 +17,11 @@ package org.apache.ignite.springdata; -import org.apache.ignite.configuration.ClientConfiguration; import org.apache.ignite.internal.IgniteEx; import org.apache.ignite.springdata.misc.IgniteClientApplicationConfiguration; import org.apache.ignite.springdata.misc.PersonRepository; import org.apache.ignite.springdata.misc.PersonRepositoryWithCompoundKey; -import org.apache.ignite.springdata.repository.config.EnableIgniteRepositories; -import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import static org.apache.ignite.springdata.compoundkey.CompoundKeyApplicationConfiguration.CLI_CONN_PORT; /** Tests Spring Data CRUD operation when thin client is used for accessing the Ignite cluster. */ public class IgniteClientSpringDataCrudSelfTest extends IgniteSpringDataCrudSelfTest { @@ -43,28 +36,4 @@ public class IgniteClientSpringDataCrudSelfTest extends IgniteSpringDataCrudSelf repoWithCompoundKey = ctx.getBean(PersonRepositoryWithCompoundKey.class); ignite = ctx.getBean(IgniteEx.class); } - - /** - * Tests repository configuration in case {@link ClientConfiguration} is used to provide access to Ignite cluster. - */ - @Test - public void testRepositoryWithClientConfiguration() { - ctx = new AnnotationConfigApplicationContext(); - - ctx.register(TestApplicationConfiguration.class); - ctx.refresh(); - - assertTrue(ctx.getBean(PersonRepository.class).count() > 0); - } - - /** */ - @Configuration - @EnableIgniteRepositories("org.apache.ignite.springdata.misc") - static class TestApplicationConfiguration { - /** Ignite client configuration bean. */ - @Bean - public ClientConfiguration igniteCfg() { - return new ClientConfiguration().setAddresses("127.0.0.1:" + CLI_CONN_PORT); - } - } } diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataConnectionConfigurationTest.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataConnectionConfigurationTest.java new file mode 100644 index 0000000..aa3d6a7 --- /dev/null +++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/IgniteSpringDataConnectionConfigurationTest.java @@ -0,0 +1,217 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.ignite.springdata; + +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.Ignition; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.ClientConfiguration; +import org.apache.ignite.configuration.ClientConnectorConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; +import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder; +import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder; +import org.apache.ignite.springdata.misc.Person; +import org.apache.ignite.springdata.misc.PersonRepository; +import org.apache.ignite.springdata.repository.IgniteRepository; +import org.apache.ignite.springdata.repository.config.EnableIgniteRepositories; +import org.apache.ignite.springdata.repository.config.RepositoryConfig; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Configuration; + +import static org.apache.ignite.testframework.GridTestUtils.assertThrowsAnyCause; +import static org.springframework.context.annotation.FilterType.ASSIGNABLE_TYPE; + +/** Tests Spring Data repository cluster connection configurations. */ +public class IgniteSpringDataConnectionConfigurationTest extends GridCommonAbstractTest { + /** */ + private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true); + + /** */ + private static final String CACHE_NAME = "PersonCache"; + + /** */ + private static final int CLI_CONN_PORT = 10810; + + /** */ + private static final String CLI_NAME = "cli-node"; + + /** */ + private static final String SRV_NAME = "srv-node"; + + /** */ + private static final String LOCAL_HOST = "127.0.0.1"; + + /** Tests repository configuration in case {@link IgniteConfiguration} is used to access the Ignite cluster. */ + @Test + public void testRepositoryWithIgniteConfiguration() { + checkRepositoryConfiguration(IgniteConfigurationApplication.class); + + assertClientNodeIsStopped(); + } + + /** Tests repository configuration in case {@link ClientConfiguration} is used to access the Ignite cluster. */ + @Test + public void testRepositoryWithClientConfiguration() { + checkRepositoryConfiguration(ClientConfigurationApplication.class); + } + + /** + * Tests repository configuration in case {@link IgniteConfiguration} that refers to existing Ignite node instance + * is used to access the Ignite cluster. + */ + @Test + public void testRepositoryWithExistingIgniteInstance() throws Exception { + try (Ignite ignored = startGrid(getIgniteConfiguration(CLI_NAME, true))) { + checkRepositoryConfiguration(IgniteConfigurationApplication.class); + + assertNotNull(Ignition.ignite(CLI_NAME)); + } + } + + /** Tests repository configuration in case specified cache name is invalid. */ + @Test + public void testRepositoryWithInvalidCacheNameConfiguration() { + try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext()) { + ctx.register(InvalidCacheNameApplication.class); + + assertThrowsAnyCause(log, + () -> { + ctx.refresh(); + + return null; + }, + IllegalArgumentException.class, + "Invalid repository configuration [name=" + InvalidCacheRepository.class.getName() + "]. Set a" + + " name of an Apache Ignite cache using org.apache.ignite.springdata.repository.config.RepositoryConfig" + + " annotation to map this repository to the underlying cache."); + } + + assertClientNodeIsStopped(); + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + super.afterTest(); + + grid(SRV_NAME).cache(CACHE_NAME).clear(); + } + + /** */ + private void assertClientNodeIsStopped() { + assertFalse(Ignition.allGrids().stream().map(Ignite::name).anyMatch(CLI_NAME::equals)); + } + + /** {@inheritDoc} */ + @Override protected void beforeTestsStarted() throws Exception { + super.beforeTestsStarted(); + + startGrid(getIgniteConfiguration(SRV_NAME, false)); + } + + /** + * Checks that repository created based on specified Spring application configuration is properly initialized and + * got access to the Ignite cluster. + */ + private void checkRepositoryConfiguration(Class<?> cfgCls) { + try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext()) { + ctx.register(cfgCls); + ctx.refresh(); + + PersonRepository repo = ctx.getBean(PersonRepository.class); + + IgniteCache<Integer, Person> cache = grid(SRV_NAME).cache(CACHE_NAME); + + assertEquals(0, repo.count()); + assertEquals(0, cache.size()); + + int key = 0; + + repo.save(key, new Person("Domenico", "Scarlatti")); + + assertEquals(1, repo.count()); + assertNotNull(cache.get(key)); + } + } + + /** + * Spring Application configuration for repository testing in case {@link IgniteConfiguration} is used + * for accessing the cluster. + */ + @Configuration + @EnableIgniteRepositories(includeFilters = @Filter(type = ASSIGNABLE_TYPE, classes = PersonRepository.class)) + public static class IgniteConfigurationApplication { + /** Ignite configuration bean. */ + @Bean + public IgniteConfiguration igniteCfg() { + return getIgniteConfiguration(CLI_NAME, true); + } + } + + /** + * Spring Application configuration for repository testing in case {@link IgniteConfiguration} is used + * for accessing the cluster. + */ + @Configuration + @EnableIgniteRepositories( + considerNestedRepositories = true, + includeFilters = @Filter(type = ASSIGNABLE_TYPE, classes = InvalidCacheRepository.class)) + public static class InvalidCacheNameApplication { + /** Ignite configuration bean. */ + @Bean + public IgniteConfiguration igniteCfg() { + return getIgniteConfiguration(CLI_NAME, true); + } + } + + /** + * Spring Application configuration for repository testing in case {@link ClientConfiguration} is used + * for accessing the cluster. + */ + @Configuration + @EnableIgniteRepositories(includeFilters = @Filter(type = ASSIGNABLE_TYPE, classes = PersonRepository.class)) + public static class ClientConfigurationApplication { + /** Ignite client configuration bean. */ + @Bean + public ClientConfiguration igniteCfg() { + return new ClientConfiguration().setAddresses(LOCAL_HOST + ':' + CLI_CONN_PORT); + } + } + + /** Repository for testing application behavior in case the cache name is not specified in the repository configuration. */ + @RepositoryConfig + interface InvalidCacheRepository extends IgniteRepository<Person, Integer> { + // No-op. + } + + /** */ + private static IgniteConfiguration getIgniteConfiguration(String name, boolean clientMode) { + return new IgniteConfiguration() + .setIgniteInstanceName(name) + .setClientMode(clientMode) + .setLocalHost(LOCAL_HOST) + .setDiscoverySpi(new TcpDiscoverySpi().setIpFinder(IP_FINDER)) + .setClientConnectorConfiguration(new ClientConnectorConfiguration().setPort(CLI_CONN_PORT)) + .setCacheConfiguration(new CacheConfiguration<>(CACHE_NAME)); + } +} diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java index 66379f7..5044aef 100644 --- a/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java +++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/springdata/misc/IgniteClientApplicationConfiguration.java @@ -25,6 +25,8 @@ import org.apache.ignite.configuration.ClientConfiguration; import org.apache.ignite.configuration.ClientConnectorConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; +import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder; import org.apache.ignite.springdata.repository.config.EnableIgniteRepositories; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -40,6 +42,7 @@ public class IgniteClientApplicationConfiguration { public IgniteEx igniteServerNode() { IgniteConfiguration cfg = new IgniteConfiguration() .setClientConnectorConfiguration(new ClientConnectorConfiguration().setPort(CLI_CONN_PORT)) + .setDiscoverySpi(new TcpDiscoverySpi().setIpFinder(new TcpDiscoveryVmIpFinder(true))) .setCacheConfiguration(new CacheConfiguration<Integer, Person>("PersonCache") .setIndexedTypes(Integer.class, Person.class)); diff --git a/modules/spring-data-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringDataTestSuite.java b/modules/spring-data-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringDataTestSuite.java index cb6e34c..4a47568 100644 --- a/modules/spring-data-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringDataTestSuite.java +++ b/modules/spring-data-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringDataTestSuite.java @@ -21,6 +21,7 @@ import org.apache.ignite.springdata.IgniteClientSpringDataCompoundKeyTest; import org.apache.ignite.springdata.IgniteClientSpringDataCrudSelfTest; import org.apache.ignite.springdata.IgniteClientSpringDataQueriesSelfTest; import org.apache.ignite.springdata.IgniteSpringDataCompoundKeyTest; +import org.apache.ignite.springdata.IgniteSpringDataConnectionConfigurationTest; import org.apache.ignite.springdata.IgniteSpringDataCrudSelfTest; import org.apache.ignite.springdata.IgniteSpringDataQueriesSelfTest; import org.junit.runner.RunWith; @@ -34,6 +35,7 @@ import org.junit.runners.Suite; IgniteSpringDataCrudSelfTest.class, IgniteSpringDataQueriesSelfTest.class, IgniteSpringDataCompoundKeyTest.class, + IgniteSpringDataConnectionConfigurationTest.class, IgniteClientSpringDataCrudSelfTest.class, IgniteClientSpringDataQueriesSelfTest.class, IgniteClientSpringDataCompoundKeyTest.class