/*******************************************************************************
* Copyright (c) 2012 Pivotal Software, Inc.
* 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:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.grails.ide.eclipse.longrunning.client;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.TimeoutException;
import org.eclipse.core.runtime.CoreException;
import org.grails.ide.eclipse.commands.GrailsCommand;
import org.grails.ide.eclipse.core.GrailsCoreActivator;
import org.grails.ide.eclipse.core.launch.SynchLaunch.ILaunchResult;
import org.grails.ide.eclipse.core.launch.SynchLaunch.ResultFromTerminatedLaunch;
import org.grails.ide.eclipse.core.util.LimitedByteArrayOutputStream;
import org.grails.ide.eclipse.longrunning.Console;
import org.grails.ide.eclipse.longrunning.Grails20OutputStreamCleaner;
import org.grails.ide.eclipse.longrunning.GrailsProcessManager;
import org.springsource.ide.eclipse.commons.core.util.MultiplexingOutputStream;
/**
* An instance of this class represents and tracks the execution of a Grails command
* in the long running process execution infrastructure. It plays a similar role
* to an IProcess in the DebugUI but it doesn't actually correspond to a process
* in the same sense. This is because a single process can be used to execute
* multiple commands.
*
* @author Kris De Volder
*/
public class GrailsCommandExecution extends ExecutionEventSource {
private GrailsClient process;
private GrailsCommand cmd;
private Console console;
private ByteArrayOutputStream bytesOut;
private ByteArrayOutputStream bytesErr;
private boolean isTerminated = false;
public GrailsCommandExecution(GrailsClient process, GrailsCommand cmd) {
this.process = process;
this.cmd = cmd;
this.bytesOut = new LimitedByteArrayOutputStream(GrailsCoreActivator.getDefault().getGrailsCommandOutputLimit());
this.bytesErr = new LimitedByteArrayOutputStream(GrailsCoreActivator.getDefault().getGrailsCommandOutputLimit());
this.console = buildConsole(cmd, bytesOut, bytesErr);
// this.console.setExection(this);
}
protected Console buildConsole(GrailsCommand cmd, ByteArrayOutputStream bytesOut, ByteArrayOutputStream bytesErr) {
if (cmd.isShowOutput()) {
//Create a UI console and send output there.
Console console = GrailsProcessManager.consoleProvider.getConsole(cmd.getCommand(), this);
OutputStream out = clean(new MultiplexingOutputStream(bytesOut, console.getOutputStream()));
OutputStream err = new MultiplexingOutputStream(bytesErr, console.getErrorStream());
return Console.make(console.getInputStream(), out, err);
} else {
//Create a dummy console that only sends output to 'bytes'
return Console.make(clean(bytesOut), bytesErr);
}
}
private OutputStream clean(OutputStream out) {
if (GrailsCoreActivator.getDefault().getCleanOutput()) {
return new Grails20OutputStreamCleaner(out);
}
return out;
}
/**
* Starts the execution and blocks while waiting for command to terminate.
*/
public ResultFromTerminatedLaunch execute() throws CoreException {
try {
ResultFromTerminatedLaunch result = null;
final String cmdInfo = cmd.toString();
try {
int code = process.executeCommand(cmd, console);
result = new ResultFromTerminatedLaunch(cmdInfo, code, bytesOut.toString(), bytesErr.toString());
} catch (TimeoutException e) {
String extraInfo = e.getMessage();
if (extraInfo!=null) {
try {
bytesErr.write(extraInfo.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e1) {
} catch (IOException e1) {
}
}
result = new ResultFromTerminatedLaunch(cmdInfo, ILaunchResult.EXIT_TIMEOUT, bytesOut.toString(), bytesErr.toString());
} catch (final Exception e) {
result = new ResultFromTerminatedLaunch(cmdInfo, -999, bytesOut.toString(), bytesErr.toString()) {
public Exception getException() {
return e;
}
};
} finally {
try {
console.close();
} catch (IOException e) {
}
}
if (result.isOK()) {
cmd.runPostOp();
return result;
} else {
throw result.getCoreException();
}
} finally {
isTerminated = true;
notifyExecutionListeners();
clearListeners(); //No more state changes possible in the future!
}
}
public boolean canTerminate() {
return !isTerminated;
}
/**
* Warning! Only use this method for forceful termination. It will kill the long running
* process as there is no other way to 'nicely ask' grails to stop working on its current
* command.
*/
public void destroy() {
if (!isTerminated && process!=null) {
process.destroy();
process = null;
}
}
public boolean isTerminated() {
return isTerminated;
}
}