EclipseLaunchConfiguration.java
package de.uka.ipd.sdq.beagle.core.facade;
import de.uka.ipd.sdq.beagle.core.LaunchConfiguration;
import de.uka.ipd.sdq.beagle.core.failurehandling.FailureHandler;
import de.uka.ipd.sdq.beagle.core.failurehandling.FailureReport;
import org.apache.commons.lang3.Validate;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.ILaunchesListener2;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.jdt.launching.JavaRuntime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* A launch configuration that starts in its {@link #execute()} method an eclipse launch
* configuration.
*
* @author Roman Langrehr
* @author Joshua Gleitze
*/
public class EclipseLaunchConfiguration implements LaunchConfiguration {
/**
* Handler of failures.
*/
private static final FailureHandler FAILURE_HANDLER = FailureHandler.getHandler("Eclipse Launch Manager");
/**
* The eclipse launch configuration to run.
*/
private final ILaunchConfiguration originalLaunchConfiguration;
/**
* The project that will be launched when executing
* {@link #originalLaunchConfiguration}.
*/
private final IJavaProject launchedProject;
/**
* The working copy we operate on to not modify the original configuration.
*/
private ILaunchConfigurationWorkingCopy workingCopy;
/**
* When launched, indicates whether the launch has finished.
*/
private boolean done;
/**
* Indicates whether the execution should be tried (again). Used the retry if the
* execution threw an exception.
*/
private boolean execute;
/**
* Creates a Beagle launch configuration from an eclipse launch configuration.
*
* @param launchConfiguration The eclipse launch configuration which should be
* launched to execute the code under test.
* @param launchedProject The project that will be launched when executing the
* {@code launchConfiguration}.
*/
public EclipseLaunchConfiguration(final ILaunchConfiguration launchConfiguration,
final IJavaProject launchedProject) {
Validate.notNull(launchConfiguration);
Validate.notNull(launchedProject);
this.originalLaunchConfiguration = launchConfiguration;
this.launchedProject = launchedProject;
this.copy();
DebugPlugin.getDefault().getLaunchManager().addLaunchListener(new FinishWaiter());
}
@Override
public void execute() throws InterruptedException {
this.done = false;
this.execute = true;
// try again until we succeed or we’re told to stop by the failure handler
while (this.execute) {
// catches core exceptions. These might be thrown when terminating the
// launch, too.
try {
final ILaunch launch = this.workingCopy.launch(ILaunchManager.RUN_MODE, null);
// handles interrupts.
try {
synchronized (this) {
while (!this.done) {
this.wait();
}
}
} catch (final InterruptedException interrupt) {
launch.terminate();
this.copy();
throw interrupt;
}
this.execute = false;
} catch (final CoreException runException) {
final FailureReport<Void> failure = new FailureReport<Void>().cause(runException)
.retryWith(() -> this.execute = true)
.continueWith(() -> this.execute = false);
FAILURE_HANDLER.handle(failure);
}
}
// reset the launch configuration
this.copy();
}
@Override
public LaunchConfiguration prependClasspath(final String classPathEntry) {
final IRuntimeClasspathEntry newEntry = JavaRuntime.newArchiveRuntimeClasspathEntry(new Path(classPathEntry));
try {
this.workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, false);
final List<String> entries =
this.workingCopy.getAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, new ArrayList<>());
if (entries.isEmpty()) {
entries.add(JavaRuntime.newDefaultProjectClasspathEntry(this.launchedProject).getMemento());
}
entries.add(0, newEntry.getMemento());
this.workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, entries);
} catch (final CoreException coreException) {
final FailureReport<LaunchConfiguration> failure =
new FailureReport<LaunchConfiguration>().cause(coreException);
return FAILURE_HANDLER.handle(failure);
}
return this;
}
@Override
public LaunchConfiguration appendJvmArgument(final String argument) {
try {
final String oldAttributes =
this.workingCopy.getAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, "");
this.workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS,
oldAttributes + " " + argument);
} catch (final CoreException coreException) {
final FailureReport<LaunchConfiguration> failure =
new FailureReport<LaunchConfiguration>().cause(coreException);
return FAILURE_HANDLER.handle(failure);
}
return this;
}
/**
* Puts a copy of {@link #originalLaunchConfiguration} into {@link #workingCopy}.
*/
private void copy() {
// Calling .getWorkingCopy() two times assures that nobody can modify the
// original LaunchConfiguration.
try {
this.workingCopy = this.originalLaunchConfiguration.getWorkingCopy().getWorkingCopy();
} catch (final CoreException coreException) {
final FailureReport<LaunchConfiguration> failure =
new FailureReport<LaunchConfiguration>().cause(coreException);
FAILURE_HANDLER.handle(failure);
}
}
/**
* Monitors the launched configurations for being terminated. Notifies this monitor
* once the launch finished.
*
* @author Joshua Gleitze
*/
private class FinishWaiter implements ILaunchesListener2 {
/**
* Reference to “own” {@link EclipseLaunchConfiguration} instance.
*/
private final EclipseLaunchConfiguration parent = EclipseLaunchConfiguration.this;
@Override
public void launchesRemoved(final ILaunch[] launches) {
// not interesting to us
}
@Override
public void launchesAdded(final ILaunch[] launches) {
// not interesting to us
}
@Override
public void launchesChanged(final ILaunch[] launches) {
// not interesting to us
}
@Override
public void launchesTerminated(final ILaunch[] launches) {
if (Arrays.stream(launches)
.anyMatch((launch) -> launch.getLaunchConfiguration() == this.parent.workingCopy)) {
synchronized (this.parent) {
this.parent.done = true;
this.parent.notifyAll();
}
}
}
}
}