Commit | Line | Data |
---|---|---|
eb1bab5b BH |
1 | /********************************************************************** |
2 | * Copyright (c) 2012 Ericsson | |
cfdb727a | 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 | |
cfdb727a AM |
8 | * |
9 | * Contributors: | |
eb1bab5b BH |
10 | * Patrick Tasse - Initial API and implementation |
11 | * Bernd Hufmann - Updated using Executor Framework | |
12 | **********************************************************************/ | |
9315aeee | 13 | package org.eclipse.linuxtools.internal.lttng2.ui.views.control.remote; |
eb1bab5b BH |
14 | |
15 | import java.io.BufferedReader; | |
16 | import java.io.IOException; | |
17 | import java.io.InputStreamReader; | |
18 | import java.util.ArrayList; | |
19 | import java.util.concurrent.Callable; | |
20 | import java.util.concurrent.CancellationException; | |
21 | import java.util.concurrent.ExecutorService; | |
22 | import java.util.concurrent.Executors; | |
23 | import java.util.concurrent.FutureTask; | |
24 | import java.util.concurrent.TimeUnit; | |
25 | import java.util.concurrent.TimeoutException; | |
26 | ||
27 | import org.eclipse.core.commands.ExecutionException; | |
28 | import org.eclipse.core.runtime.IProgressMonitor; | |
29 | import org.eclipse.core.runtime.NullProgressMonitor; | |
9315aeee | 30 | import org.eclipse.linuxtools.internal.lttng2.ui.views.control.messages.Messages; |
eb1bab5b BH |
31 | import org.eclipse.rse.services.shells.HostShellProcessAdapter; |
32 | import org.eclipse.rse.services.shells.IHostShell; | |
33 | import org.eclipse.rse.services.shells.IShellService; | |
34 | ||
35 | /** | |
eb1bab5b | 36 | * <p> |
cfdb727a | 37 | * Implementation of remote command execution using RSE's shell service. |
eb1bab5b | 38 | * </p> |
cfdb727a | 39 | * |
dbd4432d BH |
40 | * @author Patrick Tasse |
41 | * @author Bernd Hufmann | |
eb1bab5b BH |
42 | */ |
43 | public class CommandShell implements ICommandShell { | |
44 | ||
45 | // ------------------------------------------------------------------------ | |
46 | // Constants | |
47 | // ------------------------------------------------------------------------ | |
48 | ||
cfdb727a | 49 | /** String to be echo'ed when running command in shell, used to indicate that the command has finished running */ |
eb1bab5b | 50 | public final static String DONE_MARKUP_STRING = "--RSE:donedonedone:--"; //$NON-NLS-1$ |
cfdb727a AM |
51 | |
52 | /** Command delimiter for shell */ | |
eb1bab5b BH |
53 | public final static String CMD_DELIMITER = "\n"; //$NON-NLS-1$ |
54 | ||
cfdb727a | 55 | /** Shell "echo" command */ |
eb1bab5b BH |
56 | public final static String SHELL_ECHO_CMD = " echo "; //$NON-NLS-1$ |
57 | ||
cfdb727a AM |
58 | /** Default timeout, in milliseconds */ |
59 | private final static int DEFAULT_TIMEOUT_VALUE = 15000; | |
eb1bab5b BH |
60 | |
61 | // ------------------------------------------------------------------------ | |
62 | // Attributes | |
63 | // ------------------------------------------------------------------------ | |
64 | private IRemoteSystemProxy fProxy = null; | |
65 | private IHostShell fHostShell = null; | |
66 | private BufferedReader fBufferReader = null; | |
cfdb727a | 67 | private final ExecutorService fExecutor = Executors.newFixedThreadPool(1); |
eb1bab5b | 68 | private boolean fIsConnected = false; |
cfdb727a | 69 | |
eb1bab5b BH |
70 | // ------------------------------------------------------------------------ |
71 | // Constructors | |
72 | // ------------------------------------------------------------------------ | |
cfdb727a AM |
73 | |
74 | /** | |
75 | * Constructor | |
76 | * | |
77 | * @param proxy | |
78 | * The Remote System proxy | |
79 | */ | |
eb1bab5b BH |
80 | public CommandShell(IRemoteSystemProxy proxy) { |
81 | fProxy = proxy; | |
82 | } | |
83 | ||
84 | // ------------------------------------------------------------------------ | |
85 | // Operations | |
86 | // ------------------------------------------------------------------------ | |
87 | /* | |
88 | * (non-Javadoc) | |
115b4a01 | 89 | * @see org.eclipse.linuxtools.internal.lttng2.ui.views.control.service.ICommandShell#connect() |
eb1bab5b BH |
90 | */ |
91 | @Override | |
92 | public void connect() throws ExecutionException { | |
93 | IShellService shellService = fProxy.getShellService(); | |
94 | Process p = null; | |
95 | try { | |
96 | fHostShell = shellService.launchShell("", new String[0], new NullProgressMonitor()); //$NON-NLS-1$ | |
97 | p = new HostShellProcessAdapter(fHostShell); | |
98 | } catch (Exception e) { | |
99 | throw new ExecutionException(Messages.TraceControl_CommandShellError, e); | |
100 | } | |
101 | fBufferReader = new BufferedReader(new InputStreamReader(p.getInputStream())); | |
102 | fIsConnected = true; | |
103 | ||
104 | // Flush Login messages | |
105 | executeCommand(" ", new NullProgressMonitor(), false); //$NON-NLS-1$ | |
106 | } | |
107 | ||
108 | /* | |
109 | * (non-Javadoc) | |
115b4a01 | 110 | * @see org.eclipse.linuxtools.internal.lttng2.ui.views.control.service.ICommandShell#disconnect() |
eb1bab5b BH |
111 | */ |
112 | @Override | |
113 | public void disconnect() { | |
114 | fIsConnected = false; | |
115 | try { | |
116 | fBufferReader.close(); | |
117 | } catch (IOException e) { | |
118 | // ignore | |
119 | } | |
120 | } | |
121 | ||
122 | /* | |
123 | * (non-Javadoc) | |
115b4a01 | 124 | * @see org.eclipse.linuxtools.internal.lttng2.ui.views.control.service.ICommandShell#executeCommand(java.lang.String, org.eclipse.core.runtime.IProgressMonitor) |
eb1bab5b BH |
125 | */ |
126 | @Override | |
127 | public ICommandResult executeCommand(String command, IProgressMonitor monitor) throws ExecutionException { | |
128 | return executeCommand(command, monitor, true); | |
129 | } | |
130 | ||
131 | /* | |
132 | * (non-Javadoc) | |
115b4a01 | 133 | * @see org.eclipse.linuxtools.internal.lttng2.ui.views.control.service.ICommandShell#executeCommand(java.lang.String, org.eclipse.core.runtime.IProgressMonitor, boolean) |
eb1bab5b BH |
134 | */ |
135 | @Override | |
136 | public ICommandResult executeCommand(final String command, final IProgressMonitor monitor, final boolean checkReturnValue) throws ExecutionException { | |
137 | if (fIsConnected) { | |
138 | FutureTask<CommandResult> future = new FutureTask<CommandResult>(new Callable<CommandResult>() { | |
139 | @Override | |
140 | public CommandResult call() throws IOException, CancellationException { | |
141 | final ArrayList<String> result = new ArrayList<String>(); | |
142 | int returnValue = 0; | |
143 | ||
144 | synchronized (fHostShell) { | |
145 | fHostShell.writeToShell(formatShellCommand(command)); | |
146 | String nextLine; | |
147 | while ((nextLine = fBufferReader.readLine()) != null) { | |
148 | ||
149 | if (monitor.isCanceled()) { | |
150 | flushInput(); | |
cfdb727a | 151 | throw new CancellationException(); |
eb1bab5b BH |
152 | } |
153 | ||
154 | if (nextLine.contains(DONE_MARKUP_STRING) && nextLine.contains(SHELL_ECHO_CMD)) { | |
155 | break; | |
156 | } | |
157 | } | |
158 | ||
159 | while ((nextLine = fBufferReader.readLine()) != null) { | |
160 | // check if job was cancelled | |
161 | if (monitor.isCanceled()) { | |
162 | flushInput(); | |
cfdb727a | 163 | throw new CancellationException(); |
eb1bab5b BH |
164 | } |
165 | ||
166 | if (!nextLine.contains(DONE_MARKUP_STRING)) { | |
167 | result.add(nextLine); | |
168 | } else { | |
169 | if (checkReturnValue) { | |
170 | returnValue = Integer.valueOf(nextLine.substring(DONE_MARKUP_STRING.length()+1)); | |
171 | } | |
172 | break; | |
173 | } | |
174 | } | |
175 | ||
176 | flushInput(); | |
177 | } | |
178 | return new CommandResult(returnValue, result.toArray(new String[result.size()])); | |
179 | } | |
180 | }); | |
181 | ||
182 | fExecutor.execute(future); | |
183 | ||
184 | try { | |
185 | return future.get(DEFAULT_TIMEOUT_VALUE, TimeUnit.MILLISECONDS); | |
186 | } catch (java.util.concurrent.ExecutionException ex) { | |
187 | throw new ExecutionException(Messages.TraceControl_ExecutionFailure, ex); | |
188 | } catch (InterruptedException ex) { | |
189 | throw new ExecutionException(Messages.TraceControl_ExecutionCancelled, ex); | |
190 | } catch (TimeoutException ex) { | |
191 | throw new ExecutionException(Messages.TraceControl_ExecutionTimeout, ex); | |
192 | } | |
193 | } | |
194 | throw new ExecutionException(Messages.TraceControl_ShellNotConnected, null); | |
195 | } | |
cfdb727a | 196 | |
eb1bab5b BH |
197 | // ------------------------------------------------------------------------ |
198 | // Helper methods | |
199 | // ------------------------------------------------------------------------ | |
200 | /** | |
cfdb727a | 201 | * Flushes the buffer reader |
eb1bab5b BH |
202 | * @throws IOException |
203 | */ | |
204 | private void flushInput() throws IOException { | |
205 | char[] cbuf = new char[1]; | |
206 | while (fBufferReader.ready()) { | |
207 | if (fBufferReader.read(cbuf, 0, 1) == -1) { | |
208 | break; | |
209 | } | |
210 | } | |
211 | } | |
cfdb727a | 212 | |
eb1bab5b | 213 | /** |
cfdb727a AM |
214 | * Format the command to be sent into the shell command with the done markup |
215 | * string. The done markup string is needed so we can tell that end of | |
216 | * output has been reached. | |
217 | * | |
eb1bab5b | 218 | * @param cmd |
cfdb727a | 219 | * The original command |
eb1bab5b BH |
220 | * @return formatted command string |
221 | */ | |
0a78d11a | 222 | private static String formatShellCommand(String cmd) { |
cfdb727a | 223 | if (cmd == null || cmd.equals("")) { //$NON-NLS-1$ |
eb1bab5b | 224 | return cmd; |
cfdb727a | 225 | } |
eb1bab5b | 226 | StringBuffer formattedCommand = new StringBuffer(); |
4775bcbf | 227 | // Make a multi line command by using \ and \r. This is needed for matching |
cfdb727a | 228 | // the DONE_MARKUP_STRING in echoed command when having a long command |
4775bcbf | 229 | // (bigger than max SSH line) |
cfdb727a | 230 | formattedCommand.append(cmd).append("\\\r;"); //$NON-NLS-1$ |
eb1bab5b BH |
231 | formattedCommand.append(SHELL_ECHO_CMD).append(DONE_MARKUP_STRING); |
232 | formattedCommand.append(" $?"); //$NON-NLS-1$ | |
233 | formattedCommand.append(CMD_DELIMITER); | |
234 | return formattedCommand.toString(); | |
235 | } | |
236 | ||
237 | } |