/** * This software is GPLv2. * Take a look at the LICENSE file for more info. */ package de.tu.dresden.dud.dc.WorkCycle; import java.io.IOException; import java.util.Arrays; import java.util.Iterator; import java.util.Observable; import java.util.Observer; import java.util.TreeSet; import org.apache.log4j.Logger; import de.tu.dresden.dud.dc.Connection; import de.tu.dresden.dud.dc.Util; import de.tu.dresden.dud.dc.KeyGenerators.KeyGenerator; import de.tu.dresden.dud.dc.ManagementMessage.ManagementMessageAdd; import de.tu.dresden.dud.dc.ManagementMessage.ManagementMessageAdded; /** * @author klobs * */ public class WorkCycleSending extends WorkCycle implements Observer, Runnable { // Logging private static Logger log = Logger.getLogger(WorkCycleSending.class); public static final long MODULUS = 0x100000000L; // internal variables protected boolean finished = false; protected byte[] payloadSend = null; protected WorkCycle assocWorkCycle = null; protected TreeSet<WorkCycleRound> rounds = new TreeSet<WorkCycleRound>( new WorkCycleRoundComparator()); private boolean successful = false; // sending protected int currentRound = 0; public WorkCycleSending(WorkCycle r) { assocWorkCycle = r; assocWorkCycleManag = r.getAssocWorkCycleManager(); assocKeyGenerator = assocWorkCycleManag.getKeyGenerator(); broadcastConnections = r.getBroadcastConnections(); expectedConnections = r.getExpectedConnections(); expectedRounds = r.getExpectedRounds() < 1 ? -1 : r .getExpectedRounds(); keyGenerationMethod = r.getKeyGenerationMethod(); relativeRound = r.getRelativeRound(); systemPayloadLength = r.getSystemPayloadLength(); workcycleNumber = r.getWorkCycleNumber(); timeout = r.getTimeout(); if (relativeRound >= 0) payloadSend = r.consumePayload(); else payloadSend = null; if (assocWorkCycleManag.getMessageLengthMode() == WorkCycleManager.MESSAGE_LENGTHS_VARIABLE){ individualPayloadLengths = r.getIndividualMessageLenghts(); } this.addObserver(this); } public void addMessageArrived(Connection c, ManagementMessageAdd m) { synchronized (assocWorkCycleManag) { WorkCycleRound rn = getRoundByRoundNumber(m.getRoundNumber()); rn.addMessageArrived(c, m); } } public synchronized void addedMessageArrived(ManagementMessageAdded m) { if (relativeRound == m.getRoundNumber() && !Arrays.equals(m.getPayload(), payloadSend)) { log.error("Expected and actual received payloads are not equal! Something is messing around with us!"); successful = false; } else successful = true; } protected void addUp() { byte[] b = null; if (assocWorkCycle.getCurrentPhase() == WorkCycle.WC_RESERVATION) { b = new byte[WorkCycleReservationPayload.RESERVATION_PAYLOAD_SIZE]; } else { b = new byte[systemPayloadLength]; if (assocWorkCycleManag.getMessageLengthMode() == WorkCycleManager.MESSAGE_LENGTHS_VARIABLE){ b = new byte[assocWorkCycle.getIndividualMessageLenghts().get(currentRound)]; } Iterator<byte[]> i = payloads.iterator(); while (i.hasNext()) { byte[] c = i.next(); b = Util.mergeDCwise(b, c, WorkCycleSending.MODULUS); } } payloadSend = b; } protected void broadcastSum() { ManagementMessageAdded m = new ManagementMessageAdded(workcycleNumber, currentRound, payloadSend); if (assocWorkCycle.getCurrentPhase() == WorkCycle.WC_RESERVATION) assocWorkCycle.checkWhetherReservationIsFinishedOnServerSide(m); try { Iterator<Connection> i = getBroadcastConnections().iterator(); while (i.hasNext()) { i.next().sendMessage(m.getMessage()); } } catch (IOException o) { log.error(o.toString()); } } public void finalizeRoundServerSide(WorkCycleRound r) { // set right payloads payloads = r.getPayloads(); // Add up addUp(); log.debug("add up this " + Arrays.toString(payloadSend)); // After adding up // send out the the added broadcastSum(); // increase the round counter. currentRound++; // clean up the round rounds.remove(r); if (!(this instanceof WorkCycleReserving) && (assocWorkCycle.getCurrentPhase() == WorkCycle.WC_SENDING) && (currentRound == expectedRounds)) { finished = true; setChanged(); notifyObservers(WorkCycle.WC_FINISHED); } } /** * Returns a work cycle with a desired work cycle number. If the work cycle does not exsist * yet in the database, it will be created. * * After creation, the work cycle will be saved in the database, and be returned * on the next request. * * @param d * @return The desired work cycle */ public synchronized WorkCycleRound getRoundByRoundNumber(int d) { Iterator<WorkCycleRound> i = rounds.iterator(); WorkCycleRound r = null; int srn = 0; // Long.MIN_VALUE; while (i.hasNext()) { r = i.next(); srn = r.getRoundNumber(); if (srn < d) { if (!i.hasNext()) r = null; continue; } else if (srn == d) break; else { r = null; break; } } if (r == null) { r = new WorkCycleRound(d, this); r.addObserver(this); rounds.add(r); } return r; } public boolean hasFinished() { return this.finished; } public boolean hasWorkCycleBeenSuccessful(){ return successful; } public boolean isServerMode() { return assocWorkCycle.getAssocWorkCycleManager().isServerMode(); } /** * The rounds of a DC-Network are deterministic. What we can do is * prepare all messages and then fire them as fast as possible to the * server, hoping it can handle them. */ public void performDCRoundsParticipantSide() { ManagementMessageAdd m = null; int currentMessageLength = 0; if (relativeRound >= 0 && assocWorkCycleManag.getMessageLengthMode() == WorkCycleManager.MESSAGE_LENGTHS_VARIABLE) { payloadSend = Util.fillAndMergeSending(payloadSend, new byte[individualPayloadLengths.get(relativeRound) .intValue()]); } while (!finished) { if (assocWorkCycleManag.getMessageLengthMode() == WorkCycleManager.MESSAGE_LENGTHS_VARIABLE) { currentMessageLength = individualPayloadLengths.get( currentRound).intValue(); } else { currentMessageLength = systemPayloadLength; } if (currentRound == relativeRound) { byte[] p = Util.mergeDCwise(payloadSend, assocKeyGenerator.calcKeys(currentMessageLength, workcycleNumber, currentRound), MODULUS); m = new ManagementMessageAdd(workcycleNumber, currentRound, p); } else { byte[] p = assocKeyGenerator.calcKeys(currentMessageLength, workcycleNumber, currentRound); m = new ManagementMessageAdd(workcycleNumber, currentRound, p); } try { getAssocParticipantManager().getMyInfo().getAssocConnection() .sendMessage(m.getMessage()); } catch (IOException o) { log.error(o.toString()); } currentRound++; if (currentRound >= expectedRounds) finished = true; try{ if(KeyGenerator.isSynchronous(keyGenerationMethod) && finished != true) assocWorkCycle.getSemaphore().acquire(); } catch (InterruptedException e){ log.error(e.toString()); } } setChanged(); notifyObservers(WorkCycle.WC_FINISHED); } public void run() { performDCRoundsParticipantSide(); } @Override public void update(Observable o, Object arg) { if (o instanceof WorkCycleRound && ((Integer) arg).intValue() == WorkCycle.WC_ROUND_ADDUP) { finalizeRoundServerSide((WorkCycleRound) o); } } }