-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Extract subprocess running logic into Subprocess * TempDir is now a separate class * Extract Spinner into more general StatusBar * Merge WorkflowCommand core logic into a single method
- Loading branch information
1 parent
4aa8e3d
commit 7b1e602
Showing
4 changed files
with
274 additions
and
192 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package org.ilastik.ilastik4ij.util; | ||
|
||
import org.scijava.app.StatusService; | ||
|
||
import java.util.Collection; | ||
import java.util.Objects; | ||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.ScheduledExecutorService; | ||
import java.util.concurrent.ScheduledFuture; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.function.Consumer; | ||
|
||
/** | ||
* Update status bar while functions are running or collections are iterated. | ||
*/ | ||
public final class StatusBar implements AutoCloseable { | ||
private static final String SPINNER_CHARS = "|/-\\"; | ||
|
||
private final ScheduledExecutorService pool; | ||
private final StatusService status; | ||
private final int period; | ||
|
||
public StatusBar(StatusService statusService, int updatePeriodMillis) { | ||
pool = Executors.newScheduledThreadPool(1); | ||
status = Objects.requireNonNull(statusService); | ||
period = updatePeriodMillis; | ||
if (updatePeriodMillis <= 0) { | ||
throw new IllegalArgumentException("update period should be positive"); | ||
} | ||
} | ||
|
||
@Override | ||
public void close() { | ||
pool.shutdown(); | ||
status.clearStatus(); | ||
} | ||
|
||
/** | ||
* Periodically update status bar from another thread by showing message with textual spinner. | ||
*/ | ||
public void withSpinner(String message, Runnable func) { | ||
Objects.requireNonNull(message); | ||
Objects.requireNonNull(func); | ||
|
||
final int[] index = {0}; | ||
Runnable update = () -> { | ||
status.showStatus(message + " " + SPINNER_CHARS.charAt(index[0])); | ||
index[0] = (index[0] + 1) % SPINNER_CHARS.length(); | ||
}; | ||
|
||
ScheduledFuture<?> sf = pool.scheduleAtFixedRate(update, 0, period, TimeUnit.MILLISECONDS); | ||
try { | ||
func.run(); | ||
} finally { | ||
sf.cancel(true); | ||
} | ||
} | ||
|
||
/** | ||
* Update status bar each time a new collection item is processed. | ||
*/ | ||
public <E> void withProgress(String message, Collection<E> items, Consumer<E> func) { | ||
Objects.requireNonNull(message); | ||
Objects.requireNonNull(items); | ||
Objects.requireNonNull(func); | ||
|
||
int progress = 0; | ||
status.showStatus(progress++, items.size(), message); | ||
for (E item : items) { | ||
func.accept(item); | ||
status.showStatus(progress++, items.size(), message); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package org.ilastik.ilastik4ij.util; | ||
|
||
import java.io.*; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Executors; | ||
import java.util.function.Consumer; | ||
import java.util.function.IntSupplier; | ||
|
||
/** | ||
* Wrapper around {@link ProcessBuilder}. | ||
* Run subprocess with the specified arguments and environment, | ||
* optionally capturing output and error streams. | ||
*/ | ||
public final class Subprocess implements IntSupplier { | ||
private final List<String> args; | ||
private final Map<String, String> env; | ||
private final Consumer<String> stdout; | ||
private final Consumer<String> stderr; | ||
|
||
public Subprocess(String arg) { | ||
this(Collections.singletonList(arg)); | ||
} | ||
|
||
public Subprocess(List<String> args) { | ||
this(args, Collections.emptyMap()); | ||
} | ||
|
||
public Subprocess(List<String> args, Map<String, String> env) { | ||
this(args, env, null); | ||
} | ||
|
||
public Subprocess(List<String> args, Map<String, String> env, Consumer<String> sink) { | ||
this(args, env, sink, sink); | ||
} | ||
|
||
public Subprocess( | ||
List<String> args, | ||
Map<String, String> env, | ||
Consumer<String> stdout, | ||
Consumer<String> stderr) { | ||
this.args = Objects.requireNonNull(args); | ||
this.env = Objects.requireNonNull(env); | ||
this.stdout = stdout; | ||
this.stderr = stderr; | ||
} | ||
|
||
@Override | ||
public int getAsInt() { | ||
ExecutorService pool = Executors.newFixedThreadPool(2); | ||
ProcessBuilder pb = new ProcessBuilder(args); | ||
pb.environment().putAll(env); | ||
try { | ||
Process process = pb.start(); | ||
redirect(pool, process.getInputStream(), stdout); | ||
redirect(pool, process.getErrorStream(), stderr); | ||
return process.waitFor(); | ||
} catch (IOException | InterruptedException e) { | ||
throw new RuntimeException(e); | ||
} finally { | ||
pool.shutdown(); | ||
} | ||
} | ||
|
||
private static void redirect(ExecutorService pool, InputStream src, Consumer<String> dst) { | ||
if (dst == null) { | ||
return; | ||
} | ||
pool.submit(() -> { | ||
try (BufferedReader reader = new BufferedReader( | ||
new InputStreamReader(src, StandardCharsets.UTF_8))) { | ||
reader.lines().forEachOrdered(dst); | ||
} catch (IOException e) { | ||
throw new UncheckedIOException(e); | ||
} | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package org.ilastik.ilastik4ij.util; | ||
|
||
import java.io.IOException; | ||
import java.nio.file.*; | ||
import java.nio.file.attribute.BasicFileAttributes; | ||
import java.util.Objects; | ||
|
||
/** | ||
* Create a temporary directory {@link #path}, and recursively delete it on {@link #close()}. | ||
*/ | ||
public final class TempDir implements AutoCloseable { | ||
private static final FileVisitor<Path> RECURSIVE_DELETE = new SimpleFileVisitor<Path>() { | ||
@Override | ||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { | ||
Files.delete(file); | ||
return FileVisitResult.CONTINUE; | ||
} | ||
|
||
@Override | ||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { | ||
Files.delete(dir); | ||
return FileVisitResult.CONTINUE; | ||
} | ||
}; | ||
|
||
public final Path path; | ||
|
||
public TempDir(String prefix) throws IOException { | ||
path = Files.createTempDirectory(Objects.requireNonNull(prefix)); | ||
} | ||
|
||
@Override | ||
public void close() throws IOException { | ||
Files.walkFileTree(path, RECURSIVE_DELETE); | ||
} | ||
} |
Oops, something went wrong.