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

com.sleepycat.je.rep.stream.Protocol 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.stream;

import java.nio.ByteBuffer;
import java.util.UUID;

import com.sleepycat.je.JEVersion;
import com.sleepycat.je.dbi.DatabaseId;
import com.sleepycat.je.log.LogEntryType;
import com.sleepycat.je.log.LogUtils;
import com.sleepycat.je.rep.NodeType;
import com.sleepycat.je.rep.impl.RepGroupImpl;
import com.sleepycat.je.rep.impl.RepImpl;
import com.sleepycat.je.rep.impl.node.NameIdPair;
import com.sleepycat.je.rep.impl.node.RepNode;
import com.sleepycat.je.rep.utilint.RepUtils.Clock;

/**
 * {@literal
 * Defines the messages used to set up a feeder-replica replication stream.
 *
 * Note: this protocol is an extension of je.rep.stream.BaseProtocol, which
 * defines a set of basic message operation codes that are to be used by
 * its subclasses.
 *
 * From Feeder to Replica
 *
 *    Heartbeat -> HeartbeatResponse
 *    Commit -> Ack
 *    Commit+ -> GroupAck
 *    Entry
 *    ShutdownRequest -> ShutdownResponse
 *
 * Note: in the future, we may want to support bulk entry messages
 *
 * From Replica to Feeder
 *
 * The following subset of messages represents the handshake protocol that
 * precedes the transmission of replication log entries.
 *
 *    ReplicaProtocolVersion -> FeederProtocolVersion | DuplicateNodeReject
 *    ReplicaJEVersions -> FeederJEVersions | JEVersionsReject
 *    NodeGroupInfo -> NodeGroupInfoOK | NodeGroupInfoReject
 *    SNTPRequest -> SNTPResponse
 *    -> HeartbeatResponse
 *
 * A HeartbeatResponse is not strictly a response message and may also be sent
 * spontaneously if there is no output activity in a heartbeat interval. This
 * spontaneous generation of a HeartbeatReponse ensures that a socket is not
 * timed out if the feeder or the replica replay are otherwise busy.
 *
 * Note that there may be multiple SNTPRequest/SNTPResponse message pairs that
 * are exchanged as part of a single handshake. So a successful handshake
 * requested sequence generated by the Replica looks like:
 *
 * ReplicaProtocolVersion ReplicaJEVersions MembershipInfo [SNTPRequest]+
 *
 * For a Subscriber, there is an optional additional message exchange used to
 * discover the database ID for the partition generation database if the name
 * of the database was supplied and the message is supported by the protocol
 * version:
 *
 *    DBIDRequest -> DBIDResponse
 *
 * The following messages constitute the syncup and the transmission of log
 * entries.
 *
 *    EntryRequest -> Entry | EntryNotFound | AlternateMatchpoint
 *    RestoreRequest -> RestoreResponse
 *    StartStream
 *
 * The Protocol instance has local state in terms of buffers that are reused
 * across multiple messages. A Protocol instance is expected to be used in
 * strictly serial fashion. Consequently, there is an instance for each Replica
 * to Feeder connection, and two instances per Feeder to Replica connection:
 * one for the InputThread and one for the OutputThread.
 * }
 */
public class Protocol extends BaseProtocol {

    /**
     * Normally the op code should fall in the range defined in BaseProtocol,
     * however following op code inherit from original implementation of
     * stream protocol thus we just copy them here for backward compatibility.
     */
    public final static MessageOp REPLICA_PROTOCOL_VERSION =
        new MessageOp((short) 1, ReplicaProtocolVersion.class);

    public final static MessageOp FEEDER_PROTOCOL_VERSION =
        new MessageOp((short) 2, FeederProtocolVersion.class);

    public final static MessageOp DUP_NODE_REJECT =
        new MessageOp((short) 3, DuplicateNodeReject.class);

    public final static MessageOp REPLICA_JE_VERSIONS =
        new MessageOp((short) 4, ReplicaJEVersions.class);

    public final static MessageOp FEEDER_JE_VERSIONS =
        new MessageOp((short) 5, FeederJEVersions.class);

    public final static MessageOp JE_VERSIONS_REJECT =
        new MessageOp((short) 6, JEVersionsReject.class);

    public final static MessageOp MEMBERSHIP_INFO =
        new MessageOp((short) 7, NodeGroupInfo.class);

