blob: ab0f6b5ccd64c44de0d7f5f0dc9af4b26e83d7fc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 IBM Corporation.
* 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:
* IBM Corporation - initial API and implementation
******************************************************************************/
package org.eclipse.ptp.rm.core.rtsystem;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.ptp.remote.core.IRemoteProcess;
import org.eclipse.ptp.remote.core.IRemoteProcessBuilder;
import org.eclipse.ptp.rm.core.ToolsRMPlugin;
import org.eclipse.ptp.rm.core.messages.Messages;
import org.eclipse.ptp.rm.core.utils.DebugUtil;
import org.eclipse.ptp.utils.core.linux.ArgumentParser;
/**
* Abstract implementation of a job that executes a command on the remote host and parses its output.
* The job might be automatically rescheduled if created with the proper constructor.
* Use this class a useful starting point to implement discover and monitor jobs.
*
* @author Daniel Felix Ferber
*/
abstract public class AbstractRemoteCommandJob extends Job {
static final String EMPTY_STRING = ""; //$NON-NLS-1$
String command;
String interruptedErrorMessage;
String processErrorMessage;
String parsingErrorMessage;
int reschedule = 0;
IRemoteProcess jobProcess;
AbstractToolRuntimeSystem rtSystem;
/**
* A job for a remote command that is run only once.
* @param name Name of the job
* @param command Command executed remotely
* @param interruptedErrorMessage Error message if the job is interrupted or <code>null</code>.
* @param processErrorMessage Error message remote command fails or <code>null</code>.
* @param parsingErrorMessage Error message if the output of the remote command cannot be parsed or <code>null</code>.
*/
public AbstractRemoteCommandJob(AbstractToolRuntimeSystem rtSystem, String name, String command, String interruptedErrorMessage, String processErrorMessage,
String parsingErrorMessage) {
super(name);
this.rtSystem = rtSystem;
this.command = command;
this.interruptedErrorMessage = interruptedErrorMessage;
this.processErrorMessage = processErrorMessage;
this.parsingErrorMessage = parsingErrorMessage;
}
/**
* A job for a remote command that is run periodically.
* @param name Name of the job
* @param command Command executed remotely
* @param interruptedErrorMessage Error message if the job is interrupted or <code>null</code>.
* @param processErrorMessage Error message remote command fails or <code>null</code>.
* @param parsingErrorMessage Error message if the output of the remote command cannot be parsed or <code>null</code>.
* @param reschedule Time in milliseconds between executions of the command.
*/
public AbstractRemoteCommandJob(AbstractToolRuntimeSystem rtSystem, String name, String command, String interruptedErrorMessage, String processErrorMessage,
String parsingErrorMessage, int reschedule) {
super(name);
this.rtSystem = rtSystem;
this.command = command;
this.interruptedErrorMessage = interruptedErrorMessage;
this.processErrorMessage = processErrorMessage;
this.parsingErrorMessage = parsingErrorMessage;
this.reschedule = reschedule;
}
/**
* Parses output of the command.
* @param output Reader with output from the command.
* @throws CoreException Parsing failed
*/
protected abstract void parse(BufferedReader output) throws CoreException;
/**
* Default implementation of the job.
*/
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
Assert.isNotNull(rtSystem);
Assert.isNotNull(command);
Assert.isTrue(! command.trim().equals(EMPTY_STRING));
/*
* Proposed enhancements
* TODO: Substitution of variables in the command string
* TODO: Substitution of attributes in the command strng
* TODO: Append remote installation path to launch command.
* TODO: Extend class to provide XML SAX parser.
*/
ArgumentParser argumentParser = new ArgumentParser(command);
List<String> arguments = argumentParser.getTokenList();
checkCancel(monitor);
try {
IRemoteProcessBuilder cmdBuilder = rtSystem.createProcessBuilder(arguments);
synchronized (this) {
DebugUtil.trace(DebugUtil.COMMAND_TRACING, "Run command: {0}", command); //$NON-NLS-1$
jobProcess = cmdBuilder.start();
}
} catch (IOException e) {
throw new CoreException(new Status(IStatus.ERROR, ToolsRMPlugin.PLUGIN_ID, processErrorMessage, e));
}
checkCancel(monitor);
BufferedReader stdout = new BufferedReader(new InputStreamReader(jobProcess.getInputStream()));
IStatus parseStatus = Status.OK_STATUS;
try {
parse(stdout);
} catch (CoreException e) {
DebugUtil.error(DebugUtil.COMMAND_TRACING_MORE, "Command parsing failed: {0}", e); //$NON-NLS-1$
parseStatus = e.getStatus();
if (parseStatus.getSeverity() == IStatus.ERROR)
throw e;
}
checkCancel(monitor);
try {
DebugUtil.trace(DebugUtil.COMMAND_TRACING_MORE, "Command: waiting to finish."); //$NON-NLS-1$
jobProcess.waitFor();
} catch (InterruptedException e) {
throw new CoreException(new Status(IStatus.INFO, ToolsRMPlugin.PLUGIN_ID, interruptedErrorMessage, e));
}
DebugUtil.trace(DebugUtil.COMMAND_TRACING_MORE, "Command: exit value {0}.", jobProcess.exitValue()); //$NON-NLS-1$
if (reschedule > 0) {
DebugUtil.trace(DebugUtil.COMMAND_TRACING_MORE, "Command: reschedule in {0} miliseconds.", reschedule); //$NON-NLS-1$
schedule(reschedule);
}
return parseStatus;
} catch (CoreException e) {
DebugUtil.error(DebugUtil.COMMAND_TRACING_MORE, "Command failed: {0}", e); //$NON-NLS-1$
return new Status(IStatus.ERROR, ToolsRMPlugin.getDefault().getBundle().getSymbolicName(), Messages.AbstractRemoteCommandJob_Exception_CommandFailed, e);
} catch (Exception e) {
DebugUtil.error(DebugUtil.COMMAND_TRACING_MORE, "Command failed: {0}", e); //$NON-NLS-1$
return new Status(IStatus.ERROR, ToolsRMPlugin.PLUGIN_ID, Messages.AbstractRemoteCommandJob_Exception_InternalError, e);
} finally {
synchronized (this) {
if (jobProcess != null) {
jobProcess.destroy();
}
jobProcess = null;
}
}
}
private void checkCancel(IProgressMonitor monitor) throws CoreException {
if (monitor.isCanceled())
throw new CoreException(new Status(IStatus.INFO, ToolsRMPlugin.PLUGIN_ID, interruptedErrorMessage, null));
}
@Override
protected void canceling() {
synchronized (this) {
if (jobProcess != null) {
jobProcess.destroy();
jobProcess = null;
}
}
}
}