/** * SAMOA - PROTOCOL FRAMEWORK * Copyright (C) 2005 Olivier Rütti (EPFL) (olivier.rutti@a3.epfl.ch) * * This program 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 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package seqSamoa.protocols.rpt2pt; import java.io.IOException; import java.io.OutputStream; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import seqSamoa.AtomicTask; import seqSamoa.Message; import seqSamoa.ProtocolModule; import seqSamoa.ProtocolStack; import seqSamoa.ServiceCallOrResponse; import seqSamoa.exceptions.AlreadyExistingProtocolModuleException; import seqSamoa.exceptions.AlreadyExistingServiceException; import seqSamoa.services.monitoring.ProcessSuspicion; import seqSamoa.services.monitoring.ProcessSuspicionCallParameters; import seqSamoa.services.network.Network; import seqSamoa.services.network.NetworkResponseParameters; import seqSamoa.services.rpt2pt.RPT2PT; import seqSamoa.services.rpt2pt.RPT2PTCallParameters; import seqSamoa.services.rpt2pt.RPT2PTResponseParameters; import uka.transport.Transportable; import framework.Constants; import framework.GroupCommEventArgs; import framework.GroupCommException; import framework.GroupCommMessage; import framework.PID; import framework.libraries.DefaultSerialization; import framework.libraries.Serialize; import framework.libraries.Timer; import framework.libraries.Trigger; import framework.libraries.serialization.TByteArray; import framework.libraries.serialization.THashSet; import framework.libraries.serialization.TInteger; import framework.libraries.serialization.TSet; import framework.libraries.tcp.Connection; import framework.libraries.tcp.NonBlockingTCP; import framework.libraries.tcp.Server; import framework.libraries.tcp.TCPStackInterface; import groupcomm.common.rpt2pt.ReliablePt2Pt; /** * This class implement a Protocol that allows Reliable Point to Point * communication * * The service implemented is rpt2pt (described in util/Services.java) */ public class ProtocolRPT2PT extends ProtocolModule implements Trigger, NonBlockingTCP, TCPStackInterface, Serialize, Timer { final private static int MAX_PROCESSES = 7; final private static int MAX_MESSAGES = 45; // Service Provided private RPT2PT rpt2pt; private ServiceCallOrResponse rpt2ptCOR; // Service required private ProcessSuspicion processSuspicion; // Internal Service for connections private Network net; // Object that describe behaviour of the Handlers protected ReliablePt2Pt handlers; // Timers scheduled Map<Transportable, AtomicTask> timers = null; // TCP Suspicion parameters int fc_threshold; int to_suspect; int to_retry_connect; // Is the Protocol Closed boolean layerClosed; // The Executers // It send reliably a message protected RPT2PT.Executer rpt2ptExecuter; // The listener // It processs the events from the connections protected Network.Listener netListener; /** * Constructor. <br> * * @param name * Name of the layer * @param stack * The stack in which the module will be * @param fc_threshold * The max number of messages treated by TCP * @param to_suspect * The time before suspecting TCP connection to be broken * @param to_retry_connect * The time before trying to reconnect */ public ProtocolRPT2PT(String name, ProtocolStack stack, int fc_threshold, int to_suspect, int to_retry_connect, RPT2PT rpt2pt, ProcessSuspicion processSuspicion) throws AlreadyExistingProtocolModuleException, AlreadyExistingServiceException { super(name, stack); this.rpt2pt = rpt2pt; this.rpt2ptCOR = ServiceCallOrResponse.createServiceCallOrResponse(rpt2pt, true); this.processSuspicion = processSuspicion; this.net = new Network(name+"-Net", stack); // Init the fields handlers = new ReliablePt2Pt(this, stack.getFlowControl(), this, this, this, stack.getPID()); this.fc_threshold = fc_threshold; this.to_suspect = to_suspect; this.to_retry_connect = to_retry_connect; this.layerClosed = false; timers = new HashMap<Transportable, AtomicTask>(); LinkedList<ServiceCallOrResponse> initiatedRpt2pt = new LinkedList<ServiceCallOrResponse>(); rpt2ptExecuter = rpt2pt.new Executer(this, initiatedRpt2pt) { public void evaluate(RPT2PTCallParameters params, Message dmessage) { synchronized (ProtocolRPT2PT.this) { GroupCommEventArgs ga = new GroupCommEventArgs(); if (params.send.booleanValue()) { ga.addLast(dmessage.toGroupCommMessage()); ga.addLast(params.pid); ga.addLast(params.added); try { handlers.handlePt2PtSend(ga); } catch (GroupCommException ex) { ex.printStackTrace(); throw new RuntimeException( "ProtocolRPT2PT: handlePt2PtSend: " + ex.getMessage()); } } else { THashSet singleton = new THashSet(); singleton.add(params.pid); if (params.added.booleanValue()) { ga.addLast(singleton); ga.addLast(new THashSet()); } else { ga.addLast(new THashSet()); ga.addLast(singleton); } try { handlers.handleJoinRemoveList(ga); } catch (GroupCommException ex) { throw new RuntimeException( "ProtocolRPT2PT: handleJoinRemoveList: " + ex.getMessage()); } } } } }; LinkedList<ServiceCallOrResponse> initiatedNet = new LinkedList<ServiceCallOrResponse>(); for (int i=0; i<(MAX_PROCESSES*MAX_MESSAGES); i++) initiatedNet.add(ServiceCallOrResponse.createServiceCallOrResponse(rpt2pt, false)); netListener = net.new Listener(this, initiatedNet) { public void evaluate(NetworkResponseParameters params, Transportable message) { synchronized (ProtocolRPT2PT.this) { final GroupCommEventArgs m = new GroupCommEventArgs(); try { switch (params.code) { case 1: // Accept m.addLast(params.connection); handlers.handleAccepted(m); break; case 2: // Connected m.addLast(params.pid); m.addLast(params.connection); handlers.handleConnected(m); break; case 3: // Closed m.addLast(params.connection); handlers.handleClosed(m); break; case 4: // Broken m.addLast(params.connection); handlers.handleBroken(m); break; case 5: // Recv m.addLast(message); m.addLast(params.connection); if (!layerClosed) handlers.handleRecv(m); break; case 6: // ReadyForNextMessage m.addLast(params.connection); handlers.handleReadyForNextMessage(m); break; default: } } catch (GroupCommException ex) { throw new RuntimeException( "ProtocolRPT2PT: net: " + ex.getMessage()); } } } }; } public void dump(OutputStream stream) { handlers.handleDump(stream); } public void init() { GroupCommEventArgs ga = new GroupCommEventArgs(); ga.addLast(new TInteger(fc_threshold)); ga.addLast(new TInteger(to_suspect)); ga.addLast(new TInteger(to_retry_connect)); try { handlers.handleInit(ga); } catch (GroupCommException ex) { throw new RuntimeException("ProtocolRPT2PT: init: " + ex.getMessage()); } super.init(); } public void close() { layerClosed = true; super.close(); } /** * Manage the triggering of the events */ public void trigger(int type, GroupCommEventArgs l) { switch (type) { case Constants.PT2PTDELIVER: Message message = new Message((GroupCommMessage) l.remove(0)); RPT2PTResponseParameters infos = new RPT2PTResponseParameters( (PID) l.remove(0)); if (!layerClosed) rpt2pt.response(infos, message); break; case Constants.SUSPECT2: TSet suspected = (TSet) l.remove(0); ProcessSuspicionCallParameters p = new ProcessSuspicionCallParameters( suspected); if (!layerClosed) processSuspicion.externalCall(p, null); break; default: throw new RuntimeException("ProtocolRPT2PT: trigger: " + "Unexpected event type"); } } // Interface for the timers public void schedule(final Transportable key, boolean periodic, int time) { AtomicTask trigger = new AtomicTask() { public void execute() { synchronized (ProtocolRPT2PT.this) { if (!timers.containsKey(key)) // Timer already canceled return; GroupCommEventArgs ga = new GroupCommEventArgs(); ga.addLast(key); try { handlers.handleTimeout(ga); } catch (GroupCommException gce) { throw new RuntimeException("ProtocolRP2PT: Timeout: " + gce.getMessage()); } } } public ServiceCallOrResponse getCOR() { return ProtocolRPT2PT.this.rpt2ptCOR; } }; timers.put(key, trigger); stack.getScheduler().schedule(trigger, periodic, time); } public void cancel(Transportable key) { throw new RuntimeException("ProtocolRPt2Pt: Weird: " + "cancel was not expected to be used"); } public void reset(Transportable key) { throw new RuntimeException("ProtocolRPt2Pt: Weird: " + "cancel was not expected to be used"); } // Interface for NonBlockingTCP and TCPStackInterface /** * Listen */ public Server startServer(PID myself) { Server s = null; try { s = new Server(myself, this); } catch (IOException ioe) { ioe.printStackTrace(); System.exit(1); } return s; } public void stopServer(Server s) { s.close(); } public void accepted(Connection c) { NetworkResponseParameters params = new NetworkResponseParameters(1,c,null); net.externalResponse(params, null); } /** * Connect/disconnect/Link failure */ public void connect(PID p) { new Connection(this.stack.getPID(), p, this); } public void disconnect(Connection c) { c.disconnect(); } public void connected(PID p, Connection c) { NetworkResponseParameters params = new NetworkResponseParameters(2,c,p); net.externalResponse(params, null); } public void closed(Connection c) { NetworkResponseParameters params = new NetworkResponseParameters(3,c,null); net.externalResponse(params, null); } public void broken(Connection c) { NetworkResponseParameters params = new NetworkResponseParameters(4,c,null); net.externalResponse(params, null); } /** * Receive / Blocking send */ public void sendMessage(byte[] b, Connection c) { /* May block */ c.sendMessage(b); } public void startReceiver(Connection c) { c.startReceiver(); } public void stopReceiver(Connection c) { c.stopReceiver(); } public void recv(byte[] b, Connection c) { NetworkResponseParameters params = new NetworkResponseParameters(5,c,null); net.externalResponse(params, new Message(new TByteArray(b), null)); } /** * Non-blocking send */ public void startSender(Connection c) { c.startSender(); } public void stopSender(Connection c) { c.stopSender(); } public void setMessageToSend(byte[] b, Connection c) { c.setMessageToSend(b); } public void readyForNextMessage(Connection c) { NetworkResponseParameters params = new NetworkResponseParameters(6,c,null); net.externalResponse(params, null); } /** * Marshalling/unmarshalling */ public byte[] marshall(Transportable m) { byte[] b = null; try { b = DefaultSerialization.marshall(m); } catch (IOException ioe) { ioe.printStackTrace(); System.exit(1); } return b; } public Transportable unmarshall(byte[] b) { Transportable m = null; try { m = DefaultSerialization.unmarshall(b); } catch (ClassNotFoundException cnfe) { cnfe.printStackTrace(); System.exit(1); } catch (IOException ioe) { ioe.printStackTrace(); System.exit(1); } return m; } /** * Miscellaneous */ public PID getRemotePID(Connection c) { return c.getRemotePID(); } }