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

com.sleepycat.je.rep.utilint.NamedChannelWithTimeout Maven / Gradle / Ivy

The newest version!
/*-
 * Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle Berkeley
 * DB Java Edition made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle Berkeley DB Java Edition for a copy of the
 * license and additional information.
 */
package com.sleepycat.je.rep.utilint;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.logging.Logger;

import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.rep.impl.RepImpl;
import com.sleepycat.je.rep.impl.node.ChannelTimeoutTask;
import com.sleepycat.je.rep.impl.node.RepNode;
import com.sleepycat.je.rep.net.DataChannel;
import com.sleepycat.je.utilint.LoggerUtils;

/**
 * NamedChannelWithTimeout permits association of timeouts with a DataChannel.
 * This mechanism is necessary, since the standard mechanism for associating
 * timeouts with sockets using Socket.setSoTimeout is not supported by nio
 * SocketChannels.
 */
public class NamedChannelWithTimeout
    extends NamedChannel {

    /*
     * Denotes read activity associated with the channel. It's set each time a
     * read is successfully executed on the channel. The presence of heartbeats
     * is typically used to guarantee some minimum level of activity over the
     * channel.
     */
    private volatile boolean readActivity;

    /*
     * The timeout associated with the channel. A value of zero indicates no
     * timeout.
     */
    private volatile int timeoutMs;

    /* Values to help with logging. */
    private final EnvironmentImpl envImpl;
    private final Logger logger;

    /* The "time" of the last check for read activity on the channel. */
    private long lastCheckMs = 0l;

    public NamedChannelWithTimeout(RepNode repNode,
                                   DataChannel channel,
                                   int timeoutMs) {
        this(repNode.getRepImpl(),
             repNode.getLogger(),
             repNode.getChannelTimeoutTask(),
             channel,
             timeoutMs);
    }

    public NamedChannelWithTimeout(RepImpl repImpl,
                                   Logger logger,
                                   ChannelTimeoutTask channelTimeoutTask,
                                   DataChannel channel,
                                   int timeoutMs) {
        super(channel);
        this.timeoutMs = timeoutMs;
        this.envImpl = repImpl;
        this.logger = logger;
        readActivity = true;
        if (timeoutMs > 0) {
            /* Only register with a timer, if a timeout is being requested. */
            channelTimeoutTask.register(this);
        }
    }

    /**
     * Used to modify the timeout associated with the channel.
     *
     * @param timeoutMs the new timeout value
     */
    public void setTimeoutMs(int timeoutMs) {
        this.timeoutMs = timeoutMs;
        /* Ensure that the next tick resets the time and counter. */
        readActivity = true;
    }

    /*
     * Methods below supply the Protocol for byte channel. The implementations
     * wrap read operations to track i/o activity by setting readActivity.
     * getCurrentActivity() can be used to determine if there was any read
     * activity since an earlier call to the method.
     */
    @Override
    public int read(ByteBuffer dst)
        throws IOException {

        final int bytes = channel.read(dst);
        if (bytes > 0) {
            readActivity = true;
        }
        return bytes;
    }

    @Override
    public void close()
        throws IOException {
        channel.close();
        readActivity = false;
    }

    private void resetActivityCounter(long timeMs) {
        lastCheckMs = timeMs;
        readActivity = false;
    }

    /**
     * Method invoked by the time thread to check on the channel on a periodic
     * basis. Note that the time that is passed in is a "pseudo" time that is
     * only meaningful for calculating time differences.
     *
     * @param timeMs the pseudo time
     *
     * @return true if the channel is active, false if it isn't and has been
     * closed
     */
    public boolean isActive(long timeMs) {

        if (!channel.isOpen()) {
            /* some thread closed it. */
            return false;
        }

        if (!channel.isConnected()) {
            /* Not yet connected, wait for it to be connected. */
            return true;
        }

        if (readActivity) {
            resetActivityCounter(timeMs);
            return true;
        }

        if ((timeoutMs == 0) || (timeMs - lastCheckMs) < timeoutMs) {
            return true;
        }

        /*
         * No activity, force the channel closed thus generating an
         * AsynchronousCloseException in the read/write threads.
         */
        LoggerUtils.info(logger, envImpl,
                         "Inactive channel: " + getNameIdPair() +
                         " forced close. Timeout: " + timeoutMs + "ms.");
        final long startTime = System.currentTimeMillis();
        try {
            channel.closeForcefully();
        } catch (IOException e) {
            LoggerUtils.warning(logger, envImpl,
                                "Fail to close inactive channel: " +
                                getNameIdPair() +
                                ". Local socket port: " +
                                channel.getSocketChannel().
                                    socket().getLocalPort() +
                                ". Exception: " + e);
        } catch (Error e) {
            LoggerUtils.warning(logger, envImpl,
                                "Error while closing inactive channel: " +
                                getNameIdPair() +
                                ". Local socket port: " +
                                channel.getSocketChannel().
                                    socket().getLocalPort() +
                                ". Error: " + e);
            /* JVM to handle the error */
            throw e;
        }
        final long timeElapsed = System.currentTimeMillis() - startTime;
        /* Log the unexpected closure time which is longer than 5 seconds */
        if (timeElapsed > 5000) {
            LoggerUtils.info(logger, envImpl,
                             "Time to close inactive channel " +
                             getNameIdPair() + ": " + timeElapsed + "ms");
        }
        return false;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy