summaryrefslogtreecommitdiff
path: root/src/main/java/simulator/builder
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/simulator/builder')
-rw-r--r--src/main/java/simulator/builder/SimulationBuilder.java362
-rw-r--r--src/main/java/simulator/builder/SimulationFactory.java115
2 files changed, 477 insertions, 0 deletions
diff --git a/src/main/java/simulator/builder/SimulationBuilder.java b/src/main/java/simulator/builder/SimulationBuilder.java
new file mode 100644
index 0000000..8ac5d04
--- /dev/null
+++ b/src/main/java/simulator/builder/SimulationBuilder.java
@@ -0,0 +1,362 @@
+package simulator.builder;
+
+import simulator.*;
+import core.*;
+import prefs.*;
+import events.*;
+import events.internal.*;
+import serialize.VSSerialize;
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.ArrayList;
+
+/**
+ * Builder for creating DS-Sim simulations programmatically without GUI.
+ *
+ * Example usage:
+ * <pre>
+ * SimulationBuilder builder = new SimulationBuilder()
+ * .withProcesses(5)
+ * .withProtocol("protocols.implementations.VSRaftProtocol")
+ * .activateServers(0, 1, 2)
+ * .activateClients(3, 4)
+ * .addCrashEvent(0, 2000)
+ * .addRecoveryEvent(0, 3000)
+ * .save("saved-simulations/my-raft.dat");
+ * </pre>
+ */
+public class SimulationBuilder {
+
+ private VSDefaultPrefs prefs;
+ private VSSimulator simulator;
+ private VSSimulatorVisualization visualization;
+ private VSTaskManager taskManager;
+ private String protocolClass;
+ private int numProcesses = 3; // default
+ private List<ScheduledTask> scheduledTasks = new ArrayList<>();
+ private int simulationDuration = 10000; // default 10 seconds
+
+ /**
+ * Internal class to hold task scheduling information
+ */
+ private static class ScheduledTask {
+ long time;
+ int processId;
+ VSAbstractEvent event;
+ boolean isGlobalTimed;
+
+ ScheduledTask(long time, int processId, VSAbstractEvent event, boolean isGlobalTimed) {
+ this.time = time;
+ this.processId = processId;
+ this.event = event;
+ this.isGlobalTimed = isGlobalTimed;
+ }
+ }
+
+ /**
+ * Initialize the builder with default preferences
+ */
+ public SimulationBuilder() throws Exception {
+ // Initialize preferences
+ prefs = new VSDefaultPrefs();
+ prefs.fillWithDefaults();
+
+ // Initialize registered events
+ VSRegisteredEvents.init(prefs);
+ }
+
+ /**
+ * Set the simulation duration in milliseconds
+ */
+ public SimulationBuilder withDuration(int durationMs) {
+ this.simulationDuration = durationMs;
+ return this;
+ }
+
+ /**
+ * Set the number of processes in the simulation
+ */
+ public SimulationBuilder withProcesses(int count) {
+ this.numProcesses = count;
+ return this;
+ }
+
+ /**
+ * Set the protocol class to use
+ */
+ public SimulationBuilder withProtocol(String protocolClassName) {
+ this.protocolClass = protocolClassName;
+ return this;
+ }
+
+ /**
+ * Activate protocol as server on specified processes
+ */
+ public SimulationBuilder activateServers(int... processIds) {
+ for (int pid : processIds) {
+ VSProtocolEvent event = new VSProtocolEvent();
+ event.onInit(); // Initialize the event first
+ setProtocolClassname(event, protocolClass);
+ setIsServer(event, true);
+
+ scheduledTasks.add(new ScheduledTask(0, pid, event, true));
+ }
+ return this;
+ }
+
+ /**
+ * Activate protocol as client on specified processes
+ */
+ public SimulationBuilder activateClients(int... processIds) {
+ return activateClients(500, processIds); // default delay
+ }
+
+ /**
+ * Activate protocol as client on specified processes with custom start time
+ */
+ public SimulationBuilder activateClients(long startTime, int... processIds) {
+ for (int i = 0; i < processIds.length; i++) {
+ VSProtocolEvent event = new VSProtocolEvent();
+ event.onInit(); // Initialize the event first
+ setProtocolClassname(event, protocolClass);
+ setIsServer(event, false);
+
+ // Stagger client starts
+ long time = startTime + (i * 200);
+ scheduledTasks.add(new ScheduledTask(time, processIds[i], event, true));
+ }
+ return this;
+ }
+
+ /**
+ * Add a process crash event
+ */
+ public SimulationBuilder addCrashEvent(int processId, long time) {
+ try {
+ // Use reflection to create crash event
+ Class<?> crashClass = Class.forName("events.implementations.VSProcessCrashEvent");
+ VSAbstractEvent crashEvent = (VSAbstractEvent) crashClass.getDeclaredConstructor().newInstance();
+ scheduledTasks.add(new ScheduledTask(time, processId, crashEvent, true));
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to create crash event", e);
+ }
+ return this;
+ }
+
+ /**
+ * Add a process recovery event
+ */
+ public SimulationBuilder addRecoveryEvent(int processId, long time) {
+ try {
+ // Use reflection to create recovery event
+ Class<?> recoverClass = Class.forName("events.implementations.VSProcessRecoverEvent");
+ VSAbstractEvent recoverEvent = (VSAbstractEvent) recoverClass.getDeclaredConstructor().newInstance();
+ scheduledTasks.add(new ScheduledTask(time, processId, recoverEvent, true));
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to create recovery event", e);
+ }
+ return this;
+ }
+
+ /**
+ * Add a custom event
+ */
+ public SimulationBuilder addEvent(String eventClassName, int processId, long time) {
+ try {
+ Class<?> eventClass = Class.forName(eventClassName);
+ VSAbstractEvent event = (VSAbstractEvent) eventClass.getDeclaredConstructor().newInstance();
+ scheduledTasks.add(new ScheduledTask(time, processId, event, true));
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to create event: " + eventClassName, e);
+ }
+ return this;
+ }
+
+ /**
+ * Set protocol classname using reflection (since field is private)
+ */
+ private void setProtocolClassname(VSProtocolEvent event, String classname) {
+ try {
+ Field field = VSProtocolEvent.class.getDeclaredField("protocolClassname");
+ field.setAccessible(true);
+ field.set(event, classname);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to set protocol classname", e);
+ }
+ }
+
+ /**
+ * Set isServer flag using reflection (field is called isClientProtocol)
+ */
+ private void setIsServer(VSProtocolEvent event, boolean isServer) {
+ try {
+ // The field is actually called isClientProtocol, and server = !client
+ Field field = VSProtocolEvent.class.getDeclaredField("isClientProtocol");
+ field.setAccessible(true);
+ field.set(event, !isServer); // Invert: server means NOT client
+
+ // Also set protocol activation to true
+ Field activationField = VSProtocolEvent.class.getDeclaredField("isProtocolActivation");
+ activationField.setAccessible(true);
+ activationField.set(event, true);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to set protocol flags", e);
+ }
+ }
+
+ /**
+ * Build the simulation (must be called before save)
+ */
+ private void build() throws Exception {
+ if (simulator != null) {
+ return; // Already built
+ }
+
+ // Set simulation duration
+ prefs.setInteger("sim.seconds", simulationDuration / 1000);
+
+ // Set network delay parameters for message delivery
+ prefs.setInteger("process.msg.delay.min", 10); // 10ms minimum delay
+ prefs.setInteger("process.msg.delay.max", 50); // 50ms maximum delay
+
+ // Create simulator without frame for headless
+ simulator = new VSSimulator(prefs, null);
+
+ // Create visualization without GUI
+ VSLogging logging = new VSLogging();
+ visualization = new VSSimulatorVisualization(prefs, simulator, logging);
+
+ // Set visualization in simulator using reflection
+ Field vizField = VSSimulator.class.getDeclaredField("simulatorVisualization");
+ vizField.setAccessible(true);
+ vizField.set(simulator, visualization);
+
+ // Add processes if needed (default is 3)
+ Method addProcessMethod = VSSimulatorVisualization.class.getDeclaredMethod("addProcess");
+ addProcessMethod.setAccessible(true);
+
+ // Remove default processes if we want fewer
+ if (numProcesses < 3) {
+ Field processesField = VSSimulatorVisualization.class.getDeclaredField("processes");
+ processesField.setAccessible(true);
+ ArrayList<VSInternalProcess> processes = (ArrayList<VSInternalProcess>) processesField.get(visualization);
+ while (processes.size() > numProcesses) {
+ processes.remove(processes.size() - 1);
+ }
+ }
+
+ // Add more processes if needed
+ for (int i = 3; i < numProcesses; i++) {
+ addProcessMethod.invoke(visualization);
+ }
+
+ // Get task manager
+ taskManager = visualization.getTaskManager();
+
+ // Initialize all events with their processes
+ for (ScheduledTask st : scheduledTasks) {
+ VSInternalProcess process = visualization.getProcess(st.processId);
+ st.event.init(process);
+
+ // For protocol events, update the shortname after init
+ if (st.event instanceof VSProtocolEvent) {
+ VSProtocolEvent protocolEvent = (VSProtocolEvent) st.event;
+ // Force shortname update by calling the method via reflection
+ try {
+ Method createShortname = VSProtocolEvent.class.getDeclaredMethod("createShortname", String.class);
+ createShortname.setAccessible(true);
+ String shortname = (String) createShortname.invoke(protocolEvent, (String)null);
+ protocolEvent.setShortname(shortname);
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+
+ // Create task
+ VSTask task = new VSTask(st.time, process, st.event,
+ st.isGlobalTimed ? VSTask.GLOBAL : VSTask.LOCAL);
+ taskManager.addTask(task);
+ }
+ }
+
+ /**
+ * Save the simulation to a file
+ */
+ public SimulationBuilder save(String filename) throws Exception {
+ build();
+
+ File outputFile = new File(filename);
+ outputFile.getParentFile().mkdirs();
+
+ VSSerialize serialize = new VSSerialize();
+
+ // Save using the serializer
+ try {
+ FileOutputStream fos = new FileOutputStream(outputFile);
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+
+ // Create serializable prefs from our prefs
+ VSSerializablePrefs serializablePrefs = new VSSerializablePrefs();
+
+ // Copy all preferences
+ for (String key : prefs.getIntegerKeySet()) {
+ serializablePrefs.initInteger(key, prefs.getInteger(key));
+ }
+ for (String key : prefs.getBooleanKeySet()) {
+ serializablePrefs.initBoolean(key, prefs.getBoolean(key));
+ }
+ for (String key : prefs.getStringKeySet()) {
+ serializablePrefs.initString(key, prefs.getString(key));
+ }
+ for (String key : prefs.getFloatKeySet()) {
+ serializablePrefs.initFloat(key, prefs.getFloat(key));
+ }
+ for (String key : prefs.getColorKeySet()) {
+ serializablePrefs.initColor(key, prefs.getColor(key));
+ }
+ for (String key : prefs.getVectorKeySet()) {
+ serializablePrefs.initVector(key, prefs.getVector(key));
+ }
+ for (String key : prefs.getLongKeySet()) {
+ serializablePrefs.initLong(key, prefs.getLong(key));
+ }
+
+ // Serialize preferences first
+ serializablePrefs.serialize(serialize, oos);
+
+ // Then serialize simulator
+ simulator.serialize(serialize, oos);
+
+ oos.close();
+ fos.close();
+
+ System.out.println("Simulation saved to: " + outputFile.getAbsolutePath());
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to save simulation", e);
+ }
+
+ return this;
+ }
+
+ /**
+ * Get the built simulator (for testing/verification)
+ */
+ public VSSimulator getSimulator() throws Exception {
+ build();
+ return simulator;
+ }
+
+ /**
+ * Fluent API for common protocol setups
+ */
+ public static class Protocols {
+ public static final String RAFT = "protocols.implementations.VSRaftProtocol";
+ public static final String PING_PONG = "protocols.implementations.VSPingPongProtocol";
+ public static final String BERKLEY_TIME = "protocols.implementations.VSBerkelyTimeProtocol";
+ public static final String BROADCAST = "protocols.implementations.VSBroadcastProtocol";
+ public static final String ONE_PHASE_COMMIT = "protocols.implementations.VSOnePhaseCommitProtocol";
+ public static final String TWO_PHASE_COMMIT = "protocols.implementations.VSTwoPhaseCommitProtocol";
+ public static final String RELIABLE_MULTICAST = "protocols.implementations.VSReliableMulticastProtocol";
+ }
+} \ No newline at end of file
diff --git a/src/main/java/simulator/builder/SimulationFactory.java b/src/main/java/simulator/builder/SimulationFactory.java
new file mode 100644
index 0000000..c06be00
--- /dev/null
+++ b/src/main/java/simulator/builder/SimulationFactory.java
@@ -0,0 +1,115 @@
+package simulator.builder;
+
+import java.util.stream.IntStream;
+
+/**
+ * Factory for creating common simulation patterns using SimulationBuilder.
+ * Provides convenience methods for standard distributed systems scenarios.
+ */
+public class SimulationFactory {
+
+ /**
+ * Create a standard Raft consensus simulation
+ * @param numServers Number of Raft servers (minimum 3 for consensus)
+ * @param numClients Number of client processes
+ * @return Configured SimulationBuilder
+ */
+ public static SimulationBuilder createRaftSimulation(int numServers, int numClients) throws Exception {
+ if (numServers < 3) {
+ throw new IllegalArgumentException("Raft requires at least 3 servers for consensus");
+ }
+
+ return new SimulationBuilder()
+ .withProcesses(numServers + numClients)
+ .withProtocol(SimulationBuilder.Protocols.RAFT)
+ .withDuration(15000) // 15 seconds to see leader election
+ .activateServers(IntStream.range(0, numServers).toArray())
+ .activateClients(500, IntStream.range(numServers, numServers + numClients).toArray());
+ }
+
+ /**
+ * Create a Raft simulation with fault tolerance testing
+ * @param numServers Number of Raft servers
+ * @return Configured SimulationBuilder with crash/recovery events
+ */
+ public static SimulationBuilder createRaftFaultToleranceSimulation(int numServers) throws Exception {
+ return createRaftSimulation(numServers, 0)
+ .withDuration(30000) // 30 seconds for fault testing
+ .addCrashEvent(0, 5000) // Crash leader after 5s
+ .addRecoveryEvent(0, 10000) // Recover after 10s
+ .addCrashEvent(1, 15000) // Crash another server
+ .addRecoveryEvent(1, 20000); // Recover after 20s
+ }
+
+ /**
+ * Create a simple ping-pong simulation
+ * @param numProcesses Number of processes to ping-pong between
+ * @return Configured SimulationBuilder
+ */
+ public static SimulationBuilder createPingPongSimulation(int numProcesses) throws Exception {
+ return new SimulationBuilder()
+ .withProcesses(numProcesses)
+ .withProtocol(SimulationBuilder.Protocols.PING_PONG)
+ .withDuration(5000)
+ .activateServers(IntStream.range(0, numProcesses).toArray());
+ }
+
+ /**
+ * Create a Berkeley time synchronization simulation
+ * @param numProcesses Number of processes to synchronize
+ * @return Configured SimulationBuilder
+ */
+ public static SimulationBuilder createBerkeleyTimeSimulation(int numProcesses) throws Exception {
+ if (numProcesses < 2) {
+ throw new IllegalArgumentException("Berkeley algorithm needs at least 2 processes");
+ }
+
+ return new SimulationBuilder()
+ .withProcesses(numProcesses)
+ .withProtocol(SimulationBuilder.Protocols.BERKLEY_TIME)
+ .withDuration(10000)
+ .activateServers(0) // First process is time server
+ .activateClients(IntStream.range(1, numProcesses).toArray());
+ }
+
+ /**
+ * Create a two-phase commit simulation
+ * @param numParticipants Number of participant processes
+ * @return Configured SimulationBuilder
+ */
+ public static SimulationBuilder createTwoPhaseCommitSimulation(int numParticipants) throws Exception {
+ return new SimulationBuilder()
+ .withProcesses(numParticipants + 1) // +1 for coordinator
+ .withProtocol(SimulationBuilder.Protocols.TWO_PHASE_COMMIT)
+ .withDuration(10000)
+ .activateServers(0) // Process 0 is coordinator
+ .activateClients(300, IntStream.range(1, numParticipants + 1).toArray());
+ }
+
+ /**
+ * Create a reliable multicast simulation
+ * @param numProcesses Number of processes in the multicast group
+ * @return Configured SimulationBuilder
+ */
+ public static SimulationBuilder createReliableMulticastSimulation(int numProcesses) throws Exception {
+ return new SimulationBuilder()
+ .withProcesses(numProcesses)
+ .withProtocol(SimulationBuilder.Protocols.RELIABLE_MULTICAST)
+ .withDuration(10000)
+ .activateServers(IntStream.range(0, numProcesses).toArray());
+ }
+
+ /**
+ * Create a broadcast protocol simulation
+ * @param numProcesses Number of processes
+ * @return Configured SimulationBuilder
+ */
+ public static SimulationBuilder createBroadcastSimulation(int numProcesses) throws Exception {
+ return new SimulationBuilder()
+ .withProcesses(numProcesses)
+ .withProtocol(SimulationBuilder.Protocols.BROADCAST)
+ .withDuration(8000)
+ .activateServers(0) // First process broadcasts
+ .activateClients(IntStream.range(1, numProcesses).toArray());
+ }
+} \ No newline at end of file