![JAR search and dependency download from the Maven repository](/logo.png)
co.edu.uniquindio.chord.node.ChordNode Maven / Gradle / Ivy
Show all versions of jdhtuq-chord Show documentation
/*
* Chord project implement of lookup algorithm Chord
* Copyright (C) 2010 - 2018 Daniel Pelaez
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
*/
package co.edu.uniquindio.chord.node;
import co.edu.uniquindio.chord.Chord;
import co.edu.uniquindio.chord.ChordKey;
import co.edu.uniquindio.chord.protocol.Protocol;
import co.edu.uniquindio.chord.protocol.Protocol.LookupResponseParams;
import co.edu.uniquindio.overlay.Key;
import co.edu.uniquindio.overlay.KeyFactory;
import co.edu.uniquindio.utils.communication.message.Address;
import co.edu.uniquindio.utils.communication.message.Message;
import co.edu.uniquindio.utils.communication.message.SequenceGenerator;
import co.edu.uniquindio.utils.communication.transfer.CommunicationManager;
import org.apache.log4j.Logger;
import java.util.Observable;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture;
/**
* The ChordNode
class represents a node in the Chord network. It
* implements the peer-to-peer algorithm described in paper Chord: A
* Scalable Peer-to-peer Lookup Protocol for Internet Applications
.
*
* A Chord node is initialized from the method boot
of the class
* {@link BootStrap}. The node is added to the network when you know who is your
* successor and is stable when it knows who is its successor and its
* predecessor.
*
* @author Daniel Pelaez
* @author Hector Hurtado
* @author Daniel Lopez
* @version 1.0, 17/06/2010
* @see Key
* @see FingersTable
* @see SuccessorList
* @see BootStrap
* @since 1.0
*/
public class ChordNode extends Observable implements Chord {
/**
* Logger
*/
private static final Logger logger = Logger
.getLogger(ChordNode.class);
/**
* Communication manager
*/
private CommunicationManager communicationManager;
/**
* The next node on the identifier circle
*/
private ChordKey successor;
/**
* The previous node on the identifier circle
*/
private ChordKey predecessor;
/**
* Routing table
*/
private FingersTable fingersTable;
/**
* Successor list
*/
private SuccessorList successorList;
/**
* Identifier of the node
*/
private ChordKey key;
private BootStrap bootStrap;
private KeyFactory keyFactory;
private final SequenceGenerator sequenceGenerator;
private ScheduledFuture> stableRing;
ChordNode(ChordKey key, CommunicationManager communicationManager, int successorListAmount, BootStrap bootStrap, KeyFactory keyFactory, SequenceGenerator sequenceGenerator) {
this.keyFactory = keyFactory;
this.sequenceGenerator = sequenceGenerator;
this.fingersTable = newFingersTable();
this.successorList = new SuccessorList(this, communicationManager, successorListAmount, keyFactory, sequenceGenerator);
this.key = key;
this.communicationManager = communicationManager;
this.bootStrap = bootStrap;
logger.info("New ChordNode created = " + key);
}
ChordNode(CommunicationManager communicationManager, ChordKey successor, ChordKey predecessor, FingersTable fingersTable, SuccessorList successorList, ChordKey key, SequenceGenerator sequenceGenerator, ScheduledFuture> stableRing) {
this.communicationManager = communicationManager;
this.successor = successor;
this.predecessor = predecessor;
this.fingersTable = fingersTable;
this.successorList = successorList;
this.key = key;
this.sequenceGenerator = sequenceGenerator;
this.stableRing = stableRing;
}
/**
* Makes a look up of the given key.
*
* @param id The key which will be found.
* @return The key which is responsible for the given id
.
*/
public Key lookUp(Key id) {
return findSuccessor((ChordKey) id, LookupType.LOOKUP);
}
/**
* Find successor for id
.
*
* If id
falls between n
and its successor,
* findSuccessor
is finished and node n
returns
* its successor. Otherwise, n
searches its finger table for
* the node next
whose ID most immediately precedes
* id
, and then invokes findSuccessor
at
* next
. The reason behind this choice of next
is
* that the closer next
is to id
.
*
* @param id Identifier searched.
* @return successor of the given id
.
*/
public ChordKey findSuccessor(ChordKey id, LookupType typeLookUp) {
Key next;
Message lookupMessage;
if (successor != null && id.isBetweenRightIncluded(key, successor)) {
return successor;
} else {
next = fingersTable.findClosestPresedingNode(id);
lookupMessage = Message.builder()
.sequenceNumber(sequenceGenerator.getSequenceNumber())
.sendType(Message.SendType.REQUEST)
.messageType(Protocol.LOOKUP)
.address(Address.builder()
.destination(next.getValue())
.source(key.getValue())
.build())
.param(Protocol.LookupParams.HASHING.name(), id.getHashing().toString())
.param(Protocol.LookupParams.TYPE.name(), typeLookUp.name())
.build();
return communicationManager.sendMessageUnicast(lookupMessage,
ChordKey.class, LookupResponseParams.NODE_FIND.name());
}
}
/**
* Create a new Chord ring.
*/
public void createRing() {
predecessor = null;
successor = key;
successorList.initializeSuccessors();
}
/**
* Join a Chord ring using node node
.
*
* The join
function asks node
to find the
* immediate successor of n
.
*
* @param node Identifier of the known node.
*/
public void join(Key node) {
Message lookupMessage;
predecessor = null;
lookupMessage = Message.builder()
.sequenceNumber(sequenceGenerator.getSequenceNumber())
.sendType(Message.SendType.REQUEST)
.messageType(Protocol.LOOKUP)
.address(Address.builder()
.destination(node.getValue())
.source(key.getValue())
.build())
.param(Protocol.LookupParams.HASHING.name(), key.getHashing().toString())
.param(Protocol.LookupParams.TYPE.name(), LookupType.JOIN.name())
.build();
successor = communicationManager.sendMessageUnicast(lookupMessage,
ChordKey.class, LookupResponseParams.NODE_FIND.name());
successorList.initializeSuccessors();
}
/**
* Accept a new predecessor. Also notifies through by ChordNode.observable
* the change predecessor
*
* @param node Identifier of potential predecessor.
*/
public void notify(ChordKey node) {
if (predecessor == null || node.isBetween(predecessor, key)) {
/*
* Have to validate if the given key is equal of node's key, then
* the node must not have a predecessor
*/
if (node.equals(key)) {
predecessor = null;
return;
}
predecessor = node;
Message message = Message.builder()
.messageType(Protocol.RE_ASSIGN)
.param(Protocol.ReAssignParams.PREDECESSOR.name(), predecessor.getValue())
.build();
setChanged();
notifyObservers(message);
clearChanged();
logger.debug("Node: '" + key.getValue()
+ "' Predecessor changed for '" + Optional.ofNullable(predecessor).map(p -> p.getValue()).orElse(null));
}
logger.info("Notify to node '" + key.getValue() + "', predecessor is '"
+ Optional.ofNullable(predecessor).map(p -> p.getValue()).orElse(null) + "'");
}
/**
* Called periodically in {@link StableRing }.
*
* Clear the node's predecessor pointer if predecessor
has
* failed.
*/
public void checkPredecessor() {
if (predecessor == null) {
return;
}
Message pingMessage;
pingMessage = Message.builder()
.sequenceNumber(sequenceGenerator.getSequenceNumber())
.sendType(Message.SendType.REQUEST)
.messageType(Protocol.PING)
.address(Address.builder()
.destination(predecessor.getValue())
.source(key.getValue())
.build())
.build();
Boolean success = communicationManager.sendMessageUnicast(pingMessage,
Boolean.class);
if (success == null) {
predecessor = null;
}
}
/**
* Called periodically in {@link StableRing }.
*
* Each time node n
runs stabilize
, it asks its
* successor for the successor�s predecessor x
, and decides
* whether x
should be n�s successor instead. This would be the
* case if node x
recently joined the system. In addition,
* stabilize
notifies node n�s successor of n�s existence,
* giving the successor the chance to change its predecessor to n. The
* successor does this only if it knows of no closer predecessor than n.
*/
public void stabilize() {
ChordKey x;
Boolean success;
Message pingMessage;
Message getPredecessorMessage;
Message notifyMessage;
pingMessage = Message.builder()
.sequenceNumber(sequenceGenerator.getSequenceNumber())
.sendType(Message.SendType.REQUEST)
.messageType(Protocol.PING)
.address(Address.builder()
.destination(successor.getValue())
.source(key.getValue())
.build())
.build();
success = communicationManager.sendMessageUnicast(pingMessage,
Boolean.class);
if (success == null) {
/* When node's successor fails, then node must find a new successor */
ChordKey successorNew = successorList.getNextSuccessorAvailable();
logger.error("Node: " + key.getValue() + ", successor failed");
if (successorNew != null) {
successor = successorNew;
fingersTable.setSuccessor(successor);
successorList.setSuccessor(successor);
} else {
/*
* If all successors fail, the the node must boot again, that
* is, find a node to join the network.
*/
logger.error("Node: " + key.getValue()
+ ", successor list failed... new bootstrap");
fingersTable = newFingersTable();
bootUp();
}
} else {
/*
* Stabilizes by finding a new successor with successor's
* predecessor
*/
getPredecessorMessage = Message.builder()
.sequenceNumber(sequenceGenerator.getSequenceNumber())
.sendType(Message.SendType.REQUEST)
.messageType(Protocol.GET_PREDECESSOR)
.address(Address.builder()
.destination(successor.getValue())
.source(key.getValue())
.build())
.build();
x = communicationManager.sendMessageUnicast(getPredecessorMessage,
ChordKey.class);
if (x != null) {
if (x.isBetween(key, successor) || key.equals(successor)) {
successor = x;
fingersTable.setSuccessor(successor);
successorList.setSuccessor(successor);
}
}
logger.info("Node '" + key.getValue()
+ "' stabilized, its succesor is '" + successor.getValue()
+ "'");
logger.debug("Node '" + key.getValue() + "' Succesor list '"
+ successorList + "'");
notifyMessage = Message.builder()
.sequenceNumber(sequenceGenerator.getSequenceNumber())
.sendType(Message.SendType.REQUEST)
.messageType(Protocol.NOTIFY)
.address(Address.builder()
.destination(successor.getValue())
.source(key.getValue())
.build())
.build();
communicationManager.sendMessageUnicast(notifyMessage);
}
}
void bootUp() {
bootStrap.boot(this, communicationManager, sequenceGenerator);
}
FingersTable newFingersTable() {
return new FingersTable(this, keyFactory);
}
/**
* Gets node's table of fingers.
*
* @return {@link FingersTable}
*/
public FingersTable getFingersTable() {
return fingersTable;
}
/**
* Gets node's key.
*
* @return {@link Key}
*/
public ChordKey getKey() {
return key;
}
/**
* Gets node's successor.
*
* @return {@link Key}
*/
public ChordKey getSuccessor() {
return successor;
}
/**
* Sets node's successor in the node and its fingersTable and succesorList.
*
* @param successor
*/
public void setSuccessor(ChordKey successor) {
this.successor = successor;
this.fingersTable.setSuccessor(successor);
this.successorList.setSuccessor(successor);
}
/**
* Sets the predecessor of the node.
*
* @param predecessor
*/
public void setPredecessor(ChordKey predecessor) {
/*
* Have to validate if the given key is equal of node's key, then the
* node must not have a predecessor
*/
if (predecessor.equals(key)) {
this.predecessor = null;
} else {
this.predecessor = predecessor;
}
}
/**
* Gets the predecessor of the node.
*
* @return {@link Key} the key of the predecessor
*/
public ChordKey getPredecessor() {
return predecessor;
}
/**
* Gets the observable of the node.
*
* @return Observable
*/
public Observable getObservable() {
return this;
}
/**
* Gets the list of successors of the node.
*
* @return {@link SuccessorList}
*/
public SuccessorList getSuccessorList() {
return successorList;
}
/**
* Indicates whether some other object is "equal to" this one.
*
* @param object the reference object with which to compare.
* @return true
if this object is the same as the object.
*/
@Override
public boolean equals(Object object) {
if (object == null) {
return false;
}
if (object instanceof ChordNode) {
ChordNode nodeChord = (ChordNode) object;
return nodeChord.getKey().equals(key);
}
return false;
}
/**
* Takes the node out of the network by sending a message of leave.
*
* @return ChordKey[] An array with node's successors
*/
@Override
public ChordKey[] leave() {
Message setSuccessorMessage;
Message setPredecessorMessage;
/* Ends all stable threads */
stableRing.cancel(true);
if (!successor.equals(key) && predecessor != null) {
setSuccessorMessage = Message.builder()
.sequenceNumber(sequenceGenerator.getSequenceNumber())
.sendType(Message.SendType.REQUEST)
.messageType(Protocol.SET_SUCCESSOR)
.address(Address.builder()
.destination(predecessor.getValue())
.source(key.getValue())
.build())
.param(Protocol.SetSuccessorParams.SUCCESSOR.name(), successor.getValue())
.build();
communicationManager.sendMessageUnicast(setSuccessorMessage);
setPredecessorMessage = Message.builder()
.sequenceNumber(sequenceGenerator.getSequenceNumber())
.sendType(Message.SendType.REQUEST)
.messageType(Protocol.SET_PREDECESSOR)
.address(Address.builder()
.destination(successor.getValue())
.source(key.getValue())
.build())
.param(Protocol.SetPredecessorParams.PREDECESSOR.name(), predecessor.toString())
.build();
communicationManager.sendMessageUnicast(setPredecessorMessage);
}
communicationManager.removeMessageProcessor(key.getValue());
return successorList.getKeyList();
}
/**
* Gets node's successor list.
*
* @return ChordKey[] An array with node's successors
*/
public ChordKey[] getNeighborsList() {
return successorList.getKeyList();
}
void setStableRing(ScheduledFuture> stableRing) {
this.stableRing = stableRing;
}
public void stopStabilizing() {
stableRing.cancel(true);
}
}