/*
* ###
* Android Maven Plugin - android-maven-plugin
*
* Copyright (C) 1999 - 2012 Photon Infotech Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ###
*/
/*
* Copyright (C) 2009 Jayway AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.photon.maven.plugins.android;
import org.apache.maven.plugin.logging.Log;
import org.codehaus.plexus.util.cli.*;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
*/
public interface CommandExecutor {
/**
* Sets the plexus logger.
*
* @param logger the plexus logger
*/
void setLogger(Log logger);
/**
* Executes the command for the specified executable and list of command options.
*
* @param executable the name of the executable (csc, xsd, etc).
* @param commands the command options for the compiler/executable
* @throws ExecutionException if compiler or executable writes anything to the standard error stream or if the process
* returns a process result != 0.
*/
void executeCommand(String executable, List<String> commands)
throws ExecutionException;
/**
* Executes the command for the specified executable and list of command options.
*
* @param executable the name of the executable (csc, xsd, etc).
* @param commands the commands options for the compiler/executable
* @param failsOnErrorOutput if true, throws an <code>ExecutionException</code> if there the compiler or executable
* writes anything to the error output stream. By default, this value is true
* @throws ExecutionException if compiler or executable writes anything to the standard error stream (provided the
* failsOnErrorOutput is not false) or if the process returns a process result != 0.
*/
void executeCommand(String executable, List<String> commands, boolean failsOnErrorOutput)
throws ExecutionException;
/**
* Executes the command for the specified executable and list of command options. If the compiler or executable is
* not within the environmental path, you should use this method to specify the working directory. Always use this
* method for executables located within the local maven repository.
*
* @param executable the name of the executable (csc, xsd, etc).
* @param commands the command options for the compiler/executable
* @param workingDirectory the directory where the command will be executed
* @throws ExecutionException if compiler or executable writes anything to the standard error stream (provided the
* failsOnErrorOutput is not false) or if the process returns a process result != 0.
*/
void executeCommand(String executable, List<String> commands, File workingDirectory, boolean failsOnErrorOutput)
throws ExecutionException;
/**
* Returns the process result of executing the command. Typically a value of 0 means that the process executed
* successfully.
*
* @return the process result of executing the command
*/
int getResult();
/**
* Get the process id for the executed command.
*
* @return
*/
long getPid();
/**
* Returns the standard output from executing the command.
*
* @return the standard output from executing the command
*/
String getStandardOut();
/**
* Returns the standard error from executing the command.
*
* @return the standard error from executing the command
*/
String getStandardError();
/** Adds an environment variable with the specified name and value to the executor.
*
* @param name
* @param value
*/
void addEnvironment( String name, String value );
void setErrorListener(ErrorListener errorListener);
interface ErrorListener {
boolean isError(String error);
}
/**
* Provides factory services for creating a default instance of the command executor.
*/
class Factory {
/**
* Constructor
*/
private Factory() {
}
/**
* Returns a default instance of the command executor
*
* @return a default instance of the command executor
*/
public static CommandExecutor createDefaultCommmandExecutor() {
return new CommandExecutor() {
private Map<String, String> environment;
/**
* Instance of a plugin logger.
*/
private Log logger;
/**
* Standard Out
*/
private StreamConsumer stdOut;
/**
* Standard Error
*/
private ErrorStreamConsumer stdErr;
/**
* Process result
*/
private int result;
/*
*/
public ErrorListener errorListener;
public void setLogger(Log logger) {
this.logger = logger;
}
long pid;
private Commandline commandline;
public void executeCommand(String executable, List<String> commands)
throws ExecutionException {
executeCommand(executable, commands, null, true);
}
public void executeCommand(String executable, List<String> commands, boolean failsOnErrorOutput)
throws ExecutionException {
executeCommand(executable, commands, null, failsOnErrorOutput);
}
public void executeCommand(String executable, List<String> commands, File workingDirectory,
boolean failsOnErrorOutput)
throws ExecutionException {
if (commands == null) {
commands = new ArrayList<String>();
}
stdOut = new StreamConsumerImpl();
stdErr = new ErrorStreamConsumer();
commandline = new Commandline();
commandline.setExecutable(executable);
// Add the environment variables as needed
if (environment != null) {
for ( Map.Entry<String, String> entry : environment.entrySet() ) {
commandline.addEnvironment( entry.getKey(), entry.getValue() );
}
}
commandline.addArguments(commands.toArray(new String[commands.size()]));
if (workingDirectory != null && workingDirectory.exists()) {
commandline.setWorkingDirectory(workingDirectory.getAbsolutePath());
}
try {
result = CommandLineUtils.executeCommandLine(commandline, stdOut, stdErr);
if (logger != null) {
logger.debug("ANDROID-040-000: Executed command: Commandline = " + commandline +
", Result = " + result);
} else {
System.out.println("ANDROID-040-000: Executed command: Commandline = " + commandline +
", Result = " + result);
}
if ((failsOnErrorOutput && stdErr.hasError()) || result != 0) {
throw new ExecutionException("ANDROID-040-001: Could not execute: Command = " +
commandline.toString() + ", Result = " + result);
}
} catch (CommandLineException e) {
throw new ExecutionException(
"ANDROID-040-002: Could not execute: Command = " + commandline.toString() + ", Error message = " + e.getMessage());
}
setPid(commandline.getPid());
}
public int getResult() {
return result;
}
public String getStandardOut() {
return stdOut.toString();
}
public String getStandardError() {
return stdErr.toString();
}
@Override
public void addEnvironment( String name, String value ) {
if (environment == null) {
environment = new HashMap<String, String>( );
}
environment.put( name, value );
}
public void setErrorListener(ErrorListener errorListener) {
this.errorListener = errorListener;
}
public void setPid(long pid) {
this.pid = pid;
}
public long getPid() {
return pid;
}
/**
* Provides behavior for determining whether the command utility wrote anything to the Standard Error Stream.
* NOTE: I am using this to decide whether to fail the NMaven build. If the compiler implementation chooses
* to write warnings to the error stream, then the build will fail on warnings!!!
*/
class ErrorStreamConsumer
implements StreamConsumer {
/**
* Is true if there was anything consumed from the stream, otherwise false
*/
private boolean error;
/**
* Buffer to store the stream
*/
private StringBuffer sbe = new StringBuffer();
public ErrorStreamConsumer() {
if (logger == null) {
System.out.println("ANDROID-040-003: Error Log not set: Will not output error logs");
}
error = false;
}
public void consumeLine(String line) {
sbe.append(line);
if (logger != null) {
logger.info(line);
}
if (errorListener != null)
{
error = errorListener.isError(line);
}
else
{
error = true;
}
}
/**
* Returns false if the command utility wrote to the Standard Error Stream, otherwise returns true.
*
* @return false if the command utility wrote to the Standard Error Stream, otherwise returns true.
*/
public boolean hasError() {
return error;
}
/**
* Returns the error stream
*
* @return error stream
*/
public String toString() {
return sbe.toString();
}
}
/**
* StreamConsumer instance that buffers the entire output
*/
class StreamConsumerImpl
implements StreamConsumer {
private DefaultConsumer consumer;
private StringBuffer sb = new StringBuffer();
public StreamConsumerImpl() {
consumer = new DefaultConsumer();
}
public void consumeLine(String line) {
sb.append(line);
if (logger != null) {
consumer.consumeLine(line);
}
}
/**
* Returns the stream
*
* @return the stream
*/
public String toString() {
return sb.toString();
}
}
};
}
}
}