blob: 3f303383a9ba1ab09a29fca44c83548dbc5ff20a [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.osgi.util.NLS;
import org.eclipse.ptp.remote.core.IRemoteProcess;
import org.eclipse.ptp.remote.core.IRemoteProcessBuilder;
import org.eclipse.ptp.rm.core.RMCorePlugin;
import org.eclipse.ptp.rm.core.messages.Messages;
import org.eclipse.ptp.rm.core.utils.DebugUtil;
import org.eclipse.ptp.utils.core.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 {
protected static final String EMPTY_STRING = ""; //$NON-NLS-1$
protected String fCommand;
protected String fInterruptedErrorMessage;
protected String fProcessErrorMessage;
protected String fParsingErrorMessage;
protected int fReschedule = 0;
protected IRemoteProcess fJobProcess;
protected AbstractToolRuntimeSystem fRtSystem;
protected IProgressMonitor fMonitor;
/**
* 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>.
* @param monitor progress monitor to use, or null to use system progress monitor
*/
public AbstractRemoteCommandJob(AbstractToolRuntimeSystem rtSystem, String name, String command, String interruptedErrorMessage, String processErrorMessage,
String parsingErrorMessage, IProgressMonitor monitor) {
super(name);
fRtSystem = rtSystem;
fCommand = command;
fInterruptedErrorMessage = interruptedErrorMessage;
fProcessErrorMessage = processErrorMessage;
fParsingErrorMessage = parsingErrorMessage;
fMonitor = monitor;
}
/**
* 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.
* @param monitor progress monitor to use, or null to use system progress monitor
*/
public AbstractRemoteCommandJob(AbstractToolRuntimeSystem rtSystem, String name, String command, String interruptedErrorMessage, String processErrorMessage,
String parsingErrorMessage, int reschedule, IProgressMonitor monitor) {
super(name);
fRtSystem = rtSystem;
fCommand = command;
fInterruptedErrorMessage = interruptedErrorMessage;
fProcessErrorMessage = processErrorMessage;
fParsingErrorMessage = parsingErrorMessage;
fReschedule = reschedule;
fMonitor = monitor;
}
/**
* Parses output of the command.
* @param output Reader with output from the command.
* @throws CoreException Parsing failed
*/
protected abstract IStatus parse(BufferedReader output) throws CoreException;
/* (non-Javadoc)
* @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
Assert.isNotNull(fRtSystem);
Assert.isNotNull(fCommand);
Assert.isTrue(!fCommand.trim().equals(EMPTY_STRING));
if (fMonitor == null) {
fMonitor = monitor;
}
/*
* Proposed enhancements
* TODO: Substitution of variables in the command string
* TODO: Substitution of attributes in the command string
* TODO: Append remote installation path to launch command.
* TODO: Extend class to provide XML SAX parser.
*/
ArgumentParser argumentParser = new ArgumentParser(fCommand);
List<String> arguments = argumentParser.getTokenList();
if (fMonitor.isCanceled()) {
return new Status(IStatus.CANCEL, RMCorePlugin.PLUGIN_ID, fInterruptedErrorMessage, null);
}
try {
IRemoteProcessBuilder cmdBuilder = fRtSystem.createProcessBuilder(arguments);
synchronized (this) {
DebugUtil.trace(DebugUtil.COMMAND_TRACING, "Run command: {0}", fCommand); //$NON-NLS-1$
fJobProcess = cmdBuilder.start();
}
} catch (IOException e) {
return new Status(IStatus.ERROR, RMCorePlugin.PLUGIN_ID, fProcessErrorMessage, e);
}
if (fMonitor.isCanceled()) {
return new Status(IStatus.CANCEL, RMCorePlugin.PLUGIN_ID, fInterruptedErrorMessage, null);
}
BufferedReader stdout = new BufferedReader(new InputStreamReader(fJobProcess.getInputStream()));
IStatus parseStatus = parse(stdout);
if (parseStatus.getSeverity() == IStatus.ERROR) {
DebugUtil.error(DebugUtil.COMMAND_TRACING_MORE, "Command parsing failed: {0}", parseStatus.getMessage()); //$NON-NLS-1$
return parseStatus;
}
/*
* Wait for job to complete so that we can check for exit value of command.
*/
while (!fJobProcess.isCompleted() && !fMonitor.isCanceled()) {
System.out.println("waiting for job"); //$NON-NLS-1$
synchronized (this) {
wait(500);
}
}
if (fMonitor.isCanceled()) {
return new Status(IStatus.CANCEL, RMCorePlugin.PLUGIN_ID, fInterruptedErrorMessage, null);
}
if (fJobProcess.exitValue() != 0) {
return new Status(IStatus.ERROR, RMCorePlugin.getDefault().getBundle().getSymbolicName(),
NLS.bind("Command failed with exit status {0}", Integer.valueOf(fJobProcess.exitValue())), null); //$NON-NLS-1$
}
if (fMonitor.isCanceled()) {
return new Status(IStatus.CANCEL, RMCorePlugin.PLUGIN_ID, fInterruptedErrorMessage, null);
}
try {
DebugUtil.trace(DebugUtil.COMMAND_TRACING_MORE, "Command: waiting to finish."); //$NON-NLS-1$
fJobProcess.waitFor();
} catch (InterruptedException e) {
return new Status(IStatus.INFO, RMCorePlugin.PLUGIN_ID, fInterruptedErrorMessage, e);
}
DebugUtil.trace(DebugUtil.COMMAND_TRACING_MORE, "Command: exit value {0}.", Integer.valueOf(fJobProcess.exitValue())); //$NON-NLS-1$
if (fReschedule > 0) {
DebugUtil.trace(DebugUtil.COMMAND_TRACING_MORE, "Command: reschedule in {0} miliseconds.", Integer.valueOf(fReschedule)); //$NON-NLS-1$
schedule(fReschedule);
}
return parseStatus;
} catch (Exception e) {
DebugUtil.error(DebugUtil.COMMAND_TRACING_MORE, "Command failed: {0}", e); //$NON-NLS-1$
return new Status(IStatus.ERROR, RMCorePlugin.PLUGIN_ID, Messages.AbstractRemoteCommandJob_Exception_InternalError, e);
} finally {
synchronized (this) {
if (fJobProcess != null) {
fJobProcess.destroy();
}
fJobProcess = null;
}
}
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.jobs.Job#canceling()
*/
@Override
protected void canceling() {
synchronized (this) {
if (fJobProcess != null) {
fJobProcess.destroy();
fJobProcess = null;
}
}
}
}