Skip to content

Commit c59e900

Browse files
author
Mark A. Matney, Jr
authored
[SERV-126] Allow audiowaveform processes to exit (#7)
* Make WaveformVerticle a worker verticle * Fix IndexOutOfBoundsError when input CSV doesn't have IIIF Access URL
1 parent e558675 commit c59e900

File tree

5 files changed

+44
-25
lines changed

5 files changed

+44
-25
lines changed

src/main/java/edu/ucla/library/avpairtree/Config.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ public final class Config {
7777
*/
7878
public static final String CONVERSION_WORKERS = "conversion.workers";
7979

80+
/**
81+
* The number of workers that should work to do audiowaveform generation.
82+
*/
83+
public static final String WAVEFORM_WORKERS = "waveform.workers";
84+
8085
/**
8186
* The environment variable for the S3 bucket for audio waveforms.
8287
*/

src/main/java/edu/ucla/library/avpairtree/verticles/MainVerticle.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ private void configureServer(final JsonObject aConfig, final Promise<Void> aProm
132132
futures.add(deployVerticle(new WatcherVerticle(), aConfig));
133133
futures.add(deployVerticle(new PairtreeVerticle(), aConfig));
134134
futures.add(deployVerticle(new ConverterVerticle(), aConfig.copy().put(WORKER, true)));
135-
futures.add(deployVerticle(new WaveformVerticle(), aConfig));
135+
futures.add(deployVerticle(new WaveformVerticle(), aConfig.copy().put(WORKER, true)));
136136

137137
CompositeFuture.all(futures).onSuccess(result -> {
138138
startCsvDirWatcher(aConfig).onComplete(startup -> {
@@ -166,8 +166,17 @@ private Future<Void> deployVerticle(final Verticle aVerticle, final JsonObject a
166166

167167
// If the configuration for this verticle mentions it should be a worker, find out how many to set
168168
if (aConfig.getBoolean(WORKER, false)) {
169+
final int nWorkerThreads;
170+
171+
if (ConverterVerticle.class.equals(aVerticle.getClass())) {
172+
nWorkerThreads = aConfig.getInteger(Config.CONVERSION_WORKERS, DEFAULT_WORKER_COUNT);
173+
} else if (WaveformVerticle.class.equals(aVerticle.getClass())) {
174+
nWorkerThreads = aConfig.getInteger(Config.WAVEFORM_WORKERS, DEFAULT_WORKER_COUNT);
175+
} else {
176+
nWorkerThreads = DEFAULT_WORKER_COUNT;
177+
}
169178
options.setWorker(true).setWorkerPoolName(aVerticle.getClass().getSimpleName());
170-
options.setWorkerPoolSize(aConfig.getInteger(Config.CONVERSION_WORKERS, DEFAULT_WORKER_COUNT));
179+
options.setWorkerPoolSize(nWorkerThreads);
171180
options.setMaxWorkerExecuteTime(Integer.MAX_VALUE).setMaxWorkerExecuteTimeUnit(TimeUnit.MINUTES);
172181

173182
LOGGER.debug(MessageCodes.AVPT_012, options.getWorkerPoolName(), options.getWorkerPoolSize());

src/main/java/edu/ucla/library/avpairtree/verticles/WatcherVerticle.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,12 @@ private Future<String> updateCSV(final String aCsvFilePath, final Map<String, Cs
200200
if (aCsvItemMap.containsKey(ark)) {
201201
row[index] = constructAccessURL(csvItem);
202202
} else {
203-
// Don't overwrite what may already be there (e.g. in the case of images)
204-
row[index] = originalRow.get(index + 1);
203+
if (originalAccessUrlIndex != -1) {
204+
// Don't overwrite what was already there (e.g. in the case of images)
205+
row[index] = originalRow.get(index + 1);
206+
} else {
207+
row[index] = "";
208+
}
205209
}
206210
} else if (waveformIndex == index) {
207211
row[index] = aWaveformMap.getString(ark, "");

src/main/java/edu/ucla/library/avpairtree/verticles/WaveformVerticle.java

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,14 @@ public void start(final Promise<Void> aPromise) {
5959
final JsonObject config = config();
6060
final String[] cmd = { "which", AUDIOWAVEFORM };
6161
final String cmdline = String.join(SPACE, cmd);
62-
final ProcessBuilder pb = new ProcessBuilder(cmd).redirectOutput(ProcessBuilder.Redirect.PIPE);
63-
6462
final String configErrorMsg;
6563

64+
LOGGER.debug(MessageCodes.AVPT_011, WaveformVerticle.class.getSimpleName(), Thread.currentThread().getName());
65+
6666
// Make sure that audiowaveform is installed on the system
6767

6868
try {
69-
final Process which = pb.start();
69+
final Process which = new ProcessBuilder(cmd).start();
7070
final int exitValue = which.waitFor();
7171
final InputStream stdout;
7272
final String cmdResult;
@@ -202,30 +202,28 @@ private Future<ByteBuffer> audiowaveform(final Path anAudioFilePath) throws IOEx
202202
final String[] cmd = { AUDIOWAVEFORM, "--input-filename", anAudioFilePath.toString(), "--output-format", "dat",
203203
"--bits", "8" };
204204
final String cmdline = String.join(SPACE, cmd);
205-
final ProcessBuilder pb = new ProcessBuilder(cmd).redirectOutput(ProcessBuilder.Redirect.PIPE);
206205

207206
try {
208-
pb.start().onExit().thenAccept(subprocess -> {
209-
try (InputStream stdout = subprocess.getInputStream();
210-
InputStream stderr = subprocess.getErrorStream()) {
211-
212-
final int exitValue = subprocess.exitValue();
207+
final Process audiowaveform = new ProcessBuilder(cmd).start();
213208

214-
if (0 == exitValue) {
215-
// Redact the binary audiowaveform data for logging
216-
final String cmdResultMsg = LOGGER.getMessage(MessageCodes.AVPT_015, cmdline, exitValue,
217-
"[binary audiowaveform data]");
209+
// Unless we read its output before calling `onExit()`, the audiowaveform process will stay asleep until it
210+
// receives an interrupt signal
211+
final byte[] stdout = audiowaveform.getInputStream().readAllBytes();
212+
final String stderr = new String(audiowaveform.getErrorStream().readAllBytes(), StandardCharsets.UTF_8);
218213

219-
LOGGER.debug(cmdResultMsg);
214+
audiowaveform.onExit().thenAccept(process -> {
215+
final int exitValue = process.exitValue();
220216

221-
asyncResult.complete(ByteBuffer.wrap(stdout.readAllBytes()));
222-
} else {
223-
final String errorOutput = new String(stderr.readAllBytes());
224-
225-
asyncResult.fail(LOGGER.getMessage(MessageCodes.AVPT_015, cmdline, exitValue, errorOutput));
217+
if (0 == exitValue) {
218+
for (final String line : stderr.split("\\r?\\n")) {
219+
LOGGER.debug(line);
226220
}
227-
} catch (final IOException details) {
228-
asyncResult.fail(LOGGER.getMessage(MessageCodes.AVPT_016, cmdline, details));
221+
// Redact the binary audiowaveform data for logging
222+
LOGGER.debug(MessageCodes.AVPT_015, cmdline, exitValue, "[binary audiowaveform data]");
223+
224+
asyncResult.complete(ByteBuffer.wrap(stdout));
225+
} else {
226+
asyncResult.fail(LOGGER.getMessage(MessageCodes.AVPT_015, cmdline, exitValue, stderr));
229227
}
230228
});
231229
} catch (final IOException details) {

src/test/resources/test-config.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,6 @@ iiif.access.url.id.index = 1
2828

2929
# The number of threads working on media file conversions
3030
conversion.workers = 2
31+
32+
# The number of threads working on audiowaveform generation
33+
waveform.workers = 2

0 commit comments

Comments
 (0)