/** * Licensed to the zk1931 under one or more contributor license * agreements. See the NOTICE file distributed with this work * for additional information regarding copyright ownership. * The ASF licenses this file to you 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 com.github.zk1931.jzab; import java.nio.ByteBuffer; import com.google.protobuf.ByteString; import com.github.zk1931.jzab.proto.ZabMessage; import com.github.zk1931.jzab.proto.ZabMessage.Ack; import com.github.zk1931.jzab.proto.ZabMessage.AckEpoch; import com.github.zk1931.jzab.proto.ZabMessage.Commit; import com.github.zk1931.jzab.proto.ZabMessage.Diff; import com.github.zk1931.jzab.proto.ZabMessage.Disconnected; import com.github.zk1931.jzab.proto.ZabMessage.Handshake; import com.github.zk1931.jzab.proto.ZabMessage.InvalidMessage; import com.github.zk1931.jzab.proto.ZabMessage.Message; import com.github.zk1931.jzab.proto.ZabMessage.NewEpoch; import com.github.zk1931.jzab.proto.ZabMessage.NewLeader; import com.github.zk1931.jzab.proto.ZabMessage.Proposal; import com.github.zk1931.jzab.proto.ZabMessage.ProposedEpoch; import com.github.zk1931.jzab.proto.ZabMessage.PullTxnReq; import com.github.zk1931.jzab.proto.ZabMessage.QueryReply; import com.github.zk1931.jzab.proto.ZabMessage.Remove; import com.github.zk1931.jzab.proto.ZabMessage.Request; import com.github.zk1931.jzab.proto.ZabMessage.Snapshot; import com.github.zk1931.jzab.proto.ZabMessage.Truncate; import static com.github.zk1931.jzab.proto.ZabMessage.Message.MessageType; import static com.github.zk1931.jzab.proto.ZabMessage.Proposal.ProposalType; /** * Helper class used for creating protobuf messages. */ final class MessageBuilder { private MessageBuilder() { // Can't be instantiated. } /** * Converts Zxid object to protobuf Zxid object. * * @param zxid the Zxid object. * @return the ZabMessage.Zxid */ public static ZabMessage.Zxid toProtoZxid(Zxid zxid) { ZabMessage.Zxid z = ZabMessage.Zxid.newBuilder() .setEpoch(zxid.getEpoch()) .setXid(zxid.getXid()) .build(); return z; } /** * Converts protobuf Zxid object to Zxid object. * * @param zxid the protobuf Zxid object. * @return the Zxid object. */ public static Zxid fromProtoZxid(ZabMessage.Zxid zxid) { return new Zxid(zxid.getEpoch(), zxid.getXid()); } /** * Converts protobuf Proposal object to Transaction object. * * @param prop the protobuf Proposal object. * @return the Transaction object. */ public static Transaction fromProposal(Proposal prop) { Zxid zxid = fromProtoZxid(prop.getZxid()); ByteBuffer buffer = prop.getBody().asReadOnlyByteBuffer(); return new Transaction(zxid, prop.getType().getNumber(), buffer); } /** * Converts Transaction object to protobuf Proposal object. * * @param txn the Transaction object. * @return the protobuf Proposal object. */ public static Proposal fromTransaction(Transaction txn) { ZabMessage.Zxid zxid = toProtoZxid(txn.getZxid()); ByteString bs = ByteString.copyFrom(txn.getBody()); return Proposal.newBuilder().setZxid(zxid).setBody(bs) .setType(ProposalType.values()[txn.getType()]).build(); } /** * Creates CEPOCH message. * * @param proposedEpoch the last proposed epoch. * @param acknowledgedEpoch the acknowledged epoch. * @param config the current seen configuration. * @return the protobuf message. */ public static Message buildProposedEpoch(long proposedEpoch, long acknowledgedEpoch, ClusterConfiguration config, int syncTimeoutMs) { ZabMessage.Zxid version = toProtoZxid(config.getVersion()); ZabMessage.ClusterConfiguration zCnf = ZabMessage.ClusterConfiguration.newBuilder() .setVersion(version) .addAllServers(config.getPeers()) .build(); ProposedEpoch pEpoch = ProposedEpoch.newBuilder() .setProposedEpoch(proposedEpoch) .setCurrentEpoch(acknowledgedEpoch) .setConfig(zCnf) .setSyncTimeout(syncTimeoutMs) .build(); return Message.newBuilder().setType(MessageType.PROPOSED_EPOCH) .setProposedEpoch(pEpoch) .build(); } /** * Creates a NEW_EPOCH message. * * @param epoch the new proposed epoch number. * @param syncTimeoutMs the timeout for synchronization. * @return the protobuf message. */ public static Message buildNewEpochMessage(long epoch, int syncTimeoutMs) { NewEpoch nEpoch = NewEpoch.newBuilder() .setNewEpoch(epoch) .setSyncTimeout(syncTimeoutMs) .build(); return Message.newBuilder().setType(MessageType.NEW_EPOCH) .setNewEpoch(nEpoch) .build(); } /** * Creates a ACK_EPOCH message. * * @param epoch the last leader proposal the follower has acknowledged. * @param lastZxid the last zxid of the follower. * @return the protobuf message. */ public static Message buildAckEpoch(long epoch, Zxid lastZxid) { ZabMessage.Zxid zxid = toProtoZxid(lastZxid); AckEpoch ackEpoch = AckEpoch.newBuilder() .setAcknowledgedEpoch(epoch) .setLastZxid(zxid) .build(); return Message.newBuilder().setType(MessageType.ACK_EPOCH) .setAckEpoch(ackEpoch) .build(); } /** * Creates an INVALID message. This message will not be transmitted among * workers. It's created when protobuf fails to parse the byte buffer. * * @param content the byte array which can't be parsed by protobuf. * @return a message represents an invalid message. */ public static Message buildInvalidMessage(byte[] content) { InvalidMessage msg = InvalidMessage.newBuilder() .setReceivedBytes(ByteString.copyFrom(content)) .build(); return Message.newBuilder().setType(MessageType.INVALID_MESSAGE) .setInvalid(msg) .build(); } /** * Creates a PULL_TXN_REQ message to ask follower sync its history to leader. * * @param lastZxid the last transaction id of leader, the sync starts one * after this transaction (not including this one). * @return a protobuf message. */ public static Message buildPullTxnReq(Zxid lastZxid) { ZabMessage.Zxid zxid = toProtoZxid(lastZxid); PullTxnReq req = PullTxnReq.newBuilder().setLastZxid(zxid) .build(); return Message.newBuilder().setType(MessageType.PULL_TXN_REQ) .setPullTxnReq(req) .build(); } /** * Creates a PROPOSAL message. * * @param txn the transaction of this proposal. * @return a protobuf message. */ public static Message buildProposal(Transaction txn) { ZabMessage.Zxid zxid = toProtoZxid(txn.getZxid()); Proposal prop = Proposal.newBuilder() .setZxid(zxid) .setBody(ByteString.copyFrom(txn.getBody())) .setType(ProposalType.values()[txn.getType()]) .build(); return Message.newBuilder().setType(MessageType.PROPOSAL) .setProposal(prop) .build(); } /** * Creates a PROPOSAL message. * * @param txn the transaction of this proposal. * @param clientId the id of the client who sends the request. * @return a protobuf message. */ public static Message buildProposal(Transaction txn, String clientId) { ZabMessage.Zxid zxid = toProtoZxid(txn.getZxid()); Proposal prop = Proposal.newBuilder() .setZxid(zxid) .setBody(ByteString.copyFrom(txn.getBody())) .setClientId(clientId) .setType(ProposalType.values()[txn.getType()]) .build(); return Message.newBuilder().setType(MessageType.PROPOSAL) .setProposal(prop) .build(); } /** * Creates a DIFF message. * * @param lastZxid the last zxid of the server who initiates the sync. * @return a protobuf message. */ public static Message buildDiff(Zxid lastZxid) { ZabMessage.Zxid lz = toProtoZxid(lastZxid); Diff diff = Diff.newBuilder().setLastZxid(lz).build(); return Message.newBuilder().setType(MessageType.DIFF) .setDiff(diff) .build(); } /** * Creates a TRUNCATE message. * * @param lastPrefixZxid truncate receiver's log from lastPrefixZxid * exclusively. * @param lastZxid the last zxid for the synchronization. * @return a protobuf message. */ public static Message buildTruncate(Zxid lastPrefixZxid, Zxid lastZxid) { ZabMessage.Zxid lpz = toProtoZxid(lastPrefixZxid); ZabMessage.Zxid lz = toProtoZxid(lastZxid); Truncate trunc = Truncate.newBuilder().setLastPrefixZxid(lpz) .setLastZxid(lz) .build(); return Message.newBuilder().setType(MessageType.TRUNCATE) .setTruncate(trunc) .build(); } /** * Creates a NEW_LEADER message. * * @param epoch the established epoch. * @return a protobuf message. */ public static Message buildNewLeader(long epoch) { NewLeader nl = NewLeader.newBuilder().setEpoch(epoch).build(); return Message.newBuilder().setType(MessageType.NEW_LEADER) .setNewLeader(nl) .build(); } /** * Creates a ACK message. * * @param zxid the zxid of the transaction ACK. * @return a protobuf message. */ public static Message buildAck(Zxid zxid) { ZabMessage.Zxid zzxid = toProtoZxid(zxid); Ack ack = Ack.newBuilder().setZxid(zzxid).build(); return Message.newBuilder().setType(MessageType.ACK).setAck(ack).build(); } /** * Creates a SNAPSHOT message. * * @param lastZxid the last zxid of the sender. * @return a protobuf message. */ public static Message buildSnapshot(Zxid lastZxid) { ZabMessage.Zxid zxid = toProtoZxid(lastZxid); Snapshot snapshot = Snapshot.newBuilder().setLastZxid(zxid).build(); return Message.newBuilder().setType(MessageType.SNAPSHOT) .setSnapshot(snapshot) .build(); } /** * Creates a SNAPSHOT message. * * @param lastZxid the last zxid of the sender. * @param snapZxid the last guaranteed applied zxid in snapshot. * @return a protobuf message. */ public static Message buildSnapshot(Zxid lastZxid, Zxid snapZxid) { ZabMessage.Zxid lZxid = toProtoZxid(lastZxid); ZabMessage.Zxid sZxid = toProtoZxid(snapZxid); Snapshot snapshot = Snapshot.newBuilder().setLastZxid(lZxid) .setSnapZxid(sZxid) .build(); return Message.newBuilder().setType(MessageType.SNAPSHOT) .setSnapshot(snapshot) .build(); } /** * Creates a REQUEST message. * * @param request the ByteBuffer represents the request. * @return a protobuf message. */ public static Message buildRequest(ByteBuffer request) { Request req = Request.newBuilder() .setRequest(ByteString.copyFrom(request)) .build(); return Message.newBuilder().setType(MessageType.REQUEST) .setRequest(req) .build(); } /** * Creates a COMMIT message. * * @param zxid the id of the committed transaction. * @return a protobuf message. */ public static Message buildCommit(Zxid zxid) { ZabMessage.Zxid cZxid = toProtoZxid(zxid); Commit commit = Commit.newBuilder().setZxid(cZxid).build(); return Message.newBuilder().setType(MessageType.COMMIT) .setCommit(commit) .build(); } /** * Creates a HANDSHAKE message. * * @param nodeId the node ID in the handshake message. * @return a protobuf message. */ public static Message buildHandshake(String nodeId) { Handshake handshake = Handshake.newBuilder().setNodeId(nodeId).build(); return Message.newBuilder().setType(MessageType.HANDSHAKE) .setHandshake(handshake) .build(); } /** * Creates a HEARTBEAT message. * * @return a protobuf message. */ public static Message buildHeartbeat() { return Message.newBuilder().setType(MessageType.HEARTBEAT).build(); } /** * Creates a DISCONNECTED message. * * @param serverId the ID of the disconnected peer. * @return a protobuf message. */ public static Message buildDisconnected(String serverId) { Disconnected dis = Disconnected.newBuilder() .setServerId(serverId) .build(); return Message.newBuilder().setType(MessageType.DISCONNECTED) .setDisconnected(dis) .build(); } /** * Creates a QUERY_LEADER message. * * @return a protobuf message. */ public static Message buildQueryLeader() { return Message.newBuilder().setType(MessageType.QUERY_LEADER).build(); } /** * Creates a QUERY_REPLY message. * * @param leader the current leader in broadcasting phase. * @return a protobuf message. */ public static Message buildQueryReply(String leader) { QueryReply reply = QueryReply.newBuilder() .setLeader(leader) .build(); return Message.newBuilder().setType(MessageType.QUERY_LEADER_REPLY) .setReply(reply) .build(); } /** * Creates a JOIN message. * * @return a protobuf message. */ public static Message buildJoin(Zxid lastZxid) { ZabMessage.Zxid zxid = toProtoZxid(lastZxid); ZabMessage.Join join = ZabMessage.Join.newBuilder().setLastZxid(zxid).build(); return Message.newBuilder().setType(MessageType.JOIN).setJoin(join).build(); } /** * Creates a ZabMessage.ClusterConfiguration object. * * @param config the ClusterConfiguration object. * @return protobuf ClusterConfiguration object. */ public static ZabMessage.ClusterConfiguration buildConfig(ClusterConfiguration config) { ZabMessage.Zxid version = toProtoZxid(config.getVersion()); return ZabMessage.ClusterConfiguration.newBuilder() .setVersion(version) .addAllServers(config.getPeers()) .build(); } /** * Creates a SYNC_END message. * * @param config the cluster configuration. * @return a protobuf message. */ public static Message buildSyncEnd(ClusterConfiguration config) { ZabMessage.Zxid version = toProtoZxid(config.getVersion()); ZabMessage.ClusterConfiguration zConfig = ZabMessage.ClusterConfiguration.newBuilder() .setVersion(version) .addAllServers(config.getPeers()) .build(); return Message.newBuilder().setType(MessageType.SYNC_END) .setConfig(zConfig) .build(); } /** * Creates a REMOVE message. * * @param serverId the id of server who will be removed from the cluster * configuration. * @return a protobuf message. */ public static Message buildRemove(String serverId) { Remove remove = Remove.newBuilder().setServerId(serverId).build(); return Message.newBuilder().setType(MessageType.REMOVE) .setRemove(remove) .build(); } /** * Creates a SHUT_DOWN message. * * @return a protobuf message. */ public static Message buildShutDown() { return Message.newBuilder().setType(MessageType.SHUT_DOWN).build(); } /** * Creates a DELIVERED message. * * @param deliveredZxid the last zxid of delivered transaction. * @return a protobuf message. */ public static Message buildDelivered(Zxid deliveredZxid) { ZabMessage.Zxid zxid = toProtoZxid(deliveredZxid); ZabMessage.Delivered delivered = ZabMessage.Delivered.newBuilder().setZxid(zxid).build(); return Message.newBuilder().setType(MessageType.DELIVERED) .setDelivered(delivered).build(); } /** * Creates a FILE_HEADER message. * * @param length the lenght of the file. * @return a protobuf message. */ public static Message buildFileHeader(long length) { ZabMessage.FileHeader header = ZabMessage.FileHeader.newBuilder() .setLength(length).build(); return Message.newBuilder().setType(MessageType.FILE_HEADER) .setFileHeader(header).build(); } /** * Creates a FILE_RECEIVED message. * * @param fullPath the path of the received file. * @return a protobuf message. */ public static Message buildFileReceived(String fullPath) { ZabMessage.FileReceived received = ZabMessage.FileReceived .newBuilder() .setFullPath(fullPath) .build(); return Message.newBuilder().setType(MessageType.FILE_RECEIVED) .setFileReceived(received).build(); } /** * Creates a FLUSH_REQ message. * * @param body the data of the request. * @return a protobuf message. */ public static Message buildFlushRequest(ByteBuffer body) { ZabMessage.FlushRequest flushReq = ZabMessage.FlushRequest.newBuilder().setBody(ByteString.copyFrom(body)) .build(); return Message.newBuilder().setType(MessageType.FLUSH_REQ) .setFlushRequest(flushReq) .build(); } /** * Creates a FLUSH message. * * @param lastProposedZxid the last proposed zxid before the FLUSH. * @param body the data of the request. * @return a protobuf message. */ public static Message buildFlush(Zxid lastProposedZxid, ByteBuffer body) { ZabMessage.Zxid zxid = toProtoZxid(lastProposedZxid); ZabMessage.Flush flush = ZabMessage.Flush.newBuilder() .setZxid(zxid) .setBody(ByteString.copyFrom(body)) .build(); return Message.newBuilder().setType(MessageType.FLUSH).setFlush(flush) .build(); } /** * Creates a SYNC_HISTORY message. Leader will synchronize everything it has * to follower after receiving this message. */ public static Message buildSyncHistory(Zxid lastZxid) { ZabMessage.Zxid zxid = toProtoZxid(lastZxid); ZabMessage.SyncHistory sync = ZabMessage.SyncHistory.newBuilder().setLastZxid(zxid).build(); return Message.newBuilder().setType(MessageType.SYNC_HISTORY) .setSyncHistory(sync).build(); } /** * Creates the SYNC_HISTORY_REPLY message. It's the first time to tell the * joiner the synchronization timeout. * * @param timeout timeout in milliseconds. * @return a protobuf message. */ public static Message buildSyncHistoryReply(int timeout) { ZabMessage.SyncHistoryReply reply = ZabMessage.SyncHistoryReply.newBuilder().setSyncTimeout(timeout).build(); return Message.newBuilder().setType(MessageType.SYNC_HISTORY_REPLY) .setSyncHistoryReply(reply).build(); } /** * Creates the ELECTION_INFO message. * * @param vote the server who is selected as the leader. * @param lastZxid the last zxid of the selected leader. * @param round the round number. * @param electing true if current server is in electing phase, false otw. * @return a protobuf message. */ public static Message buildElectionInfo(String vote, Zxid lastZxid, long ackEpoch, long round, boolean electing) { ZabMessage.Zxid zxid = toProtoZxid(lastZxid); ZabMessage.ElectionInfo ei = ZabMessage.ElectionInfo.newBuilder() .setVote(vote) .setZxid(zxid) .setAckEpoch(ackEpoch) .setIsElecting(electing) .setRound(round) .build(); return Message.newBuilder().setType(MessageType.ELECTION_INFO) .setElectionInfo(ei) .build(); } /** * Creates the SNAPSHOT_DONE message. * * @param filePath the file path for the snapshot. * @return a protobuf message. */ public static Message buildSnapshotDone(String filePath) { ZabMessage.SnapshotDone done = ZabMessage.SnapshotDone.newBuilder().setFilePath(filePath).build(); return Message.newBuilder().setType(MessageType.SNAPSHOT_DONE) .setSnapshotDone(done).build(); } }