Refactoring: states now has their special enum: ScanningState. No more int constants. StateMachine introduced, but not used much yet.

git-svn-id: https://ipscan.svn.sourceforge.net/svnroot/ipscan/trunk@135 375186e5-ef17-0410-b0b6-91563547dcda
This commit is contained in:
angryziber 2007-06-24 17:09:32 +00:00
parent 603f7c0357
commit 2c68534ea7
9 changed files with 179 additions and 74 deletions

View File

@ -10,7 +10,7 @@
<attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="ipscan/ext/rocksaw/lib"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="ext/picocontainer/picocontainer-1.0.jar" sourcepath="ext/picocontainer/src"/>
<classpathentry kind="lib" path="ext/picocontainer/picocontainer-1.0.jar"/>
<classpathentry kind="lib" path="ext/swt/swt-gtk.jar"/>
<classpathentry kind="lib" path="ext/swt/swt-mac.jar"/>
<classpathentry kind="lib" path="ext/swt/swt-win32.jar"/>

View File

@ -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);

View File

@ -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;
}
/**

View File

@ -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);
}
}

View File

@ -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.

View File

@ -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<StateTransitionListener> listeners = new HashSet<StateTransitionListener>();
/**
* 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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

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