Task Launcher - refactored odd use-case

This commit is contained in:
dragonmacher 2019-05-23 14:14:58 -04:00
parent 2cf9f7dded
commit 454ce9c817
7 changed files with 80 additions and 95 deletions

View file

@ -32,8 +32,9 @@ public class MoveBlockTask extends ProgramTask {
private Address currentStart; private Address currentStart;
private Address newStart; private Address newStart;
private MoveBlockListener listener; private MoveBlockListener listener;
private boolean wasCancelled; private boolean cancelled;
private boolean status; private String statusMessage;
private boolean success;
/** /**
* Creates a background command for moving memory blocks. The memory block * Creates a background command for moving memory blocks. The memory block
@ -62,58 +63,53 @@ public class MoveBlockTask extends ProgramTask {
Memory mem = program.getMemory(); Memory mem = program.getMemory();
MemoryBlock block = mem.getBlock(currentStart); MemoryBlock block = mem.getBlock(currentStart);
monitor.setMessage("Moving Memory Block ..."); monitor.setMessage("Moving Memory Block ...");
String msg = ""; statusMessage = "";
Throwable cause = null; Throwable cause = null;
try { try {
mem.moveBlock(block, newStart, monitor); mem.moveBlock(block, newStart, monitor);
if (monitor.isCancelled()) { if (monitor.isCancelled()) {
wasCancelled = true; cancelled = true;
} }
else { else {
status = true; success = true;
listener.moveBlockCompleted(this); listener.moveBlockCompleted(this);
return; return;
} }
} }
catch (OutOfMemoryError e) { catch (OutOfMemoryError e) {
msg = "Insufficient memory to complete operation"; statusMessage = "Insufficient memory to complete operation";
cause = e; cause = e;
} }
catch (NotFoundException exc) { catch (NotFoundException e) {
msg = "Memory block not found"; statusMessage = "Memory block not found";
cause = exc; cause = e;
} }
catch (MemoryConflictException exc) { catch (MemoryConflictException | MemoryBlockException | IllegalArgumentException e) {
msg = exc.getMessage(); statusMessage = e.getMessage();
cause = exc;
}
catch (MemoryBlockException exc) {
msg = exc.getMessage();
cause = exc;
}
catch (IllegalArgumentException e) {
msg = e.getMessage();
cause = e; cause = e;
} }
catch (Throwable t) { catch (Throwable t) {
Msg.error(this, "Unexpected Exception: " + t.getMessage(), t); Msg.error(this, "Unexpected Exception: " + t.getMessage(), t);
msg = t.getMessage(); statusMessage = t.getMessage();
if (msg == null) { if (statusMessage == null) {
msg = t.toString(); statusMessage = t.toString();
} }
cause = t; cause = t;
} }
monitor.setMessage(msg);
listener.moveBlockCompleted(this); listener.moveBlockCompleted(this);
throw new RollbackException(msg, cause); throw new RollbackException(statusMessage, cause);
} }
public boolean isCancelled() { public boolean isCancelled() {
return wasCancelled; return cancelled;
} }
public boolean getStatus() { public boolean wasSuccessful() {
return status; return success;
}
public String getStatusMessage() {
return statusMessage;
} }
} }

View file

@ -29,9 +29,9 @@ import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory; import ghidra.program.model.address.AddressFactory;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.Swing;
import ghidra.util.layout.PairLayout; import ghidra.util.layout.PairLayout;
import ghidra.util.task.BackgroundThreadTaskLauncher; import ghidra.util.task.TaskBuilder;
import ghidra.util.task.TaskMonitorAdapter;
/** /**
* Dialog that uses a model to validate the fields for moving a block of memory. * Dialog that uses a model to validate the fields for moving a block of memory.
@ -49,15 +49,6 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc
private MoveBlockModel model; private MoveBlockModel model;
private PluginTool tool; private PluginTool tool;
/**
* Constructor for MoveBlockDialog.
*
* @param dialog
* @param title
* @param modal
* @param includeStatus
* @param includeButtons
*/
MoveBlockDialog(MoveBlockModel model, PluginTool tool) { MoveBlockDialog(MoveBlockModel model, PluginTool tool) {
super("Move Memory Block"); super("Move Memory Block");
this.model = model; this.model = model;
@ -69,33 +60,22 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc
addCancelButton(); addCancelButton();
} }
/**
* @see ghidra.app.plugin.contrib.memory.MoveBlockListener#moveBlockCompleted(boolean,
* java.lang.String)
*/
@Override @Override
public void moveBlockCompleted(final MoveBlockTask cmd) { public void moveBlockCompleted(MoveBlockTask task) {
Runnable r = () -> {
if (cmd.getStatus()) { setCursor(Cursor.getDefaultCursor());
boolean success = task.wasSuccessful();
setOkEnabled(success);
setStatusText(task.getStatusMessage());
Swing.runLater(() -> {
if (success) {
close(); close();
model.dispose(); model.dispose();
} }
else { });
setCursor(Cursor.getDefaultCursor());
setOkEnabled(false);
if (cmd.isCancelled()) {
tool.setStatusInfo(getStatusText());
close();
model.dispose();
}
}
};
SwingUtilities.invokeLater(r);
} }
/**
* @see ghidra.app.plugin.contrib.memory.MoveBlockListener#stateChanged()
*/
@Override @Override
public void stateChanged() { public void stateChanged() {
setOkEnabled(false); setOkEnabled(false);
@ -138,18 +118,20 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc
setOkEnabled(false); setOkEnabled(false);
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
BackgroundThreadTaskLauncher launcher = new BackgroundThreadTaskLauncher(model.makeTask()); MoveBlockTask task = model.makeTask();
launcher.run(new TaskMonitorAdapter() {
@Override //@formatter:off
public void setMessage(String message) { TaskBuilder.withTask(task)
setStatusText(message); .setParent(this.getComponent())
} .launchModal()
}); ;
//@formatter:on
} }
@Override @Override
protected void cancelCallback() { protected void cancelCallback() {
close(); close();
model.dispose();
} }
private JPanel buildMainPanel() { private JPanel buildMainPanel() {

View file

@ -17,15 +17,10 @@ package ghidra.app.plugin.core.memory;
import ghidra.app.cmd.memory.MoveBlockListener; import ghidra.app.cmd.memory.MoveBlockListener;
import ghidra.app.cmd.memory.MoveBlockTask; import ghidra.app.cmd.memory.MoveBlockTask;
import ghidra.framework.model.DomainObject; import ghidra.framework.model.*;
import ghidra.framework.model.DomainObjectChangedEvent; import ghidra.program.model.address.*;
import ghidra.framework.model.DomainObjectListener;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.mem.MemoryBlock;
import ghidra.util.task.Task;
/** /**
* Model for moving a memory block; this class does validation of the new start * Model for moving a memory block; this class does validation of the new start
@ -172,11 +167,11 @@ class MoveBlockModel implements DomainObjectListener {
} }
/** /**
* Start the task that will move the block. * Create the task that will move the block
* *
* @param delay number of ms to delay until the progress dialog is displayed * @return the new task
*/ */
Task makeTask() { MoveBlockTask makeTask() {
return new MoveBlockTask(program, block.getStart(), newStartAddr, listener); return new MoveBlockTask(program, block.getStart(), newStartAddr, listener);
} }

View file

@ -30,7 +30,7 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.mem.MemoryBlock;
import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.TestEnv; import ghidra.test.TestEnv;
import ghidra.util.task.*; import ghidra.util.task.TaskBuilder;
public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
implements MoveBlockListener { implements MoveBlockListener {
@ -42,7 +42,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
private MemoryBlock block; private MemoryBlock block;
private volatile boolean moveCompleted; private volatile boolean moveCompleted;
private volatile boolean status; private volatile boolean success;
private volatile String errMsg; private volatile String errMsg;
private Program buildProgram1(String programName) throws Exception { private Program buildProgram1(String programName) throws Exception {
@ -134,7 +134,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
// wait until the we get the move complete notification // wait until the we get the move complete notification
waitForCondition(() -> moveCompleted && notepad.canLock()); waitForCondition(() -> moveCompleted && notepad.canLock());
assertTrue("Error message= [" + errMsg + "], ", status); assertTrue("Error message= [" + errMsg + "], ", success);
} }
@Test @Test
@ -145,7 +145,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
// wait until the we get the move complete notification // wait until the we get the move complete notification
waitForCondition(() -> moveCompleted && notepad.canLock()); waitForCondition(() -> moveCompleted && notepad.canLock());
assertTrue("Error message= [" + errMsg + "], ", status); assertTrue("Error message= [" + errMsg + "], ", success);
} }
@Test @Test
@ -177,7 +177,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
waitForCondition(() -> moveCompleted && x8051.canLock()); waitForCondition(() -> moveCompleted && x8051.canLock());
setErrorsExpected(false); setErrorsExpected(false);
assertFalse("Error message= [" + errMsg + "], ", status); assertFalse("Error message= [" + errMsg + "], ", success);
} }
@Test @Test
@ -242,15 +242,10 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
assertNotNull(errMsg); assertNotNull(errMsg);
} }
private void launch(Task task) { private void launch(MoveBlockTask task) {
BackgroundThreadTaskLauncher launcher = new BackgroundThreadTaskLauncher(task); TaskBuilder.withTask(task).launchModal();
launcher.run(new TaskMonitorAdapter() { errMsg = task.getStatusMessage();
@Override
public void setMessage(String message) {
errMsg = message;
}
});
} }
private Address getNotepadAddr(int offset) { private Address getNotepadAddr(int offset) {
@ -265,7 +260,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
@Override @Override
public void moveBlockCompleted(MoveBlockTask cmd) { public void moveBlockCompleted(MoveBlockTask cmd) {
moveCompleted = true; moveCompleted = true;
this.status = cmd.getStatus(); this.success = cmd.wasSuccessful();
} }
@Override @Override

View file

@ -221,8 +221,8 @@ public abstract class DataLoadingConstraintEditor<T> extends AbstractColumnConst
reloadDataButton.setVisible(false); reloadDataButton.setVisible(false);
Task task = new LoadDataTask(); Task task = new LoadDataTask();
task.addTaskListener(this); task.addTaskListener(this);
BackgroundThreadTaskLauncher launcher = new BackgroundThreadTaskLauncher(task);
launcher.run(taskMonitorComponent); TaskBuilder.withTask(task).launchInBackground(taskMonitorComponent);
} }
@Override @Override

