summaryrefslogtreecommitdiff
path: root/docs/decoupling-implementation-guide.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/decoupling-implementation-guide.md')
-rw-r--r--docs/decoupling-implementation-guide.md223
1 files changed, 223 insertions, 0 deletions
diff --git a/docs/decoupling-implementation-guide.md b/docs/decoupling-implementation-guide.md
new file mode 100644
index 0000000..7896bf9
--- /dev/null
+++ b/docs/decoupling-implementation-guide.md
@@ -0,0 +1,223 @@
+# DS-Sim GUI Decoupling - Implementation Guide
+
+## Overview
+
+This guide provides step-by-step instructions for implementing the GUI decoupling in DS-Sim to eliminate all GUI errors in headless mode.
+
+## Key Principle
+
+The core issue is that `VSSimulatorVisualization` extends `Canvas`, making it inherently a GUI component. Our solution extracts all simulation logic into a separate `SimulationEngine` that has no GUI dependencies.
+
+## Implementation Steps
+
+### Step 1: Create Core Interfaces (✓ Completed)
+
+1. **SimulationEngine.java** - Core simulation operations
+2. **SimulationVisualizer.java** - Observer interface for visualization
+3. **MessageHandler.java** - Message handling abstraction
+
+### Step 2: Implement Headless Engine (✓ Completed)
+
+1. **AbstractSimulationEngine.java** - Base implementation
+2. **HeadlessSimulationEngine.java** - Headless-specific logic
+
+### Step 3: Modify VSInternalProcess
+
+Current code in `VSInternalProcess.sendMessage()`:
+```java
+public void sendMessage(VSMessage message) {
+ incSentMessages();
+ simulatorVisualization.sendMessage(this, destProcess, message, delay);
+}
+```
+
+Modified code:
+```java
+public class VSInternalProcess extends VSAbstractProcess {
+ private MessageHandler messageHandler; // Injected
+
+ public void sendMessage(VSMessage message) {
+ incSentMessages();
+
+ if (messageHandler != null) {
+ messageHandler.handleMessage(message);
+ } else {
+ // Fallback to old behavior for compatibility
+ simulatorVisualization.sendMessage(this, destProcess, message, delay);
+ }
+ }
+
+ public void setMessageHandler(MessageHandler handler) {
+ this.messageHandler = handler;
+ }
+}
+```
+
+### Step 4: Create Message Handler Implementations
+
+```java
+// Headless implementation
+public class HeadlessMessageHandler implements MessageHandler {
+ private final SimulationEngine engine;
+
+ public void handleMessage(VSMessage message) {
+ engine.sendMessage(message); // Pure logic, no visualization
+ }
+
+ public void visualizeMessage(VSMessage message) {
+ // No-op in headless mode
+ }
+}
+
+// Visual implementation
+public class VisualMessageHandler implements MessageHandler {
+ private final SimulationEngine engine;
+ private final VSSimulatorVisualization viz;
+
+ public void handleMessage(VSMessage message) {
+ engine.sendMessage(message);
+ visualizeMessage(message);
+ }
+
+ public void visualizeMessage(VSMessage message) {
+ if (viz.isDisplayable()) {
+ // Create visual message line
+ new VSMessageLine(message, viz);
+ }
+ }
+}
+```
+
+### Step 5: Modify VSSimulatorVisualization
+
+Change the `paint()` method to check for headless mode:
+
+```java
+public void paint() {
+ // Check if we're in headless mode
+ if (Boolean.getBoolean("ds.sim.headless")) {
+ return; // Don't paint in headless mode
+ }
+
+ // Original paint code...
+ while (getBufferStrategy() == null) {
+ createBufferStrategy(3);
+ // ...
+ }
+}
+```
+
+### Step 6: Update VSSimulator Constructor
+
+```java
+public VSSimulator(VSPrefs prefs, VSSimulatorFrame simulatorFrame) {
+ boolean headless = simulatorFrame == null ||
+ Boolean.getBoolean("ds.sim.headless");
+
+ if (headless) {
+ // Create headless engine
+ this.engine = new HeadlessSimulationEngine(prefs, loging);
+ this.messageHandler = new HeadlessMessageHandler(engine);
+ } else {
+ // Create visual engine with visualization
+ this.simulatorVisualization = new VSSimulatorVisualization(prefs, this, loging);
+ this.engine = new VisualizableSimulationEngine(prefs, loging, simulatorVisualization);
+ this.messageHandler = new VisualMessageHandler(engine, simulatorVisualization);
+ }
+}
+```
+
+### Step 7: Create Factory Methods
+
+```java
+public class SimulationFactory {
+ public static VSSimulator createSimulator(VSPrefs prefs, boolean headless) {
+ if (headless) {
+ System.setProperty("ds.sim.headless", "true");
+ return new VSSimulator(prefs, null);
+ } else {
+ VSSimulatorFrame frame = new VSSimulatorFrame(prefs, null);
+ return new VSSimulator(prefs, frame);
+ }
+ }
+}
+```
+
+## Minimal Changes for Immediate Fix
+
+If full refactoring is too extensive, here's a minimal fix:
+
+### Option 1: Modify VSSimulatorVisualization.paint()
+
+Add this at the beginning of the paint() method:
+```java
+public void paint() {
+ // Skip painting in headless mode
+ if (GraphicsEnvironment.isHeadless() ||
+ Boolean.getBoolean("ds.sim.headless") ||
+ !isDisplayable() ||
+ getParent() == null) {
+ return;
+ }
+
+ // Original paint code...
+}
+```
+
+### Option 2: Override paint() in Subclass
+
+Create a headless subclass:
+```java
+public class HeadlessVisualization extends VSSimulatorVisualization {
+ @Override
+ public void paint() {
+ // Do nothing
+ }
+
+ @Override
+ public void sendMessage(VSMessage message) {
+ // Just update counters, no visual elements
+ VSInternalProcess src = getProcess(message.getSourceProcess());
+ VSInternalProcess dst = getProcess(message.getDestProcess());
+ if (src != null) src.incSentMessages();
+ if (dst != null) dst.incReceivedMessages();
+
+ // Schedule delivery without creating visual elements
+ scheduleMessageDelivery(message);
+ }
+}
+```
+
+## Testing the Implementation
+
+1. Run existing GUI tests to ensure compatibility
+2. Run headless tests with no GUI errors:
+ ```bash
+ java -Dds.sim.headless=true -cp target/classes testing.EngineBasedHeadlessRunner
+ ```
+
+## Benefits of Full Implementation
+
+1. **Clean Architecture** - Clear separation of concerns
+2. **No GUI Errors** - True headless operation
+3. **Better Testing** - Can unit test simulation logic without GUI
+4. **Performance** - Headless mode runs faster without painting overhead
+5. **Flexibility** - Easy to add new visualization types
+
+## Risks and Mitigation
+
+1. **Backward Compatibility**
+ - Keep old methods with deprecation warnings
+ - Provide adapter classes for smooth transition
+
+2. **Serialization**
+ - May need to update serialization format
+ - Provide migration tools
+
+3. **Third-party Code**
+ - Document API changes clearly
+ - Provide migration guide
+
+## Conclusion
+
+The full decoupling requires significant changes but results in a much cleaner architecture. The minimal fix options provide immediate relief from GUI errors with less risk. Choose based on available time and risk tolerance. \ No newline at end of file