    public final static MessageOp MEMBERSHIP_INFO_OK =
        new MessageOp((short) 8, NodeGroupInfoOK.class);

    public final static MessageOp MEMBERSHIP_INFO_REJECT =
        new MessageOp((short) 9, NodeGroupInfoReject.class);

    public final static MessageOp SNTP_REQUEST =
        new MessageOp((short)10, SNTPRequest.class);

    public final static MessageOp SNTP_RESPONSE =
        new MessageOp((short)11, SNTPResponse.class);

    public final static MessageOp REAUTHENTICATE =
        new MessageOp((short)12, ReAuthenticate.class);

    public final static MessageOp DBID_REQUEST =
        new MessageOp((short)13, DBIdRequest.class);

    public final static MessageOp DBID_RESPONSE =
        new MessageOp((short)14, DBIdResponse.class);

    public final static MessageOp SECURITY_FAILURE_RESPONSE =
        new MessageOp((short)15, SecurityFailureResponse.class);

    /** All message ops needed by protocol */
    private static final MessageOp[] ALL_MESSAGE_OPS = {
        REPLICA_PROTOCOL_VERSION,
        FEEDER_PROTOCOL_VERSION,
        DUP_NODE_REJECT,
        REPLICA_JE_VERSIONS,
        FEEDER_JE_VERSIONS,
        JE_VERSIONS_REJECT,
        MEMBERSHIP_INFO,
        MEMBERSHIP_INFO_OK,
        MEMBERSHIP_INFO_REJECT,
        SNTP_REQUEST,
        SNTP_RESPONSE,
        ENTRY,
        START_STREAM,
        HEARTBEAT,
        HEARTBEAT_RESPONSE,
        COMMIT,
        ACK,
        ENTRY_REQUEST,
        ENTRY_NOTFOUND,
        RESTORE_REQUEST,
        RESTORE_RESPONSE,
        ALT_MATCHPOINT,
        SHUTDOWN_REQUEST,
        SHUTDOWN_RESPONSE,
        GROUP_ACK,
        REAUTHENTICATE,
        DBID_REQUEST,
        DBID_RESPONSE,
        SECURITY_FAILURE_RESPONSE
    };

    private final Clock clock;

    /**
     * Returns a Protocol object configured that implements the specified
     * (supported) protocol version.
     *
     * @param repImpl the node using the protocol
     *
     * @param nameIdPair name-id pair of the node using the protocol
     *
     * @param clock clock used by the node
     *
     * @param protocolVersion the version of the protocol that must be
     *        implemented by this object
     *
     * @param maxProtocolVersion the highest supported protocol version, which
     *        may be lower than the code version, for testing purposes
     *
     * @param streamLogVersion the log version of the format used to write log
     *        entries
     */
    private Protocol(final RepImpl repImpl,
                     final NameIdPair nameIdPair,
                     final Clock clock,
                     final int protocolVersion,
                     final int maxProtocolVersion,
                     final int streamLogVersion,
                     @SuppressWarnings("unused")
                     final int groupFormatVersion) {

        super(repImpl, nameIdPair, protocolVersion, maxProtocolVersion,
              streamLogVersion, ALL_MESSAGE_OPS,
              /* no validity check, from legacy protocol */
              false);

        this.configuredVersion = protocolVersion;
        this.clock = clock;
    }

    /**
     * Returns a protocol object that supports the specific requested protocol
     * version, which must not be higher than the specified maximum version, or
     * null if no such version is supported.
     */
    public static Protocol get(final RepNode repNode,
            final int protocolVersion,
            final int maxProtocolVersion) {
         return get(repNode.getRepImpl(),
                    repNode.getNameIdPair(),
                    repNode.getClock(),
                    protocolVersion,
                    maxProtocolVersion,
                    repNode.getGroup().getFormatVersion());
    }

    public static Protocol get(final RepImpl repImpl,
                               final NameIdPair nameIdPair,
                               final Clock clock,
                               final int protocolVersion,
                               final int maxProtocolVersion,
                               final int groupFormatVersion) {
        return get(repImpl, nameIdPair, clock,
                   protocolVersion, maxProtocolVersion,
                   LogEntryType.LOG_VERSION, groupFormatVersion);
    }

