blob: 134c785f2a749e7575471872a2233cfa1b3b8c46 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.ant.svn;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Execute;
import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
import org.apache.tools.ant.taskdefs.LogOutputStream;
import org.apache.tools.ant.taskdefs.PumpStreamHandler;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.Environment;
import org.apache.tools.ant.util.StringUtils;
/**
* Just like Ant's built-in CVS task, the main loginc lives in this
* abstract task.
*/
public abstract class AbstractSvnTask extends Task {
private Commandline cmd = new Commandline();
/** list of Commandline children */
private Vector vecCommandlines = new Vector();
/**
* the URL the subcommand should apply to.
*/
private String svnURL;
/**
* the revision
*/
private String revision;
/**
* the default command.
*/
private static final String DEFAULT_SUBCOMMAND = "checkout";
/**
* the SVN command to execute.
*/
private String subCommand = null;
/**
* suppress information messages.
*/
private boolean quiet = false;
/**
* be verbose
*/
private boolean verbose = false;
/**
* report only, don't change any files.
*/
private boolean dryrun = false;
/**
* the directory where the checked out files should be placed.
*/
private File dest;
/** whether or not to append stdout/stderr to existing files */
private boolean append = false;
/**
* the file to direct standard output from the command.
*/
private File output;
/**
* the file to direct standard error from the command.
*/
private File error;
/**
* If true it will stop the build if svn exits with error.
* Default is false. (Iulian)
*/
private boolean failOnError = false;
/**
* Uses the contents of the file passed as an argument to this
* switch for the specified subcommand.
*/
private File file;
/**
* Forces a particular command or operation to run.
*/
private boolean force;
/**
* Makes a subcommand recurse into subdirectories.
*/
private Boolean recursive = null;
/**
* Tells Subversion to get the list of files that you wish to
* operate on from the filename you provide instead of listing all
* the files on the command line.
*/
private File targets;
/**
* Create accessors for the following, to allow different handling of
* the output.
*/
private ExecuteStreamHandler executeStreamHandler;
private OutputStream outputStream;
private OutputStream errorStream;
/** empty no-arg constructor*/
public AbstractSvnTask() {
super();
}
/**
* sets the handler
* @param handler a handler able of processing the output and error streams from the svn exe
*/
public void setExecuteStreamHandler(ExecuteStreamHandler handler) {
this.executeStreamHandler = handler;
}
/**
* find the handler and instantiate it if it does not exist yet
* @return handler for output and error streams
*/
protected ExecuteStreamHandler getExecuteStreamHandler() {
if (this.executeStreamHandler == null) {
setExecuteStreamHandler(new PumpStreamHandler(getOutputStream(),
getErrorStream()));
}
return this.executeStreamHandler;
}
/**
* sets a stream to which the output from the svn executable should be sent
* @param outputStream stream to which the stdout from svn should go
*/
protected void setOutputStream(OutputStream outputStream) {
this.outputStream = outputStream;
}
/**
* access the stream to which the stdout from svn should go
* if this stream has already been set, it will be returned
* if the stream has not yet been set, if the attribute output
* has been set, the output stream will go to the output file
* otherwise the output will go to ant's logging system
* @return output stream to which svn' stdout should go to
*/
protected OutputStream getOutputStream() {
if (this.outputStream == null) {
if (output != null) {
try {
setOutputStream(new PrintStream(
new BufferedOutputStream(
new FileOutputStream(output
.getPath(),
append))));
} catch (IOException e) {
throw new BuildException(e, getLocation());
}
} else {
setOutputStream(new LogOutputStream(this, Project.MSG_INFO));
}
}
return this.outputStream;
}
/**
* sets a stream to which the stderr from the svn exe should go
* @param errorStream an output stream willing to process stderr
*/
protected void setErrorStream(OutputStream errorStream) {
this.errorStream = errorStream;
}
/**
* access the stream to which the stderr from svn should go
* if this stream has already been set, it will be returned
* if the stream has not yet been set, if the attribute error
* has been set, the output stream will go to the file denoted by the error attribute
* otherwise the stderr output will go to ant's logging system
* @return output stream to which svn' stderr should go to
*/
protected OutputStream getErrorStream() {
if (this.errorStream == null) {
if (error != null) {
try {
setErrorStream(new PrintStream(
new BufferedOutputStream(
new FileOutputStream(error.getPath(),
append))));
} catch (IOException e) {
throw new BuildException(e, getLocation());
}
} else {
setErrorStream(new LogOutputStream(this, Project.MSG_WARN));
}
}
return this.errorStream;
}
/**
* Sets up the environment for toExecute and then runs it.
* @param toExecute the command line to execute
* @throws BuildException if failonError is set to true and the svn command fails
*/
protected void runCommand(Commandline toExecute) throws BuildException {
Environment env = new Environment();
//
// Just call the getExecuteStreamHandler() and let it handle
// the semantics of instantiation or retrieval.
//
Execute exe = new Execute(getExecuteStreamHandler(), null);
exe.setAntRun(getProject());
if (dest == null) {
dest = getProject().getBaseDir();
}
if (!dest.exists()) {
dest.mkdirs();
}
exe.setWorkingDirectory(dest);
exe.setCommandline(toExecute.getCommandline());
exe.setEnvironment(env.getVariables());
try {
String actualCommandLine = executeToString(exe);
log(actualCommandLine, Project.MSG_VERBOSE);
int retCode = exe.execute();
log("retCode=" + retCode, Project.MSG_DEBUG);
/*Throw an exception if svn exited with error. (Iulian)*/
if (failOnError && Execute.isFailure(retCode)) {
throw new BuildException("svn exited with error code "
+ retCode
+ StringUtils.LINE_SEP
+ "Command line was ["
+ actualCommandLine + "]", getLocation());
}
} catch (IOException e) {
if (failOnError) {
throw new BuildException(e, getLocation());
} else {
log("Caught exception: " + e.getMessage(), Project.MSG_WARN);
}
} catch (BuildException e) {
if (failOnError) {
throw(e);
} else {
Throwable t = e.getException();
if (t == null) {
t = e;
}
log("Caught exception: " + t.getMessage(), Project.MSG_WARN);
}
} catch (Exception e) {
if (failOnError) {
throw new BuildException(e, getLocation());
} else {
log("Caught exception: " + e.getMessage(), Project.MSG_WARN);
}
}
}
/**
* do the work
* @throws BuildException if failonerror is set to true and the svn command fails.
*/
public void execute() throws BuildException {
String savedCommand = getSubCommand();
if (this.getSubCommand() == null && vecCommandlines.size() == 0) {
// re-implement legacy behaviour:
this.setSubCommand(AbstractSvnTask.DEFAULT_SUBCOMMAND);
}
String c = this.getSubCommand();
Commandline cloned = null;
if (c != null) {
cloned = (Commandline) cmd.clone();
cloned.createArgument(true).setLine(c);
if (svnURL != null) {
cloned.createArgument().setValue(svnURL);
}
this.addConfiguredCommandline(cloned, true);
}
try {
for (int i = 0; i < vecCommandlines.size(); i++) {
this.runCommand((Commandline) vecCommandlines.elementAt(i));
}
} finally {
if (cloned != null) {
removeCommandline(cloned);
}
setSubCommand(savedCommand);
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
//ignore
}
}
if (errorStream != null) {
try {
errorStream.close();
} catch (IOException e) {
//ignore
}
}
}
}
private String executeToString(Execute execute) {
StringBuffer stringBuffer =
new StringBuffer(Commandline.describeCommand(execute
.getCommandline()));
String newLine = StringUtils.LINE_SEP;
String[] variableArray = execute.getEnvironment();
if (variableArray != null) {
stringBuffer.append(newLine);
stringBuffer.append(newLine);
stringBuffer.append("environment:");
stringBuffer.append(newLine);
for (int z = 0; z < variableArray.length; z++) {
stringBuffer.append(newLine);
stringBuffer.append("\t");
stringBuffer.append(variableArray[z]);
}
}
return stringBuffer.toString();
}
/**
* The URL the subcommand should apply to.
*
* @param url the URL the subcommand should apply to.
*/
public void setSvnURL(String url) {
// Check if not real svnroot => set it to null
if (url != null) {
if (url.trim().equals("")) {
url = null;
}
}
this.svnURL = url;
}
/**
* access the svnURL variable
* @return svnURL
*/
public String getSvnURL() {
return this.svnURL;
}
/**
* The directory where the checked out files should be placed.
*
* <p>Note that this is different from SVN's -d command line
* switch as Ant will never shorten pathnames to avoid empty
* directories.</p>
*
* @param dest directory where the checked out files should be placed
*/
public void setDest(File dest) {
this.dest = dest;
}
/**
* get the file where the checked out files should be placed
*
* @return directory where the checked out files should be placed
*/
public File getDest() {
return this.dest;
}
/**
* revision
* @return revision
*/
public String getRevision() {
return revision;
}
/**
* The revision to operate upon.
* @param p revision
*/
public void setRevision(String p) {
// Check if not real revision => set it to null
if (p != null && p.trim().length() > 0) {
revision = p;
addSubCommandArgument("--revision");
addSubCommandArgument(p);
}
}
/**
* Uses the contents of the file passed as an argument to this
* switch for the specified subcommand.
*/
public void setFile(File file) {
this.file = file;
}
/**
* Uses the contents of the file passed as an argument to this
* switch for the specified subcommand.
*/
public File getFile() {
return file;
}
/**
* Forces a particular command or operation to run.
*/
public void setForce(boolean force) {
this.force = force;
}
/**
* Forces a particular command or operation to run.
*/
public boolean getForce() {
return force;
}
/**
* Makes a subcommand recurse into subdirectories. Most
* subcommands recurse by default.
*/
public void setRecursive(Boolean recursive) {
this.recursive = recursive;
}
/**
* Makes a subcommand recurse into subdirectories. Most
* subcommands recurse by default.
*/
public Boolean getRecursive() {
return recursive;
}
/**
* Tells Subversion to get the list of files that you wish to
* operate on from the filename you provide instead of listing all
* the files on the command line.
*/
public void setTargets(File targets) {
this.targets = targets;
}
/**
* Tells Subversion to get the list of files that you wish to
* operate on from the filename you provide instead of listing all
* the files on the command line.
*/
public File getTargets() {
return targets;
}
/**
* This needs to be public to allow configuration
* of commands externally.
* @param arg command argument
*/
public void addSubCommandArgument(String arg) {
this.addSubCommandArgument(cmd, arg);
}
/**
* This method adds a command line argument to an external subcommand.
*
* @param c command line to which one argument should be added
* @param arg argument to add
*/
protected void addSubCommandArgument(Commandline c, String arg) {
c.createArgument().setValue(arg);
}
/**
* The SVN command to execute.
*
* @param c a command as string
*/
public void setSubCommand(String c) {
this.subCommand = c;
}
/**
* accessor to a command line as string
*
* @return command line as string
*/
public String getSubCommand() {
return this.subCommand;
}
/**
* If true, suppress informational messages.
* @param q if true, suppress informational messages
*/
public void setQuiet(boolean q) {
quiet = q;
}
/**
* If true, be verbose.
* @param v if true, be verbose.
*/
public void setVerbose(boolean v) {
verbose = v;
}
/**
* If true, report only and don't change any files.
*
* @param ne if true, report only and do not change any files.
*/
public void setDryrun(boolean ne) {
dryrun = ne;
}
/**
* The file to direct standard output from the command.
* @param output a file to which stdout should go
*/
public void setOutput(File output) {
this.output = output;
}
/**
* The file to direct standard error from the command.
*
* @param error a file to which stderr should go
*/
public void setError(File error) {
this.error = error;
}
/**
* Whether to append output/error when redirecting to a file.
* @param value true indicated you want to append
*/
public void setAppend(boolean value) {
this.append = value;
}
/**
* Stop the build process if the command exits with
* a return code other than 0.
* Defaults to false.
* @param failOnError stop the build process if the command exits with
* a return code other than 0
*/
public void setFailOnError(boolean failOnError) {
this.failOnError = failOnError;
}
/**
* Configure a commandline element for things like quiet, etc.
* @param c the command line which will be configured
* if the commandline is initially null, the function is a noop
* otherwise the function append to the commandline arguments concerning
* <ul>
* <li>
* svn
* </li>
* <li>
* quiet
* </li>
* <li>verbose</li>
* <li>dryrun</li>
* </ul>
*/
protected void configureCommandline(Commandline c) {
if (c == null) {
return;
}
c.setExecutable("svn");
if (quiet) {
c.createArgument(true).setValue("--quiet");
}
if (verbose) {
c.createArgument(true).setValue("--verbose");
}
if (dryrun) {
c.createArgument(true).setValue("--dry-run");
}
if (file != null) {
c.createArgument(true).setValue("--file");
c.createArgument(true).setFile(file);
}
if (force) {
c.createArgument(true).setValue("--force");
}
if (recursive != null) {
if (recursive.booleanValue()) {
c.createArgument(true).setValue("--recursive");
} else {
c.createArgument(true).setValue("--non-recursive");
}
}
if (targets != null) {
c.createArgument(true).setValue("--targets");
c.createArgument(true).setFile(targets);
}
}
/**
* remove a particular command from a vector of command lines
* @param c command line which should be removed
*/
protected void removeCommandline(Commandline c) {
vecCommandlines.removeElement(c);
}
/**
* Adds direct command-line to execute.
* @param c command line to execute
*/
public void addConfiguredCommandline(Commandline c) {
this.addConfiguredCommandline(c, false);
}
/**
* Configures and adds the given Commandline.
* @param c commandline to insert
* @param insertAtStart If true, c is
* inserted at the beginning of the vector of command lines
*/
public void addConfiguredCommandline(Commandline c,
boolean insertAtStart) {
if (c == null) {
return;
}
this.configureCommandline(c);
if (insertAtStart) {
vecCommandlines.insertElementAt(c, 0);
} else {
vecCommandlines.addElement(c);
}
}
}