/******************************************************************************* * Copyright (c) 2006, 2009 Mountainminds GmbH & Co. KG * This software is provided under the terms of the Eclipse Public License v1.0 * See http://www.eclipse.org/legal/epl-v10.html. * * $Id: CoverageLauncher.java 521 2009-01-29 19:59:16Z mtnminds $ ******************************************************************************/ package com.mountainminds.eclemma.core.launching; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import java.util.zip.ZipEntry; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExecutableExtension; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.Launch; import org.eclipse.debug.core.model.ILaunchConfigurationDelegate; import org.eclipse.debug.core.model.ILaunchConfigurationDelegate2; import org.eclipse.jdt.launching.IRuntimeClasspathEntry; import org.eclipse.jdt.launching.IRuntimeClasspathProvider; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.osgi.util.NLS; import com.mountainminds.eclemma.core.CoverageTools; import com.mountainminds.eclemma.core.EclEmmaStatus; import com.mountainminds.eclemma.core.IClassFiles; import com.mountainminds.eclemma.internal.core.CoreMessages; import com.mountainminds.eclemma.internal.core.DebugOptions; import com.mountainminds.eclemma.internal.core.EclEmmaCorePlugin; import com.mountainminds.eclemma.internal.core.instr.InstrMarker; import com.mountainminds.eclemma.internal.core.launching.CoverageLaunchInfo; import com.mountainminds.eclemma.internal.core.launching.InstrumentedClasspathProvider; import com.vladium.emma.AppLoggers; import com.vladium.emma.EMMAProperties; /** * Abstract base class for coverage mode launchers. Coverage launchers perform * class instrumentation and then delegate to the corresponding launcher * responsible for the "run" mode. * * @author Marc R. Hoffmann * @version $Revision: 521 $ */ public abstract class CoverageLauncher implements ICoverageLauncher, IExecutableExtension { /** * Name of the file that will EMMA pick from the classpath to reads its * properties. */ protected static final String EMMA_PROPERTIES_FILE = "emma.properties"; //$NON-NLS-1$ /** Launch mode for the launch delegates used internally. */ public static final String DELEGATELAUNCHMODE = ILaunchManager.RUN_MODE; protected String launchtype; protected ILaunchConfigurationDelegate launchdelegate; protected ILaunchConfigurationDelegate2 launchdelegate2; /** * Hook method to modify the launch configuration before it is passed on to * the delegate launcher. * * @param workingcopy * Configuration to modify * @param info * Info object of this launch * @throws CoreException * may be thrown by implementations */ protected void modifyConfiguration( ILaunchConfigurationWorkingCopy workingcopy, ICoverageLaunchInfo info) throws CoreException { // Does nothing by default } /** * Returns whether in-place instrumentation should be performed. The default * implementation looks-up the corresponding entry in the passed launch * configuration. Specific launchers may modify this behavior. * * @param configuration * launch configuration for coverage run * @return true, if instrumentation should be performed in-place * @throws CoreException * May be thrown when accessing the launch configuration */ protected boolean hasInplaceInstrumentation(ILaunchConfiguration configuration) throws CoreException { return configuration.getAttribute( ICoverageLaunchConfigurationConstants.ATTR_INPLACE_INSTRUMENTATION, false); } /** * Creates the a JAR file including the <code>emma.properties</code> file that * will be injected in the class path. * * @param configuration * Configuration object for this launch * @param info * Launch Info object of this launch * @throws CoreException * Thrown when the JAR file cannot be created */ private void createPropertiesJAR(ILaunchConfiguration configuration, ICoverageLaunchInfo info) throws CoreException { Properties properties = new Properties(); properties.put(EMMAProperties.PROPERTY_COVERAGE_DATA_OUT_FILE, info .getCoverageFile().toOSString()); properties.put(AppLoggers.PROPERTY_VERBOSITY_LEVEL, DebugOptions.EMMAVERBOSITYLEVEL); IPath jarfile = info.getPropertiesJARFile(); Manifest mf = new Manifest(); try { JarOutputStream jar = new JarOutputStream(new FileOutputStream(jarfile .toFile()), mf); jar.putNextEntry(new ZipEntry(EMMA_PROPERTIES_FILE)); properties.store(jar, "Created for launch configuration " + configuration.getName()); //$NON-NLS-1$ jar.close(); } catch (IOException e) { throw new CoreException(EclEmmaStatus.EMMA_PROPERTIES_CREATION_ERROR .getStatus(jarfile, e)); } } // IExecutableExtension interface: public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException { launchtype = config.getAttribute("type"); //$NON-NLS-1$ launchdelegate = getLaunchDelegate(launchtype); if (launchdelegate instanceof ILaunchConfigurationDelegate2) { launchdelegate2 = (ILaunchConfigurationDelegate2) launchdelegate; } } private ILaunchConfigurationDelegate getLaunchDelegate(String launchtype) throws CoreException { ILaunchConfigurationType type = DebugPlugin.getDefault().getLaunchManager() .getLaunchConfigurationType(launchtype); if (type == null) { throw new CoreException(EclEmmaStatus.UNKOWN_LAUNCH_TYPE_ERROR .getStatus(launchtype)); } return type.getDelegate(DELEGATELAUNCHMODE); } // ILaunchConfigurationDelegate interface: public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException { monitor.beginTask(NLS.bind(CoreMessages.Launching_task, configuration .getName()), 2); IRuntimeClasspathProvider provider = JavaRuntime .getClasspathProvider(configuration); ICoverageLaunchInfo info = CoverageTools.getLaunchInfo(launch); if (info == null) { // Must not happen as we should have created the launch throw new CoreException(EclEmmaStatus.MISSING_LAUNCH_INFO_ERROR .getStatus(null)); } info.instrument(new SubProgressMonitor(monitor, 1), hasInplaceInstrumentation(configuration)); if (monitor.isCanceled()) { return; } createPropertiesJAR(configuration, info); ILaunchConfigurationWorkingCopy wc = configuration.getWorkingCopy(); modifyConfiguration(wc, info); InstrumentedClasspathProvider.enable(provider, info); try { launchdelegate.launch(wc, DELEGATELAUNCHMODE, launch, new SubProgressMonitor(monitor, 1)); } finally { InstrumentedClasspathProvider.disable(); } monitor.done(); } // ILaunchConfigurationDelegate2 interface: public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException { ILaunch launch = new Launch(configuration, CoverageTools.LAUNCH_MODE, null); new CoverageLaunchInfo(launch); return launch; } public boolean buildForLaunch(ILaunchConfiguration configuration, String mode, IProgressMonitor monitor) throws CoreException { if (launchdelegate2 == null) { return true; } else { return launchdelegate2.buildForLaunch(configuration, DELEGATELAUNCHMODE, monitor); } } public boolean preLaunchCheck(ILaunchConfiguration configuration, String mode, IProgressMonitor monitor) throws CoreException { boolean inplace = hasInplaceInstrumentation(configuration); if (CoverageTools.getClassFilesForInstrumentation(configuration, inplace).length == 0) { IStatus status = EclEmmaStatus.NO_INSTRUMENTED_CLASSES.getStatus(); EclEmmaCorePlugin.getInstance().showPrompt(status, configuration); return false; } if (inplace) { // Issue an inplace instrumentation warning: IStatus status = EclEmmaStatus.INPLACE_INSTRUMENTATION_INFO.getStatus(); if (!EclEmmaCorePlugin.getInstance().showPrompt(status, configuration)) { return false; } } else { // check whether inpace instrumentation has been performed before if (checkForPreviousInplace(configuration)) { IStatus status = EclEmmaStatus.ALREADY_INSTRUMENTED_ERROR.getStatus(); EclEmmaCorePlugin.getInstance().showPrompt(status, configuration); return false; } } // Then allow the delegate's veto: if (launchdelegate2 == null) { return true; } else { return launchdelegate2.preLaunchCheck(configuration, DELEGATELAUNCHMODE, monitor); } } private boolean checkForPreviousInplace(ILaunchConfiguration config) throws CoreException { IClassFiles[] classfiles = CoverageTools.getClassFilesForInstrumentation( config, false); for (int i = 0; i < classfiles.length; i++) { if (InstrMarker.isMarked(classfiles[i].getLocation())) { return true; } } return false; } public boolean finalLaunchCheck(ILaunchConfiguration configuration, String mode, IProgressMonitor monitor) throws CoreException { if (launchdelegate2 == null) { return true; } else { return launchdelegate2.finalLaunchCheck(configuration, DELEGATELAUNCHMODE, monitor); } } // ICoverageLauncher interface: /* * The default implemenation delegates to the classpath provider. */ public IClassFiles[] getClassFiles(ILaunchConfiguration configuration, boolean includebinaries) throws CoreException { List l = new ArrayList(); IRuntimeClasspathEntry[] entries = JavaRuntime .computeUnresolvedRuntimeClasspath(configuration); entries = JavaRuntime.resolveRuntimeClasspath(entries, configuration); for (int i = 0; i < entries.length; i++) { if (entries[i].getClasspathProperty() == IRuntimeClasspathEntry.USER_CLASSES) { IClassFiles ic = CoverageTools .getClassFilesAtAbsoluteLocation(entries[i].getLocation()); if (ic != null && (includebinaries || !ic.isBinary())) { l.add(ic); } } } IClassFiles[] arr = new IClassFiles[l.size()]; return (IClassFiles[]) l.toArray(arr); } /** * Internal utility to find the {@link IClassFiles} descriptor for the given * class path location. * * @param location * class path location * @return descriptor or <code>null</code> * @throws CoreException * in case of internal inconsistencies * @deprecated please user * {@link CoverageTools#getClassFilesAtAbsoluteLocation(String)} * instead */ protected IClassFiles findClassFiles(String location) throws CoreException { return CoverageTools.getClassFilesAtAbsoluteLocation(location); } }