diff --git a/src/net/azib/ipscan/core/ScannerThread.java b/src/net/azib/ipscan/core/ScannerThread.java index 82b968bb..9739d765 100755 --- a/src/net/azib/ipscan/core/ScannerThread.java +++ b/src/net/azib/ipscan/core/ScannerThread.java @@ -38,6 +38,7 @@ public class ScannerThread extends Thread { // this thread is daemon because we want JVM to terminate it // automatically if user closes the program (Main thread, that is) setDaemon(true); + this.feeder = feeder; this.scanner = scanner; this.scanningResultList = scanningResults; @@ -47,11 +48,8 @@ public class ScannerThread extends Thread { } public void run() { - stateMachine.transitionTo(ScanningState.SCANNING); - - while(feeder.hasNext() && stateMachine.isState(ScanningState.SCANNING)) { + while(feeder.hasNext() && stateMachine.inState(ScanningState.SCANNING)) { try { - // make a small delay between thread creation Thread.sleep(config.threadDelay); @@ -81,13 +79,13 @@ public class ScannerThread extends Thread { // scan each IP in parallel, in a separate thread new IPThread(address, preparationNumber).start(); } - catch (InterruptedException e) { + catch (InterruptedException e) { return; } } // inform that no more addresses left - stateMachine.transitionTo(ScanningState.STOPPING); + stateMachine.stop(); // now wait for all threads, which are still running try { @@ -104,17 +102,9 @@ public class ScannerThread extends Thread { scanner.cleanup(); // finally, the scanning is complete - stateMachine.transitionTo(ScanningState.IDLE); + stateMachine.complete(); } - - public void forceStop() { - stateMachine.transitionTo(ScanningState.STOPPING); - } - - public void abort() { - stateMachine.transitionTo(ScanningState.KILLING); - } - + // TODO: remove me and change to constructor injection public void setResultsCallback(ScanningResultsCallback resultsCallback) { this.resultsCallback = resultsCallback; diff --git a/src/net/azib/ipscan/core/ScanningProgressCallback.java b/src/net/azib/ipscan/core/ScanningProgressCallback.java index 526f790b..8e8374d2 100755 --- a/src/net/azib/ipscan/core/ScanningProgressCallback.java +++ b/src/net/azib/ipscan/core/ScanningProgressCallback.java @@ -7,23 +7,13 @@ package net.azib.ipscan.core; import java.net.InetAddress; -import net.azib.ipscan.core.state.ScanningState; - /** * This callback is called on scanning state updates. * * @author anton */ public interface ScanningProgressCallback { - - /** - * This method is called on scanner state changes, - * eg. when scanning is about to stop. - * - * @param new state - */ - public void scannerStateChanged(ScanningState state); - + /** * This method is called on scanning progress updates. * There are no guarantees that this method is called on every diff --git a/src/net/azib/ipscan/core/state/ScanningState.java b/src/net/azib/ipscan/core/state/ScanningState.java index 6643a58f..0aa254a6 100644 --- a/src/net/azib/ipscan/core/state/ScanningState.java +++ b/src/net/azib/ipscan/core/state/ScanningState.java @@ -6,8 +6,8 @@ package net.azib.ipscan.core.state; -import java.util.HashSet; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; /** * ScanningState enum - all possible states. @@ -21,7 +21,7 @@ public enum ScanningState { STOPPING, KILLING; - private Set listeners = new HashSet(); + private List listeners = new ArrayList(); /** * Transitions the state to the next one @@ -31,6 +31,10 @@ public enum ScanningState { return states[ordinal()+1 % states.length]; } + public void addTransitionListener(StateTransitionListener listener) { + listeners.add(listener); + } + /** * Notifies all registered listeners of the transition to this state. */ diff --git a/src/net/azib/ipscan/core/state/StateMachine.java b/src/net/azib/ipscan/core/state/StateMachine.java index eb8fdb0d..ab64d9ec 100644 --- a/src/net/azib/ipscan/core/state/StateMachine.java +++ b/src/net/azib/ipscan/core/state/StateMachine.java @@ -6,7 +6,10 @@ package net.azib.ipscan.core.state; -import net.azib.ipscan.core.ScanningProgressCallback; +import java.util.logging.Logger; + +import net.azib.ipscan.config.LoggerFactory; + /** * StateMachine @@ -15,19 +18,15 @@ import net.azib.ipscan.core.ScanningProgressCallback; */ public class StateMachine { - private ScanningProgressCallback progressCallback; + private static final Logger LOG = LoggerFactory.getLogger(); + private ScanningState state = ScanningState.IDLE; - public void transitionTo(ScanningState newState) { - this.state = newState; - progressCallback.scannerStateChanged(newState); - } - /** * @param state * @return true if current state is as specified */ - public boolean isState(ScanningState state) { + public boolean inState(ScanningState state) { return this.state == state; } @@ -38,8 +37,48 @@ public class StateMachine { return state; } - public void setScanningProgressCallback(ScanningProgressCallback progressCallback) { - this.progressCallback = progressCallback; + /** + * Transitions to the specified state, notifying all listeners. + * @param newState + */ + public void transitionTo(ScanningState newState) { + state = newState; + state.notifyOnEntry(); } - + + /** + * Transitions to the next state in the sequence. + * Called when user presses the scan button. + */ + public void transitionToNext() { + // killing state cannot be transitioned from by pressing a button + if (state != ScanningState.KILLING) { + transitionTo(state.next()); + } + } + + /** + * Transitions to the stopping state + */ + public void stop() { + if (state == ScanningState.SCANNING) { + transitionTo(ScanningState.STOPPING); + } + else { + LOG.warning("Attempt to stop from " + state); + } + } + + /** + * Transitions back to the idle state + */ + public void complete() { + if (state == ScanningState.STOPPING || state == ScanningState.KILLING) { + transitionTo(ScanningState.IDLE); + } + else { + LOG.warning("Attempt to complete from " + state); + } + } + } diff --git a/src/net/azib/ipscan/gui/actions/StartStopScanningAction.java b/src/net/azib/ipscan/gui/actions/StartStopScanningAction.java index f4ed28d4..a75f12fd 100755 --- a/src/net/azib/ipscan/gui/actions/StartStopScanningAction.java +++ b/src/net/azib/ipscan/gui/actions/StartStopScanningAction.java @@ -13,6 +13,7 @@ import net.azib.ipscan.core.ScannerThreadFactory; import net.azib.ipscan.core.ScanningProgressCallback; import net.azib.ipscan.core.state.ScanningState; import net.azib.ipscan.core.state.StateMachine; +import net.azib.ipscan.core.state.StateTransitionListener; import net.azib.ipscan.gui.ResultTable; import net.azib.ipscan.gui.ScanningResultsConsumer; import net.azib.ipscan.gui.StatusBar; @@ -30,7 +31,7 @@ import org.eclipse.swt.widgets.Display; * * @author anton */ -public class StartStopScanningAction implements SelectionListener, ScanningProgressCallback { +public class StartStopScanningAction implements SelectionListener, ScanningProgressCallback, StateTransitionListener { private ScannerThreadFactory scannerThreadFactory; private ScannerThread scannerThread; @@ -53,10 +54,7 @@ public class StartStopScanningAction implements SelectionListener, ScanningProgr this.feederRegistry = feederRegistry; this.button = startStopButton; this.display = button.getDisplay(); - this.stateMachine = stateMachine; - // TODO: remove this, use state transition notifications - this.stateMachine.setScanningProgressCallback(this); // pre-load button images buttonImages[ScanningState.IDLE.ordinal()] = new Image(null, Labels.getInstance().getImageAsStream("button.start.img")); @@ -69,6 +67,11 @@ public class StartStopScanningAction implements SelectionListener, ScanningProgr buttonTexts[ScanningState.SCANNING.ordinal()] = Labels.getLabel("button.stop"); buttonTexts[ScanningState.STOPPING.ordinal()] = Labels.getLabel("button.kill"); buttonTexts[ScanningState.KILLING.ordinal()] = Labels.getLabel("button.kill"); + + // add listeners to all state changes + for (ScanningState state : ScanningState.values()) { + state.addTransitionListener(this); + } // set the defaultimage ScanningState state = stateMachine.getState(); @@ -81,31 +84,13 @@ public class StartStopScanningAction implements SelectionListener, ScanningProgr } public void widgetSelected(SelectionEvent e) { - switch (stateMachine.getState()) { - case IDLE: - // start the scan! - resultTable.initNewScan(feederRegistry.current().getInfo()); - ScanningResultsConsumer resultsConsumer = new ScanningResultsConsumer(resultTable); - scannerThread = scannerThreadFactory.createScannerThread(feederRegistry.current().getFeeder(), this); - // TODO: fix this: this is needed here to avoid cylic dependencies... - scannerThread.setResultsCallback(resultsConsumer); - scannerThread.start(); - break; - case SCANNING: - scannerThread.forceStop(); - break; - case STOPPING: - scannerThread.abort(); - break; - case KILLING: - break; - } + stateMachine.transitionToNext(); } - public void scannerStateChanged(final ScanningState state) { + public void transitionTo(final ScanningState state) { if (display.isDisposed()) return; - display.asyncExec(new Runnable() { + display.syncExec(new Runnable() { public void run() { if (statusBar.isDisposed()) return; @@ -116,6 +101,15 @@ public class StartStopScanningAction implements SelectionListener, ScanningProgr statusBar.setStatusText(null); statusBar.setProgress(0); break; + case SCANNING: + // start the scan! + resultTable.initNewScan(feederRegistry.current().getInfo()); + ScanningResultsConsumer resultsConsumer = new ScanningResultsConsumer(resultTable); + scannerThread = scannerThreadFactory.createScannerThread(feederRegistry.current().getFeeder(), StartStopScanningAction.this); + // TODO: fix this: this is needed here to avoid cylic dependencies... + scannerThread.setResultsCallback(resultsConsumer); + scannerThread.start(); + break; case STOPPING: statusBar.setStatusText(Labels.getLabel("state.waitForThreads")); break; @@ -150,5 +144,5 @@ public class StartStopScanningAction implements SelectionListener, ScanningProgr } }); } - + }