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