    /**
     * Returns a protocol object that supports the specified protocol, which
     * must be less than the specified maximum version, and writes log entries
     * in the specified log version format.  Returns null if no such version is
     * supported.
     */
    public static Protocol get(final RepNode repNode,
            final int protocolVersion,
            final int maxProtocolVersion,
            final int streamLogVersion) {
        return get(repNode.getRepImpl(),
                   repNode.getNameIdPair(),
                   repNode.getClock(),
                   protocolVersion,
                   maxProtocolVersion,
                   streamLogVersion,
                   repNode.getGroup().getFormatVersion());

    }
    public static Protocol get(final RepImpl repImpl,
                               final NameIdPair nameIdPair,
                               final Clock clock,
                               final int protocolVersion,
                               final int maxProtocolVersion,
                               final int streamLogVersion,
                               final int groupFormatVersion) {

        /*
         * If the RepGroupImpl has been upgraded to version 3, then require
         * protocol version 5, which is required to support that RepGroupImpl
         * version.  This check prevents new facilities that depend on
         * RepGroupImpl version 3 from being seen by non-upgraded replicas.
         */
        int minProtocolVersion = MIN_VERSION;
        if (groupFormatVersion >= RepGroupImpl.FORMAT_VERSION_3) {
            minProtocolVersion = VERSION_5;
        }

        return get(repImpl, nameIdPair, clock,
                   protocolVersion, minProtocolVersion,
                   maxProtocolVersion, streamLogVersion, groupFormatVersion);
    }

    /**
     * Returns a protocol object using the specified minimum and maximum
     * values, returning null if no supported version is found.  Use this
     * method for testing when the RepGroupImpl object is not available.
     */
    static Protocol get(final RepNode repNode,
            final int protocolVersion,
            final int minProtocolVersion,
            final int maxProtocolVersion,
            final int streamLogVersion) {
        int formatVersion = RepGroupImpl.MAX_FORMAT_VERSION;
        if (repNode.getGroup() != null) {
            formatVersion = repNode.getGroup().getFormatVersion();
        }

        return get(repNode.getRepImpl(),
                   repNode.getNameIdPair(),
                   repNode.getClock(),
                   protocolVersion,
                   minProtocolVersion,
                   maxProtocolVersion,
                   streamLogVersion,
                   formatVersion);
    }

    private static Protocol get(final RepImpl repImpl,
                                final NameIdPair nameIdPair,
                                final Clock clock,
                                final int protocolVersion,
                                final int minProtocolVersion,
                                final int maxProtocolVersion,
                                final int streamLogVersion,
                                final int groupFormatVersion) {

        if (!isSupportedVersion(protocolVersion, minProtocolVersion,
                                maxProtocolVersion)) {
            return null;
        }

        /*
         * Future code will do what is appropriate in support of the version
         * depending on the nature of the incompatibility.
         */
        return new Protocol(repImpl, nameIdPair, clock,
                            protocolVersion, maxProtocolVersion,
                            streamLogVersion, groupFormatVersion);
    }

    /**
     * Returns a protocol object using the specified protocol version.
     */
    static Protocol getProtocol(final RepNode repNode,
                                final int protocolVersion) {
        int formatVersion = RepGroupImpl.MAX_FORMAT_VERSION;
        if (repNode.getGroup() != null) {
            formatVersion = repNode.getGroup().getFormatVersion();
        }

        return
        getProtocol(repNode.getRepImpl(),
                    repNode.getNameIdPair(),
                    repNode.getClock(),
                    protocolVersion,
                    formatVersion);
    }

    static Protocol getProtocol(final RepImpl repImpl,
                                final NameIdPair nameIdPair,
                                final Clock clock,
                                final int protocolVersion,
                                final int groupFormatVersion) {

        return new Protocol(repImpl, nameIdPair, clock,
                            protocolVersion, protocolVersion,
                            LogEntryType.LOG_VERSION, groupFormatVersion);
    }

    /**
     * Returns true if the code can support the version.
     *
     * @param protocolVersion protocol version being queried
     * @param minProtocolVersion minimum protocol version supported
     * @param maxProtocolVersion maximum protocol version supported
     *
     * @return true if the protocol version is supported by this implementation
     *         of the protocol
     */
    private static boolean isSupportedVersion(final int protocolVersion,
                                              final int minProtocolVersion,
                                              final int maxProtocolVersion) {
        if (protocolVersion == Integer.MIN_VALUE) {
            /* For testing purposes. */
            return false;
        }

        /*
         * Version compatibility check: for now, a simple range check.  We can
         * make this fancier in the future if necessary.
         */
        return minProtocolVersion <= protocolVersion &&
            protocolVersion <= maxProtocolVersion;
    }

