blob: 5caad088ad65b70646b674297e461eb854969ea0 [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2012, 2015 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Patrick Tasse - Initial API and implementation
* Bernd Hufmann - Updated using Executor Framework
* Markus Schorn - Bug 448058: Use org.eclipse.remote in favor of RSE
* Bernd Hufmann - Update to org.eclipse.remote API 2.0
**********************************************************************/
package org.eclipse.tracecompass.internal.tmf.remote.core.shell;
import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.remote.core.IRemoteConnection;
import org.eclipse.remote.core.IRemoteProcess;
import org.eclipse.remote.core.IRemoteProcessService;
import org.eclipse.tracecompass.internal.tmf.remote.core.messages.Messages;
import org.eclipse.tracecompass.internal.tmf.remote.core.preferences.TmfRemotePreferences;
import org.eclipse.tracecompass.tmf.remote.core.shell.ICommandInput;
import org.eclipse.tracecompass.tmf.remote.core.shell.ICommandOutputListener;
import org.eclipse.tracecompass.tmf.remote.core.shell.ICommandResult;
import org.eclipse.tracecompass.tmf.remote.core.shell.ICommandShell;
/**
* <p>
* Implementation of remote command execution using IRemoteConnection.
* </p>
*
* @author Patrick Tasse
* @author Bernd Hufmann
*/
public class CommandShell implements ICommandShell {
// ------------------------------------------------------------------------
// Attributes
// ------------------------------------------------------------------------
private final IRemoteConnection fConnection;
private final ExecutorService fExecutor = checkNotNull(Executors.newFixedThreadPool(1));
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
/**
* Create a new command shell
*
* @param connection the remote connection for this shell
*/
public CommandShell(IRemoteConnection connection) {
fConnection = connection;
}
// ------------------------------------------------------------------------
// Operations
// ------------------------------------------------------------------------
@Override
public void dispose() {
fExecutor.shutdown();
}
@Override
public ICommandInput createCommand() {
return new CommandInput();
}
@Override
public ICommandResult executeCommand(final ICommandInput command, final IProgressMonitor aMonitor) throws ExecutionException {
return executeCommand(command, aMonitor, null);
}
@Override
public ICommandResult executeCommand(final ICommandInput command, final IProgressMonitor aMonitor, ICommandOutputListener listener) throws ExecutionException {
if (fConnection.isOpen()) {
FutureTask<CommandResult> future = new FutureTask<>(new Callable<CommandResult>() {
@Override
public CommandResult call() throws IOException, InterruptedException {
IProgressMonitor monitor = aMonitor;
if (monitor == null) {
monitor = new NullProgressMonitor();
}
if (!monitor.isCanceled()) {
IRemoteProcessService service = fConnection.getService(IRemoteProcessService.class);
if (service == null) {
return new CommandResult(1, new @NonNull String[0], new @NonNull String[] { nullToEmptyString(Messages.RemoteConnection_ServiceNotDefined) });
}
IRemoteProcess process = service.getProcessBuilder(command.getInput()).start();
InputReader stdout = new InputReader(checkNotNull(process.getInputStream()), listener, true);
InputReader stderr = new InputReader(checkNotNull(process.getErrorStream()), listener, false);
try {
stdout.waitFor(monitor);
stderr.waitFor(monitor);
if (!monitor.isCanceled()) {
return createResult(process.waitFor(), stdout.toString(), stderr.toString());
}
} catch (OperationCanceledException e) {
} catch (InterruptedException e) {
return new CommandResult(1, new @NonNull String[0],
new @NonNull String[] { checkNotNull(e.getMessage()) });
} finally {
stdout.stop();
stderr.stop();
process.destroy();
}
}
return new CommandResult(1, new @NonNull String[0], new @NonNull String[] { "cancelled" }); //$NON-NLS-1$
}
});
fExecutor.execute(future);
try {
return checkNotNull(future.get(TmfRemotePreferences.getCommandTimeout(), TimeUnit.SECONDS));
} catch (InterruptedException ex) {
throw new ExecutionException(Messages.RemoteConnection_ExecutionCancelled, ex);
} catch (TimeoutException ex) {
throw new ExecutionException(Messages.RemoteConnection_ExecutionTimeout, ex);
} catch (Exception ex) {
throw new ExecutionException(Messages.RemoteConnection_ExecutionFailure, ex);
}
finally {
future.cancel(true);
}
}
throw new ExecutionException(Messages.RemoteConnection_ShellNotConnected, null);
}
// ------------------------------------------------------------------------
// Helper methods
// ------------------------------------------------------------------------
private static CommandResult createResult(int origResult, String origStdout, String origStderr) {
final int result;
final String stdout, stderr;
result = origResult;
stdout = origStdout;
stderr = origStderr;
@NonNull String[] output = splitLines(stdout);
@NonNull String[] error = splitLines(stderr);
return new CommandResult(result, output, error);
}
private static @NonNull String @NonNull [] splitLines(String output) {
return output.split("\\r?\\n"); //$NON-NLS-1$
}
}