You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by nt...@apache.org on 2017/10/25 11:43:20 UTC

ignite git commit: IGNITE-6555 When a CacheStore with a @SpringResource annotated field is configured Ignite fails to start via igniteSpringBean. This closes #2893.

Repository: ignite
Updated Branches:
  refs/heads/master 1816fb920 -> dde348607


IGNITE-6555 When a CacheStore with a @SpringResource annotated field is configured Ignite fails to start via igniteSpringBean. This closes #2893.

Signed-off-by: nikolay_tikhonov <nt...@gridgain.com>


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/dde34860
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/dde34860
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/dde34860

Branch: refs/heads/master
Commit: dde348607ed266f87bf594cb2a221ec39d84cfdb
Parents: 1816fb9
Author: Alexander Fedotov <al...@gmail.com>
Authored: Wed Oct 25 14:43:16 2017 +0300
Committer: nikolay_tikhonov <nt...@gridgain.com>
Committed: Wed Oct 25 14:43:16 2017 +0300

----------------------------------------------------------------------
 .../org/apache/ignite/IgniteSpringBean.java     |  32 ++-
 .../GridSpringBeanSerializationSelfTest.java    |   4 +-
 .../ignite/internal/IgniteSpringBeanTest.java   |   2 +-
 .../GridServiceInjectionSpringResourceTest.java |  10 +-
 ...teSpringBeanSpringResourceInjectionTest.java | 215 +++++++++++++++++++
 .../ignite/spring/injection/spring-bean.xml     |  84 ++++++++
 .../testsuites/IgniteSpringTestSuite.java       |   2 +
 7 files changed, 334 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/dde34860/modules/spring/src/main/java/org/apache/ignite/IgniteSpringBean.java
----------------------------------------------------------------------
diff --git a/modules/spring/src/main/java/org/apache/ignite/IgniteSpringBean.java b/modules/spring/src/main/java/org/apache/ignite/IgniteSpringBean.java
index 4cba76e..95fceec 100644
--- a/modules/spring/src/main/java/org/apache/ignite/IgniteSpringBean.java
+++ b/modules/spring/src/main/java/org/apache/ignite/IgniteSpringBean.java
@@ -38,9 +38,10 @@ import org.apache.ignite.plugin.PluginNotFoundException;
 import org.jetbrains.annotations.Nullable;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.DisposableBean;
-import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.SmartInitializingSingleton;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.event.ContextRefreshedEvent;
 
 /**
  * Ignite Spring bean allows to bypass {@link Ignition} methods.
@@ -48,9 +49,20 @@ import org.springframework.context.ApplicationContextAware;
  * Spring configuration file directly without invoking static
  * {@link Ignition} methods. This class can be wired directly from
  * Spring and can be referenced from within other Spring beans.
- * By virtue of implementing {@link DisposableBean} and {@link InitializingBean}
+ * By virtue of implementing {@link DisposableBean} and {@link SmartInitializingSingleton}
  * interfaces, {@code IgniteSpringBean} automatically starts and stops underlying
  * grid instance.
+ *
+ * <p>
+ * A note should be taken that Ignite instance is started after all other
+ * Spring beans have been initialized and right before Spring context is refreshed.
+ * That implies that it's not valid to reference IgniteSpringBean from
+ * any kind of Spring bean init methods like {@link javax.annotation.PostConstruct}.
+ * If it's required to reference IgniteSpringBean for other bean
+ * initialization purposes, it should be done from a {@link ContextRefreshedEvent}
+ * listener method declared in that bean.
+ * </p>
+ *
  * <p>
  * <h1 class="header">Spring Configuration Example</h1>
  * Here is a typical example of describing it in Spring file:
@@ -79,7 +91,7 @@ import org.springframework.context.ApplicationContextAware;
  * </pre>
  * <p>
  */