    /**
     * Gets the JE version that corresponds to the specified protocol version,
     * for use in creating error messages that explain protocol version errors
     * in terms of JE versions.  Returns null if the associated version is not
     * known.
     */
    static JEVersion getProtocolJEVersion(final int protocolVersion) {
        switch (protocolVersion) {
        case VERSION_9:
            return VERSION_9_JE_VERSION;
        case VERSION_8:
            return VERSION_8_JE_VERSION;
        case VERSION_7:
            return VERSION_7_JE_VERSION;
        case VERSION_6:
            return VERSION_6_JE_VERSION;
        case VERSION_5:
            return VERSION_5_JE_VERSION;
        case VERSION_4:
            return VERSION_4_JE_VERSION;
        case VERSION_3:
            return VERSION_3_JE_VERSION;
        default:
            return null;
        }
    }

    /**
     * Gets the protocol version that corresponds to the specified JE version,
     * throwing an IllegalArgumentException if the version is not supported.
     */
    static int getJEVersionProtocolVersion(final JEVersion jeVersion) {
        if (jeVersion == null) {
            return VERSION_9;
        } else if (jeVersion.compareTo(VERSION_9_JE_VERSION) >= 0) {
            return VERSION_9;
        } else if (jeVersion.compareTo(VERSION_8_JE_VERSION) >= 0) {
            return VERSION_8;
        } else if (jeVersion.compareTo(VERSION_7_JE_VERSION) >= 0) {
            return VERSION_7;
        } else if (jeVersion.compareTo(VERSION_6_JE_VERSION) >= 0) {
            return VERSION_6;
        } else if (jeVersion.compareTo(VERSION_5_JE_VERSION) >= 0) {
            return VERSION_5;
        } else if (jeVersion.compareTo(VERSION_4_JE_VERSION) >= 0) {
            return VERSION_4;
        } else if (jeVersion.compareTo(VERSION_3_JE_VERSION) >= 0) {
            return VERSION_3;
        } else {
            throw new IllegalArgumentException(
                "JE version not supported: " + jeVersion);
        }
    }

    /**
     * The replica sends the feeder its protocol version.
     *
     * IMPORTANT: This message must not change.
     */
    public class ReplicaProtocolVersion extends ProtocolVersion {

        public ReplicaProtocolVersion() {
            super(configuredVersion);
        }

        public ReplicaProtocolVersion(ByteBuffer buffer) {
            super(buffer);
        }

        @Override
        public MessageOp getOp() {
            return REPLICA_PROTOCOL_VERSION;
        }
    }

    /**
     * The feeder sends the replica its proposed version.
     *
     * IMPORTANT: This message must not change.
     */
    public class FeederProtocolVersion extends ProtocolVersion {

        public FeederProtocolVersion(int proposedVersion) {
            super(proposedVersion);
        }

        public FeederProtocolVersion(ByteBuffer buffer) {
            super(buffer);
        }

        @Override
        public MessageOp getOp() {
            return FEEDER_PROTOCOL_VERSION;
        }
    }

    /* Reject response to a ReplicaProtocolVersion request */
    public class DuplicateNodeReject extends RejectMessage {

        DuplicateNodeReject(String errorMessage) {
            super(errorMessage);
        }

        public DuplicateNodeReject(ByteBuffer buffer) {
            super(buffer);
        }

        @Override
        public MessageOp getOp() {
            return DUP_NODE_REJECT;
        }
    }

    public class SNTPRequest extends HandshakeMessage {

        private final long originateTimestamp;

        /* Set by the receiver at the time the message is recreated. */
        private long receiveTimestamp = -1;

        /*
         * Determines whether this is the last in a consecutive stream of
         * requests to determine the skew.
         */
        private boolean isLast = true;

        public SNTPRequest(boolean isLast) {
            super();
            this.isLast = isLast;
            originateTimestamp = clock.currentTimeMillis();
        }

