/* * Copyright 2009 Amit Levy, Jeff Prouty, Rylan Hawkins * * Licensed 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 edu.washington.cs.cse490h.donut.business; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import edu.washington.cs.cse490h.donut.Constants; import edu.washington.cs.cse490h.donut.business.KeyId; import edu.washington.cs.cse490h.donut.business.TNode; import edu.washington.cs.cse490h.donut.util.KeyIdUtil; /** * @author alevy, jprouty */ public class Node { private final TNode tNode; private List<TNode> fingers; private List<TNode> successorList; private TNode predecessor; /** * Create a new Chord ring * * @param tNode * The Thrift Node object that describes the physical topology of the node. */ public Node(TNode tNode) { this.tNode = tNode; this.predecessor = null; initFingers(); initSuccessorList(); } /** * Create a new Chord ring * * @param name * The [host]name of the node (or IP) * @param port * The port on which the node will reside * @param id * The KeyId where this know will live in the Chord ring */ public Node(String name, int port, KeyId id) { this(new TNode(name, port, id)); } /** * Initializes the successor list. */ private void initSuccessorList() { this.successorList = new ArrayList<TNode>(Constants.SUCCESSOR_LIST_SIZE); // Adds the initial successor for (int i = 0; i < Constants.SUCCESSOR_LIST_SIZE; i++) this.successorList.add(tNode); } /** * Initializes the finger table. The successor and all fingers will become this, creating a * complete chord ring. */ private void initFingers() { this.fingers = new ArrayList<TNode>(Constants.KEY_SPACE); for (int i = 0; i < Constants.KEY_SPACE; i++) this.fingers.add(tNode); } /** * Scans this Node's finger table for the closest preceding node to the given key. * * @param entryId * @return the {@link Node} from the finger table that is the closest and preceding the entryId */ public TNode closestPrecedingNode(KeyId entryId) throws IllegalArgumentException { for (int i = fingers.size() - 1; i >= 0; --i) { KeyId currentFinger = getFinger(i).getNodeId(); // (id, finger, us) // (finger, us, id) if (!currentFinger.equals(getNodeId()) && KeyIdUtil.isAfterXButBeforeEqualY(entryId, currentFinger, getNodeId())) { return getFinger(i); } } return getTNode(); } public KeyId getNodeId() { return tNode.getNodeId(); } public String getName() { return tNode.getName(); } public void setPredecessor(TNode predecessor) { this.predecessor = predecessor; } public TNode getPredecessor() { return predecessor; } public TNode getSuccessor() { return successorList.get(0); } /** * Get a finger entry * * @param i * The index to retrieve * @return fingers[i] */ public TNode getFinger(int i) { if (i < 0 && i >= fingers.size()) // Invalid range throw new IndexOutOfBoundsException(); if (i == 0) return getSuccessor(); return fingers.get(i); } /** * finger[i] = n * * @param i * The index to set * @param n * The node to set */ public void setFinger(int i, TNode n) { if (i < 0 || i >= fingers.size()) // Invalid range throw new IndexOutOfBoundsException(); if (i == 0) { setSuccessor(n); return; } fingers.set(i, n); } @Override public boolean equals(Object obj) { if (Node.class.isInstance(obj)) { Node other = (Node) obj; return other.getTNode().equals(this.getTNode()); } return false; } /** * Formats a TNode into a prettier String. We cannot change the toString of TNode because it's * implementation is generated each time with a thrift call. Therefore, this method subverts * their efforts. * * @param n * The node to print * @return Returns a String in the format of hostname:port. If n == null, then returns "NULL". */ public static String TNodeToString(TNode n) { if (n == null) return "NULL"; else return n.getName() + ":" + n.getPort(); } /** * Formats a list of TNodes. Note: This implementation is based off Java 6 AbstractCollection * toString. * * @param l * List of TNodes * @return Produced String */ public static String TNodeListToString(List<TNode> l) { Iterator<TNode> i = l.iterator(); if (!i.hasNext()) // Empty list return "[]"; StringBuilder result = new StringBuilder(); result.append("["); while (true) { TNode e = i.next(); result.append(TNodeToString(e)); if (!i.hasNext()) return result.append(']').toString(); result.append(", "); } } public void updateSuccessorList(List<TNode> list) { int i; for (i = 0; (i < Constants.SUCCESSOR_LIST_SIZE - 1) && (i < list.size()); i++) { try { setSuccessor(i + 1, list.get(i)); } catch (IndexOutOfBoundsException e) { addSuccessor(list.get(i)); } } // Remove any leftover successors i++; while (i < getSuccessorList().size()) { removeSuccessor(i); } } @Override public String toString() { return TNodeToString(this.tNode); } public TNode getTNode() { return tNode; } public int getPort() { return tNode.getPort(); } public void setSuccessor(TNode node) { this.successorList.set(0, node); } public List<TNode> getFingers() { List<TNode> result = new ArrayList<TNode>(fingers); result.set(0, getSuccessor()); return result; } public List<TNode> getSuccessorList() { return new ArrayList<TNode>(successorList); } public void setSuccessor(int i, TNode node) { this.successorList.set(i, node); } public void addSuccessor(TNode node) { this.successorList.add(node); } public void removeSuccessor() { this.successorList.remove(0); } public void removeSuccessor(int i) { this.successorList.remove(i); } }