-public class IgniteSpringBean implements Ignite, DisposableBean, InitializingBean,
+public class IgniteSpringBean implements Ignite, DisposableBean, SmartInitializingSingleton,
     ApplicationContextAware, Externalizable {
     /** */
     private static final long serialVersionUID = 0L;
@@ -145,7 +157,6 @@ public class IgniteSpringBean implements Ignite, DisposableBean, InitializingBea
 
     /** {@inheritDoc} */
     @Override public void destroy() throws Exception {
-        // If there were some errors when afterPropertiesSet() was called.
         if (g != null) {
             // Do not cancel started tasks, wait for them.
             G.stop(g.name(), false);
@@ -153,11 +164,16 @@ public class IgniteSpringBean implements Ignite, DisposableBean, InitializingBea
     }
 
     /** {@inheritDoc} */
-    @Override public void afterPropertiesSet() throws Exception {
+    @Override public void afterSingletonsInstantiated() {
         if (cfg == null)
             cfg = new IgniteConfiguration();
 
-        g = IgniteSpring.start(cfg, appCtx);
+        try {
+            g = IgniteSpring.start(cfg, appCtx);
+        }
+        catch (IgniteCheckedException e) {
+            throw new IgniteException("Failed to start IgniteSpringBean", e);
+        }
     }
 
     /** {@inheritDoc} */
@@ -620,7 +636,9 @@ public class IgniteSpringBean implements Ignite, DisposableBean, InitializingBea
     protected void checkIgnite() throws IllegalStateException {
         if (g == null) {
             throw new IllegalStateException("Ignite is in invalid state to perform this operation. " +
-                "It either not started yet or has already being or have stopped " +
+                "It either not started yet or has already being or have stopped.\n" +
+                "Make sure that IgniteSpringBean is not referenced from any kind of Spring bean init methods " +
+                "like @PostConstruct}.\n" +
                 "[ignite=" + g + ", cfg=" + cfg + ']');
         }
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/dde34860/modules/spring/src/test/java/org/apache/ignite/internal/GridSpringBeanSerializationSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/spring/src/test/java/org/apache/ignite/internal/GridSpringBeanSerializationSelfTest.java b/modules/spring/src/test/java/org/apache/ignite/internal/GridSpringBeanSerializationSelfTest.java
index 6f7d27a..6a72213 100644
--- a/modules/spring/src/test/java/org/apache/ignite/internal/GridSpringBeanSerializationSelfTest.java
+++ b/modules/spring/src/test/java/org/apache/ignite/internal/GridSpringBeanSerializationSelfTest.java
@@ -53,7 +53,7 @@ public class GridSpringBeanSerializationSelfTest extends GridCommonAbstractTest
 
         bean.setConfiguration(cfg);
 
-        bean.afterPropertiesSet();
+        bean.afterSingletonsInstantiated();
     }
 
     /**
@@ -97,4 +97,4 @@ public class GridSpringBeanSerializationSelfTest extends GridCommonAbstractTest
         assert bean0.cluster().localNode() != null;
         assert bean0.cluster().localNode().<Boolean>attribute(ATTR_KEY);
     }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/dde34860/modules/spring/src/test/java/org/apache/ignite/internal/IgniteSpringBeanTest.java
----------------------------------------------------------------------
diff --git a/modules/spring/src/test/java/org/apache/ignite/internal/IgniteSpringBeanTest.java b/modules/spring/src/test/java/org/apache/ignite/internal/IgniteSpringBeanTest.java
index d465904..c7f0d59 100644
--- a/modules/spring/src/test/java/org/apache/ignite/internal/IgniteSpringBeanTest.java
+++ b/modules/spring/src/test/java/org/apache/ignite/internal/IgniteSpringBeanTest.java
@@ -31,7 +31,7 @@ public class IgniteSpringBeanTest extends GridCommonAbstractTest {
         try (IgniteSpringBean bean = new IgniteSpringBean()) {
             bean.setConfiguration(getConfiguration("test"));
 
-            bean.afterPropertiesSet();
+            bean.afterSingletonsInstantiated();
 
             bean.compute();
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/dde34860/modules/spring/src/test/java/org/apache/ignite/spring/injection/GridServiceInjectionSpringResourceTest.java
----------------------------------------------------------------------
diff --git a/modules/spring/src/test/java/org/apache/ignite/spring/injection/GridServiceInjectionSpringResourceTest.java b/modules/spring/src/test/java/org/apache/ignite/spring/injection/GridServiceInjectionSpringResourceTest.java
index c6a6d05..891c42e 100644
--- a/modules/spring/src/test/java/org/apache/ignite/spring/injection/GridServiceInjectionSpringResourceTest.java
+++ b/modules/spring/src/test/java/org/apache/ignite/spring/injection/GridServiceInjectionSpringResourceTest.java
@@ -26,10 +26,11 @@ import org.apache.ignite.resources.SpringResource;
 import org.apache.ignite.services.Service;
 import org.apache.ignite.services.ServiceContext;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.context.event.EventListener;
 import org.springframework.context.support.AbstractApplicationContext;
 import org.springframework.context.support.FileSystemXmlApplicationContext;
 
-import javax.annotation.PostConstruct;
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.IOException;
@@ -234,12 +235,11 @@ public class GridServiceInjectionSpringResourceTest extends GridCommonAbstractTe
         /**
          * @throws Exception If failed.
          */
-        @PostConstruct
-        public void init() throws Exception {
+        @EventListener
+        public void init(ContextRefreshedEvent evt) throws Exception {
             DummyService srv = ignite.services().serviceProxy(SERVICE_NAME, DummyService.class, false);
 
             assertNotNull(srv);
         }
     }
-
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/dde34860/modules/spring/src/test/java/org/apache/ignite/spring/injection/IgniteSpringBeanSpringResourceInjectionTest.java
----------------------------------------------------------------------
diff --git a/modules/spring/src/test/java/org/apache/ignite/spring/injection/IgniteSpringBeanSpringResourceInjectionTest.java b/modules/spring/src/test/java/org/apache/ignite/spring/injection/IgniteSpringBeanSpringResourceInjectionTest.java
new file mode 100644
index 0000000..2a06deb
--- /dev/null
+++ b/modules/spring/src/test/java/org/apache/ignite/spring/injection/IgniteSpringBeanSpringResourceInjectionTest.java
@@ -0,0 +1,215 @@
+/*
+ * 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.spring.injection;
+
+import java.io.Serializable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import javax.cache.Cache;
+import javax.cache.integration.CacheLoaderException;
+import javax.cache.integration.CacheWriterException;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.cache.store.CacheStoreAdapter;
+import org.apache.ignite.resources.SpringResource;
+import org.apache.ignite.services.Service;
+import org.apache.ignite.services.ServiceContext;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * Test checking injections of {@link SpringResource} annotated fields.
+ */
+public class IgniteSpringBeanSpringResourceInjectionTest extends GridCommonAbstractTest {
+    /** */
+    private static final String SPRING_CFG_LOCATION = "/org/apache/ignite/spring/injection/spring-bean.xml";
+
+    /** */
+    private static final String BEAN_TO_INJECT_NAME = "beanToInject";
+
+    /**
+     * Cache store with {@link SpringResource} fields to be injected.
+     */
+    public static class IgniteCacheStoreWithSpringResource<K, V> extends CacheStoreAdapter<K, V>
+        implements Serializable
+    {
+        /** */
+        private static final long serialVersionUID = 0L;
+
+        /** */
+        @SpringResource(resourceClass = Integer.class)
+        private transient Integer injectedSpringFld;
+
+        /**
+         * @return Injected Spring field.
+         */
+        public Integer getInjectedSpringField() {
+            return injectedSpringFld;
+        }
+
+        /** {@inheritDoc} */
+        @Override public V load(K key) throws CacheLoaderException {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void write(Cache.Entry<? extends K, ? extends V> entry) throws CacheWriterException {
+            // No-op.
+        }
+
+        /** {@inheritDoc} */
+        @Override public void delete(Object key) throws CacheWriterException {
+            // No-op.
+        }
+    }
+
+    /**
+     * Interface of a service with {@link SpringResource} fields to be injected.
+     */
+    public interface ServiceWithSpringResource {
+        /**
+         * @return Injected Spring field.
+         */
+        Integer getInjectedSpringField();
+    }
+
+    /**
+     * Service with {@link SpringResource} fields to be injected.
+     */
+    public static class ServiceWithSpringResourceImpl implements ServiceWithSpringResource, Service {
+        /** */
+        private static final long serialVersionUID = 0L;
+        /** */
+        @SpringResource(resourceClass = Integer.class)
+        private transient Integer injectedSpringFld;
+
+        /** {@inheritDoc} */
+        @Override public Integer getInjectedSpringField() {
+            return injectedSpringFld;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void cancel(ServiceContext ctx) {
+            // No-op.
+        }
+
+        /** {@inheritDoc} */
+        @Override public void init(ServiceContext ctx) throws Exception {
+            // No-op.
+        }
+
+        /** {@inheritDoc} */
+        @Override public void execute(ServiceContext ctx) throws Exception {
+            // No-op.
+        }
+    }
+
+    /**
+     *
+     */
+    private abstract static class TestSpringResourceInjectedRunnable implements Runnable {
+        /** */
+        private final String springCfgLocation;
+
+        /** */
+        private final String beanToInjectName;
+
+        /** */
+        protected BeanFactory appCtx;
+
+        /**
+         * Constructor.
+         *
+         * @param springCfgLocation Spring config location.
+         * @param beanToInjectName Bean to inject name.
+         */
+        protected TestSpringResourceInjectedRunnable(String springCfgLocation, String beanToInjectName) {
+            this.springCfgLocation = springCfgLocation;
+            this.beanToInjectName = beanToInjectName;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void run() {
+            appCtx = new ClassPathXmlApplicationContext(springCfgLocation);
+
+            Integer beanToInject = (Integer)appCtx.getBean(beanToInjectName);
+
+            assertEquals(beanToInject, getInjectedBean());
+        }
+
+        /**
+         * @return Injected bean to check.
+         */
+        abstract Integer getInjectedBean();
+    }
+
+    /** */
+    private void doTestSpringResourceInjected(Runnable testRunnable) throws Exception {
+        ExecutorService executorSvc = Executors.newSingleThreadExecutor();
+
+        Future<?> fut = executorSvc.submit(testRunnable);
+
+        try {
+            fut.get(5, TimeUnit.SECONDS);
+        }
+        catch (TimeoutException ignored) {
+            fail("Failed to wait for completion. Deadlock is possible");
+        }
+        finally {
+            executorSvc.shutdownNow();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        stopAllGrids(true);
+    }
+
+    /** */
+    public void testSpringResourceInjectedInCacheStore() throws Exception {
+        doTestSpringResourceInjected(
+            new TestSpringResourceInjectedRunnable(SPRING_CFG_LOCATION, BEAN_TO_INJECT_NAME) {
+                /** {@inheritDoc} */
+                @Override Integer getInjectedBean() {
+                    IgniteCacheStoreWithSpringResource cacheStore =
+                        appCtx.getBean(IgniteCacheStoreWithSpringResource.class);
+
+                    return cacheStore.getInjectedSpringField();
+                }
+            }
+        );
+    }
+
+    /** */
+    public void testSpringResourceInjectedInService() throws Exception {
+        doTestSpringResourceInjected(
+            new TestSpringResourceInjectedRunnable(SPRING_CFG_LOCATION, BEAN_TO_INJECT_NAME) {
+                /** {@inheritDoc} */
+                @Override Integer getInjectedBean() {
+                    Ignite ignite = appCtx.getBean(Ignite.class);
+                    ServiceWithSpringResource svc = ignite.services().service("ServiceWithSpringResource");
+
+                    return svc.getInjectedSpringField();
+                }
+            }
+        );
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/dde34860/modules/spring/src/test/java/org/apache/ignite/spring/injection/spring-bean.xml
----------------------------------------------------------------------
diff --git a/modules/spring/src/test/java/org/apache/ignite/spring/injection/spring-bean.xml b/modules/spring/src/test/java/org/apache/ignite/spring/injection/spring-bean.xml
new file mode 100644
index 0000000..d40f152
--- /dev/null
+++ b/modules/spring/src/test/java/org/apache/ignite/spring/injection/spring-bean.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  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.
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+    <bean id="beanToInject" class="java.lang.Integer">
+        <constructor-arg value="111" type="int"/>
+    </bean>
+
+    <bean id="cacheStore" class="org.apache.ignite.spring.injection.IgniteSpringBeanSpringResourceInjectionTest.IgniteCacheStoreWithSpringResource"/>
+
+    <bean id="serviceWithSpringResource"
+          class="org.apache.ignite.spring.injection.IgniteSpringBeanSpringResourceInjectionTest.ServiceWithSpringResourceImpl"/>
+
+    <bean id="igniteSpringBean" class="org.apache.ignite.IgniteSpringBean">
+        <property name="configuration">
+            <bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
+                <property name="peerClassLoadingEnabled" value="true"/>
+
+                <property name="cacheConfiguration">
+                    <list>
+                        <bean class="org.apache.ignite.configuration.CacheConfiguration">
+                            <property name="name" value="cache1"/>
+                            <property name="cacheStoreFactory">
+                                <bean class="javax.cache.configuration.FactoryBuilder" factory-method="factoryOf">
+                                    <constructor-arg ref="cacheStore"/>
+                                </bean>
+                            </property>
+
+                            <property name="readThrough" value="true"/>
+                            <property name="writeThrough" value="true"/>
+                        </bean>
+                    </list>
+                </property>
+
+                <property name="serviceConfiguration">
+                    <list>
+                        <bean class="org.apache.ignite.services.ServiceConfiguration">
+                            <property name="name" value="ServiceWithSpringResource"/>
+                            <property name="maxPerNodeCount" value="1"/>
+                            <property name="totalCount" value="1"/>
+                            <property name="service">
+                                <ref bean="serviceWithSpringResource"/>
+                            </property>
+                        </bean>
+                    </list>
+                </property>
+
+                <property name="discoverySpi">
+                    <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
+                        <property name="ipFinder">
+                            <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">
+                                <property name="addresses">
+                                    <list>
+                                        <value>127.0.0.1:47500..47509</value>
+                                    </list>
+                                </property>
+                            </bean>
+                        </property>
+                    </bean>
+                </property>
+            </bean>
+        </property>
+    </bean>
+</beans>

http://git-wip-us.apache.org/repos/asf/ignite/blob/dde34860/modules/spring/src/test/java/org/apache/ignite/testsuites/IgniteSpringTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/spring/src/test/java/org/apache/ignite/testsuites/IgniteSpringTestSuite.java b/modules/spring/src/test/java/org/apache/ignite/testsuites/IgniteSpringTestSuite.java
index 9ebb784..ff96c7b 100644
--- a/modules/spring/src/test/java/org/apache/ignite/testsuites/IgniteSpringTestSuite.java
+++ b/modules/spring/src/test/java/org/apache/ignite/testsuites/IgniteSpringTestSuite.java
@@ -21,6 +21,7 @@ import junit.framework.TestSuite;
 import org.apache.ignite.cache.spring.GridSpringCacheManagerSelfTest;
 import org.apache.ignite.cache.spring.SpringCacheManagerContextInjectionTest;
 import org.apache.ignite.cache.spring.SpringCacheTest;
+import org.apache.ignite.spring.injection.IgniteSpringBeanSpringResourceInjectionTest;
 import org.apache.ignite.internal.IgniteSpringBeanTest;
 import org.apache.ignite.cache.store.jdbc.CacheJdbcBlobStoreFactorySelfTest;
 import org.apache.ignite.cache.store.jdbc.CacheJdbcPojoStoreFactorySelfTest;
@@ -77,6 +78,7 @@ public class IgniteSpringTestSuite extends TestSuite {
         suite.addTestSuite(GridSpringTransactionManagerSelfTest.class);
 
         suite.addTestSuite(GridServiceInjectionSpringResourceTest.class);
+        suite.addTestSuite(IgniteSpringBeanSpringResourceInjectionTest.class);
 
         suite.addTestSuite(GridTransformSpringInjectionSelfTest.class);