/*
* 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.network.cluster;
import net.openhft.chronicle.core.io.Closeable;
import net.openhft.chronicle.core.threads.EventLoop;
import net.openhft.chronicle.network.NetworkContext;
import net.openhft.chronicle.network.NetworkStatsListener;
import net.openhft.chronicle.network.RemoteConnector;
import net.openhft.chronicle.network.connection.WireOutPublisher;
import net.openhft.chronicle.wire.WireType;
import net.openhft.chronicle.wire.WriteMarshallable;
import org.jetbrains.annotations.NotNull;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
public class HostConnector implements Closeable {
private final WireType wireType;
// private final WriteMarshallable header;
private final Function<WireType, WireOutPublisher> wireOutPublisherFactory;
private final List<WriteMarshallable> bootstraps = new LinkedList<>();
private final RemoteConnector remoteConnector;
private final String connectUri;
private final Function<ClusterContext, NetworkContext> networkContextFactory;
@NotNull
private final ClusterContext clusterContext;
private final Function<ClusterContext, NetworkStatsListener> networkStatsListenerFactory;
private NetworkContext nc;
@NotNull
private volatile AtomicReference<WireOutPublisher> wireOutPublisher = new AtomicReference<>();
@NotNull
private EventLoop eventLoop;
HostConnector(@NotNull ClusterContext clusterContext,
final RemoteConnector remoteConnector,
@NotNull final HostDetails hostdetails) {
this.clusterContext = clusterContext;
this.remoteConnector = remoteConnector;
this.networkStatsListenerFactory = clusterContext.networkStatsListenerFactory();
this.networkContextFactory = clusterContext.networkContextFactory();
this.connectUri = hostdetails.connectUri();
this.wireType = clusterContext.wireType();
this.wireOutPublisherFactory = clusterContext.wireOutPublisherFactory();
this.eventLoop = clusterContext.eventLoop();
}
@Override
public synchronized void close() {
WireOutPublisher wp = wireOutPublisher.getAndSet(null);
SocketChannel socketChannel = nc.socketChannel();
if (socketChannel != null) {
Closeable.closeQuietly(socketChannel);
Closeable.closeQuietly(socketChannel.socket());
}
if (wp != null)
wp.close();
}
public synchronized void bootstrap(WriteMarshallable subscription) {
bootstraps.add(subscription);
WireOutPublisher wp = wireOutPublisher.get();
if (wp != null)
wp.put("", subscription);
}
public synchronized void connect() {
WireOutPublisher wireOutPublisher = wireOutPublisherFactory.apply(clusterContext.wireType());
if (!this.wireOutPublisher.compareAndSet(null, wireOutPublisher)) {
wireOutPublisher.close();
return;
}
// we will send the initial header as text wire, then the rest will be sent in
// what ever wire is configured
nc = networkContextFactory.apply(clusterContext);
nc.wireOutPublisher(wireOutPublisher);
nc.isAcceptor(false);
nc.heartbeatTimeoutMs(clusterContext.heartbeatTimeoutMs() * 2);
nc.socketReconnector(this::reconnect);
nc.serverThreadingStrategy(clusterContext.serverThreadingStrategy());
if (networkStatsListenerFactory != null) {
final NetworkStatsListener networkStatsListener = networkStatsListenerFactory.apply(clusterContext);
nc.networkStatsListener(networkStatsListener);
networkStatsListener.networkContext(nc);
}
boolean firstTime = true;
for (WriteMarshallable bootstrap : bootstraps) {
wireOutPublisher.publish(bootstrap);
// its only the uber-handler that we want to publish using TEXT_WIRE
if (firstTime)
wireOutPublisher.wireType(this.wireType);
}
nc.wireType(this.wireType);
remoteConnector.connect(connectUri, eventLoop, nc, 1_000L);
}
synchronized void reconnect() {
close();
if (!nc.isAcceptor())
HostConnector.this.connect();
}
}