From 2b5e42a4b82f507b395026ba0c475351e49b5c2d Mon Sep 17 00:00:00 2001 From: Nicolas Chiaruttini Date: Sun, 10 Dec 2023 19:25:56 +0100 Subject: [PATCH 1/2] Adds net.imagej.legacy.task.TaskHelper static class * enables Tasks usage in ij macro language * adds Task example scripts in ij macro language and in groovy, with and without cancellation --- .../net/imagej/legacy/task/TaskHelper.java | 219 ++++++++++++++++++ .../ImageJ_1.x/Task/SimpleTask.groovy | 22 ++ .../ImageJ_1.x/Task/SimpleTask.ijm | 39 ++++ .../ImageJ_1.x/Task/SimpleTaskRawAPI.ijm | 18 ++ .../ImageJ_1.x/Task/TaskWithCancel.groovy | 29 +++ .../ImageJ_1.x/Task/TaskWithCancel.ijm | 44 ++++ .../ImageJ_1.x/Task/Task_ASync.groovy | 62 +++++ 7 files changed, 433 insertions(+) create mode 100644 src/main/java/net/imagej/legacy/task/TaskHelper.java create mode 100644 src/main/resources/script_templates/ImageJ_1.x/Task/SimpleTask.groovy create mode 100644 src/main/resources/script_templates/ImageJ_1.x/Task/SimpleTask.ijm create mode 100644 src/main/resources/script_templates/ImageJ_1.x/Task/SimpleTaskRawAPI.ijm create mode 100644 src/main/resources/script_templates/ImageJ_1.x/Task/TaskWithCancel.groovy create mode 100644 src/main/resources/script_templates/ImageJ_1.x/Task/TaskWithCancel.ijm create mode 100644 src/main/resources/script_templates/ImageJ_1.x/Task/Task_ASync.groovy diff --git a/src/main/java/net/imagej/legacy/task/TaskHelper.java b/src/main/java/net/imagej/legacy/task/TaskHelper.java new file mode 100644 index 000000000..c9d6866b3 --- /dev/null +++ b/src/main/java/net/imagej/legacy/task/TaskHelper.java @@ -0,0 +1,219 @@ +package net.imagej.legacy.task; + +import net.imagej.legacy.LegacyService; +import org.scijava.Context; +import org.scijava.log.LogService; +import org.scijava.object.ObjectService; +import org.scijava.task.Task; +import org.scijava.task.TaskService; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Static methods enabling the use of {@link Task} in ImageJ macro language through the call method: + * output = call('net.imagej.legacy.task.Taskhelper.METHODNAME', args, ...); + * where METHODNAME belong to the {@link Task} interface + *