        @Override
        public ByteBuffer wireFormat() {
            return wireFormat(originateTimestamp, isLast);
        }

        public SNTPRequest(ByteBuffer buffer) {
            this.originateTimestamp = LogUtils.readLong(buffer);
            this.isLast = getBoolean(buffer);
            this.receiveTimestamp = clock.currentTimeMillis();
        }

        @Override
        public MessageOp getOp() {
            return SNTP_REQUEST;
        }

        public long getOriginateTimestamp() {
            return originateTimestamp;
        }

        public long getReceiveTimestamp() {
            return receiveTimestamp;
        }

        public boolean isLast() {
            return isLast;
        }
    }

    public class SNTPResponse extends HandshakeMessage {

        /* These fields have the standard SNTP interpretation */
        private final long originateTimestamp; // time request sent by client
        private final long receiveTimestamp; // time request received by server

        /*
         * Initialized when the message is serialized to ensure it's as
         * accurate as possible.
         */
        private long transmitTimestamp = -1; // time reply sent by server

        /* Initialized at de-serialization for similar reasons. */
        private long destinationTimestamp = -1; //time reply received by client

        public SNTPResponse(SNTPRequest request) {
            this.originateTimestamp = request.originateTimestamp;
            this.receiveTimestamp = request.receiveTimestamp;
        }

        @Override
        public ByteBuffer wireFormat() {
            transmitTimestamp = clock.currentTimeMillis();
            return wireFormat(originateTimestamp,
                              receiveTimestamp,
                              transmitTimestamp);
        }

        public SNTPResponse(ByteBuffer buffer) {
            originateTimestamp = LogUtils.readLong(buffer);
            receiveTimestamp = LogUtils.readLong(buffer);
            transmitTimestamp = LogUtils.readLong(buffer);
            destinationTimestamp = clock.currentTimeMillis();
        }

        @Override
        public MessageOp getOp() {
            return SNTP_RESPONSE;
        }

        public long getOriginateTimestamp() {
            return originateTimestamp;
        }

        public long getReceiveTimestamp() {
            return receiveTimestamp;
        }

        public long getTransmitTimestamp() {
            return transmitTimestamp;
        }

        public long getDestinationTimestamp() {
            return destinationTimestamp;
        }

        public long getDelay() {
            assert(destinationTimestamp != -1);
            return (destinationTimestamp - originateTimestamp) -
                    (transmitTimestamp - receiveTimestamp);
        }

        public long getDelta() {
            assert(destinationTimestamp != -1);
            return ((receiveTimestamp - originateTimestamp) +
                    (transmitTimestamp - destinationTimestamp))/2;
        }
    }

    public class ReAuthenticate extends SimpleMessage {

        private final byte[] tokenBytes;

        public ReAuthenticate(byte[] tokenBytes) {
            this.tokenBytes = tokenBytes;
        }

        @Override
        public ByteBuffer wireFormat() {
            return wireFormat(tokenBytes);
        }

        public ReAuthenticate(ByteBuffer buffer) {
            tokenBytes = getByteArray(buffer);
        }

        public byte[] getTokenBytes() {
            if (tokenBytes.length == 0) {
                return null;
            }

            return tokenBytes;
        }

        @Override
        public MessageOp getOp() {
            return REAUTHENTICATE;
        }
    }

    /**
     * Security check failure response message
     */
    public class SecurityFailureResponse extends SimpleMessage {

        /* non-empty error message */
        private final String message;

        public SecurityFailureResponse(String message) {
            if (message == null || message.isEmpty()) {
                throw new IllegalArgumentException(
                    "Message cannot be null or empty");
            }
            this.message = message;
        }

        @Override
        public ByteBuffer wireFormat() {
            return wireFormat(message);
        }

        public SecurityFailureResponse(ByteBuffer buffer) {
            message = getString(buffer);
        }

        public String getMessage() {
            return message;
        }

        @Override
        public MessageOp getOp() {
            return SECURITY_FAILURE_RESPONSE;
        }
    }

    /**
     * Request for the partition gen db id
     */
    public class DBIdRequest extends SimpleMessage {

        final String dbName;

        public DBIdRequest(String dbName) {
            super();
            this.dbName = dbName;
        }

        public DBIdRequest(ByteBuffer buffer) {
            super();
            dbName = getString(buffer);
        }

        public String getDbName() {
            return dbName;
        }

