diff options
author | Markus Duft | 2016-11-25 13:41:41 +0000 |
---|---|---|
committer | Sarika Sinha | 2017-01-13 03:30:26 +0000 |
commit | 1c1d17b82a223fb8fcc69b4883a71b8744899ccb (patch) | |
tree | c202e984d42407b607ac29cf95168bd6c04ffc29 /org.eclipse.debug.core | |
parent | fb136251cd02ed0b4812d1c65923b3abe79a1d40 (diff) | |
download | eclipse.platform.debug-1c1d17b82a223fb8fcc69b4883a71b8744899ccb.tar.gz eclipse.platform.debug-1c1d17b82a223fb8fcc69b4883a71b8744899ccb.tar.xz eclipse.platform.debug-1c1d17b82a223fb8fcc69b4883a71b8744899ccb.zip |
Launch Groups: Support to wait for output on child stdout
This adds support to listen for output on the console of the launched
item before continuing the launch.
Bug: 508420
Change-Id: I84027592c82b546ddf60c415bcc7047d9bc5490a
Signed-off-by: Markus Duft <markus.duft@ssi-schaefer.com>
Diffstat (limited to 'org.eclipse.debug.core')
8 files changed, 183 insertions, 6 deletions
diff --git a/org.eclipse.debug.core/META-INF/MANIFEST.MF b/org.eclipse.debug.core/META-INF/MANIFEST.MF index e306ac17c..5e1017435 100644 --- a/org.eclipse.debug.core/META-INF/MANIFEST.MF +++ b/org.eclipse.debug.core/META-INF/MANIFEST.MF @@ -15,6 +15,7 @@ Export-Package: org.eclipse.debug.core, org.eclipse.debug.internal.core;x-friends:="org.eclipse.debug.ui,org.eclipse.debug.tests,org.eclipse.debug.examples.mixedmode", org.eclipse.debug.internal.core.commands;x-friends:="org.eclipse.debug.ui", org.eclipse.debug.internal.core.groups, + org.eclipse.debug.internal.core.groups.observer;x-internal:=true, org.eclipse.debug.internal.core.sourcelookup;x-friends:="org.eclipse.debug.ui", org.eclipse.debug.internal.core.sourcelookup.containers;x-friends:="org.eclipse.debug.ui", org.eclipse.debug.internal.core.variables;x-friends:="org.eclipse.debug.ui,org.eclipse.jdt.debug.ui" diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.java index 48bec126b..e3bb0ccc7 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.java +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.java @@ -32,20 +32,19 @@ public class DebugCoreMessages extends NLS { public static String DebugPlugin_8; public static String DebugPlugin_Eclipse_runtime_does_not_support_working_directory_2; public static String EnvironmentVariableResolver_0; + public static String GroupLaunchConfigurationDelegate_waiting; + public static String GroupLaunchConfigurationDelegate_failedWait; public static String GroupLaunchConfigurationDelegate_Delay; public static String GroupLaunchConfigurationDelegate_Delaying; public static String GroupLaunchConfigurationDelegate_Launching; public static String GroupLaunchConfigurationDelegate_mode_debug; - public static String GroupLaunchConfigurationDelegate_mode_inherit; - public static String GroupLaunchConfigurationDelegate_mode_profile; - public static String GroupLaunchConfigurationDelegate_mode_run; - public static String GroupLaunchConfigurationDelegate_None; public static String GroupLaunchConfigurationDelegate_Wait_until_terminated; public static String GroupLaunchConfigurationDelegate_Waiting_for_termination; + public static String GroupLaunchElement_outputRegexp; public static String LaunchConfiguration_0; public static String LaunchConfiguration_11; public static String LaunchConfiguration_13; diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.properties b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.properties index 0e20ad951..eb9e35f51 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.properties +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.properties @@ -26,6 +26,8 @@ DebugPlugin_6=An exception occurred in asynchronous runnable. DebugPlugin_7=An exception occurred while filtering debug events. DebugPlugin_8=An exception occurred while dispatching debug events. EnvironmentVariableResolver_0=Environment variable not specified +GroupLaunchConfigurationDelegate_waiting=Waiting for ''{0}'' on the console of ''{1}''. +GroupLaunchConfigurationDelegate_failedWait=Failed to await output ''{0}'' on the console of ''{1}''. GroupLaunchConfigurationDelegate_Delay=Delay GroupLaunchConfigurationDelegate_Delaying=Delaying next launch by {0} seconds GroupLaunchConfigurationDelegate_Launching=Launching ''{0}'' @@ -36,6 +38,7 @@ GroupLaunchConfigurationDelegate_mode_run=run GroupLaunchConfigurationDelegate_None=None GroupLaunchConfigurationDelegate_Wait_until_terminated=Wait until terminated GroupLaunchConfigurationDelegate_Waiting_for_termination=Waiting for termination of ''{0}'' +GroupLaunchElement_outputRegexp=Wait for console output (regexp) SystemPropertyResolver_0=System property not specified InputStreamMonitor_label=Input Stream Monitor Launch_terminate_failed=Terminate failed diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunch.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunch.java index 2c1b519c6..e611a4686 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunch.java +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunch.java @@ -91,7 +91,8 @@ public class GroupLaunch extends Launch implements ILaunchesListener2 { } if (subLaunches.size() == 0) { - return false; + return fLaunched; // in case we're done launching and there is + // nobody -> terminated } for (ILaunch launch : subLaunches.keySet()) { diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchConfigurationDelegate.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchConfigurationDelegate.java index fccd5f8c3..69885d7cd 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchConfigurationDelegate.java +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchConfigurationDelegate.java @@ -19,6 +19,10 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; @@ -33,10 +37,13 @@ import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.IStatusHandler; import org.eclipse.debug.core.model.ILaunchConfigurationDelegate2; +import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.core.model.LaunchConfigurationDelegate; import org.eclipse.debug.internal.core.DebugCoreMessages; import org.eclipse.debug.internal.core.IInternalDebugCoreConstants; import org.eclipse.debug.internal.core.groups.GroupLaunchElement.GroupElementPostLaunchAction; +import org.eclipse.debug.internal.core.groups.observer.ProcessObserver; +import org.eclipse.debug.internal.core.groups.observer.StreamObserver; import org.eclipse.osgi.util.NLS; /** @@ -193,11 +200,46 @@ public class GroupLaunchConfigurationDelegate extends LaunchConfigurationDelegat } break; + case OUTPUT_REGEXP: + String regexp = (String) le.actionParam; + if (regexp != null) { + monitor.subTask(NLS.bind(DebugCoreMessages.GroupLaunchConfigurationDelegate_waiting, regexp, subLaunch.getLaunchConfiguration().getName())); + if (!waitForOutputMatching(subLaunch, monitor, regexp)) { + throw new RuntimeException(NLS.bind(DebugCoreMessages.GroupLaunchConfigurationDelegate_failedWait, regexp, le.name)); + } + } + + break; + default: assert false : "new post launch action type is missing logic"; //$NON-NLS-1$ } } + // blocks until a specific string is in the log output + private boolean waitForOutputMatching(ILaunch launch, IProgressMonitor m, String regexp) { + int processCount = launch.getProcesses().length; + final ExecutorService executor = Executors.newCachedThreadPool(); + final CountDownLatch countDownLatch = new CountDownLatch(processCount); + Future<Integer> process = null; + for (IProcess p : launch.getProcesses()) { + process = executor.submit(new ProcessObserver(m, p, countDownLatch)); + executor.submit(new StreamObserver(m, p, regexp, countDownLatch)); + } + try { + countDownLatch.await(); + } catch (InterruptedException e) { + // should not happen at all! + } + executor.shutdown(); + // process terminated before condition + if (process == null || process.isDone()) { + return false; + } + // condition matched + return true; + } + /* * (non-Javadoc) * @see @@ -293,6 +335,9 @@ public class GroupLaunchConfigurationDelegate extends LaunchConfigurationDelegat DebugPlugin.log(exc); } } + if (action == GroupElementPostLaunchAction.OUTPUT_REGEXP) { + actionParam = attrs.get(getProp(index, ACTION_PARAM_PROP)); + } el.action = action; el.actionParam = actionParam; if (attrs.containsKey(getProp(index, ADOPT_PROP))) { diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchElement.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchElement.java index 767f90514..5a409b8c8 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchElement.java +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchElement.java @@ -34,7 +34,8 @@ public class GroupLaunchElement { public static enum GroupElementPostLaunchAction { NONE(DebugCoreMessages.GroupLaunchConfigurationDelegate_None), // WAIT_FOR_TERMINATION(DebugCoreMessages.GroupLaunchConfigurationDelegate_Wait_until_terminated), // - DELAY(DebugCoreMessages.GroupLaunchConfigurationDelegate_Delay); + DELAY(DebugCoreMessages.GroupLaunchConfigurationDelegate_Delay), // + OUTPUT_REGEXP(DebugCoreMessages.GroupLaunchElement_outputRegexp); private final String description; diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/observer/ProcessObserver.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/observer/ProcessObserver.java new file mode 100644 index 000000000..7261d9234 --- /dev/null +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/observer/ProcessObserver.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2012, 2016 SSI Schaefer and others. + * 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: + * SSI Schaefer + *******************************************************************************/ +package org.eclipse.debug.internal.core.groups.observer; + +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.debug.core.model.IProcess; + +/** + * The {@code ProcessObserver} observes a given {@linkplain IProcess process} instance and notifies + * a {@linkplain CountDownLatch synchronization object} when the process terminates. + */ +public final class ProcessObserver implements Callable<Integer> { + private final IProcess p; + private final IProgressMonitor pMonitor; + private final CountDownLatch countDownLatch; + + public ProcessObserver(IProgressMonitor monitor, IProcess p, CountDownLatch countDownLatch) { + this.p = p; + this.pMonitor = monitor; + this.countDownLatch = countDownLatch; + } + + @Override + public Integer call() throws Exception { + try { + while (!p.isTerminated() && !pMonitor.isCanceled()) { + TimeUnit.MILLISECONDS.sleep(250); + + if (countDownLatch.getCount() == 0) { + break; + } + } + // check if terminated or timeout + if (p.isTerminated()) { + return p.getExitValue(); + } + return 0; + } finally { + countDownLatch.countDown(); + } + } +} diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/observer/StreamObserver.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/observer/StreamObserver.java new file mode 100644 index 000000000..437382149 --- /dev/null +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/observer/StreamObserver.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2012, 2016 SSI Schaefer and others. + * 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: + * SSI Schaefer + *******************************************************************************/ +package org.eclipse.debug.internal.core.groups.observer; + +import java.util.concurrent.CountDownLatch; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.debug.core.IStreamListener; +import org.eclipse.debug.core.model.IProcess; +import org.eclipse.debug.core.model.IStreamMonitor; +import org.eclipse.debug.core.model.IStreamsProxy; + +/** + * The {@code StreamObserver} observes a given {@linkplain IStreamsProxy output stream} instance and + * notifies a {@linkplain CountDownLatch synchronization object} when a given string appears in the + * output. + */ +public class StreamObserver implements Runnable { + private final String stringPattern; + private final IProcess process; + private final CountDownLatch countDownLatch; + private final IProgressMonitor pMonitor; + + public StreamObserver(IProgressMonitor monitor, IProcess process, String pattern, + CountDownLatch countDownLatch) { + this.process = process; + this.pMonitor = monitor; + this.stringPattern = pattern; + this.countDownLatch = countDownLatch; + } + + @Override + public void run() { + // append wild card if not provided + StringBuilder patternBuilder = new StringBuilder(); + if (!stringPattern.startsWith(".*")) { //$NON-NLS-1$ + patternBuilder.append(".*"); //$NON-NLS-1$ + } + patternBuilder.append(stringPattern); + if (!stringPattern.endsWith(".*")) { //$NON-NLS-1$ + patternBuilder.append(".*"); //$NON-NLS-1$ + } + // create pattern and start listening to the output + final Pattern pattern = Pattern.compile(patternBuilder.toString(), Pattern.MULTILINE); + final IStreamMonitor outputStreamMonitor = process.getStreamsProxy() + .getOutputStreamMonitor(); + outputStreamMonitor.addListener(new IStreamListener() { + @Override + public void streamAppended(String text, IStreamMonitor monitor) { + if (countDownLatch.getCount() == 0) { + outputStreamMonitor.removeListener(this); + return; + } + + Matcher matcher = pattern.matcher(text); + if (!matcher.find() && !pMonitor.isCanceled()) { + return; + } + countDownLatch.countDown(); + } + }); + } +} |