ignite-3233 Added 'resourceClass' for SpringResource annotation.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/d59e5f56 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/d59e5f56 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/d59e5f56 Branch: refs/heads/ignite-1232 Commit: d59e5f5612bc916432e13bfa38a034d9120274fc Parents: 41f81da Author: sboikov <[email protected]> Authored: Thu Jun 23 12:48:13 2016 +0300 Committer: sboikov <[email protected]> Committed: Thu Jun 23 12:48:13 2016 +0300 ---------------------------------------------------------------------- .../apache/ignite/resources/SpringResource.java | 15 +- .../GridResourceSpringBeanInjector.java | 39 ++- .../GridSpringResourceInjectionSelfTest.java | 311 ++++++++++++++++--- .../spring-resource-with-duplicate-beans.xml | 30 ++ .../processors/resource/spring-resource.xml | 2 +- 5 files changed, 353 insertions(+), 44 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/d59e5f56/modules/core/src/main/java/org/apache/ignite/resources/SpringResource.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/resources/SpringResource.java b/modules/core/src/main/java/org/apache/ignite/resources/SpringResource.java index f4c041e..0a8b832 100644 --- a/modules/core/src/main/java/org/apache/ignite/resources/SpringResource.java +++ b/modules/core/src/main/java/org/apache/ignite/resources/SpringResource.java @@ -112,5 +112,18 @@ public @interface SpringResource { * * @return Resource bean name. */ - String resourceName(); + String resourceName() default ""; + + /** + * Resource bean class in provided {@code ApplicationContext} to look up + * a Spring bean. + * + * @return Resource bean class. + */ + Class<?> resourceClass() default DEFAULT.class; + + /** Dummy class to compensate for impossibility of having default null value for annotation method. */ + final class DEFAULT { + // No-op. + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/d59e5f56/modules/spring/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceSpringBeanInjector.java ---------------------------------------------------------------------- diff --git a/modules/spring/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceSpringBeanInjector.java b/modules/spring/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceSpringBeanInjector.java index adcf141..816a597 100644 --- a/modules/spring/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceSpringBeanInjector.java +++ b/modules/spring/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceSpringBeanInjector.java @@ -24,6 +24,7 @@ import org.apache.ignite.internal.managers.deployment.GridDeployment; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.resources.SpringResource; import org.springframework.context.ApplicationContext; +import org.springframework.util.StringUtils; /** * Spring bean injector implementation works with resources provided @@ -59,10 +60,8 @@ public class GridResourceSpringBeanInjector implements GridResourceInjector { field.getField()); } - String name = ann.resourceName(); - if (springCtx != null) { - Object bean = springCtx.getBean(name); + Object bean = getBeanByResourceAnnotation(ann); GridResourceUtils.inject(field.getField(), target, bean); } @@ -78,10 +77,8 @@ public class GridResourceSpringBeanInjector implements GridResourceInjector { if (mtd.getMethod().getParameterTypes().length != 1) throw new IgniteCheckedException("Method injection setter must have only one parameter: " + mtd.getMethod()); - String name = ann.resourceName(); - if (springCtx != null) { - Object bean = springCtx.getBean(name); + Object bean = getBeanByResourceAnnotation(ann); GridResourceUtils.inject(mtd.getMethod(), target, bean); } @@ -96,4 +93,34 @@ public class GridResourceSpringBeanInjector implements GridResourceInjector { @Override public String toString() { return S.toString(GridResourceSpringBeanInjector.class, this); } + + /** + * Retrieves from {@link #springCtx} the bean specified by {@link SpringResource} annotation. + * + * @param annotation {@link SpringResource} annotation instance from field or method. + * @return Bean object retrieved from spring context. + * @throws IgniteCheckedException If failed. + */ + private Object getBeanByResourceAnnotation(SpringResource annotation) throws IgniteCheckedException { + assert springCtx != null; + + String beanName = annotation.resourceName(); + Class<?> beanCls = annotation.resourceClass(); + + boolean oneParamSet = !StringUtils.isEmpty(beanName) ^ beanCls != SpringResource.DEFAULT.class; + + if (!oneParamSet) { + throw new IgniteCheckedException("Either bean name or its class must be specified in @SpringResource, " + + "but not both"); + } + + Object bean; + + if (!StringUtils.isEmpty(beanName)) + bean = springCtx.getBean(beanName); + else + bean = springCtx.getBean(beanCls); + + return bean; + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/d59e5f56/modules/spring/src/test/java/org/apache/ignite/internal/processors/resource/GridSpringResourceInjectionSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/spring/src/test/java/org/apache/ignite/internal/processors/resource/GridSpringResourceInjectionSelfTest.java b/modules/spring/src/test/java/org/apache/ignite/internal/processors/resource/GridSpringResourceInjectionSelfTest.java index 968c8c4..b989ac8 100644 --- a/modules/spring/src/test/java/org/apache/ignite/internal/processors/resource/GridSpringResourceInjectionSelfTest.java +++ b/modules/spring/src/test/java/org/apache/ignite/internal/processors/resource/GridSpringResourceInjectionSelfTest.java @@ -17,17 +17,22 @@ package org.apache.ignite.internal.processors.resource; +import java.util.concurrent.Callable; import org.apache.ignite.Ignite; import org.apache.ignite.IgniteException; import org.apache.ignite.IgniteSpring; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.internal.util.typedef.G; import org.apache.ignite.lang.IgniteCallable; import org.apache.ignite.resources.SpringResource; +import org.apache.ignite.testframework.GridTestUtils; import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Tests for injected resource. */ +@SuppressWarnings("unused") public class GridSpringResourceInjectionSelfTest extends GridCommonAbstractTest { /** Bean name. */ private static final String DUMMY_BEAN = "dummyResourceBean"; @@ -49,13 +54,13 @@ public class GridSpringResourceInjectionSelfTest extends GridCommonAbstractTest /** * @throws Exception If failed. */ - public void testClosureField() throws Exception { + public void testClosureFieldByResourceName() throws Exception { grid.compute().call(new IgniteCallable<Object>() { @SpringResource(resourceName = DUMMY_BEAN) - private transient DummyResourceBean dummyResourceBean; + private transient DummyResourceBean dummyRsrcBean; @Override public Object call() throws Exception { - assertNotNull(dummyResourceBean); + assertNotNull(dummyRsrcBean); return null; } @@ -63,81 +68,315 @@ public class GridSpringResourceInjectionSelfTest extends GridCommonAbstractTest } /** + * @throws Exception If failed. + */ + public void testClosureFieldByResourceClass() throws Exception { + grid.compute().call(new IgniteCallable<Object>() { + @SpringResource(resourceClass = DummyResourceBean.class) + private transient DummyResourceBean dummyRsrcBean; + + @Override public Object call() throws Exception { + assertNotNull(dummyRsrcBean); + + return null; + } + }); + } + + /** + * @throws Exception If failed. + */ + public void testClosureFieldByResourceClassWithMultipleBeans() throws Exception { + IgniteConfiguration anotherCfg = new IgniteConfiguration(); + anotherCfg.setGridName("anotherGrid"); + + Ignite anotherGrid = IgniteSpring.start(anotherCfg, new ClassPathXmlApplicationContext( + "/org/apache/ignite/internal/processors/resource/spring-resource-with-duplicate-beans.xml")); + + Throwable err = assertError(new IgniteCallable<Object>() { + @SpringResource(resourceClass = DummyResourceBean.class) + private transient DummyResourceBean dummyRsrcBean; + + @Override public Object call() throws Exception { + assertNotNull(dummyRsrcBean); + + return null; + } + }, anotherGrid, null); + + assertTrue("Unexpected message: " + err.getMessage(), err.getMessage().startsWith("No qualifying bean of type " + + "[org.apache.ignite.internal.processors.resource.GridSpringResourceInjectionSelfTest$DummyResourceBean]" + + " is defined: expected single matching bean but found 2:")); + + G.stop("anotherGrid", false); + } + + /** * Resource injection with non-existing resource name. */ - public void testClosureFieldWithWrongResourceName() throws Exception { - try { - grid.compute().call(new IgniteCallable<Object>() { - @SpringResource(resourceName = "") - private transient DummyResourceBean dummyResourceBean; + public void testClosureFieldWithWrongResourceName() { + assertError(new IgniteCallable<Object>() { + @SpringResource(resourceName = "nonExistentResource") + private transient DummyResourceBean dummyRsrcBean; - @Override public Object call() throws Exception { - assertNull(dummyResourceBean); + @Override public Object call() throws Exception { + assertNull(dummyRsrcBean); - return null; - } - }); - } - catch (IgniteException e) { - if (e.getMessage().contains("No bean named '' is defined")) - return; - } + return null; + } + }, "No bean named 'nonExistentResource' is defined"); + } + + /** + * Resource injection with non-existing resource class. + */ + public void testClosureFieldWithWrongResourceClass() { + assertError(new IgniteCallable<Object>() { + @SpringResource(resourceClass = AnotherDummyResourceBean.class) + private transient AnotherDummyResourceBean dummyRsrcBean; + + @Override public Object call() throws Exception { + assertNull(dummyRsrcBean); + + return null; + } + }, "No qualifying bean of type [org.apache.ignite.internal.processors.resource." + + "GridSpringResourceInjectionSelfTest$AnotherDummyResourceBean] is defined"); + } - fail(); + /** + * Resource injection with both resource and class set (ambiguity). + */ + public void testClosureFieldByResourceClassAndName() { + assertError(new IgniteCallable<Object>() { + @SpringResource(resourceClass = DummyResourceBean.class, resourceName = DUMMY_BEAN) + private transient DummyResourceBean dummyRsrcBean; + + @Override public Object call() throws Exception { + assertNull(dummyRsrcBean); + + return null; + } + }, "Either bean name or its class must be specified in @SpringResource, but not both"); + } + + /** + * Resource injection with no name and class set. + */ + public void testClosureFieldWithNoParams() { + assertError(new IgniteCallable<Object>() { + @SpringResource + private transient DummyResourceBean dummyRsrcBean; + + @Override public Object call() throws Exception { + assertNull(dummyRsrcBean); + + return null; + } + }, "Either bean name or its class must be specified in @SpringResource, but not both"); } /** * @throws Exception If failed. */ - public void testClosureMethod() throws Exception { + public void testClosureMethodWithResourceName() throws Exception { grid.compute().call(new IgniteCallable<Object>() { - private DummyResourceBean dummyResourceBean; + private DummyResourceBean dummyRsrcBean; @SpringResource(resourceName = DUMMY_BEAN) - private void setDummyResourceBean(DummyResourceBean dummyResourceBean) { - assertNotNull(dummyResourceBean); + private void setDummyResourceBean(DummyResourceBean dummyRsrcBean) { + assertNotNull(dummyRsrcBean); - this.dummyResourceBean = dummyResourceBean; + this.dummyRsrcBean = dummyRsrcBean; } @Override public Object call() throws Exception { + assertNotNull(dummyRsrcBean); + return null; } }); } /** - * Resource injection with non-existing resource name. + * @throws Exception If failed. */ - public void testClosureMethodWithWrongResourceName() throws Exception { + public void testClosureMethodWithResourceClass() throws Exception { + grid.compute().call(new IgniteCallable<Object>() { + private DummyResourceBean dummyRsrcBean; + + @SpringResource(resourceClass = DummyResourceBean.class) + private void setDummyResourceBean(DummyResourceBean dummyRsrcBean) { + assertNotNull(dummyRsrcBean); + + this.dummyRsrcBean = dummyRsrcBean; + } + + @Override public Object call() throws Exception { + assertNotNull(dummyRsrcBean); + + return null; + } + }); + } + + /** + * @throws Exception If failed. + */ + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") + public void testClosureMethodWithResourceClassWithMultipleBeans() throws Exception { + IgniteConfiguration anotherCfg = new IgniteConfiguration(); + anotherCfg.setGridName("anotherGrid"); + + Ignite anotherGrid = IgniteSpring.start(anotherCfg, new ClassPathXmlApplicationContext( + "/org/apache/ignite/internal/processors/resource/spring-resource-with-duplicate-beans.xml")); + try { - grid.compute().call(new IgniteCallable<Object>() { - private DummyResourceBean dummyResourceBean; + Throwable err = assertError(new IgniteCallable<Object>() { + private DummyResourceBean dummyRsrcBean; + + @SpringResource(resourceClass = DummyResourceBean.class) + private void setDummyResourceBean(DummyResourceBean dummyRsrcBean) { + assertNotNull(dummyRsrcBean); - @SpringResource(resourceName = "") - private void setDummyResourceBean(DummyResourceBean dummyResourceBean) { + this.dummyRsrcBean = dummyRsrcBean; } @Override public Object call() throws Exception { - assertNull(dummyResourceBean); + assertNotNull(dummyRsrcBean); return null; } - }); + }, anotherGrid, null); + + assertTrue("Unexpected message: " + err.getMessage(), err.getMessage().startsWith("No qualifying bean of type " + + "[org.apache.ignite.internal.processors.resource.GridSpringResourceInjectionSelfTest$DummyResourceBean]" + + " is defined: expected single matching bean but found 2:")); } - catch (IgniteException e) { - if (e.getMessage().contains("No bean named '' is defined")) - return; + finally { + G.stop("anotherGrid", false); } + } + + /** + * Resource injection with non-existing resource name. + */ + public void testClosureMethodWithWrongResourceName() { + assertError(new IgniteCallable<Object>() { + private DummyResourceBean dummyRsrcBean; - fail(); + @SpringResource(resourceName = "nonExistentResource") + private void setDummyResourceBean(DummyResourceBean dummyRsrcBean) { + // No-op. + } + + @Override public Object call() throws Exception { + assertNull(dummyRsrcBean); + + return null; + } + }, "No bean named 'nonExistentResource' is defined"); + } + + /** + * Resource injection with non-existing resource class. + */ + public void testClosureMethodWithWrongResourceClass() { + assertError(new IgniteCallable<Object>() { + private AnotherDummyResourceBean dummyRsrcBean; + + @SpringResource(resourceClass = AnotherDummyResourceBean.class) + private void setDummyResourceBean(AnotherDummyResourceBean dummyRsrcBean) { + // No-op. + } + + @Override public Object call() throws Exception { + assertNull(dummyRsrcBean); + + return null; + } + }, "No qualifying bean of type [org.apache.ignite.internal.processors.resource" + + ".GridSpringResourceInjectionSelfTest$AnotherDummyResourceBean] is defined"); + } + + /** + * Resource injection with both resource and class set (ambiguity). + */ + public void testClosureMethodByResourceClassAndName() { + assertError(new IgniteCallable<Object>() { + @SpringResource(resourceClass = DummyResourceBean.class, resourceName = DUMMY_BEAN) + private transient DummyResourceBean dummyRsrcBean; + + @Override public Object call() throws Exception { + assertNull(dummyRsrcBean); + + return null; + } + }, "Either bean name or its class must be specified in @SpringResource, but not both"); + } + + /** + * Resource injection with no params. + */ + public void testClosureMethodWithNoParams() { + assertError(new IgniteCallable<Object>() { + @SpringResource + private transient DummyResourceBean dummyRsrcBean; + + @Override public Object call() throws Exception { + assertNull(dummyRsrcBean); + + return null; + } + }, "Either bean name or its class must be specified in @SpringResource, but not both"); + } + + /** + * @param job {@link IgniteCallable} to be run + * @param grid Node. + * @param expEMsg Message that {@link IgniteException} thrown from <tt>job</tt> should bear + * @return Thrown error. + */ + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") + private Throwable assertError(final IgniteCallable<?> job, final Ignite grid, String expEMsg) { + return GridTestUtils.assertThrows(log, new Callable<Object>() { + @Override public Object call() throws Exception { + grid.compute(grid.cluster().forLocal()).call(job); + return null; + } + }, IgniteException.class, expEMsg); + } + + /** + * @param job {@link IgniteCallable} to be run + * @param expEMsg Message that {@link IgniteException} thrown from <tt>job</tt> should bear + */ + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") + private void assertError(final IgniteCallable<?> job, String expEMsg) { + assertError(job, grid, expEMsg); } /** * Dummy resource bean. */ public static class DummyResourceBean { + /** + * + */ public DummyResourceBean() { + // No-op. + } + } + + /** + * Another dummy resource bean. + */ + private static class AnotherDummyResourceBean { + /** + * + */ + public AnotherDummyResourceBean() { + // No-op. } } } http://git-wip-us.apache.org/repos/asf/ignite/blob/d59e5f56/modules/spring/src/test/java/org/apache/ignite/internal/processors/resource/spring-resource-with-duplicate-beans.xml ---------------------------------------------------------------------- diff --git a/modules/spring/src/test/java/org/apache/ignite/internal/processors/resource/spring-resource-with-duplicate-beans.xml b/modules/spring/src/test/java/org/apache/ignite/internal/processors/resource/spring-resource-with-duplicate-beans.xml new file mode 100644 index 0000000..98a94f8 --- /dev/null +++ b/modules/spring/src/test/java/org/apache/ignite/internal/processors/resource/spring-resource-with-duplicate-beans.xml @@ -0,0 +1,30 @@ +<?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-3.0.xsd"> + + <bean id="dummyBean1" + class="org.apache.ignite.internal.processors.resource.GridSpringResourceInjectionSelfTest$DummyResourceBean"> + </bean> + <bean id="dummyBean2" + class="org.apache.ignite.internal.processors.resource.GridSpringResourceInjectionSelfTest$DummyResourceBean"> + </bean> +</beans> http://git-wip-us.apache.org/repos/asf/ignite/blob/d59e5f56/modules/spring/src/test/java/org/apache/ignite/internal/processors/resource/spring-resource.xml ---------------------------------------------------------------------- diff --git a/modules/spring/src/test/java/org/apache/ignite/internal/processors/resource/spring-resource.xml b/modules/spring/src/test/java/org/apache/ignite/internal/processors/resource/spring-resource.xml index 3abb521..6baf116 100644 --- a/modules/spring/src/test/java/org/apache/ignite/internal/processors/resource/spring-resource.xml +++ b/modules/spring/src/test/java/org/apache/ignite/internal/processors/resource/spring-resource.xml @@ -19,7 +19,7 @@ <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-3.0.xsd"> + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="dummyResourceBean" class="org.apache.ignite.internal.processors.resource.GridSpringResourceInjectionSelfTest$DummyResourceBean">
