diff --git a/.classpath b/.classpath index 9c27f6f8..6b9b5d96 100755 --- a/.classpath +++ b/.classpath @@ -10,7 +10,7 @@ - + diff --git a/src/net/azib/ipscan/config/ComponentRegistry.java b/src/net/azib/ipscan/config/ComponentRegistry.java index cceff79f..4d3958d6 100755 --- a/src/net/azib/ipscan/config/ComponentRegistry.java +++ b/src/net/azib/ipscan/config/ComponentRegistry.java @@ -10,6 +10,7 @@ import net.azib.ipscan.core.ScannerThreadFactory; import net.azib.ipscan.core.ScanningResultList; import net.azib.ipscan.core.net.PingerRegistry; import net.azib.ipscan.core.net.PingerRegistryImpl; +import net.azib.ipscan.core.state.StateMachine; import net.azib.ipscan.exporters.CSVExporter; import net.azib.ipscan.exporters.ExporterRegistry; import net.azib.ipscan.exporters.IPListExporter; @@ -92,6 +93,7 @@ public class ComponentRegistry { container.registerComponentImplementation(PingerRegistry.class, PingerRegistryImpl.class); container.registerComponentImplementation(ScanningResultList.class); container.registerComponentImplementation(Scanner.class); + container.registerComponentImplementation(StateMachine.class); container.registerComponentImplementation(ScannerThreadFactory.class); // GUI follows (TODO: move GUI to a separate place) @@ -157,7 +159,7 @@ public class ComponentRegistry { container.registerComponentImplementation(OptionsDialog.class); container.registerComponentImplementation(SelectFetchersDialog.class); - + // various actions / listener container.registerComponentImplementation(StartStopScanningAction.class); container.registerComponentImplementation(ColumnsActions.SortBy.class); diff --git a/src/net/azib/ipscan/core/ScannerThread.java b/src/net/azib/ipscan/core/ScannerThread.java index 37d55e27..82b968bb 100755 --- a/src/net/azib/ipscan/core/ScannerThread.java +++ b/src/net/azib/ipscan/core/ScannerThread.java @@ -8,6 +8,8 @@ package net.azib.ipscan.core; import java.net.InetAddress; import net.azib.ipscan.config.GlobalConfig; +import net.azib.ipscan.core.state.ScanningState; +import net.azib.ipscan.core.state.StateMachine; import net.azib.ipscan.feeders.Feeder; /** @@ -18,18 +20,20 @@ import net.azib.ipscan.feeders.Feeder; public class ScannerThread extends Thread { private Scanner scanner; + private StateMachine stateMachine; private ScanningResultList scanningResultList; private Feeder feeder; - private ScanningStateCallback statusCallback; + private ScanningProgressCallback progressCallback; private ScanningResultsCallback resultsCallback; - private int state; private int runningThreads; private GlobalConfig config; - public ScannerThread(Feeder feeder, Scanner scanner, ScanningResultList scanningResults, GlobalConfig globalConfig) { + public ScannerThread(Feeder feeder, Scanner scanner, StateMachine stateMachine, ScanningProgressCallback progressCallback, ScanningResultList scanningResults, GlobalConfig globalConfig) { super("Scanner Thread"); this.config = globalConfig; + this.stateMachine = stateMachine; + this.progressCallback = progressCallback; // this thread is daemon because we want JVM to terminate it // automatically if user closes the program (Main thread, that is) @@ -43,9 +47,9 @@ public class ScannerThread extends Thread { } public void run() { - changeStatus(ScanningStateCallback.STATE_SCANNING); + stateMachine.transitionTo(ScanningState.SCANNING); - while(feeder.hasNext() && state == ScanningStateCallback.STATE_SCANNING) { + while(feeder.hasNext() && stateMachine.isState(ScanningState.SCANNING)) { try { // make a small delay between thread creation @@ -72,7 +76,7 @@ public class ScannerThread extends Thread { int preparationNumber = resultsCallback.prepareForResults(address); // notify listeners of the progress we are doing - statusCallback.updateProgress(address, runningThreads, feeder.getPercentageComplete()); + progressCallback.updateProgress(address, runningThreads, feeder.getPercentageComplete()); // scan each IP in parallel, in a separate thread new IPThread(address, preparationNumber).start(); @@ -83,14 +87,14 @@ public class ScannerThread extends Thread { } // inform that no more addresses left - changeStatus(ScanningStateCallback.STATE_STOPPING); + stateMachine.transitionTo(ScanningState.STOPPING); // now wait for all threads, which are still running try { // TODO: make a better and safer implementation while (runningThreads > 0) { Thread.sleep(200); - statusCallback.updateProgress(null, runningThreads, 100); + progressCallback.updateProgress(null, runningThreads, 100); } } catch (InterruptedException e) { @@ -100,28 +104,20 @@ public class ScannerThread extends Thread { scanner.cleanup(); // finally, the scanning is complete - changeStatus(ScanningStateCallback.STATE_IDLE); + stateMachine.transitionTo(ScanningState.IDLE); } - - private void changeStatus(int status) { - this.state = status; - statusCallback.scannerStateChanged(status); - } - + public void forceStop() { - changeStatus(ScanningStateCallback.STATE_STOPPING); + stateMachine.transitionTo(ScanningState.STOPPING); } public void abort() { - changeStatus(ScanningStateCallback.STATE_KILLING); - } - - public void setResultsCallback(ScanningResultsCallback resultsCallback) { - this.resultsCallback = resultsCallback; + stateMachine.transitionTo(ScanningState.KILLING); } - public void setStatusCallback(ScanningStateCallback statusCallback) { - this.statusCallback = statusCallback; + // TODO: remove me and change to constructor injection + public void setResultsCallback(ScanningResultsCallback resultsCallback) { + this.resultsCallback = resultsCallback; } /** diff --git a/src/net/azib/ipscan/core/ScannerThreadFactory.java b/src/net/azib/ipscan/core/ScannerThreadFactory.java index f2b4fdf8..3bd41713 100755 --- a/src/net/azib/ipscan/core/ScannerThreadFactory.java +++ b/src/net/azib/ipscan/core/ScannerThreadFactory.java @@ -6,10 +6,13 @@ package net.azib.ipscan.core; import net.azib.ipscan.config.GlobalConfig; +import net.azib.ipscan.core.state.StateMachine; import net.azib.ipscan.feeders.Feeder; /** - * ScannerThreadFactory + * ScannerThreadFactory. + * + * Note: setter injection is used for this class to avoid cyclic dependency conflicts. * * @author anton */ @@ -17,16 +20,17 @@ public class ScannerThreadFactory { private ScanningResultList scanningResults; private Scanner scanner; + private StateMachine stateMachine; private GlobalConfig globalConfig; - - public ScannerThreadFactory(ScanningResultList scanningResults, Scanner scanner, GlobalConfig globalConfig) { + + public ScannerThreadFactory(ScanningResultList scanningResults, Scanner scanner, StateMachine stateMachine, GlobalConfig globalConfig) { this.scanningResults = scanningResults; this.scanner = scanner; + this.stateMachine = stateMachine; this.globalConfig = globalConfig; } - public ScannerThread createScannerThread(Feeder feeder) { - return new ScannerThread(feeder, scanner, scanningResults, globalConfig); + public ScannerThread createScannerThread(Feeder feeder, ScanningProgressCallback progressCallback) { + return new ScannerThread(feeder, scanner, stateMachine, progressCallback, scanningResults, globalConfig); } - } diff --git a/src/net/azib/ipscan/core/ScanningStateCallback.java b/src/net/azib/ipscan/core/ScanningProgressCallback.java similarity index 61% rename from src/net/azib/ipscan/core/ScanningStateCallback.java rename to src/net/azib/ipscan/core/ScanningProgressCallback.java index ad95fd2b..526f790b 100755 --- a/src/net/azib/ipscan/core/ScanningStateCallback.java +++ b/src/net/azib/ipscan/core/ScanningProgressCallback.java @@ -7,26 +7,22 @@ package net.azib.ipscan.core; import java.net.InetAddress; +import net.azib.ipscan.core.state.ScanningState; + /** - * This callback is called on scanning status updates. + * This callback is called on scanning state updates. * * @author anton */ -public interface ScanningStateCallback { - - public static final int STATE_IDLE = 0; - public static final int STATE_SCANNING = 1; - public static final int STATE_STOPPING = 2; - public static final int STATE_KILLING = 3; - +public interface ScanningProgressCallback { + /** - * This method is called on scanner status changes, + * This method is called on scanner state changes, * eg. when scanning is about to stop. * - * @param status integer value of current status, having the - * corresponding STATE_XXX constant + * @param new state */ - public void scannerStateChanged(int status); + public void scannerStateChanged(ScanningState state); /** * This method is called on scanning progress updates. diff --git a/src/net/azib/ipscan/core/state/ScanningState.java b/src/net/azib/ipscan/core/state/ScanningState.java new file mode 100644 index 00000000..6643a58f --- /dev/null +++ b/src/net/azib/ipscan/core/state/ScanningState.java @@ -0,0 +1,43 @@ +/** + * This file is a part of Angry IP Scanner source code, + * see http://www.azib.net/ for more information. + * Licensed under GPLv2. + */ + +package net.azib.ipscan.core.state; + +import java.util.HashSet; +import java.util.Set; + +/** + * ScanningState enum - all possible states. + * + * @author Anton Keks + */ +public enum ScanningState { + + IDLE, + SCANNING, + STOPPING, + KILLING; + + private Set listeners = new HashSet(); + + /** + * Transitions the state to the next one + */ + public ScanningState next() { + ScanningState[] states = values(); + return states[ordinal()+1 % states.length]; + } + + /** + * Notifies all registered listeners of the transition to this state. + */ + public void notifyOnEntry() { + for (StateTransitionListener listener : listeners) { + listener.transitionTo(this); + } + } + +} diff --git a/src/net/azib/ipscan/core/state/StateMachine.java b/src/net/azib/ipscan/core/state/StateMachine.java new file mode 100644 index 00000000..fd51aef1 --- /dev/null +++ b/src/net/azib/ipscan/core/state/StateMachine.java @@ -0,0 +1,38 @@ +/** + * This file is a part of Angry IP Scanner source code, + * see http://www.azib.net/ for more information. + * Licensed under GPLv2. + */ + +package net.azib.ipscan.core.state; + +import net.azib.ipscan.core.ScanningProgressCallback; + +/** + * StateMachine + * + * @author Anton Keks + */ +public class StateMachine { + + private ScanningProgressCallback progressCallback; + private ScanningState state; + + 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) { + return this.state == state; + } + + public void setScanningProgressCallback(ScanningProgressCallback progressCallback) { + this.progressCallback = progressCallback; + } + +} diff --git a/src/net/azib/ipscan/core/state/StateTransitionListener.java b/src/net/azib/ipscan/core/state/StateTransitionListener.java new file mode 100644 index 00000000..492a6d8f --- /dev/null +++ b/src/net/azib/ipscan/core/state/StateTransitionListener.java @@ -0,0 +1,22 @@ +/** + * This file is a part of Angry IP Scanner source code, + * see http://www.azib.net/ for more information. + * Licensed under GPLv2. + */ + +package net.azib.ipscan.core.state; + +/** + * StateTransitionListener + * + * @author Anton Keks + */ +public interface StateTransitionListener { + + /** + * Notifies on transition to the specified state. + * @param state + */ + public void transitionTo(ScanningState state); + +} diff --git a/src/net/azib/ipscan/gui/actions/StartStopScanningAction.java b/src/net/azib/ipscan/gui/actions/StartStopScanningAction.java index 726bb623..35bd5cc8 100755 --- a/src/net/azib/ipscan/gui/actions/StartStopScanningAction.java +++ b/src/net/azib/ipscan/gui/actions/StartStopScanningAction.java @@ -10,7 +10,9 @@ import java.net.InetAddress; import net.azib.ipscan.config.Labels; import net.azib.ipscan.core.ScannerThread; import net.azib.ipscan.core.ScannerThreadFactory; -import net.azib.ipscan.core.ScanningStateCallback; +import net.azib.ipscan.core.ScanningProgressCallback; +import net.azib.ipscan.core.state.ScanningState; +import net.azib.ipscan.core.state.StateMachine; import net.azib.ipscan.gui.ResultTable; import net.azib.ipscan.gui.ScanningResultsConsumer; import net.azib.ipscan.gui.StatusBar; @@ -28,7 +30,7 @@ import org.eclipse.swt.widgets.Display; * * @author anton */ -public class StartStopScanningAction implements SelectionListener, ScanningStateCallback { +public class StartStopScanningAction implements SelectionListener, ScanningProgressCallback { private ScannerThreadFactory scannerThreadFactory; private ScannerThread scannerThread; @@ -37,14 +39,14 @@ public class StartStopScanningAction implements SelectionListener, ScanningState private ResultTable resultTable; private FeederGUIRegistry feederRegistry; private Button button; - private Image[] buttonImages = new Image[4]; - private String[] buttonTexts = new String[4]; + private Image[] buttonImages = new Image[ScanningState.values().length]; + private String[] buttonTexts = new String[ScanningState.values().length]; private Display display; - private int state = ScanningStateCallback.STATE_IDLE; + private ScanningState state = ScanningState.IDLE; - public StartStopScanningAction(ScannerThreadFactory scannerThreadFactory, ResultTable resultTable, StatusBar statusBar, FeederGUIRegistry feederRegistry, Button startStopButton) { + public StartStopScanningAction(ScannerThreadFactory scannerThreadFactory, StateMachine stateMachine, ResultTable resultTable, StatusBar statusBar, FeederGUIRegistry feederRegistry, Button startStopButton) { this.scannerThreadFactory = scannerThreadFactory; this.resultTable = resultTable; this.statusBar = statusBar; @@ -52,21 +54,23 @@ public class StartStopScanningAction implements SelectionListener, ScanningState this.button = startStopButton; this.display = button.getDisplay(); + stateMachine.setScanningProgressCallback(this); + // pre-load button images - buttonImages[ScanningStateCallback.STATE_IDLE] = new Image(null, Labels.getInstance().getImageAsStream("button.start.img")); - buttonImages[ScanningStateCallback.STATE_SCANNING] = new Image(null, Labels.getInstance().getImageAsStream("button.stop.img")); - buttonImages[ScanningStateCallback.STATE_STOPPING] = new Image(null, Labels.getInstance().getImageAsStream("button.kill.img")); - buttonImages[ScanningStateCallback.STATE_KILLING] = buttonImages[ScanningStateCallback.STATE_STOPPING]; + buttonImages[ScanningState.IDLE.ordinal()] = new Image(null, Labels.getInstance().getImageAsStream("button.start.img")); + buttonImages[ScanningState.SCANNING.ordinal()] = new Image(null, Labels.getInstance().getImageAsStream("button.stop.img")); + buttonImages[ScanningState.STOPPING.ordinal()] = new Image(null, Labels.getInstance().getImageAsStream("button.kill.img")); + buttonImages[ScanningState.KILLING.ordinal()] = buttonImages[ScanningState.STOPPING.ordinal()]; // pre-load button texts - buttonTexts[ScanningStateCallback.STATE_IDLE] = Labels.getLabel("button.start"); - buttonTexts[ScanningStateCallback.STATE_SCANNING] = Labels.getLabel("button.stop"); - buttonTexts[ScanningStateCallback.STATE_STOPPING] = Labels.getLabel("button.kill"); - buttonTexts[ScanningStateCallback.STATE_KILLING] = Labels.getLabel("button.kill"); + buttonTexts[ScanningState.IDLE.ordinal()] = Labels.getLabel("button.start"); + buttonTexts[ScanningState.SCANNING.ordinal()] = Labels.getLabel("button.stop"); + buttonTexts[ScanningState.STOPPING.ordinal()] = Labels.getLabel("button.kill"); + buttonTexts[ScanningState.KILLING.ordinal()] = Labels.getLabel("button.kill"); // set the defaultimage - button.setImage(buttonImages[state]); - button.setText(buttonTexts[state]); + button.setImage(buttonImages[state.ordinal()]); + button.setText(buttonTexts[state.ordinal()]); } public void widgetDefaultSelected(SelectionEvent e) { @@ -75,28 +79,28 @@ public class StartStopScanningAction implements SelectionListener, ScanningState public void widgetSelected(SelectionEvent e) { switch (state) { - case ScanningStateCallback.STATE_IDLE: + case IDLE: // start the scan! resultTable.initNewScan(feederRegistry.current().getInfo()); ScanningResultsConsumer resultsConsumer = new ScanningResultsConsumer(resultTable); - scannerThread = scannerThreadFactory.createScannerThread(feederRegistry.current().getFeeder()); + scannerThread = scannerThreadFactory.createScannerThread(feederRegistry.current().getFeeder(), this); + // TODO: fix this: this is needed here to avoid cylic dependencies... scannerThread.setResultsCallback(resultsConsumer); - scannerThread.setStatusCallback(this); scannerThread.start(); break; - case ScanningStateCallback.STATE_SCANNING: + case SCANNING: scannerThread.forceStop(); break; - case ScanningStateCallback.STATE_STOPPING: + case STOPPING: scannerThread.abort(); break; - case ScanningStateCallback.STATE_KILLING: + case KILLING: break; } } - public void scannerStateChanged(int status) { - this.state = status; + public void scannerStateChanged(ScanningState newState) { + this.state = newState; if (display.isDisposed()) return; display.asyncExec(new Runnable() { @@ -104,22 +108,22 @@ public class StartStopScanningAction implements SelectionListener, ScanningState if (statusBar.isDisposed()) return; - switch (StartStopScanningAction.this.state) { - case STATE_IDLE: + switch (state) { + case IDLE: // reset state text statusBar.setStatusText(null); statusBar.setProgress(0); break; - case STATE_STOPPING: + case STOPPING: statusBar.setStatusText(Labels.getLabel("state.waitForThreads")); break; - case STATE_KILLING: + case KILLING: statusBar.setStatusText(Labels.getLabel("state.killingThreads")); break; } // change button image - button.setImage(buttonImages[StartStopScanningAction.this.state]); - button.setText(buttonTexts[StartStopScanningAction.this.state]); + button.setImage(buttonImages[state.ordinal()]); + button.setText(buttonTexts[state.ordinal()]); } }); } @@ -140,7 +144,7 @@ public class StartStopScanningAction implements SelectionListener, ScanningState statusBar.setProgress(percentageComplete); // change button image - button.setImage(buttonImages[StartStopScanningAction.this.state]); + button.setImage(buttonImages[state.ordinal()]); } }); }