You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by al...@apache.org on 2020/05/22 22:45:21 UTC
[ignite] branch master updated: IGNITE-10100 Implement calling .NET
service from java - Fixes #7751.
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.git
The following commit(s) were added to refs/heads/master by this push:
new 5e3cadc IGNITE-10100 Implement calling .NET service from java - Fixes #7751.
5e3cadc is described below
commit 5e3cadc9d4cf4a6afcedef02112a9f7228ce78d2
Author: Ivan Daschinskiy <iv...@gmail.com>
AuthorDate: Sat May 23 01:40:48 2020 +0300
IGNITE-10100 Implement calling .NET service from java - Fixes #7751.
Signed-off-by: Aleksey Plekhanov <pl...@gmail.com>
---
.../platform/services/PlatformAbstractService.java | 8 +-
.../platform/services/PlatformService.java | 13 +
.../processors/platform/utils/PlatformUtils.java | 16 +-
.../processors/service/GridServiceProcessor.java | 10 +-
.../processors/service/GridServiceProxy.java | 81 ++++-
.../processors/service/IgniteServiceProcessor.java | 14 +-
.../ignite/platform/PlatformServiceMethod.java | 48 +++
.../platform/AbstractPlatformServiceCallTask.java | 278 +++++++++++++++++
.../PlatformServiceCallCollectionsTask.java | 93 ++++++
.../ignite/platform/PlatformServiceCallTask.java | 81 +++++
.../Apache.Ignite.Core.Tests.DotNetCore.csproj | 1 +
.../Apache.Ignite.Core.Tests.csproj | 1 +
.../Services/CallPlatformServiceTest.cs | 345 +++++++++++++++++++++
13 files changed, 966 insertions(+), 23 deletions(-)
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformAbstractService.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformAbstractService.java
index ea95f73..59d87cb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformAbstractService.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformAbstractService.java
@@ -181,6 +181,12 @@ public abstract class PlatformAbstractService implements PlatformService, Extern
/** {@inheritDoc} */
@Override public Object invokeMethod(String mthdName, boolean srvKeepBinary, Object[] args)
+ throws IgniteCheckedException {
+ return invokeMethod(mthdName, srvKeepBinary, false, args);
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object invokeMethod(String mthdName, boolean srvKeepBinary, boolean deserializeResult, Object[] args)
throws IgniteCheckedException {
assert ptr != 0;
assert platformCtx != null;
@@ -213,7 +219,7 @@ public abstract class PlatformAbstractService implements PlatformService, Extern
BinaryRawReaderEx reader = platformCtx.reader(in);
- return PlatformUtils.readInvocationResult(platformCtx, reader);
+ return PlatformUtils.readInvocationResult(platformCtx, reader, deserializeResult);
}
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformService.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformService.java
index 2d6e1cc..de0b142 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformService.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformService.java
@@ -36,6 +36,19 @@ public interface PlatformService extends Service {
public Object invokeMethod(String mthdName, boolean srvKeepBinary, Object[] args) throws IgniteCheckedException;
/**
+ * Invokes native service method.
+ *
+ * @param mthdName Method name.
+ * @param srvKeepBinary Server keep binary flag.
+ * @param args Arguments.
+ * @param deserializeResult If {@code true}, call service in cross-platform compatible manner.
+ * @return Resulting data.
+ * @throws org.apache.ignite.IgniteCheckedException If failed.
+ */
+ public Object invokeMethod(String mthdName, boolean srvKeepBinary, boolean deserializeResult, Object[] args)
+ throws IgniteCheckedException;
+
+ /**
* Gets native pointer.
*
* @return Native pointer.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java
index 47cc30b..1fa5fe1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java
@@ -833,12 +833,26 @@ public class PlatformUtils {
*/
public static Object readInvocationResult(PlatformContext ctx, BinaryRawReaderEx reader)
throws IgniteCheckedException {
+ return readInvocationResult(ctx, reader, false);
+ }
+
+ /**
+ * Reads invocation result (of a job/service/etc) using a common protocol.
+ *
+ * @param ctx Platform context.
+ * @param reader Reader.
+ * @param deserialize If {@code true} deserialize invocation result.
+ * @return Result.
+ * @throws IgniteCheckedException When invocation result is an error.
+ */
+ public static Object readInvocationResult(PlatformContext ctx, BinaryRawReaderEx reader, boolean deserialize)
+ throws IgniteCheckedException {
// 1. Read success flag.
boolean success = reader.readBoolean();
if (success)
// 2. Return result as is.
- return reader.readObjectDetached();
+ return deserialize ? reader.readObject() : reader.readObjectDetached();
else {
// 3. Read whether exception is in form of object or string.
boolean hasException = reader.readBoolean();
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 9b5f689..c0f624f 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
@@ -73,6 +73,7 @@ import org.apache.ignite.internal.processors.cache.query.CacheQuery;
import org.apache.ignite.internal.processors.cache.query.GridCacheQueryManager;
import org.apache.ignite.internal.processors.cluster.DiscoveryDataClusterState;
import org.apache.ignite.internal.processors.cluster.IgniteChangeGlobalStateSupport;
+import org.apache.ignite.internal.processors.platform.services.PlatformService;
import org.apache.ignite.internal.processors.task.GridInternal;
import org.apache.ignite.internal.processors.timeout.GridTimeoutObject;
import org.apache.ignite.internal.util.GridEmptyIterator;
@@ -1030,11 +1031,12 @@ public class GridServiceProcessor extends ServiceProcessorAdapter implements Ign
Service svc = ctx.service();
if (svc != null) {
- if (!srvcCls.isAssignableFrom(svc.getClass()))
+ if (srvcCls.isAssignableFrom(svc.getClass()))
+ return (T)svc;
+ else if (!PlatformService.class.isAssignableFrom(svc.getClass())) {
throw new IgniteException("Service does not implement specified interface [svcItf=" +
- srvcCls.getName() + ", svcCls=" + svc.getClass().getName() + ']');
-
- return (T)svc;
+ srvcCls.getName() + ", svcCls=" + svc.getClass().getName() + ']');
+ }
}
}
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProxy.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProxy.java
index ab0422d..64eada3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProxy.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProxy.java
@@ -34,7 +34,6 @@ import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicReference;
-import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteLogger;
@@ -45,12 +44,15 @@ import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
import org.apache.ignite.internal.managers.communication.GridIoPolicy;
+import org.apache.ignite.internal.processors.platform.PlatformNativeException;
+import org.apache.ignite.internal.processors.platform.services.PlatformService;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteCallable;
+import org.apache.ignite.platform.PlatformServiceMethod;
import org.apache.ignite.resources.IgniteInstanceResource;
import org.apache.ignite.services.Service;
@@ -63,6 +65,19 @@ public class GridServiceProxy<T> implements Serializable {
/** */
private static final long serialVersionUID = 0L;
+ /** */
+ private static final Method PLATFORM_SERVICE_INVOKE_METHOD;
+
+ static {
+ try {
+ PLATFORM_SERVICE_INVOKE_METHOD = PlatformService.class.getMethod("invokeMethod", String.class,
+ boolean.class, Object[].class);
+ }
+ catch (NoSuchMethodException e) {
+ throw new ExceptionInInitializerError("'invokeMethod' is not defined in " + PlatformService.class.getName());
+ }
+ }
+
/** Grid logger. */
@GridToStringExclude
private final IgniteLogger log;
@@ -113,7 +128,8 @@ public class GridServiceProxy<T> implements Serializable {
this.ctx = ctx;
this.name = name;
this.sticky = sticky;
- this.waitTimeout = timeout;
+
+ waitTimeout = timeout;
hasLocNode = hasLocalNode(prj);
log = ctx.log(getClass());
@@ -176,7 +192,7 @@ public class GridServiceProxy<T> implements Serializable {
Service svc = svcCtx.service();
if (svc != null)
- return mtd.invoke(svc, args);
+ return callServiceLocally(svc, mtd, args);
}
}
else {
@@ -185,7 +201,7 @@ public class GridServiceProxy<T> implements Serializable {
// Execute service remotely.
return ctx.closure().callAsyncNoFailover(
GridClosureCallMode.BROADCAST,
- new ServiceProxyCallable(mtd.getName(), name, mtd.getParameterTypes(), args),
+ new ServiceProxyCallable(methodName(mtd), name, mtd.getParameterTypes(), args),
Collections.singleton(node),
false,
waitTimeout,
@@ -245,6 +261,19 @@ public class GridServiceProxy<T> implements Serializable {
}
/**
+ * @param svc Service to be called.
+ * @param mtd Method to call.
+ * @param args Method args.
+ * @return Invocation result.
+ */
+ private Object callServiceLocally(Service svc, Method mtd, Object[] args) throws Exception {
+ if (svc instanceof PlatformService && !PLATFORM_SERVICE_INVOKE_METHOD.equals(mtd))
+ return ((PlatformService)svc).invokeMethod(methodName(mtd), false, true, args);
+ else
+ return mtd.invoke(svc, args);
+ }
+
+ /**
* @param sticky Whether multi-node request should be done.
* @param name Service name.
* @return Node with deployed service or {@code null} if there is no such node.
@@ -355,6 +384,15 @@ public class GridServiceProxy<T> implements Serializable {
}
/**
+ * @param mtd Method to invoke.
+ */
+ String methodName(Method mtd) {
+ PlatformServiceMethod ann = mtd.getDeclaredAnnotation(PlatformServiceMethod.class);
+
+ return ann == null ? mtd.getName() : ann.value();
+ }
+
+ /**
* Invocation handler for service proxy.
*/
private class ProxyInvocationHandler implements InvocationHandler {
@@ -379,14 +417,14 @@ public class GridServiceProxy<T> implements Serializable {
private String svcName;
/** Argument types. */
- private Class[] argTypes;
+ private Class<?>[] argTypes;
/** Args. */
private Object[] args;
/** Grid instance. */
@IgniteInstanceResource
- private transient Ignite ignite;
+ private transient IgniteEx ignite;
/**
* Empty constructor required for {@link Externalizable}.
@@ -401,7 +439,7 @@ public class GridServiceProxy<T> implements Serializable {
* @param argTypes Argument types.
* @param args Arguments for invocation.
*/
- private ServiceProxyCallable(String mtdName, String svcName, Class[] argTypes, Object[] args) {
+ private ServiceProxyCallable(String mtdName, String svcName, Class<?>[] argTypes, Object[] args) {
this.mtdName = mtdName;
this.svcName = svcName;
this.argTypes = argTypes;
@@ -410,20 +448,41 @@ public class GridServiceProxy<T> implements Serializable {
/** {@inheritDoc} */
@Override public Object call() throws Exception {
- ServiceContextImpl svcCtx = ((IgniteEx)ignite).context().service().serviceContext(svcName);
+ ServiceContextImpl ctx = ignite.context().service().serviceContext(svcName);
- if (svcCtx == null || svcCtx.service() == null)
+ if (ctx == null || ctx.service() == null)
throw new GridServiceNotFoundException(svcName);
GridServiceMethodReflectKey key = new GridServiceMethodReflectKey(mtdName, argTypes);
- Method mtd = svcCtx.method(key);
+ Method mtd = ctx.method(key);
+
+ if (ctx.service() instanceof PlatformService && mtd == null)
+ return callPlatformService((PlatformService)ctx.service());
+ else
+ return callService(ctx.service(), mtd);
+ }
+
+ /** */
+ private Object callPlatformService(PlatformService srv) {
+ try {
+ return srv.invokeMethod(mtdName, false, true, args);
+ }
+ catch (PlatformNativeException ne) {
+ throw new ServiceProxyException(U.convertException(ne));
+ }
+ catch (Exception e) {
+ throw new ServiceProxyException(e);
+ }
+ }
+ /** */
+ private Object callService(Service srv, Method mtd) throws Exception {
if (mtd == null)
throw new GridServiceMethodNotFoundException(svcName, mtdName, argTypes);
try {
- return mtd.invoke(svcCtx.service(), args);
+ return mtd.invoke(srv, args);
}
catch (InvocationTargetException e) {
throw new ServiceProxyException(e.getCause());
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 8fc0616..79aa579 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
@@ -62,7 +62,7 @@ import org.apache.ignite.internal.processors.cache.DynamicCacheChangeRequest;
import org.apache.ignite.internal.processors.cluster.ChangeGlobalStateMessage;
import org.apache.ignite.internal.processors.cluster.DiscoveryDataClusterState;
import org.apache.ignite.internal.processors.cluster.IgniteChangeGlobalStateSupport;
-import org.apache.ignite.spi.systemview.view.ServiceView;
+import org.apache.ignite.internal.processors.platform.services.PlatformService;
import org.apache.ignite.internal.util.future.GridCompoundFuture;
import org.apache.ignite.internal.util.future.GridFinishedFuture;
import org.apache.ignite.internal.util.future.GridFutureAdapter;
@@ -83,6 +83,7 @@ import org.apache.ignite.spi.communication.CommunicationSpi;
import org.apache.ignite.spi.discovery.DiscoveryDataBag;
import org.apache.ignite.spi.discovery.DiscoverySpi;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.systemview.view.ServiceView;
import org.apache.ignite.thread.IgniteThreadFactory;
import org.apache.ignite.thread.OomExceptionHandler;
import org.jetbrains.annotations.NotNull;
@@ -945,11 +946,12 @@ public class IgniteServiceProcessor extends ServiceProcessorAdapter implements I
Service srvc = ctx.service();
if (srvc != null) {
- if (!srvcCls.isAssignableFrom(srvc.getClass()))
- throw new IgniteException("Service does not implement specified interface [srvcCls=" +
- srvcCls.getName() + ", srvcCls=" + srvc.getClass().getName() + ']');
-
- return (T)srvc;
+ if (srvcCls.isAssignableFrom(srvc.getClass()))
+ return (T)srvc;
+ else if (!PlatformService.class.isAssignableFrom(srvc.getClass())) {
+ throw new IgniteException("Service does not implement specified interface [srvcCls="
+ + srvcCls.getName() + ", srvcCls=" + srvc.getClass().getName() + ']');
+ }
}
}
}
diff --git a/modules/core/src/main/java/org/apache/ignite/platform/PlatformServiceMethod.java b/modules/core/src/main/java/org/apache/ignite/platform/PlatformServiceMethod.java
new file mode 100644
index 0000000..7e90cb6
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/platform/PlatformServiceMethod.java
@@ -0,0 +1,48 @@
+/*
+ * 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.platform;
+
+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;
+
+/**
+ * Annotation for setting mapping between java interface's method and platform service's method.
+ * Platform service method name is defined in {@link PlatformServiceMethod#value}
+ * <p/>
+ * For example, this annotated java inerface method:
+ * <pre>
+ * @PlatformServiceMethod("SomeMethod")
+ * Object someMethod(Object[] args)
+ * </pre>
+ * will be mapped to {@code SomeMethod}, for example (.NET service):
+ * <pre>
+ * object SomeMethod(object[] args)
+ * </pre>
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface PlatformServiceMethod {
+ /**
+ * Method name in corresponding platform service.
+ */
+ String value();
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/AbstractPlatformServiceCallTask.java b/modules/core/src/test/java/org/apache/ignite/platform/AbstractPlatformServiceCallTask.java
new file mode 100644
index 0000000..8609314
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/platform/AbstractPlatformServiceCallTask.java
@@ -0,0 +1,278 @@
+/*
+ * 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.platform;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.binary.BinaryObjectException;
+import org.apache.ignite.binary.BinaryReader;
+import org.apache.ignite.binary.BinaryWriter;
+import org.apache.ignite.binary.Binarylizable;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.compute.ComputeJob;
+import org.apache.ignite.compute.ComputeJobAdapter;
+import org.apache.ignite.compute.ComputeJobResult;
+import org.apache.ignite.compute.ComputeTaskAdapter;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.resources.IgniteInstanceResource;
+import org.apache.ignite.services.ServiceDescriptor;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Abstract task to test platform service.
+ */
+public abstract class AbstractPlatformServiceCallTask extends ComputeTaskAdapter<Object[], Object> {
+ /** */
+ @SuppressWarnings("unused")
+ @IgniteInstanceResource
+ private transient Ignite ignite;
+
+ /** {@inheritDoc} */
+ @Override public Map<? extends ComputeJob, ClusterNode> map(List<ClusterNode> subgrid, @Nullable Object[] arg)
+ throws IgniteException {
+ assert arg.length == 2;
+
+ String srvcName = (String)arg[0];
+ boolean loc = (Boolean)arg[1];
+
+ Optional<ServiceDescriptor> desc = ignite.services().serviceDescriptors().stream()
+ .filter(d -> d.name().equals(srvcName)).findAny();
+
+ assert desc.isPresent();
+
+ ClusterNode node;
+
+ Set<UUID> srvTop = desc.get().topologySnapshot().keySet();
+
+ if (loc) {
+ UUID nodeId = F.rand(srvTop);
+
+ node = ignite.cluster().node(nodeId);
+ }
+ else {
+ node = ignite.cluster().nodes().stream().filter(n -> !srvTop.contains(n.id())).findAny().orElse(null);
+
+ assert node != null;
+ }
+
+ return Collections.singletonMap(createJob(srvcName), node);
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object reduce(List<ComputeJobResult> results) throws IgniteException {
+ return results.get(0).getData();
+ }
+
+ /**
+ * Create task job.
+ *
+ * @param svcName Service name
+ * @return Instance of task job.
+ */
+ abstract ComputeJobAdapter createJob(String svcName);
+
+ /** */
+ protected abstract static class AbstractServiceCallJob extends ComputeJobAdapter {
+ /** */
+ protected final String srvcName;
+
+ /**
+ * @param srvcName Service name.
+ */
+ protected AbstractServiceCallJob(String srvcName) {
+ assert srvcName != null;
+
+ this.srvcName = srvcName;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object execute() throws IgniteException {
+ try {
+ runTest();
+
+ return null;
+ }
+ catch (Exception e) {
+ throw new IgniteException(e);
+ }
+ }
+
+ /**
+ * Test method to call platform service.
+ */
+ abstract void runTest();
+ }
+
+ /** */
+ protected static void assertEquals(Object exp, Object res) throws IgniteException {
+ if ((exp != null && !exp.equals(res)) || (res != null && !res.equals(exp)))
+ throw new IgniteException(String.format("Expected equals to %s, got %s", exp, res));
+ }
+
+ /** */
+ protected static void assertTrue(boolean res) {
+ assertEquals(true, res);
+ }
+
+ /** */
+ public interface TestPlatformService
+ {
+ /** */
+ @PlatformServiceMethod("get_NodeId")
+ UUID getNodeId();
+
+ /** */
+ @PlatformServiceMethod("get_GuidProp")
+ UUID getGuidProp();
+
+ /** */
+ @PlatformServiceMethod("set_GuidProp")
+ void setGuidProp(UUID val);
+
+ /** */
+ @PlatformServiceMethod("get_ValueProp")
+ TestValue getValueProp();
+
+ /** */
+ @PlatformServiceMethod("set_ValueProp")
+ void setValueProp(TestValue val);
+
+ /** */
+ @PlatformServiceMethod("ErrorMethod")
+ void errorMethod();
+
+ /** */
+ @PlatformServiceMethod("AddOneToEach")
+ TestValue[] addOneToEach(TestValue[] col);
+
+ /** */
+ @PlatformServiceMethod("AddOneToEachCollection")
+ Collection<TestValue> addOneToEachCollection(Collection<TestValue> col);
+
+ /** */
+ @PlatformServiceMethod("AddOneToEachDictionary")
+ Map<TestKey, TestValue> addOneToEachDictionary(Map<TestKey, TestValue> dict);
+
+ /** */
+ @PlatformServiceMethod("AddOne")
+ BinarizableTestValue addOne(BinarizableTestValue val);
+ }
+
+ /** */
+ public static class TestKey {
+ /** */
+ private final int id;
+
+ /** */
+ public TestKey(int id) {
+ this.id = id;
+ }
+
+ /** */
+ public int id() {
+ return id;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ return id == ((TestKey)o).id;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return Objects.hash(id);
+ }
+ }
+
+ /** */
+ public static class TestValue {
+ /** */
+ protected int id;
+
+ /** */
+ protected String name;
+
+ /** */
+ public TestValue(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ /** */
+ public int id() {
+ return id;
+ }
+
+ /** */
+ public String name() {
+ return name;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ TestValue val = (TestValue) o;
+
+ return id == val.id && Objects.equals(name, val.name);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return Objects.hash(id, name);
+ }
+ }
+
+ /** */
+ public static class BinarizableTestValue extends TestValue implements Binarylizable {
+ /** */
+ public BinarizableTestValue(int id, String name) {
+ super(id, name);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeBinary(BinaryWriter writer) throws BinaryObjectException {
+ writer.writeInt("id", id);
+ writer.writeString("name", name);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void readBinary(BinaryReader reader) throws BinaryObjectException {
+ id = reader.readInt("id");
+ name = reader.readString("name");
+ }
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/PlatformServiceCallCollectionsTask.java b/modules/core/src/test/java/org/apache/ignite/platform/PlatformServiceCallCollectionsTask.java
new file mode 100644
index 0000000..7e2806b
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/platform/PlatformServiceCallCollectionsTask.java
@@ -0,0 +1,93 @@
+/*
+ * 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.platform;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.compute.ComputeJobAdapter;
+import org.apache.ignite.internal.util.typedef.T2;
+import org.apache.ignite.resources.IgniteInstanceResource;
+
+/**
+ * Test invoke methods with collections and arrays as arguments and return type.
+ */
+public class PlatformServiceCallCollectionsTask extends AbstractPlatformServiceCallTask {
+ /** {@inheritDoc} */
+ @Override ComputeJobAdapter createJob(String svcName) {
+ return new PlatformServiceCallCollectionsJob(svcName);
+ }
+
+ /** */
+ public static class PlatformServiceCallCollectionsJob extends AbstractServiceCallJob {
+ /** */
+ @SuppressWarnings("unused")
+ @IgniteInstanceResource
+ private transient Ignite ignite;
+
+ /**
+ * @param svcName Service name.
+ */
+ PlatformServiceCallCollectionsJob(String svcName) {
+ super(svcName);
+ }
+
+ /** {@inheritDoc} */
+ @Override void runTest() {
+ TestPlatformService srv = ignite.services().serviceProxy(srvcName, TestPlatformService.class, false);
+
+ {
+ TestValue[] exp = IntStream.range(0, 10).mapToObj(i -> new TestValue(i, "name_" + i))
+ .toArray(TestValue[]::new);
+
+ TestValue[] res = srv.addOneToEach(exp);
+
+ assertEquals(exp.length, res.length);
+
+ for (int i = 0; i < exp.length; i++)
+ assertEquals(exp[i].id() + 1, res[i].id());
+ }
+
+ {
+ List<TestValue> exp = IntStream.range(0, 10).mapToObj(i -> new TestValue(i, "name_" + i))
+ .collect(Collectors.toList());
+
+ Collection<TestValue> res = srv.addOneToEachCollection(exp);
+
+ assertEquals(exp.size(), res.size());
+
+ res.forEach(v -> assertEquals(exp.get(v.id() - 1).name(), v.name()));
+ }
+
+ {
+ Map<TestKey, TestValue> exp = IntStream.range(0, 10)
+ .mapToObj(i -> new T2<>(new TestKey(i), new TestValue(i, "name_" + i)))
+ .collect(Collectors.toMap(T2::getKey, T2::getValue));
+
+ Map<TestKey, TestValue> res = srv.addOneToEachDictionary(exp);
+
+ assertEquals(exp.size(), res.size());
+
+ res.forEach((k, v) -> assertEquals(exp.get(new TestKey(k.id() - 1)).name(), v.name()));
+ }
+ }
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/PlatformServiceCallTask.java b/modules/core/src/test/java/org/apache/ignite/platform/PlatformServiceCallTask.java
new file mode 100644
index 0000000..f39a3ee
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/platform/PlatformServiceCallTask.java
@@ -0,0 +1,81 @@
+/*
+ * 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.platform;
+
+import java.util.UUID;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.compute.ComputeJobAdapter;
+import org.apache.ignite.internal.processors.platform.PlatformNativeException;
+import org.apache.ignite.internal.processors.platform.services.PlatformService;
+import org.apache.ignite.resources.IgniteInstanceResource;
+import org.apache.ignite.testframework.GridTestUtils;
+
+/**
+ * Basic task to calling {@link PlatformService} from Java.
+ */
+public class PlatformServiceCallTask extends AbstractPlatformServiceCallTask {
+ /** {@inheritDoc} */
+ @Override ComputeJobAdapter createJob(String svcName) {
+ return new PlatformServiceCallJob(svcName);
+ }
+
+ /** */
+ static class PlatformServiceCallJob extends AbstractServiceCallJob {
+ /** */
+ @SuppressWarnings("unused")
+ @IgniteInstanceResource
+ private transient Ignite ignite;
+
+ /**
+ * @param srvcName Service name.
+ */
+ PlatformServiceCallJob(String srvcName) {
+ super(srvcName);
+ }
+
+ /** {@inheritDoc} */
+ @Override void runTest() {
+ TestPlatformService srv = ignite.services().serviceProxy(srvcName, TestPlatformService.class, false);
+
+ {
+ UUID nodeId = srv.getNodeId();
+ assertTrue(ignite.cluster().nodes().stream().anyMatch(n -> n.id().equals(nodeId)));
+ }
+
+ {
+ UUID expUuid = UUID.randomUUID();
+ srv.setGuidProp(expUuid);
+ assertEquals(expUuid, srv.getGuidProp());
+ }
+
+ {
+ TestValue exp = new TestValue(1, "test");
+ srv.setValueProp(exp);
+ assertEquals(exp, srv.getValueProp());
+ }
+
+ {
+ PlatformNativeException nativeEx = (PlatformNativeException)GridTestUtils
+ .assertThrowsWithCause(srv::errorMethod, PlatformNativeException.class)
+ .getCause();
+
+ assertTrue(nativeEx.toString().contains("Failed method"));
+ }
+ }
+ }
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj
index 3465c7d..0178a0c 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj
@@ -310,6 +310,7 @@
<Compile Include="..\Apache.Ignite.Core.Tests\Plugin\TestIgnitePluginException.cs" Link="Plugin\TestIgnitePluginException.cs" />
<Compile Include="..\Apache.Ignite.Core.Tests\Plugin\TestIgnitePluginProvider.cs" Link="Plugin\TestIgnitePluginProvider.cs" />
<Compile Include="..\Apache.Ignite.Core.Tests\Query\BinarizablePerson.cs" Link="Cache\Query\BinarizablePerson.cs" />
+ <Compile Include="..\Apache.Ignite.Core.Tests\Services\CallPlatformServiceTest.cs" Link="Services\CallPlatformServiceTest.cs" />
<Compile Include="..\Apache.Ignite.Core.Tests\Services\ServiceProxyTest.cs" Link="Services\ServiceProxyTest.cs" />
<Compile Include="..\Apache.Ignite.Core.Tests\Services\ServicesAsyncWrapper.cs" Link="Services\ServicesAsyncWrapper.cs" />
<Compile Include="..\Apache.Ignite.Core.Tests\Services\ServicesTest.cs" Link="Services\ServicesTest.cs" />
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
index 028992f..d32da1c 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
@@ -366,6 +366,7 @@
<Compile Include="Query\ImplicitBinarizablePerson.cs" />
<Compile Include="Query\NoDefBinarizablePerson.cs" />
<Compile Include="Query\BinarizablePerson.cs" />
+ <Compile Include="Services\CallPlatformServiceTest.cs" />
<Compile Include="Services\ServicesTest.cs" />
<Compile Include="Services\ServicesTestAsync.cs" />
<Compile Include="Services\ServiceProxyTest.cs" />
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/CallPlatformServiceTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/CallPlatformServiceTest.cs
new file mode 100644
index 0000000..b06671a
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/CallPlatformServiceTest.cs
@@ -0,0 +1,345 @@
+/*
+ * 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.
+ */
+
+namespace Apache.Ignite.Core.Tests.Services
+{
+ using System;
+ using System.Collections;
+ using System.Linq;
+ using Apache.Ignite.Core.Binary;
+ using Apache.Ignite.Core.Resource;
+ using Apache.Ignite.Core.Services;
+ using NUnit.Framework;
+
+ /// <summary>
+ /// Tests calling platform service from java.
+ /// </summary>
+ public class CallPlatformServiceTest
+ {
+ /** */
+ private const string ServiceName = "TestPlatformService";
+
+ /** */
+ private const string CheckTaskName = "org.apache.ignite.platform.PlatformServiceCallTask";
+
+ /** */
+ private const string CheckCollectionsTaskName = "org.apache.ignite.platform.PlatformServiceCallCollectionsTask";
+
+ /** */
+ protected IIgnite Grid1;
+
+ /** */
+ protected IIgnite Grid2;
+
+ /** */
+ protected IIgnite Grid3;
+
+ /// <summary>
+ /// Start grids and deploy test service.
+ /// </summary>
+ [SetUp]
+ public void SetUp()
+ {
+ StartGrids();
+ }
+
+ /// <summary>
+ /// Stop grids after test.
+ /// </summary>
+ [TearDown]
+ public void TearDown()
+ {
+ StopGrids();
+ }
+
+ /// <summary>
+ /// Tests call a platform service by invoking a special compute java task,
+ /// in which real invocation of the service is made.
+ /// <para/>
+ /// Tests common methods.
+ /// <param name="local">If true call on local node.</param>
+ /// </summary>
+ [Test]
+ public void TestCallPlatformService([Values(true, false)] bool local)
+ {
+ var cfg = new ServiceConfiguration
+ {
+ Name = ServiceName,
+ TotalCount = 1,
+ Service = new TestPlatformService()
+ };
+
+ Grid1.GetServices().Deploy(cfg);
+
+ Grid1.GetCompute().ExecuteJavaTask<object>(CheckTaskName, new object[] { ServiceName, local });
+ }
+
+ /// <summary>
+ /// Tests call a platform service by invoking a special compute java task,
+ /// in which real invocation of the service is made.
+ /// <para/>
+ /// Tests collections method.
+ /// <param name="local">If true call on local node.</param>
+ /// </summary>
+ [Test]
+ public void TestCallPlatformServiceCollections([Values(true, false)] bool local)
+ {
+ var cfg = new ServiceConfiguration
+ {
+ Name = ServiceName,
+ TotalCount = 1,
+ Service = new TestPlatformService()
+ };
+
+ Grid1.GetServices().Deploy(cfg);
+
+ Grid1.GetCompute().ExecuteJavaTask<object>(CheckCollectionsTaskName, new object[] { ServiceName, local });
+ }
+
+ /// <summary>
+ /// Starts the grids.
+ /// </summary>
+ private void StartGrids()
+ {
+ if (Grid1 != null)
+ return;
+
+ Grid1 = Ignition.Start(GetConfiguration(1));
+ Grid2 = Ignition.Start(GetConfiguration(2));
+ Grid3 = Ignition.Start(GetConfiguration(3));
+ }
+
+ /// <summary>
+ /// Stops the grids.
+ /// </summary>
+ private void StopGrids()
+ {
+ Grid1 = Grid2 = Grid3 = null;
+
+ Ignition.StopAll(true);
+ }
+
+ /// <summary>
+ /// Gets the Ignite configuration.
+ /// </summary>
+ private IgniteConfiguration GetConfiguration(int idx)
+ {
+ return new IgniteConfiguration(TestUtils.GetTestConfiguration())
+ {
+ IgniteInstanceName = "grid" + idx,
+ BinaryConfiguration = new BinaryConfiguration(typeof(TestKey), typeof(TestValue),
+ typeof(BinarizableTestValue))
+ {
+ NameMapper = BinaryBasicNameMapper.SimpleNameInstance
+ }
+ };
+ }
+
+ /** */
+ public interface ITestPlatformService : IService
+ {
+ /** */
+ Guid NodeId { get; }
+
+ /** */
+ Guid? GuidProp { get; set; }
+
+ /** */
+ TestValue ValueProp { get; set; }
+
+ /** */
+ void ErrorMethod();
+
+ /** */
+ TestValue[] AddOneToEach(TestValue[] arr);
+
+ /** */
+ ICollection AddOneToEachCollection(ICollection col);
+
+ /** */
+ IDictionary AddOneToEachDictionary(IDictionary dict);
+
+ /** */
+ BinarizableTestValue AddOne(BinarizableTestValue val);
+ }
+
+ #pragma warning disable 649
+
+ /** */
+ private class TestPlatformService : ITestPlatformService
+ {
+ /** */
+ [InstanceResource]
+ private IIgnite _grid;
+
+ /** <inheritdoc /> */
+ public Guid NodeId
+ {
+ get { return _grid.GetCluster().GetLocalNode().Id;}
+ }
+
+ /** <inheritdoc /> */
+ public Guid? GuidProp { get; set; }
+
+ /** <inheritdoc /> */
+ public TestValue ValueProp { get; set; }
+
+ /** <inheritdoc /> */
+ public void ErrorMethod()
+ {
+ throw new Exception("Failed method");
+ }
+
+ /** <inheritdoc /> */
+ public TestValue[] AddOneToEach(TestValue[] arr)
+ {
+ return arr.Select(val => new TestValue()
+ {
+ Id = val.Id + 1,
+ Name = val.Name
+
+ }).ToArray();
+ }
+
+ /** <inheritdoc /> */
+ public ICollection AddOneToEachCollection(ICollection col)
+ {
+ var res = col.Cast<TestValue>().Select(val => new TestValue()
+ {
+ Id = val.Id + 1,
+ Name = val.Name
+
+ }).ToList();
+
+ return new ArrayList(res);
+ }
+
+ /** <inheritdoc /> */
+ public IDictionary AddOneToEachDictionary(IDictionary dict)
+ {
+ var res = new Hashtable();
+
+ foreach (DictionaryEntry pair in dict)
+ {
+ var k = new TestKey(((TestKey) pair.Key).Id + 1);
+
+ var v = new TestValue()
+ {
+ Id = ((TestValue)pair.Value).Id + 1,
+ Name = ((TestValue)pair.Value).Name
+ };
+
+ res.Add(k, v);
+ }
+
+ return res;
+ }
+
+ /** <inheritdoc /> */
+ public BinarizableTestValue AddOne(BinarizableTestValue val)
+ {
+ return new BinarizableTestValue()
+ {
+ Id = val.Id + 1,
+ Name = val.Name
+ };
+ }
+
+ /** <inheritdoc /> */
+ public void Init(IServiceContext context)
+ {
+ // No-op.
+ }
+
+ /** <inheritdoc /> */
+ public void Execute(IServiceContext context)
+ {
+ // No-op.
+ }
+
+ /** <inheritdoc /> */
+ public void Cancel(IServiceContext context)
+ {
+ // No-op;
+ }
+ }
+
+ #pragma warning restore 649
+
+ /** */
+ public class TestKey
+ {
+ /** */
+ public TestKey(int id)
+ {
+ Id = id;
+ }
+
+ /** */
+ public int Id { get; set; }
+
+ /** <inheritdoc /> */
+ public override int GetHashCode()
+ {
+ return Id;
+ }
+
+ /** <inheritdoc /> */
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj))
+ return false;
+
+ if (ReferenceEquals(this, obj))
+ return true;
+
+ if (obj.GetType() != GetType())
+ return false;
+
+ return Id == ((TestKey)obj).Id;
+ }
+ }
+
+ /** */
+ public class TestValue
+ {
+ /** */
+ public int Id { get; set; }
+
+ /** */
+ public string Name { get; set; }
+ }
+
+ /** */
+ public class BinarizableTestValue : TestValue, IBinarizable
+ {
+ /** <inheritdoc /> */
+ public void WriteBinary(IBinaryWriter writer)
+ {
+ writer.WriteInt("id", Id);
+ writer.WriteString("name", Name);
+ }
+
+ /** <inheritdoc /> */
+ public void ReadBinary(IBinaryReader reader)
+ {
+ Id = reader.ReadInt("id");
+ Name = reader.ReadString("name");
+ }
+ }
+ }
+}