diff options
Diffstat (limited to 'docs/decoupling-implementation-guide.md')
| -rw-r--r-- | docs/decoupling-implementation-guide.md | 223 |
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 |
