package org.jgroups.blocks;
import org.jgroups.*;
import org.jgroups.protocols.FRAG;
import org.jgroups.protocols.FRAG2;
import org.jgroups.protocols.TP;
import org.jgroups.stack.Protocol;
import org.jgroups.util.RpcStats;
import org.jgroups.util.Rsp;
import org.jgroups.util.RspList;
import org.jgroups.util.Util;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Stream;
/**
* A collection of tests to test the RpcDispatcher.
*
* NOTE on processing return values:
*
* The method RspDispatcher.callRemoteMethods(...) returns an RspList, containing one Rsp
* object for each group member receiving the RPC call. Rsp.getValue() returns the
* value returned by the RPC call from the corresponding member. Rsp.getValue() may
* contain several classes of values, depending on what happened during the call:
*
* (i) a value of the expected return data type, if the RPC call completed successfully
* (ii) null, if the RPC call timed out before the value could be returned
* (iii) an object of type java.lang.Throwable, if an exception (e.g. lava.lang.OutOfMemoryException)
* was raised during the processing of the call
*
* It is wise to check for such cases when processing RpcDispatcher calls.
*
* This also applies to the return value of callRemoteMethod(...).
*
* @author Bela Ban
*/
@Test(groups=Global.FUNCTIONAL,singleThreaded=true)
public class RpcDispatcherTest {
protected RpcDispatcher da, db, dc;
protected JChannel a, b, c;
protected static final String GROUP="RpcDispatcherTest";
// specify return values sizes which should work correctly with a 64Mb heap
final static int[] SIZES={10000, 20000, 40000, 80000, 100000, 200000, 400000, 800000,
1000000, 2000000, 5000000};
// timeout (in secs) for large value tests
final static int LARGE_VALUE_TIMEOUT = 60;
@BeforeMethod
protected void setUp() throws Exception {
a=createChannel("A");
da=new RpcDispatcher(a, new ServerObject(1));
a.connect(GROUP);
b=createChannel("B");
db=new RpcDispatcher(b, new ServerObject(2));
b.connect(GROUP);
c=createChannel("C");
dc=new RpcDispatcher(c, new ServerObject(3));
c.connect(GROUP);
Util.waitUntilAllChannelsHaveSameView(10000, 1000, a, b, c);
System.out.println("A=" + a.getView() + "\nB=" + b.getView() + "\nC=" + c.getView());
}
@AfterMethod
protected void tearDown() throws Exception {
Util.close(dc, db, da, c, b, a);
}
public void testEmptyConstructor() throws Exception {
RpcDispatcher d1=new RpcDispatcher(), d2=new RpcDispatcher();
JChannel d=null, e=null;
try {
d=createChannel("D");
e=createChannel("E");
d1.setChannel(d);
d2.setChannel(e);
d1.setServerObject(new ServerObject(1));
d2.setServerObject(new ServerObject(2));
d1.start();
d2.start();
d.connect("RpcDispatcherTest-DifferentGroup");
e.connect("RpcDispatcherTest-DifferentGroup");
Util.sleep(500);
View view=e.getView();
System.out.println("view channel 2= " + view);
view=d.getView();
System.out.println("view channel 1= " + view);
assert view.size() == 2;
RspList<Integer> rsps=d1.callRemoteMethods(null, "foo", null, null, new RequestOptions(ResponseMode.GET_ALL, 5000));
System.out.println("rsps:\n" + rsps);
assert rsps.size() == 2;
for(Rsp<Integer> rsp: rsps.values()) {
assert rsp.wasReceived();
assert !rsp.wasSuspected();
assert rsp.getValue() != null;
}
Object server_object=new Object() {
public long foobar() {
return System.currentTimeMillis();
}
};
d1.setServerObject(server_object);
d2.setServerObject(server_object);
rsps=d2.callRemoteMethods(null, "foobar", null, null, new RequestOptions(ResponseMode.GET_ALL, 5000));
System.out.println("rsps:\n" + rsps);
assert rsps.size() == 2;
for(Rsp rsp: rsps.values()) {
assert rsp.wasReceived();
assert !rsp.wasSuspected();
assert rsp.getValue() != null;
}
}
finally {
d2.stop();
d1.stop();
Util.close(e, d);
}
}
public void testException() throws Exception {
RspList<Object> rsps=da.callRemoteMethods(null, "throwException", null, null, new RequestOptions(ResponseMode.GET_ALL, 5000));
rsps.values().forEach(System.out::println);
for(Rsp<Object> rsp: rsps.values())
assert rsp.getException() != null && rsp.getValue() == null;
}
public void testExceptionAsReturnValue() throws Exception {
RspList<Object> rsps=da.callRemoteMethods(null, "returnException", null, null, new RequestOptions(ResponseMode.GET_ALL, 5000));
rsps.values().forEach(System.out::println);
for(Rsp<Object> rsp: rsps.values())
assert rsp.getException() == null && rsp.getValue() != null && rsp.getValue() instanceof Throwable;
}
public void testUnicastInvocation() throws Exception {
RequestOptions opts=RequestOptions.SYNC().timeout(2000);
Void result=da.callRemoteMethod(b.getAddress(), "bar", null, null, opts);
assert result == null;
Integer res=da.callRemoteMethod(b.getAddress(), "foo", null, null, opts);
assert res != null && res == 2;
}
public void testUnicastInvocationWithTimeout() throws Exception {
RequestOptions opts=RequestOptions.SYNC().timeout(1000);
Method meth=ServerObject.class.getDeclaredMethod("sleep", long.class);
long start=System.currentTimeMillis();
try {
da.callRemoteMethod(b.getAddress(), new MethodCall(meth, 5000), opts);
assert false: "should have thrown a TimeoutException";
}
catch(TimeoutException ex) {
long time=System.currentTimeMillis()-start;
System.out.printf("received %s as expected; call took ~%d ms\n", ex, time);
}
}
public void testUnicastInvocationWithFutureAndTimeout() throws Exception {
RequestOptions opts=RequestOptions.SYNC().timeout(6000);
Method meth=ServerObject.class.getDeclaredMethod("sleep", long.class);
CompletableFuture<Long> future;
long start=System.currentTimeMillis();
future=da.callRemoteMethodWithFuture(b.getAddress(), new MethodCall(meth, 5000), opts);
try {
future.get(1000, TimeUnit.MILLISECONDS);
assert false : "should have thrown a TimeoutException";
}
catch(TimeoutException ex) {
long time=System.currentTimeMillis()-start;
System.out.printf("received %s as expected; call took ~%d ms\n", ex, time);
}
}
public void testUnicastInvocationWithFuture() throws Exception {
RequestOptions opts=RequestOptions.SYNC().timeout(2000).flags(Message.Flag.OOB);
MethodCall call=new MethodCall("bar", null, null);
CompletableFuture<Void> future=da.callRemoteMethodWithFuture(b.getAddress(), call, opts);
Void result=future.get(10000, TimeUnit.MILLISECONDS);
assert result == null;
call=new MethodCall("foo", null, null);
CompletableFuture<Integer> fut=da.callRemoteMethodWithFuture(b.getAddress(), call, opts);
Integer res=fut.get(10000, TimeUnit.MILLISECONDS);
assert res != null && res == 2;
Method meth=ServerObject.class.getDeclaredMethod("sleep", long.class);
try {
CompletableFuture<Long> f=da.callRemoteMethodWithFuture(b.getAddress(), new MethodCall(meth, 5000), opts);
f.get(100, TimeUnit.MILLISECONDS);
assert false: "should have thrown a TimeoutException";
}
catch(TimeoutException ex) {
System.out.printf("received %s as expected\n", ex);
}
try {
meth=ServerObject.class.getDeclaredMethod("throwException");
call=new MethodCall(meth);
CompletableFuture<Object> f=da.callRemoteMethodWithFuture(b.getAddress(), call, opts);
f.get();
assert false : "should have thrown ExecutionException";
}
catch(ExecutionException ex) {
System.out.printf("received %s as expected\n", ex);
assert ex.getCause() instanceof Exception;
}
}
public void testUnicastException() {
try {
da.callRemoteMethod(b.getAddress(), "throwException", null, null, new RequestOptions(ResponseMode.GET_ALL, 5000));
}
catch(Throwable throwable) {
System.out.println("received exception (as expected)");
}
}
public void testUnicastExceptionNested() {
try {
da.callRemoteMethod(b.getAddress(), "throwExceptionNested", null, null, new RequestOptions(ResponseMode.GET_ALL, 5000));
}
catch(Throwable throwable) {
System.out.printf("received exception (as expected): %s\n", throwable);
assert throwable instanceof IllegalArgumentException;
assert throwable.getCause() instanceof NullPointerException;
}
}
public void testAsyncUnicast() throws Exception {
MethodCall call=new MethodCall(ServerObject.class.getMethod("foo"));
Integer result=da.callRemoteMethod(b.getAddress(), call, RequestOptions.ASYNC());
assert result == null;
}
public void testAsyncUnicastWithFuture() throws Exception {
MethodCall call=new MethodCall(ServerObject.class.getMethod("throwException"));
Future<Object> future=da.callRemoteMethodWithFuture(b.getAddress(), call, RequestOptions.ASYNC());
assert future == null;
}
public void testUnicastExceptionWithFuture() {
try {
MethodCall call=new MethodCall(ServerObject.class.getMethod("throwException"));
Future<Object> future=da.callRemoteMethodWithFuture(b.getAddress(), call, new RequestOptions(ResponseMode.GET_ALL, 5000));
Object val=future.get();
assert val == null;
assert false : " should not get here";
}
catch(Throwable throwable) {
System.out.println("received exception (as expected): " + throwable);
}
}
public void testUnicastExceptionAsReturnValue() throws Exception {
Object rsp=da.callRemoteMethod(b.getAddress(), "returnException", null, null, new RequestOptions(ResponseMode.GET_ALL, 5000));
System.out.println("rsp = " + rsp);
assert rsp != null && rsp instanceof Throwable;
}
public void testUnicastExceptionAsReturnValueWithFuture() throws Exception {
MethodCall call=new MethodCall(ServerObject.class.getMethod("returnException"));
Future<Object> future=da.callRemoteMethodWithFuture(b.getAddress(), call, new RequestOptions(ResponseMode.GET_ALL, 5000));
Object val=future.get();
assert val instanceof Exception;
}
public void testMulticastInvocationWithMethodLookup() throws Exception {
MethodCall call=new MethodCall((short)6, 3, 4); // ServerObject.add()
Stream.of(da,db,dc).forEach(d -> d.setMethodLookup(id -> ServerObject.methods[id]));
RspList<Integer> rsps=da.callRemoteMethods(null, call, RequestOptions.SYNC());
System.out.printf("rsps:\n%s\n", rsps);
assert rsps != null;
assert rsps.size() == 3;
for(Rsp<Integer> rsp: rsps.values())
assert rsp.getValue() != null && rsp.getValue().equals(7);
}
public void testMulticastInvocationWithTimeout() throws Exception {
RequestOptions opts=RequestOptions.SYNC().timeout(1000);
Method meth=ServerObject.class.getDeclaredMethod("sleep", long.class);
long start=System.currentTimeMillis();
RspList<Long> rsps=da.callRemoteMethods(null, new MethodCall(meth, 5000), opts);
long time=System.currentTimeMillis()-start;
System.out.printf("responses:\n%s\ncall took ~%d ms\n", rsps, time);
rsps.values().stream().allMatch(rsp -> !rsp.wasReceived());
}
public void testMulticastInvocationWithFutureAndTimeout() throws Exception {
RequestOptions opts=RequestOptions.SYNC().timeout(1000);
Method meth=ServerObject.class.getDeclaredMethod("sleep", long.class);
CompletableFuture<RspList<Long>> future=da.callRemoteMethodsWithFuture(null, new MethodCall(meth, 5000), opts);
RspList<Long> rsps=future.get(100, TimeUnit.MILLISECONDS);
System.out.printf("rsps:\n%s\n", rsps);
assert rsps != null;
assert !rsps.values().stream().anyMatch(Rsp::wasReceived);
}
/**
* Test the response filter mechanism which can be used to filter responses received with
* a call to RpcDispatcher.
*
* The test filters requests based on the id of the server object they were received
* from, and only accept responses from servers with id > 1.
*
* The expected behaviour is that the response from server 1 is rejected, but the responses
* from servers 2 and 3 are accepted.
*
*/
public void testResponseFilter() throws Exception {
RequestOptions options=new RequestOptions(ResponseMode.GET_ALL, 10000, false,
new RspFilter() {
int num=0;
public boolean isAcceptable(Object response, Address sender) {
boolean retval=(Integer)response > 1;
if(retval)
num++;
return retval;
}
public boolean needMoreResponses() {
return num < 2;
}
});
RspList rsps=da.callRemoteMethods(null, "foo", null, null, options);
System.out.println("responses are:\n" + rsps);
assert rsps.size() == 3 : "there should be three response values";
assert rsps.numReceived() == 2 : "number of responses received should be 2";
}
/** Test a unicast blocking RPC with a stupid response filter which never terminates */
public void testResponseFilterWithUnicast() throws Exception {
RequestOptions options=RequestOptions.SYNC().timeout(5000).rspFilter(
new RspFilter() {
public boolean isAcceptable(Object response, Address sender) {return false;}
public boolean needMoreResponses() {return true;}
});
Object retval=da.callRemoteMethod(b.getAddress(), "bar", null, null, options);
System.out.println("retval = " + retval);
assert retval == null;
}
/**
* Tests an incorrect response filter which always returns false for isAcceptable() and true for needsMoreResponses().
* The call should return anyway after having received all responses, even if none of them was accepted by the
* filter.
*/
public void testNonTerminatingResponseFilter() throws Exception {
RequestOptions options=new RequestOptions(ResponseMode.GET_ALL, 10000, false,
new RspFilter() {
public boolean isAcceptable(Object response, Address sender) {
return false;
}
public boolean needMoreResponses() {return true;}
});
RspList rsps=da.callRemoteMethods(null, "foo", null, null, options);
System.out.println("responses are:\n" + rsps);
Util.assertEquals("there should be three response values", 3, rsps.size());
Util.assertEquals("number of responses received should be 3", 0, rsps.numReceived());
}
/**
* Runs with response mode of GET_FIRST and the response filter accepts only the last response
* @throws Exception
*/
public void testAcceptLastResponseFilter() throws Exception {
RequestOptions options=new RequestOptions(ResponseMode.GET_FIRST, 10000, false,
new RspFilter() {
int count=0;
public boolean isAcceptable(Object response, Address sender) {
return ++count >= 3;
}
public boolean needMoreResponses() {return count < 3;}
});
RspList rsps=da.callRemoteMethods(null, "foo", null, null, options);
System.out.println("responses are:\n" + rsps);
Util.assertEquals("there should be three response values", 3, rsps.size());
Util.assertEquals("number of responses received should be 3", 1, rsps.numReceived());
}
public void testFuture() throws Exception {
MethodCall sleep=new MethodCall("sleep", new Object[]{5000L}, new Class[]{long.class});
CompletableFuture<RspList<Long>> future=da.callRemoteMethodsWithFuture(null, sleep,
new RequestOptions(ResponseMode.GET_ALL, 5000L, false, null));
assert !future.isDone();
assert !future.isCancelled();
RspList<Long> rsps=future.get(300, TimeUnit.MILLISECONDS);
long num_not_received=rsps.values().stream().filter(rsp -> !rsp.wasReceived()).count();
System.out.printf("rsps:\n%s\nnot received: %d\n", rsps, num_not_received);
assert rsps.size() == 3;
assert num_not_received == 3 : "none of the 3 requests should have received a response, rsps:\n" + rsps;
assert future.isDone();
}
public void testNotifyingFuture() throws Exception {
MethodCall sleep=new MethodCall("sleep", new Object[]{1000L}, new Class[]{long.class});
CompletableFuture<RspList<Long>> future=da.callRemoteMethodsWithFuture(null, sleep, new RequestOptions(ResponseMode.GET_ALL, 5000L));
assert !future.isDone();
assert !future.isCancelled();
for(int i=0; i < 10; i++) {
if(future.isDone())
break;
Util.sleep(1000);
}
assert future.isDone();
RspList<Long> result=future.get(1L, TimeUnit.MILLISECONDS);
System.out.println("result:\n" + result);
assert result != null;
assert result.size() == 3;
assert future.isDone();
RspList<Long> result2=future.get();
System.out.println("result2:\n" + result2);
assert result2 != null;
assert result2.size() == 3;
assert future.isDone();
}
public void testNotifyingFutureWithDelayedListener() throws Exception {
MethodCall sleep=new MethodCall("sleep", new Object[]{100L}, new Class[]{long.class});
CompletableFuture<RspList<Long>> future=da.callRemoteMethodsWithFuture(null, sleep, new RequestOptions(ResponseMode.GET_ALL,5000L));
assert !future.isDone();
assert !future.isCancelled();
Util.sleep(2000);
assert future.isDone();
RspList result=future.get(1L, TimeUnit.MILLISECONDS);
System.out.println("result:\n" + result);
assert result != null;
assert result.size() == 3;
}
/**
* Invoke a call which sleeps for 5s 5 times. Since the sleep should be done in parallel (OOB msgs), all 5 futures
* should be done in roughly 5s. JIRA: https://issues.jboss.org/browse/JGRP-2039
*/
public void testMultipleFutures() throws Exception {
final int NUM_CALLS=5, MAX_SLEEP=10000; // should be done in ~5s, make it 10 to be on the safe side
MethodCall sleep=new MethodCall("sleep", new Object[]{5000L}, new Class[]{long.class});
List<Future<RspList<Long>>> futures=new ArrayList<>();
long target=System.currentTimeMillis() + MAX_SLEEP;
// if we didn't use DONT_BUNDLE, all OOB msgs from the same sender in a batch would be delivered sequentially!
RequestOptions options=new RequestOptions(ResponseMode.GET_ALL, 30000L)
.flags(Message.Flag.OOB, Message.Flag.DONT_BUNDLE);
for(int i=0; i < NUM_CALLS; i++) {
Future<RspList<Long>> future=da.callRemoteMethodsWithFuture(null, sleep, options);
futures.add(future);
}
List<Future<RspList<Long>>> rsps=new ArrayList<>();
while(!futures.isEmpty() && System.currentTimeMillis() < target) {
for(Iterator<Future<RspList<Long>>> it=futures.iterator(); it.hasNext();) {
Future<RspList<Long>> future=it.next();
if(future.isDone()) {
it.remove();
rsps.add(future);
}
}
Util.sleep(500);
}
System.out.println("\n" + rsps.size() + " responses:\n");
rsps.forEach(System.out::println);
assert rsps.size() == NUM_CALLS;
}
public void testMultipleNotifyingFutures() throws Exception {
MethodCall sleep=new MethodCall("sleep", new Object[]{100L}, new Class[]{long.class});
List<CompletableFuture<RspList<Long>>> listeners=new ArrayList<>();
RequestOptions options=new RequestOptions(ResponseMode.GET_ALL, 30000L);
for(int i=0; i < 10; i++) {
CompletableFuture<RspList<Long>> f=da.callRemoteMethodsWithFuture(null, sleep, options);
listeners.add(f);
}
Util.sleep(1000);
for(int i=0; i < 10; i++) {
boolean all_done=true;
for(CompletableFuture<RspList<Long>> listener: listeners) {
boolean done=listener.isDone();
System.out.print(done? "+ " : "- ");
if(!listener.isDone())
all_done=false;
}
if(all_done)
break;
Util.sleep(500);
System.out.println("");
}
for(CompletableFuture<RspList<Long>> listener: listeners)
assert listener.isDone();
}
public void testFutureCancel() throws Exception {
MethodCall sleep=new MethodCall("sleep", new Object[]{1000L}, new Class[]{long.class});
Future<RspList<Long>> future=da.callRemoteMethodsWithFuture(null, sleep, new RequestOptions(ResponseMode.GET_ALL, 5000L));
assert !future.isDone();
assert !future.isCancelled();
future.cancel(true);
assert future.isDone();
assert future.isCancelled();
future=da.callRemoteMethodsWithFuture(null, sleep, new RequestOptions(ResponseMode.GET_ALL, 0));
assert !future.isDone();
assert !future.isCancelled();
future.cancel(true);
assert future.isDone();
assert future.isCancelled();
}
/**
* Test the ability of RpcDispatcher to handle large argument and return values
* with multicast RPC calls.
*
* The test sends requests for return values (byte arrays) having increasing sizes,
* which increase the processing time for requests as well as the amount of memory
* required to process requests.
*
* The expected behaviour is that all RPC requests complete successfully.
*
*/
public void testLargeReturnValue() throws Exception {
setProps(a,b,c);
for(int i=0; i < SIZES.length; i++) {
_testLargeValue(SIZES[i]);
}
}
/**
* Tests a method call to {A,B,C} where C left *before* the call. http://jira.jboss.com/jira/browse/JGRP-620
*/
public void testMethodInvocationToNonExistingMembers() throws Exception {
final int timeout = 5 * 1000 ;
// get the current membership, as seen by C
View view=c.getView();
List<Address> members=view.getMembers();
System.out.println("list is " + members);
// cause C to leave the group and close its channel
System.out.println("closing c3");
c.close();
Util.sleep(1000);
// make an RPC call using C's now outdated view of membership
System.out.println("calling method foo() in " + members + " (view=" + b.getView() + ")");
RspList<Integer> rsps=da.callRemoteMethods(members, "foo", null, null, new RequestOptions(ResponseMode.GET_ALL, timeout));
// all responses
System.out.println("responses:\n" + rsps);
assert rsps.size() == 2;
for(Map.Entry<Address,Rsp<Integer>> entry: rsps.entrySet()) {
Rsp rsp=entry.getValue();
Util.assertTrue("response from " + entry.getKey() + " was received", rsp.wasReceived());
Util.assertFalse(rsp.wasSuspected());
}
List<Address> mbrs=new ArrayList<>(members);
mbrs.remove(b.getAddress());
System.out.println("calling method foo() in " + mbrs + " (view=" + b.getView() + ")");
rsps=da.callRemoteMethods(mbrs, "foo", null, null, new RequestOptions(ResponseMode.GET_ALL, timeout));
// all responses
System.out.println("responses:\n" + rsps);
assert rsps.size() == 1;
for(Map.Entry<Address,Rsp<Integer>> entry: rsps.entrySet()) {
Rsp rsp=entry.getValue();
Util.assertTrue("response from " + entry.getKey() + " was received", rsp.wasReceived());
Util.assertFalse(rsp.wasSuspected());
}
rsps=da.callRemoteMethods(mbrs, "foo", null, null,
new RequestOptions(ResponseMode.GET_ALL, timeout).transientFlags(Message.TransientFlag.DONT_LOOPBACK));
System.out.println("responses:\n" + rsps);
assert rsps.isEmpty();
mbrs.clear();
rsps=da.callRemoteMethods(mbrs, "foo", null, null,
new RequestOptions(ResponseMode.GET_ALL, timeout).transientFlags(Message.TransientFlag.DONT_LOOPBACK));
// all responses
System.out.println("responses:\n" + rsps);
assert rsps.isEmpty();
}
/**
* Test the ability of RpcDispatcher to handle large argument and return values
* with unicast RPC calls.
*
* The test sends requests for return values (byte arrays) having increasing sizes,
* which increase the processing time for requests as well as the amount of memory
* required to process requests.
*
* The expected behaviour is that all RPC requests complete successfully.
*
*/
public void testLargeReturnValueUnicastCall() throws Exception {
setProps(a,b,c);
for(int i=0; i < SIZES.length; i++) {
_testLargeValueUnicastCall(a.getAddress(), SIZES[i]);
}
}
public void testRpcStats() throws Exception {
Method meth=ServerObject.class.getDeclaredMethod("foo");
List<Address> targets=Arrays.asList(b.getAddress(), c.getAddress());
RpcStats stats=da.rpcStats().extendedStats(true);
// sync mcast with future
da.callRemoteMethodsWithFuture(null, new MethodCall(meth), RequestOptions.SYNC());
System.out.println("stats = " + stats);
assert stats.multicasts(true) == 1;
// async mcast with future
da.callRemoteMethodsWithFuture(null, new MethodCall(meth), RequestOptions.ASYNC());
System.out.println("stats = " + stats);
assert stats.multicasts(false) == 1;
// sync anycast with future
da.callRemoteMethodsWithFuture(targets, new MethodCall(meth), RequestOptions.SYNC().anycasting(true));
System.out.println("stats = " + stats);
assert stats.anycasts(true) == 1;
// async anycast with future
da.callRemoteMethodsWithFuture(targets, new MethodCall(meth), RequestOptions.ASYNC().anycasting(true));
System.out.println("stats = " + stats);
assert stats.anycasts(false) == 1;
// sync unicast with future
da.callRemoteMethodWithFuture(b.getAddress(), new MethodCall(meth), RequestOptions.SYNC());
System.out.println("stats = " + stats);
assert stats.unicasts(true) == 1;
// async unicast with future
da.callRemoteMethodWithFuture(b.getAddress(), new MethodCall(meth), RequestOptions.ASYNC());
System.out.println("stats = " + stats);
assert stats.unicasts(false) == 1;
// sync mcast
da.callRemoteMethods(null, new MethodCall(meth), RequestOptions.SYNC());
System.out.println("stats = " + stats);
assert stats.multicasts(true) == 2;
// async mcast
da.callRemoteMethods(null, new MethodCall(meth), RequestOptions.ASYNC());
System.out.println("stats = " + stats);
assert stats.multicasts(false) == 2;
// sync anycast
da.callRemoteMethods(targets, new MethodCall(meth), RequestOptions.SYNC().anycasting(true));
System.out.println("stats = " + stats);
assert stats.anycasts(true) == 2;
// async anycast
da.callRemoteMethods(targets, new MethodCall(meth), RequestOptions.ASYNC().anycasting(true));
System.out.println("stats = " + stats);
assert stats.anycasts(false) == 2;
// sync unicast
da.callRemoteMethod(b.getAddress(), new MethodCall(meth), RequestOptions.SYNC());
System.out.println("stats = " + stats);
assert stats.unicasts(true) == 2;
// async unicast
da.callRemoteMethod(b.getAddress(), new MethodCall(meth), RequestOptions.ASYNC());
System.out.println("stats = " + stats);
assert stats.unicasts(false) == 2;
}
protected static void setProps(JChannel... channels) {
for(JChannel ch: channels) {
Protocol prot=ch.getProtocolStack().findProtocol(FRAG2.class);
if(prot != null) {
((FRAG2)prot).setFragSize(12000);
}
prot=ch.getProtocolStack().findProtocol(FRAG.class);
if(prot != null) {
((FRAG)prot).setFragSize(12000);
}
prot=ch.getProtocolStack().getTransport();
if(prot != null)
((TP)prot).setMaxBundleSize(14000);
}
}
protected JChannel createChannel(String name) throws Exception {
return new JChannel(Util.getTestStack()).name(name);
}
/**
* Helper method to perform a RPC call on server method "returnValue(int size)" for
* all group members.
*
* The method checks that each returned value is non-null and has the correct size.
*
*/
void _testLargeValue(int size) throws Exception {
final long timeout = LARGE_VALUE_TIMEOUT * 1000 ;
System.out.println("\ntesting with " + size + " bytes");
long startTime = System.currentTimeMillis();
RspList<Object> rsps=da.callRemoteMethods(null, "largeReturnValue", new Object[]{size}, new Class[]{int.class},
new RequestOptions(ResponseMode.GET_ALL, timeout));
long stopTime = System.currentTimeMillis();
System.out.println("test took: " + (stopTime-startTime) + " ms");
System.out.println("rsps:");
assert rsps.size() == 3 : "there should be three responses to the RPC call but only " + rsps.size() +
" were received: " + rsps;
for(Map.Entry<Address,Rsp<Object>> entry: rsps.entrySet()) {
// its possible that an exception was raised in processing
Object obj = entry.getValue().getValue() ;
// this should not happen
assert !(obj instanceof Throwable) : "exception was raised in processing reasonably sized argument";
byte[] val=(byte[]) obj;
assert val != null;
System.out.println(val.length + " bytes from " + entry.getKey());
assert val.length == size : "return value does not match required size";
}
}
/**
* Helper method to perform a RPC call on server method "returnValue(int size)" for
* all group members.
*
* This method need to take into account that RPC calls can timeout with huge values,
* and they can also trigger OOMEs. But if we are lucky, they can also return
* reasonable values.
*
*/
void _testHugeValue(int size) throws Exception {
// 20 second timeout
final long timeout = 20 * 1000 ;
System.out.println("\ntesting with " + size + " bytes");
RspList<Object> rsps=da.callRemoteMethods(null, "largeReturnValue", new Object[]{size}, new Class[]{int.class},
new RequestOptions(ResponseMode.GET_ALL, timeout));
System.out.println("rsps:");
assert rsps != null;
assert rsps.size() == 3 : "there should be three responses to the RPC call but only " + rsps.size() +
" were received: " + rsps;
// in checking the return values, we need to take account of timeouts (i.e. when
// a null value is returned) and exceptions
for(Map.Entry<Address,Rsp<Object>> entry: rsps.entrySet()) {
Object obj = entry.getValue().getValue() ;
// its possible that an exception was raised
if (obj instanceof java.lang.Throwable) {
Throwable t = (Throwable) obj ;
System.out.println(t.toString() + " exception was raised processing argument from " +
entry.getKey() + " -this is expected") ;
continue ;
}
// its possible that the request timed out before the serve could reply
if (obj == null) {
System.out.println("request timed out processing argument from " +
entry.getKey() + " - this is expected") ;
continue ;
}
// if we reach here, we sould have a reasonable value
byte[] val=(byte[]) obj;
System.out.println(val.length + " bytes from " + entry.getKey());
assert val.length == size : "return value does not match required size";
}
}
/**
* Helper method to perform a RPC call on server method "returnValue(int size)" for
* an individual group member.
*
* The method checks that the returned value is non-null and has the correct size.
*
* @param dst the group member
* @param size the size of the byte array to be returned
*/
void _testLargeValueUnicastCall(Address dst, int size) throws Exception {
final long timeout = LARGE_VALUE_TIMEOUT * 1000 ;
System.out.println("\ntesting unicast call with " + size + " bytes");
Util.assertNotNull(dst);
long startTime = System.currentTimeMillis();
byte[] val=da.callRemoteMethod(dst, "largeReturnValue", new Object[]{size}, new Class[]{int.class},
new RequestOptions(ResponseMode.GET_ALL, timeout));
long stopTime = System.currentTimeMillis();
System.out.println("test took: " + (stopTime-startTime) + " ms");
// check value is not null, otherwise fail the test
Util.assertNotNull("return value should be non-null", val);
System.out.println("rsp: " + val.length + " bytes");
// returned value should have requested size
Util.assertEquals("return value does not match requested size", size, val.length);
}
/**
* This class serves as a server obect to turn requests into replies.
* It is initialised with an integer id value.
*
* It implements two functions:
* function foo() returns the id of the server
* function largeReturnValue(int size) returns a byte array of size 'size'
*/
protected static class ServerObject {
protected int i;
protected static final Method[] methods;
static {
try {
methods=new Method[] {
ServerObject.class.getDeclaredMethod("foo"), // index 0
ServerObject.class.getDeclaredMethod("bar"),
ServerObject.class.getDeclaredMethod("sleep", long.class),
ServerObject.class.getDeclaredMethod("throwException"),
ServerObject.class.getDeclaredMethod("returnException"),
ServerObject.class.getDeclaredMethod("largeReturnValue", int.class),
ServerObject.class.getDeclaredMethod("add", int.class, int.class) // index 6
};
}
catch(NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
public ServerObject(int i) {
this.i=i;
}
public int foo() {return i;}
public static void bar() {;}
public static long sleep(long timeout) {
long start=System.currentTimeMillis();
Util.sleep(timeout);
return System.currentTimeMillis() - start;
}
public static void throwException() throws Exception {
throw new Exception("booom");
}
public static Exception returnException() {
return new Exception("booom");
}
public static byte[] largeReturnValue(int size) {
return new byte[size];
}
public static int add(int a, int b) {return a+b;}
public static void throwExceptionNested() throws Exception {
Exception ex=new IllegalArgumentException("illegal argument - see cause for details");
Exception cause=new NullPointerException("the arg was null!");
ex.initCause(cause);
throw ex;
}
}
}