        @Override
        public ByteBuffer wireFormat() {
            return wireFormat(dbName);
        }

        @Override
        public MessageOp getOp() {
            return DBID_REQUEST;
        }
    }


    /**
     * Response when the PartMDRequest asks for partition md db id
     */
    public class DBIdResponse extends SimpleMessage {

        private final DatabaseId dbId;

        public DBIdResponse(DatabaseId dbId) {
            this.dbId = dbId;
        }

        public DBIdResponse(ByteBuffer buffer) {
            super();
            dbId = new DatabaseId(LogUtils.readLong(buffer));
        }

        public DatabaseId getDbId() {
            return dbId;
        }

        @Override
        public ByteBuffer wireFormat() {
            return wireFormat(dbId.getId());
        }

        @Override
        public MessageOp getOp() {
            return DBID_RESPONSE;
        }
    }


    /**
     * Abstract message used as the basis for the exchange of software versions
     * between replicated nodes
     */
    abstract class JEVersions extends HandshakeMessage {
        private final JEVersion version;

        private final int logVersion;

        public JEVersions(JEVersion version, int logVersion) {
            this.version = version;
            this.logVersion = logVersion;
        }

        @Override
        public ByteBuffer wireFormat() {
            return wireFormat(version.getVersionString(), logVersion);
        }

        public JEVersions(ByteBuffer buffer) {
            this.version = new JEVersion(getString(buffer));
            this.logVersion = LogUtils.readInt(buffer);
        }

        public JEVersion getVersion() {
            return version;
        }

        public byte getLogVersion() {
            return (byte)logVersion;
        }
    }

    public class ReplicaJEVersions extends JEVersions {

        ReplicaJEVersions(JEVersion version, int logVersion) {
            super(version, logVersion);
        }

        public ReplicaJEVersions(ByteBuffer buffer) {
            super(buffer);
        }

        @Override
        public MessageOp getOp() {
            return REPLICA_JE_VERSIONS;
        }

    }

    public class FeederJEVersions extends JEVersions {

        /* Is null if protocol version < VERSION_7. */
        private JEVersion minJEVersion;

        FeederJEVersions(JEVersion version,
                         int logVersion,
                         JEVersion minJEVersion) {
            super(version, logVersion);
            this.minJEVersion = minJEVersion;
        }

        @Override
        public ByteBuffer wireFormat() {
            if (configuredVersion < VERSION_7){
                return super.wireFormat();
            }
            return wireFormat(
                getVersion().getVersionString(),
                (int) getLogVersion(),
                minJEVersion.getVersionString());
        }

        public FeederJEVersions(ByteBuffer buffer) {
            super(buffer);
            if (configuredVersion < VERSION_7){
                return;
            }
            this.minJEVersion = new JEVersion(getString(buffer));
        }

        @Override
        public MessageOp getOp() {
            return FEEDER_JE_VERSIONS;
        }

        /**
         * Returns the minJEVersion of the group, or null if unknown (in
         * protocol versions < VERSION_7).
         */
        public JEVersion getMinJEVersion() {
            return minJEVersion;
        }
    }

    /* Reject response to a ReplicaJEVersions request */
    public class JEVersionsReject extends RejectMessage {

        public JEVersionsReject(String errorMessage) {
            super(errorMessage);
        }

        public JEVersionsReject(ByteBuffer buffer) {
            super(buffer);
        }

        @Override
        public MessageOp getOp() {
            return JE_VERSIONS_REJECT;
        }
    }

    public class NodeGroupInfo extends HandshakeMessage {
        private final String groupName;
        private final UUID uuid;

        @SuppressWarnings("hiding")
        private final NameIdPair nameIdPair;
        private final String hostName;
        private final int port;
        private final NodeType nodeType;
        private final boolean designatedPrimary;

        /**
         * A string version of the JE version running on this node, or the
         * empty string if not known.
         */
        private final String jeVersion;

        NodeGroupInfo(final String groupName,
                      final UUID uuid,
                      final NameIdPair nameIdPair,
                      final String hostName,
                      final int port,
                      final NodeType nodeType,
                      final boolean designatedPrimary,
                      final JEVersion jeVersion) {

            this.groupName = groupName;
            this.uuid = uuid;
            this.nameIdPair = nameIdPair;
            this.hostName = hostName;
            this.port = port;
            this.nodeType = nodeType;
            this.designatedPrimary = designatedPrimary;
            this.jeVersion = (jeVersion != null) ?
                jeVersion.getNumericVersionString() :
                "";
        }

