ProcessService extends StatefulService { protected void startProcess(Operation op, ProcessState state) { if (this.stopProcess()) { if ((this.processStopTimeMillis - this.processStartTimeMillis) >= STABLE_THRESHOLD_MILLIS) { this.startDelayExp = 0; } this.startDelayMillis = TimeUnit.SECONDS .toMillis((int) Math.pow(2, this.startDelayExp) - 1); if (this.startDelayMillis < MAX_START_DELAY_MILLIS) { this.startDelayExp++; } else { this.startDelayMillis = MAX_START_DELAY_MILLIS; } if (state.logLink != null && !state.logLink.isEmpty()) { Operation getLog = Operation.createGet(UriUtils.buildUri(this.getHost(), state.logLink, "lineCount=" + LINES)) .setCompletion((o, e) -> { if (e != null) { logWarning("Unable to get logs: %s", e.getMessage()); return; } String json = Utils.toJsonHtml(o.getBodyRaw()); logInfo("%s", json); }); sendRequest(getLog); } setStat(STAT_NAME_START_DELAY_MILLIS, this.startDelayMillis); logWarning("Process %s exited with status %d, restarting in %ds", state.arguments[0], this.processExitStatus, TimeUnit.MILLISECONDS.toSeconds(this.startDelayMillis)); } if ((new Date()).getTime() < this.processStopTimeMillis + this.startDelayMillis) { op.complete(); return; } logInfo("Starting %s", state.arguments[0]); adjustStat(STAT_NAME_START_COUNT, 1); ProcessBuilder pb = new ProcessBuilder(state.arguments); if (state.logFile != null) { File file = new File(state.logFile); pb.redirectErrorStream(true); pb.redirectOutput(Redirect.appendTo(file)); } try { this.process = pb.start(); this.processStartTimeMillis = (new Date()).getTime(); } catch (Exception e) { logWarning("Failure starting %s (%s)", state.arguments[0], e.toString()); op.fail(e); return; } op.complete(); } ProcessService(); @Override void handleStart(Operation op); @Override void handleStop(Operation op); @Override void handleMaintenance(Operation op);  }
@Test public void startProcess() throws Throwable { final ProcessState[] state = { new ProcessState() }; if (host.getSystemInfo().osFamily == OsFamily.WINDOWS) { state[0].arguments = new String[] { "cmd.exe", "/C", "echo" }; } else { state[0].arguments = new String[] { "echo" }; } this.host.testStart(1); final URI[] processURI = { null }; URI uri = UriUtils.buildUri(this.host, ProcessFactoryService.SELF_LINK); Operation op = Operation.createPost(uri) .setBody(state[0]) .setCompletion((o, e) -> { if (e != null) { this.host.failIteration(e); return; } ProcessState newState = o.getBody(ProcessState.class); processURI[0] = UriUtils.buildUri(this.host, newState.documentSelfLink); this.host.completeIteration(); }); this.host.send(op); this.host.testWait(); int startCount; int startDelayMillis; startCount = getStat(processURI[0], ProcessService.STAT_NAME_START_COUNT); assertTrue(startCount >= 1); Date expiration = this.host.getTestExpiration(); while (new Date().before(expiration)) { startCount = getStat(processURI[0], ProcessService.STAT_NAME_START_COUNT); if (startCount >= 3) { startDelayMillis = getStat(processURI[0], ProcessService.STAT_NAME_START_DELAY_MILLIS); assertTrue(startDelayMillis > 0); return; } Thread.sleep(TimeUnit.MICROSECONDS.toMillis(this.host.getMaintenanceIntervalMicros())); } }