blob: 06443e405525166f0d1ddb96f31bb3a027986207 [file] [log] [blame]
/*******************************************************************************
* 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();
}
};
}
}