Merge pull request #497 from pierredavidbelanger/unacknowledged-fire-and-forget

proof of concept for unacknowledged and/or fire-and-forget calls in RRemoteService
pull/508/head
Nikita Koksharov 9 years ago
commit 056c3a8afa

@ -15,6 +15,16 @@
*/
package org.redisson;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.ThreadLocalRandom;
import org.redisson.core.*;
import org.redisson.remote.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@ -23,27 +33,6 @@ import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.redisson.core.RBatch;
import org.redisson.core.RBlockingQueue;
import org.redisson.core.RBlockingQueueAsync;
import org.redisson.core.RRemoteService;
import org.redisson.remote.RRemoteServiceResponse;
import org.redisson.remote.RemoteServiceAck;
import org.redisson.remote.RemoteServiceAckTimeoutException;
import org.redisson.remote.RemoteServiceKey;
import org.redisson.remote.RemoteServiceMethod;
import org.redisson.remote.RemoteServiceRequest;
import org.redisson.remote.RemoteServiceResponse;
import org.redisson.remote.RemoteServiceTimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.ThreadLocalRandom;
/**
*
* @author Nikita Koksharov
@ -108,9 +97,10 @@ public class RedissonRemoteService implements RRemoteService {
// do not subscribe now, see https://github.com/mrniko/redisson/issues/493
// subscribe(remoteInterface, requestQueue);
final RemoteServiceRequest request = future.getNow();
if (System.currentTimeMillis() - request.getDate() > request.getAckTimeout()) {
// check the ack only if expected
if (request.getOptions().isAckExpected() && System.currentTimeMillis() - request.getDate() > request.getOptions().getAckTimeoutInMillis()) {
log.debug("request: {} has been skipped due to ackTimeout");
// re-subscribe after a skipped ackTimeout
subscribe(remoteInterface, requestQueue);
@ -119,24 +109,29 @@ public class RedissonRemoteService implements RRemoteService {
final RemoteServiceMethod method = beans.get(new RemoteServiceKey(remoteInterface, request.getMethodName()));
final String responseName = name + ":{" + remoteInterface.getName() + "}:" + request.getRequestId();
Future<List<?>> ackClientsFuture = send(request.getAckTimeout(), responseName, new RemoteServiceAck());
ackClientsFuture.addListener(new FutureListener<List<?>>() {
@Override
public void operationComplete(Future<List<?>> future) throws Exception {
if (!future.isSuccess()) {
log.error("Can't send ack for request: " + request, future.cause());
if (future.cause() instanceof RedissonShutdownException) {
// send the ack only if expected
if (request.getOptions().isAckExpected()) {
Future<List<?>> ackClientsFuture = send(request.getOptions().getAckTimeoutInMillis(), responseName, new RemoteServiceAck());
ackClientsFuture.addListener(new FutureListener<List<?>>() {
@Override
public void operationComplete(Future<List<?>> future) throws Exception {
if (!future.isSuccess()) {
log.error("Can't send ack for request: " + request, future.cause());
if (future.cause() instanceof RedissonShutdownException) {
return;
}
// re-subscribe after a failed send (ack)
subscribe(remoteInterface, requestQueue);
return;
}
// re-subscribe after a failed send (ack)
subscribe(remoteInterface, requestQueue);
return;
}
invokeMethod(remoteInterface, requestQueue, request, method, responseName);
}
});
invokeMethod(remoteInterface, requestQueue, request, method, responseName);
}
});
} else {
invokeMethod(remoteInterface, requestQueue, request, method, responseName);
}
}
});
@ -153,35 +148,50 @@ public class RedissonRemoteService implements RRemoteService {
responseHolder.set(response);
log.error("Can't execute: " + request, e);
}
Future<List<?>> clientsFuture = send(request.getResponseTimeout(), responseName, responseHolder.get());
clientsFuture.addListener(new FutureListener<List<?>>() {
@Override
public void operationComplete(Future<List<?>> future) throws Exception {
if (!future.isSuccess()) {
log.error("Can't send response: " + responseHolder.get() + " for request: " + request, future.cause());
if (future.cause() instanceof RedissonShutdownException) {
return;
// send the response only if expected
if (request.getOptions().isResultExpected()) {
Future<List<?>> clientsFuture = send(request.getOptions().getExecutionTimeoutInMillis(), responseName, responseHolder.get());
clientsFuture.addListener(new FutureListener<List<?>>() {
@Override
public void operationComplete(Future<List<?>> future) throws Exception {
if (!future.isSuccess()) {
log.error("Can't send response: " + responseHolder.get() + " for request: " + request, future.cause());
if (future.cause() instanceof RedissonShutdownException) {
return;
}
}
// re-subscribe anyways (fail or success) after the send (response)
subscribe(remoteInterface, requestQueue);
}
// re-subscribe anyways (fail or success) after the send (response)
subscribe(remoteInterface, requestQueue);
}
});
});
} else {
// re-subscribe anyways after the method invocation
subscribe(remoteInterface, requestQueue);
}
}
@Override
public <T> T get(Class<T> remoteInterface) {
return get(remoteInterface, 30, TimeUnit.SECONDS);
return get(remoteInterface, RemoteInvocationOptions.defaults());
}
@Override
public <T> T get(final Class<T> remoteInterface, final long executionTimeout, final TimeUnit executionTimeUnit) {
return get(remoteInterface, executionTimeout, executionTimeUnit, 1, TimeUnit.SECONDS);
return get(remoteInterface, RemoteInvocationOptions.defaults()
.expectResultWithin(executionTimeout, executionTimeUnit));
}
public <T> T get(final Class<T> remoteInterface, final long executionTimeout, final TimeUnit executionTimeUnit,
final long ackTimeout, final TimeUnit ackTimeUnit) {
public <T> T get(final Class<T> remoteInterface, final long executionTimeout, final TimeUnit executionTimeUnit,
final long ackTimeout, final TimeUnit ackTimeUnit) {
return get(remoteInterface, RemoteInvocationOptions.defaults()
.expectAckWithin(ackTimeout, ackTimeUnit)
.expectResultWithin(executionTimeout, executionTimeUnit));
}
public <T> T get(final Class<T> remoteInterface, final RemoteInvocationOptions options) {
// local copy of the options, to prevent mutation
final RemoteInvocationOptions optionsCopy = new RemoteInvocationOptions(options);
final String toString = getClass().getSimpleName() + "-" + remoteInterface.getSimpleName() + "-proxy-" + generateRequestId();
InvocationHandler handler = new InvocationHandler() {
@Override
@ -194,33 +204,47 @@ public class RedissonRemoteService implements RRemoteService {
return toString.hashCode();
}
if (!optionsCopy.isResultExpected() && !(method.getReturnType().equals(Void.class) || method.getReturnType().equals(Void.TYPE)))
throw new IllegalArgumentException("The noResult option only supports void return value");
String requestId = generateRequestId();
String requestQueueName = name + ":{" + remoteInterface.getName() + "}";
RBlockingQueue<RemoteServiceRequest> requestQueue = redisson.getBlockingQueue(requestQueueName);
RemoteServiceRequest request = new RemoteServiceRequest(requestId, method.getName(), args,
ackTimeUnit.toMillis(ackTimeout), executionTimeUnit.toMillis(executionTimeout), System.currentTimeMillis());
RemoteServiceRequest request = new RemoteServiceRequest(requestId,
method.getName(), args, optionsCopy, System.currentTimeMillis());
requestQueue.add(request);
String responseName = name + ":{" + remoteInterface.getName() + "}:" + requestId;
RBlockingQueue<RRemoteServiceResponse> responseQueue = redisson.getBlockingQueue(responseName);
RemoteServiceAck ack = (RemoteServiceAck) responseQueue.poll(ackTimeout, ackTimeUnit);
if (ack == null) {
throw new RemoteServiceAckTimeoutException("No ACK response after " + ackTimeUnit.toMillis(ackTimeout) + "ms for request: " + request);
RBlockingQueue<RRemoteServiceResponse> responseQueue = null;
if (optionsCopy.isAckExpected() || optionsCopy.isResultExpected()) {
String responseName = name + ":{" + remoteInterface.getName() + "}:" + requestId;
responseQueue = redisson.getBlockingQueue(responseName);
}
RemoteServiceResponse response = (RemoteServiceResponse) responseQueue.poll(executionTimeout, executionTimeUnit);
if (response == null) {
throw new RemoteServiceTimeoutException("No response after " + executionTimeUnit.toMillis(executionTimeout) + "ms for request: " + request);
// poll for the ack only if expected
if (optionsCopy.isAckExpected()) {
RemoteServiceAck ack = (RemoteServiceAck) responseQueue.poll(optionsCopy.getAckTimeoutInMillis(), TimeUnit.MILLISECONDS);
if (ack == null) {
throw new RemoteServiceAckTimeoutException("No ACK response after " + optionsCopy.getAckTimeoutInMillis() + "ms for request: " + request);
}
}
if (response.getError() != null) {
throw response.getError();
// poll for the response only if expected
if (optionsCopy.isResultExpected()) {
RemoteServiceResponse response = (RemoteServiceResponse) responseQueue.poll(optionsCopy.getExecutionTimeoutInMillis(), TimeUnit.MILLISECONDS);
if (response == null) {
throw new RemoteServiceTimeoutException("No response after " + optionsCopy.getExecutionTimeoutInMillis() + "ms for request: " + request);
}
if (response.getError() != null) {
throw response.getError();
}
return response.getResult();
}
return response.getResult();
return null;
}
};
return (T) Proxy.newProxyInstance(remoteInterface.getClassLoader(), new Class[] {remoteInterface}, handler);
return (T) Proxy.newProxyInstance(remoteInterface.getClassLoader(), new Class[]{remoteInterface}, handler);
}
private String generateRequestId() {
@ -237,5 +261,4 @@ public class RedissonRemoteService implements RRemoteService {
queue.expireAsync(timeout, TimeUnit.MILLISECONDS);
return batch.executeAsync();
}
}

@ -77,21 +77,32 @@ public interface RRemoteService {
/**
* Get remote service object for remote invocations.
* <p/>
* Ack timeout = 1000 ms by default
* <p/>
* Execution timeout = 30 sec by default
*
* This method is a shortcut for
* <pre>
* get(remoteInterface, RemoteInvocationOptions.defaults())
* </pre>
*
* @see RemoteInvocationOptions#defaults()
* @see #get(Class, RemoteInvocationOptions)
*
* @param remoteInterface
* @return
*/
<T> T get(Class<T> remoteInterface);
/**
* Get remote service object for remote invocations
* with specified invocation timeout.
* <p/>
* Ack timeout = 1000 ms by default
*
* <p/>
* This method is a shortcut for
* <pre>
* get(remoteInterface, RemoteInvocationOptions.defaults()
* .expectResultWithin(executionTimeout, executionTimeUnit))
* </pre>
*
* @see RemoteInvocationOptions#defaults()
* @see #get(Class, RemoteInvocationOptions)
*
* @param remoteInterface
* @param executionTimeout - invocation timeout
* @param executionTimeUnit
@ -102,7 +113,17 @@ public interface RRemoteService {
/**
* Get remote service object for remote invocations
* with specified invocation and ack timeouts
*
* <p/>
* This method is a shortcut for
* <pre>
* get(remoteInterface, RemoteInvocationOptions.defaults()
* .expectAckWithin(ackTimeout, ackTimeUnit)
* .expectResultWithin(executionTimeout, executionTimeUnit))
* </pre>
*
* @see RemoteInvocationOptions
* @see #get(Class, RemoteInvocationOptions)
*
* @param remoteInterface
* @param executionTimeout - invocation timeout
* @param executionTimeUnit
@ -111,5 +132,17 @@ public interface RRemoteService {
* @return
*/
<T> T get(Class<T> remoteInterface, long executionTimeout, TimeUnit executionTimeUnit, long ackTimeout, TimeUnit ackTimeUnit);
/**
* Get remote service object for remote invocations
* with the specified options
* <p/>
* Note that when using the noResult() option,
* it is expected that the invoked method returns void,
* or else IllegalArgumentException will be thrown.
*
* @see RemoteInvocationOptions
*/
<T> T get(Class<T> remoteInterface, RemoteInvocationOptions options);
}

