All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.openhft.chronicle.network.WireTcpHandler Maven / Gradle / Ivy

There is a newer version: 2.17.4
Show newest version
/*
 * 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;

import net.openhft.chronicle.bytes.Bytes;
import net.openhft.chronicle.core.annotation.Nullable;
import net.openhft.chronicle.network.api.TcpHandler;
import net.openhft.chronicle.network.connection.WireOutPublisher;
import net.openhft.chronicle.wire.*;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static net.openhft.chronicle.network.connection.CoreFields.reply;
import static net.openhft.chronicle.wire.WriteMarshallable.EMPTY;

public abstract class WireTcpHandler implements TcpHandler,
        NetworkContextManager {

    private static final int SIZE_OF_SIZE = 4;
    private static final Logger LOG = LoggerFactory.getLogger(WireTcpHandler.class);
    // this is the point at which it is worth doing more work to get more data.

    @NotNull
    protected Wire outWire;
    @NotNull
    private Wire inWire;
    private boolean recreateWire;
    @Nullable
    private WireType wireType;
    private WireOutPublisher publisher;
    private T nc;
    private volatile boolean closed;
    private boolean isAcceptor;

    private static void logYaml(final WireOut outWire) {
        if (YamlLogging.showServerWrites())
            try {
                LOG.info("\nServer Sends:\n" +
                        Wires.fromSizePrefixedBlobs(outWire.bytes()));
            } catch (Exception e) {
                LOG.info("\nServer Sends ( corrupted ) :\n" +
                        outWire.bytes().toDebugString());
            }
    }

    public boolean isAcceptor() {
        return this.isAcceptor;
    }

    public void wireType(@NotNull WireType wireType) {
        this.wireType = wireType;
        if (publisher != null)
            publisher.wireType(wireType);
    }

    public WireOutPublisher publisher() {
        return publisher;
    }

    public void publisher(WireOutPublisher publisher) {
        this.publisher = publisher;
        if (wireType() != null)
            publisher.wireType(wireType());
    }

    public void isAcceptor(boolean isAcceptor) {
        this.isAcceptor = isAcceptor;
    }

    @Override
    public void process(@NotNull Bytes in, @NotNull Bytes out) {

        if (closed)
            return;

        final WireType wireType = wireType();
        checkWires(in, out, wireType == null ? WireType.TEXT : wireType);

        if (publisher != null && out.writePosition() < TcpEventHandler.TCP_BUFFER)
            publisher.applyAction(outWire);

        if (in.readRemaining() >= SIZE_OF_SIZE)
            onRead0();

        if (out.writePosition() < TcpEventHandler.TCP_BUFFER)
            onWrite(outWire);

    }

    @Override
    public void onEndOfConnection(boolean heartbeatTimeOut) {
        if (publisher != null)
            publisher.close();
    }

    protected void onWrite(@NotNull WireOut out) {

    }

    /**
     * process all messages in this batch, provided there is plenty of output space.
     */
    private void onRead0() {
        assert inWire.startUse();

        try {
            while (!inWire.bytes().isEmpty()) {
                long start = inWire.bytes().readPosition();
                try (DocumentContext dc = inWire.readingDocument()) {
                    if (!dc.isPresent()) {
                        return;
                    }

                    try {
                        logYaml(start);
                        onRead(dc, outWire);

                    } catch (Exception e) {
                        LOG.error("inWire=" + inWire.getClass(), e);
                    }
                }
            }
        } finally {
            assert inWire.endUse();
        }
    }

    private void logYaml(long start) {
        if (YamlLogging.showServerReads() && !inWire.bytes().isEmpty()) {
            String s = Wires.fromSizePrefixedBlobs(inWire.bytes(), start, inWire.bytes()
                    .readLimit());
            LOG.info("handler=" + this.getClass().getSimpleName() + ", read:\n" + s);
        }
    }

    protected void checkWires(Bytes in, Bytes out, @NotNull WireType wireType) {
        if (recreateWire) {
            recreateWire = false;
            inWire = wireType.apply(in);
            outWire = wireType.apply(out);
            return;
        }

        if (inWire == null) {
            inWire = wireType.apply(in);
            recreateWire = false;
        }

        assert inWire.startUse();
        if (inWire.bytes() != in) {
            inWire = wireType.apply(in);
            recreateWire = false;
        }
        assert inWire.endUse();

        boolean replace = outWire == null;
        if (!replace) {
            assert outWire.startUse();
            replace = outWire.bytes() != out;
            assert outWire.endUse();
        }
        if (replace) {
            outWire = wireType.apply(out);
            recreateWire = false;
        }
    }

    /**
     * Process an incoming request
     */
    public WireType wireType() {
        return this.wireType;
    }

    /**
     * @param in  the wire to be processed
     * @param out the result of processing the {@code in}
     */
    protected abstract void onRead(@NotNull DocumentContext in,
                                   @NotNull WireOut out);

    /**
     * write and exceptions and rolls back if no data was written
     */
    protected void writeData(@NotNull Bytes inBytes, @NotNull WriteMarshallable c) {
        outWire.writeDocument(false, out -> {
            final long readPosition = inBytes.readPosition();
            final long position = outWire.bytes().writePosition();
            try {
                c.writeMarshallable(outWire);
            } catch (Throwable t) {
                inBytes.readPosition(readPosition);
                if (LOG.isInfoEnabled())
                    LOG.info("While reading " + inBytes.toDebugString(),
                            " processing wire " + c, t);
                outWire.bytes().writePosition(position);
                outWire.writeEventName(() -> "exception").throwable(t);
            }

            // write 'reply : {} ' if no data was sent
            if (position == outWire.bytes().writePosition()) {
                outWire.writeEventName(reply).marshallable(EMPTY);
            }
        });

        logYaml(outWire);
    }

    /**
     * write and exceptions and rolls back if no data was written
     */
    protected void writeData(boolean isNotComplete, @NotNull Bytes inBytes, @NotNull WriteMarshallable c) {

        final WriteMarshallable marshallable = out -> {
            final long readPosition = inBytes.readPosition();
            final long position = outWire.bytes().writePosition();
            try {
                c.writeMarshallable(outWire);
            } catch (Throwable t) {
                inBytes.readPosition(readPosition);
                if (LOG.isInfoEnabled())
                    LOG.info("While reading " + inBytes.toDebugString(),
                            " processing wire " + c, t);
                outWire.bytes().writePosition(position);
                outWire.writeEventName(() -> "exception").throwable(t);
            }

            // write 'reply : {} ' if no data was sent
            if (position == outWire.bytes().writePosition()) {
                outWire.writeEventName(reply).marshallable(EMPTY);
            }
        };

        if (isNotComplete)
            outWire.writeNotCompleteDocument(false, marshallable);
        else
            outWire.writeDocument(false, marshallable);

        logYaml(outWire);
    }

    public final void nc(T nc) {
        this.nc = nc;
        onInitialize();
    }

    public T nc() {
        return nc;
    }

    protected abstract void onInitialize();

    @Override
    public void close() {
        closed = true;
        nc.connectionClosed(true);
    }

    protected void publish(WriteMarshallable w) {
        publisher.put("", w);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy