You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ni...@apache.org on 2021/11/09 15:14:10 UTC
[ignite] branch master updated: IGNITE-15801 Annotation-based
injection of ServiceContext. (#9542)
This is an automated email from the ASF dual-hosted git repository.
nizhikov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 7785089 IGNITE-15801 Annotation-based injection of ServiceContext. (#9542)
7785089 is described below
commit 77850899d7326dd630e2d0ef6624da1e38b5d996
Author: Pavel Pereslegin <xx...@gmail.com>
AuthorDate: Tue Nov 9 18:13:38 2021 +0300
IGNITE-15801 Annotation-based injection of ServiceContext. (#9542)
---
docs/_docs/services/services.adoc | 6 +-
.../examples/servicegrid/SimpleMapServiceImpl.java | 11 +-
.../processors/resource/GridResourceIoc.java | 6 +-
.../processors/resource/GridResourceProcessor.java | 7 +-
.../processors/service/GridServiceProcessor.java | 10 +-
.../processors/service/IgniteServiceProcessor.java | 10 +-
.../ignite/resources/ServiceContextResource.java | 65 ++++++
.../java/org/apache/ignite/services/Service.java | 75 ++++++-
.../org/apache/ignite/services/ServiceContext.java | 8 +-
.../GridServiceContextInjectionSelfTest.java | 246 +++++++++++++++++++++
.../testsuites/IgniteResourceSelfTestSuite.java | 2 +
11 files changed, 418 insertions(+), 28 deletions(-)
diff --git a/docs/_docs/services/services.adoc b/docs/_docs/services/services.adoc
index 1823473..dfdf114 100644
--- a/docs/_docs/services/services.adoc
+++ b/docs/_docs/services/services.adoc
@@ -45,9 +45,9 @@ Refer to a service example implementation in the Apache Ignite link:{githubUrl}/
A service implements the javadoc:org.apache.ignite.services.Service[Service] interface.
The `Service` interface has three methods:
-* `init(ServiceContext)`: this method is called by Ignite before the service is deployed (and before the `execute()` method is called)
-* `execute(ServiceContext)`: starts execution of the service
-* `cancel(ServiceContext)`: cancels service execution
+* `init()`: this method is called by Ignite before the service is deployed (and before the `execute()` method is called)
+* `execute()`: starts execution of the service
+* `cancel()`: cancels service execution
//The service must be available in the classpash of all server nodes. *TODO: deployment options*
diff --git a/examples/src/main/java/org/apache/ignite/examples/servicegrid/SimpleMapServiceImpl.java b/examples/src/main/java/org/apache/ignite/examples/servicegrid/SimpleMapServiceImpl.java
index 7ede239..1ac27b1 100644
--- a/examples/src/main/java/org/apache/ignite/examples/servicegrid/SimpleMapServiceImpl.java
+++ b/examples/src/main/java/org/apache/ignite/examples/servicegrid/SimpleMapServiceImpl.java
@@ -21,6 +21,7 @@ import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.resources.IgniteInstanceResource;
+import org.apache.ignite.resources.ServiceContextResource;
import org.apache.ignite.services.Service;
import org.apache.ignite.services.ServiceContext;
@@ -36,6 +37,10 @@ public class SimpleMapServiceImpl<K, V> implements Service, SimpleMapService<K,
@IgniteInstanceResource
private Ignite ignite;
+ /** Service context. */
+ @ServiceContextResource
+ private ServiceContext ctx;
+
/** Underlying cache map. */
private IgniteCache<K, V> cache;
@@ -60,14 +65,14 @@ public class SimpleMapServiceImpl<K, V> implements Service, SimpleMapService<K,
}
/** {@inheritDoc} */
- @Override public void cancel(ServiceContext ctx) {
+ @Override public void cancel() {
ignite.destroyCache(ctx.name());
System.out.println("Service was cancelled: " + ctx.name());
}
/** {@inheritDoc} */
- @Override public void init(ServiceContext ctx) throws Exception {
+ @Override public void init() {
// Create a new cache for every service deployment.
// Note that we use service name as cache name, which allows
// for each service deployment to use its own isolated cache.
@@ -77,7 +82,7 @@ public class SimpleMapServiceImpl<K, V> implements Service, SimpleMapService<K,
}
/** {@inheritDoc} */
- @Override public void execute(ServiceContext ctx) throws Exception {
+ @Override public void execute() throws Exception {
System.out.println("Executing distributed service: " + ctx.name());
}
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceIoc.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceIoc.java
index 9bc47fb..e748be6 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceIoc.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceIoc.java
@@ -42,6 +42,7 @@ import org.apache.ignite.resources.IgniteInstanceResource;
import org.apache.ignite.resources.JobContextResource;
import org.apache.ignite.resources.LoadBalancerResource;
import org.apache.ignite.resources.LoggerResource;
+import org.apache.ignite.resources.ServiceContextResource;
import org.apache.ignite.resources.ServiceResource;
import org.apache.ignite.resources.SpringApplicationContextResource;
import org.apache.ignite.resources.SpringResource;
@@ -506,7 +507,10 @@ public class GridResourceIoc {
JOB_CONTEXT(JobContextResource.class),
/** */
- CACHE_STORE_SESSION(CacheStoreSessionResource.class);
+ CACHE_STORE_SESSION(CacheStoreSessionResource.class),
+
+ /** */
+ SERVICE_CONTEXT(ServiceContextResource.class);
/** */
public final Class<? extends Annotation> clazz;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceProcessor.java
index 891eeb1..e3c16e7 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceProcessor.java
@@ -39,6 +39,7 @@ import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.lifecycle.LifecycleBean;
import org.apache.ignite.services.Service;
+import org.apache.ignite.services.ServiceContext;
import org.apache.ignite.spi.IgniteSpi;
import org.jetbrains.annotations.Nullable;
@@ -329,6 +330,7 @@ public class GridResourceProcessor extends GridProcessorAdapter {
case LOAD_BALANCER:
case TASK_CONTINUOUS_MAPPER:
case CACHE_STORE_SESSION:
+ case SERVICE_CONTEXT:
res = new GridResourceBasicInjector<>(param);
break;
@@ -514,10 +516,13 @@ public class GridResourceProcessor extends GridProcessorAdapter {
* Injects resources into service.
*
* @param svc Service to inject.
+ * @param svcCtx Service context to be injected into the service.
* @throws IgniteCheckedException If failed.
*/
- public void inject(Service svc) throws IgniteCheckedException {
+ public void inject(Service svc, ServiceContext svcCtx) throws IgniteCheckedException {
injectGeneric(svc);
+
+ inject(svc, GridResourceIoc.ResourceAnnotation.SERVICE_CONTEXT, null, null, svcCtx);
}
/**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProcessor.java
index 4836991..13b1f02 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProcessor.java
@@ -103,6 +103,7 @@ import org.apache.ignite.resources.JobContextResource;
import org.apache.ignite.resources.LoggerResource;
import org.apache.ignite.services.Service;
import org.apache.ignite.services.ServiceConfiguration;
+import org.apache.ignite.services.ServiceContext;
import org.apache.ignite.services.ServiceDeploymentException;
import org.apache.ignite.services.ServiceDescriptor;
import org.apache.ignite.thread.IgniteThreadFactory;
@@ -1298,7 +1299,7 @@ public class GridServiceProcessor extends ServiceProcessorAdapter implements Ign
final Service svc;
try {
- svc = copyAndInject(assigns.configuration());
+ svc = copyAndInject(assigns.configuration(), svcCtx);
// Initialize service.
svc.init(svcCtx);
@@ -1368,10 +1369,11 @@ public class GridServiceProcessor extends ServiceProcessorAdapter implements Ign
/**
* @param cfg Service configuration.
+ * @param svcCtx Service context to be injected into the service.
* @return Copy of service.
* @throws IgniteCheckedException If failed.
*/
- private Service copyAndInject(ServiceConfiguration cfg) throws IgniteCheckedException {
+ private Service copyAndInject(ServiceConfiguration cfg, ServiceContext svcCtx) throws IgniteCheckedException {
Marshaller m = ctx.config().getMarshaller();
if (cfg instanceof LazyServiceConfiguration) {
@@ -1379,7 +1381,7 @@ public class GridServiceProcessor extends ServiceProcessorAdapter implements Ign
Service srvc = U.unmarshal(m, bytes, U.resolveClassLoader(null, ctx.config()));
- ctx.resource().inject(srvc);
+ ctx.resource().inject(srvc, svcCtx);
return srvc;
}
@@ -1391,7 +1393,7 @@ public class GridServiceProcessor extends ServiceProcessorAdapter implements Ign
Service cp = U.unmarshal(m, bytes, U.resolveClassLoader(svc.getClass().getClassLoader(), ctx.config()));
- ctx.resource().inject(cp);
+ ctx.resource().inject(cp, svcCtx);
return cp;
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java
index e3ad553..c353460 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java
@@ -79,6 +79,7 @@ import org.apache.ignite.marshaller.jdk.JdkMarshaller;
import org.apache.ignite.plugin.security.SecurityPermission;
import org.apache.ignite.services.Service;
import org.apache.ignite.services.ServiceConfiguration;
+import org.apache.ignite.services.ServiceContext;
import org.apache.ignite.services.ServiceDeploymentException;
import org.apache.ignite.services.ServiceDescriptor;
import org.apache.ignite.spi.communication.CommunicationSpi;
@@ -1188,7 +1189,7 @@ public class IgniteServiceProcessor extends ServiceProcessorAdapter implements I
final Service srvc;
try {
- srvc = copyAndInject(cfg);
+ srvc = copyAndInject(cfg, srvcCtx);
// Initialize service.
srvc.init(srvcCtx);
@@ -1253,10 +1254,11 @@ public class IgniteServiceProcessor extends ServiceProcessorAdapter implements I
/**
* @param cfg Service configuration.
+ * @param svcCtx Service context to be injected into the service.
* @return Copy of service.
* @throws IgniteCheckedException If failed.
*/
- private Service copyAndInject(ServiceConfiguration cfg) throws IgniteCheckedException {
+ private Service copyAndInject(ServiceConfiguration cfg, ServiceContext svcCtx) throws IgniteCheckedException {
if (cfg instanceof LazyServiceConfiguration) {
LazyServiceConfiguration srvcCfg = (LazyServiceConfiguration)cfg;
@@ -1267,7 +1269,7 @@ public class IgniteServiceProcessor extends ServiceProcessorAdapter implements I
Service srvc = U.unmarshal(marsh, bytes,
U.resolveClassLoader(srvcDep != null ? srvcDep.classLoader() : null, ctx.config()));
- ctx.resource().inject(srvc);
+ ctx.resource().inject(srvc, svcCtx);
return srvc;
}
@@ -1279,7 +1281,7 @@ public class IgniteServiceProcessor extends ServiceProcessorAdapter implements I
Service cp = U.unmarshal(marsh, bytes, U.resolveClassLoader(srvc.getClass().getClassLoader(), ctx.config()));
- ctx.resource().inject(cp);
+ ctx.resource().inject(cp, svcCtx);
return cp;
}
diff --git a/modules/core/src/main/java/org/apache/ignite/resources/ServiceContextResource.java b/modules/core/src/main/java/org/apache/ignite/resources/ServiceContextResource.java
new file mode 100644
index 0000000..564e538
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/resources/ServiceContextResource.java
@@ -0,0 +1,65 @@
+/*
+ * 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.resources;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.ignite.services.Service;
+import org.apache.ignite.services.ServiceContext;
+
+/**
+ * Annotates a field or a setter method for injecting a {@link ServiceContext service context} into a {@link Service
+ * service} instance.
+ * <p>
+ * It is guaranteed that context will be injected before calling the {@link Service#init()} method.
+ * <p>
+ * Here is how injection would typically happen:
+ * <pre>{@code
+ * public class MyServiceImpl implements MyService {
+ * ...
+ * @ServiceContextResource
+ * private ServiceContext ctx;
+ * ...
+ * }
+ * }</pre>
+ * or attach the same annotation to the method:
+ * <pre>{@code
+ * public class MyServiceImpl implements MyService {
+ * ...
+ * private ServiceContext ctx;
+ * ...
+ * @ServiceContextResource
+ * public void setServiceContext(ServiceContext ctx) {
+ * this.ctx = ctx;
+ * }
+ * ...
+ * }
+ * }</pre>
+ *
+ * @see Service
+ * @see ServiceContext
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.FIELD})
+public @interface ServiceContextResource {
+ // No-op.
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/services/Service.java b/modules/core/src/main/java/org/apache/ignite/services/Service.java
index e82f6c3..3d8838f 100644
--- a/modules/core/src/main/java/org/apache/ignite/services/Service.java
+++ b/modules/core/src/main/java/org/apache/ignite/services/Service.java
@@ -18,6 +18,7 @@
package org.apache.ignite.services;
import java.io.Serializable;
+import org.apache.ignite.resources.ServiceContextResource;
/**
* An instance of grid-managed service. Grid-managed services may be deployed from
@@ -26,7 +27,7 @@ import java.io.Serializable;
* Whenever service is deployed, Ignite will automatically calculate how many
* instances of this service should be deployed on each node within the cluster.
* Whenever service is deployed on a cluster node, Ignite will call
- * {@link #execute(ServiceContext)} method on that service. It is up to the user
+ * {@link #execute()} method on that service. It is up to the user
* to control whenever the service should exit from the {@code execute} method.
* For example, user may choose to implement service as follows:
* <pre name="code" class="java">
@@ -36,12 +37,15 @@ import java.io.Serializable;
* // You should inject resources only as needed.
* @IgniteInstanceResource
* private Ignite ignite;
+ *
+ * @ServiceContextResource
+ * private ServiceContext ctx;
* ...
- * @Override public void cancel(ServiceContext ctx) {
+ * @Override public void cancel() {
* // No-op.
* }
*
- * @Override public void execute(ServiceContext ctx) {
+ * @Override public void execute() {
* // Loop until service is cancelled.
* while (!ctx.isCancelled()) {
* // Do something.
@@ -76,10 +80,10 @@ import java.io.Serializable;
* <h1 class="header">Cancellation</h1>
* Services can be cancelled by calling any of the {@code cancel} methods on {@link org.apache.ignite.IgniteServices} API.
* Whenever a deployed service is cancelled, Ignite will automatically call
- * {@link Service#cancel(ServiceContext)} method on that service.
+ * {@link #cancel()} method on that service.
* <p>
- * Note that Ignite cannot guarantee that the service exits from {@link Service#execute(ServiceContext)}
- * method whenever {@link #cancel(ServiceContext)} is called. It is up to the user to
+ * Note that Ignite cannot guarantee that the service exits from {@link #execute()}
+ * method whenever {@link #cancel()} is called. It is up to the user to
* make sure that the service code properly reacts to cancellations.
*/
public interface Service extends Serializable {
@@ -87,13 +91,58 @@ public interface Service extends Serializable {
* Cancels this service. Ignite will automatically call this method whenever any of the
* {@code cancel} methods on {@link org.apache.ignite.IgniteServices} API are called.
* <p>
+ * Note that Ignite cannot guarantee that the service exits from {@link #execute()}
+ * method whenever {@code cancel()} method is called. It is up to the user to
+ * make sure that the service code properly reacts to cancellations.
+ *
+ * @see ServiceContextResource
+ */
+ public default void cancel() {
+ // No-op.
+ }
+
+ /**
+ * Pre-initializes service before execution. This method is guaranteed to be called before
+ * service deployment is complete (this guarantees that this method will be called
+ * before method {@link #execute()} is called).
+ *
+ * @see ServiceContextResource
+ * @throws Exception If service initialization failed.
+ */
+ public default void init() throws Exception {
+ // No-op.
+ }
+
+ /**
+ * Starts execution of this service. This method is automatically invoked whenever an instance of the service
+ * is deployed on a grid node. Note that service is considered deployed even after it exits the {@code execute}
+ * method and can be cancelled (or undeployed) only by calling any of the {@code cancel} methods on
+ * {@link org.apache.ignite.IgniteServices} API. Also note that service is not required to exit from {@code execute} method until
+ * {@link #cancel()} method was called.
+ *
+ * @see ServiceContextResource
+ * @throws Exception If service execution failed. Not that service will still remain deployed, until
+ * {@link org.apache.ignite.IgniteServices#cancel(String)} method will be called.
+ */
+ public default void execute() throws Exception {
+ // No-op.
+ }
+
+ /**
+ * Cancels this service. Ignite will automatically call this method whenever any of the
+ * {@code cancel} methods on {@link org.apache.ignite.IgniteServices} API are called.
+ * <p>
* Note that Ignite cannot guarantee that the service exits from {@link #execute(ServiceContext)}
* method whenever {@code cancel(ServiceContext)} method is called. It is up to the user to
* make sure that the service code properly reacts to cancellations.
*
* @param ctx Service execution context.
+ * @deprecated Use {@link #cancel()} instead.
*/
- public void cancel(ServiceContext ctx);
+ @Deprecated
+ public default void cancel(ServiceContext ctx) {
+ cancel();
+ }
/**
* Pre-initializes service before execution. This method is guaranteed to be called before
@@ -102,8 +151,12 @@ public interface Service extends Serializable {
*
* @param ctx Service execution context.
* @throws Exception If service initialization failed.
+ * @deprecated Use {@link #init()} instead.
*/
- public void init(ServiceContext ctx) throws Exception;
+ @Deprecated
+ public default void init(ServiceContext ctx) throws Exception {
+ init();
+ }
/**
* Starts execution of this service. This method is automatically invoked whenever an instance of the service
@@ -115,6 +168,10 @@ public interface Service extends Serializable {
* @param ctx Service execution context.
* @throws Exception If service execution failed. Not that service will still remain deployed, until
* {@link org.apache.ignite.IgniteServices#cancel(String)} method will be called.
+ * @deprecated Use {@link #execute()} instead.
*/
- public void execute(ServiceContext ctx) throws Exception;
+ @Deprecated
+ public default void execute(ServiceContext ctx) throws Exception {
+ execute();
+ }
}
diff --git a/modules/core/src/main/java/org/apache/ignite/services/ServiceContext.java b/modules/core/src/main/java/org/apache/ignite/services/ServiceContext.java
index 1ba64a9..bebf77a 100644
--- a/modules/core/src/main/java/org/apache/ignite/services/ServiceContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/services/ServiceContext.java
@@ -19,12 +19,14 @@ package org.apache.ignite.services;
import java.io.Serializable;
import java.util.UUID;
+import org.apache.ignite.resources.ServiceContextResource;
import org.jetbrains.annotations.Nullable;
/**
- * Service execution context. Execution context is provided into {@link Service#execute(ServiceContext)}
- * and {@link Service#cancel(ServiceContext)} methods and contains information about specific service
- * execution.
+ * Service execution context. This context is provided using {@link ServiceContextResource} annotation and contains
+ * information about specific service execution.
+ *
+ * @see ServiceContextResource
*/
public interface ServiceContext extends Serializable {
/**
diff --git a/modules/spring/src/test/java/org/apache/ignite/internal/processors/resource/GridServiceContextInjectionSelfTest.java b/modules/spring/src/test/java/org/apache/ignite/internal/processors/resource/GridServiceContextInjectionSelfTest.java
new file mode 100644
index 0000000..3281366
--- /dev/null
+++ b/modules/spring/src/test/java/org/apache/ignite/internal/processors/resource/GridServiceContextInjectionSelfTest.java
@@ -0,0 +1,246 @@
+/*
+ * 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.internal.processors.resource;
+
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.ignite.internal.IgniteInterruptedCheckedException;
+import org.apache.ignite.resources.ServiceContextResource;
+import org.apache.ignite.services.Service;
+import org.apache.ignite.services.ServiceContext;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+
+/**
+ * Tests for injected service context.
+ */
+public class GridServiceContextInjectionSelfTest extends GridCommonAbstractTest {
+ /** Service name. */
+ private static final String DUMMY_SERVICE = "dummy";
+
+ /** Compatibiity service name. */
+ private static final String COMPATIBILITY_SERVICE = "compatibility";
+
+ /** Error handler. */
+ private static final ErrorHandler errHnd = new ErrorHandler();
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ super.beforeTestsStarted();
+
+ startGrids(2);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ grid(0).context().service().cancelAll();
+
+ errHnd.reset();
+ }
+
+ /**
+ * Checks that the context is correctly injected into the service.
+ */
+ @Test
+ public void testInjection() {
+ grid(0).services().deployClusterSingleton(DUMMY_SERVICE, new DummyServiceImpl());
+
+ doLifeCycleTest(DUMMY_SERVICE);
+ }
+
+ /**
+ * Ensures that the injected context instance is the same as obtained in legacy methods.
+ */
+ @Test
+ public void testCompatibility() {
+ grid(0).services().deployClusterSingleton(COMPATIBILITY_SERVICE, new CompatibilityServiceImpl());
+
+ doLifeCycleTest(COMPATIBILITY_SERVICE);
+ }
+
+ /**
+ * @param svcName Service name.
+ */
+ private void doLifeCycleTest(String svcName) {
+ DummyService svc = grid(1).services().serviceProxy(svcName, DummyService.class, false);
+
+ svc.method();
+
+ grid(0).services().cancel(svcName);
+
+ errHnd.validate();
+ }
+
+ /**
+ * Dummy service.
+ */
+ public interface DummyService {
+ /** */
+ public void method();
+ }
+
+ /** */
+ public static class DummyServiceImpl implements DummyService, Service {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Service context. */
+ @ServiceContextResource
+ private ServiceContext ctxField;
+
+ /** Service context. */
+ private ServiceContext ctxSetter;
+
+ /** Service initialized flag. */
+ private volatile boolean initialized;
+
+ /** Service executed flag. */
+ private volatile boolean executed;
+
+ /** Service canceled flag. */
+ private volatile boolean canceled;
+
+ /**
+ * @param ctx Service context.
+ */
+ @ServiceContextResource
+ private void serviceContext(ServiceContext ctx) {
+ ctxSetter = ctx;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void init() {
+ errHnd.ensure(ctxSetter != null);
+ errHnd.ensure(ctxSetter == ctxField);
+
+ errHnd.ensure(!initialized);
+ errHnd.ensure(!executed);
+ errHnd.ensure(!canceled);
+
+ initialized = true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void execute() {
+ errHnd.ensure(ctxSetter != null);
+ errHnd.ensure(ctxSetter == ctxField);
+
+ errHnd.ensure(initialized);
+ errHnd.ensure(!executed);
+ errHnd.ensure(!canceled);
+
+ executed = true;
+
+ try {
+ GridTestUtils.waitForCondition(() -> ctxSetter.isCancelled(), GridTestUtils.DFLT_TEST_TIMEOUT);
+ } catch (IgniteInterruptedCheckedException ignore) {
+ // Execution interrupted when service is cancelled.
+ }
+
+ errHnd.ensure(ctxSetter.isCancelled());
+ errHnd.ensure(canceled);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void method() {
+ assertTrue(initialized);
+ assertTrue(executed);
+ assertFalse(canceled);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void cancel() {
+ errHnd.ensure(ctxSetter != null);
+ errHnd.ensure(ctxSetter == ctxField);
+
+ errHnd.ensure(initialized);
+ errHnd.ensure(executed);
+ errHnd.ensure(!canceled);
+
+ canceled = true;
+ }
+ }
+
+ /**
+ * Service for checking compatibility of the injected service context.
+ */
+ public static class CompatibilityServiceImpl implements DummyService, Service {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Service context. */
+ @ServiceContextResource
+ private ServiceContext ctx;
+
+ /** {@inheritDoc} */
+ @Override public void cancel(ServiceContext ctx) {
+ errHnd.ensure(ctx == this.ctx);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void init(ServiceContext ctx) {
+ errHnd.ensure(ctx == this.ctx);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void execute(ServiceContext ctx) {
+ errHnd.ensure(ctx == this.ctx);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void method() {
+ // No-op.
+ }
+ }
+
+ /** */
+ private static class ErrorHandler {
+ /** Error reference. */
+ private final AtomicReference<AssertionError> errRef = new AtomicReference<>();
+
+ /**
+ * @param condition Assertion condition to check.
+ */
+ void ensure(boolean condition) {
+ if (!condition) {
+ AssertionError err = new AssertionError();
+
+ errRef.compareAndSet(null, err);
+
+ throw err;
+ }
+ }
+
+ /**
+ * @throws AssertionError If there was an error.
+ */
+ void validate() throws AssertionError {
+ Error err = errRef.get();
+
+ if (errRef.get() != null)
+ throw err;
+ }
+
+ /** */
+ void reset() {
+ errRef.set(null);
+ }
+ }
+}
diff --git a/modules/spring/src/test/java/org/apache/ignite/testsuites/IgniteResourceSelfTestSuite.java b/modules/spring/src/test/java/org/apache/ignite/testsuites/IgniteResourceSelfTestSuite.java
index 0a9f82d..382a8f2 100644
--- a/modules/spring/src/test/java/org/apache/ignite/testsuites/IgniteResourceSelfTestSuite.java
+++ b/modules/spring/src/test/java/org/apache/ignite/testsuites/IgniteResourceSelfTestSuite.java
@@ -19,6 +19,7 @@ package org.apache.ignite.testsuites;
import org.apache.ignite.internal.processors.resource.GridLoggerInjectionSelfTest;
import org.apache.ignite.internal.processors.resource.GridResourceProcessorSelfTest;
+import org.apache.ignite.internal.processors.resource.GridServiceContextInjectionSelfTest;
import org.apache.ignite.internal.processors.resource.GridServiceInjectionSelfTest;
import org.apache.ignite.internal.processors.resource.GridSpringResourceInjectionSelfTest;
import org.junit.runner.RunWith;
@@ -33,6 +34,7 @@ import org.junit.runners.Suite;
GridLoggerInjectionSelfTest.class,
GridServiceInjectionSelfTest.class,
GridSpringResourceInjectionSelfTest.class,
+ GridServiceContextInjectionSelfTest.class,
})
public class IgniteResourceSelfTestSuite {
}