@ -0,0 +1,141 @@
/**
* Copyright 2014 Nikita Koksharov, Nickolay Borbit
*
* Licensed 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.redisson.core;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
/**
* RRemoteService invocation options.
*
* Used to tune how RRemoteService will behave
* in regard to the remote invocations acknowledgement
* and execution timeout.
* <p/>
* Examples:
* <pre>
* // 1 second ack timeout and 30 seconds execution timeout
* RemoteInvocationOptions options =
* RemoteInvocationOptions.defaults();
*
* // no ack but 30 seconds execution timeout
* RemoteInvocationOptions options =
* RemoteInvocationOptions.defaults()
* .noAck();
*
* // 1 second ack timeout then forget the result
* RemoteInvocationOptions options =
* RemoteInvocationOptions.defaults()
* .noResult();
*
* // 1 minute ack timeout then forget about the result
* RemoteInvocationOptions options =
* RemoteInvocationOptions.defaults()
* .expectAckWithin(1, TimeUnit.MINUTES)
* .noResult();
*
* // no ack and forget about the result (fire and forget)
* RemoteInvocationOptions options =
* RemoteInvocationOptions.defaults()
* .noAck()
* .noResult();
* </pre>
*
* @see RRemoteService#get(Class, RemoteInvocationOptions)
*/
public class RemoteInvocationOptions implements Serializable {
private Long ackTimeoutInMillis;
private Long executionTimeoutInMillis;
private RemoteInvocationOptions() {
}
public RemoteInvocationOptions(RemoteInvocationOptions copy) {
this.ackTimeoutInMillis = copy.ackTimeoutInMillis;
this.executionTimeoutInMillis = copy.executionTimeoutInMillis;
}
/**
* Creates a new instance of RemoteInvocationOptions with opinionated defaults.
* <p/>
* This is equivalent to:
* <pre>
* new RemoteInvocationOptions()
* .expectAckWithin(1, TimeUnit.SECONDS)
* .expectResultWithin(30, TimeUnit.SECONDS)
* </pre>
*/
public static RemoteInvocationOptions defaults() {
return new RemoteInvocationOptions()
.expectAckWithin(1, TimeUnit.SECONDS)
.expectResultWithin(20, TimeUnit.SECONDS);
}
public Long getAckTimeoutInMillis() {
return ackTimeoutInMillis;
}
public Long getExecutionTimeoutInMillis() {
return executionTimeoutInMillis;
}
public boolean isAckExpected() {
return ackTimeoutInMillis != null;
}
public boolean isResultExpected() {
return executionTimeoutInMillis != null;
}
public RemoteInvocationOptions expectAckWithin(long ackTimeoutInMillis) {
this.ackTimeoutInMillis = ackTimeoutInMillis;
return this;
}
public RemoteInvocationOptions expectAckWithin(long ackTimeout, TimeUnit timeUnit) {
this.ackTimeoutInMillis = timeUnit.toMillis(ackTimeout);
return this;
}
public RemoteInvocationOptions noAck() {
ackTimeoutInMillis = null;
return this;
}
public RemoteInvocationOptions expectResultWithin(long executionTimeoutInMillis) {
this.executionTimeoutInMillis = executionTimeoutInMillis;
return this;
}
public RemoteInvocationOptions expectResultWithin(long executionTimeout, TimeUnit timeUnit) {
this.executionTimeoutInMillis = timeUnit.toMillis(executionTimeout);
return this;
}
public RemoteInvocationOptions noResult() {
executionTimeoutInMillis = null;
return this;
}
@Override
public String toString() {
return "RemoteInvocationOptions[" +
"ackTimeoutInMillis=" + ackTimeoutInMillis +
", executionTimeoutInMillis=" + executionTimeoutInMillis +
']';
}
}

