/*
# Licensed Materials - Property of IBM
# Copyright IBM Corp. 2015
*/
package com.ibm.streamsx.topology.internal.streams;
import static com.ibm.streamsx.topology.internal.streams.InvokeSc.trace;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import com.google.gson.JsonObject;
import com.ibm.streams.operator.logging.TraceLevel;
import com.ibm.streamsx.topology.jobconfig.JobConfig;
import com.ibm.streamsx.topology.jobconfig.SubmissionParameter;
public class InvokeStandalone {
private final File bundle;
// Keep bundle when finished.
private final boolean keepBundle;
private final Map<String,String> envVars = new HashMap<>();
public InvokeStandalone(File bundle, boolean keepBundle) {
super();
this.bundle = bundle;
this.keepBundle = keepBundle;
}
public void addEnvironmentVariable(String key, String value) {
envVars.put(key, value);
}
public Future<Integer> invoke(JsonObject deploy)
throws Exception, InterruptedException {
String si = System.getProperty("java.home");
File jvm = new File(si, "bin/java");
JobConfig jc = JobConfigOverlay.fromFullOverlay(deploy);
List<String> commands = new ArrayList<>();
commands.add(jvm.getAbsolutePath());
commands.add("-jar");
commands.add(bundle.getAbsolutePath());
Level traceLevel = jc.getTracing();
if (traceLevel != null) {
commands.add("-t");
// -t, --trace-level=INT Trace level: 0 - OFF, 1 - ERROR, 2 - WARN,
// 3 - INFO, 4 - DEBUG, 5 - TRACE.
int tli = traceLevel.intValue();
String tls;
if (tli == Level.OFF.intValue())
tls = "0";
else if (tli == Level.ALL.intValue())
tls = "5";
else if (tli >= TraceLevel.ERROR.intValue())
tls = "1";
else if (tli >= TraceLevel.WARN.intValue())
tls = "2";
else if (tli >= TraceLevel.INFO.intValue())
tls = "3";
else if (tli >= TraceLevel.DEBUG.intValue())
tls = "4";
else
tls = "5";
commands.add(tls);
}
if (jc.hasSubmissionParameters()) {
for(SubmissionParameter param : jc.getSubmissionParameters()) {
// note: this execution path does correctly
// handle / preserve the semantics of escaped \t and \n.
// e.g., "\\n" is NOT treated as a newline
// rather it's the two char '\','n'
commands.add(param.getName()+"="+param.getValue());
}
}
trace.info("Invoking standalone application");
trace.info(Util.concatenate(commands));
ProcessBuilder pb = new ProcessBuilder(commands);
pb.inheritIO();
for (Entry<String,String> ev : envVars.entrySet()) {
trace.fine("Setting environment variable for standalone: " + ev.getKey() + "=" + ev.getValue());
pb.environment().put(ev.getKey(), ev.getValue());
}
Process standaloneProcess = pb.start();
return new ProcessFuture(standaloneProcess, keepBundle ? null : bundle);
}
private static class ProcessFuture implements Future<Integer> {
private final Process process;
// Set if bundle to be deleted.
private File bundle;
private int rc;
private boolean isDone;
private boolean isCancelled;
ProcessFuture(Process process, File bundle) {
this.process = process;
this.bundle = bundle;
if (bundle != null)
bundle.deleteOnExit();
}
private void deleteBundle() {
if (bundle != null) {
bundle.delete();
bundle = null;
}
}
@Override
public synchronized boolean cancel(boolean mayInterruptIfRunning) {
if (isDone())
return false;
if (!mayInterruptIfRunning)
return false;
try {
process.destroy();
} finally {
deleteBundle();
}
isCancelled = true;
notifyAll();
return true;
}
@Override
public synchronized boolean isCancelled() {
return isCancelled;
}
@Override
public synchronized boolean isDone() {
boolean done = isDone || isCancelled;
if (done)
deleteBundle();
return done;
}
@Override
public synchronized Integer get() throws InterruptedException,
ExecutionException {
if (isDone())
return rc;
try {
rc = process.waitFor();
} finally {
deleteBundle();
}
trace.info("Standalone application completed: return code=" + rc);
notifyAll();
return rc;
}
@Override
public synchronized Integer get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException,
TimeoutException {
if (!isDone()) {
wait(unit.toMillis(timeout));
}
if (isCancelled())
throw new CancellationException();
if (!isDone())
throw new TimeoutException();
return rc;
}
}
}