/******************************************************************************* * 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 java.io.File; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; 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.model.IProcess; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.console.IConsole; import org.eclipse.debug.ui.console.IConsoleLineTracker; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IRegion; import org.eclipse.ui.IEditorDescriptor; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.console.IHyperlink; import org.eclipse.ui.ide.IDE; import org.codehaus.plexus.util.DirectoryScanner; import org.eclipse.m2e.actions.MavenLaunchConstants; import org.eclipse.m2e.core.MavenPlugin; import org.eclipse.m2e.core.project.IMavenProjectFacade; import org.eclipse.m2e.core.project.IMavenProjectRegistry; /** * Maven Console line tracker * * @author Eugene Kuleshov */ public class MavenConsoleLineTracker implements IConsoleLineTracker { private static final Logger log = LoggerFactory.getLogger(MavenConsoleLineTracker.class); private static final String PLUGIN_ID = "org.eclipse.m2e.launching"; //$NON-NLS-1$ private static final String LISTENING_MARKER = "Listening for transport dt_socket at address: "; private static final String RUNNING_MARKER = "Running "; private static final String TEST_TEMPLATE = "(?: )test.+\\(([\\w\\.]+)\\)"; //$NON-NLS-1$ private static final Pattern PATTERN2 = Pattern.compile(TEST_TEMPLATE); private IConsole console; public void init(IConsole console) { this.console = console; } public void lineAppended(IRegion line) { IProcess process = console.getProcess(); ILaunch launch = process.getLaunch(); ILaunchConfiguration launchConfiguration = launch.getLaunchConfiguration(); if(launchConfiguration != null && isMavenProcess(launchConfiguration)) { try { int offset = line.getOffset(); int length = line.getLength(); String text = console.getDocument().get(offset, length); String testName = null; int index = text.indexOf(RUNNING_MARKER); if(index > -1) { testName = text.substring(RUNNING_MARKER.length()); offset += RUNNING_MARKER.length(); } else if(text.startsWith(LISTENING_MARKER)) { // create and start remote Java app launch configuration String baseDir = getBaseDir(launchConfiguration); if(baseDir != null) { String portString = text.substring(LISTENING_MARKER.length()).trim(); MavenDebugHyperLink link = new MavenDebugHyperLink(baseDir, portString); console.addLink(link, offset, LISTENING_MARKER.length() + portString.length()); // launchRemoteJavaApp(baseDir, portString); } } else { Matcher m = PATTERN2.matcher(text); if(m.find()) { testName = m.group(1); offset += m.start(1); } } if(testName != null) { String baseDir = getBaseDir(launchConfiguration); if(baseDir != null) { MavenConsoleHyperLink link = new MavenConsoleHyperLink(baseDir, testName); console.addLink(link, offset, testName.length()); } } } catch(BadLocationException ex) { // ignore } catch(CoreException ex) { log.error(ex.getMessage(), ex); } } } private String getBaseDir(ILaunchConfiguration launchConfiguration) throws CoreException { return launchConfiguration.getAttribute(MavenLaunchConstants.ATTR_POM_DIR, (String) null); } public void dispose() { } private boolean isMavenProcess(ILaunchConfiguration launchConfiguration) { try { ILaunchConfigurationType type = launchConfiguration.getType(); return PLUGIN_ID.equals(type.getPluginIdentifier()); } catch(CoreException ex) { log.error(ex.getMessage(), ex); return false; } } static void launchRemoteJavaApp(String baseDir, String portString) throws CoreException { ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager(); ILaunchConfigurationType launchConfigurationType = launchManager .getLaunchConfigurationType(IJavaLaunchConfigurationConstants.ID_REMOTE_JAVA_APPLICATION); /* <launchConfiguration type="org.eclipse.jdt.launching.remoteJavaApplication"> <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="foo-launch"/> <stringAttribute key="org.eclipse.jdt.launching.VM_CONNECTOR_ID" value="org.eclipse.jdt.launching.socketAttachConnector"/> <booleanAttribute key="org.eclipse.jdt.launching.ALLOW_TERMINATE" value="false"/> <mapAttribute key="org.eclipse.jdt.launching.CONNECT_MAP"> <mapEntry key="port" value="8000"/> <mapEntry key="hostname" value="localhost"/> </mapAttribute> <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> <listEntry value="/foo-launch"/> </listAttribute> <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> <listEntry value="4"/> </listAttribute> <listAttribute key="org.eclipse.debug.ui.favoriteGroups"> <listEntry value="org.eclipse.debug.ui.launchGroup.debug"/> </listAttribute> */ ILaunchConfigurationWorkingCopy workingCopy = launchConfigurationType.newInstance(null, // "Connecting debugger to port " + portString); workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_ALLOW_TERMINATE, false); workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_CONNECTOR, IJavaLaunchConfigurationConstants.ID_SOCKET_ATTACH_VM_CONNECTOR); Map<String, String> connectMap = new HashMap<String, String>(); connectMap.put("port", portString); //$NON-NLS-1$ connectMap.put("hostname", "localhost"); //$NON-NLS-1$ //$NON-NLS-2$ workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CONNECT_MAP, connectMap); IProject project = getProject(baseDir); if(project != null) { workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, project.getName()); } DebugUITools.launch(workingCopy, "debug"); //$NON-NLS-1$ } static IProject getProject(String baseDir) { IMavenProjectRegistry projectManager = MavenPlugin.getMavenProjectRegistry(); for(IMavenProjectFacade projectFacade : projectManager.getProjects()) { IContainer base = projectFacade.getPom().getParent(); String baseLocation = base.getLocation().toPortableString(); if(baseDir.equals(baseLocation)) { return projectFacade.getProject(); } } return null; } /** * Opens a text editor for Maven test report */ public class MavenConsoleHyperLink implements IHyperlink { private final String baseDir; private final String testName; public MavenConsoleHyperLink(String baseDir, String testName) { this.baseDir = baseDir; this.testName = testName; } public void linkActivated() { DirectoryScanner ds = new DirectoryScanner(); ds.setBasedir(baseDir); ds.setIncludes(new String[] {"**/" + testName + ".txt"}); //$NON-NLS-1$ //$NON-NLS-2$ ds.scan(); String[] includedFiles = ds.getIncludedFiles(); // TODO show selection dialog when there is more then one result found if(includedFiles != null && includedFiles.length > 0) { IWorkbench workbench = PlatformUI.getWorkbench(); IWorkbenchWindow window = workbench.getActiveWorkbenchWindow(); IWorkbenchPage page = window.getActivePage(); IEditorDescriptor desc = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor("foo.txt"); //$NON-NLS-1$ File reportFile = new File(baseDir, includedFiles[0]); try { IDE.openEditor(page, new MavenFileEditorInput(reportFile.getAbsolutePath()), desc.getId()); } catch(PartInitException ex) { log.error(ex.getMessage(), ex); } } } public void linkEntered() { } public void linkExited() { } } /** * Creates debug launch configuration for remote Java application. For example, with surefire plugin the following * property can be specified: -Dmaven.surefire.debug= * "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -Xnoagent -Djava.compiler=NONE" */ public class MavenDebugHyperLink implements IHyperlink { private final String baseDir; private final String portString; public MavenDebugHyperLink(String baseDir, String portString) { this.baseDir = baseDir; this.portString = portString; } public void linkActivated() { try { launchRemoteJavaApp(baseDir, portString); } catch(CoreException ex) { log.error(ex.getMessage(), ex); } } public void linkEntered() { } public void linkExited() { } } }