| /******************************************************************************* |
| * Copyright (c) 2008-2010 Sonatype, Inc. All rights reserved. This program and the accompanying |
| * materials are made available under the terms of the Eclipse Public License v1.0 which accompanies |
| * this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: Sonatype, Inc. - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.m2e.internal.launch; |
| |
| import static org.eclipse.m2e.actions.MavenLaunchConstants.ATTR_WORKSPACE_RESOLUTION; |
| import static org.eclipse.m2e.actions.MavenLaunchConstants.PLUGIN_ID; |
| import static org.eclipse.m2e.core.embedder.IMavenLauncherConfiguration.LAUNCHER_REALM; |
| import static org.eclipse.m2e.internal.launch.MavenLaunchUtils.quote; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.debug.core.DebugEvent; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.IDebugEventSetListener; |
| import org.eclipse.debug.core.ILaunch; |
| import org.eclipse.debug.core.ILaunchConfiguration; |
| import org.eclipse.debug.core.model.IProcess; |
| import org.eclipse.debug.ui.RefreshTab; |
| import org.eclipse.jdt.launching.IVMRunner; |
| |
| import org.eclipse.m2e.core.internal.MavenPluginActivator; |
| import org.eclipse.m2e.core.internal.launch.AbstractMavenRuntime; |
| import org.eclipse.m2e.workspace.WorkspaceState; |
| |
| |
| /** |
| * Helper class to configure and launch MavenRuntime instance. |
| * <p> |
| * Generates classworld configuration file, i.e. m2.conf. Generated classworld configuration file will include |
| * cliresolver for launch configuration that have workspace resolution enabled. |
| * <p> |
| * Sets the following conventional launch configuration attributes. |
| * <ul> |
| * <li>WorkspaceState.SYSPROP_STATEFILE_LOCATION, full absolute path of m2e workspace state file. See |
| * {@link WorkspaceState} for details of the state file format. Only set if workspace dependency resolution is enabled |
| * for the launch configuration.</li> |
| * <li>maven.bootclasspath, maven runtime bootstrap classpath, normally only contains classworlds jar.</li> |
| * <li>maven.home, location of maven runtime, logical name is used for embedded and workspace runtimes</li> |
| * <li>classworlds.conf, location of classworlds configuration file, i.e. m2.conf</li> |
| * </ul> |
| * |
| * @since 1.4 |
| */ |
| @SuppressWarnings("restriction") |
| public class MavenRuntimeLaunchSupport { |
| |
| private final AbstractMavenRuntime runtime; |
| |
| private final MavenLauncherConfigurationHandler cwconf; |
| |
| private final boolean resolveWorkspaceArtifacts; |
| |
| private final File cwconfFile; |
| |
| public static class VMArguments { |
| |
| private final StringBuilder properties = new StringBuilder(); |
| |
| public void append(String str) { |
| if(str != null) { |
| str = str.trim(); |
| } |
| if(str != null && str.length() > 0) { |
| if(properties.length() > 0) { |
| properties.append(' '); |
| } |
| properties.append(str); |
| } |
| } |
| |
| public void appendProperty(String key, String value) { |
| append("-D" + key + "=" + value); |
| } |
| |
| @Override |
| public String toString() { |
| return properties.toString(); |
| } |
| } |
| |
| public static class Builder { |
| private final ILaunchConfiguration configuration; |
| |
| private boolean resolveWorkspaceArtifacts; |
| |
| private boolean injectWorkspaceResolver; |
| |
| Builder(ILaunchConfiguration configuration) throws CoreException { |
| this.configuration = configuration; |
| enableWorkspaceResolution(configuration.getAttribute(ATTR_WORKSPACE_RESOLUTION, false)); |
| } |
| |
| public Builder enableWorkspaceResolution(boolean enable) { |
| this.resolveWorkspaceArtifacts = enable; |
| this.injectWorkspaceResolver = enable; |
| return this; |
| } |
| |
| public Builder enableWorkspaceResolver(boolean enable) { |
| this.injectWorkspaceResolver = enable; |
| return this; |
| } |
| |
| public MavenRuntimeLaunchSupport build(IProgressMonitor monitor) throws CoreException { |
| |
| final AbstractMavenRuntime runtime = MavenLaunchUtils.getMavenRuntime(configuration); |
| |
| final MavenLauncherConfigurationHandler cwconf = new MavenLauncherConfigurationHandler(); |
| runtime.createLauncherConfiguration(cwconf, monitor); |
| if(injectWorkspaceResolver) { |
| for(String entry : MavenLaunchUtils.getCliResolver(runtime)) { |
| cwconf.forceArchiveEntry(entry); |
| } |
| } |
| |
| final File cwconfFile; |
| try { |
| File state = MavenLaunchPlugin.getDefault().getStateLocation().toFile(); |
| File dir = new File(state, "launches"); //$NON-NLS-1$ |
| dir.mkdirs(); |
| cwconfFile = File.createTempFile("m2conf", ".tmp", dir); //$NON-NLS-1$ //$NON-NLS-2$ |
| OutputStream os = new FileOutputStream(cwconfFile); |
| try { |
| cwconf.save(os); |
| } finally { |
| os.close(); |
| } |
| } catch(IOException e) { |
| throw new CoreException( |
| new Status(IStatus.ERROR, PLUGIN_ID, -1, Messages.MavenLaunchDelegate_error_cannot_create_conf, e)); |
| } |
| |
| return new MavenRuntimeLaunchSupport(runtime, cwconf, cwconfFile, resolveWorkspaceArtifacts); |
| } |
| } |
| |
| /** |
| * Refreshes resources as specified by a launch configuration, when an associated process terminates. Adapted from |
| * org.eclipse.ui.externaltools.internal.program.launchConfigurations.BackgroundResourceRefresher |
| */ |
| private class BackgroundResourceRefresher implements IDebugEventSetListener { |
| final ILaunchConfiguration configuration; |
| |
| final IProcess process; |
| |
| public BackgroundResourceRefresher(ILaunchConfiguration configuration, ILaunch launch) { |
| this.configuration = configuration; |
| this.process = launch.getProcesses()[0]; |
| } |
| |
| /** |
| * If the process has already terminated, resource refreshing is done immediately in the current thread. Otherwise, |
| * refreshing is done when the process terminates. |
| */ |
| public void init() { |
| synchronized(process) { |
| if(process.isTerminated()) { |
| processResources(); |
| } else { |
| DebugPlugin.getDefault().addDebugEventListener(this); |
| } |
| } |
| } |
| |
| public void handleDebugEvents(DebugEvent[] events) { |
| for(int i = 0; i < events.length; i++ ) { |
| DebugEvent event = events[i]; |
| if(event.getSource() == process && event.getKind() == DebugEvent.TERMINATE) { |
| DebugPlugin.getDefault().removeDebugEventListener(this); |
| processResources(); |
| break; |
| } |
| } |
| } |
| |
| protected void processResources() { |
| getClassworldConfFile().delete(); |
| Job job = new Job(Messages.MavenLaunchDelegate_job_name) { |
| public IStatus run(IProgressMonitor monitor) { |
| try { |
| RefreshTab.refreshResources(configuration, monitor); |
| return Status.OK_STATUS; |
| } catch(CoreException e) { |
| return e.getStatus(); |
| } |
| } |
| }; |
| job.schedule(); |
| } |
| } |
| |
| MavenRuntimeLaunchSupport(AbstractMavenRuntime runtime, MavenLauncherConfigurationHandler cwconf, File cwconfFile, |
| boolean resolveWorkspaceArtifacts) { |
| this.runtime = runtime; |
| this.cwconf = cwconf; |
| this.cwconfFile = cwconfFile; |
| this.resolveWorkspaceArtifacts = resolveWorkspaceArtifacts; |
| } |
| |
| public static Builder builder(ILaunchConfiguration configuration) throws CoreException { |
| return new Builder(configuration); |
| } |
| |
| public static MavenRuntimeLaunchSupport create(ILaunchConfiguration configuration, ILaunch launch, |
| IProgressMonitor monitor) throws CoreException { |
| return builder(configuration).build(monitor); |
| } |
| |
| /** |
| * MAVEN_HOME or equivalent location of the Maven runtime. |
| */ |
| public String getLocation() { |
| return runtime.getLocation(); |
| } |
| |
| /** |
| * Location of classworld configuration file, i.e. m2.conf, of the Maven runtime. |
| */ |
| public File getClassworldConfFile() { |
| return cwconfFile; |
| } |
| |
| /** |
| * Bootstrap classpath of the Maven runtime, normally only contains classworlds jar. |
| */ |
| @SuppressWarnings("deprecation") |
| public List<String> getBootClasspath() { |
| return cwconf.getRealmEntries(LAUNCHER_REALM); |
| } |
| |
| public String getSettings() { |
| return runtime.getSettings(); |
| } |
| |
| public String getVersion() { |
| return runtime.getVersion(); |
| } |
| |
| public VMArguments getVMArguments() { |
| VMArguments properties = new VMArguments(); |
| |
| applyMavenRuntime(properties); |
| |
| if(resolveWorkspaceArtifacts) { |
| applyWorkspaceArtifacts(properties); // workspace artifact resolution |
| } |
| |
| return properties; |
| } |
| |
| public void applyMavenRuntime(VMArguments properties) { |
| // maven.home |
| String location = runtime.getLocation(); |
| if(location != null) { |
| properties.appendProperty("maven.home", quote(location)); //$NON-NLS-1$ |
| } |
| |
| // m2.conf |
| properties.appendProperty("classworlds.conf", quote(cwconfFile.getAbsolutePath())); //$NON-NLS-1$ |
| } |
| |
| public static void applyWorkspaceArtifacts(VMArguments properties) { |
| File state = MavenPluginActivator.getDefault().getMavenProjectManager().getWorkspaceStateFile(); |
| properties.appendProperty(WorkspaceState.SYSPROP_STATEFILE_LOCATION, quote(state.getAbsolutePath())); //$NON-NLS-1$ |
| } |
| |
| public IVMRunner decorateVMRunner(final IVMRunner runner) { |
| return (runnerConfiguration, launch, monitor) -> { |
| runner.run(runnerConfiguration, launch, monitor); |
| |
| IProcess[] processes = launch.getProcesses(); |
| if(processes != null && processes.length > 0) { |
| ILaunchConfiguration configuration = launch.getLaunchConfiguration(); |
| BackgroundResourceRefresher refresher = new BackgroundResourceRefresher(configuration, launch); |
| refresher.init(); |
| } else { |
| // the process didn't start, remove temp classworlds.conf right away |
| getClassworldConfFile().delete(); |
| } |
| }; |
| } |
| } |