package ch.usi.da.paxos.ring; /* * Copyright (c) 2015 Università della Svizzera italiana (USI) * * This file is part of URingPaxos. * * URingPaxos is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * URingPaxos 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with URingPaxos. If not, see <http://www.gnu.org/licenses/>. */ import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import org.apache.log4j.Logger; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooKeeper; import ch.usi.da.paxos.Util; import ch.usi.da.paxos.api.Learner; import ch.usi.da.paxos.api.PaxosNode; import ch.usi.da.paxos.api.PaxosRole; import ch.usi.da.paxos.api.Proposer; import ch.usi.da.paxos.lab.DummyWatcher; /** * Name: Node<br> * Description: <br> * * Creation date: Aug 12, 2012<br> * $Id$ * * @author Samuel Benz benz@geoid.ch */ public class Node implements PaxosNode { private final Logger logger; private final int nodeID; private final int groupID; private final InetAddress ip; private final List<ZooKeeper> zoos = new ArrayList<ZooKeeper>(); // hold refs to close private final String zoo_host; private final List<RingDescription> rings; private boolean running = false; private final Map<Integer, Proposer> ringProposer = new HashMap<Integer, Proposer>(); private Learner learner = null; /** * @param nodeID * @param zoo_host * @param rings */ public Node(int nodeID,String zoo_host,List<RingDescription> rings) { this(nodeID,-1,zoo_host,rings); } /** * @param nodeID * @param groupID * @param zoo_host * @param rings */ public Node(int nodeID,int groupID,String zoo_host,List<RingDescription> rings) { this.logger = Logger.getLogger(Node.class); this.nodeID = nodeID; this.groupID = groupID; this.zoo_host = zoo_host; this.rings = rings; this.ip = Util.getHostAddress(); } public void start() throws IOException, KeeperException, InterruptedException { try { int pid = Integer.parseInt((new File("/proc/self")).getCanonicalFile().getName()); logger.info("PID: " + pid); } catch (NumberFormatException | IOException e) { } for(RingDescription ring : rings){ update(ring); } if(isMultiLearner(rings)){ logger.error("MultiRingLearner will be replaced by ElasticLearner!"); @SuppressWarnings("deprecation") MultiLearnerRole mr = new MultiLearnerRole(rings); learner = mr; Thread t = new Thread(mr); t.setName("MultiRingLearner"); t.start(); } running = true; } private void update(RingDescription ring) throws IOException, KeeperException, InterruptedException { // ring socket port Random rand = new Random(); int port = 2000 + rand.nextInt(1000); // assign port between 2000-3000 InetSocketAddress addr = new InetSocketAddress(ip,port); // create ring manager ZooKeeper zoo = new ZooKeeper(zoo_host,3000,new DummyWatcher()); zoos.add(zoo); RingManager rm = new RingManager(this,ring.getRingID(),addr,zoo,"/ringpaxos"); ring.setRingManager(rm); rm.init(); // register and start roles for(PaxosRole role : ring.getRoles()){ if(role.equals(PaxosRole.Proposer)){ ProposerRole r = new ProposerRole(rm); logger.debug("Node register role: " + role + " at node " + nodeID + " in ring " + ring.getRingID()); rm.registerRole(role); ringProposer.put(ring.getRingID(), r); Thread t = new Thread(r); t.setName(role.toString()); t.start(); }else if(role.equals(PaxosRole.Acceptor)){ Role r = new AcceptorRole(rm); logger.debug("Node register role: " + role + " at node " + nodeID + " in ring " + ring.getRingID()); rm.registerRole(role); Thread t = new Thread(r); t.setName(role.toString()); t.start(); }else if(role.equals(PaxosRole.Learner) && learner == null && !isMultiLearner(rings)){ ElasticLearnerRole r = new ElasticLearnerRole(ring); logger.debug("Node register role: " + role + " at node " + nodeID + " in ring " + ring.getRingID()); learner = r; Thread t = new Thread(r); t.setName("ElasticLearner"); t.start(); } } } public void stop() throws InterruptedException{ for(RingDescription r : rings){ RingManager ring = r.getRingManager(); if(ring.getNetwork().getAcceptor() != null){ ((AcceptorRole)ring.getNetwork().getAcceptor()).getStableStorage().close(); } ring.getNetwork().disconnectClient(); ring.getNetwork().closeServer(); } for(ZooKeeper zoo : zoos){ zoo.close(); } running = false; } private boolean isMultiLearner(List<RingDescription> rings) { int learner_count = 0; for(RingDescription ring : rings){ if(ring.getRoles().contains(PaxosRole.Learner)){ learner_count++; } } return learner_count > 1 ? true : false; } @Override public int getNodeID(){ return nodeID; } @Override public int getGroupID(){ return groupID; } @Override public InetAddress getInetAddress(){ return ip; } @Override public List<RingDescription> getRings(){ return rings; } @Override public Learner getLearner() { if (!running) { throw new RuntimeException("Paxos node is not running. Call node.start()"); } return learner; } @Override public Proposer getProposer(int ringID) { if (!running) { throw new RuntimeException("Paxos node is not running. Call node.start()"); } return ringProposer.get(ringID); } @Override public boolean updateRing(RingDescription ring) { try { for(RingDescription r : rings){ if(r.getRingID() == ring.getRingID()){ r.getRoles().addAll(ring.getRoles()); ring.setRingManager(r.getRingManager()); ring.getRoles().clear(); ring.getRoles().addAll(r.getRoles()); return true; } } update(ring); rings.add(ring); return true; } catch (Exception e){ logger.debug("Error updating ring " + ring); } return false; } }