+ * The ObjectService is used to store tasks created using this static class (within {@link TaskHelper#createTask(String)}) + * Tasks are removed from the ObjectService when {@link TaskHelper#finish(String)} is called. + *

+ * Using this class, it is possible to create two tasks with the same name. Name uniqueness is not guaranteed. + * If two tasks with the same name exist in the ObjectService, a warning message will be displayed via the + * {@link LogService}. It is possible to clear all tasks present in the ObjectService with a specific name + * using {@link TaskHelper#removeAll(String)}. This may be necessary to remove dangling tasks existing after + * a macro has not not been completed successfully, or in the case where calling task.finish was forgotten. + *

+ * The {@link LogService} and {@link ObjectService} are accessed + * statically via the {@link LegacyService}, where a single instance can exist anyway. + *

+ * @author Nicolas Chiaruttini, EPFL, 2023 + */ +@SuppressWarnings("unused") +public class TaskHelper { + // Since the LegacyService exists only once, the LogService and the ObjectService can be accessed statically + static { + Context ctx = LegacyService.getInstance().context(); + logService = ctx.getService(LogService.class); + objectService = ctx.getService(ObjectService.class); + taskService = ctx.getService(TaskService.class); + } + final private static LogService logService; + final private static ObjectService objectService; + final private static TaskService taskService; + + private static Task getTaskNamed(String name) throws IllegalArgumentException { + + List tasks = objectService.getObjects(Task.class) + .stream() + .filter(task -> task.getName().equals(name)).collect(Collectors.toList()); + + if (tasks.size() == 0) { + throw new IllegalArgumentException("No task named "+name+" was found in the object service."); + } + + if (tasks.size() > 1) { + logService.warn("Multiple tasks named "+name+" found! Taking the first one."); + } + + return tasks.get(0); + } + + private static List getTasksNamed(String name) { + return objectService.getObjects(Task.class) + .stream() + .filter(task -> task.getName().equals(name)).collect(Collectors.toList()); + } + + /** + * Returns true as a String if the (first) task named taskName found in the {@link ObjectService} has been cancelled + * Returns false as a String if the (first) task named taskName found in the {@link ObjectService} has not been cancelled + * @param taskName name of the task to act on + * @return true or false as String depending on whether the task named taskName has been cancelled + */ + public static String isCanceled(String taskName) { + try { + Task task = getTaskNamed(taskName); + return task.isCanceled()?"true":"false"; + } catch (IllegalArgumentException e) { + logService.error("Error: task not found. ("+e.getMessage()+")"); + return "Error: task not found. ("+e.getMessage()+")"; + } + } + + /** + * Returns {@link Task#getCancelReason()} on the (first) task named taskName found in the {@link ObjectService} + * @param taskName name of the task to act on + * @return the cancellation reason of the task named taskName + */ + public static String getCancelReason(String taskName) { + try { + Task task = getTaskNamed(taskName); + return task.getCancelReason(); + } catch (IllegalArgumentException e) { + logService.error("Error: task not found. ("+e.getMessage()+")"); + return "Error: task not found. ("+e.getMessage()+")"; + } + } + + /** + * Creates a new {@link Task} named taskNamer and stores it into {@link ObjectService} + * @param taskName name of the task to act on + */ + public static void createTask(String taskName) { + Task task = taskService.createTask(taskName); + objectService.addObject(task); + } + + /** + * Sets the maximal progression of the first task {@link Task} named taskName found in the {@link ObjectService} + * @param taskName name of the task to act on + * @param max the progression maximum (when the task is done) + */ + public static void setProgressMaximum(String taskName, String max) { + try { + Task task = getTaskNamed(taskName); + task.setProgressMaximum(Long.parseLong(max)); + } catch (IllegalArgumentException e) { + logService.error("Error: task not found. ("+e.getMessage()+")"); + } + } + + /** + * Returns {@link Task#getProgressMaximum()} on the (first) task named taskName found in the {@link ObjectService} + * @param taskName name of the task to act on + * @return the current maximal progression of the task named taskName + */ + public static String getProgressMaximum(String taskName) { + try { + Task task = getTaskNamed(taskName); + return Long.toString(task.getProgressMaximum()); + } catch (Exception e) { + logService.error("Error: task not found. ("+e.getMessage()+")"); + return "task "+taskName+" not found."; + } + } + + /** + * Sets the status message of the first task {@link Task} named taskName found in the {@link ObjectService} + * @param taskName name of the task to act on + * @param status the status message to set + */ + public static void setStatusMessage(String taskName, String status) { + try { + Task task = getTaskNamed(taskName); + task.setStatusMessage(status); + } catch (Exception e) { + logService.error("Error: task not found. ("+e.getMessage()+")"); + } + } + + /** + * Returns {@link Task#getStatusMessage()} on the (first) task named taskName found in the {@link ObjectService} + * @param taskName name of the task to act on + * @return the current status message of the task named taskName + */ + public static String getStatusMessage(String taskName) { + try { + Task task = getTaskNamed(taskName); + return task.getStatusMessage(); + } catch (Exception e) { + logService.error("Error: task not found. ("+e.getMessage()+")"); + return "task "+taskName+" not found."; + } + } + + /** + * Calls {@link Task#start()} on the (first) task named taskName found in the {@link ObjectService} + * @param taskName name of the task to act on + */ + public static void start(String taskName) { + try { + Task task = getTaskNamed(taskName); + task.start(); + } catch (Exception e) { + logService.error("Error: task not found. ("+e.getMessage()+")"); + } + } + + /** + * Calls {@link Task#setProgressValue(long)} on the (first) task named taskName found in the {@link ObjectService} + * @param taskName name of the task to act on + * @param step the current task progression value + */ + public static void setProgressValue(String taskName, String step) { + try { + Task task = getTaskNamed(taskName); + task.setProgressValue(Long.parseLong(step)); + } catch (Exception e) { + logService.error("Error: task not found. ("+e.getMessage()+")"); + } + } + + /** + * Calls {@link Task#finish()} on the (first) task named taskName found in the {@link ObjectService} + * @param taskName name of the task to act on + */ + public static void finish(String taskName) { + try { + Task task = getTaskNamed(taskName); + task.finish(); + objectService.removeObject(task); + } catch (Exception e) { + logService.error("Error: task not found. ("+e.getMessage()+")"); + } + } + + /** + * Clean up method to removes tasks dangling after a macro has crashed. + * If several tasks exist with the same name, they will all be removed + * @param taskName name of the tasks to remove + */ + public static void removeAll(String taskName) { + for (Task task: getTasksNamed(taskName)) { + task.finish(); + objectService.removeObject(task); + } + } + +} diff --git a/src/main/resources/script_templates/ImageJ_1.x/Task/SimpleTask.groovy b/src/main/resources/script_templates/ImageJ_1.x/Task/SimpleTask.groovy new file mode 100644 index 000000000..a18fb84f3 --- /dev/null +++ b/src/main/resources/script_templates/ImageJ_1.x/Task/SimpleTask.groovy @@ -0,0 +1,22 @@ +#@ TaskService taskService +#@ Double (label = "Waiting time per step (s)", style = "0.000", value = "0.5") wait_s +#@ Integer(label = "Number of steps", value = "20") n_steps + +def task = taskService.createTask("A task"); +task.setProgressMaximum(n_steps) + +try { + + task.start() + //... do stuff + for (int i = 0; i Date: Thu, 14 Dec 2023 09:32:11 +0100 Subject: [PATCH 2/2] Adds double-checked locking for delayed services initialisation in TaskHelper --- .../net/imagej/legacy/task/TaskHelper.java | 77 +++++++++++++------ 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/src/main/java/net/imagej/legacy/task/TaskHelper.java b/src/main/java/net/imagej/legacy/task/TaskHelper.java index c9d6866b3..54e3d4a2b 100644 --- a/src/main/java/net/imagej/legacy/task/TaskHelper.java +++ b/src/main/java/net/imagej/legacy/task/TaskHelper.java @@ -1,7 +1,6 @@ package net.imagej.legacy.task; import net.imagej.legacy.LegacyService; -import org.scijava.Context; import org.scijava.log.LogService; import org.scijava.object.ObjectService; import org.scijava.task.Task; @@ -31,20 +30,48 @@ */ @SuppressWarnings("unused") public class TaskHelper { + // Since the LegacyService exists only once, the LogService and the ObjectService can be accessed statically - static { - Context ctx = LegacyService.getInstance().context(); - logService = ctx.getService(LogService.class); - objectService = ctx.getService(ObjectService.class); - taskService = ctx.getService(TaskService.class); + private static volatile LogService logService; + private static volatile ObjectService objectService; + private static volatile TaskService taskService; + public static LogService logService() { + if (logService == null) { + synchronized (LogService.class) { + if (logService == null) { + logService = LegacyService.getInstance().log(); + } + } + } + return logService; + } + + public static ObjectService objectService() { + if (objectService == null) { + synchronized (ObjectService.class) { + if (objectService == null) { + objectService = LegacyService.getInstance().context().getService(ObjectService.class); + } + } + } + return objectService; } - final private static LogService logService; - final private static ObjectService objectService; - final private static TaskService taskService; + + public static TaskService taskService() { + if (taskService == null) { + synchronized (TaskService.class) { + if (taskService == null) { + taskService = LegacyService.getInstance().context().getService(TaskService.class); + } + } + } + return taskService; + } + private static Task getTaskNamed(String name) throws IllegalArgumentException { - List tasks = objectService.getObjects(Task.class) + List tasks = objectService().getObjects(Task.class) .stream() .filter(task -> task.getName().equals(name)).collect(Collectors.toList()); @@ -53,14 +80,14 @@ private static Task getTaskNamed(String name) throws IllegalArgumentException { } if (tasks.size() > 1) { - logService.warn("Multiple tasks named "+name+" found! Taking the first one."); + logService().warn("Multiple tasks named "+name+" found! Taking the first one."); } return tasks.get(0); } private static List getTasksNamed(String name) { - return objectService.getObjects(Task.class) + return objectService().getObjects(Task.class) .stream() .filter(task -> task.getName().equals(name)).collect(Collectors.toList()); } @@ -76,7 +103,7 @@ public static String isCanceled(String taskName) { Task task = getTaskNamed(taskName); return task.isCanceled()?"true":"false"; } catch (IllegalArgumentException e) { - logService.error("Error: task not found. ("+e.getMessage()+")"); + logService().error("Error: task not found. ("+e.getMessage()+")"); return "Error: task not found. ("+e.getMessage()+")"; } } @@ -91,7 +118,7 @@ public static String getCancelReason(String taskName) { Task task = getTaskNamed(taskName); return task.getCancelReason(); } catch (IllegalArgumentException e) { - logService.error("Error: task not found. ("+e.getMessage()+")"); + logService().error("Error: task not found. ("+e.getMessage()+")"); return "Error: task not found. ("+e.getMessage()+")"; } } @@ -101,8 +128,8 @@ public static String getCancelReason(String taskName) { * @param taskName name of the task to act on */ public static void createTask(String taskName) { - Task task = taskService.createTask(taskName); - objectService.addObject(task); + Task task = taskService().createTask(taskName); + objectService().addObject(task); } /** @@ -115,7 +142,7 @@ public static void setProgressMaximum(String taskName, String max) { Task task = getTaskNamed(taskName); task.setProgressMaximum(Long.parseLong(max)); } catch (IllegalArgumentException e) { - logService.error("Error: task not found. ("+e.getMessage()+")"); + logService().error("Error: task not found. ("+e.getMessage()+")"); } } @@ -129,7 +156,7 @@ public static String getProgressMaximum(String taskName) { Task task = getTaskNamed(taskName); return Long.toString(task.getProgressMaximum()); } catch (Exception e) { - logService.error("Error: task not found. ("+e.getMessage()+")"); + logService().error("Error: task not found. ("+e.getMessage()+")"); return "task "+taskName+" not found."; } } @@ -144,7 +171,7 @@ public static void setStatusMessage(String taskName, String status) { Task task = getTaskNamed(taskName); task.setStatusMessage(status); } catch (Exception e) { - logService.error("Error: task not found. ("+e.getMessage()+")"); + logService().error("Error: task not found. ("+e.getMessage()+")"); } } @@ -158,7 +185,7 @@ public static String getStatusMessage(String taskName) { Task task = getTaskNamed(taskName); return task.getStatusMessage(); } catch (Exception e) { - logService.error("Error: task not found. ("+e.getMessage()+")"); + logService().error("Error: task not found. ("+e.getMessage()+")"); return "task "+taskName+" not found."; } } @@ -172,7 +199,7 @@ public static void start(String taskName) { Task task = getTaskNamed(taskName); task.start(); } catch (Exception e) { - logService.error("Error: task not found. ("+e.getMessage()+")"); + logService().error("Error: task not found. ("+e.getMessage()+")"); } } @@ -186,7 +213,7 @@ public static void setProgressValue(String taskName, String step) { Task task = getTaskNamed(taskName); task.setProgressValue(Long.parseLong(step)); } catch (Exception e) { - logService.error("Error: task not found. ("+e.getMessage()+")"); + logService().error("Error: task not found. ("+e.getMessage()+")"); } } @@ -198,9 +225,9 @@ public static void finish(String taskName) { try { Task task = getTaskNamed(taskName); task.finish(); - objectService.removeObject(task); + objectService().removeObject(task); } catch (Exception e) { - logService.error("Error: task not found. ("+e.getMessage()+")"); + logService().error("Error: task not found. ("+e.getMessage()+")"); } } @@ -212,7 +239,7 @@ public static void finish(String taskName) { public static void removeAll(String taskName) { for (Task task: getTasksNamed(taskName)) { task.finish(); - objectService.removeObject(task); + objectService().removeObject(task); } }