/*
* Copyright 2014 Higher Frequency Trading
*
* http://www.higherfrequencytrading.com
*
* 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 net.openhft.chronicle.engine;
import net.openhft.chronicle.core.Jvm;
import net.openhft.chronicle.core.annotation.Nullable;
import net.openhft.chronicle.core.onoes.ExceptionKey;
import net.openhft.chronicle.core.threads.ThreadDump;
import net.openhft.chronicle.engine.api.map.MapView;
import net.openhft.chronicle.engine.api.tree.AssetTree;
import net.openhft.chronicle.engine.server.ServerEndpoint;
import net.openhft.chronicle.engine.tree.VanillaAssetTree;
import net.openhft.chronicle.network.TCPRegistry;
import net.openhft.chronicle.network.connection.ClientConnectionMonitor;
import net.openhft.chronicle.network.connection.TcpChannelHub;
import net.openhft.chronicle.wire.WireType;
import org.jetbrains.annotations.NotNull;
import org.junit.*;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.Closeable;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.channels.ServerSocketChannel;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentMap;
import static java.util.concurrent.TimeUnit.SECONDS;
/**
* test using the listener both remotely or locally via the engine
*
* @author Rob Austin.
*/
@Ignore
@RunWith(Parameterized.class)
public class TcpFailoverWithMonitoringTest {
public static final WireType WIRE_TYPE = WireType.TEXT;
private static final String NAME = "test";
private static final String CONNECTION_1 = "Test1.host.port";
private final static String CONNECTION_2 = "Test2.host.port";
private static ConcurrentMap<String, String> map;
private final BlockingQueue<String> activity = new ArrayBlockingQueue<>(2);
ServerSocketChannel connection1;
ServerSocketChannel connection2;
private AssetTree failOverClient;
private VanillaAssetTree serverAssetTree1;
private VanillaAssetTree serverAssetTree2;
private ServerEndpoint serverEndpoint1;
private ServerEndpoint serverEndpoint2;
private ThreadDump threadDump;
private Map<ExceptionKey, Integer> exceptions;
public TcpFailoverWithMonitoringTest() {
}
@Parameterized.Parameters
public static List<Object[]> data() {
return Arrays.asList(new Object[10][0]);
}
@Before
public void threadDump() {
threadDump = new ThreadDump();
}
@After
public void afterMethod() {
if (!exceptions.isEmpty()) {
Jvm.dumpException(exceptions);
Jvm.resetExceptionHandlers();
Assert.fail();
}
}
@Before
public void before() throws IOException {
exceptions = Jvm.recordExceptions();
//YamlLogging.setAll(true);
serverAssetTree1 = new VanillaAssetTree().forTesting();
serverAssetTree2 = new VanillaAssetTree().forTesting();
TCPRegistry.createServerSocketChannelFor(CONNECTION_1);
TCPRegistry.createServerSocketChannelFor(CONNECTION_2);
connection1 = TCPRegistry.acquireServerSocketChannel(CONNECTION_1);
connection2 = TCPRegistry.acquireServerSocketChannel(CONNECTION_2);
@NotNull final String[] connection = {CONNECTION_1, CONNECTION_2};
failOverClient = new VanillaAssetTree("failoverClient").forRemoteAccess(connection,
WIRE_TYPE, clientConnectionMonitor());
map = failOverClient.acquireMap(NAME, String.class, String.class);
serverEndpoint1 = new ServerEndpoint(CONNECTION_1, serverAssetTree1);
serverEndpoint2 = new ServerEndpoint(CONNECTION_2, serverAssetTree2);
}
@NotNull
private ClientConnectionMonitor clientConnectionMonitor() {
return new ClientConnectionMonitor() {
@Override
public void onConnected(@Nullable String name, @NotNull SocketAddress socketAddress) {
System.out.println("onConnected - with name=" + name + ", " +
"to socketAddress=" +
socketAddress.toString());
activity.add("connected " + socketAddress.toString());
}
@Override
public void onDisconnected(@Nullable String name, @NotNull SocketAddress socketAddress) {
System.out.println("onDisconnected - with name=" + name + ", " +
"to socketAddress=" +
socketAddress.toString());
activity.add("disconnected " + socketAddress.toString());
}
};
}
@After
public void after() throws IOException {
failOverClient.close();
if (serverEndpoint1 != null)
serverEndpoint1.close();
if (serverEndpoint2 != null)
serverEndpoint2.close();
serverAssetTree1.close();
serverAssetTree2.close();
if (map instanceof Closeable)
((Closeable) map).close();
TcpChannelHub.closeAllHubs();
TCPRegistry.reset();
TCPRegistry.assertAllServersStopped();
threadDump.assertNoNewThreads();
}
/**
* the fail over client connects to server1 ( server1 is the primary) , server1 is then shut
* down and the client connects to the secondary
*
*/
@Test
public void test() throws InterruptedException {
@NotNull final MapView<String, String> failoverClient = failOverClient.acquireMap(NAME,
String.class,
String.class);
@NotNull final MapView<String, String> map1 = serverAssetTree1.acquireMap(NAME,
String.class, String.class);
Assert.assertEquals("connected " + toString(connection1), activity.poll(10, SECONDS));
@NotNull final MapView<String, String> map2 = serverAssetTree2.acquireMap(NAME,
String.class, String.class);
map1.put("hello", "server1");
map2.put("hello", "server2");
Assert.assertEquals("server1", failoverClient.get("hello"));
// we are now going to shut down server 1
serverAssetTree1.close();
Assert.assertEquals("disconnected " + toString(connection1), activity.poll(4, SECONDS));
Assert.assertEquals("connected " + toString(connection2), activity.poll(100, SECONDS));
// shutting server1 down should cause the failover client to connect to server 2
Assert.assertEquals("server2", failoverClient.get("hello"));
}
private SocketAddress toString(@NotNull final ServerSocketChannel connection2) {
return connection2.socket().getLocalSocketAddress();
}
}