package bndtools.launch.bnd; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.IStatusHandler; import org.eclipse.debug.core.model.IProcess; import org.eclipse.jdt.internal.launching.JavaRemoteApplicationLaunchConfigurationDelegate; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; import aQute.bnd.build.Project; import aQute.bnd.build.ProjectLauncher; import aQute.bnd.build.Run; import aQute.bnd.build.RunSession; import aQute.bnd.osgi.Processor; import aQute.lib.io.IO; import bndtools.Plugin; import bndtools.central.Central; import bndtools.launch.LaunchConstants; import bndtools.launch.ui.internal.LaunchStatusHandler; import bndtools.launch.util.LaunchUtils; import bndtools.preferences.BndPreferences; /** * The link between the Eclipse launching subsystem and the bnd launcher. We bypass the standard Eclipse launching and * will always setup the launch as a bnd launch and attach the debugger if necessary. This has the advantage we can add * features in bnd and we are sure fidelity is maintained. */ public class NativeBndLaunchDelegate extends JavaRemoteApplicationLaunchConfigurationDelegate { volatile boolean canceled = false; /* * The Eclipse launch interface. */ @Override public void launch(ILaunchConfiguration configuration, String mode, final ILaunch launch, IProgressMonitor m) throws CoreException { final IProgressMonitor monitor = m == null ? new NullProgressMonitor() : m; Callable<Boolean> isCancelled = new Callable<Boolean>() { @Override public Boolean call() throws Exception { return canceled || monitor.isCanceled(); } }; Processor p = new Processor(); try { monitor.setTaskName("Detecting if configuration is already launched"); if (isAlreadyRunning(configuration)) { return; } String target = configuration.getAttribute(LaunchConstants.ATTR_LAUNCH_TARGET, (String) null); if (target == null || target.length() == 0) { p.error("No target specified in the launch configuration"); return; } IResource targetResource = ResourcesPlugin.getWorkspace().getRoot().findMember(target); if (targetResource == null) { p.error("No actual resource found for " + target); return; } IProject parent = targetResource.getProject(); if (parent == null) { p.error("Not part of a project " + targetResource); return; } Project parentModel = Central.getProject(parent); if (parentModel == null) { p.error("Cannot locate Bnd project for " + targetResource); return; } Project model; if (targetResource.getName().equals(Project.BNDFILE)) { model = parentModel; } else { File file = targetResource.getLocation().toFile(); if (file == null || !file.isFile()) { p.error("No file associated with the entry " + targetResource); return; } model = new Run(parentModel.getWorkspace(), parentModel.getBase(), file); } monitor.setTaskName("Target is " + model); boolean debug = "debug".equals(mode); try { List<LaunchThread> lts = new ArrayList<LaunchThread>(); ProjectLauncher projectLauncher = model.getProjectLauncher(); try { List< ? extends RunSession> sessions = projectLauncher.getRunSessions(); if (sessions == null) { projectLauncher.error("This launcher for %s cannot handle the new style", target); return; } for (RunSession session : sessions) try { monitor.setTaskName("validating session " + session.getLabel()); if (!session.validate(isCancelled)) { continue; } LaunchThread lt = new LaunchThread(projectLauncher, session, launch); if (debug) { lt.doDebug(monitor); } if (monitor.isCanceled()) return; launch.addProcess(lt); lts.add(lt); } catch (Exception e) { projectLauncher.exception(e, "Starting session %s in project %s", session.getName(), model); } } catch (Exception e) { projectLauncher.exception(e, "starting processes"); } finally { p.getInfo(projectLauncher); } if (!p.isOk()) { IStatus status = Central.toStatus(projectLauncher, "Errors detected during the launch"); IStatusHandler prompter = DebugPlugin.getDefault().getStatusHandler(status); Boolean cont = (Boolean) prompter.handleStatus(status, null); if (cont == null || !cont || monitor.isCanceled()) { launch.terminate(); return; } } for (LaunchThread lt : lts) { lt.start(); } } catch (Exception e) { launch.terminate(); abort("Internal error", e, IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR); } } catch (Exception e) { p.exception(e, "While starting a launch %s", configuration); } finally { if (!p.isOk()) { IStatus status = Central.toStatus(p, "Errors detected during the launch"); IStatusHandler prompter = new LaunchStatusHandler(); prompter.handleStatus(status, null); launch.terminate(); } monitor.done(); IO.close(p); } } /** * Check if we already have a configuration running */ public boolean isAlreadyRunning(ILaunchConfiguration configuration) throws CoreException { // Check for existing launches of same resource BndPreferences prefs = new BndPreferences(); if (prefs.getWarnExistingLaunches()) { IResource launchResource = LaunchUtils.getTargetResource(configuration); if (launchResource == null) return false; int processCount = 0; for (ILaunch l : DebugPlugin.getDefault().getLaunchManager().getLaunches()) { // ... is it the same launch resource? ILaunchConfiguration launchConfig = l.getLaunchConfiguration(); if (launchConfig == null) { continue; } if (launchResource.equals(LaunchUtils.getTargetResource(launchConfig))) { // Iterate existing processes for (IProcess process : l.getProcesses()) { if (!process.isTerminated()) processCount++; } } } // Warn if existing processes running if (processCount > 0) { Status status = new Status(IStatus.WARNING, Plugin.PLUGIN_ID, 0, "One or more OSGi Frameworks have already been launched for this configuration. Additional framework instances may interfere with each other due to the shared storage directory.", null); IStatusHandler prompter = DebugPlugin.getDefault().getStatusHandler(status); if (prompter != null) { boolean okay = (Boolean) prompter.handleStatus(status, launchResource); return !okay; } } } return false; } }