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

org.jgroups.protocols.raft.NO_DUPES Maven / Gradle / Ivy

There is a newer version: 1.0.13.Final
Show newest version
package org.jgroups.protocols.raft;

import org.jgroups.*;
import org.jgroups.annotations.MBean;
import org.jgroups.conf.ClassConfigurator;
import org.jgroups.protocols.pbcast.GMS;
import org.jgroups.protocols.pbcast.JoinRsp;
import org.jgroups.stack.Protocol;
import org.jgroups.util.ByteArray;
import org.jgroups.util.ExtendedUUID;
import org.jgroups.util.MessageBatch;
import org.jgroups.util.Util;

import java.util.Arrays;
import java.util.Iterator;

/**
 * Intercepts JOIN and MERGE requests on the coordinator and rejects members whose addition would lead to members
 * with duplicate raft-ids in the view.

* Every member's address must be an {@link org.jgroups.util.ExtendedUUID} and have a "raft-id" key whose value * is the raft-id. When intercepting a JOIN request whose sender has a raft-id that's already in the view, we send * back a {@link org.jgroups.protocols.pbcast.JoinRsp} with a rejection message.

* Very similar to {@link org.jgroups.protocols.AUTH}. * @author Bela Ban * @since 0.2 */ @MBean(description="Rejects views with duplicate members (identical raft-ids)") public class NO_DUPES extends Protocol { protected static final short gms_id=ClassConfigurator.getProtocolId(GMS.class); protected volatile View view; public Object down(Event evt) { switch(evt.getType()) { case Event.VIEW_CHANGE: view=evt.getArg(); break; } return down_prot.down(evt); } public Object up(Message msg) { GMS.GmsHeader hdr=msg.getHeader(gms_id); if(hdr != null && !handleGmsHeader(hdr, msg.src())) return null; return up_prot.up(msg); } public void up(MessageBatch batch) { for(Iterator it=batch.iterator(); it.hasNext();) { Message msg=it.next(); GMS.GmsHeader hdr=msg.getHeader(gms_id); if(hdr != null && !handleGmsHeader(hdr, msg.src())) it.remove(); } if(!batch.isEmpty()) up_prot.up(batch); } /** * @return True if the message should be passed up, false if it should be discarded */ protected boolean handleGmsHeader(GMS.GmsHeader hdr, Address sender) { switch(hdr.getType()) { case GMS.GmsHeader.JOIN_REQ: case GMS.GmsHeader.JOIN_REQ_WITH_STATE_TRANSFER: Address joiner=hdr.getMember(); if(!(joiner instanceof ExtendedUUID)) { log.debug("joiner %s needs to have an ExtendedUUID but has a %s", sender, joiner.getClass().getSimpleName()); break; } View v=view; if(contains(v, (ExtendedUUID)joiner)) { String msg=String.format("join of %s rejected as it would create a view with duplicate members (current view: %s)", joiner, v); log.warn(msg); sendJoinRejectedMessageTo(sender, msg); return false; } break; case GMS.GmsHeader.MERGE_REQ: // to be done later when we know how to handle merges in jgroups-raft break; } return true; } protected static boolean contains(View v, ExtendedUUID joiner) { byte[] raft_id=joiner.get(RAFT.raft_id_key); for(Address addr: v) { if(addr instanceof ExtendedUUID) { ExtendedUUID uuid=(ExtendedUUID)addr; byte[] tmp=uuid.get(RAFT.raft_id_key); // compare byte[] buffers to avoid the cost of deserialization if(Arrays.equals(raft_id, tmp)) return true; } } return false; } protected void sendJoinRejectedMessageTo(Address joiner, String reject_message) { try { // needs to be a BytesMessage for now (no ObjectMessage) as GMS itself also uses a BytesMessage; // once GMS has been changed in JGroups itself to use an ObjectMessage, we can change this here, too ByteArray buffer=Util.streamableToBuffer(new JoinRsp(reject_message)); Message msg=new BytesMessage(joiner, buffer).putHeader(gms_id, new GMS.GmsHeader(GMS.GmsHeader.JOIN_RSP)); down_prot.down(msg); } catch(Exception ex) { log.error("failed sending JoinRsp to %s: %s", joiner, ex); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy