blob: 31e83c33f6c3f0d95e65e809ba9fc7773ffc72aa [file] [log] [blame]
/*
* 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 java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
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.util.typedef.F;
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);
TestService.latch = new CountDownLatch(1);
GridTestUtils.assertThrowsAnyCause(log, svc::waitLatch, ClientException.class, "timed out");
}
finally {
if (TestService.latch != null) {
TestService.latch.countDown();
TestService.latch = null;
}
}
}
/** */
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 boolean waitLatch() throws Exception;
}
/**
* Implementation of TestServiceInterface.
*/
public static class TestService implements Service, TestServiceInterface {
/** Latch. */
public static CountDownLatch latch;
/** {@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 boolean waitLatch() throws Exception {
latch.await(10L, TimeUnit.SECONDS);
return true;
}
}
/**
* 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();
}
}
}