/*******************************************************************************
* 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 staticContent.evaluation.loadGenerator.traceBasedTraffic;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import staticContent.evaluation.loadGenerator.LoadGenerator;
import staticContent.evaluation.loadGenerator.scheduler.InOrderYieldWaitScheduler;
import staticContent.evaluation.loadGenerator.scheduler.Scheduler;
import staticContent.evaluation.loadGenerator.scheduler.ThreadPoolScheduler;
import staticContent.evaluation.loadGenerator.traceBasedTraffic.event.Event;
import staticContent.framework.AnonNode;
import staticContent.framework.launcher.ToolName;
import staticContent.framework.routing.RoutingMode;
import staticContent.framework.socket.socketInterfaces.StreamAnonSocket;
import staticContent.framework.socket.socketInterfaces.AnonSocketOptions.CommunicationDirection;
import staticContent.framework.util.Util;
/**
* This class establishes the connections of the clients to the first mix and
* instruments the clients. The inner-class ReplyReceiverThread is responsible
* for receiving the incoming replies from the server in a dedicated thread.
*
* @author Johannes Wendel, Simon Lecheler, kpf
*
*/
public class RaFM_LoadGenerator {
private AnonNode client; // we use only one "real" client to retrieve public keys of mixes etc and then create several anonymous connections (sockets) with this client (note: those connections are unlinkable for mixes, so this simplification should not affect simulation accuracy but improve the performance of the load generator)
private Scheduler<Event> scheduler; // scheduler used by clients to schedule send events
private RaFM_TraceReplayClient[] clients; // the virtual clients as present in the trace (note: clients are event-based; all events are called by the threads of this class (and its scheduler))
private StreamAnonSocket[] clientSockets;
private ReplyReceiverThread[] replyThreads; // for duplex simulations (notifies virtual clients about received replies)
private int voteStopCounter = 0;
private long start;
protected RaFM_LoadGenerator(LoadGenerator owner) {
int numberOfThreads = owner.settings.getPropertyAsInt("AL-RaFM-NUMBER_OF_SCHEDULER_THREADS");
boolean useYieldWaitScheduler = !owner.settings.isPropertyPresent("USE_SLOW_BUT_ACCURATE_SCHEDULER") ? false : owner.settings.getPropertyAsBoolean("USE_SLOW_BUT_ACCURATE_SCHEDULER");
if (useYieldWaitScheduler)
this.scheduler = new InOrderYieldWaitScheduler<Event>(owner.settings);
else
this.scheduler = new ThreadPoolScheduler<Event>(owner.settings, numberOfThreads);
RaFM_TraceFileModel traceFileModel = new RaFM_TraceFileModel(owner.settings);
this.clients = traceFileModel.createClientsArray();
this.clientSockets = new StreamAnonSocket[this.clients.length];
this.replyThreads = new ReplyReceiverThread[this.clients.length];
//create anon client we will use to create sockets:
owner.commandLineParameters.gMixTool = ToolName.CLIENT;
this.client = new AnonNode(owner.commandLineParameters);
CommunicationDirection cd = client.IS_DUPLEX ? CommunicationDirection.DUPLEX : CommunicationDirection.SIMPLEX_SENDER;
int port = owner.settings.getPropertyAsInt("SERVICE_PORT1");
System.out.println("RaFM_LOAD_GENERATOR: connecting clients...");
for (int i=0; i<this.clients.length; i++) { // connect all virtual clients
clientSockets[i] = client.createStreamSocket(cd, client.ROUTING_MODE != RoutingMode.GLOBAL_ROUTING);
try {
clientSockets[i].connect(port);
} catch (IOException e) {
e.printStackTrace();
}
}
if (client.IS_DUPLEX) { // start reply threads
for (int i=0; i<this.clients.length; i++) {
try {
replyThreads[i] = new ReplyReceiverThread(clients[i], clientSockets[i]);
} catch (IOException e) {
e.printStackTrace();
}
replyThreads[i].start();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.start = System.currentTimeMillis();
for (int i=0; i<this.clients.length; i++) { // tell virtual clients to start scheduling/sending
clients[i].startSending(this, scheduler, clientSockets[i]);
}
}
public synchronized void voteForStop(StreamAnonSocket socket) {
voteStopCounter++;
System.out.println("Stopped Clients: "+voteStopCounter);
if (voteStopCounter == clients.length)
stopSimulation("end of trace reached (variable SIMULATION_END in experiment config)");
}
public void stopSimulation(String reason) {
long duration = System.currentTimeMillis() - this.start;
System.out.println("### stopping simulation. reason: " + reason);
RaFM_TraceReplayClient.displayResults(duration);
System.exit(0);
}
private class ReplyReceiverThread extends Thread {
private InputStream inputStream;
private RaFM_TraceReplayClient client;
public ReplyReceiverThread(RaFM_TraceReplayClient client, StreamAnonSocket socket) throws IOException {
this.client = client;
this.inputStream = socket.getInputStream();
}
@Override
public void run() {
while (true) {
try {
byte[] reply = Util.forceRead(inputStream, Util.forceReadInt(inputStream));
int transactionId = Util.byteArrayToInt(Arrays.copyOf(reply, 4));
client.incomingReply(transactionId);
} catch (IOException e) {
stopSimulation(e.getLocalizedMessage());
}
}
}
}
public static RaFM_LoadGenerator createInstance(LoadGenerator loadGenerator) {
return new RaFM_LoadGenerator(loadGenerator);
}
}