/*
* Copyright 2016 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.server.internal;
import net.openhft.chronicle.core.annotation.UsedViaReflection;
import net.openhft.chronicle.engine.api.map.MapView;
import net.openhft.chronicle.engine.api.tree.Asset;
import net.openhft.chronicle.engine.cfg.EngineClusterContext;
import net.openhft.chronicle.engine.tree.VanillaAsset;
import net.openhft.chronicle.network.*;
import net.openhft.chronicle.network.api.TcpHandler;
import net.openhft.chronicle.network.cluster.ClusterContext;
import net.openhft.chronicle.wire.AbstractMarshallable;
import net.openhft.chronicle.wire.Demarshallable;
import net.openhft.chronicle.wire.WireIn;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import static net.openhft.chronicle.engine.server.internal.EngineWireNetworkContext.ConnectionStatus.CONNECTED;
import static net.openhft.chronicle.engine.server.internal.EngineWireNetworkContext.ConnectionStatus.DISCONNECTED;
/**
* @author Rob Austin.
*/
public class EngineWireNetworkContext<T extends EngineWireNetworkContext>
extends VanillaNetworkContext<T> {
static final Logger LOG = LoggerFactory.getLogger(EngineWireNetworkContext.class);
private static final String PROC_CONNECTIONS_CLUSTER = "/proc/connections/cluster/";
private static final String CONNECTIVITY_URI = PROC_CONNECTIONS_CLUSTER + "connectivity";
private static final String CONNECTIVITY_HOSTS_URI = PROC_CONNECTIONS_CLUSTER + "hosts";
private Asset rootAsset;
private MapView<ConnectionDetails, ConnectionEvent> hostByConnectionStatus;
private MapView<String, ConnectionStatus> connectivityHosts;
private TcpHandler handler;
public TcpHandler handler() {
return handler;
}
public EngineWireNetworkContext(@NotNull Asset asset) {
this.rootAsset = asset.root();
// TODO make configurable
serverThreadingStrategy(ServerThreadingStrategy.CONCURRENT);
((VanillaAsset) rootAsset.acquireAsset("/proc")).configMapServer();
hostByConnectionStatus = rootAsset.root().acquireMap(
CONNECTIVITY_URI,
ConnectionDetails.class,
ConnectionEvent.class);
connectivityHosts = rootAsset.root().acquireMap(
CONNECTIVITY_HOSTS_URI,
String.class,
ConnectionStatus.class);
}
@NotNull
public Asset rootAsset() {
return this.rootAsset;
}
@Override
public void onHandlerChanged(TcpHandler handler) {
this.handler = handler;
}
@Nullable
@Override
public ConnectionListener acquireConnectionListener() {
return new ConnectionListener() {
@Override
public void onConnected(int localIdentifier, int remoteIdentifier, boolean isAcceptor) {
@NotNull ConnectionDetails key = new ConnectionDetails(localIdentifier, remoteIdentifier, isAcceptor);
hostByConnectionStatus.put(key, new ConnectionEvent(CONNECTED));
LOG.info(key + ", connectionStatus=" + CONNECTED);
onConnectionChanged(localIdentifier, remoteIdentifier, isAcceptor);
}
@Override
public void onDisconnected(int localIdentifier, int remoteIdentifier, boolean isAcceptor) {
@NotNull ConnectionDetails key = new ConnectionDetails(localIdentifier, remoteIdentifier, isAcceptor);
hostByConnectionStatus.put(key, new ConnectionEvent(DISCONNECTED));
LOG.info(key + ", connectionStatus=" + DISCONNECTED);
onConnectionChanged(localIdentifier, remoteIdentifier, isAcceptor);
}
private void onConnectionChanged(int localIdentifier, int remoteIdentifier, boolean isAcceptor) {
@NotNull ConnectionDetails k1a = new ConnectionDetails(localIdentifier,
remoteIdentifier, isAcceptor);
@NotNull ConnectionDetails k1b = new ConnectionDetails(remoteIdentifier,
localIdentifier, !isAcceptor);
@NotNull ConnectionDetails k2a = new ConnectionDetails(localIdentifier,
remoteIdentifier, !isAcceptor);
@NotNull ConnectionDetails k2b = new ConnectionDetails(remoteIdentifier,
localIdentifier, isAcceptor);
@NotNull ConnectionStatus connectionStatus = DISCONNECTED;
if ((get(k1a) == CONNECTED && get(k1b) == CONNECTED) ||
(get(k2a) == CONNECTED && get(k2b) == CONNECTED)) {
connectionStatus = CONNECTED;
}
connectivityHosts.put("" + localIdentifier + "<->" + remoteIdentifier,
connectionStatus);
}
private ConnectionStatus get(ConnectionDetails connectionDetails) {
@Nullable ConnectionEvent connectionEvent = hostByConnectionStatus.get(connectionDetails);
if (connectionEvent == null)
return DISCONNECTED;
return connectionEvent.connectionStatus;
}
};
}
@NotNull
@Override
public String toString() {
return "hostByConnectionStatus=" + hostByConnectionStatus.entrySet().toString();
}
enum ConnectionStatus implements Serializable {
CONNECTED, DISCONNECTED
}
private static class ConnectionEvent extends AbstractMarshallable implements Serializable {
// has to be a string as enums wont go in the chronicle map
public ConnectionStatus connectionStatus;
public long timeStamp;
ConnectionEvent(ConnectionStatus connectionStatus) {
this.connectionStatus = connectionStatus;
this.timeStamp = System.currentTimeMillis();
}
}
public static class ConnectionDetails extends AbstractMarshallable implements Serializable {
public int localIdentifier;
public int remoteIdentifier;
public boolean isAcceptor;
ConnectionDetails(int localIdentifier, int remoteIdentifier, boolean isAcceptor) {
this.localIdentifier = localIdentifier;
this.remoteIdentifier = remoteIdentifier;
this.isAcceptor = isAcceptor;
}
public int localIdentifier() {
return localIdentifier;
}
public int remoteIdentifier() {
return remoteIdentifier;
}
@NotNull
@Override
public String toString() {
return "localId=" + localIdentifier + ", remoteId=" + remoteIdentifier + ", isAcceptor=" + isAcceptor;
}
}
public static class Factory implements
MarshallableFunction<ClusterContext,
NetworkContext>, Demarshallable {
@UsedViaReflection
private Factory(@NotNull WireIn wireIn) {
}
public Factory() {
}
@NotNull
@Override
public NetworkContext apply(@NotNull ClusterContext context) {
return new EngineWireNetworkContext<>(((EngineClusterContext) context).assetRoot());
}
}
}