        @Override
        public MessageOp getOp() {
            return MEMBERSHIP_INFO;
        }

        @Override
        public ByteBuffer wireFormat() {
            final boolean repGroupV3 = (getVersion() >= VERSION_5);
            if (!repGroupV3 && nodeType.compareTo(NodeType.ELECTABLE) > 0) {
                throw new IllegalStateException(
                    "Node type not supported before group version 3: " +
                    nodeType);
            }
            final Object[] args = new Object[repGroupV3 ? 9 : 8];
            args[0] = groupName;
            args[1] = uuid.getMostSignificantBits();
            args[2] = uuid.getLeastSignificantBits();
            args[3] = nameIdPair;
            args[4] = hostName;
            args[5] = port;
            args[6] = nodeType;
            args[7] = designatedPrimary;
            if (repGroupV3) {
                args[8] = jeVersion;
            }
            return wireFormat(args);
        }

        public NodeGroupInfo(ByteBuffer buffer) {
            this.groupName = getString(buffer);
            this.uuid = new UUID(LogUtils.readLong(buffer),
                                 LogUtils.readLong(buffer));
            this.nameIdPair = getNameIdPair(buffer);
            this.hostName = getString(buffer);
            this.port = LogUtils.readInt(buffer);
            this.nodeType = getEnum(NodeType.class, buffer);
            this.designatedPrimary = getBoolean(buffer);
            jeVersion = (getVersion() >= VERSION_5) ? getString(buffer) : "";
        }

        public String getGroupName() {
            return groupName;
        }

        public UUID getUUID() {
            return uuid;
        }

        public String getNodeName() {
            return nameIdPair.getName();
        }

        public int getNodeId() {
            return nameIdPair.getId();
        }

        public String getHostName() {
            return hostName;
        }

        public NameIdPair getNameIdPair() {
            return nameIdPair;
        }

        public int port() {
            return port;
        }
        public NodeType getNodeType() {
            return nodeType;
        }

        public boolean isDesignatedPrimary() {
            return designatedPrimary;
        }

        /**
         * Returns the JE version most recently noted running on the associated
         * node, or null if not known.
         */
        public JEVersion getJEVersion() {
            return !jeVersion.isEmpty() ? new JEVersion(jeVersion) : null;
        }
    }

    /**
     * Response to a NodeGroupInfo request that was successful.  The object
     * contains the group's UUID and the replica's NameIdPair.  The group UUID
     * is used to update the replica's notion of the group UUID on first
     * joining.  The NameIdPair is used to update the replica's node ID for a
     * secondary node, which is not available in the RepGroupDB.
     */
    public class NodeGroupInfoOK extends HandshakeMessage {

        private final UUID uuid;
        @SuppressWarnings("hiding")
        private final NameIdPair nameIdPair;

        public NodeGroupInfoOK(UUID uuid, NameIdPair nameIdPair) {
            super();
            this.uuid = uuid;
            this.nameIdPair = nameIdPair;
        }

        public NodeGroupInfoOK(ByteBuffer buffer) {
            uuid = new UUID(LogUtils.readLong(buffer),
                            LogUtils.readLong(buffer));
            nameIdPair = getNameIdPair(buffer);
        }

        @Override
        public ByteBuffer wireFormat() {
            return wireFormat(uuid.getMostSignificantBits(),
                              uuid.getLeastSignificantBits(),
                              nameIdPair);
        }

        @Override
        public MessageOp getOp() {
            return MEMBERSHIP_INFO_OK;
        }

        public NameIdPair getNameIdPair() {
            return nameIdPair;
        }

        public UUID getUUID() {
            return uuid;
        }
    }

    public class NodeGroupInfoReject extends RejectMessage {

        NodeGroupInfoReject(String errorMessage) {
            super(errorMessage);
        }

        @Override
        public MessageOp getOp() {
            return MEMBERSHIP_INFO_REJECT;
        }

        @Override
        public ByteBuffer wireFormat() {
            return wireFormat(errorMessage);
        }

        public NodeGroupInfoReject(ByteBuffer buffer) {
            super(buffer);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy