package bndtools.launch.bnd;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import org.bndtools.api.BndtoolsConstants;
import org.bndtools.api.ILogger;
import org.bndtools.api.Logger;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IStreamsProxy;
import org.eclipse.jdt.launching.IVMConnector;
import org.eclipse.jdt.launching.JavaRuntime;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import aQute.bnd.build.ProjectLauncher;
import aQute.bnd.build.Run;
import aQute.bnd.build.RunSession;
import bndtools.launch.OSGiRunLaunchDelegate;
import bndtools.launch.UpdateGuard;
class LaunchThread extends Thread implements IProcess {
private static final ILogger logger = Logger.getLogger(OSGiRunLaunchDelegate.class);
private final ProjectLauncher launcher;
private final AtomicBoolean terminated = new AtomicBoolean(false);
private final BundleContext context = FrameworkUtil.getBundle(LaunchThread.class).getBundleContext();
private final ILaunch launch;
private final Map<String,String> attributes = new HashMap<String,String>();
private int exitValue;
private BndStreamsProxy sproxy;
private final RunSession session;
LaunchThread(ProjectLauncher pl, RunSession session, ILaunch launch) {
super("bnd::launch-" + pl.getProject());
super.setDaemon(true);
this.launcher = pl;
this.launch = launch;
this.session = session;
attributes.put(IProcess.ATTR_PROCESS_TYPE, session.getName());
attributes.put(IProcess.ATTR_PROCESS_LABEL, session.getLabel());
attributes.put(IProcess.ATTR_CMDLINE, session.getLabel());
}
void doDebug(IProgressMonitor monitor) throws InterruptedException {
monitor.setTaskName("Connecting debugger " + session.getName() + " to " + session.getHost() + ":" + session.getJdb());
Map<String,String> parameters = new HashMap<String,String>();
parameters.put("hostname", session.getHost());
parameters.put("port", session.getJdb() + "");
parameters.put("timeout", session.getTimeout() + "");
IVMConnector connector = JavaRuntime.getDefaultVMConnector();
while (!monitor.isCanceled()) {
try {
connector.connect(parameters, monitor, launch);
break;
} catch (Exception e) {
Thread.sleep(500);
}
}
}
/**
* This is the reason for this thread. We launch the remote process and wait until it returns.
*/
@Override
public void run() {
fireCreationEvent();
//
// We wait for build changes. We never update during a build
// and we will wait a bit after a build ends.
//
UpdateGuard guard = new UpdateGuard(context) {
@Override
protected void update() {
LaunchThread.this.update();
}
};
guard.open();
try {
exitValue = session.launch();
} catch (Exception e) {
logger.logWarning("Exception from launcher", e);
e.printStackTrace();
} finally {
guard.close();
terminate();
}
}
private void update() {
if (isTerminated())
return;
try {
//
// TODO Should use listener
//
if (launcher.getProject() instanceof Run)
launcher.getProject().refresh();
launcher.update();
} catch (Exception e) {
logger.logWarning("Exception from update", e);
}
fireChangeEvent();
}
@Override
public void terminate() {
if (terminated.getAndSet(true))
return;
if (sproxy != null)
sproxy.close();
try {
launcher.cancel();
} catch (Exception e) {
// ignore
} finally {
fireTerminateEvent();
}
IDebugTarget[] debugTargets = launch.getDebugTargets();
for (int i = 0; i < debugTargets.length; i++) {
IDebugTarget target = debugTargets[i];
if (target.canDisconnect()) {
try {
target.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@SuppressWarnings("rawtypes")
@Override
public Object getAdapter(Class adapter) {
if (adapter.equals(IProcess.class)) {
return this;
}
if (adapter.equals(IDebugTarget.class)) {
ILaunch launch = getLaunch();
IDebugTarget[] targets = launch.getDebugTargets();
for (int i = 0; i < targets.length; i++) {
if (this.equals(targets[i].getProcess())) {
return targets[i];
}
}
return null;
}
if (adapter.equals(ILaunch.class)) {
return getLaunch();
}
if (adapter.equals(ILaunchConfiguration.class)) {
return getLaunch().getLaunchConfiguration();
}
return null;
}
@Override
public boolean canTerminate() {
return !isTerminated();
}
@Override
public boolean isTerminated() {
return terminated.get();
}
@Override
public String getLabel() {
return launcher.getProject().toString();
}
@Override
public ILaunch getLaunch() {
return launch;
}
@Override
public IStreamsProxy getStreamsProxy() {
if (sproxy == null) {
sproxy = new BndStreamsProxy(launcher, session);
}
return sproxy;
}
@Override
public void setAttribute(String key, String value) {
attributes.put(key, value);
}
@Override
public String getAttribute(String key) {
return attributes.get(key);
}
@Override
public int getExitValue() throws DebugException {
if (!terminated.get())
throw new DebugException(new Status(IStatus.ERROR, BndtoolsConstants.CORE_PLUGIN_ID, ""));
return exitValue;
}
/**
* Fires a creation event.
*/
protected void fireCreationEvent() {
fireEvent(new DebugEvent(this, DebugEvent.CREATE));
}
/**
* Fires a terminate event.
*/
protected void fireTerminateEvent() {
fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
}
/**
* Fires a change event.
*/
protected void fireChangeEvent() {
fireEvent(new DebugEvent(this, DebugEvent.CHANGE));
}
/**
* Fires the given debug event.
*
* @param event
* debug event to fire
*/
protected void fireEvent(DebugEvent event) {
DebugPlugin manager = DebugPlugin.getDefault();
if (manager != null) {
manager.fireDebugEventSet(new DebugEvent[] {
event
});
}
}
}