Commit | Line | Data |
---|---|---|
eb1bab5b | 1 | /********************************************************************** |
ed902a2b | 2 | * Copyright (c) 2012, 2015 Ericsson |
ea21cd65 | 3 | * |
eb1bab5b BH |
4 | * All rights reserved. This program and the accompanying materials are |
5 | * made available under the terms of the Eclipse Public License v1.0 which | |
6 | * accompanies this distribution, and is available at | |
7 | * http://www.eclipse.org/legal/epl-v10.html | |
ea21cd65 AM |
8 | * |
9 | * Contributors: | |
eb1bab5b BH |
10 | * Patrick Tasse - Initial API and implementation |
11 | * Bernd Hufmann - Updated using Executor Framework | |
b732adaa | 12 | * Markus Schorn - Bug 448058: Use org.eclipse.remote in favor of RSE |
533d0bc3 | 13 | * Bernd Hufmann - Update to org.eclipse.remote API 2.0 |
eb1bab5b | 14 | **********************************************************************/ |
13729cbc BH |
15 | package org.eclipse.tracecompass.internal.tmf.remote.core.shell; |
16 | ||
17 | import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; | |
2f79cfbc | 18 | import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString; |
eb1bab5b | 19 | |
eb1bab5b | 20 | import java.io.IOException; |
eb1bab5b | 21 | import java.util.concurrent.Callable; |
eb1bab5b BH |
22 | import java.util.concurrent.ExecutorService; |
23 | import java.util.concurrent.Executors; | |
24 | import java.util.concurrent.FutureTask; | |
25 | import java.util.concurrent.TimeUnit; | |
26 | import java.util.concurrent.TimeoutException; | |
27 | ||
28 | import org.eclipse.core.commands.ExecutionException; | |
29 | import org.eclipse.core.runtime.IProgressMonitor; | |
13729cbc | 30 | import org.eclipse.core.runtime.NullProgressMonitor; |
b732adaa | 31 | import org.eclipse.core.runtime.OperationCanceledException; |
f4648c68 | 32 | import org.eclipse.jdt.annotation.NonNull; |
b732adaa MS |
33 | import org.eclipse.remote.core.IRemoteConnection; |
34 | import org.eclipse.remote.core.IRemoteProcess; | |
533d0bc3 | 35 | import org.eclipse.remote.core.IRemoteProcessService; |
ec619615 BH |
36 | import org.eclipse.tracecompass.internal.tmf.remote.core.messages.Messages; |
37 | import org.eclipse.tracecompass.internal.tmf.remote.core.preferences.TmfRemotePreferences; | |
364dcfaf | 38 | import org.eclipse.tracecompass.tmf.remote.core.shell.ICommandInput; |
90700072 | 39 | import org.eclipse.tracecompass.tmf.remote.core.shell.ICommandOutputListener; |
13729cbc BH |
40 | import org.eclipse.tracecompass.tmf.remote.core.shell.ICommandResult; |
41 | import org.eclipse.tracecompass.tmf.remote.core.shell.ICommandShell; | |
eb1bab5b BH |
42 | |
43 | /** | |
eb1bab5b | 44 | * <p> |
b732adaa | 45 | * Implementation of remote command execution using IRemoteConnection. |
eb1bab5b | 46 | * </p> |
cfdb727a | 47 | * |
dbd4432d BH |
48 | * @author Patrick Tasse |
49 | * @author Bernd Hufmann | |
eb1bab5b BH |
50 | */ |
51 | public class CommandShell implements ICommandShell { | |
52 | ||
eb1bab5b BH |
53 | // ------------------------------------------------------------------------ |
54 | // Attributes | |
55 | // ------------------------------------------------------------------------ | |
13729cbc BH |
56 | private final IRemoteConnection fConnection; |
57 | private final ExecutorService fExecutor = checkNotNull(Executors.newFixedThreadPool(1)); | |
ea21cd65 | 58 | |
eb1bab5b BH |
59 | // ------------------------------------------------------------------------ |
60 | // Constructors | |
61 | // ------------------------------------------------------------------------ | |
ea21cd65 AM |
62 | |
63 | /** | |
64 | * Create a new command shell | |
65 | * | |
b732adaa | 66 | * @param connection the remote connection for this shell |
ea21cd65 | 67 | */ |
b732adaa MS |
68 | public CommandShell(IRemoteConnection connection) { |
69 | fConnection = connection; | |
eb1bab5b BH |
70 | } |
71 | ||
72 | // ------------------------------------------------------------------------ | |
73 | // Operations | |
74 | // ------------------------------------------------------------------------ | |
11252342 | 75 | |
eb1bab5b | 76 | @Override |
13729cbc | 77 | public void dispose() { |
b732adaa | 78 | fExecutor.shutdown(); |
eb1bab5b BH |
79 | } |
80 | ||
eb1bab5b | 81 | @Override |
364dcfaf BH |
82 | public ICommandInput createCommand() { |
83 | return new CommandInput(); | |
84 | } | |
85 | ||
86 | @Override | |
c07150f8 | 87 | public ICommandResult executeCommand(final ICommandInput command, final IProgressMonitor aMonitor) throws ExecutionException { |
90700072 BH |
88 | return executeCommand(command, aMonitor, null); |
89 | } | |
90 | ||
91 | @Override | |
92 | public ICommandResult executeCommand(final ICommandInput command, final IProgressMonitor aMonitor, ICommandOutputListener listener) throws ExecutionException { | |
b732adaa | 93 | if (fConnection.isOpen()) { |
e0838ca1 | 94 | FutureTask<CommandResult> future = new FutureTask<>(new Callable<CommandResult>() { |
eb1bab5b | 95 | @Override |
b732adaa | 96 | public CommandResult call() throws IOException, InterruptedException { |
13729cbc BH |
97 | IProgressMonitor monitor = aMonitor; |
98 | if (monitor == null) { | |
99 | monitor = new NullProgressMonitor(); | |
100 | } | |
101 | if (!monitor.isCanceled()) { | |
2f79cfbc AM |
102 | IRemoteProcessService service = fConnection.getService(IRemoteProcessService.class); |
103 | if (service == null) { | |
104 | return new CommandResult(1, new @NonNull String[0], new @NonNull String[] { nullToEmptyString(Messages.RemoteConnection_ServiceNotDefined) }); | |
105 | } | |
106 | IRemoteProcess process = service.getProcessBuilder(command.getInput()).start(); | |
90700072 BH |
107 | InputReader stdout = new InputReader(checkNotNull(process.getInputStream()), listener, true); |
108 | InputReader stderr = new InputReader(checkNotNull(process.getErrorStream()), listener, false); | |
b732adaa MS |
109 | |
110 | try { | |
111 | stdout.waitFor(monitor); | |
112 | stderr.waitFor(monitor); | |
13729cbc | 113 | if (!monitor.isCanceled()) { |
533d0bc3 | 114 | return createResult(process.waitFor(), stdout.toString(), stderr.toString()); |
1a18ada9 | 115 | } |
b732adaa MS |
116 | } catch (OperationCanceledException e) { |
117 | } catch (InterruptedException e) { | |
118 | return new CommandResult(1, new String[0], new String[] {e.getMessage()}); | |
119 | } finally { | |
120 | stdout.stop(); | |
121 | stderr.stop(); | |
122 | process.destroy(); | |
d6fc6e1b | 123 | } |
eb1bab5b | 124 | } |
b732adaa | 125 | return new CommandResult(1, new String[0], new String[] {"cancelled"}); //$NON-NLS-1$ |
eb1bab5b BH |
126 | } |
127 | }); | |
128 | ||
129 | fExecutor.execute(future); | |
130 | ||
131 | try { | |
13729cbc | 132 | return checkNotNull(future.get(TmfRemotePreferences.getCommandTimeout(), TimeUnit.SECONDS)); |
eb1bab5b | 133 | } catch (InterruptedException ex) { |
faeb45e2 | 134 | throw new ExecutionException(Messages.RemoteConnection_ExecutionCancelled, ex); |
eb1bab5b | 135 | } catch (TimeoutException ex) { |
faeb45e2 | 136 | throw new ExecutionException(Messages.RemoteConnection_ExecutionTimeout, ex); |
13729cbc | 137 | } catch (Exception ex) { |
faeb45e2 | 138 | throw new ExecutionException(Messages.RemoteConnection_ExecutionFailure, ex); |
13729cbc BH |
139 | } |
140 | finally { | |
b732adaa | 141 | future.cancel(true); |
eb1bab5b BH |
142 | } |
143 | } | |
faeb45e2 | 144 | throw new ExecutionException(Messages.RemoteConnection_ShellNotConnected, null); |
eb1bab5b | 145 | } |
cfdb727a | 146 | |
eb1bab5b BH |
147 | // ------------------------------------------------------------------------ |
148 | // Helper methods | |
149 | // ------------------------------------------------------------------------ | |
b732adaa | 150 | |
533d0bc3 | 151 | private static CommandResult createResult(int origResult, String origStdout, String origStderr) { |
b732adaa MS |
152 | final int result; |
153 | final String stdout, stderr; | |
533d0bc3 BH |
154 | result = origResult; |
155 | stdout = origStdout; | |
156 | stderr = origStderr; | |
b732adaa | 157 | String[] output = splitLines(stdout); |
5de50ac9 | 158 | String[] error = splitLines(stderr); |
b732adaa | 159 | return new CommandResult(result, output, error); |
eb1bab5b | 160 | } |
cfdb727a | 161 | |
4c4e2816 | 162 | private static String @NonNull [] splitLines(String output) { |
13729cbc | 163 | return checkNotNull(output.split("\\r?\\n")); //$NON-NLS-1$ |
eb1bab5b | 164 | } |
eb1bab5b | 165 | } |