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()]);
}
});
}