/** * Adds the given node reference to the finger table and successor list, if appropriate. The * reference is NOT set as predecessor, even if is closer to this node. Therefore use {@link * #addReferenceAsPredecessor(Node)}. * * @param newReference Reference to be added to the local data structures. * @throws NullPointerException If the given reference is null. */ final synchronized void addReference(Node newReference) { if (newReference == null) { NullPointerException e = new NullPointerException("Node reference to be added must not be null!"); this.logger.error("Null pointer", e); throw e; } boolean debug = this.logger.isEnabledFor(DEBUG); // June 21, 2006. Moved here by sven to avoid failing of checkIfProxy() if (newReference.getNodeID().equals(this.localID)) { if (debug) { this.logger.debug("Reference on myself was not added"); } return; } // check parameters this.checkIfProxy(newReference); this.fingerTable.addReference(newReference); this.successorList.addSuccessor(newReference); if (debug) { this.logger.debug( "Attempted to add reference " + newReference.getNodeID().toString() + " to finger table and successor list. Whether it fit " + "or not depends on those data structures."); } }
/** * Determines if the given reference is contained in any one data structure, ie. finger table, * successor list, or predecessor reference. * * @param newReference Reference to look up. * @throws NullPointerException If given reference is <code>null</code>. * @return <code>true</code> if the reference is contained and <code>false</code> if not. */ final synchronized boolean containsReference(Node newReference) { if (newReference == null) { NullPointerException e = new NullPointerException("Reference to look up must not be null!"); this.logger.error("Null pointer", e); throw e; } return (this.fingerTable.containsReference(newReference) || this.successorList.containsReference(newReference) || newReference.equals(this.predecessor)); }
/** * Adds the given node reference to the finger table and successor list AND sets it as new * predecessor reference, if appropriate. Appropriateness is given if either the former * predecessor reference was <code>null</code> or the new reference's ID is located between the * old predecessor ID and this node's ID. Even if not appropriate as predecessor, the reference is * added to finger table and successor list. * * @param potentialPredecessor Reference which should be this node's predecessor. * @throws NullPointerException If the given reference is <code>null</code>. */ void addReferenceAsPredecessor(Node potentialPredecessor) { this.checkIfProxy(potentialPredecessor); if (potentialPredecessor == null) { NullPointerException e = new NullPointerException("Reference to potential predecessor may not be null!"); this.logger.error("Null pointer", e); throw e; } // if the local node did not have a predecessor reference before // or if the potential predecessor is closer to this local node, // replace the predecessor reference if (this.predecessor == null || potentialPredecessor .getNodeID() .isInInterval(this.predecessor.getNodeID(), this.localID)) { if (this.logger.isEnabledFor(INFO)) { this.logger.info( "Setting a new predecessor reference: New reference is " + potentialPredecessor + ", old reference was " + (this.predecessor == null ? "null" : this.predecessor)); } // replace predecessor reference this.setPredecessor(potentialPredecessor); /* * If a new predecessor, better than the old one has arrived, we * have to copy the entries, that are relevant for this new * predecessor. This has to be done by the predecessor itself, NOT * here. 18.06.2007. sven */ // final Set<Entry> entries = this.entries.getEntriesInInterval( // this.predecessor.nodeID, this.localID); // this.localChord.getAsyncExecutor().execute(new Runnable() { // public void run() { // try { // for (Entry entryToInsert : entries) { // getPredecessor().insertEntry(entryToInsert); // } // } catch (CommunicationException e) { // } // } // }); } // add reference this.addReference(potentialPredecessor); }
/** {@inheritDoc} */ @Override public final void leavesNetwork(Node predecessor) { if (this.logger.isEnabledFor(INFO)) { this.logger.info("Leaves network invoked; " + this.nodeID + ". Updating references."); this.logger.info("New predecessor " + predecessor.getNodeID()); } if (this.logger.isEnabledFor(DEBUG)) { this.logger.debug("References before update: " + this.references.toString()); } this.references.removeReference(this.references.getPredecessor()); if (this.logger.isEnabledFor(DEBUG)) { this.logger.debug("References after update: " + this.references.toString()); } }
/** * Closes the connection to the given reference, if it is not kept in any data structure (ie. * finger table, successor list, predecessor) any more. * * @param removedReference Node to which the connection shall be closed, if there exists no * reference any more. * @throws NullPointerException If given reference is null. */ void disconnectIfUnreferenced(Node removedReference) { if (removedReference == null) { NullPointerException e = new NullPointerException("Reference may not be null!"); this.logger.error("Null pointer", e); throw e; } if (!this.containsReference(removedReference)) { if (!(removedReference instanceof Proxy)) { this.logger.error("Attempt to disconnect unused reference failed"); throw new RuntimeException("Reference should be of type Proxy"); } this.logger.debug("Disconnecting unused reference on node " + removedReference); removedReference.disconnect(); } }
/** * Sets the given reference as this node's predecessor. If the former value of this predecessor's * node was <code>null</code> and if this reference is not used any more (eg. in finger table or * successor list), the connection to it is closed. * * @param potentialPredecessor Reference on the node to be set as new predecessor; may not be null * @throws NullPointerException If potential predecessor is null. */ final synchronized void setPredecessor(Node potentialPredecessor) { if (potentialPredecessor == null) { NullPointerException e = new NullPointerException( "Potential predecessor of method setPredecessor may not be " + "null!"); this.logger.error("Null pointer", e); throw e; } this.checkIfProxy(potentialPredecessor); boolean info = this.logger.isEnabledFor(INFO); if (!(potentialPredecessor.equals(this.predecessor))) { Node formerPredecessor = this.predecessor; this.predecessor = potentialPredecessor; if (formerPredecessor != null) { this.disconnectIfUnreferenced(formerPredecessor); /* * The replicas, which are in the range between the old and the * new predecessor, on the last successor of this node have to * be removed if the successor list is full. => capacity of sl == * length of sl. */ int sLSize = this.successorList.getSize(); if (this.successorList.getCapacity() == sLSize) { Node lastSuccessor = this.successorList.getReferences().get(sLSize - 1); try { lastSuccessor.removeReplicas(this.predecessor.getNodeID(), new HashSet<Entry>()); } catch (CommunicationException e) { logger.warn("Could not remove replicas on last predecessor", e); } } if (this.logger.isEnabledFor(DEBUG)) { this.logger.debug( "Old predecessor " + formerPredecessor + " was replaced by " + potentialPredecessor); } } else { if (info) { this.logger.info( "Predecessor reference set to " + potentialPredecessor + "; was null before."); } Set<Entry> entriesToRep = this.entries.getEntriesInInterval(this.predecessor.getNodeID(), this.localID); List<Node> successors = this.successorList.getReferences(); for (Node successor : successors) { try { successor.insertReplicas(entriesToRep); } catch (CommunicationException e) { this.logger.warn("Damn. Could not replicate to successor " + successor.getNodeID(), e); } } } } }
/** {@inheritDoc} */ @Override public final RefsAndEntries notifyAndCopyEntries(Node potentialPredecessor) throws CommunicationException { /* * Mutual exclusion between notify and notifyAndCopyEntries. 17.03.2008. sven. */ this.notifyLock.lock(); try { // copy all entries which lie between the local node ID and the ID // of // the potential predecessor, including those equal to potential // predecessor Set<Entry> copiedEntries = this.entries.getEntriesInInterval(this.nodeID, potentialPredecessor.getNodeID()); return new RefsAndEntries(this.notify(potentialPredecessor), copiedEntries); } finally { this.notifyLock.unlock(); } }
/** * Removes the given node reference from the finger table and the successor list. If the given * reference is the current predecessor, the predecessor reference will be <code>null</code> * afterwards. * * @param oldReference Reference to remove from ALL data structures. * @throws NullPointerException If reference to remove is <code>null</code>. */ final synchronized void removeReference(Node oldReference) { if (oldReference == null) { NullPointerException e = new NullPointerException("Reference to remove must not be null!"); this.logger.error("Null pointer", e); throw e; } this.fingerTable.removeReference(oldReference); this.successorList.removeReference(oldReference); if (oldReference.equals(this.getPredecessor())) { this.predecessor = null; } disconnectIfUnreferenced(oldReference); if (this.logger.isEnabledFor(DEBUG)) { this.logger.debug( "Attempted to remove reference " + oldReference + " from all data structures including predecessor reference."); } }
/** * Determines the closest preceding node for the given ID based on finger table, successor list, * and predecessor, but without testing the node's liveliness. * * @param key ID to find closest preceding node for. * @throws NullPointerException If ID is <code>null</code>. * @return Reference on closest preceding node. */ final synchronized Node getClosestPrecedingNode(ID key) { if (key == null) { NullPointerException e = new NullPointerException("ID may not be null!"); this.logger.error("Null pointer", e); throw e; } Map<ID, Node> foundNodes = new HashMap<ID, Node>(); // determine closest preceding reference of finger table Node closestNodeFT = this.fingerTable.getClosestPrecedingNode(key); if (closestNodeFT != null) { foundNodes.put(closestNodeFT.getNodeID(), closestNodeFT); } // determine closest preceding reference of successor list Node closestNodeSL = this.successorList.getClosestPrecedingNode(key); if (closestNodeSL != null) { foundNodes.put(closestNodeSL.getNodeID(), closestNodeSL); } // predecessor is appropriate only if it precedes the given id Node predecessorIfAppropriate = null; if (this.predecessor != null && key.isInInterval(this.predecessor.getNodeID(), this.localID)) { predecessorIfAppropriate = this.predecessor; foundNodes.put(this.predecessor.getNodeID(), predecessor); } // with three references which may be null, there are eight (8) cases we // have to enumerate... Node closestNode = null; List<ID> orderedIDList = new ArrayList<ID>(foundNodes.keySet()); orderedIDList.add(key); int sizeOfList = orderedIDList.size(); // size of list must be greater than one to not only contain the key. // if (sizeOfList > 1) { /* * Sort list in ascending order */ Collections.sort(orderedIDList); /* * The list item with one index lower than that of the key must be the * id of the closest predecessor or the key. */ int keyIndex = orderedIDList.indexOf(key); /* * As all ids are located on a ring if the key is the first item in the * list we have to select the last item as predecessor with help of this * calculation. */ int index = (sizeOfList + (keyIndex - 1)) % sizeOfList; /* * Get the references to the node from the map of collected nodes. */ ID idOfclosestNode = orderedIDList.get(index); closestNode = foundNodes.get(idOfclosestNode); if (closestNode == null) { throw new NullPointerException("closestNode must not be null!"); } /* * Following code is too complicated. */ // if (closestNodeFT == null) { // if (closestNodeSL == null) { // if (predecessorIfAppropriate == null) { // // no reference is appropriate // closestNode = null; // } else { // // only predecessor is appropriate (case should not occur, // // but anyway... // closestNode = predecessorIfAppropriate; // } // } else { // if (predecessorIfAppropriate == null) { // // only reference of successor list is appropriate // closestNode = closestNodeSL; // } else { // // either predecessor or reference of successor list is // // appropriate; determine one of both // if (predecessorIfAppropriate.nodeID.isInInterval( // closestNodeSL.nodeID, key)) { // closestNode = predecessorIfAppropriate; // } else { // closestNode = closestNodeSL; // } // } // } // } else { // if (closestNodeSL == null) { // if (predecessorIfAppropriate == null) { // // only reference of finger table is appropriate // closestNode = closestNodeFT; // } else { // // either predecessor or reference of finger table is // // appropriate; determine one of both // if (predecessorIfAppropriate.nodeID.isInInterval( // closestNodeFT.nodeID, key)) { // closestNode = predecessorIfAppropriate; // } else { // closestNode = closestNodeFT; // } // } // } else { // if (predecessorIfAppropriate == null) { // // either reference of successor list or reference of finger // // table is appropriate; determine one of both // if (closestNodeSL.nodeID.isInInterval(closestNodeFT.nodeID, // key)) { // closestNode = closestNodeSL; // } else { // closestNode = closestNodeFT; // } // } else { // // either of the three reference is appropriate; determine // // first one of the references of successor list and finger // // table is more appropriate and afterwards compare with // // predecessor // if (closestNodeSL.nodeID.isInInterval(closestNodeFT.nodeID, // key)) { // if (predecessorIfAppropriate.nodeID.isInInterval( // closestNodeSL.nodeID, key)) { // closestNode = predecessorIfAppropriate; // } else { // closestNode = closestNodeSL; // } // } else { // if (predecessorIfAppropriate.nodeID.isInInterval( // closestNodeFT.nodeID, key)) { // closestNode = predecessorIfAppropriate; // } else { // closestNode = closestNodeFT; // } // } // } // } // } if (this.logger.isEnabledFor(DEBUG)) { this.logger.debug( "Closest preceding node of ID " + key + " at node " + this.localID.toString() + " is " + closestNode.getNodeID() + " with closestNodeFT=" + (closestNodeFT == null ? "null" : "" + closestNodeFT.getNodeID()) + " and closestNodeSL=" + (closestNodeSL == null ? "null" : "" + closestNodeSL.getNodeID()) + " and predecessor (only if it precedes given ID)=" + (predecessorIfAppropriate == null ? "null" : "" + predecessorIfAppropriate.getNodeID())); } return closestNode; }
// TODO: implement this function in TTP @Override public final void broadcast(Broadcast info) throws CommunicationException { if (this.logger.isEnabledFor(DEBUG)) { this.logger.debug(" Send broadcast message"); } // System.out.println("NodeImpl: Send broadcast: " + info); // Check transaction Number, only accept ascending Numbers if (info.getTransaction() > this.getTransactionCount()) { System.out.println( "NodeImpl: Updating transaction Number from " + this.getTransactionCount() + " to " + info.getTransaction()); this.setTransactionCount(info.getTransaction()); } else { System.out.println("NodeImpl: Ignoring old transaction ID " + info.getTransaction()); System.out.println("################################################################"); return; } // Get and sort known nodes List<Node> fingerTable = references.getFingerTableEntries(); // make IDs in fingerTable unique HashSet<Node> fingerSet = new HashSet<Node>(fingerTable); // but list is better for iteration (we'll frequently need next's next) List<Node> knownNodes = new ArrayList<Node>(fingerSet); Collections.sort(knownNodes); // Iterate over nodes for (int i = 0; i < knownNodes.size(); ++i) { Node n = knownNodes.get(i); // Only broadcast to nodes between own ID and range if (!n.getNodeID().isInInterval(this.getNodeID(), info.getRange())) { System.out.println("NodeImpl: Not in range, not sending broadcast"); break; } // find successor of Node n (stays if we're at the last node) ID successorID = info.getRange(); if (knownNodes.size() > i + 1) { successorID = knownNodes.get(i + 1).getNodeID(); } // Create broadcast object and send Broadcast bc = new Broadcast( successorID, this.getNodeID(), info.getTarget(), info.getTransaction(), info.getHit()); // System.out.println("NodeImpl: Broadcasting: " + bc); n.broadcast(bc); } // finally inform application if (this.notifyCallback != null) { this.notifyCallback.broadcast(info.getSource(), info.getTarget(), info.getHit()); } System.out.println("################################################################"); }