@ -15,6 +15,8 @@
*/
package org.redisson.remote;
import org.redisson.core.RemoteInvocationOptions;
import java.io.Serializable;
import java.util.Arrays;
@ -23,36 +25,26 @@ public class RemoteServiceRequest implements Serializable {
private String requestId;
private String methodName;
private Object[] args;
private long ackTimeout;
private long responseTimeout;
private RemoteInvocationOptions options;
private long date;
public RemoteServiceRequest() {
}
public RemoteServiceRequest(String requestId, String methodName, Object[] args, long ackTimeout, long responseTimeout, long date) {
public RemoteServiceRequest(String requestId, String methodName, Object[] args, RemoteInvocationOptions options, long date) {
super();
this.requestId = requestId;
this.methodName = methodName;
this.args = args;
this.ackTimeout = ackTimeout;
this.responseTimeout = responseTimeout;
this.options = options;
this.date = date;
}
public long getResponseTimeout() {
return responseTimeout;
}
public long getDate() {
return date;
}
public long getAckTimeout() {
return ackTimeout;
}
public String getRequestId() {
return requestId;
}
@ -60,7 +52,11 @@ public class RemoteServiceRequest implements Serializable {
public Object[] getArgs() {
return args;
}
public RemoteInvocationOptions getOptions() {
return options;
}
public String getMethodName() {
return methodName;
}
@ -68,7 +64,7 @@ public class RemoteServiceRequest implements Serializable {
@Override
public String toString() {
return "RemoteServiceRequest [requestId=" + requestId + ", methodName=" + methodName + ", args="
+ Arrays.toString(args) + ", ackTimeout=" + ackTimeout + ", date=" + date + "]";
+ Arrays.toString(args) + ", options=" + options + ", date=" + date + "]";
}
}

@ -5,6 +5,8 @@ import org.junit.Assert;
import org.junit.Test;
import org.redisson.codec.FstCodec;
import org.redisson.codec.SerializationCodec;
import org.redisson.core.RemoteInvocationOptions;
import org.redisson.remote.RemoteServiceAckTimeoutException;
import org.redisson.remote.RemoteServiceTimeoutException;
import java.io.IOException;
@ -61,7 +63,7 @@ public class RedissonRemoteServiceTest extends BaseTest {
public interface RemoteInterface {
void voidMethod(String name, Long param);
Long resultMethod(Long value);
void errorMethod() throws IOException;
@ -82,7 +84,7 @@ public class RedissonRemoteServiceTest extends BaseTest {
public void voidMethod(String name, Long param) {
System.out.println(name + " " + param);
}
@Override
public Long resultMethod(Long value) {
return value*2;
@ -248,19 +250,33 @@ public class RedissonRemoteServiceTest extends BaseTest {
@Test
public void testInvocationWithServiceName() {
String name = "MyServiceName";
RedissonClient server = Redisson.create();
RedissonClient client = Redisson.create();
RedissonClient r1 = Redisson.create();
r1.getRemoteSerivce(name).register(RemoteInterface.class, new RemoteImpl());
server.getRemoteSerivce("MyServiceNamespace").register(RemoteInterface.class, new RemoteImpl());
RedissonClient r2 = Redisson.create();
RemoteInterface ri = r2.getRemoteSerivce(name).get(RemoteInterface.class);
RemoteInterface serviceRemoteInterface = client.getRemoteSerivce("MyServiceNamespace").get(RemoteInterface.class);
RemoteInterface otherServiceRemoteInterface = client.getRemoteSerivce("MyOtherServiceNamespace").get(RemoteInterface.class);
RemoteInterface defaultServiceRemoteInterface = client.getRemoteSerivce().get(RemoteInterface.class);
ri.voidMethod("someName", 100L);
assertThat(ri.resultMethod(100L)).isEqualTo(200);
assertThat(serviceRemoteInterface.resultMethod(21L)).isEqualTo(42L);
r1.shutdown();
r2.shutdown();
try {
otherServiceRemoteInterface.resultMethod(21L);
Assert.fail("Invoking a service in an unregistered custom services namespace should throw");
} catch (Exception e) {
assertThat(e).isInstanceOf(RemoteServiceAckTimeoutException.class);
}
try {
defaultServiceRemoteInterface.resultMethod(21L);
Assert.fail("Invoking a service in the unregistered default services namespace should throw");
} catch (Exception e) {
assertThat(e).isInstanceOf(RemoteServiceAckTimeoutException.class);
}
client.shutdown();
server.shutdown();
}
@Test
@ -362,4 +378,143 @@ public class RedissonRemoteServiceTest extends BaseTest {
server.shutdown();
}
}
@Test
public void testNoAckWithResultInvocations() throws InterruptedException {
RedissonClient server = Redisson.create();
RedissonClient client = Redisson.create();
try {
server.getRemoteSerivce().register(RemoteInterface.class, new RemoteImpl());
// no ack but an execution timeout of 1 second
RemoteInvocationOptions options = RemoteInvocationOptions.defaults().noAck().expectResultWithin(1, TimeUnit.SECONDS);
RemoteInterface service = client.getRemoteSerivce().get(RemoteInterface.class, options);
service.voidMethod("noAck", 100L);
assertThat(service.resultMethod(21L)).isEqualTo(42);
try {
service.errorMethod();
Assert.fail();
} catch (IOException e) {
assertThat(e.getMessage()).isEqualTo("Checking error throw");
}
try {
service.errorMethodWithCause();
Assert.fail();
} catch (Exception e) {
assertThat(e.getCause()).isInstanceOf(ArithmeticException.class);
assertThat(e.getCause().getMessage()).isEqualTo("/ by zero");
}
try {
service.timeoutMethod();
Assert.fail("noAck option should still wait for the server to return a response and throw if the execution timeout is exceeded");
} catch (Exception e) {
assertThat(e).isInstanceOf(RemoteServiceTimeoutException.class);
}
} finally {
client.shutdown();
server.shutdown();
}
}
@Test
public void testAckWithoutResultInvocations() throws InterruptedException {
RedissonClient server = Redisson.create();
RedissonClient client = Redisson.create();
try {
server.getRemoteSerivce().register(RemoteInterface.class, new RemoteImpl());
// fire and forget with an ack timeout of 1 sec
RemoteInvocationOptions options = RemoteInvocationOptions.defaults().expectAckWithin(1, TimeUnit.SECONDS).noResult();
RemoteInterface service = client.getRemoteSerivce().get(RemoteInterface.class, options);
service.voidMethod("noResult", 100L);
try {
service.resultMethod(100L);
Assert.fail();
} catch (Exception e) {
assertThat(e).isInstanceOf(IllegalArgumentException.class);
}
try {
service.errorMethod();
} catch (IOException e) {
Assert.fail("noResult option should not throw server side exception");
}
try {
service.errorMethodWithCause();
} catch (Exception e) {
Assert.fail("noResult option should not throw server side exception");
}
long time = System.currentTimeMillis();
service.timeoutMethod();
time = System.currentTimeMillis() - time;
assertThat(time).describedAs("noResult option should not wait for the server to return a response").isLessThan(2000);
try {
service.timeoutMethod();
Assert.fail("noResult option should still wait for the server to ack the request and throw if the ack timeout is exceeded");
} catch (Exception e) {
assertThat(e).isInstanceOf(RemoteServiceAckTimeoutException.class);
}
} finally {
client.shutdown();
server.shutdown();
}
}
@Test
public void testNoAckWithoutResultInvocations() throws InterruptedException {
RedissonClient server = Redisson.create();
RedissonClient client = Redisson.create();
try {
server.getRemoteSerivce().register(RemoteInterface.class, new RemoteImpl());
// no ack fire and forget
RemoteInvocationOptions options = RemoteInvocationOptions.defaults().noAck().noResult();
RemoteInterface service = client.getRemoteSerivce().get(RemoteInterface.class, options);
RemoteInterface invalidService = client.getRemoteSerivce("Invalid").get(RemoteInterface.class, options);
service.voidMethod("noAck/noResult", 100L);
try {
service.resultMethod(100L);
Assert.fail();
} catch (Exception e) {
assertThat(e).isInstanceOf(IllegalArgumentException.class);
}
try {
service.errorMethod();
} catch (IOException e) {
Assert.fail("noAck with noResult options should not throw server side exception");
}
try {
service.errorMethodWithCause();
} catch (Exception e) {
Assert.fail("noAck with noResult options should not throw server side exception");
}
long time = System.currentTimeMillis();
service.timeoutMethod();
time = System.currentTimeMillis() - time;
assertThat(time).describedAs("noAck with noResult options should not wait for the server to return a response").isLessThan(2000);
try {
invalidService.voidMethod("noAck/noResult", 21L);
} catch (Exception e) {
Assert.fail("noAck with noResult options should not throw any exception even while invoking a service in an unregistered services namespace");
}
} finally {
client.shutdown();
server.shutdown();
}
}
}

Loading…
Cancel
Save