/* * 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; import net.openhft.chronicle.core.io.Closeable; import net.openhft.chronicle.core.threads.EventLoop; import net.openhft.chronicle.engine.api.tree.AssetTree; import net.openhft.chronicle.engine.server.internal.EngineWireHandler; import net.openhft.chronicle.engine.server.internal.EngineWireNetworkContext; import net.openhft.chronicle.network.*; import net.openhft.chronicle.network.api.TcpHandler; import net.openhft.chronicle.network.api.session.SessionDetailsProvider; import net.openhft.chronicle.network.connection.VanillaWireOutPublisher; import net.openhft.chronicle.threads.Threads; import net.openhft.chronicle.wire.WireType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; import static net.openhft.chronicle.core.io.Closeable.closeQuietly; /** * Created by Rob Austin */ public class ServerEndpoint implements Closeable { private static final Logger LOG = LoggerFactory.getLogger(ServerEndpoint.class); @Nullable private final EventLoop eg; @NotNull private final AtomicBoolean isClosed = new AtomicBoolean(); @Nullable private AcceptorEventHandler eah; public ServerEndpoint(@NotNull String hostPortDescription, @NotNull AssetTree assetTree, @NotNull NetworkStatsListener networkStatsListener) throws IOException { eg = assetTree.root().acquireView(EventLoop.class); Threads.<Void, IOException>withThreadGroup(assetTree.root().getView(ThreadGroup.class), () -> { start(hostPortDescription, assetTree, networkStatsListener); return null; }); assetTree.root().addView(ServerEndpoint.class, this); } public ServerEndpoint(@NotNull String hostPortDescription, @NotNull AssetTree assetTree) throws IOException { this(hostPortDescription, assetTree, new NetworkStatsListener() { private String host; private long port; @Override public void close() { LOG.info(" host=" + host + ", port=" + port + ", isConnected=false"); } @Override public void networkContext(NetworkContext networkContext) { } @Override public void onNetworkStats(long writeBps, long readBps, long socketPollCountPerSecond) { LOG.info("writeKBps=" + writeBps / 1000 + ", readKBps=" + readBps / 1000 + ", socketPollCountPerSecond=" + socketPollCountPerSecond / 1000 + "K, " + "host=" + host + ", port=" + port + ", isConnected=true"); } @Override public void onHostPort(String hostName, int port) { host = hostName; this.port = port; } @Override public void onRoundTripLatency(long nanosecondLatency) { } }); } @Nullable private AcceptorEventHandler start(@NotNull String hostPortDescription, @NotNull final AssetTree assetTree, @NotNull NetworkStatsListener networkStatsListener) throws IOException { assert eg != null; eg.start(); if (LOG.isInfoEnabled()) LOG.info("starting server=" + hostPortDescription); @Nullable final EventLoop eventLoop = assetTree.root().findOrCreateView(EventLoop.class); assert eventLoop != null; @NotNull Function<NetworkContext, TcpEventHandler> networkContextTcpEventHandlerFunction = (networkContext) -> { @NotNull final EngineWireNetworkContext nc = (EngineWireNetworkContext) networkContext; if (nc.isAcceptor()) nc.wireOutPublisher(new VanillaWireOutPublisher(WireType.TEXT)); @NotNull final TcpEventHandler handler = new TcpEventHandler(networkContext); @NotNull final Function<Object, TcpHandler> consumer = o -> { if (o instanceof SessionDetailsProvider) { @NotNull final SessionDetailsProvider sessionDetails = (SessionDetailsProvider) o; nc.sessionDetails(sessionDetails); nc.wireType(sessionDetails.wireType()); @Nullable final WireType wireType = nc.sessionDetails().wireType(); if (wireType != null) nc.wireOutPublisher().wireType(wireType); return new EngineWireHandler(); } else if (o instanceof TcpHandler) return (TcpHandler) o; throw new UnsupportedOperationException("not supported class=" + o.getClass()); }; @Nullable final Function<EngineWireNetworkContext, TcpHandler> f = x -> new HeaderTcpHandler<>(handler, consumer, x); @NotNull final WireTypeSniffingTcpHandler sniffer = new WireTypeSniffingTcpHandler<>(handler, f); handler.tcpHandler(sniffer); return handler; }; @NotNull final AcceptorEventHandler eah = new AcceptorEventHandler( hostPortDescription, networkContextTcpEventHandlerFunction, () -> createNetworkContext(assetTree, networkStatsListener)); eg.addHandler(eah); this.eah = eah; return eah; } @NotNull private EngineWireNetworkContext createNetworkContext(@NotNull AssetTree assetTree, @NotNull final NetworkStatsListener networkStatsListener) { @NotNull final EngineWireNetworkContext nc = new EngineWireNetworkContext(assetTree.root()); nc.networkStatsListener(networkStatsListener); networkStatsListener.networkContext(nc); return nc; } @Override public void close() { isClosed.set(true); closeQuietly(eah); eah = null; } }