/*******************************************************************************
* gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/
* Copyright (C) 2014 SVS
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package userGeneratedContent.simulatorPlugIns.plugins.trafficSource;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import staticContent.evaluation.simulator.Simulator;
import staticContent.evaluation.simulator.core.event.Event;
import staticContent.evaluation.simulator.core.message.EndToEndMessage;
import staticContent.evaluation.simulator.core.message.TransportMessage;
import staticContent.evaluation.simulator.core.networkComponent.AbstractClient;
import staticContent.evaluation.traceParser.engine.dataStructure.ExtendedTransaction;
import staticContent.evaluation.traceParser.engine.dataStructure.Flow;
import staticContent.evaluation.traceParser.engine.dataStructure.Flow.Restriction;
import staticContent.evaluation.traceParser.engine.fileReader.FlowGroupFlowIterator;
import staticContent.evaluation.traceParser.engine.fileReader.FlowReader;
import staticContent.framework.logger.OutputCap;
import staticContent.framework.util.Util;
public class TraceReplayClient extends AbstractClient {
private FlowSource flowSource;
private int idOfFirstFlowOfCurrentFlowGroup = 0;
private int idOfLatestStartedFlow = Util.NOT_SET;
private HashSet<Integer> finishedFlows;
private HashMap<Integer, ActiveFlow> activeFlows = new HashMap<Integer, ActiveFlow>();
private OutputCap noMoreFlowsMessage = new OutputCap(this.toString() +": no more flows to schedule", Long.MAX_VALUE);
private boolean noMoreFlowsToSchedule = false;
private int clientIdInTrace;
public TraceReplayClient(String identifier, Simulator simulator, FlowReader trace, int clientId, int clientIdInTrace) {
super(identifier, simulator);
this.clientId = clientId;
this.clientIdInTrace = clientIdInTrace;
this.flowSource = new FlowSource(trace);
}
public void startSending() {
tryScheduleNextFlows();
}
@Override
public void messageReachedServer(EndToEndMessage message) {
if (closedLoopSending)
messageTransmitted(message);
}
private void messageTransmitted(EndToEndMessage message) {
ExtendedTransaction at = (ExtendedTransaction)message.getPayload();
if (!simulateReplyChannel) { // simplex simulation -> schedule replies here -> assume replies arrive as in original trace (NOT_BEFORE_END_OF_TRANSACTION-restriction requires knowledge of reply times)
if (at.containsReplies()) { // create and schedule replies:
int[] replySizes = at.getDistinctReplySizes();
for (int i=0; i<replySizes.length; i++) {
EndToEndMessage reply = message.createReplyForThisMessage(at, replySizes[i]);
long replyDelay = at.endsOfReplies[i] - at.startOfRequest; // delay as observed in original trace
Event incomingReplyEvent = new Event(this, Simulator.getNow() + replyDelay, TraceReplayClientEvent.CALL_INCOMING_MESSAGE, reply);
simulator.scheduleEvent(incomingReplyEvent, this);
//System.out.println("simplex, scheduling reply for transaction " +at.getTransactionId() +" for t=" +(Simulator.getNow() + replyDelay));
}
} else { // no reply
// nothing to do
}
}
if (!at.containsReplies()) { // try to schedule next transaction if we do not have to wait for a reply from the server
tryScheduleNextTransaction((ActiveFlow) message.getPayload().attachment);
}
}
@Override
public void incomingMessage(EndToEndMessage message) {
ActiveFlow relatedFlow = (ActiveFlow) message.getPayload().attachment;
//System.out.println(super.toString() +": received reply for transaction " +message.getPayload().getTransactionId() +" (flow " +relatedFlow.flow.flowId + ", now: " +Simulator.getNow() +", object-ref: " +message +")");
relatedFlow.replyReceived();
tryScheduleNextTransaction(relatedFlow);
}
private void tryScheduleNextTransaction(ActiveFlow activeFlow) {
if (activeFlow.allRepliesForCurrentTransactionReceived()) {
if (activeFlow.hasNextTransaction()) { // next transaction available -> schedule its send
ExtendedTransaction nextTransaction = activeFlow.getNextTransaction();
//System.out.println(nextTransaction.getSendDelay());
Event replayNextTransactionEvent = new Event(this, Simulator.getNow() + nextTransaction.getSendDelay(), TraceReplayClientEvent.REPLAY_NEXT_TRANSACTION, activeFlow);
//System.out.println(super.toString() +": scheduling next transaction (id: " +nextTransaction.getTransactionId() +", arrayOffset: " +activeFlow.getArrayOffsetOfCurrentTransaction() +", flow: " +activeFlow.flow.flowId +", time:" +Simulator.getNow() +") for " +(Simulator.getNow() + nextTransaction.getSendDelay()));
simulator.scheduleEvent(replayNextTransactionEvent, this);
} else { // no next transaction (end of flow)
//System.out.println(super.toString() +": flow " +activeFlow.flow.flowId +" is finished (now: " +Simulator.getNow() +")");
finishedFlows.add(activeFlow.getFlow().flowId);
activeFlows.remove(activeFlow.getFlow().flowId);
if (noMoreFlowsToSchedule && activeFlows.size() == 0) { // nothing left to schedule and no more active flows -> end simulation
simulator.voteForStop();
//simulator.stopSimulation("end of trace reached (variable SIMULATION_END in experiment config)");
//System.out.println("thats it now " +Simulator.getNow());
}
}
}
tryScheduleNextFlows();
}
private void tryScheduleNextFlows() {
while (flowSource.peekNextFlow() != null) {
if (flowSource.peekNextFlow().restriction == Restriction.NONE) { // schedule none restricted flow
Flow nextFlow = flowSource.readNextFlow();
Event replayNextFlowEvent = new Event(this, Simulator.getNow() , TraceReplayClientEvent.REPLAY_FLOW, nextFlow);
//System.out.println(super.toString() +": scheduling next flow (no restriction, id: " +nextFlow.flowId +", time:" +Simulator.getNow() +") for " +Simulator.getNow());
simulator.scheduleEvent(replayNextFlowEvent, this);
} else if (flowSource.peekNextFlow().restriction == Restriction.SIMPLE_DELAY) {
Flow nextFlow = flowSource.readNextFlow();
Event replayNextFlowEvent = new Event(this, Simulator.getNow() + nextFlow.offsetFromRestriction, TraceReplayClientEvent.REPLAY_FLOW, nextFlow);
//System.out.println(super.toString() +": scheduling next flow (SIMPLE_DELAY restriction, id: " +nextFlow.flowId +", time:" +Simulator.getNow() +") for " +(Simulator.getNow() + nextFlow.offsetFromRestriction));
simulator.scheduleEvent(replayNextFlowEvent, this);
} else if (flowSource.peekNextFlow().restriction == Restriction.NOT_BEFORE_END_OF_OTHER_FLOW) {
if (flowSource.peekNextFlow().idOfRestrictingFlow < idOfFirstFlowOfCurrentFlowGroup || finishedFlows.contains(flowSource.peekNextFlow().idOfRestrictingFlow)) { // no more blocked (restricting flow is finished now)
Flow nextFlow = flowSource.readNextFlow();
Event replayNextFlowEvent = new Event(this, Simulator.getNow() + nextFlow.offsetFromRestriction, TraceReplayClientEvent.REPLAY_FLOW, nextFlow);
//System.out.println(super.toString() +": scheduling next flow (NOT_BEFORE_END_OF_OTHER_FLOW-restriction, id: " +nextFlow.flowId +", id of restricting flow: " +nextFlow.idOfRestrictingFlow +", time:" +Simulator.getNow() +", delay: " +nextFlow.offsetFromRestriction +") for " +(Simulator.getNow() + nextFlow.offsetFromRestriction));
simulator.scheduleEvent(replayNextFlowEvent, this);
} else { // next flow is blocked
break;
}
} else if (flowSource.peekNextFlow().restriction == Restriction.NOT_BEFORE_END_OF_TRANSACTION) {
int idOfRestrictingFlow = flowSource.peekNextFlow().idOfRestrictingFlow;
int idOfRestrictingTransaction = flowSource.peekNextFlow().idOfRestrictingTransaction;
ActiveFlow restrictingFlow = activeFlows.get(idOfRestrictingFlow);
if ( (restrictingFlow == null && idOfLatestStartedFlow >= idOfRestrictingFlow) // restricting flow is finished already
|| (restrictingFlow != null && idOfRestrictingTransaction < restrictingFlow.getArrayOffsetOfCurrentTransaction()) // restricting transaction is already finished
|| (restrictingFlow != null && (idOfRestrictingTransaction == restrictingFlow.getArrayOffsetOfCurrentTransaction() // restricting transaction is currently being replayed
&& flowSource.peekNextFlow().idOfRestrictingReply <= restrictingFlow.getIdOfLatestFinishedReply())) // the restricting reply of the restricting transaction is already replayed
) { // restriction no longer given
Flow nextFlow = flowSource.readNextFlow();
Event replayNextFlowEvent = new Event(this, Simulator.getNow() + nextFlow.offsetFromRestriction, TraceReplayClientEvent.REPLAY_FLOW, nextFlow);
//System.out.println(super.toString() +": scheduling next flow (NOT_BEFORE_END_OF_TRANSACTION-restriction, id: " +nextFlow.flowId +", time:" +Simulator.getNow() +") for " +(Simulator.getNow() + nextFlow.offsetFromRestriction));
//String rsfid = restrictingFlow == null ? "none" : ""+restrictingFlow.flow.flowId;
//System.out.println("restrictingFlow: " +rsfid);
simulator.scheduleEvent(replayNextFlowEvent, this);
} else { // next flow is blocked
break;
}
} else {
throw new RuntimeException("no handler implemented for restriction " +flowSource.peekNextFlow().restriction);
}
}
if (flowSource.peekNextFlow() == null) { // no more flows to schedule
flowSource.close();
noMoreFlowsMessage.putOut();
noMoreFlowsToSchedule = true;
}
}
/*
* private void tryScheduleNextFlows() {
try {
if (currentFlowGroup == null || !currentFlowGroup.hasNext()) { // need next flow group
if (trace.hasNextFlow() && trace.peekNextFlow().senderId == clientId) { // next flow group is available -> schedule all none-restricted flows
currentFlowGroup = trace.getFlowGroupFlowIterator();
finishedFlows.clear();
finishedFlows = new HashSet<Integer>();
while (currentFlowGroup.hasNext() && trace.peekNextFlow().notBeforeRestriction == Util.NOT_SET) { geht so nicht, notBeforeRestriction wird erst durch fgiterator gesetzt..
Flow nextFlow = currentFlowGroup.next();
Event replayNextFlowEvent = new Event(this, Simulator.getNow() + nextFlow.offsetFromLastFlow, TraceReplayClientEvent.REPLAY_FLOW, nextFlow);
simulator.scheduleEvent(replayNextFlowEvent, this);
}
}
} else { // check if blocked flows of the current flow group can be scheduled:
while (currentFlowGroup.hasNext() && finishedFlows.contains(trace.peekNextFlow().notBeforeRestriction)) { geht so nicht, notBeforeRestriction wird erst durch fgiterator gesetzt..
Flow nextFlow = currentFlowGroup.next();
Event replayNextFlowEvent = new Event(this, Simulator.getNow() + nextFlow.offsetFromRestrictingFlow, TraceReplayClientEvent.REPLAY_FLOW, nextFlow);
simulator.scheduleEvent(replayNextFlowEvent, this);
}
}
} catch (IOException e) {
throw new RuntimeException("could not read trace file " +e.getLocalizedMessage());
}
}
*/
private void replayFlow(Flow flow) {
//System.out.println(super.toString() +": start replaying flow " +flow.flowId +" (now: " +Simulator.getNow() +")");
ActiveFlow activeFlow = new ActiveFlow(flow);
activeFlows.put(flow.flowId, activeFlow);
idOfLatestStartedFlow = flow.flowId;
assert activeFlow.hasNextTransaction();
tryScheduleNextTransaction(activeFlow);
}
private void replayTransaction(ActiveFlow activeFlow) {
assert activeFlow.getCurrentTransaction() != null;
ExtendedTransaction transaction = activeFlow.getCurrentTransaction();
transaction.attachment = activeFlow;
if (transaction.getRequestSize() == 0) // -> server shall send first message
transaction.setRequestSize(16); // we assume that the client must first send a message to the final mix to open a socket that can receive data from the server (e.g. socks 5 bind command)
EndToEndMessage eteMessage = new EndToEndMessage(transaction.getServerId(), transaction, !activeFlow.hasNextTransaction());
//System.out.println(super.toString() +": sending transaction (id: " +transaction.getTransactionId() +", flow " +activeFlow.flow.flowId +", time:" +Simulator.getNow() +")");
TransportMessage tm = super.sendMessage(eteMessage);
if (!closedLoopSending) {
assert tm.reltedEndToEndMessage == eteMessage;
EndToEndMessage message = tm.reltedEndToEndMessage;
message.transportMessage = tm;
/*
* EndToEndMessage message = tm.reltedEndToEndMessage;
message.transportMessage = tm;
statistics.addValue(tm.getLength(), StatisticsType.DISTANTPROXY_DATAVOLUME_SENDANDRECEIVE);
server.incomingMessage(message);
tm.getOwner().messageReachedServer(message);
*/
/*EndToEndMessage message = eteMessage.r
message.transportMessage = tm;
statistics.addValue(tm.getLength(), StatisticsType.DISTANTPROXY_DATAVOLUME_SENDANDRECEIVE);
server.incomingMessage(message);
tm.getOwner().messageReachedServer(message); // notify client that his message has now reached the server
*/
messageTransmitted(eteMessage);
}
}
@Override
public void executeEvent(Event event) {
if (event.getEventType() instanceof TraceReplayClientEvent) {
if (event.getEventType() == TraceReplayClientEvent.REPLAY_FLOW) {
replayFlow((Flow)event.getAttachment());
} else if (event.getEventType() == TraceReplayClientEvent.REPLAY_NEXT_TRANSACTION) {
replayTransaction((ActiveFlow)event.getAttachment());
} else if (event.getEventType() == TraceReplayClientEvent.CALL_INCOMING_MESSAGE) {
incomingMessage((EndToEndMessage)event.getAttachment());
} else
throw new RuntimeException("ERROR: received unknown Event: " +event.toString());
} else {
super.executeEvent(event);
}
}
private class FlowSource {
private FlowReader trace;
private FlowGroupFlowIterator currentFlowGroup;
private Flow nextFlow = null;
private boolean wasPeekNextFlowCalled = false;
private boolean newFlowGroup = false;
private FlowSource(FlowReader trace) {
this.trace = trace;
}
private Flow peekNextFlow() {
if (wasPeekNextFlowCalled)
return nextFlow;
try {
wasPeekNextFlowCalled = true;
if (nextFlow != null) {
return nextFlow;
} else {
if (trace.peekNextFlow() == null || trace.peekNextFlow().senderId != clientIdInTrace) { // no more flows for this client
return null;
} else { // more flows available
if (currentFlowGroup == null || !currentFlowGroup.hasNext()) { // need next flow group
currentFlowGroup = trace.getFlowGroupFlowIterator();
newFlowGroup = true;
}
nextFlow = currentFlowGroup.next();
return nextFlow;
}
}
} catch (IOException e) {
throw new RuntimeException("could not read trace file " +e.getLocalizedMessage());
}
}
private Flow readNextFlow() {
if (!wasPeekNextFlowCalled)
peekNextFlow();
wasPeekNextFlowCalled = false;
Flow result = nextFlow;
if (newFlowGroup) {
if (finishedFlows != null) {
finishedFlows.clear();
activeFlows.clear();
}
finishedFlows = new HashSet<Integer>();
idOfFirstFlowOfCurrentFlowGroup = result.flowId;
newFlowGroup = false;
}
nextFlow = null;
return result;
}
private void close() {
try {
trace.close();
} catch (IOException e) {
throw new RuntimeException("could not close trace file " +e.getLocalizedMessage());
}
}
}
@Override
public void close() {
flowSource.close();
}
}