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/06/17 08:06:42 UTC
[ignite] branch master updated: IGNITE-13033 Java thin client:
Service invocation - Fixes #7908.
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 7e0d656 IGNITE-13033 Java thin client: Service invocation - Fixes #7908.
7e0d656 is described below
commit 7e0d6568c5ce20e98c1be2949c36baa34170f229
Author: Aleksey Plekhanov <pl...@gmail.com>
AuthorDate: Wed Jun 17 12:58:22 2020 +0500
IGNITE-13033 Java thin client: Service invocation - Fixes #7908.
Signed-off-by: Aleksey Plekhanov <pl...@gmail.com>
---
.../org/apache/ignite/client/ClientServices.java | 56 +++
.../org/apache/ignite/client/IgniteClient.java | 20 +
.../internal/client/thin/ClientOperation.java | 4 +-
.../internal/client/thin/ClientServicesImpl.java | 180 +++++++++
.../ignite/internal/client/thin/ClientUtils.java | 11 +-
.../client/thin/ProtocolBitmaskFeature.java | 5 +-
.../internal/client/thin/ProtocolContext.java | 12 +
.../internal/client/thin/TcpIgniteClient.java | 16 +
.../platform/client/ClientBitmaskFeature.java | 5 +-
.../platform/client/ClientMessageParser.java | 12 +-
.../client/service/ClientServiceInvokeRequest.java | 323 ++++++++++++++++
.../platform/services/PlatformServices.java | 13 +
.../processors/service/GridServiceProxy.java | 10 +-
.../org/apache/ignite/client/AsyncChannelTest.java | 16 +-
.../ignite/client/ConnectToStartingNodeTest.java | 12 +-
.../test/java/org/apache/ignite/client/Person.java | 6 +
.../client/thin/AbstractThinClientTest.java | 79 ++++
.../internal/client/thin/ClusterApiTest.java | 13 +-
.../internal/client/thin/ClusterGroupTest.java | 11 +-
.../internal/client/thin/ComputeTaskTest.java | 17 +-
.../ignite/internal/client/thin/ServicesTest.java | 417 +++++++++++++++++++++
.../platform/AbstractPlatformServiceCallTask.java | 30 ++
.../PlatformServiceCallCollectionsTask.java | 9 +-
.../PlatformServiceCallCollectionsThinTask.java | 65 ++++
.../ignite/platform/PlatformServiceCallTask.java | 58 +--
.../platform/PlatformServiceCallThinTask.java | 76 ++++
.../org/apache/ignite/client/ClientTestSuite.java | 2 +
.../Services/CallPlatformServiceTest.cs | 37 +-
.../config/benchmark-thin-services.properties | 78 ++++
.../yardstick/config/ignite-services-config.xml | 51 +++
.../IgniteThinServiceInvocationBenchmark.java | 45 +++
.../yardstick/thin/service/SimpleService.java | 28 ++
.../yardstick/thin/service/SimpleServiceImpl.java | 46 +++
33 files changed, 1638 insertions(+), 125 deletions(-)
diff --git a/modules/core/src/main/java/org/apache/ignite/client/ClientServices.java b/modules/core/src/main/java/org/apache/ignite/client/ClientServices.java
new file mode 100644
index 0000000..fe2408e
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/client/ClientServices.java
@@ -0,0 +1,56 @@
+/*
+ * 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.client;
+
+/**
+ * Thin client services facade.
+ */
+public interface ClientServices {
+ /**
+ * Gets the cluster group to which this {@code ClientServices} instance belongs.
+ *
+ * @return Cluster group to which this {@code ClientServices} instance belongs.
+ */
+ public ClientClusterGroup clusterGroup();
+
+ /**
+ * Gets a remote handle on the service.
+ * <p>
+ * Note: There are no guarantees that each method invocation for the same proxy will always contact the same remote
+ * service (on the same remote node).
+ *
+ * @param name Service name.
+ * @param svcItf Interface for the service.
+ * @return Proxy over remote service.
+ */
+ public <T> T serviceProxy(String name, Class<? super T> svcItf);
+
+ /**
+ * Gets a remote handle on the service with timeout.
+ * <p>
+ * Note: There are no guarantees that each method invocation for the same proxy will always contact the same remote
+ * service (on the same remote node).
+ *
+ * @param name Service name.
+ * @param svcItf Interface for the service.
+ * @param timeout If greater than 0 created proxy will wait for service availability only specified time,
+ * and will limit remote service invocation time.
+ * @return Proxy over remote service.
+ */
+ public <T> T serviceProxy(String name, Class<? super T> svcItf, long timeout);
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/client/IgniteClient.java b/modules/core/src/main/java/org/apache/ignite/client/IgniteClient.java
index fa8cf8f..1d10a8a 100644
--- a/modules/core/src/main/java/org/apache/ignite/client/IgniteClient.java
+++ b/modules/core/src/main/java/org/apache/ignite/client/IgniteClient.java
@@ -119,4 +119,24 @@ public interface IgniteClient extends AutoCloseable {
* @return Client cluster facade.
*/
public ClientCluster cluster();
+
+ /**
+ * Gets {@code services} facade over all cluster nodes started in server mode.
+ *
+ * @return Services facade over all cluster nodes started in server mode.
+ */
+ public ClientServices services();
+
+ /**
+ * Gets {@code services} facade over nodes within the cluster group. All operations
+ * on the returned {@link ClientServices} instance will only include nodes from
+ * the specified cluster group.
+ *
+ * Note: In some cases there will be additional requests for each service invocation from client to server
+ * to resolve cluster group.
+ *
+ * @param grp Cluster group.
+ * @return {@code Services} functionality over given cluster group.
+ */
+ public ClientServices services(ClientClusterGroup grp);
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ClientOperation.java b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ClientOperation.java
index 3cf228c..34fb17d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ClientOperation.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ClientOperation.java
@@ -74,7 +74,9 @@ enum ClientOperation {
/** Get nodes info by IDs. */CLUSTER_GROUP_GET_NODE_INFO(5101),
/** Execute compute task. */COMPUTE_TASK_EXECUTE(6000),
- /** Finished compute task notification. */COMPUTE_TASK_FINISHED(6001, true);
+ /** Finished compute task notification. */COMPUTE_TASK_FINISHED(6001, true),
+
+ /** Invoke service. */SERVICE_INVOKE(7000);
/** Code. */
private final int code;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ClientServicesImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ClientServicesImpl.java
new file mode 100644
index 0000000..5bf5234
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ClientServicesImpl.java
@@ -0,0 +1,180 @@
+/*
+ * 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.client.thin;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Collection;
+import java.util.UUID;
+import org.apache.ignite.client.ClientClusterGroup;
+import org.apache.ignite.client.ClientException;
+import org.apache.ignite.client.ClientServices;
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.A;
+import org.apache.ignite.platform.PlatformServiceMethod;
+
+/**
+ * Implementation of {@link ClientServices}.
+ */
+class ClientServicesImpl implements ClientServices {
+ /** Channel. */
+ private final ReliableChannel ch;
+
+ /** Binary marshaller. */
+ private final ClientBinaryMarshaller marsh;
+
+ /** Utils for serialization/deserialization. */
+ private final ClientUtils utils;
+
+ /** Cluster group. */
+ private final ClientClusterGroupImpl grp;
+
+ /** Constructor. */
+ ClientServicesImpl(ReliableChannel ch, ClientBinaryMarshaller marsh, ClientClusterGroupImpl grp) {
+ this.ch = ch;
+ this.marsh = marsh;
+ this.grp = grp;
+
+ utils = new ClientUtils(marsh);
+ }
+
+ /** {@inheritDoc} */
+ @Override public ClientClusterGroup clusterGroup() {
+ return grp;
+ }
+
+ /** {@inheritDoc} */
+ @Override public <T> T serviceProxy(String name, Class<? super T> svcItf) {
+ return serviceProxy(name, svcItf, 0);
+ }
+
+ /** {@inheritDoc} */
+ @Override public <T> T serviceProxy(String name, Class<? super T> svcItf, long timeout) {
+ A.notNullOrEmpty(name, "name");
+ A.notNull(svcItf, "svcItf");
+
+ return (T)Proxy.newProxyInstance(svcItf.getClassLoader(), new Class[] {svcItf},
+ new ServiceInvocationHandler<>(name, timeout, grp));
+ }
+
+ /**
+ * Gets services facade over the specified cluster group.
+ *
+ * @param grp Cluster group.
+ */
+ ClientServices withClusterGroup(ClientClusterGroupImpl grp) {
+ A.notNull(grp, "grp");
+
+ return new ClientServicesImpl(ch, marsh, grp);
+ }
+
+ /**
+ * Service invocation handler.
+ */
+ private class ServiceInvocationHandler<T> implements InvocationHandler {
+ /** Flag "Has parameter types" mask. */
+ private static final byte FLAG_PARAMETER_TYPES_MASK = 0x02;
+
+ /** Service name. */
+ private final String name;
+
+ /** Timeout. */
+ private final long timeout;
+
+ /** Cluster group. */
+ private final ClientClusterGroupImpl grp;
+
+ /**
+ * @param name Service name.
+ * @param timeout Timeout.
+ */
+ private ServiceInvocationHandler(String name, long timeout, ClientClusterGroupImpl grp) {
+ this.name = name;
+ this.timeout = timeout;
+ this.grp = grp;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ try {
+ Collection<UUID> nodeIds = grp.nodeIds();
+
+ if (nodeIds != null && nodeIds.isEmpty())
+ throw new ClientException("Cluster group is empty.");
+
+ return ch.service(ClientOperation.SERVICE_INVOKE,
+ req -> writeServiceInvokeRequest(req, nodeIds, method, args),
+ res -> utils.readObject(res.in(), false)
+ );
+ }
+ catch (ClientError e) {
+ throw new ClientException(e);
+ }
+ }
+
+ /**
+ * @param ch Payload output channel.
+ */
+ private void writeServiceInvokeRequest(
+ PayloadOutputChannel ch,
+ Collection<UUID> nodeIds,
+ Method method,
+ Object[] args
+ ) {
+ ch.clientChannel().protocolCtx().checkFeatureSupported(ProtocolBitmaskFeature.SERVICE_INVOKE);
+
+ try (BinaryRawWriterEx writer = utils.createBinaryWriter(ch.out())) {
+ writer.writeString(name);
+ writer.writeByte(FLAG_PARAMETER_TYPES_MASK); // Flags.
+ writer.writeLong(timeout);
+
+ if (nodeIds == null)
+ writer.writeInt(0);
+ else {
+ writer.writeInt(nodeIds.size());
+
+ for (UUID nodeId : nodeIds) {
+ writer.writeLong(nodeId.getMostSignificantBits());
+ writer.writeLong(nodeId.getLeastSignificantBits());
+ }
+ }
+
+ PlatformServiceMethod ann = method.getDeclaredAnnotation(PlatformServiceMethod.class);
+
+ writer.writeString(ann != null ? ann.value() : method.getName());
+
+ Class<?>[] paramTypes = method.getParameterTypes();
+
+ if (F.isEmpty(args))
+ writer.writeInt(0);
+ else {
+ writer.writeInt(args.length);
+
+ assert args.length == paramTypes.length : "args=" + args.length + ", types=" + paramTypes.length;
+
+ for (int i = 0; i < args.length; i++) {
+ writer.writeInt(marsh.context().typeId(paramTypes[i].getName()));
+ writer.writeObject(args[i]);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ClientUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ClientUtils.java
index a781546..d59200f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ClientUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ClientUtils.java
@@ -18,6 +18,7 @@
package org.apache.ignite.internal.client.thin;
import java.io.IOException;
+import java.lang.reflect.Array;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Arrays;
@@ -55,6 +56,7 @@ import org.apache.ignite.internal.binary.BinaryRawWriterEx;
import org.apache.ignite.internal.binary.BinaryReaderExImpl;
import org.apache.ignite.internal.binary.BinaryReaderHandles;
import org.apache.ignite.internal.binary.BinarySchema;
+import org.apache.ignite.internal.binary.BinaryThreadLocalContext;
import org.apache.ignite.internal.binary.BinaryUtils;
import org.apache.ignite.internal.binary.BinaryWriterExImpl;
import org.apache.ignite.internal.binary.streams.BinaryHeapInputStream;
@@ -527,6 +529,13 @@ final class ClientUtils {
out.writeByteArray(marsh.marshal(obj));
}
+ /**
+ * @param out Output stream.
+ */
+ BinaryRawWriterEx createBinaryWriter(BinaryOutputStream out) {
+ return new BinaryWriterExImpl(marsh.context(), out, BinaryThreadLocalContext.get().schemaHolder(), null);
+ }
+
/** Read Ignite binary object from input stream. */
<T> T readObject(BinaryInputStream in, boolean keepBinary) {
if (keepBinary)
@@ -590,7 +599,7 @@ final class ClientUtils {
if (BinaryUtils.knownArray(arr))
return arr;
- Object[] res = new Object[arr.length];
+ Object[] res = (Object[])Array.newInstance(arr.getClass().getComponentType(), arr.length);
for (int i = 0; i < arr.length; i++)
res[i] = unwrapBinary(arr[i], hnds);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ProtocolBitmaskFeature.java b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ProtocolBitmaskFeature.java
index 3b705b9..c86dd7b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ProtocolBitmaskFeature.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ProtocolBitmaskFeature.java
@@ -37,7 +37,10 @@ public enum ProtocolBitmaskFeature {
CLUSTER_STATES(2),
/** Cluster groups. */
- CLUSTER_GROUPS(4);
+ CLUSTER_GROUPS(4),
+
+ /** Invoke service methods. */
+ SERVICE_INVOKE(5);
/** */
private static final EnumSet<ProtocolBitmaskFeature> ALL_FEATURES_AS_ENUM_SET =
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ProtocolContext.java b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ProtocolContext.java
index e5be575..ec8fb6e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ProtocolContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ProtocolContext.java
@@ -18,6 +18,7 @@
package org.apache.ignite.internal.client.thin;
import java.util.EnumSet;
+import org.apache.ignite.client.ClientFeatureNotSupportedByServerException;
/**
* Protocol Context.
@@ -46,6 +47,17 @@ public class ProtocolContext {
}
/**
+ * Check that feature is supported by the server.
+ *
+ * @param feature Feature.
+ * @throws ClientFeatureNotSupportedByServerException If feature is not supported by the server.
+ */
+ public void checkFeatureSupported(ProtocolBitmaskFeature feature) throws ClientFeatureNotSupportedByServerException {
+ if (!isFeatureSupported(feature))
+ throw new ClientFeatureNotSupportedByServerException(feature);
+ }
+
+ /**
* @return {@code true} if protocol version feature supported.
*/
public boolean isFeatureSupported(ProtocolVersionFeature feature) {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/TcpIgniteClient.java b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/TcpIgniteClient.java
index e4c6b9f..3db66f5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/TcpIgniteClient.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/TcpIgniteClient.java
@@ -38,6 +38,7 @@ import org.apache.ignite.client.ClientCluster;
import org.apache.ignite.client.ClientClusterGroup;
import org.apache.ignite.client.ClientCompute;
import org.apache.ignite.client.ClientException;
+import org.apache.ignite.client.ClientServices;
import org.apache.ignite.client.ClientTransactions;
import org.apache.ignite.client.IgniteClient;
import org.apache.ignite.configuration.ClientConfiguration;
@@ -78,6 +79,9 @@ public class TcpIgniteClient implements IgniteClient {
/** Cluster facade. */
private final ClientClusterImpl cluster;
+ /** Services facade. */
+ private final ClientServicesImpl services;
+
/** Marshaller. */
private final ClientBinaryMarshaller marsh;
@@ -115,6 +119,8 @@ public class TcpIgniteClient implements IgniteClient {
cluster = new ClientClusterImpl(ch, marsh);
compute = new ClientComputeImpl(ch, marsh, cluster.defaultClusterGroup());
+
+ services = new ClientServicesImpl(ch, marsh, cluster.defaultClusterGroup());
}
/** {@inheritDoc} */
@@ -228,6 +234,16 @@ public class TcpIgniteClient implements IgniteClient {
return cluster;
}
+ /** {@inheritDoc} */
+ @Override public ClientServices services() {
+ return services;
+ }
+
+ /** {@inheritDoc} */
+ @Override public ClientServices services(ClientClusterGroup grp) {
+ return services.withClusterGroup((ClientClusterGroupImpl)grp);
+ }
+
/**
* Initializes new instance of {@link IgniteClient}.
*
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientBitmaskFeature.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientBitmaskFeature.java
index 3da4e11..3ec7530 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientBitmaskFeature.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientBitmaskFeature.java
@@ -37,7 +37,10 @@ public enum ClientBitmaskFeature implements ThinProtocolFeature {
CLUSTER_GROUP_GET_NODES_ENDPOINTS(3),
/** Cluster groups. */
- CLUSTER_GROUPS(4);
+ CLUSTER_GROUPS(4),
+
+ /** Service invocation. */
+ SERVICE_INVOKE(5);
/** */
private static final EnumSet<ClientBitmaskFeature> ALL_FEATURES_AS_ENUM_SET =
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientMessageParser.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientMessageParser.java
index 37ef131..94d32bc 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientMessageParser.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientMessageParser.java
@@ -17,7 +17,6 @@
package org.apache.ignite.internal.processors.platform.client;
-import org.apache.ignite.internal.binary.BinaryRawReaderEx;
import org.apache.ignite.internal.binary.BinaryRawWriterEx;
import org.apache.ignite.internal.binary.BinaryReaderExImpl;
import org.apache.ignite.internal.binary.GridBinaryMarshaller;
@@ -75,6 +74,7 @@ import org.apache.ignite.internal.processors.platform.client.cluster.ClientClust
import org.apache.ignite.internal.processors.platform.client.cluster.ClientClusterWalChangeStateRequest;
import org.apache.ignite.internal.processors.platform.client.cluster.ClientClusterWalGetStateRequest;
import org.apache.ignite.internal.processors.platform.client.compute.ClientExecuteTaskRequest;
+import org.apache.ignite.internal.processors.platform.client.service.ClientServiceInvokeRequest;
import org.apache.ignite.internal.processors.platform.client.tx.ClientTxEndRequest;
import org.apache.ignite.internal.processors.platform.client.tx.ClientTxStartRequest;
@@ -250,6 +250,9 @@ public class ClientMessageParser implements ClientListenerMessageParser {
/** */
public static final short OP_COMPUTE_TASK_FINISHED = 6001;
+ /** Service invocation. */
+ private static final short OP_SERVICE_INVOKE = 7000;
+
/** Marshaller. */
private final GridBinaryMarshaller marsh;
@@ -280,7 +283,7 @@ public class ClientMessageParser implements ClientListenerMessageParser {
BinaryInputStream inStream = new BinaryHeapInputStream(msg);
// skipHdrCheck must be true (we have 103 op code).
- BinaryRawReaderEx reader = new BinaryReaderExImpl(marsh.context(), inStream,
+ BinaryReaderExImpl reader = new BinaryReaderExImpl(marsh.context(), inStream,
null, null, true, true);
return decode(reader);
@@ -292,7 +295,7 @@ public class ClientMessageParser implements ClientListenerMessageParser {
* @param reader Reader.
* @return Request.
*/
- public ClientListenerRequest decode(BinaryRawReaderEx reader) {
+ public ClientListenerRequest decode(BinaryReaderExImpl reader) {
short opCode = reader.readShort();
switch (opCode) {
@@ -450,6 +453,9 @@ public class ClientMessageParser implements ClientListenerMessageParser {
case OP_COMPUTE_TASK_EXECUTE:
return new ClientExecuteTaskRequest(reader);
+
+ case OP_SERVICE_INVOKE:
+ return new ClientServiceInvokeRequest(reader);
}
return new ClientRawRequest(reader.readLong(), ClientStatus.INVALID_OP_CODE,
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/service/ClientServiceInvokeRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/service/ClientServiceInvokeRequest.java
new file mode 100644
index 0000000..e51780b
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/service/ClientServiceInvokeRequest.java
@@ -0,0 +1,323 @@
+/*
+ * 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.platform.client.service;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import org.apache.ignite.IgniteBinary;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteServices;
+import org.apache.ignite.internal.binary.BinaryRawReaderEx;
+import org.apache.ignite.internal.binary.BinaryReaderExImpl;
+import org.apache.ignite.internal.cluster.ClusterGroupAdapter;
+import org.apache.ignite.internal.processors.platform.PlatformNativeException;
+import org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
+import org.apache.ignite.internal.processors.platform.client.ClientObjectResponse;
+import org.apache.ignite.internal.processors.platform.client.ClientRequest;
+import org.apache.ignite.internal.processors.platform.client.ClientResponse;
+import org.apache.ignite.internal.processors.platform.services.PlatformService;
+import org.apache.ignite.internal.processors.platform.services.PlatformServices;
+import org.apache.ignite.internal.processors.service.GridServiceProxy;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.services.Service;
+import org.apache.ignite.services.ServiceDescriptor;
+
+/**
+ * Request to invoke service method.
+ */
+public class ClientServiceInvokeRequest extends ClientRequest {
+ /** Flag keep binary mask. */
+ private static final byte FLAG_KEEP_BINARY_MASK = 0x01;
+
+ /** Flag "has parameter types", indicates that method arguments prefixed by typeId to help to resolve methods. */
+ private static final byte FLAG_PARAMETER_TYPES_MASK = 0x02;
+
+ /** Methods cache. */
+ private static final Map<MethodDescriptor, Method> methodsCache = new ConcurrentHashMap<>();
+
+ /** Service name. */
+ private final String name;
+
+ /** Flags. */
+ private final byte flags;
+
+ /** Timeout. */
+ private final long timeout;
+
+ /** Nodes. */
+ private final Collection<UUID> nodeIds;
+
+ /** Method name. */
+ private final String methodName;
+
+ /** Method parameter type IDs. */
+ private final int[] paramTypeIds;
+
+ /** Service arguments. */
+ private final Object[] args;
+
+ /** Objects reader. */
+ private final BinaryRawReaderEx reader;
+
+ /**
+ * Constructor.
+ *
+ * @param reader Reader.
+ */
+ public ClientServiceInvokeRequest(BinaryReaderExImpl reader) {
+ super(reader);
+
+ name = reader.readString();
+
+ flags = reader.readByte();
+
+ timeout = reader.readLong();
+
+ int cnt = reader.readInt();
+
+ nodeIds = new ArrayList<>(cnt);
+
+ for (int i = 0; i < cnt; i++)
+ nodeIds.add(new UUID(reader.readLong(), reader.readLong()));
+
+ methodName = reader.readString();
+
+ int argCnt = reader.readInt();
+
+ paramTypeIds = hasParameterTypes() ? new int[argCnt] : null;
+
+ args = new Object[argCnt];
+
+ // We can't deserialize some types (arrays of user defined types for example) from detached objects.
+ // On the other hand, deserialize should be done as part of process() call (not in constructor) for proper
+ // error handling.
+ // To overcome these issues we store binary reader reference, parse request in constructor (by reading detached
+ // objects), restore arguments starting position in input stream and deserialize arguments from input stream
+ // in process() method.
+ this.reader = reader;
+
+ int argsStartPos = reader.in().position();
+
+ for (int i = 0; i < argCnt; i++) {
+ if (paramTypeIds != null)
+ paramTypeIds[i] = reader.readInt();
+
+ args[i] = reader.readObjectDetached();
+ }
+
+ reader.in().position(argsStartPos);
+ }
+
+ /** {@inheritDoc} */
+ @Override public ClientResponse process(ClientConnectionContext ctx) {
+ if (F.isEmpty(name))
+ throw new IgniteException("Service name can't be empty");
+
+ if (F.isEmpty(methodName))
+ throw new IgniteException("Method name can't be empty");
+
+ ServiceDescriptor desc = findServiceDescriptor(ctx, name);
+
+ Class<?> svcCls = desc.serviceClass();
+
+ ClusterGroupAdapter grp = ctx.kernalContext().cluster().get();
+
+ if (ctx.securityContext() != null)
+ grp = (ClusterGroupAdapter)grp.forSubjectId(ctx.securityContext().subject().id());
+
+ grp = (ClusterGroupAdapter)(nodeIds.isEmpty() ? grp.forServers() : grp.forNodeIds(nodeIds));
+
+ IgniteServices services = grp.services();
+
+ if (!keepBinary() && args.length > 0) {
+ for (int i = 0; i < args.length; i++) {
+ if (paramTypeIds != null)
+ reader.readInt(); // Skip parameter typeId, we already read it in constructor.
+
+ args[i] = reader.readObject();
+ }
+ }
+
+ try {
+ Object res;
+
+ if (PlatformService.class.isAssignableFrom(svcCls)) {
+ PlatformService proxy = services.serviceProxy(name, PlatformService.class, false, timeout);
+
+ res = proxy.invokeMethod(methodName, keepBinary(), !keepBinary(), args);
+ }
+ else {
+ GridServiceProxy<?> proxy = new GridServiceProxy<>(grp, name, Service.class, false, timeout,
+ ctx.kernalContext());
+
+ Method method = resolveMethod(ctx, svcCls);
+
+ res = proxy.invokeMethod(method, args);
+ }
+
+ return new ClientObjectResponse(requestId(), res);
+ }
+ catch (PlatformNativeException e) {
+ ctx.kernalContext().log(getClass()).error("Failed to invoke platform service", e);
+
+ throw new IgniteException("Failed to invoke platform service, see server logs for details");
+ }
+ catch (Throwable e) {
+ throw new IgniteException(e);
+ }
+ }
+
+ /**
+ * Keep binary flag.
+ */
+ private boolean keepBinary() {
+ return (flags & FLAG_KEEP_BINARY_MASK) != 0;
+ }
+
+ /**
+ * "Has parameter types" flag.
+ */
+ private boolean hasParameterTypes() {
+ return (flags & FLAG_PARAMETER_TYPES_MASK) != 0;
+ }
+
+ /**
+ * @param ctx Connection context.
+ * @param name Service name.
+ */
+ private static ServiceDescriptor findServiceDescriptor(ClientConnectionContext ctx, String name) {
+ for (ServiceDescriptor desc : ctx.kernalContext().service().serviceDescriptors()) {
+ if (name.equals(desc.name()))
+ return desc;
+ }
+
+ throw new IgniteException("Service not found: " + name);
+ }
+
+ /**
+ * Resolve method by method name and parameter types or parameter values.
+ */
+ private Method resolveMethod(ClientConnectionContext ctx, Class<?> cls) throws ReflectiveOperationException {
+ if (paramTypeIds != null) {
+ MethodDescriptor desc = new MethodDescriptor(cls, methodName, paramTypeIds);
+
+ Method method = methodsCache.get(desc);
+
+ if (method != null)
+ return method;
+
+ IgniteBinary binary = ctx.kernalContext().grid().binary();
+
+ for (Method method0 : cls.getMethods()) {
+ if (methodName.equals(method0.getName())) {
+ MethodDescriptor desc0 = MethodDescriptor.forMethod(binary, method0);
+
+ methodsCache.putIfAbsent(desc0, method0);
+
+ if (desc0.equals(desc))
+ return method0;
+ }
+ }
+
+ throw new NoSuchMethodException("Method not found: " + desc);
+ }
+
+ // Try to find method by name and parameter values.
+ return PlatformServices.getMethod(cls, methodName, args);
+ }
+
+ /**
+ *
+ */
+ private static class MethodDescriptor {
+ /** Class. */
+ private final Class<?> cls;
+
+ /** Method name. */
+ private final String methodName;
+
+ /** Parameter type IDs. */
+ private final int[] paramTypeIds;
+
+ /** Hash code. */
+ private final int hash;
+
+ /**
+ * @param cls Class.
+ * @param methodName Method name.
+ * @param paramTypeIds Parameter type ids.
+ */
+ private MethodDescriptor(Class<?> cls, String methodName, int[] paramTypeIds) {
+ assert cls != null;
+ assert methodName != null;
+ assert paramTypeIds != null;
+
+ this.cls = cls;
+ this.methodName = methodName;
+ this.paramTypeIds = paramTypeIds;
+
+ // Precalculate hash in constructor, since we need it for all objects of this class.
+ hash = 31 * ((31 * cls.hashCode()) + methodName.hashCode()) + Arrays.hashCode(paramTypeIds);
+ }
+
+ /**
+ * @param binary Ignite binary.
+ * @param method Method.
+ */
+ private static MethodDescriptor forMethod(IgniteBinary binary, Method method) {
+ Class<?>[] paramTypes = method.getParameterTypes();
+
+ int[] paramTypeIds = new int[paramTypes.length];
+
+ for (int i = 0; i < paramTypes.length; i++)
+ paramTypeIds[i] = binary.typeId(paramTypes[i].getName());
+
+ return new MethodDescriptor(method.getDeclaringClass(), method.getName(), paramTypeIds);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ MethodDescriptor that = (MethodDescriptor)o;
+
+ return cls.equals(that.cls) && methodName.equals(that.methodName)
+ && Arrays.equals(paramTypeIds, that.paramTypeIds);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return hash;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MethodDescriptor.class, this, "paramTypeIds", paramTypeIds);
+ }
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java
index 24d473a..6a6e05e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java
@@ -515,6 +515,19 @@ public class PlatformServices extends PlatformAbstractTarget {
}
/**
+ * Finds a suitable method in a class.
+ *
+ * @param clazz Class.
+ * @param mthdName Name.
+ * @param args Args.
+ * @return Method.
+ * @throws NoSuchMethodException On error.
+ */
+ public static Method getMethod(Class<?> clazz, String mthdName, Object[] args) throws NoSuchMethodException {
+ return ServiceProxyHolder.getMethod(clazz, mthdName, args);
+ }
+
+ /**
* Proxy holder.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
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 64eada3..3c92ebc 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
@@ -191,8 +191,14 @@ public class GridServiceProxy<T> implements Serializable {
if (svcCtx != null) {
Service svc = svcCtx.service();
- if (svc != null)
- return callServiceLocally(svc, mtd, args);
+ if (svc != null) {
+ try {
+ return callServiceLocally(svc, mtd, args);
+ }
+ catch (InvocationTargetException e) {
+ throw e.getTargetException();
+ }
+ }
}
}
else {
diff --git a/modules/core/src/test/java/org/apache/ignite/client/AsyncChannelTest.java b/modules/core/src/test/java/org/apache/ignite/client/AsyncChannelTest.java
index 543321c..9ac12e1 100644
--- a/modules/core/src/test/java/org/apache/ignite/client/AsyncChannelTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/client/AsyncChannelTest.java
@@ -24,25 +24,22 @@ import java.util.concurrent.locks.Lock;
import javax.cache.Cache;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
-import org.apache.ignite.Ignition;
import org.apache.ignite.cache.CacheAtomicityMode;
import org.apache.ignite.cache.CachePeekMode;
import org.apache.ignite.cache.query.Query;
import org.apache.ignite.cache.query.QueryCursor;
import org.apache.ignite.cache.query.ScanQuery;
import org.apache.ignite.configuration.CacheConfiguration;
-import org.apache.ignite.configuration.ClientConfiguration;
-import org.apache.ignite.configuration.ClientConnectorConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.client.thin.AbstractThinClientTest;
import org.apache.ignite.testframework.GridTestUtils;
-import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.junit.Test;
/**
* Async channel tests.
*/
-public class AsyncChannelTest extends GridCommonAbstractTest {
+public class AsyncChannelTest extends AbstractThinClientTest {
/** Nodes count. */
private static final int NODES_CNT = 3;
@@ -52,9 +49,6 @@ public class AsyncChannelTest extends GridCommonAbstractTest {
/** Cache name. */
private static final String CACHE_NAME = "tx_cache";
- /** Client connector address. */
- private static final String CLIENT_CONN_ADDR = "127.0.0.1:" + ClientConnectorConfiguration.DFLT_PORT;
-
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
return super.getConfiguration(igniteInstanceName).setCacheConfiguration(
@@ -75,7 +69,7 @@ public class AsyncChannelTest extends GridCommonAbstractTest {
*/
@Test
public void testAsyncRequests() throws Exception {
- try (IgniteClient client = Ignition.startClient(new ClientConfiguration().setAddresses(CLIENT_CONN_ADDR))) {
+ try (IgniteClient client = startClient(0)) {
Ignite ignite = grid(0);
IgniteCache<Integer, Integer> igniteCache = ignite.cache(CACHE_NAME);
@@ -130,7 +124,7 @@ public class AsyncChannelTest extends GridCommonAbstractTest {
*/
@Test
public void testConcurrentRequests() throws Exception {
- try (IgniteClient client = Ignition.startClient(new ClientConfiguration().setAddresses(CLIENT_CONN_ADDR))) {
+ try (IgniteClient client = startClient(0)) {
ClientCache<Integer, Integer> clientCache = client.cache(CACHE_NAME);
clientCache.clear();
@@ -164,7 +158,7 @@ public class AsyncChannelTest extends GridCommonAbstractTest {
*/
@Test
public void testConcurrentQueries() throws Exception {
- try (IgniteClient client = Ignition.startClient(new ClientConfiguration().setAddresses(CLIENT_CONN_ADDR))) {
+ try (IgniteClient client = startClient(0)) {
ClientCache<Integer, Integer> clientCache = client.cache(CACHE_NAME);
clientCache.clear();
diff --git a/modules/core/src/test/java/org/apache/ignite/client/ConnectToStartingNodeTest.java b/modules/core/src/test/java/org/apache/ignite/client/ConnectToStartingNodeTest.java
index f79928d..bbb2c87 100644
--- a/modules/core/src/test/java/org/apache/ignite/client/ConnectToStartingNodeTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/client/ConnectToStartingNodeTest.java
@@ -20,25 +20,19 @@ package org.apache.ignite.client;
import java.util.concurrent.Callable;
import java.util.concurrent.CyclicBarrier;
import org.apache.ignite.Ignite;
-import org.apache.ignite.Ignition;
-import org.apache.ignite.configuration.ClientConfiguration;
-import org.apache.ignite.configuration.ClientConnectorConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.client.thin.AbstractThinClientTest;
import org.apache.ignite.spi.IgniteSpiException;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import org.apache.ignite.testframework.GridTestUtils;
-import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.jetbrains.annotations.Nullable;
import org.junit.Test;
/**
* Checks that connection with starting node will be established correctly.
*/
-public class ConnectToStartingNodeTest extends GridCommonAbstractTest {
- /** Client connector address. */
- private static final String CLIENT_CONN_ADDR = "127.0.0.1:" + ClientConnectorConfiguration.DFLT_PORT;
-
+public class ConnectToStartingNodeTest extends AbstractThinClientTest {
/** Barrier to suspend discovery SPI start. */
private final CyclicBarrier barrier = new CyclicBarrier(2);
@@ -75,7 +69,7 @@ public class ConnectToStartingNodeTest extends GridCommonAbstractTest {
barrier.await();
IgniteInternalFuture<IgniteClient> futStartClient = GridTestUtils.runAsync(
- () -> Ignition.startClient(new ClientConfiguration().setAddresses(CLIENT_CONN_ADDR)));
+ () -> startClient(grid()));
// Server doesn't accept connection before discovery SPI started.
assertFalse(GridTestUtils.waitForCondition(futStartClient::isDone, 500L));
diff --git a/modules/core/src/test/java/org/apache/ignite/client/Person.java b/modules/core/src/test/java/org/apache/ignite/client/Person.java
index c92305d..27510a7 100644
--- a/modules/core/src/test/java/org/apache/ignite/client/Person.java
+++ b/modules/core/src/test/java/org/apache/ignite/client/Person.java
@@ -19,6 +19,7 @@ package org.apache.ignite.client;
import java.util.Objects;
import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.internal.util.typedef.internal.S;
/**
* A person entity used for the tests.
@@ -62,4 +63,9 @@ public class Person {
return other.id.equals(id) && other.name.equals(name);
}
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(Person.class, this);
+ }
}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/client/thin/AbstractThinClientTest.java b/modules/core/src/test/java/org/apache/ignite/internal/client/thin/AbstractThinClientTest.java
new file mode 100644
index 0000000..b985a77
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/client/thin/AbstractThinClientTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.client.thin;
+
+import java.util.Arrays;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.internal.processors.odbc.ClientListenerProcessor;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+/**
+ * Abstract thin client test.
+ */
+public abstract class AbstractThinClientTest extends GridCommonAbstractTest {
+ /**
+ * Gets default client configuration.
+ */
+ protected ClientConfiguration getClientConfiguration() {
+ return new ClientConfiguration();
+ }
+
+ /**
+ * Start thin client with configured endpoints to specified nodes.
+ *
+ * @param nodes Nodes to connect.
+ * @return Thin client.
+ */
+ protected IgniteClient startClient(ClusterNode... nodes) {
+ String[] addrs = new String[nodes.length];
+
+ for (int i = 0; i < nodes.length; i++) {
+ ClusterNode node = nodes[i];
+
+ addrs[i] = F.first(node.addresses()) + ":" + node.attribute(ClientListenerProcessor.CLIENT_LISTENER_PORT);
+ }
+
+ return Ignition.startClient(getClientConfiguration().setAddresses(addrs));
+ }
+
+ /**
+ * Start thin client with configured endpoints to specified ignite instances.
+ *
+ * @param ignites Ignite instances to connect.
+ * @return Thin client.
+ */
+ protected IgniteClient startClient(Ignite... ignites) {
+ return startClient(Arrays.stream(ignites).map(ignite -> ignite.cluster().localNode()).toArray(ClusterNode[]::new));
+ }
+
+ /**
+ * Start thin client with configured endpoints to specified ignite instance indexes.
+ *
+ * @param igniteIdxs Ignite instance indexes to connect.
+ * @return Thin client.
+ */
+ protected IgniteClient startClient(int... igniteIdxs) {
+ return startClient(Arrays.stream(igniteIdxs).mapToObj(igniteIdx -> grid(igniteIdx).cluster().localNode())
+ .toArray(ClusterNode[]::new));
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/client/thin/ClusterApiTest.java b/modules/core/src/test/java/org/apache/ignite/internal/client/thin/ClusterApiTest.java
index 9f78342..5540ea3 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/client/thin/ClusterApiTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/client/thin/ClusterApiTest.java
@@ -18,25 +18,18 @@
package org.apache.ignite.internal.client.thin;
import org.apache.ignite.IgniteCluster;
-import org.apache.ignite.Ignition;
import org.apache.ignite.client.ClientCluster;
import org.apache.ignite.client.IgniteClient;
import org.apache.ignite.cluster.ClusterState;
-import org.apache.ignite.configuration.ClientConfiguration;
-import org.apache.ignite.configuration.ClientConnectorConfiguration;
import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
-import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.junit.Test;
/**
* Checks cluster state/WAL state operations for thin client.
*/
-public class ClusterApiTest extends GridCommonAbstractTest {
- /** Client connector address. */
- private static final String CLIENT_CONN_ADDR = "127.0.0.1:" + ClientConnectorConfiguration.DFLT_PORT;
-
+public class ClusterApiTest extends AbstractThinClientTest {
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
return super.getConfiguration(igniteInstanceName)
@@ -69,7 +62,7 @@ public class ClusterApiTest extends GridCommonAbstractTest {
*/
@Test
public void testClusterState() throws Exception {
- try (IgniteClient client = Ignition.startClient(new ClientConfiguration().setAddresses(CLIENT_CONN_ADDR))) {
+ try (IgniteClient client = startClient(0)) {
ClientCluster clientCluster = client.cluster();
IgniteCluster igniteCluster = grid(0).cluster();
@@ -85,7 +78,7 @@ public class ClusterApiTest extends GridCommonAbstractTest {
*/
@Test
public void testWalState() throws Exception {
- try (IgniteClient client = Ignition.startClient(new ClientConfiguration().setAddresses(CLIENT_CONN_ADDR))) {
+ try (IgniteClient client = startClient(0)) {
ClientCluster clientCluster = client.cluster();
IgniteCluster igniteCluster = grid(0).cluster();
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/client/thin/ClusterGroupTest.java b/modules/core/src/test/java/org/apache/ignite/internal/client/thin/ClusterGroupTest.java
index e253fab..e366204 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/client/thin/ClusterGroupTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/client/thin/ClusterGroupTest.java
@@ -23,27 +23,20 @@ import java.util.HashSet;
import java.util.Map;
import java.util.UUID;
import org.apache.ignite.Ignite;
-import org.apache.ignite.Ignition;
import org.apache.ignite.client.ClientClusterGroup;
import org.apache.ignite.client.IgniteClient;
import org.apache.ignite.cluster.ClusterNode;
-import org.apache.ignite.configuration.ClientConfiguration;
-import org.apache.ignite.configuration.ClientConnectorConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.G;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
-import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.junit.Test;
/**
* Checks cluster groups for thin client.
*/
-public class ClusterGroupTest extends GridCommonAbstractTest {
- /** Client connector address. */
- private static final String CLIENT_CONN_ADDR = "127.0.0.1:" + ClientConnectorConfiguration.DFLT_PORT;
-
+public class ClusterGroupTest extends AbstractThinClientTest {
/** Grid index attribute name. */
private static final String GRID_IDX_ATTR_NAME = "GRID_IDX";
@@ -66,7 +59,7 @@ public class ClusterGroupTest extends GridCommonAbstractTest {
startGrid(3, true, null);
startGrid(4, true, F.asMap(CUSTOM_ATTR_NAME, CUSTOM_ATTR_VAL));
- client = Ignition.startClient(new ClientConfiguration().setAddresses(CLIENT_CONN_ADDR));
+ client = startClient(0);
}
/** {@inheritDoc} */
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/client/thin/ComputeTaskTest.java b/modules/core/src/test/java/org/apache/ignite/internal/client/thin/ComputeTaskTest.java
index 4e13f49..4adf228 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/client/thin/ComputeTaskTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/client/thin/ComputeTaskTest.java
@@ -35,7 +35,6 @@ import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteException;
-import org.apache.ignite.Ignition;
import org.apache.ignite.client.ClientCache;
import org.apache.ignite.client.ClientClusterGroup;
import org.apache.ignite.client.ClientCompute;
@@ -43,7 +42,6 @@ import org.apache.ignite.client.ClientException;
import org.apache.ignite.client.IgniteClient;
import org.apache.ignite.compute.ComputeJobResult;
import org.apache.ignite.compute.ComputeTaskName;
-import org.apache.ignite.configuration.ClientConfiguration;
import org.apache.ignite.configuration.ClientConnectorConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.configuration.ThinClientConfiguration;
@@ -53,14 +51,13 @@ import org.apache.ignite.internal.util.typedef.G;
import org.apache.ignite.internal.util.typedef.T2;
import org.apache.ignite.mxbean.ClientProcessorMXBean;
import org.apache.ignite.testframework.GridTestUtils;
-import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.jetbrains.annotations.Nullable;
import org.junit.Test;
/**
* Checks compute grid funtionality of thin client.
*/
-public class ComputeTaskTest extends GridCommonAbstractTest {
+public class ComputeTaskTest extends AbstractThinClientTest {
/** Grids count. */
private static final int GRIDS_CNT = 4;
@@ -82,18 +79,6 @@ public class ComputeTaskTest extends GridCommonAbstractTest {
.setClientMode(getTestIgniteInstanceIndex(igniteInstanceName) == 3);
}
- /**
- *
- */
- private IgniteClient startClient(int... gridIdxs) {
- String[] addrs = new String[gridIdxs.length];
-
- for (int i = 0; i < gridIdxs.length; i++)
- addrs[i] = "127.0.0.1:" + (ClientConnectorConfiguration.DFLT_PORT + gridIdxs[i]);
-
- return Ignition.startClient(new ClientConfiguration().setAddresses(addrs));
- }
-
/** {@inheritDoc} */
@Override protected void beforeTestsStarted() throws Exception {
super.beforeTestsStarted();
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/client/thin/ServicesTest.java b/modules/core/src/test/java/org/apache/ignite/internal/client/thin/ServicesTest.java
new file mode 100644
index 0000000..9f0641a
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/client/thin/ServicesTest.java
@@ -0,0 +1,417 @@
+/*
+ * 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.client.thin;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.UUID;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.client.ClientClusterGroup;
+import org.apache.ignite.client.ClientException;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.client.Person;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.resources.IgniteInstanceResource;
+import org.apache.ignite.services.Service;
+import org.apache.ignite.services.ServiceContext;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.Test;
+
+/**
+ * Checks service invocation for thin client.
+ */
+public class ServicesTest extends AbstractThinClientTest {
+ /** Node-id service name. */
+ private static final String NODE_ID_SERVICE_NAME = "node_id_svc";
+
+ /** Node-singleton service name. */
+ private static final String NODE_SINGLTON_SERVICE_NAME = "node_svc";
+
+ /** Cluster-singleton service name. */
+ private static final String CLUSTER_SINGLTON_SERVICE_NAME = "cluster_svc";
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ super.beforeTestsStarted();
+
+ startGrids(3);
+
+ startClientGrid(3);
+
+ grid(0).createCache(DEFAULT_CACHE_NAME);
+
+ awaitPartitionMapExchange();
+
+ grid(0).services().deployNodeSingleton(NODE_ID_SERVICE_NAME, new TestNodeIdService());
+
+ grid(0).services().deployNodeSingleton(NODE_SINGLTON_SERVICE_NAME, new TestService());
+
+ // Deploy CLUSTER_SINGLTON_SERVICE_NAME to grid(1).
+ int keyGrid1 = primaryKey(grid(1).cache(DEFAULT_CACHE_NAME));
+
+ grid(0).services().deployKeyAffinitySingleton(CLUSTER_SINGLTON_SERVICE_NAME, new TestService(),
+ DEFAULT_CACHE_NAME, keyGrid1);
+ }
+
+ /**
+ * Test that overloaded methods resolved correctly.
+ */
+ @Test
+ public void testOverloadedMethods() throws Exception {
+ try (IgniteClient client = startClient(0)) {
+ // Test local service calls (service deployed to each node).
+ TestServiceInterface svc = client.services().serviceProxy(NODE_SINGLTON_SERVICE_NAME,
+ TestServiceInterface.class);
+
+ checkOverloadedMethods(svc);
+
+ // Test remote service calls (client connected to grid(0) but service deployed to grid(1)).
+ svc = client.services().serviceProxy(CLUSTER_SINGLTON_SERVICE_NAME, TestServiceInterface.class);
+
+ checkOverloadedMethods(svc);
+ }
+ }
+
+ /**
+ * @param svc Service.
+ */
+ private void checkOverloadedMethods(TestServiceInterface svc) {
+ assertEquals("testMethod()", svc.testMethod());
+
+ assertEquals("testMethod(String val): test", svc.testMethod("test"));
+
+ assertEquals(123, svc.testMethod(123));
+
+ assertEquals("testMethod(Object val): test", svc.testMethod(new StringBuilder("test")));
+
+ assertEquals("testMethod(String val): null", svc.testMethod((String)null));
+
+ assertEquals("testMethod(Object val): null", svc.testMethod((Object)null));
+
+ Person person1 = new Person(1, "Person 1");
+ Person person2 = new Person(2, "Person 2");
+
+ assertEquals("testMethod(Person person, Object obj): " + person1 + ", " + person2,
+ svc.testMethod(person1, (Object)person2));
+
+ assertEquals("testMethod(Object obj, Person person): " + person1 + ", " + person2,
+ svc.testMethod((Object)person1, person2));
+ }
+
+ /**
+ * Test that methods which get and return collections work correctly.
+ */
+ @Test
+ public void testCollectionMethods() throws Exception {
+ try (IgniteClient client = startClient(0)) {
+ // Test local service calls (service deployed to each node).
+ TestServiceInterface svc = client.services().serviceProxy(NODE_SINGLTON_SERVICE_NAME,
+ TestServiceInterface.class);
+
+ checkCollectionMethods(svc);
+
+ // Test remote service calls (client connected to grid(0) but service deployed to grid(1)).
+ svc = client.services().serviceProxy(CLUSTER_SINGLTON_SERVICE_NAME, TestServiceInterface.class);
+
+ checkCollectionMethods(svc);
+ }
+ }
+
+ /**
+ * @param svc Service.
+ */
+ private void checkCollectionMethods(TestServiceInterface svc) {
+ Person person1 = new Person(1, "Person 1");
+ Person person2 = new Person(2, "Person 2");
+
+ Person[] arr = new Person[] {person1, person2};
+ assertTrue(Arrays.equals(arr, svc.testArray(arr)));
+
+ Collection<Person> col = new HashSet<>(F.asList(person1, person2));
+ assertEquals(col, svc.testCollection(col));
+
+ Map<Integer, Person> map = F.asMap(1, person1, 2, person2);
+ assertEquals(map, svc.testMap(map));
+ }
+
+ /**
+ * Test that exception is thrown when invoking service with wrong interface.
+ */
+ @Test
+ public void testWrongMethodInvocation() throws Exception {
+ try (IgniteClient client = startClient(0)) {
+ TestServiceInterface svc = client.services().serviceProxy(NODE_ID_SERVICE_NAME,
+ TestServiceInterface.class);
+
+ GridTestUtils.assertThrowsAnyCause(log, () -> svc.testMethod(0), ClientException.class,
+ "Method not found");
+ }
+ }
+
+ /**
+ * Test that exception is thrown when trying to invoke non-existing service.
+ */
+ @Test
+ public void testWrongServiceName() throws Exception {
+ try (IgniteClient client = startClient(0)) {
+ TestServiceInterface svc = client.services().serviceProxy("no_such_service",
+ TestServiceInterface.class);
+
+ GridTestUtils.assertThrowsAnyCause(log, () -> svc.testMethod(0), ClientException.class,
+ "Service not found");
+ }
+ }
+
+ /**
+ * Test that service exception message is propagated to client.
+ */
+ @Test
+ public void testServiceException() throws Exception {
+ try (IgniteClient client = startClient(0)) {
+ // Test local service calls (service deployed to each node).
+ TestServiceInterface svc = client.services().serviceProxy(NODE_SINGLTON_SERVICE_NAME,
+ TestServiceInterface.class);
+
+ GridTestUtils.assertThrowsAnyCause(log, svc::testException, ClientException.class,
+ "testException()");
+
+ // Test remote service calls (client connected to grid(0) but service deployed to grid(1)).
+ client.services().serviceProxy(CLUSTER_SINGLTON_SERVICE_NAME, TestServiceInterface.class);
+
+ GridTestUtils.assertThrowsAnyCause(log, svc::testException, ClientException.class,
+ "testException()");
+ }
+ }
+
+ /**
+ * Test that services executed on cluster group.
+ */
+ @Test
+ public void testServicesOnClusterGroup() throws Exception {
+ try (IgniteClient client = startClient(0)) {
+ // Local node.
+ ClientClusterGroup grp = client.cluster().forNodeId(nodeId(0), nodeId(3));
+
+ TestNodeIdServiceInterface nodeSvc0 = client.services(grp).serviceProxy(NODE_ID_SERVICE_NAME,
+ TestNodeIdServiceInterface.class);
+
+ assertEquals(nodeId(0), nodeSvc0.nodeId());
+
+ // Remote node.
+ grp = client.cluster().forNodeId(nodeId(1), nodeId(3));
+
+ nodeSvc0 = client.services(grp).serviceProxy(NODE_ID_SERVICE_NAME,
+ TestNodeIdServiceInterface.class);
+
+ assertEquals(nodeId(1), nodeSvc0.nodeId());
+
+ // Client node.
+ grp = client.cluster().forNodeId(nodeId(3));
+
+ TestNodeIdServiceInterface nodeSvc1 = client.services(grp).serviceProxy(NODE_ID_SERVICE_NAME,
+ TestNodeIdServiceInterface.class);
+
+ GridTestUtils.assertThrowsAnyCause(log, nodeSvc1::nodeId, ClientException.class,
+ "Failed to find deployed service");
+
+ // All nodes, except service node.
+ grp = client.cluster().forNodeId(nodeId(0), nodeId(2), nodeId(3));
+
+ TestServiceInterface nodeSvc2 = client.services(grp).serviceProxy(CLUSTER_SINGLTON_SERVICE_NAME,
+ TestServiceInterface.class);
+
+ GridTestUtils.assertThrowsAnyCause(log, nodeSvc2::testMethod, ClientException.class,
+ "Failed to find deployed service");
+ }
+ }
+
+ /**
+ * Test services timeout.
+ */
+ @Test
+ public void testServiceTimeout() throws Exception {
+ long timeout = 100L;
+
+ try (IgniteClient client = startClient(0)) {
+ TestServiceInterface svc = client.services().serviceProxy(CLUSTER_SINGLTON_SERVICE_NAME,
+ TestServiceInterface.class, timeout);
+
+ IgniteInternalFuture<?> fut = GridTestUtils.runAsync(() -> svc.sleep(timeout * 2));
+
+ GridTestUtils.assertThrowsAnyCause(log, fut::get, ClientException.class, "timed out");
+ }
+ }
+
+ /** */
+ public static interface TestServiceInterface {
+ /** */
+ public String testMethod();
+
+ /** */
+ public String testMethod(String val);
+
+ /** */
+ public String testMethod(Object val);
+
+ /** */
+ public int testMethod(int val);
+
+ /** */
+ public String testMethod(Person person, Object obj);
+
+ /** */
+ public String testMethod(Object obj, Person person);
+
+ /** */
+ public Person[] testArray(Person[] persons);
+
+ /** */
+ public Collection<Person> testCollection(Collection<Person> persons);
+
+ /** */
+ public Map<Integer, Person> testMap(Map<Integer, Person> persons);
+
+ /** */
+ public Object testException();
+
+ /** */
+ public void sleep(long millis);
+ }
+
+ /**
+ * Implementation of TestServiceInterface.
+ */
+ public static class TestService implements Service, TestServiceInterface {
+ /** {@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.
+ }
+
+ /** {@inheritDoc} */
+ @Override public String testMethod() {
+ return "testMethod()";
+ }
+
+ /** {@inheritDoc} */
+ @Override public String testMethod(String val) {
+ return "testMethod(String val): " + val;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String testMethod(Object val) {
+ return "testMethod(Object val): " + val;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int testMethod(int val) {
+ return val;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String testMethod(Person person, Object obj) {
+ return "testMethod(Person person, Object obj): " + person + ", " + obj;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String testMethod(Object obj, Person person) {
+ return "testMethod(Object obj, Person person): " + obj + ", " + person;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Person[] testArray(Person[] persons) {
+ return persons;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Collection<Person> testCollection(Collection<Person> persons) {
+ return persons;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Map<Integer, Person> testMap(Map<Integer, Person> persons) {
+ return persons;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object testException() {
+ throw new IllegalStateException("testException()");
+ }
+
+ /** {@inheritDoc} */
+ @Override public void sleep(long millis) {
+ long ts = U.currentTimeMillis();
+
+ doSleep(millis);
+
+ // Make sure cached timestamp updated.
+ while (ts + millis >= U.currentTimeMillis())
+ doSleep(100L);
+ }
+ }
+
+ /**
+ * Service to return ID of node where method was executed.
+ */
+ public static interface TestNodeIdServiceInterface {
+ /** Gets ID of node where method was executed */
+ public UUID nodeId();
+ }
+
+ /**
+ * Implementation of TestNodeIdServiceInterface
+ */
+ public static class TestNodeIdService implements Service, TestNodeIdServiceInterface {
+ /** Ignite. */
+ @IgniteInstanceResource
+ Ignite ignite;
+
+ /** {@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.
+ }
+
+ /** {@inheritDoc} */
+ @Override public UUID nodeId() {
+ return ignite.cluster().localNode().id();
+ }
+ }
+}
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
index 8609314..bb5239b 100644
--- a/modules/core/src/test/java/org/apache/ignite/platform/AbstractPlatformServiceCallTask.java
+++ b/modules/core/src/test/java/org/apache/ignite/platform/AbstractPlatformServiceCallTask.java
@@ -27,15 +27,19 @@ import java.util.Set;
import java.util.UUID;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteException;
+import org.apache.ignite.Ignition;
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.client.IgniteClient;
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.configuration.ClientConfiguration;
+import org.apache.ignite.internal.processors.odbc.ClientListenerProcessor;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.resources.IgniteInstanceResource;
import org.apache.ignite.services.ServiceDescriptor;
@@ -97,6 +101,11 @@ public abstract class AbstractPlatformServiceCallTask extends ComputeTaskAdapter
/** */
protected abstract static class AbstractServiceCallJob extends ComputeJobAdapter {
/** */
+ @SuppressWarnings("unused")
+ @IgniteInstanceResource
+ protected transient Ignite ignite;
+
+ /** */
protected final String srvcName;
/**
@@ -121,9 +130,30 @@ public abstract class AbstractPlatformServiceCallTask extends ComputeTaskAdapter
}
/**
+ * Gets service proxy.
+ */
+ TestPlatformService serviceProxy() {
+ return ignite.services().serviceProxy(srvcName, TestPlatformService.class, false);
+ }
+
+ /**
* Test method to call platform service.
*/
abstract void runTest();
+
+ /**
+ * Start thin client connected to current ignite instance.
+ */
+ IgniteClient startClient() {
+ ClusterNode node = ignite.cluster().localNode();
+
+ String addr = F.first(node.addresses()) + ":" + node.attribute(ClientListenerProcessor.CLIENT_LISTENER_PORT);
+
+ return Ignition.startClient(new ClientConfiguration()
+ .setAddresses(addr)
+ .setBinaryConfiguration(ignite.configuration().getBinaryConfiguration())
+ );
+ }
}
/** */
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
index 7e2806b..65f48d9 100644
--- a/modules/core/src/test/java/org/apache/ignite/platform/PlatformServiceCallCollectionsTask.java
+++ b/modules/core/src/test/java/org/apache/ignite/platform/PlatformServiceCallCollectionsTask.java
@@ -22,10 +22,8 @@ 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.
@@ -38,11 +36,6 @@ public class PlatformServiceCallCollectionsTask extends AbstractPlatformServiceC
/** */
public static class PlatformServiceCallCollectionsJob extends AbstractServiceCallJob {
- /** */
- @SuppressWarnings("unused")
- @IgniteInstanceResource
- private transient Ignite ignite;
-
/**
* @param svcName Service name.
*/
@@ -52,7 +45,7 @@ public class PlatformServiceCallCollectionsTask extends AbstractPlatformServiceC
/** {@inheritDoc} */
@Override void runTest() {
- TestPlatformService srv = ignite.services().serviceProxy(srvcName, TestPlatformService.class, false);
+ TestPlatformService srv = serviceProxy();
{
TestValue[] exp = IntStream.range(0, 10).mapToObj(i -> new TestValue(i, "name_" + i))
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/PlatformServiceCallCollectionsThinTask.java b/modules/core/src/test/java/org/apache/ignite/platform/PlatformServiceCallCollectionsThinTask.java
new file mode 100644
index 0000000..d874edf
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/platform/PlatformServiceCallCollectionsThinTask.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.platform;
+
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.compute.ComputeJobAdapter;
+import org.apache.ignite.internal.processors.platform.services.PlatformService;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ * Test invoke {@link PlatformService} methods with collections and arrays as arguments and return type from
+ * java thin client.
+ */
+public class PlatformServiceCallCollectionsThinTask extends AbstractPlatformServiceCallTask {
+ /** {@inheritDoc} */
+ @Override ComputeJobAdapter createJob(String svcName) {
+ return new PlatformServiceCallCollectionsThinJob(svcName);
+ }
+
+ /** */
+ static class PlatformServiceCallCollectionsThinJob extends
+ PlatformServiceCallCollectionsTask.PlatformServiceCallCollectionsJob {
+ /** Thin client. */
+ IgniteClient client;
+
+ /**
+ * @param srvcName Service name.
+ */
+ PlatformServiceCallCollectionsThinJob(String srvcName) {
+ super(srvcName);
+ }
+
+ /** {@inheritDoc} */
+ @Override TestPlatformService serviceProxy() {
+ return client.services().serviceProxy(srvcName, TestPlatformService.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override void runTest() {
+ client = startClient();
+
+ try {
+ super.runTest();
+ }
+ finally {
+ U.close(client, ignite.log().getLogger(getClass()));
+ }
+ }
+ }
+}
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
index f39a3ee..97c0d54 100644
--- a/modules/core/src/test/java/org/apache/ignite/platform/PlatformServiceCallTask.java
+++ b/modules/core/src/test/java/org/apache/ignite/platform/PlatformServiceCallTask.java
@@ -18,11 +18,9 @@
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;
/**
@@ -36,11 +34,6 @@ public class PlatformServiceCallTask extends AbstractPlatformServiceCallTask {
/** */
static class PlatformServiceCallJob extends AbstractServiceCallJob {
- /** */
- @SuppressWarnings("unused")
- @IgniteInstanceResource
- private transient Ignite ignite;
-
/**
* @param srvcName Service name.
*/
@@ -50,32 +43,41 @@ public class PlatformServiceCallTask extends AbstractPlatformServiceCallTask {
/** {@inheritDoc} */
@Override void runTest() {
- TestPlatformService srv = ignite.services().serviceProxy(srvcName, TestPlatformService.class, false);
+ TestPlatformService srv = serviceProxy();
- {
- UUID nodeId = srv.getNodeId();
- assertTrue(ignite.cluster().nodes().stream().anyMatch(n -> n.id().equals(nodeId)));
- }
+ checkNodeId(srv);
+ checkUuidProp(srv);
+ checkObjectProp(srv);
+ checkErrorMethod(srv);
+ }
- {
- UUID expUuid = UUID.randomUUID();
- srv.setGuidProp(expUuid);
- assertEquals(expUuid, srv.getGuidProp());
- }
+ /** */
+ protected void checkNodeId(TestPlatformService srv) {
+ UUID nodeId = srv.getNodeId();
+ assertTrue(ignite.cluster().nodes().stream().anyMatch(n -> n.id().equals(nodeId)));
+ }
- {
- TestValue exp = new TestValue(1, "test");
- srv.setValueProp(exp);
- assertEquals(exp, srv.getValueProp());
- }
+ /** */
+ protected void checkUuidProp(TestPlatformService srv) {
+ UUID expUuid = UUID.randomUUID();
+ srv.setGuidProp(expUuid);
+ assertEquals(expUuid, srv.getGuidProp());
+ }
- {
- PlatformNativeException nativeEx = (PlatformNativeException)GridTestUtils
- .assertThrowsWithCause(srv::errorMethod, PlatformNativeException.class)
- .getCause();
+ /** */
+ protected void checkObjectProp(TestPlatformService srv) {
+ TestValue exp = new TestValue(1, "test");
+ srv.setValueProp(exp);
+ assertEquals(exp, srv.getValueProp());
+ }
+
+ /** */
+ protected void checkErrorMethod(TestPlatformService srv) {
+ PlatformNativeException nativeEx = (PlatformNativeException)GridTestUtils
+ .assertThrowsWithCause(srv::errorMethod, PlatformNativeException.class)
+ .getCause();
- assertTrue(nativeEx.toString().contains("Failed method"));
- }
+ assertTrue(nativeEx.toString().contains("Failed method"));
}
}
}
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/PlatformServiceCallThinTask.java b/modules/core/src/test/java/org/apache/ignite/platform/PlatformServiceCallThinTask.java
new file mode 100644
index 0000000..a1c840f
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/platform/PlatformServiceCallThinTask.java
@@ -0,0 +1,76 @@
+/*
+ * 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 org.apache.ignite.client.ClientException;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.compute.ComputeJobAdapter;
+import org.apache.ignite.internal.processors.platform.services.PlatformService;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.testframework.GridTestUtils;
+
+/**
+ * Basic task to calling {@link PlatformService} from java thin client.
+ */
+public class PlatformServiceCallThinTask extends AbstractPlatformServiceCallTask {
+ /** {@inheritDoc} */
+ @Override ComputeJobAdapter createJob(String svcName) {
+ return new PlatformServiceCallThinJob(svcName);
+ }
+
+ /** */
+ static class PlatformServiceCallThinJob extends PlatformServiceCallTask.PlatformServiceCallJob {
+ /** Thin client. */
+ IgniteClient client;
+
+ /**
+ * @param srvcName Service name.
+ */
+ PlatformServiceCallThinJob(String srvcName) {
+ super(srvcName);
+ }
+
+ /** {@inheritDoc} */
+ @Override TestPlatformService serviceProxy() {
+ return client.services().serviceProxy(srvcName, TestPlatformService.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override void runTest() {
+ client = startClient();
+
+ try {
+ super.runTest();
+ }
+ finally {
+ U.close(client, ignite.log().getLogger(getClass()));
+ }
+ }
+
+ /** */
+ @Override protected void checkErrorMethod(TestPlatformService srv) {
+ // For thin client only top level error message is passed from server to client, so we should override
+ // method for error check.
+ GridTestUtils.assertThrowsAnyCause(null, () -> {
+ srv.errorMethod();
+ return null;
+ }, ClientException.class, "Failed to invoke platform service");
+ }
+
+ }
+}
diff --git a/modules/indexing/src/test/java/org/apache/ignite/client/ClientTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/client/ClientTestSuite.java
index 07ac59f..c093efb 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/client/ClientTestSuite.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/client/ClientTestSuite.java
@@ -20,6 +20,7 @@ package org.apache.ignite.client;
import org.apache.ignite.internal.client.thin.ClusterApiTest;
import org.apache.ignite.internal.client.thin.ClusterGroupTest;
import org.apache.ignite.internal.client.thin.ComputeTaskTest;
+import org.apache.ignite.internal.client.thin.ServicesTest;
import org.apache.ignite.internal.client.thin.ThinClientPartitionAwarenessResourceReleaseTest;
import org.apache.ignite.internal.client.thin.ThinClientPartitionAwarenessStableTopologyTest;
import org.apache.ignite.internal.client.thin.ThinClientPartitionAwarenessUnstableTopologyTest;
@@ -47,6 +48,7 @@ import org.junit.runners.Suite;
ComputeTaskTest.class,
ClusterApiTest.class,
ClusterGroupTest.class,
+ ServicesTest.class,
ThinClientPartitionAwarenessStableTopologyTest.class,
ThinClientPartitionAwarenessUnstableTopologyTest.class,
ThinClientPartitionAwarenessResourceReleaseTest.class
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/CallPlatformServiceTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/CallPlatformServiceTest.cs
index b06671a..3efe0e8 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/CallPlatformServiceTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/CallPlatformServiceTest.cs
@@ -40,6 +40,13 @@ namespace Apache.Ignite.Core.Tests.Services
private const string CheckCollectionsTaskName = "org.apache.ignite.platform.PlatformServiceCallCollectionsTask";
/** */
+ private const string CheckThinTaskName = "org.apache.ignite.platform.PlatformServiceCallThinTask";
+
+ /** */
+ private const string CheckCollectionsThinTaskName =
+ "org.apache.ignite.platform.PlatformServiceCallCollectionsThinTask";
+
+ /** */
protected IIgnite Grid1;
/** */
@@ -70,11 +77,13 @@ namespace Apache.Ignite.Core.Tests.Services
/// 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>
+ /// <param name="taskName">Task to test.</param>
/// </summary>
[Test]
- public void TestCallPlatformService([Values(true, false)] bool local)
+ public void TestCallPlatformService([Values(true, false)] bool local,
+ [Values(CheckTaskName, CheckCollectionsTaskName, CheckThinTaskName, CheckCollectionsThinTaskName)]
+ string taskName)
{
var cfg = new ServiceConfiguration
{
@@ -85,31 +94,9 @@ namespace Apache.Ignite.Core.Tests.Services
Grid1.GetServices().Deploy(cfg);
- Grid1.GetCompute().ExecuteJavaTask<object>(CheckTaskName, new object[] { ServiceName, local });
+ Grid1.GetCompute().ExecuteJavaTask<object>(taskName, 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>
diff --git a/modules/yardstick/config/benchmark-thin-services.properties b/modules/yardstick/config/benchmark-thin-services.properties
new file mode 100644
index 0000000..9de1c74
--- /dev/null
+++ b/modules/yardstick/config/benchmark-thin-services.properties
@@ -0,0 +1,78 @@
+# 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.
+
+#
+# Contains benchmarks for distributed computations.
+#
+
+# JVM options.
+JVM_OPTS=${JVM_OPTS}" -DIGNITE_QUIET=false"
+
+# Uncomment to enable concurrent garbage collection (GC) if you encounter long GC pauses.
+# JVM_OPTS=${JVM_OPTS}" \
+# -Xms6g \
+# -Xmx6g \
+# -Xloggc:./gc${now0}.log \
+# -XX:+PrintGCDetails \
+# -verbose:gc \
+# -XX:+UseParNewGC \
+# -XX:+UseConcMarkSweepGC \
+# "
+
+#Ignite version
+ver="RELEASE-"
+
+# List of default probes.
+# Add DStatProbe or VmStatProbe if your OS supports it (e.g. if running on Linux).
+BENCHMARK_DEFAULT_PROBES=ThroughputLatencyProbe,PercentileProbe
+
+# Packages where the specified benchmark is searched by reflection mechanism.
+BENCHMARK_PACKAGES=org.yardstickframework,org.apache.ignite.yardstick
+
+# Probe point writer class name.
+# BENCHMARK_WRITER=
+
+# Comma-separated list of the hosts to run BenchmarkServers on. 2 nodes on local host are enabled by default.
+SERVER_HOSTS=localhost,localhost
+
+# Comma-separated list of the hosts to run BenchmarkDrivers on. 1 node on local host is enabled by default.
+DRIVER_HOSTS=localhost
+
+# Remote username.
+# REMOTE_USER=
+
+# Number of nodes, used to wait for the specified number of nodes to start.
+nodesNum=$((`echo ${SERVER_HOSTS} | tr ',' '\n' | wc -l` + `echo ${DRIVER_HOSTS} | tr ',' '\n' | wc -l`))
+
+# Backups count.
+b=1
+
+# Warmup.
+w=60
+
+# Duration.
+d=180
+
+# Threads count.
+t=64
+
+# Sync mode.
+sm=PRIMARY_SYNC
+
+# Run configuration.
+# Note that each benchmark is set to run for 300 seconds (5 min) with warm-up set to 60 seconds (1 minute).
+CONFIGS="\
+-cfg ${SCRIPT_DIR}/../config/ignite-services-config.xml -nn ${nodesNum} -b ${b} -w ${w} -d ${d} -t ${t} -sm ${sm} -dn IgniteThinServiceInvocationBenchmark -sn IgniteNode -ds ${ver}thin-service-invoke\
+"
diff --git a/modules/yardstick/config/ignite-services-config.xml b/modules/yardstick/config/ignite-services-config.xml
new file mode 100644
index 0000000..33a1183
--- /dev/null
+++ b/modules/yardstick/config/ignite-services-config.xml
@@ -0,0 +1,51 @@
+<?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.
+-->
+
+<!--
+ Ignite Spring configuration file to startup grid.
+-->
+<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-2.5.xsd">
+ <import resource="ignite-base-config.xml"/>
+
+ <bean id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration" parent="base-ignite.cfg">
+ <property name="discoverySpi">
+ <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
+ <property name="ipFinder">
+ <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder"/>
+ </property>
+ </bean>
+ </property>
+
+ <property name="serviceConfiguration">
+ <list>
+ <bean class="org.apache.ignite.services.ServiceConfiguration">
+ <property name="name" value="SimpleService"/>
+ <property name="maxPerNodeCount" value="1"/>
+ <property name="totalCount" value="1"/>
+ <property name="service">
+ <bean class="org.apache.ignite.yardstick.thin.service.SimpleServiceImpl"/>
+ </property>
+ </bean>
+ </list>
+ </property>
+ </bean>
+</beans>
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/thin/service/IgniteThinServiceInvocationBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/thin/service/IgniteThinServiceInvocationBenchmark.java
new file mode 100644
index 0000000..a17ab5d
--- /dev/null
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/thin/service/IgniteThinServiceInvocationBenchmark.java
@@ -0,0 +1,45 @@
+/*
+ * 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.yardstick.thin.service;
+
+import java.util.Map;
+import org.apache.ignite.yardstick.IgniteThinAbstractBenchmark;
+import org.yardstickframework.BenchmarkConfiguration;
+
+/**
+ * Class to benchmark thin client service invocation.
+ */
+public class IgniteThinServiceInvocationBenchmark extends IgniteThinAbstractBenchmark {
+ /** Service proxy. */
+ private volatile ThreadLocal<SimpleService> srvcProxy;
+
+ /** {@inheritDoc} */
+ @Override public void setUp(BenchmarkConfiguration cfg) throws Exception {
+ super.setUp(cfg);
+
+ srvcProxy = ThreadLocal.withInitial(
+ () -> client().services().serviceProxy(SimpleService.class.getSimpleName(), SimpleService.class));
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean test(Map<Object, Object> map) throws Exception {
+ srvcProxy.get().echo(nextRandom(Integer.MAX_VALUE));
+
+ return true;
+ }
+}
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/thin/service/SimpleService.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/thin/service/SimpleService.java
new file mode 100644
index 0000000..5447a28
--- /dev/null
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/thin/service/SimpleService.java
@@ -0,0 +1,28 @@
+/*
+ * 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.yardstick.thin.service;
+
+/**
+ * Simple service interface.
+ */
+public interface SimpleService {
+ /**
+ * @param val Value.
+ */
+ public int echo(int val);
+}
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/thin/service/SimpleServiceImpl.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/thin/service/SimpleServiceImpl.java
new file mode 100644
index 0000000..1cf108f
--- /dev/null
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/thin/service/SimpleServiceImpl.java
@@ -0,0 +1,46 @@
+/*
+ * 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.yardstick.thin.service;
+
+import org.apache.ignite.services.Service;
+import org.apache.ignite.services.ServiceContext;
+
+/**
+ * Simple service implementation.
+ */
+public class SimpleServiceImpl implements SimpleService, Service {
+ /** {@inheritDoc} */
+ @Override public void cancel(ServiceContext ctx) {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public void init(ServiceContext ctx) {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public void execute(ServiceContext ctx) {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public int echo(int val) {
+ return val;
+ }
+}