From ba9a586f9b2ac1dec8b8a9cb3128abb718c49c1c Mon Sep 17 00:00:00 2001 From: Jan Kozusznik <jan@kozusznik.cz> Date: Thu, 16 Nov 2017 13:51:05 +0100 Subject: [PATCH] list of performed jobs --- .../cz/it4i/fiji/haas/CheckStatusOfHaaS.java | 63 +++++++--- .../fiji/haas/CheckStatusOfHaaSWindow.java | 37 ++++++ .../main/java/cz/it4i/fiji/haas/FXFrame.java | 84 +++++++++++++ .../src/main/java/cz/it4i/fiji/haas/Job.java | 92 +++++++++------ .../java/cz/it4i/fiji/haas/JobManager.java | 60 +++++++++- .../java/cz/it4i/fiji/haas/RunWithHaaS.java | 6 +- .../it4i/fiji/haas/ui/CheckStatusOfHaaS.fxml | 17 +++ .../haas/ui/CheckStatusOfHaaSController.java | 110 ++++++++++++++++++ 8 files changed, 412 insertions(+), 57 deletions(-) create mode 100644 haas-imagej-client/src/main/java/cz/it4i/fiji/haas/CheckStatusOfHaaSWindow.java create mode 100644 haas-imagej-client/src/main/java/cz/it4i/fiji/haas/FXFrame.java create mode 100644 haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/CheckStatusOfHaaS.fxml create mode 100644 haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/CheckStatusOfHaaSController.java diff --git a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/CheckStatusOfHaaS.java b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/CheckStatusOfHaaS.java index b4c6a236..c448de1c 100644 --- a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/CheckStatusOfHaaS.java +++ b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/CheckStatusOfHaaS.java @@ -1,16 +1,24 @@ package cz.it4i.fiji.haas; +import java.awt.Frame; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; +import org.scijava.Context; import org.scijava.command.Command; import org.scijava.log.LogService; import org.scijava.plugin.Parameter; import org.scijava.plugin.Plugin; +import org.scijava.ui.ApplicationFrame; +import org.scijava.ui.UIService; +import org.scijava.widget.UIComponent; +import cz.it4i.fiji.haas.JobManager.JobInfo; +import javafx.application.Platform; import net.imagej.ImageJ; + /** * * @author koz01 @@ -21,29 +29,55 @@ public class CheckStatusOfHaaS extends CommandBase implements Command { @Parameter private LogService log; - - @Parameter(label="Work directory",persist=true) + @Parameter(label = "Work directory", persist = true, style = "directory") private File workDirectory; - - @Parameter(label="Work directory",persist=true,required = false) - private File workDirectory2; - - @SuppressWarnings("unused") + + @Parameter + private UIService uiService; + + @Parameter + private Context context; + private JobManager jobManager; - - - + @Override public void run() { try { - jobManager = new JobManager(getWorkingDirectoryPath(), getGate()); + jobManager = new JobManager(getWorkingDirectoryPath(), context); + if (uiService.isHeadless()) { + downloadAll(); + } else { + CheckStatusOfHaaSWindow window; + (window = new CheckStatusOfHaaSWindow(getFrame(),context)).setVisible(true); + Platform.runLater(() -> jobManager.getJobs().forEach(job -> window.addJob(job))); + } } catch (IOException e) { log.error(e); } + + } + + private Frame getFrame() { + ApplicationFrame af = uiService.getDefaultUI().getApplicationFrame(); + if (af instanceof Frame) { + return (Frame) af; + } else if (af instanceof UIComponent) { + Object component = ((UIComponent<?>) af).getComponent(); + if (component instanceof Frame) { + return (Frame) component; + } + } + return null; + } + + private void downloadAll() { + for (JobInfo id : jobManager.getJobsNeedingDownload()) { + System.out.println("Job " + id.getId() + " needs download"); + jobManager.downloadJob(id.getId()); + } } - private Path getWorkingDirectoryPath() { return Paths.get(workDirectory.toString()); } @@ -52,9 +86,8 @@ public class CheckStatusOfHaaS extends CommandBase implements Command { // Launch ImageJ as usual. final ImageJ ij = new ImageJ(); ij.launch(args); - - //ij.command().run(CheckStatusOfHaaS.class, true); + + // ij.command().run(CheckStatusOfHaaS.class, true); } - } diff --git a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/CheckStatusOfHaaSWindow.java b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/CheckStatusOfHaaSWindow.java new file mode 100644 index 00000000..00928d7f --- /dev/null +++ b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/CheckStatusOfHaaSWindow.java @@ -0,0 +1,37 @@ +package cz.it4i.fiji.haas; + +import java.awt.Frame; + +import org.scijava.Context; +import org.scijava.plugin.Parameter; + +import cz.it4i.fiji.haas.JobManager.JobInfo; +import cz.it4i.fiji.haas.ui.CheckStatusOfHaaSController; + +public class CheckStatusOfHaaSWindow extends FXFrame<CheckStatusOfHaaSController> { + + private static final long serialVersionUID = 1L; + + @Parameter + private Context context; + + private CheckStatusOfHaaSController controller; + public CheckStatusOfHaaSWindow(Frame applicationFrame, Context context) { + super(applicationFrame,"/cz/it4i/fiji/haas/ui/CheckStatusOfHaaS.fxml"); + this.context = context; + init(this::initController); + this.setResizable(false); + this.setTitle("Manage status of HaaS jobs"); + } + + public void addJob(JobInfo job) { + controller.addJob(job); + } + + private void initController(CheckStatusOfHaaSController controller) { + this.controller = controller; + context.inject(controller); + controller.init(); + } + +} diff --git a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/FXFrame.java b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/FXFrame.java new file mode 100644 index 00000000..a1bac967 --- /dev/null +++ b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/FXFrame.java @@ -0,0 +1,84 @@ +package cz.it4i.fiji.haas; + +import java.awt.Dimension; +import java.awt.Frame; +import java.io.IOException; +import java.net.URL; +import java.util.function.Consumer; + +import javax.swing.JDialog; + +import javafx.application.Platform; +import javafx.embed.swing.JFXPanel; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; + +public class FXFrame<C> extends JDialog { + + private static final long serialVersionUID = 1L; + private JFXPanel fxPanel; + private String fxmlFile; + private Consumer<C> controlerInit; + + public FXFrame(String fxmlFile) { + this(null, fxmlFile); + } + + public FXFrame(Frame applicationFrame, String string) { + super(applicationFrame); + fxmlFile = string; + } + + /** + * Create the JFXPanel that make the link between Swing (IJ) and JavaFX plugin. + */ + protected void init( Consumer<C> controlerInit) { + this.controlerInit = controlerInit; + this.fxPanel = new JFXPanel(); + this.add(this.fxPanel); + this.setVisible(true); + + // The call to runLater() avoid a mix between JavaFX thread and Swing thread. + Platform.runLater(new Runnable() { + @Override + public void run() { + initFX(fxPanel); + } + + }); + + } + + private void initFX(JFXPanel fxPanel) { + // Init the root layout + try { + FXMLLoader loader = new FXMLLoader(); + URL res = FXFrame.class.getResource(fxmlFile); + loader.setLocation(res); + Parent rootLayout = (Parent) loader.load(); + + // Get the controller and add an ImageJ context to it. + C controller = loader.<C>getController(); + controlerInit.accept(controller); + + // Show the scene containing the root layout. + Scene scene = new Scene(rootLayout); + this.fxPanel.setScene(scene); + this.fxPanel.setVisible(true); + + // Resize the JFrame to the JavaFX scene + Dimension dim = new Dimension((int) scene.getWidth(), (int) scene.getHeight()); + this.fxPanel.setMinimumSize(dim); + this.fxPanel.setMaximumSize(dim); + this.fxPanel.setPreferredSize(dim); + //this.setSize((int) scene.getWidth(), (int) scene.getHeight()); + this.pack(); + + } catch (IOException e) { + e.printStackTrace(); +} + + } + +} diff --git a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/Job.java b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/Job.java index b85264ee..f5eff4f2 100644 --- a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/Job.java +++ b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/Job.java @@ -17,89 +17,109 @@ import cz.it4i.fiji.haas_java_client.JobState; public class Job { - - private static final String JOB_ID_PROPERTY = "job.id"; private static final String JOB_STATE_PROPERTY = "job.state"; + private static final String JOB_HAS_DATA_TO_DOWNLOAD_PROPERTY = "job.needDownload"; + public static boolean isJobPath(Path p) { return isValidPath(p); } - + private static String JOB_INFO_FILE = ".jobinfo"; - + private Path jobDir; private Supplier<HaaSClient> haasClientSupplier; - + private JobState state; - private ImageJGate gate; - public Job(Path path, Collection<Path> files, Supplier<HaaSClient> haasClientSupplier, ImageJGate gate) throws IOException { - this(haasClientSupplier,gate); + + private Boolean needsDownload; + + private Long jobId; + + public Job(Path path, Collection<Path> files, Supplier<HaaSClient> haasClientSupplier) + throws IOException { + this(haasClientSupplier); HaaSClient client = this.haasClientSupplier.get(); - long id = client.start(files, "TestOutRedirect", - Collections.emptyList()); + long id = client.start(files, "TestOutRedirect", Collections.emptyList()); jobDir = path.resolve("" + id); Files.createDirectory(jobDir); state = client.obtainJobInfo(id).getState(); saveJobinfo(); } - - - public Job(Path p, Supplier<HaaSClient> haasClientSupplier, ImageJGate gate) throws IOException { - this(haasClientSupplier,gate); + public Job(Path p, Supplier<HaaSClient> haasClientSupplier) throws IOException { + this(haasClientSupplier); jobDir = p; loadJobInfo(); - checkStateForDownload(); + updateState(); + } + + private Job(Supplier<HaaSClient> haasClientSupplier) { + this.haasClientSupplier = haasClientSupplier; + } + + public boolean needsDownload() { + return needsDownload != null && needsDownload; } - private synchronized void checkStateForDownload() throws IOException { + synchronized public long getJobId() { + if (jobId == null) { + jobId = getJobId(jobDir); + } + return jobId; + } + + synchronized public void updateState() throws IOException { long jobId = getJobId(); JobState actualState = haasClientSupplier.get().obtainJobInfo(jobId).getState(); - gate.getLog().info("Job: " + jobId + " is " + actualState); - if(EnumSet.of(JobState.Failed, JobState.Finished, JobState.Canceled).contains(actualState) && state != actualState) { - gate.getLog().info("Downloading data."); - haasClientSupplier.get().download(jobId, jobDir); + if (EnumSet.of(JobState.Failed, JobState.Finished, JobState.Canceled).contains(actualState) + && state != actualState) { + needsDownload = true; state = actualState; saveJobinfo(); } } - - - - private Job(Supplier<HaaSClient> haasClientSupplier, ImageJGate gate) { - this.haasClientSupplier = haasClientSupplier; - this.gate = gate; + synchronized public void download() { + if(!needsDownload()) { + throw new IllegalStateException("Job: " + getJobId() + " dosn't need download"); + } + haasClientSupplier.get().download(getJobId(), jobDir); + needsDownload = false; } + public JobState getState() { + return state; + } + private synchronized void saveJobinfo() throws IOException { - try(OutputStream ow= Files.newOutputStream(jobDir.resolve(JOB_INFO_FILE), StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) { + try (OutputStream ow = Files.newOutputStream(jobDir.resolve(JOB_INFO_FILE), + StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) { Properties prop = new Properties(); prop.setProperty(JOB_ID_PROPERTY, "" + getJobId()); prop.setProperty(JOB_STATE_PROPERTY, "" + state); + if (needsDownload != null) { + prop.setProperty(JOB_HAS_DATA_TO_DOWNLOAD_PROPERTY, needsDownload.toString()); + } prop.store(ow, null); } } - - + private synchronized void loadJobInfo() throws IOException { - try(InputStream is= Files.newInputStream(jobDir.resolve(JOB_INFO_FILE))) { + try (InputStream is = Files.newInputStream(jobDir.resolve(JOB_INFO_FILE))) { Properties prop = new Properties(); prop.load(is); state = JobState.valueOf(prop.getProperty(JOB_STATE_PROPERTY)); assert getJobId() == Long.parseLong(prop.getProperty(JOB_ID_PROPERTY)); + if (prop.containsKey(JOB_HAS_DATA_TO_DOWNLOAD_PROPERTY)) { + needsDownload = Boolean.parseBoolean(prop.getProperty(JOB_HAS_DATA_TO_DOWNLOAD_PROPERTY)); + } } } - - private long getJobId() { - return getJobId(jobDir); - } - - private static boolean isValidPath(Path path) { diff --git a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/JobManager.java b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/JobManager.java index d3da7e99..278df42d 100644 --- a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/JobManager.java +++ b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/JobManager.java @@ -4,28 +4,37 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; +import java.util.Iterator; import java.util.LinkedList; +import org.scijava.Context; + import cz.it4i.fiji.haas_java_client.HaaSClient; +import cz.it4i.fiji.haas_java_client.JobState; public class JobManager { + + private Path workDirectory; private Collection<Job> jobs = new LinkedList<>(); private HaaSClient haasClient; - private ImageJGate gate; + private Context context; - public JobManager(Path workDirectory, ImageJGate gate) throws IOException { + + + public JobManager(Path workDirectory, Context ctx) throws IOException { super(); - this.gate = gate; + this.context = ctx; this.workDirectory = workDirectory; + context.inject(this); Files.list(this.workDirectory).filter(p -> Files.isDirectory(p) && Job.isJobPath(p)) .forEach(p -> { try { - jobs.add(new Job(p,this::getHaasClient, gate)); + jobs.add(inject(new Job(p,this::getHaasClient))); } catch (IOException e) { e.printStackTrace(); } @@ -33,8 +42,28 @@ public class JobManager { } + private Job inject(Job job) { + context.inject(job); + return job; + } + public void startJob(Path path, Collection<Path> files) throws IOException { - jobs.add(new Job(path, files,this::getHaasClient,gate)); + jobs.add(new Job(path, files,this::getHaasClient)); + } + + public Iterable<JobInfo> getJobsNeedingDownload() { + return ()->jobs.stream().filter(j->j.needsDownload()).map(j->new JobInfo(j)).iterator(); + } + + public Iterable<JobInfo> getJobs() { + return ()->jobs.stream().map(j->new JobInfo(j)).iterator(); + } + + public void downloadJob(Long id) { + Iterator<Job> job =jobs.stream().filter(j->j.getJobId() == id).iterator(); + assert job.hasNext(); + job.next().download(); + } private HaaSClient getHaasClient() { @@ -44,4 +73,25 @@ public class JobManager { return haasClient; } + public static class JobInfo { + + private Job job; + + + public JobInfo(Job job) { + this.job = job; + } + + public Long getId() { + return job.getJobId(); + } + + public JobState getState() { + return job.getState(); + } + + public boolean needsDownload() { + return job.needsDownload(); + } + } } diff --git a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/RunWithHaaS.java b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/RunWithHaaS.java index b120487c..2728790b 100644 --- a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/RunWithHaaS.java +++ b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/RunWithHaaS.java @@ -8,6 +8,7 @@ import java.nio.file.Paths; import java.util.Collection; import java.util.stream.Collectors; +import org.scijava.Context; import org.scijava.command.Command; import org.scijava.log.LogService; import org.scijava.plugin.Parameter; @@ -31,12 +32,15 @@ public class RunWithHaaS extends CommandBase implements Command { @Parameter(label="Data directory") private File dataDirectory; + @Parameter + private Context context; + private JobManager jobManager; @Override public void run() { try { - jobManager = new JobManager(getWorkingDirectoryPath(), getGate()); + jobManager = new JobManager(getWorkingDirectoryPath(), context); jobManager.startJob(getWorkingDirectoryPath(),getContent(dataDirectory)); } catch (IOException e) { log.error(e); diff --git a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/CheckStatusOfHaaS.fxml b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/CheckStatusOfHaaS.fxml new file mode 100644 index 00000000..7596f26b --- /dev/null +++ b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/CheckStatusOfHaaS.fxml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.scene.control.TableColumn?> +<?import javafx.scene.control.TableView?> +<?import javafx.scene.layout.BorderPane?> + + +<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="cz.it4i.fiji.haas.ui.CheckStatusOfHaaSController"> + <center> + <TableView fx:id="jobs" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER"> + <columns> + <TableColumn prefWidth="75.0" text="Job Id" /> + <TableColumn prefWidth="145.0" text="Status" /> + </columns> + </TableView> + </center> +</BorderPane> diff --git a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/CheckStatusOfHaaSController.java b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/CheckStatusOfHaaSController.java new file mode 100644 index 00000000..73428a1f --- /dev/null +++ b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/CheckStatusOfHaaSController.java @@ -0,0 +1,110 @@ +package cz.it4i.fiji.haas.ui; + + + +import org.scijava.log.LogService; +import org.scijava.plugin.Parameter; + +import cz.it4i.fiji.haas.JobManager.JobInfo; +import javafx.beans.InvalidationListener; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.MenuItem; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableColumn.CellDataFeatures; +import javafx.scene.control.TableView; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.input.ContextMenuEvent; +import javafx.util.Callback; + +public class CheckStatusOfHaaSController { + + + @Parameter + private LogService logService; + + @FXML + private TableView<JobInfo> jobs; + + public CheckStatusOfHaaSController() { + + } + + public void addJob(JobInfo job) { + jobs.getItems().add(job); + } + + @SuppressWarnings("unchecked") + public void init() { + ContextMenu cm = new ContextMenu(); + MenuItem download = new MenuItem("Download"); + cm.getItems().add(download); + ((TableColumn<JobInfo, String>)jobs.getColumns().get(0)).setCellValueFactory(new PropertyValueFactory<JobInfo,String>("id")); + ((TableColumn<JobInfo, String>)jobs.getColumns().get(1)).setCellValueFactory(new P_Factory()); + jobs.setContextMenu(cm); + jobs.setOnContextMenuRequested(new EventHandler<ContextMenuEvent>() { + + @Override + public void handle(ContextMenuEvent event) { + if(jobs.getSelectionModel().getSelectedCells().size() < 1) { + return; + } + int row = jobs.getSelectionModel().getSelectedCells().get(0).getRow(); + + if(0 >= row && row < jobs.getItems().size() && jobs.getItems().get(row).needsDownload()) { + download.setDisable(false); + } else { + download.setDisable(true); + } + + + } + }); + logService.info("init"); + } + + + + private class P_Factory implements Callback<CellDataFeatures<JobInfo, String>, ObservableValue<String>> { + + @Override + public ObservableValue<String> call(final CellDataFeatures<JobInfo, String> param) { + return new ObservableValue<String>() { + + @Override + public void addListener(InvalidationListener listener) { + + + } + + @Override + public void removeListener(InvalidationListener listener) { + + + } + + @Override + public void addListener(ChangeListener<? super String> listener) { + + + } + + @Override + public void removeListener(ChangeListener<? super String> listener) { + + + } + + @Override + public String getValue() { + JobInfo ji = param.getValue(); + return ji.getState().toString() + (ji.needsDownload()?" - needs download":""); + } + }; + } + + } +} -- GitLab