View file

@ -30,15 +30,15 @@ import ghidra.util.TaskUtilities;
* *
* <p>See {@link TaskLauncher}. * <p>See {@link TaskLauncher}.
*/ */
public class BackgroundThreadTaskLauncher { class BackgroundThreadTaskLauncher {
private Task task; private Task task;
public BackgroundThreadTaskLauncher(Task task) { BackgroundThreadTaskLauncher(Task task) {
this.task = task; this.task = task;
} }
public void run(TaskMonitor monitor) { void run(TaskMonitor monitor) {
// add the task here, so we can track it before it is actually started by the thread // add the task here, so we can track it before it is actually started by the thread
TaskUtilities.addTrackedTask(task, monitor); TaskUtilities.addTrackedTask(task, monitor);
@ -49,5 +49,6 @@ public class BackgroundThreadTaskLauncher {
Thread.currentThread().setName(name); Thread.currentThread().setName(name);
task.monitoredRun(monitor); task.monitoredRun(monitor);
}); });
} }
} }

View file

@ -255,6 +255,22 @@ public class TaskBuilder {
new TaskLauncher(t, parent, delay, dialogWidth); new TaskLauncher(t, parent, delay, dialogWidth);
} }
/**
* Runs the task in a background thread with the given monitor that cannot be null. This
* is a special case for clients that already have a task monitor widget in their UI and
* they wish to let it show the progress of the given task while not blocking the Swing
* thread.
*
* @param monitor the task monitor; may not be null
*/
public void launchInBackground(TaskMonitor monitor) {
// validate(); // not needed since we are in the background
Objects.requireNonNull(monitor);
BackgroundThreadTaskLauncher launcher =
new BackgroundThreadTaskLauncher(new TaskBuilderTask(false));
launcher.run(monitor);
}
private void validate() { private void validate() {
if (title == null) { if (title == null) {
throw new NullPointerException("Task title cannot be null"); throw new NullPointerException("Task title cannot be null");