/*
* Copyright (C) 2015 SoftIndex LLC.
*
* 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 io.datakernel.rpc.client.sender;
import io.datakernel.async.ResultCallback;
import io.datakernel.async.ResultCallbackFuture;
import io.datakernel.rpc.client.RpcClientConnectionPool;
import io.datakernel.rpc.client.sender.helper.RpcClientConnectionPoolStub;
import io.datakernel.rpc.client.sender.helper.RpcSenderStub;
import org.junit.Test;
import java.net.InetSocketAddress;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import static io.datakernel.rpc.client.sender.RpcStrategies.firstValidResult;
import static io.datakernel.rpc.client.sender.RpcStrategies.servers;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@SuppressWarnings("ConstantConditions")
public class RpcStrategyFirstValidResultTest {
private static final String HOST = "localhost";
private static final int PORT_1 = 10001;
private static final int PORT_2 = 10002;
private static final int PORT_3 = 10003;
private static final InetSocketAddress ADDRESS_1 = new InetSocketAddress(HOST, PORT_1);
private static final InetSocketAddress ADDRESS_2 = new InetSocketAddress(HOST, PORT_2);
private static final InetSocketAddress ADDRESS_3 = new InetSocketAddress(HOST, PORT_3);
@Test
public void itShouldSendRequestToAllAvailableSenders() {
RpcClientConnectionPoolStub pool = new RpcClientConnectionPoolStub();
RpcSenderStub connection1 = new RpcSenderStub();
RpcSenderStub connection2 = new RpcSenderStub();
RpcSenderStub connection3 = new RpcSenderStub();
RpcStrategy firstValidResult = firstValidResult(servers(ADDRESS_1, ADDRESS_2, ADDRESS_3));
int callsAmountIterationOne = 10;
int callsAmountIterationTwo = 25;
RpcSender senderToAll;
pool.put(ADDRESS_1, connection1);
pool.put(ADDRESS_2, connection2);
pool.put(ADDRESS_3, connection3);
senderToAll = firstValidResult.createSender(pool);
for (int i = 0; i < callsAmountIterationOne; i++) {
senderToAll.sendRequest(new Object(), 50, ResultCallbackFuture.create());
}
pool.remove(ADDRESS_1);
// we should recreate sender after changing in pool
senderToAll = firstValidResult.createSender(pool);
for (int i = 0; i < callsAmountIterationTwo; i++) {
senderToAll.sendRequest(new Object(), 50, ResultCallbackFuture.create());
}
assertEquals(callsAmountIterationOne, connection1.getRequests());
assertEquals(callsAmountIterationOne + callsAmountIterationTwo, connection2.getRequests());
assertEquals(callsAmountIterationOne + callsAmountIterationTwo, connection3.getRequests());
}
@Test
public void itShouldCallOnResultWithNullIfAllSendersReturnedNullAndValidatorAndExceptionAreNotSpecified() throws ExecutionException, InterruptedException {
RpcStrategy strategy1 = new RequestSenderOnResultWithNullStrategy();
RpcStrategy strategy2 = new RequestSenderOnResultWithNullStrategy();
RpcStrategy strategy3 = new RequestSenderOnResultWithNullStrategy();
RpcStrategyFirstValidResult firstValidResult = firstValidResult(strategy1, strategy2, strategy3);
RpcSender sender = firstValidResult.createSender(new RpcClientConnectionPoolStub());
ResultCallbackFuture<Object> callback = ResultCallbackFuture.create();
sender.sendRequest(new Object(), 50, callback);
// despite there are several sender, sendResult should be called only once after all senders returned null
assertEquals(null, callback.get());
}
@Test(expected = Exception.class)
public void itShouldCallOnExceptionIfAllSendersReturnsNullAndValidatorIsDefaultButExceptionIsSpecified() throws ExecutionException, InterruptedException {
// default validator should check whether result is not null
RpcStrategy strategy1 = new RequestSenderOnResultWithNullStrategy();
RpcStrategy strategy2 = new RequestSenderOnResultWithNullStrategy();
RpcStrategy strategy3 = new RequestSenderOnResultWithNullStrategy();
RpcStrategyFirstValidResult firstValidResult = firstValidResult(strategy1, strategy2, strategy3)
.withNoValidResultException(new Exception());
RpcSender sender = firstValidResult.createSender(new RpcClientConnectionPoolStub());
ResultCallbackFuture<Object> callback = ResultCallbackFuture.create();
sender.sendRequest(new Object(), 50, callback);
callback.get();
}
@Test
public void itShouldUseCustomValidatorIfItIsSpecified() throws ExecutionException, InterruptedException {
final int invalidKey = 1;
final int validKey = 2;
RpcStrategy strategy1 = new RequestSenderOnResultWithValueStrategy(invalidKey);
RpcStrategy strategy2 = new RequestSenderOnResultWithValueStrategy(validKey);
RpcStrategy strategy3 = new RequestSenderOnResultWithValueStrategy(invalidKey);
RpcStrategyFirstValidResult firstValidResult = firstValidResult(strategy1, strategy2, strategy3)
.withResultValidator(new RpcStrategyFirstValidResult.ResultValidator<Integer>() {
@Override
public boolean isValidResult(Integer input) {
return input == validKey;
}
})
.withNoValidResultException(new Exception());
RpcSender sender = firstValidResult.createSender(new RpcClientConnectionPoolStub());
ResultCallbackFuture<Object> callback = ResultCallbackFuture.create();
sender.sendRequest(new Object(), 50, callback);
assertEquals(validKey, callback.get());
}
@Test(expected = Exception.class)
public void itShouldCallOnExceptionIfNoSenderReturnsValidResultButExceptionWasSpecified() throws ExecutionException, InterruptedException {
final int invalidKey = 1;
final int validKey = 2;
RpcStrategy strategy1 = new RequestSenderOnResultWithValueStrategy(invalidKey);
RpcStrategy strategy2 = new RequestSenderOnResultWithValueStrategy(invalidKey);
RpcStrategy strategy3 = new RequestSenderOnResultWithValueStrategy(invalidKey);
RpcStrategyFirstValidResult firstValidResult = firstValidResult(strategy1, strategy2, strategy3)
.withResultValidator(new RpcStrategyFirstValidResult.ResultValidator<Integer>() {
@Override
public boolean isValidResult(Integer input) {
return input == validKey;
}
})
.withNoValidResultException(new Exception());
RpcSender sender = firstValidResult.createSender(new RpcClientConnectionPoolStub());
ResultCallbackFuture<Object> callback = ResultCallbackFuture.create();
sender.sendRequest(new Object(), 50, callback);
callback.get();
}
@Test
public void itShouldBeCreatedWhenThereIsAtLeastOneActiveSubSender() {
RpcClientConnectionPoolStub pool = new RpcClientConnectionPoolStub();
RpcSenderStub connection = new RpcSenderStub();
// one connection is added
pool.put(ADDRESS_2, connection);
RpcStrategy firstValidResult = firstValidResult(servers(ADDRESS_1, ADDRESS_2));
assertTrue(firstValidResult.createSender(pool) != null);
}
@Test
public void itShouldNotBeCreatedWhenThereAreNoActiveSubSenders() {
RpcClientConnectionPoolStub pool = new RpcClientConnectionPoolStub();
// no connections were added to pool
RpcStrategy firstValidResult = firstValidResult(servers(ADDRESS_1, ADDRESS_2, ADDRESS_3));
assertTrue(firstValidResult.createSender(pool) == null);
}
private static final class SenderOnResultWithNullCaller implements RpcSender {
@Override
public <I, O> void sendRequest(I request, int timeout, ResultCallback<O> callback) {
callback.setResult(null);
}
}
static final class SenderOnResultWithValueCaller implements RpcSender {
private final Object data;
public SenderOnResultWithValueCaller(Object data) {
this.data = data;
}
@SuppressWarnings("unchecked")
@Override
public <I, O> void sendRequest(I request, int timeout, ResultCallback<O> callback) {
callback.setResult((O) data);
}
}
static final class RequestSenderOnResultWithNullStrategy implements RpcStrategy {
@Override
public Set<InetSocketAddress> getAddresses() {
throw new UnsupportedOperationException();
}
@Override
public RpcSender createSender(RpcClientConnectionPool pool) {
return new SenderOnResultWithNullCaller();
}
}
static final class RequestSenderOnResultWithValueStrategy implements RpcStrategy {
private final Object data;
public RequestSenderOnResultWithValueStrategy(Object data) {
this.data = data;
}
@Override
public Set<InetSocketAddress> getAddresses() {
throw new UnsupportedOperationException();
}
@Override
public RpcSender createSender(RpcClientConnectionPool pool) {
return new SenderOnResultWithValueCaller(data);
}
}
}