diff options
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional')
213 files changed, 62092 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ANTLR.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ANTLR.java new file mode 100644 index 00000000..605b3368 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ANTLR.java @@ -0,0 +1,438 @@ +/* + * 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.tools.ant.taskdefs.optional; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +import org.apache.tools.ant.AntClassLoader; +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.LogOutputStream; +import org.apache.tools.ant.taskdefs.PumpStreamHandler; +import org.apache.tools.ant.taskdefs.condition.Os; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.CommandlineJava; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.JavaEnvUtils; +import org.apache.tools.ant.util.LoaderUtils; +import org.apache.tools.ant.util.TeeOutputStream; + +/** + * Invokes the ANTLR Translator generator on a grammar file. + * + */ +public class ANTLR extends Task { + + private CommandlineJava commandline = new CommandlineJava(); + + /** the file to process */ + private File targetFile; + + /** where to output the result */ + private File outputDirectory; + + /** an optional super grammar file */ + private File superGrammar; + + /** optional flag to enable html output */ + private boolean html; + + /** optional flag to print out a diagnostic file */ + private boolean diagnostic; + + /** optional flag to add trace methods */ + private boolean trace; + + /** optional flag to add trace methods to the parser only */ + private boolean traceParser; + + /** optional flag to add trace methods to the lexer only */ + private boolean traceLexer; + + /** optional flag to add trace methods to the tree walker only */ + private boolean traceTreeWalker; + + /** working directory */ + private File workingdir = null; + + /** captures ANTLR's output */ + private ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + /** The debug attribute */ + private boolean debug; + + + /** Instance of a utility class to use for file operations. */ + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** Constructor for ANTLR task. */ + public ANTLR() { + commandline.setVm(JavaEnvUtils.getJreExecutable("java")); + commandline.setClassname("antlr.Tool"); + } + + /** + * The grammar file to process. + * @param target the gramer file + */ + public void setTarget(File target) { + log("Setting target to: " + target.toString(), Project.MSG_VERBOSE); + this.targetFile = target; + } + + /** + * The directory to write the generated files to. + * @param outputDirectory the output directory + */ + public void setOutputdirectory(File outputDirectory) { + log("Setting output directory to: " + outputDirectory.toString(), Project.MSG_VERBOSE); + this.outputDirectory = outputDirectory; + } + + /** + * Sets an optional super grammar file. + * Use setGlib(File superGrammar) instead. + * @param superGrammar the super grammar filename + * @deprecated since ant 1.6 + */ + public void setGlib(String superGrammar) { + String sg = null; + if (Os.isFamily("dos")) { + sg = superGrammar.replace('\\', '/'); + } else { + sg = superGrammar; + } + setGlib(FILE_UTILS.resolveFile(getProject().getBaseDir(), sg)); + } + /** + * Sets an optional super grammar file + * @param superGrammar the super grammar file + * @since ant 1.6 + */ + public void setGlib(File superGrammar) { + this.superGrammar = superGrammar; + } + /** + * Sets a flag to enable ParseView debugging + * @param enable a <code>boolean</code> value + */ + public void setDebug(boolean enable) { + this.debug = enable; + } + + /** + * If true, emit html + * @param enable a <code>boolean</code> value + */ + public void setHtml(boolean enable) { + html = enable; + } + + /** + * Sets a flag to emit diagnostic text + * @param enable a <code>boolean</code> value + */ + public void setDiagnostic(boolean enable) { + diagnostic = enable; + } + + /** + * If true, enables all tracing. + * @param enable a <code>boolean</code> value + */ + public void setTrace(boolean enable) { + trace = enable; + } + + /** + * If true, enables parser tracing. + * @param enable a <code>boolean</code> value + */ + public void setTraceParser(boolean enable) { + traceParser = enable; + } + + /** + * If true, enables lexer tracing. + * @param enable a <code>boolean</code> value + */ + public void setTraceLexer(boolean enable) { + traceLexer = enable; + } + + /** + * Sets a flag to allow the user to enable tree walker tracing + * @param enable a <code>boolean</code> value + */ + public void setTraceTreeWalker(boolean enable) { + traceTreeWalker = enable; + } + + // we are forced to fork ANTLR since there is a call + // to System.exit() and there is nothing we can do + // right now to avoid this. :-( (SBa) + // I'm not removing this method to keep backward compatibility + /** + * @ant.attribute ignore="true" + * @param s a <code>boolean</code> value + */ + public void setFork(boolean s) { + //this.fork = s; + } + + /** + * The working directory of the process + * @param d the working directory + */ + public void setDir(File d) { + this.workingdir = d; + } + + /** + * Adds a classpath to be set + * because a directory might be given for Antlr debug. + * @return a path to be configured + */ + public Path createClasspath() { + return commandline.createClasspath(getProject()).createPath(); + } + + /** + * Adds a new JVM argument. + * @return create a new JVM argument so that any argument can be passed to the JVM. + * @see #setFork(boolean) + */ + public Commandline.Argument createJvmarg() { + return commandline.createVmArgument(); + } + + /** + * Adds the jars or directories containing Antlr + * this should make the forked JVM work without having to + * specify it directly. + * @throws BuildException on error + */ + public void init() throws BuildException { + addClasspathEntry("/antlr/ANTLRGrammarParseBehavior.class"); + } + + /** + * Search for the given resource and add the directory or archive + * that contains it to the classpath. + * + * <p>Doesn't work for archives in JDK 1.1 as the URL returned by + * getResource doesn't contain the name of the archive.</p> + * @param resource the resource name to search for + */ + protected void addClasspathEntry(String resource) { + /* + * pre Ant 1.6 this method used to call getClass().getResource + * while Ant 1.6 will call ClassLoader.getResource(). + * + * The difference is that Class.getResource expects a leading + * slash for "absolute" resources and will strip it before + * delegating to ClassLoader.getResource - so we now have to + * emulate Class's behavior. + */ + if (resource.startsWith("/")) { + resource = resource.substring(1); + } else { + resource = "org/apache/tools/ant/taskdefs/optional/" + + resource; + } + + File f = LoaderUtils.getResourceSource(getClass().getClassLoader(), + resource); + if (f != null) { + log("Found " + f.getAbsolutePath(), Project.MSG_DEBUG); + createClasspath().setLocation(f); + } else { + log("Couldn\'t find " + resource, Project.MSG_VERBOSE); + } + } + + /** + * Execute the task. + * @throws BuildException on error + */ + public void execute() throws BuildException { + validateAttributes(); + + //TODO: use ANTLR to parse the grammar file to do this. + File generatedFile = getGeneratedFile(); + boolean targetIsOutOfDate = + targetFile.lastModified() > generatedFile.lastModified(); + boolean superGrammarIsOutOfDate = superGrammar != null + && (superGrammar.lastModified() > generatedFile.lastModified()); + if (targetIsOutOfDate || superGrammarIsOutOfDate) { + if (targetIsOutOfDate) { + log("Compiling " + targetFile + " as it is newer than " + + generatedFile, Project.MSG_VERBOSE); + } else { + log("Compiling " + targetFile + " as " + superGrammar + + " is newer than " + generatedFile, Project.MSG_VERBOSE); + } + populateAttributes(); + commandline.createArgument().setValue(targetFile.toString()); + + log(commandline.describeCommand(), Project.MSG_VERBOSE); + int err = run(commandline.getCommandline()); + if (err != 0) { + throw new BuildException("ANTLR returned: " + err, getLocation()); + } else { + String output = bos.toString(); + if (output.indexOf("error:") > -1) { + throw new BuildException("ANTLR signaled an error: " + + output, getLocation()); + } + } + } else { + log("Skipped grammar file. Generated file " + generatedFile + + " is newer.", Project.MSG_VERBOSE); + } + } + + /** + * A refactored method for populating all the command line arguments based + * on the user-specified attributes. + */ + private void populateAttributes() { + commandline.createArgument().setValue("-o"); + commandline.createArgument().setValue(outputDirectory.toString()); + if (superGrammar != null) { + commandline.createArgument().setValue("-glib"); + commandline.createArgument().setValue(superGrammar.toString()); + } + if (html) { + commandline.createArgument().setValue("-html"); + } + if (diagnostic) { + commandline.createArgument().setValue("-diagnostic"); + } + if (trace) { + commandline.createArgument().setValue("-trace"); + } + if (traceParser) { + commandline.createArgument().setValue("-traceParser"); + } + if (traceLexer) { + commandline.createArgument().setValue("-traceLexer"); + } + if (traceTreeWalker) { + if (is272()) { + commandline.createArgument().setValue("-traceTreeParser"); + } else { + commandline.createArgument().setValue("-traceTreeWalker"); + } + } + if (debug) { + commandline.createArgument().setValue("-debug"); + } + } + + private void validateAttributes() throws BuildException { + if (targetFile == null || !targetFile.isFile()) { + throw new BuildException("Invalid target: " + targetFile); + } + + // if no output directory is specified, used the target's directory + if (outputDirectory == null) { + setOutputdirectory(new File(targetFile.getParent())); + } + if (!outputDirectory.isDirectory()) { + throw new BuildException("Invalid output directory: " + outputDirectory); + } + } + + private File getGeneratedFile() throws BuildException { + String generatedFileName = null; + try { + BufferedReader in = new BufferedReader(new FileReader(targetFile)); + String line; + while ((line = in.readLine()) != null) { + int extendsIndex = line.indexOf(" extends "); + if (line.startsWith("class ") && extendsIndex > -1) { + generatedFileName = line.substring( + "class ".length(), extendsIndex).trim(); + break; + } + } + in.close(); + } catch (Exception e) { + throw new BuildException("Unable to determine generated class", e); + } + if (generatedFileName == null) { + throw new BuildException("Unable to determine generated class"); + } + return new File(outputDirectory, generatedFileName + + (html ? ".html" : ".java")); + } + + /** execute in a forked VM */ + private int run(String[] command) throws BuildException { + PumpStreamHandler psh = + new PumpStreamHandler(new LogOutputStream(this, Project.MSG_INFO), + new TeeOutputStream( + new LogOutputStream(this, + Project.MSG_WARN), + bos) + ); + Execute exe = new Execute(psh, null); + exe.setAntRun(getProject()); + if (workingdir != null) { + exe.setWorkingDirectory(workingdir); + } + exe.setCommandline(command); + try { + return exe.execute(); + } catch (IOException e) { + throw new BuildException(e, getLocation()); + } finally { + FileUtils.close(bos); + } + } + + /** + * Whether the antlr version is 2.7.2 (or higher). + * + * @return true if the version of Antlr present is 2.7.2 or later. + * @since Ant 1.6 + */ + protected boolean is272() { + AntClassLoader l = null; + try { + l = getProject().createClassLoader(commandline.getClasspath()); + l.loadClass("antlr.Version"); + return true; + } catch (ClassNotFoundException e) { + return false; + } finally { + if (l != null) { + l.cleanup(); + } + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Cab.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Cab.java new file mode 100644 index 00000000..11c091a8 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Cab.java @@ -0,0 +1,357 @@ +/* + * 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.tools.ant.taskdefs.optional; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.Vector; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.ExecTask; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.taskdefs.LogOutputStream; +import org.apache.tools.ant.taskdefs.MatchingTask; +import org.apache.tools.ant.taskdefs.StreamPumper; +import org.apache.tools.ant.taskdefs.condition.Os; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.util.FileUtils; + + +/** + * Create a CAB archive. + * + */ + +public class Cab extends MatchingTask { + private static final int DEFAULT_RESULT = -99; + private File cabFile; + private File baseDir; + private Vector filesets = new Vector(); + private boolean doCompress = true; + private boolean doVerbose = false; + private String cmdOptions; + + // CheckStyle:VisibilityModifier OFF - bc + protected String archiveType = "cab"; + // CheckStyle:VisibilityModifier ON + + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** + * The name/location of where to create the .cab file. + * @param cabFile the location of the cab file. + */ + public void setCabfile(File cabFile) { + this.cabFile = cabFile; + } + + /** + * Base directory to look in for files to CAB. + * @param baseDir base directory for files to cab. + */ + public void setBasedir(File baseDir) { + this.baseDir = baseDir; + } + + /** + * If true, compress the files otherwise only store them. + * @param compress a <code>boolean</code> value. + */ + public void setCompress(boolean compress) { + doCompress = compress; + } + + /** + * If true, display cabarc output. + * @param verbose a <code>boolean</code> value. + */ + public void setVerbose(boolean verbose) { + doVerbose = verbose; + } + + /** + * Sets additional cabarc options that are not supported directly. + * @param options cabarc command line options. + */ + public void setOptions(String options) { + cmdOptions = options; + } + + /** + * Adds a set of files to archive. + * @param set a set of files to archive. + */ + public void addFileset(FileSet set) { + if (filesets.size() > 0) { + throw new BuildException("Only one nested fileset allowed"); + } + filesets.addElement(set); + } + + /* + * I'm not fond of this pattern: "sub-method expected to throw + * task-cancelling exceptions". It feels too much like programming + * for side-effects to me... + */ + /** + * Check if the attributes and nested elements are correct. + * @throws BuildException on error. + */ + protected void checkConfiguration() throws BuildException { + if (baseDir == null && filesets.size() == 0) { + throw new BuildException("basedir attribute or one " + + "nested fileset is required!", + getLocation()); + } + if (baseDir != null && !baseDir.exists()) { + throw new BuildException("basedir does not exist!", getLocation()); + } + if (baseDir != null && filesets.size() > 0) { + throw new BuildException( + "Both basedir attribute and a nested fileset is not allowed"); + } + if (cabFile == null) { + throw new BuildException("cabfile attribute must be set!", + getLocation()); + } + } + + /** + * Create a new exec delegate. The delegate task is populated so that + * it appears in the logs to be the same task as this one. + * @return the delegate. + * @throws BuildException on error. + */ + protected ExecTask createExec() throws BuildException { + ExecTask exec = new ExecTask(this); + return exec; + } + + /** + * Check to see if the target is up to date with respect to input files. + * @param files the list of files to check. + * @return true if the cab file is newer than its dependents. + */ + protected boolean isUpToDate(Vector files) { + boolean upToDate = true; + final int size = files.size(); + for (int i = 0; i < size && upToDate; i++) { + String file = files.elementAt(i).toString(); + if (FILE_UTILS.resolveFile(baseDir, file).lastModified() + > cabFile.lastModified()) { + upToDate = false; + } + } + return upToDate; + } + + /** + * Creates a list file. This temporary file contains a list of all files + * to be included in the cab, one file per line. + * + * <p>This method expects to only be called on Windows and thus + * quotes the file names.</p> + * @param files the list of files to use. + * @return the list file created. + * @throws IOException if there is an error. + */ + protected File createListFile(Vector files) + throws IOException { + File listFile = FILE_UTILS.createTempFile("ant", "", null, true, true); + + BufferedWriter writer = null; + try { + writer = new BufferedWriter(new FileWriter(listFile)); + + final int size = files.size(); + for (int i = 0; i < size; i++) { + writer.write('\"' + files.elementAt(i).toString() + '\"'); + writer.newLine(); + } + } finally { + FileUtils.close(writer); + } + + return listFile; + } + + /** + * Append all files found by a directory scanner to a vector. + * @param files the vector to append the files to. + * @param ds the scanner to get the files from. + */ + protected void appendFiles(Vector files, DirectoryScanner ds) { + String[] dsfiles = ds.getIncludedFiles(); + + for (int i = 0; i < dsfiles.length; i++) { + files.addElement(dsfiles[i]); + } + } + + /** + * Get the complete list of files to be included in the cab. Filenames + * are gathered from the fileset if it has been added, otherwise from the + * traditional include parameters. + * @return the list of files. + * @throws BuildException if there is an error. + */ + protected Vector getFileList() throws BuildException { + Vector files = new Vector(); + + if (baseDir != null) { + // get files from old methods - includes and nested include + appendFiles(files, super.getDirectoryScanner(baseDir)); + } else { + FileSet fs = (FileSet) filesets.elementAt(0); + baseDir = fs.getDir(); + appendFiles(files, fs.getDirectoryScanner(getProject())); + } + + return files; + } + + /** + * execute this task. + * @throws BuildException on error. + */ + public void execute() throws BuildException { + + checkConfiguration(); + + Vector files = getFileList(); + + // quick exit if the target is up to date + if (isUpToDate(files)) { + return; + } + + log("Building " + archiveType + ": " + cabFile.getAbsolutePath()); + + if (!Os.isFamily("windows")) { + log("Using listcab/libcabinet", Project.MSG_VERBOSE); + + StringBuffer sb = new StringBuffer(); + + Enumeration fileEnum = files.elements(); + + while (fileEnum.hasMoreElements()) { + sb.append(fileEnum.nextElement()).append("\n"); + } + sb.append("\n").append(cabFile.getAbsolutePath()).append("\n"); + + try { + Process p = Execute.launch(getProject(), + new String[] {"listcab"}, null, + baseDir != null ? baseDir + : getProject().getBaseDir(), + true); + OutputStream out = p.getOutputStream(); + + // Create the stream pumpers to forward listcab's stdout and stderr to the log + // note: listcab is an interactive program, and issues prompts for every new line. + // Therefore, make it show only with verbose logging turned on. + LogOutputStream outLog = new LogOutputStream(this, Project.MSG_VERBOSE); + LogOutputStream errLog = new LogOutputStream(this, Project.MSG_ERR); + StreamPumper outPump = new StreamPumper(p.getInputStream(), outLog); + StreamPumper errPump = new StreamPumper(p.getErrorStream(), errLog); + + // Pump streams asynchronously + (new Thread(outPump)).start(); + (new Thread(errPump)).start(); + + out.write(sb.toString().getBytes()); + out.flush(); + out.close(); + + // A wild default for when the thread is interrupted + int result = DEFAULT_RESULT; + + try { + // Wait for the process to finish + result = p.waitFor(); + + // Wait for the end of output and error streams + outPump.waitFor(); + outLog.close(); + errPump.waitFor(); + errLog.close(); + } catch (InterruptedException ie) { + log("Thread interrupted: " + ie); + } + + // Informative summary message in case of errors + if (Execute.isFailure(result)) { + log("Error executing listcab; error code: " + result); + } + } catch (IOException ex) { + String msg = "Problem creating " + cabFile + " " + ex.getMessage(); + throw new BuildException(msg, getLocation()); + } + } else { + try { + File listFile = createListFile(files); + ExecTask exec = createExec(); + File outFile = null; + + // die if cabarc fails + exec.setFailonerror(true); + exec.setDir(baseDir); + + if (!doVerbose) { + outFile = FILE_UTILS.createTempFile("ant", "", null, true, true); + exec.setOutput(outFile); + } + + exec.setExecutable("cabarc"); + exec.createArg().setValue("-r"); + exec.createArg().setValue("-p"); + + if (!doCompress) { + exec.createArg().setValue("-m"); + exec.createArg().setValue("none"); + } + + if (cmdOptions != null) { + exec.createArg().setLine(cmdOptions); + } + + exec.createArg().setValue("n"); + exec.createArg().setFile(cabFile); + exec.createArg().setValue("@" + listFile.getAbsolutePath()); + + exec.execute(); + + if (outFile != null) { + outFile.delete(); + } + + listFile.delete(); + } catch (IOException ioe) { + String msg = "Problem creating " + cabFile + " " + ioe.getMessage(); + throw new BuildException(msg, getLocation()); + } + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/EchoProperties.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/EchoProperties.java new file mode 100644 index 00000000..b63ef933 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/EchoProperties.java @@ -0,0 +1,543 @@ +/* + * 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.tools.ant.taskdefs.optional; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TreeSet; +import java.util.Vector; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.EnumeratedAttribute; +import org.apache.tools.ant.types.PropertySet; +import org.apache.tools.ant.util.CollectionUtils; +import org.apache.tools.ant.util.DOMElementWriter; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.JavaEnvUtils; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Displays all the current properties in the build. The output can be sent to + * a file if desired. <P> + * + * Attribute "destfile" defines a file to send the properties to. This can be + * processed as a standard property file later. <P> + * + * Attribute "prefix" defines a prefix which is used to filter the properties + * only those properties starting with this prefix will be echoed. <P> + * + * By default, the "failonerror" attribute is enabled. If an error occurs while + * writing the properties to a file, and this attribute is enabled, then a + * BuildException will be thrown. If disabled, then IO errors will be reported + * as a log statement, but no error will be thrown. <P> + * + * Examples: <pre> + * <echoproperties /> + * </pre> Report the current properties to the log. <P> + * + * <pre> + * <echoproperties destfile="my.properties" /> + * </pre> Report the current properties to the file "my.properties", and will + * fail the build if the file could not be created or written to. <P> + * + * <pre> + * <echoproperties destfile="my.properties" failonerror="false" + * prefix="ant" /> + * </pre> Report all properties beginning with 'ant' to the file + * "my.properties", and will log a message if the file could not be created or + * written to, but will still allow the build to continue. + * + *@since Ant 1.5 + */ +public class EchoProperties extends Task { + + /** + * the properties element. + */ + private static final String PROPERTIES = "properties"; + + /** + * the property element. + */ + private static final String PROPERTY = "property"; + + /** + * name attribute for property, testcase and testsuite elements. + */ + private static final String ATTR_NAME = "name"; + + /** + * value attribute for property elements. + */ + private static final String ATTR_VALUE = "value"; + + /** + * the input file. + */ + private File inFile = null; + + /** + * File object pointing to the output file. If this is null, then + * we output to the project log, not to a file. + */ + private File destfile = null; + + /** + * If this is true, then errors generated during file output will become + * build errors, and if false, then such errors will be logged, but not + * thrown. + */ + private boolean failonerror = true; + + private Vector propertySets = new Vector(); + + private String format = "text"; + + private String prefix; + + /** + * @since Ant 1.7 + */ + private String regex; + + /** + * Sets the input file. + * + * @param file the input file + */ + public void setSrcfile(File file) { + inFile = file; + } + + /** + * Set a file to store the property output. If this is never specified, + * then the output will be sent to the Ant log. + * + *@param destfile file to store the property output + */ + public void setDestfile(File destfile) { + this.destfile = destfile; + } + + + /** + * If true, the task will fail if an error occurs writing the properties + * file, otherwise errors are just logged. + * + *@param failonerror <tt>true</tt> if IO exceptions are reported as build + * exceptions, or <tt>false</tt> if IO exceptions are ignored. + */ + public void setFailOnError(boolean failonerror) { + this.failonerror = failonerror; + } + + + /** + * If the prefix is set, then only properties which start with this + * prefix string will be recorded. If regex is not set and if this + * is never set, or it is set to an empty string or <tt>null</tt>, + * then all properties will be recorded. <P> + * + * For example, if the attribute is set as: + * <PRE><echoproperties prefix="ant." /></PRE> + * then the property "ant.home" will be recorded, but "ant-example" + * will not. + * + * @param prefix The new prefix value + */ + public void setPrefix(String prefix) { + if (prefix != null && prefix.length() != 0) { + this.prefix = prefix; + PropertySet ps = new PropertySet(); + ps.setProject(getProject()); + ps.appendPrefix(prefix); + addPropertyset(ps); + } + } + + /** + * If the regex is set, then only properties whose names match it + * will be recorded. If prefix is not set and if this is never set, + * or it is set to an empty string or <tt>null</tt>, then all + * properties will be recorded.<P> + * + * For example, if the attribute is set as: + * <PRE><echoproperties prefix=".*ant.*" /></PRE> + * then the properties "ant.home" and "user.variant" will be recorded, + * but "ant-example" will not. + * + * @param regex The new regex value + * + * @since Ant 1.7 + */ + public void setRegex(String regex) { + if (regex != null && regex.length() != 0) { + this.regex = regex; + PropertySet ps = new PropertySet(); + ps.setProject(getProject()); + ps.appendRegex(regex); + addPropertyset(ps); + } + } + + /** + * A set of properties to write. + * @param ps the property set to write + * @since Ant 1.6 + */ + public void addPropertyset(PropertySet ps) { + propertySets.addElement(ps); + } + + /** + * Set the output format - xml or text. + * @param ea an enumerated <code>FormatAttribute</code> value + */ + public void setFormat(FormatAttribute ea) { + format = ea.getValue(); + } + + /** + * A enumerated type for the format attribute. + * The values are "xml" and "text". + */ + public static class FormatAttribute extends EnumeratedAttribute { + private String [] formats = new String[]{"xml", "text"}; + + /** + * @see EnumeratedAttribute#getValues() + * @return accepted values + */ + public String[] getValues() { + return formats; + } + } + + /** + * Run the task. + * + *@exception BuildException trouble, probably file IO + */ + public void execute() throws BuildException { + if (prefix != null && regex != null) { + throw new BuildException("Please specify either prefix" + + " or regex, but not both", getLocation()); + } + //copy the properties file + Hashtable allProps = new Hashtable(); + + /* load properties from file if specified, otherwise + use Ant's properties */ + if (inFile == null && propertySets.size() == 0) { + // add ant properties + allProps.putAll(getProject().getProperties()); + } else if (inFile != null) { + if (inFile.exists() && inFile.isDirectory()) { + String message = "srcfile is a directory!"; + if (failonerror) { + throw new BuildException(message, getLocation()); + } else { + log(message, Project.MSG_ERR); + } + return; + } + + if (inFile.exists() && !inFile.canRead()) { + String message = "Can not read from the specified srcfile!"; + if (failonerror) { + throw new BuildException(message, getLocation()); + } else { + log(message, Project.MSG_ERR); + } + return; + } + + FileInputStream in = null; + try { + in = new FileInputStream(inFile); + Properties props = new Properties(); + props.load(in); + allProps.putAll(props); + } catch (FileNotFoundException fnfe) { + String message = + "Could not find file " + inFile.getAbsolutePath(); + if (failonerror) { + throw new BuildException(message, fnfe, getLocation()); + } else { + log(message, Project.MSG_WARN); + } + return; + } catch (IOException ioe) { + String message = + "Could not read file " + inFile.getAbsolutePath(); + if (failonerror) { + throw new BuildException(message, ioe, getLocation()); + } else { + log(message, Project.MSG_WARN); + } + return; + } finally { + FileUtils.close(in); + } + } + + Enumeration e = propertySets.elements(); + while (e.hasMoreElements()) { + PropertySet ps = (PropertySet) e.nextElement(); + allProps.putAll(ps.getProperties()); + } + + OutputStream os = null; + try { + if (destfile == null) { + os = new ByteArrayOutputStream(); + saveProperties(allProps, os); + log(os.toString(), Project.MSG_INFO); + } else { + if (destfile.exists() && destfile.isDirectory()) { + String message = "destfile is a directory!"; + if (failonerror) { + throw new BuildException(message, getLocation()); + } else { + log(message, Project.MSG_ERR); + } + return; + } + + if (destfile.exists() && !destfile.canWrite()) { + String message = + "Can not write to the specified destfile!"; + if (failonerror) { + throw new BuildException(message, getLocation()); + } else { + log(message, Project.MSG_ERR); + } + return; + } + os = new FileOutputStream(this.destfile); + saveProperties(allProps, os); + } + } catch (IOException ioe) { + if (failonerror) { + throw new BuildException(ioe, getLocation()); + } else { + log(ioe.getMessage(), Project.MSG_INFO); + } + } finally { + if (os != null) { + try { + os.close(); + } catch (IOException ex) { + //ignore + } + } + } + } + + + /** + * Send the key/value pairs in the hashtable to the given output stream. + * Only those properties matching the <tt>prefix</tt> constraint will be + * sent to the output stream. + * The output stream will be closed when this method returns. + * + * @param allProps propfile to save + * @param os output stream + * @throws IOException on output errors + * @throws BuildException on other errors + */ + protected void saveProperties(Hashtable allProps, OutputStream os) + throws IOException, BuildException { + final List keyList = new ArrayList(allProps.keySet()); + Collections.sort(keyList); + Properties props = new Properties() { + private static final long serialVersionUID = 5090936442309201654L; + public Enumeration keys() { + return CollectionUtils.asEnumeration(keyList.iterator()); + } + public Set entrySet() { + Set result = super.entrySet(); + if (JavaEnvUtils.isKaffe()) { + TreeSet t = new TreeSet(new Comparator() { + public int compare(Object o1, Object o2) { + String key1 = (String) ((Map.Entry) o1).getKey(); + String key2 = (String) ((Map.Entry) o2).getKey(); + return key1.compareTo(key2); + } + }); + t.addAll(result); + result = t; + } + return result; + } + }; + final int size = keyList.size(); + for (int i = 0; i < size; i++) { + String name = keyList.get(i).toString(); + String value = allProps.get(name).toString(); + props.setProperty(name, value); + } + if ("text".equals(format)) { + jdkSaveProperties(props, os, "Ant properties"); + } else if ("xml".equals(format)) { + xmlSaveProperties(props, os); + } + } + + /** + * a tuple for the sort list. + */ + private static final class Tuple implements Comparable { + private String key; + private String value; + + private Tuple(String key, String value) { + this.key = key; + this.value = value; + } + + /** + * Compares this object with the specified object for order. + * @param o the Object to be compared. + * @return a negative integer, zero, or a positive integer as this object is + * less than, equal to, or greater than the specified object. + * @throws ClassCastException if the specified object's type prevents it + * from being compared to this Object. + */ + public int compareTo(Object o) { + Tuple that = (Tuple) o; + return key.compareTo(that.key); + } + } + + private List sortProperties(Properties props) { + //sort the list. Makes SCM and manual diffs easier. + List sorted = new ArrayList(props.size()); + Enumeration e = props.propertyNames(); + while (e.hasMoreElements()) { + String name = (String) e.nextElement(); + sorted.add(new Tuple(name, props.getProperty(name))); + } + Collections.sort(sorted); + return sorted; + } + + /** + * Output the properties as xml output. + * @param props the properties to save + * @param os the output stream to write to (Note this gets closed) + * @throws IOException on error in writing to the stream + */ + protected void xmlSaveProperties(Properties props, + OutputStream os) throws IOException { + // create XML document + Document doc = getDocumentBuilder().newDocument(); + Element rootElement = doc.createElement(PROPERTIES); + + List sorted = sortProperties(props); + + + // output properties + Iterator iten = sorted.iterator(); + while (iten.hasNext()) { + Tuple tuple = (Tuple) iten.next(); + Element propElement = doc.createElement(PROPERTY); + propElement.setAttribute(ATTR_NAME, tuple.key); + propElement.setAttribute(ATTR_VALUE, tuple.value); + rootElement.appendChild(propElement); + } + + Writer wri = null; + try { + wri = new OutputStreamWriter(os, "UTF8"); + wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + (new DOMElementWriter()).write(rootElement, wri, 0, "\t"); + wri.flush(); + } catch (IOException ioe) { + throw new BuildException("Unable to write XML file", ioe); + } finally { + FileUtils.close(wri); + } + } + + /** + * JDK 1.2 allows for the safer method + * <tt>Properties.store(OutputStream, String)</tt>, which throws an + * <tt>IOException</tt> on an output error. + * + *@param props the properties to record + *@param os record the properties to this output stream + *@param header prepend this header to the property output + *@exception IOException on an I/O error during a write. + */ + protected void jdkSaveProperties(Properties props, OutputStream os, + String header) throws IOException { + try { + props.store(os, header); + + } catch (IOException ioe) { + throw new BuildException(ioe, getLocation()); + } finally { + if (os != null) { + try { + os.close(); + } catch (IOException ioex) { + log("Failed to close output stream"); + } + } + } + } + + + /** + * Uses the DocumentBuilderFactory to get a DocumentBuilder instance. + * + * @return The DocumentBuilder instance + */ + private static DocumentBuilder getDocumentBuilder() { + try { + return DocumentBuilderFactory.newInstance().newDocumentBuilder(); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Javah.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Javah.java new file mode 100644 index 00000000..aaed7832 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Javah.java @@ -0,0 +1,513 @@ +/* + * 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.tools.ant.taskdefs.optional; + +import java.io.File; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.StringTokenizer; +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.optional.javah.JavahAdapter; +import org.apache.tools.ant.taskdefs.optional.javah.JavahAdapterFactory; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.Reference; +import org.apache.tools.ant.util.StringUtils; +import org.apache.tools.ant.util.facade.FacadeTaskHelper; +import org.apache.tools.ant.util.facade.ImplementationSpecificArgument; + +/** + * Generates JNI header files using javah. + * + * This task can take the following arguments: + * <ul> + * <li>classname - the fully-qualified name of a class</li> + * <li>outputFile - Concatenates the resulting header or source files for all + * the classes listed into this file</li> + * <li>destdir - Sets the directory where javah saves the header files or the + * stub files</li> + * <li>classpath</li> + * <li>bootclasspath</li> + * <li>force - Specifies that output files should always be written + (JDK1.2 only)</li> + * <li>old - Specifies that old JDK1.0-style header files should be generated + * (otherwise output file contain JNI-style native method + * function prototypes) (JDK1.2 only)</li> + * <li>stubs - generate C declarations from the Java object file (used with old)</li> + * <li>verbose - causes javah to print a message to stdout concerning the status + * of the generated files</li> + * <li>extdirs - Override location of installed extensions</li> + * </ul> + * Of these arguments, either <b>outputFile</b> or <b>destdir</b> is required, + * but not both. More than one classname may be specified, using a comma-separated + * list or by using <code><class name="xxx"></code> elements within the task. + * <p> + * When this task executes, it will generate C header and source files that + * are needed to implement native methods. + * + */ + +public class Javah extends Task { + + private Vector classes = new Vector(2); + private String cls; + private File destDir; + private Path classpath = null; + private File outputFile = null; + private boolean verbose = false; + private boolean force = false; + private boolean old = false; + private boolean stubs = false; + private Path bootclasspath; + //private Path extdirs; + private FacadeTaskHelper facade = null; + private Vector files = new Vector(); + private JavahAdapter nestedAdapter = null; + + /** + * No arg constructor. + */ + public Javah() { + facade = new FacadeTaskHelper(JavahAdapterFactory.getDefault()); + } + + /** + * the fully-qualified name of the class (or classes, separated by commas). + * @param cls the classname (or classnames). + */ + public void setClass(String cls) { + this.cls = cls; + } + + /** + * Adds class to process. + * @return a <code>ClassArgument</code> to be configured. + */ + public ClassArgument createClass() { + ClassArgument ga = new ClassArgument(); + classes.addElement(ga); + return ga; + } + + /** + * A class corresponding the the nested "class" element. + * It contains a "name" attribute. + */ + public class ClassArgument { + private String name; + + /** Constructor for ClassArgument. */ + public ClassArgument() { + } + + /** + * Set the name attribute. + * @param name the name attribute. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Get the name attribute. + * @return the name attribute. + */ + public String getName() { + return name; + } + } + + /** + * Add a fileset. + * @param fs the fileset to add. + */ + public void addFileSet(FileSet fs) { + files.add(fs); + } + + /** + * Names of the classes to process. + * @return the array of classes. + * @since Ant 1.6.3 + */ + public String[] getClasses() { + ArrayList al = new ArrayList(); + if (cls != null) { + StringTokenizer tok = new StringTokenizer(cls, ",", false); + while (tok.hasMoreTokens()) { + al.add(tok.nextToken().trim()); + } + } + + if (files.size() > 0) { + for (Enumeration e = files.elements(); e.hasMoreElements();) { + FileSet fs = (FileSet) e.nextElement(); + String[] includedClasses = fs.getDirectoryScanner( + getProject()).getIncludedFiles(); + for (int i = 0; i < includedClasses.length; i++) { + String className = + includedClasses[i].replace('\\', '.').replace('/', '.') + .substring(0, includedClasses[i].length() - 6); + al.add(className); + } + } + } + Enumeration e = classes.elements(); + while (e.hasMoreElements()) { + ClassArgument arg = (ClassArgument) e.nextElement(); + al.add(arg.getName()); + } + return (String[]) al.toArray(new String[al.size()]); + } + + /** + * Set the destination directory into which the Java source + * files should be compiled. + * @param destDir the destination directory. + */ + public void setDestdir(File destDir) { + this.destDir = destDir; + } + + /** + * The destination directory, if any. + * @return the destination directory. + * @since Ant 1.6.3 + */ + public File getDestdir() { + return destDir; + } + + /** + * the classpath to use. + * @param src the classpath. + */ + public void setClasspath(Path src) { + if (classpath == null) { + classpath = src; + } else { + classpath.append(src); + } + } + + /** + * Path to use for classpath. + * @return a path to be configured. + */ + public Path createClasspath() { + if (classpath == null) { + classpath = new Path(getProject()); + } + return classpath.createPath(); + } + + /** + * Adds a reference to a classpath defined elsewhere. + * @param r a reference to a classpath. + * @todo this needs to be documented in the HTML docs. + */ + public void setClasspathRef(Reference r) { + createClasspath().setRefid(r); + } + + /** + * The classpath to use. + * @return the classpath. + * @since Ant 1.6.3 + */ + public Path getClasspath() { + return classpath; + } + + /** + * location of bootstrap class files. + * @param src the bootstrap classpath. + */ + public void setBootclasspath(Path src) { + if (bootclasspath == null) { + bootclasspath = src; + } else { + bootclasspath.append(src); + } + } + + /** + * Adds path to bootstrap class files. + * @return a path to be configured. + */ + public Path createBootclasspath() { + if (bootclasspath == null) { + bootclasspath = new Path(getProject()); + } + return bootclasspath.createPath(); + } + + /** + * To the bootstrap path, this adds a reference to a classpath defined elsewhere. + * @param r a reference to a classpath + * @todo this needs to be documented in the HTML. + */ + public void setBootClasspathRef(Reference r) { + createBootclasspath().setRefid(r); + } + + /** + * The bootclasspath to use. + * @return the bootclass path. + * @since Ant 1.6.3 + */ + public Path getBootclasspath() { + return bootclasspath; + } + + /** + * Concatenates the resulting header or source files for all + * the classes listed into this file. + * @param outputFile the output file. + */ + public void setOutputFile(File outputFile) { + this.outputFile = outputFile; + } + + /** + * The destination file, if any. + * @return the destination file. + * @since Ant 1.6.3 + */ + public File getOutputfile() { + return outputFile; + } + + /** + * If true, output files should always be written (JDK1.2 only). + * @param force the value to use. + */ + public void setForce(boolean force) { + this.force = force; + } + + /** + * Whether output files should always be written. + * @return the force attribute. + * @since Ant 1.6.3 + */ + public boolean getForce() { + return force; + } + + /** + * If true, specifies that old JDK1.0-style header files should be + * generated. + * (otherwise output file contain JNI-style native method function + * prototypes) (JDK1.2 only). + * @param old if true use old 1.0 style header files. + */ + public void setOld(boolean old) { + this.old = old; + } + + /** + * Whether old JDK1.0-style header files should be generated. + * @return the old attribute. + * @since Ant 1.6.3 + */ + public boolean getOld() { + return old; + } + + /** + * If true, generate C declarations from the Java object file (used with old). + * @param stubs if true, generated C declarations. + */ + public void setStubs(boolean stubs) { + this.stubs = stubs; + } + + /** + * Whether C declarations from the Java object file should be generated. + * @return the stubs attribute. + * @since Ant 1.6.3 + */ + public boolean getStubs() { + return stubs; + } + + /** + * If true, causes Javah to print a message concerning + * the status of the generated files. + * @param verbose if true, do verbose printing. + */ + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + /** + * Whether verbose output should get generated. + * @return the verbose attribute. + * @since Ant 1.6.3 + */ + public boolean getVerbose() { + return verbose; + } + + /** + * Choose the implementation for this particular task. + * @param impl the name of the implementation. + * @since Ant 1.6.3 + */ + public void setImplementation(String impl) { + if ("default".equals(impl)) { + facade.setImplementation(JavahAdapterFactory.getDefault()); + } else { + facade.setImplementation(impl); + } + } + + /** + * Adds an implementation specific command-line argument. + * @return a ImplementationSpecificArgument to be configured. + * + * @since Ant 1.6.3 + */ + public ImplementationSpecificArgument createArg() { + ImplementationSpecificArgument arg = + new ImplementationSpecificArgument(); + facade.addImplementationArgument(arg); + return arg; + } + + /** + * Returns the (implementation specific) settings given as nested + * arg elements. + * @return the arguments. + * @since Ant 1.6.3 + */ + public String[] getCurrentArgs() { + return facade.getArgs(); + } + + /** + * The classpath to use when loading the javah implementation + * if it is not a built-in one. + * + * @since Ant 1.8.0 + */ + public Path createImplementationClasspath() { + return facade.getImplementationClasspath(getProject()); + } + + /** + * Set the adapter explicitly. + * @since Ant 1.8.0 + */ + public void add(JavahAdapter adapter) { + if (nestedAdapter != null) { + throw new BuildException("Can't have more than one javah" + + " adapter"); + } + nestedAdapter = adapter; + } + + /** + * Execute the task + * + * @throws BuildException is there is a problem in the task execution. + */ + public void execute() throws BuildException { + // first off, make sure that we've got a srcdir + + if ((cls == null) && (classes.size() == 0) && (files.size() == 0)) { + throw new BuildException("class attribute must be set!", + getLocation()); + } + + if ((cls != null) && (classes.size() > 0) && (files.size() > 0)) { + throw new BuildException("set class attribute OR class element OR fileset, " + + "not 2 or more of them.", getLocation()); + } + + if (destDir != null) { + if (!destDir.isDirectory()) { + throw new BuildException("destination directory \"" + destDir + + "\" does not exist or is not a directory", getLocation()); + } + if (outputFile != null) { + throw new BuildException("destdir and outputFile are mutually " + + "exclusive", getLocation()); + } + } + + if (classpath == null) { + classpath = (new Path(getProject())).concatSystemClasspath("last"); + } else { + classpath = classpath.concatSystemClasspath("ignore"); + } + + JavahAdapter ad = + nestedAdapter != null ? nestedAdapter : + JavahAdapterFactory.getAdapter(facade.getImplementation(), + this, + createImplementationClasspath()); + if (!ad.compile(this)) { + throw new BuildException("compilation failed"); + } + } + + /** + * Logs the compilation parameters, adds the files to compile and logs the + * "niceSourceList" + * @param cmd the command line. + */ + public void logAndAddFiles(Commandline cmd) { + logAndAddFilesToCompile(cmd); + } + + /** + * Logs the compilation parameters, adds the files to compile and logs the + * "niceSourceList" + * @param cmd the command line to add parameters to. + */ + protected void logAndAddFilesToCompile(Commandline cmd) { + log("Compilation " + cmd.describeArguments(), + Project.MSG_VERBOSE); + + StringBuffer niceClassList = new StringBuffer(); + String[] c = getClasses(); + for (int i = 0; i < c.length; i++) { + cmd.createArgument().setValue(c[i]); + niceClassList.append(" "); + niceClassList.append(c[i]); + niceClassList.append(StringUtils.LINE_SEP); + } + + StringBuffer prefix = new StringBuffer("Class"); + if (c.length > 1) { + prefix.append("es"); + } + prefix.append(" to be compiled:"); + prefix.append(StringUtils.LINE_SEP); + + log(prefix.toString() + niceClassList.toString(), Project.MSG_VERBOSE); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Native2Ascii.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Native2Ascii.java new file mode 100644 index 00000000..81a386fd --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Native2Ascii.java @@ -0,0 +1,328 @@ +/* + * 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.tools.ant.taskdefs.optional; + +import java.io.File; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.MatchingTask; +import org.apache.tools.ant.taskdefs.optional.native2ascii.Native2AsciiAdapter; +import org.apache.tools.ant.taskdefs.optional.native2ascii.Native2AsciiAdapterFactory; +import org.apache.tools.ant.types.Mapper; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.FileNameMapper; +import org.apache.tools.ant.util.IdentityMapper; +import org.apache.tools.ant.util.SourceFileScanner; +import org.apache.tools.ant.util.facade.FacadeTaskHelper; +import org.apache.tools.ant.util.facade.ImplementationSpecificArgument; + +/** + * Converts files from native encodings to ASCII. + * + * @since Ant 1.2 + */ +public class Native2Ascii extends MatchingTask { + + private boolean reverse = false; // convert from ascii back to native + private String encoding = null; // encoding to convert to/from + private File srcDir = null; // Where to find input files + private File destDir = null; // Where to put output files + private String extension = null; // Extension of output files if different + + private Mapper mapper; + private FacadeTaskHelper facade = null; + private Native2AsciiAdapter nestedAdapter = null; + + /** No args constructor */ + public Native2Ascii() { + facade = new FacadeTaskHelper(Native2AsciiAdapterFactory.getDefault()); + } + + /** + * Flag the conversion to run in the reverse sense, + * that is Ascii to Native encoding. + * + * @param reverse True if the conversion is to be reversed, + * otherwise false; + */ + public void setReverse(boolean reverse) { + this.reverse = reverse; + } + + /** + * The value of the reverse attribute. + * @return the reverse attribute. + * @since Ant 1.6.3 + */ + public boolean getReverse() { + return reverse; + } + + /** + * Set the encoding to translate to/from. + * If unset, the default encoding for the JVM is used. + * + * @param encoding String containing the name of the Native + * encoding to convert from or to. + */ + public void setEncoding(String encoding) { + this.encoding = encoding; + } + + /** + * The value of the encoding attribute. + * @return the encoding attribute. + * @since Ant 1.6.3 + */ + public String getEncoding() { + return encoding; + } + + /** + * Set the source directory in which to find files to convert. + * + * @param srcDir directory to find input file in. + */ + public void setSrc(File srcDir) { + this.srcDir = srcDir; + } + + + /** + * Set the destination directory to place converted files into. + * + * @param destDir directory to place output file into. + */ + public void setDest(File destDir) { + this.destDir = destDir; + } + + /** + * Set the extension which converted files should have. + * If unset, files will not be renamed. + * + * @param ext File extension to use for converted files. + */ + public void setExt(String ext) { + this.extension = ext; + } + + /** + * Choose the implementation for this particular task. + * @param impl the name of the implementation + * @since Ant 1.6.3 + */ + public void setImplementation(String impl) { + if ("default".equals(impl)) { + facade.setImplementation(Native2AsciiAdapterFactory.getDefault()); + } else { + facade.setImplementation(impl); + } + } + + /** + * Defines the FileNameMapper to use (nested mapper element). + * + * @return the mapper to use for file name translations. + * + * @throws BuildException if more than one mapper is defined. + */ + public Mapper createMapper() throws BuildException { + if (mapper != null) { + throw new BuildException("Cannot define more than one mapper", + getLocation()); + } + mapper = new Mapper(getProject()); + return mapper; + } + + /** + * A nested filenamemapper + * @param fileNameMapper the mapper to add + * @since Ant 1.6.3 + */ + public void add(FileNameMapper fileNameMapper) { + createMapper().add(fileNameMapper); + } + + /** + * Adds an implementation specific command-line argument. + * @return a ImplementationSpecificArgument to be configured + * + * @since Ant 1.6.3 + */ + public ImplementationSpecificArgument createArg() { + ImplementationSpecificArgument arg = + new ImplementationSpecificArgument(); + facade.addImplementationArgument(arg); + return arg; + } + + /** + * The classpath to use when loading the native2ascii + * implementation if it is not a built-in one. + * + * @since Ant 1.8.0 + */ + public Path createImplementationClasspath() { + return facade.getImplementationClasspath(getProject()); + } + + /** + * Set the adapter explicitly. + * @since Ant 1.8.0 + */ + public void add(Native2AsciiAdapter adapter) { + if (nestedAdapter != null) { + throw new BuildException("Can't have more than one native2ascii" + + " adapter"); + } + nestedAdapter = adapter; + } + + /** + * Execute the task + * + * @throws BuildException is there is a problem in the task execution. + */ + public void execute() throws BuildException { + + DirectoryScanner scanner = null; // Scanner to find our inputs + String[] files; // list of files to process + + // default srcDir to basedir + if (srcDir == null) { + srcDir = getProject().resolveFile("."); + } + + // Require destDir + if (destDir == null) { + throw new BuildException("The dest attribute must be set."); + } + + // if src and dest dirs are the same, require the extension + // to be set, so we don't stomp every file. One could still + // include a file with the same extension, but .... + if (srcDir.equals(destDir) && extension == null && mapper == null) { + throw new BuildException("The ext attribute or a mapper must be set if" + + " src and dest dirs are the same."); + } + + FileNameMapper m = null; + if (mapper == null) { + if (extension == null) { + m = new IdentityMapper(); + } else { + m = new ExtMapper(); + } + } else { + m = mapper.getImplementation(); + } + + scanner = getDirectoryScanner(srcDir); + files = scanner.getIncludedFiles(); + SourceFileScanner sfs = new SourceFileScanner(this); + files = sfs.restrict(files, srcDir, destDir, m); + int count = files.length; + if (count == 0) { + return; + } + String message = "Converting " + count + " file" + + (count != 1 ? "s" : "") + " from "; + log(message + srcDir + " to " + destDir); + for (int i = 0; i < files.length; i++) { + convert(files[i], m.mapFileName(files[i])[0]); + } + } + + /** + * Convert a single file. + * + * @param srcName name of the input file. + * @param destName name of the input file. + */ + private void convert(String srcName, String destName) + throws BuildException { + File srcFile; // File to convert + File destFile; // where to put the results + + // Build the full file names + srcFile = new File(srcDir, srcName); + destFile = new File(destDir, destName); + + // Make sure we're not about to clobber something + if (srcFile.equals(destFile)) { + throw new BuildException("file " + srcFile + + " would overwrite its self"); + } + + // Make intermediate directories if needed + // TODO JDK 1.1 doesn't have File.getParentFile, + String parentName = destFile.getParent(); + if (parentName != null) { + File parentFile = new File(parentName); + + if (!parentFile.exists() + && !(parentFile.mkdirs() || parentFile.isDirectory())) { + throw new BuildException("cannot create parent directory " + + parentName); + } + } + + log("converting " + srcName, Project.MSG_VERBOSE); + Native2AsciiAdapter ad = + nestedAdapter != null ? nestedAdapter : + Native2AsciiAdapterFactory.getAdapter(facade.getImplementation(), + this, + createImplementationClasspath()); + if (!ad.convert(this, srcFile, destFile)) { + throw new BuildException("conversion failed"); + } + } + + /** + * Returns the (implementation specific) settings given as nested + * arg elements. + * @return the arguments. + * @since Ant 1.6.3 + */ + public String[] getCurrentArgs() { + return facade.getArgs(); + } + + private class ExtMapper implements FileNameMapper { + + public void setFrom(String s) { + } + public void setTo(String s) { + } + + public String[] mapFileName(String fileName) { + int lastDot = fileName.lastIndexOf('.'); + if (lastDot >= 0) { + return new String[] {fileName.substring(0, lastDot) + + extension}; + } else { + return new String[] {fileName + extension}; + } + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/NetRexxC.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/NetRexxC.java new file mode 100644 index 00000000..5ba2a760 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/NetRexxC.java @@ -0,0 +1,1042 @@ +/* + * 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.tools.ant.taskdefs.optional; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.Vector; + +import netrexx.lang.Rexx; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.MatchingTask; +import org.apache.tools.ant.types.EnumeratedAttribute; +import org.apache.tools.ant.util.FileUtils; + +// CheckStyle:InnerAssignmentCheck OFF - used too much in the file to be removed +/** + * Compiles NetRexx source files. + * This task can take the following + * arguments: + * <ul> + * <li>binary</li> + * <li>classpath</li> + * <li>comments</li> + * <li>compile</li> + * <li>console</li> + * <li>crossref</li> + * <li>decimal</li> + * <li>destdir</li> + * <li>diag</li> + * <li>explicit</li> + * <li>format</li> + * <li>keep</li> + * <li>logo</li> + * <li>replace</li> + * <li>savelog</li> + * <li>srcdir</li> + * <li>sourcedir</li> + * <li>strictargs</li> + * <li>strictassign</li> + * <li>strictcase</li> + * <li>strictimport</li> + * <li>symbols</li> + * <li>time</li> + * <li>trace</li> + * <li>utf8</li> + * <li>verbose</li> + * <li>suppressMethodArgumentNotUsed</li> + * <li>suppressPrivatePropertyNotUsed</li> + * <li>suppressVariableNotUsed</li> + * <li>suppressExceptionNotSignalled</li> + * <li>suppressDeprecation</li> + * <li>removeKeepExtension</li> + * </ul> + * Of these arguments, the <b>srcdir</b> argument is required. + * + * <p>When this task executes, it will recursively scan the srcdir + * looking for NetRexx source files to compile. This task makes its + * compile decision based on timestamp. + * <p>Before files are compiled they and any other file in the + * srcdir will be copied to the destdir allowing support files to be + * located properly in the classpath. The reason for copying the source files + * before the compile is that NetRexxC has only two destinations for classfiles: + * <ol> + * <li>The current directory, and,</li> + * <li>The directory the source is in (see sourcedir option) + * </ol> + * + */ +public class NetRexxC extends MatchingTask { + + // variables to hold arguments + private boolean binary; + private String classpath; + private boolean comments; + private boolean compact = true; // should be the default, as it integrates better in ant. + private boolean compile = true; + private boolean console; + private boolean crossref; + private boolean decimal = true; + private File destDir; + private boolean diag; + private boolean explicit; + private boolean format; + private boolean keep; + private boolean logo = true; + private boolean replace; + private boolean savelog; + private File srcDir; + private boolean sourcedir = true; // ?? Should this be the default for ant? + private boolean strictargs; + private boolean strictassign; + private boolean strictcase; + private boolean strictimport; + private boolean strictprops; + private boolean strictsignal; + private boolean symbols; + private boolean time; + private String trace = "trace2"; + private boolean utf8; + private String verbose = "verbose3"; + private boolean suppressMethodArgumentNotUsed = false; + private boolean suppressPrivatePropertyNotUsed = false; + private boolean suppressVariableNotUsed = false; + private boolean suppressExceptionNotSignalled = false; + private boolean suppressDeprecation = false; + private boolean removeKeepExtension = false; + + // constants for the messages to suppress by flags and their corresponding properties + static final String MSG_METHOD_ARGUMENT_NOT_USED + = "Warning: Method argument is not used"; + static final String MSG_PRIVATE_PROPERTY_NOT_USED + = "Warning: Private property is defined but not used"; + static final String MSG_VARIABLE_NOT_USED + = "Warning: Variable is set but not used"; + static final String MSG_EXCEPTION_NOT_SIGNALLED + = "is in SIGNALS list but is not signalled within the method"; + static final String MSG_DEPRECATION = "has been deprecated"; + + // other implementation variables + private Vector compileList = new Vector(); + private Hashtable filecopyList = new Hashtable(); + + /** + * Set whether literals are treated as binary, rather than NetRexx types. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default is false. + * @param binary a <code>boolean</code> value. + */ + public void setBinary(boolean binary) { + this.binary = binary; + } + + + /** + * Set the classpath used for NetRexx compilation. + * @param classpath the classpath to use. + */ + public void setClasspath(String classpath) { + this.classpath = classpath; + } + + + /** + * Set whether comments are passed through to the generated java source. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is false. + * @param comments a <code>boolean</code> value. + */ + public void setComments(boolean comments) { + this.comments = comments; + } + + + /** + * Set whether error messages come out in compact or verbose format. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is true. + * @param compact a <code>boolean</code> value. + */ + public void setCompact(boolean compact) { + this.compact = compact; + } + + + /** + * Set whether the NetRexx compiler should compile the generated java code. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is true. + * Setting this flag to false, will automatically set the keep flag to true. + * @param compile a <code>boolean</code> value. + */ + public void setCompile(boolean compile) { + this.compile = compile; + if (!this.compile && !this.keep) { + this.keep = true; + } + } + + + /** + * Set whether or not compiler messages should be displayed on the 'console'. + * Note that this task will rely on the default value for filtering compile messages. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is false. + * @param console a <code>boolean</code> value. + */ + public void setConsole(boolean console) { + this.console = console; + } + + + /** + * Whether variable cross references are generated. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is false. + * @param crossref a <code>boolean</code> value. + */ + public void setCrossref(boolean crossref) { + this.crossref = crossref; + } + + + /** + * Set whether decimal arithmetic should be used for the netrexx code. + * Setting this to off will report decimal arithmetic as an error, for + * performance critical applications. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is true. + * @param decimal a <code>boolean</code> value. + */ + public void setDecimal(boolean decimal) { + this.decimal = decimal; + } + + + /** + * Set the destination directory into which the NetRexx source files + * should be copied and then compiled. + * @param destDirName the destination directory. + */ + public void setDestDir(File destDirName) { + destDir = destDirName; + } + + + /** + * Whether diagnostic information about the compile is generated + * @param diag a <code>boolean</code> value. + */ + public void setDiag(boolean diag) { + this.diag = diag; + } + + + /** + * Sets whether variables must be declared explicitly before use. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is false. + * @param explicit a <code>boolean</code> value. + */ + public void setExplicit(boolean explicit) { + this.explicit = explicit; + } + + + /** + * Whether the generated java code is formatted nicely or left to match + * NetRexx line numbers for call stack debugging. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value false. + * @param format a <code>boolean</code> value. + */ + public void setFormat(boolean format) { + this.format = format; + } + + + /** + * Whether the generated java code is produced. + * This is not implemented yet. + * @param java a <code>boolean</code> value. + */ + public void setJava(boolean java) { + log("The attribute java is currently unused.", Project.MSG_WARN); + } + + + /** + * Sets whether the generated java source file should be kept after + * compilation. The generated files will have an extension of .java.keep, + * <b>not</b> .java. See setRemoveKeepExtension + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is false. + * @param keep a <code>boolean</code> value. + * @see #setRemoveKeepExtension(boolean) + */ + public void setKeep(boolean keep) { + this.keep = keep; + } + + + /** + * Whether the compiler text logo is displayed when compiling. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is false. + * @param logo a <code>boolean</code> value. + */ + public void setLogo(boolean logo) { + this.logo = logo; + } + + + /** + * Whether the generated .java file should be replaced when compiling. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is false. + * @param replace a <code>boolean</code> value. + */ + public void setReplace(boolean replace) { + this.replace = replace; + } + + + /** + * Sets whether the compiler messages will be written to NetRexxC.log as + * well as to the console. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is false. + * @param savelog a <code>boolean</code> value. + */ + public void setSavelog(boolean savelog) { + this.savelog = savelog; + } + + + /** + * Tells the NetRexx compiler to store the class files in the same + * directory as the source files. The alternative is the working directory. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is true. + * @param sourcedir a <code>boolean</code> value. + */ + public void setSourcedir(boolean sourcedir) { + this.sourcedir = sourcedir; + } + + + /** + * Set the source dir to find the source Java files. + * @param srcDirName the source directory. + */ + public void setSrcDir(File srcDirName) { + srcDir = srcDirName; + } + + + /** + * Tells the NetRexx compiler that method calls always need parentheses, + * even if no arguments are needed, e.g. <code>aStringVar.getBytes</code> + * vs. <code>aStringVar.getBytes()</code>. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is false. + * @param strictargs a <code>boolean</code> value. + */ + public void setStrictargs(boolean strictargs) { + this.strictargs = strictargs; + } + + + /** + * Tells the NetRexx compile that assignments must match exactly on type. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is false. + * @param strictassign a <code>boolean</code> value. + */ + public void setStrictassign(boolean strictassign) { + this.strictassign = strictassign; + } + + + /** + * Specifies whether the NetRexx compiler should be case sensitive or not. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is false. + * @param strictcase a <code>boolean</code> value. + */ + public void setStrictcase(boolean strictcase) { + this.strictcase = strictcase; + } + + + /** + * Sets whether classes need to be imported explicitly using an <code>import</code> + * statement. By default the NetRexx compiler will import certain packages + * automatically. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is false. + * @param strictimport a <code>boolean</code> value. + */ + public void setStrictimport(boolean strictimport) { + this.strictimport = strictimport; + } + + + /** + * Sets whether local properties need to be qualified explicitly using + * <code>this</code>. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is false. + * @param strictprops a <code>boolean</code> value. + */ + public void setStrictprops(boolean strictprops) { + this.strictprops = strictprops; + } + + + /** + * Whether the compiler should force catching of exceptions by explicitly + * named types. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is false + * @param strictsignal a <code>boolean</code> value. + */ + public void setStrictsignal(boolean strictsignal) { + this.strictsignal = strictsignal; + } + + + /** + * Sets whether debug symbols should be generated into the class file. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is false. + * @param symbols a <code>boolean</code> value. + */ + public void setSymbols(boolean symbols) { + this.symbols = symbols; + } + + + /** + * Asks the NetRexx compiler to print compilation times to the console + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is false. + * @param time a <code>boolean</code> value. + */ + public void setTime(boolean time) { + this.time = time; + } + + /** + * Turns on or off tracing and directs the resultant trace output Valid + * values are: "trace", "trace1", "trace2" and "notrace". "trace" and + * "trace2". + * @param trace the value to set. + */ + public void setTrace(TraceAttr trace) { + this.trace = trace.getValue(); + } + + /** + * Turns on or off tracing and directs the resultant trace output Valid + * values are: "trace", "trace1", "trace2" and "notrace". "trace" and + * "trace2". + * @param trace the value to set. + */ + public void setTrace(String trace) { + TraceAttr t = new TraceAttr(); + + t.setValue(trace); + setTrace(t); + } + + + /** + * Tells the NetRexx compiler that the source is in UTF8. + * Valid true values are "yes", "on" or "true". Anything else sets the flag to false. + * The default value is false. + * @param utf8 a <code>boolean</code> value. + */ + public void setUtf8(boolean utf8) { + this.utf8 = utf8; + } + + + /** + * Whether lots of warnings and error messages should be generated + * @param verbose the value to set - verbose<level> or noverbose. + */ + public void setVerbose(VerboseAttr verbose) { + this.verbose = verbose.getValue(); + } + + + /** + * Whether lots of warnings and error messages should be generated + * @param verbose the value to set - verbose<level> or noverbose. + */ + public void setVerbose(String verbose) { + VerboseAttr v = new VerboseAttr(); + + v.setValue(verbose); + setVerbose(v); + } + + /** + * Whether the task should suppress the "Method argument is not used" in + * strictargs-Mode, which can not be suppressed by the compiler itself. + * The warning is logged as verbose message, though. + * @param suppressMethodArgumentNotUsed a <code>boolean</code> value. + */ + public void setSuppressMethodArgumentNotUsed(boolean suppressMethodArgumentNotUsed) { + this.suppressMethodArgumentNotUsed = suppressMethodArgumentNotUsed; + } + + + /** + * Whether the task should suppress the "Private property is defined but + * not used" in strictargs-Mode, which can be quite annoying while + * developing. The warning is logged as verbose message, though. + * @param suppressPrivatePropertyNotUsed a <code>boolean</code> value. + */ + public void setSuppressPrivatePropertyNotUsed(boolean suppressPrivatePropertyNotUsed) { + this.suppressPrivatePropertyNotUsed = suppressPrivatePropertyNotUsed; + } + + + /** + * Whether the task should suppress the "Variable is set but not used" in + * strictargs-Mode. Be careful with this one! The warning is logged as + * verbose message, though. + * @param suppressVariableNotUsed a <code>boolean</code> value. + */ + public void setSuppressVariableNotUsed(boolean suppressVariableNotUsed) { + this.suppressVariableNotUsed = suppressVariableNotUsed; + } + + + /** + * Whether the task should suppress the "FooException is in SIGNALS list + * but is not signalled within the method", which is sometimes rather + * useless. The warning is logged as verbose message, though. + * @param suppressExceptionNotSignalled a <code>boolean</code> value. + */ + public void setSuppressExceptionNotSignalled(boolean suppressExceptionNotSignalled) { + this.suppressExceptionNotSignalled = suppressExceptionNotSignalled; + } + + + /** + * Tells whether we should filter out any deprecation-messages + * of the compiler out. + * @param suppressDeprecation a <code>boolean</code> value. + */ + public void setSuppressDeprecation(boolean suppressDeprecation) { + this.suppressDeprecation = suppressDeprecation; + } + + + /** + * Tells whether the trailing .keep in nocompile-mode should be removed + * so that the resulting java source really ends on .java. + * This facilitates the use of the javadoc tool lateron. + */ + public void setRemoveKeepExtension(boolean removeKeepExtension) { + this.removeKeepExtension = removeKeepExtension; + } + + + /** + * init-Method sets defaults from Properties. That way, when ant is called + * with arguments like -Dant.netrexxc.verbose=verbose5 one can easily take + * control of all netrexxc-tasks. + */ + public void init() { + String p; + + if ((p = getProject().getProperty("ant.netrexxc.binary")) != null) { + this.binary = Project.toBoolean(p); + } + // classpath makes no sense + if ((p = getProject().getProperty("ant.netrexxc.comments")) != null) { + this.comments = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.compact")) != null) { + this.compact = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.compile")) != null) { + this.compile = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.console")) != null) { + this.console = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.crossref")) != null) { + this.crossref = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.decimal")) != null) { + this.decimal = Project.toBoolean(p); + // destDir + } + if ((p = getProject().getProperty("ant.netrexxc.diag")) != null) { + this.diag = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.explicit")) != null) { + this.explicit = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.format")) != null) { + this.format = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.keep")) != null) { + this.keep = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.logo")) != null) { + this.logo = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.replace")) != null) { + this.replace = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.savelog")) != null) { + this.savelog = Project.toBoolean(p); + // srcDir + } + if ((p = getProject().getProperty("ant.netrexxc.sourcedir")) != null) { + this.sourcedir = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.strictargs")) != null) { + this.strictargs = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.strictassign")) != null) { + this.strictassign = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.strictcase")) != null) { + this.strictcase = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.strictimport")) != null) { + this.strictimport = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.strictprops")) != null) { + this.strictprops = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.strictsignal")) != null) { + this.strictsignal = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.symbols")) != null) { + this.symbols = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.time")) != null) { + this.time = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.trace")) != null) { + setTrace(p); + } + if ((p = getProject().getProperty("ant.netrexxc.utf8")) != null) { + this.utf8 = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.verbose")) != null) { + setVerbose(p); + } + if ((p = getProject().getProperty("ant.netrexxc.suppressMethodArgumentNotUsed")) != null) { + this.suppressMethodArgumentNotUsed = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.suppressPrivatePropertyNotUsed")) != null) { + this.suppressPrivatePropertyNotUsed = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.suppressVariableNotUsed")) != null) { + this.suppressVariableNotUsed = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.suppressExceptionNotSignalled")) != null) { + this.suppressExceptionNotSignalled = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.suppressDeprecation")) != null) { + this.suppressDeprecation = Project.toBoolean(p); + } + if ((p = getProject().getProperty("ant.netrexxc.removeKeepExtension")) != null) { + this.removeKeepExtension = Project.toBoolean(p); + } + } + + + /** + * Executes the task - performs the actual compiler call. + * @throws BuildException on error. + */ + public void execute() throws BuildException { + + // first off, make sure that we've got a srcdir and destdir + if (srcDir == null || destDir == null) { + throw new BuildException("srcDir and destDir attributes must be set!"); + } + + // scan source and dest dirs to build up both copy lists and + // compile lists + // scanDir(srcDir, destDir); + DirectoryScanner ds = getDirectoryScanner(srcDir); + + String[] files = ds.getIncludedFiles(); + + scanDir(srcDir, destDir, files); + + // copy the source and support files + copyFilesToDestination(); + + // compile the source files + if (compileList.size() > 0) { + log("Compiling " + compileList.size() + " source file" + + (compileList.size() == 1 ? "" : "s") + + " to " + destDir); + doNetRexxCompile(); + if (removeKeepExtension && (!compile || keep)) { + removeKeepExtensions(); + } + } + } + + + /** + * Scans the directory looking for source files to be compiled and support + * files to be copied. + */ + private void scanDir(File srcDir, File destDir, String[] files) { + for (int i = 0; i < files.length; i++) { + File srcFile = new File(srcDir, files[i]); + File destFile = new File(destDir, files[i]); + String filename = files[i]; + // if it's a non source file, copy it if a later date than the + // dest + // if it's a source file, see if the destination class file + // needs to be recreated via compilation + if (filename.toLowerCase().endsWith(".nrx")) { + File classFile = + new File(destDir, + filename.substring(0, filename.lastIndexOf('.')) + ".class"); + File javaFile = + new File(destDir, + filename.substring(0, filename.lastIndexOf('.')) + + (removeKeepExtension ? ".java" : ".java.keep")); + + // nocompile case tests against .java[.keep] file + if (!compile && srcFile.lastModified() > javaFile.lastModified()) { + filecopyList.put(srcFile.getAbsolutePath(), destFile.getAbsolutePath()); + compileList.addElement(destFile.getAbsolutePath()); + } else if (compile && srcFile.lastModified() > classFile.lastModified()) { + // compile case tests against .class file + filecopyList.put(srcFile.getAbsolutePath(), destFile.getAbsolutePath()); + compileList.addElement(destFile.getAbsolutePath()); + } + } else { + if (srcFile.lastModified() > destFile.lastModified()) { + filecopyList.put(srcFile.getAbsolutePath(), destFile.getAbsolutePath()); + } + } + } + } + + + /** Copy eligible files from the srcDir to destDir */ + private void copyFilesToDestination() { + if (filecopyList.size() > 0) { + log("Copying " + filecopyList.size() + " file" + + (filecopyList.size() == 1 ? "" : "s") + + " to " + destDir.getAbsolutePath()); + + Enumeration e = filecopyList.keys(); + + while (e.hasMoreElements()) { + String fromFile = (String) e.nextElement(); + String toFile = (String) filecopyList.get(fromFile); + + try { + FileUtils.getFileUtils().copyFile(fromFile, toFile); + } catch (IOException ioe) { + String msg = "Failed to copy " + fromFile + " to " + toFile + + " due to " + ioe.getMessage(); + + throw new BuildException(msg, ioe); + } + } + } + } + + + /** + * Rename .java.keep files (back) to .java. The netrexxc renames all + * .java files to .java.keep if either -keep or -nocompile option is set. + */ + private void removeKeepExtensions() { + if (compileList.size() > 0) { + log("Removing .keep extension on " + compileList.size() + " file" + + (compileList.size() == 1 ? "" : "s")); + Enumeration e = compileList.elements(); + while (e.hasMoreElements()) { + String nrxName = (String) e.nextElement(); + String baseName = nrxName.substring(0, nrxName.lastIndexOf('.')); + File fromFile = new File(baseName + ".java.keep"); + File toFile = new File(baseName + ".java"); + if (fromFile.renameTo(toFile)) { + log("Successfully renamed " + fromFile + " to " + toFile, Project.MSG_VERBOSE); + } else { + log("Failed to rename " + fromFile + " to " + toFile); + } + } + } + } + + + /** Performs a compile using the NetRexx 1.1.x compiler */ + private void doNetRexxCompile() throws BuildException { + log("Using NetRexx compiler", Project.MSG_VERBOSE); + + String classpath = getCompileClasspath(); + StringBuffer compileOptions = new StringBuffer(); + + // create an array of strings for input to the compiler: one array + // comes from the compile options, the other from the compileList + String[] compileOptionsArray = getCompileOptionsAsArray(); + String[] fileListArray = new String[compileList.size()]; + Enumeration e = compileList.elements(); + int j = 0; + + while (e.hasMoreElements()) { + fileListArray[j] = (String) e.nextElement(); + j++; + } + // create a single array of arguments for the compiler + String[] compileArgs = new String[compileOptionsArray.length + fileListArray.length]; + + for (int i = 0; i < compileOptionsArray.length; i++) { + compileArgs[i] = compileOptionsArray[i]; + } + for (int i = 0; i < fileListArray.length; i++) { + compileArgs[i + compileOptionsArray.length] = fileListArray[i]; + } + + // print nice output about what we are doing for the log + compileOptions.append("Compilation args: "); + for (int i = 0; i < compileOptionsArray.length; i++) { + compileOptions.append(compileOptionsArray[i]); + compileOptions.append(" "); + } + log(compileOptions.toString(), Project.MSG_VERBOSE); + + String eol = System.getProperty("line.separator"); + StringBuffer niceSourceList = new StringBuffer("Files to be compiled:" + eol); + + final int size = compileList.size(); + for (int i = 0; i < size; i++) { + niceSourceList.append(" "); + niceSourceList.append(compileList.elementAt(i).toString()); + niceSourceList.append(eol); + } + + log(niceSourceList.toString(), Project.MSG_VERBOSE); + + // need to set java.class.path property and restore it later + // since the NetRexx compiler has no option for the classpath + String currentClassPath = System.getProperty("java.class.path"); + Properties currentProperties = System.getProperties(); + + currentProperties.put("java.class.path", classpath); + + try { + StringWriter out = new StringWriter(); + PrintWriter w = null; + int rc = + COM.ibm.netrexx.process.NetRexxC.main(new Rexx(compileArgs), + w = new PrintWriter(out)); + String sdir = srcDir.getAbsolutePath(); + String ddir = destDir.getAbsolutePath(); + boolean doReplace = !(sdir.equals(ddir)); + int dlen = ddir.length(); + String l; + BufferedReader in = new BufferedReader(new StringReader(out.toString())); + + log("replacing destdir '" + ddir + "' through sourcedir '" + + sdir + "'", Project.MSG_VERBOSE); + while ((l = in.readLine()) != null) { + int idx; + + while (doReplace && ((idx = l.indexOf(ddir)) != -1)) { + // path is mentioned in the message + l = (new StringBuffer(l)).replace(idx, idx + dlen, sdir).toString(); + } + // verbose level logging for suppressed messages + if (suppressMethodArgumentNotUsed + && l.indexOf(MSG_METHOD_ARGUMENT_NOT_USED) != -1) { + log(l, Project.MSG_VERBOSE); + } else if (suppressPrivatePropertyNotUsed + && l.indexOf(MSG_PRIVATE_PROPERTY_NOT_USED) != -1) { + log(l, Project.MSG_VERBOSE); + } else if (suppressVariableNotUsed + && l.indexOf(MSG_VARIABLE_NOT_USED) != -1) { + log(l, Project.MSG_VERBOSE); + } else if (suppressExceptionNotSignalled + && l.indexOf(MSG_EXCEPTION_NOT_SIGNALLED) != -1) { + log(l, Project.MSG_VERBOSE); + } else if (suppressDeprecation + && l.indexOf(MSG_DEPRECATION) != -1) { + log(l, Project.MSG_VERBOSE); + } else if (l.indexOf("Error:") != -1) { + // error level logging for compiler errors + log(l, Project.MSG_ERR); + } else if (l.indexOf("Warning:") != -1) { + // warning for all warning messages + log(l, Project.MSG_WARN); + } else { + log(l, Project.MSG_INFO); // info level for the rest. + } + } + if (rc > 1) { + throw new BuildException("Compile failed, messages should " + + "have been provided."); + } + if (w.checkError()) { + throw new IOException("Encountered an error"); + } + } catch (IOException ioe) { + throw new BuildException("Unexpected IOException while " + + "playing with Strings", ioe); + } finally { + // need to reset java.class.path property + // since the NetRexx compiler has no option for the classpath + currentProperties = System.getProperties(); + currentProperties.put("java.class.path", currentClassPath); + } + + } + + + /** Builds the compilation classpath. */ + private String getCompileClasspath() { + StringBuffer classpath = new StringBuffer(); + + // add dest dir to classpath so that previously compiled and + // untouched classes are on classpath + classpath.append(destDir.getAbsolutePath()); + + // add our classpath to the mix + if (this.classpath != null) { + addExistingToClasspath(classpath, this.classpath); + } + + // add the system classpath + // addExistingToClasspath(classpath,System.getProperty("java.class.path")); + return classpath.toString(); + } + + + /** This */ + private String[] getCompileOptionsAsArray() { + Vector options = new Vector(); + + options.addElement(binary ? "-binary" : "-nobinary"); + options.addElement(comments ? "-comments" : "-nocomments"); + options.addElement(compile ? "-compile" : "-nocompile"); + options.addElement(compact ? "-compact" : "-nocompact"); + options.addElement(console ? "-console" : "-noconsole"); + options.addElement(crossref ? "-crossref" : "-nocrossref"); + options.addElement(decimal ? "-decimal" : "-nodecimal"); + options.addElement(diag ? "-diag" : "-nodiag"); + options.addElement(explicit ? "-explicit" : "-noexplicit"); + options.addElement(format ? "-format" : "-noformat"); + options.addElement(keep ? "-keep" : "-nokeep"); + options.addElement(logo ? "-logo" : "-nologo"); + options.addElement(replace ? "-replace" : "-noreplace"); + options.addElement(savelog ? "-savelog" : "-nosavelog"); + options.addElement(sourcedir ? "-sourcedir" : "-nosourcedir"); + options.addElement(strictargs ? "-strictargs" : "-nostrictargs"); + options.addElement(strictassign ? "-strictassign" : "-nostrictassign"); + options.addElement(strictcase ? "-strictcase" : "-nostrictcase"); + options.addElement(strictimport ? "-strictimport" : "-nostrictimport"); + options.addElement(strictprops ? "-strictprops" : "-nostrictprops"); + options.addElement(strictsignal ? "-strictsignal" : "-nostrictsignal"); + options.addElement(symbols ? "-symbols" : "-nosymbols"); + options.addElement(time ? "-time" : "-notime"); + options.addElement("-" + trace); + options.addElement(utf8 ? "-utf8" : "-noutf8"); + options.addElement("-" + verbose); + + String[] results = new String[options.size()]; + + options.copyInto(results); + return results; + } + + + /** + * Takes a classpath-like string, and adds each element of this string to + * a new classpath, if the components exist. Components that don't exist, + * aren't added. We do this, because jikes issues warnings for + * non-existent files/dirs in his classpath, and these warnings are pretty + * annoying. + * + * @param target - target classpath + * @param source - source classpath to get file objects. + */ + private void addExistingToClasspath(StringBuffer target, String source) { + StringTokenizer tok = new StringTokenizer(source, + System.getProperty("path.separator"), false); + + while (tok.hasMoreTokens()) { + File f = getProject().resolveFile(tok.nextToken()); + + if (f.exists()) { + target.append(File.pathSeparator); + target.append(f.getAbsolutePath()); + } else { + log("Dropping from classpath: " + + f.getAbsolutePath(), Project.MSG_VERBOSE); + } + } + + } + + + /** + * Enumerated class corresponding to the trace attribute. + */ + public static class TraceAttr extends EnumeratedAttribute { + /** {@inheritDoc}. */ + public String[] getValues() { + return new String[]{"trace", "trace1", "trace2", "notrace"}; + } + } + + /** + * Enumerated class corresponding to the verbose attribute. + */ + public static class VerboseAttr extends EnumeratedAttribute { + /** {@inheritDoc}. */ + public String[] getValues() { + return new String[]{"verbose", "verbose0", "verbose1", + "verbose2", "verbose3", "verbose4", + "verbose5", "noverbose"}; + } + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/PropertyFile.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/PropertyFile.java new file mode 100644 index 00000000..162cab1a --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/PropertyFile.java @@ -0,0 +1,726 @@ +/* + * 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.tools.ant.taskdefs.optional; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.Vector; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.EnumeratedAttribute; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.LayoutPreservingProperties; + +/** + * Modifies settings in a property file. + * + * <p>The following is an example of its usage:</p> + * <pre> + * <target name="setState"> + * <property + * name="header" + * value="##Generated file - do not modify!"/> + * <propertyfile file="apropfile.properties" comment="${header}"> + * <entry key="product.version.major" type="int" value="5"/> + * <entry key="product.version.minor" type="int" value="0"/> + * <entry key="product.build.major" type="int" value="0" /> + * <entry key="product.build.minor" type="int" operation="+" /> + * <entry key="product.build.date" type="date" value="now" /> + * <entry key="intSet" type="int" operation="=" value="681"/> + * <entry key="intDec" type="int" operation="-"/> + * <entry key="StringEquals" type="string" value="testValue"/> + * </propertyfile> + * </target> + * </pre> + * + * The <propertyfile> task must have: + * <ul> + * <li>file</li> + * </ul> + * Other parameters are: + * <ul> + * <li>comment</li> + * <li>key</li> + * <li>operation</li> + * <li>type</li> + * <li>value (the final four being eliminated shortly)</li> + * </ul> + * + * The <entry> task must have: + * <ul> + * <li>key</li> + * </ul> + * Other parameters are: + * <ul> + * <li>operation</li> + * <li>type</li> + * <li>value</li> + * <li>default</li> + * <li>unit</li> + * </ul> + * + * If type is unspecified, it defaults to string. + * + * Parameter values: + * <ul> + * <li>operation:</li> + * <ul> + * <li>"=" (set -- default)</li> + * <li>"-" (dec)</li> + * <li>"+" (inc)</li> + * </ul> + * <li>type:</li> + * <ul> + * <li>"int"</li> + * <li>"date"</li> + * <li>"string"</li> + * </ul> + * <li>value:</li> + * <ul> + * <li>holds the default value, if the property + * was not found in property file</li> + * <li>"now" In case of type "date", the + * value "now" will be replaced by the current + * date/time and used even if a valid date was + * found in the property file.</li> + * </ul> + * </ul> + * + * <p>String property types can only use the "=" operation. + * Int property types can only use the "=", "-" or "+" operations.<p> + * + * The message property is used for the property file header, with "\\" being + * a newline delimiter character. + * + */ +public class PropertyFile extends Task { + + /* ======================================================================== + * + * Instance variables. + */ + + // Use this to prepend a message to the properties file + private String comment; + + private Properties properties; + private File propertyfile; + private boolean useJDKProperties; + + private Vector entries = new Vector(); + + /* ======================================================================== + * + * Constructors + */ + + /* ======================================================================== + * + * Methods + */ + + /** + * Execute the task. + * @throws BuildException on error. + */ + @Override + public void execute() throws BuildException { + checkParameters(); + readFile(); + executeOperation(); + writeFile(); + } + + /** + * The entry nested element. + * @return an entry nested element to be configured. + */ + public Entry createEntry() { + Entry e = new Entry(); + entries.addElement(e); + return e; + } + + private void executeOperation() throws BuildException { + for (Enumeration e = entries.elements(); e.hasMoreElements();) { + Entry entry = (Entry) e.nextElement(); + entry.executeOn(properties); + } + } + + private void readFile() throws BuildException { + if (useJDKProperties) { + // user chose to use standard Java properties, which loose + // comments and layout + properties = new Properties(); + } else { + properties = new LayoutPreservingProperties(); + } + try { + if (propertyfile.exists()) { + log("Updating property file: " + + propertyfile.getAbsolutePath()); + FileInputStream fis = null; + try { + fis = new FileInputStream(propertyfile); + BufferedInputStream bis = new BufferedInputStream(fis); + properties.load(bis); + } finally { + if (fis != null) { + fis.close(); + } + } + } else { + log("Creating new property file: " + + propertyfile.getAbsolutePath()); + FileOutputStream out = null; + try { + out = new FileOutputStream(propertyfile.getAbsolutePath()); + out.flush(); + } finally { + if (out != null) { + out.close(); + } + } + } + } catch (IOException ioe) { + throw new BuildException(ioe.toString()); + } + } + + private void checkParameters() throws BuildException { + if (!checkParam(propertyfile)) { + throw new BuildException("file token must not be null.", + getLocation()); + } + } + + /** + * Location of the property file to be edited; required. + * @param file the property file. + */ + public void setFile(File file) { + propertyfile = file; + } + + /** + * optional header comment for the file + * @param hdr the string to use for the comment. + */ + public void setComment(String hdr) { + comment = hdr; + } + + /** + * optional flag to use original Java properties (as opposed to + * layout preserving properties) + */ + public void setJDKProperties(boolean val) { + useJDKProperties = val; + } + + private void writeFile() throws BuildException { + // Write to RAM first, as an OOME could otherwise produce a truncated file: + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + properties.store(baos, comment); + } catch (IOException x) { // should not happen + throw new BuildException(x, getLocation()); + } + try { + OutputStream os = new FileOutputStream(propertyfile); + try { + try { + os.write(baos.toByteArray()); + } finally { + os.close(); + } + } catch (IOException x) { // possibly corrupt + FileUtils.getFileUtils().tryHardToDelete(propertyfile); + throw x; + } + } catch (IOException x) { // opening, writing, or closing + throw new BuildException(x, getLocation()); + } + } + + private boolean checkParam(File param) { + return !(param == null); + } + + /** + * Instance of this class represents nested elements of + * a task propertyfile. + */ + public static class Entry { + private static final int DEFAULT_INT_VALUE = 0; + private static final String DEFAULT_DATE_VALUE = "now"; + private static final String DEFAULT_STRING_VALUE = ""; + + private String key = null; + private int type = Type.STRING_TYPE; + private int operation = Operation.EQUALS_OPER; + private String value = null; + private String defaultValue = null; + private String newValue = null; + private String pattern = null; + private int field = Calendar.DATE; + + /** + * Name of the property name/value pair + * @param value the key. + */ + public void setKey(String value) { + this.key = value; + } + + /** + * Value to set (=), to add (+) or subtract (-) + * @param value the value. + */ + public void setValue(String value) { + this.value = value; + } + + /** + * operation to apply. + * "+" or "=" + *(default) for all datatypes; "-" for date and int only)\. + * @param value the operation enumerated value. + */ + public void setOperation(Operation value) { + this.operation = Operation.toOperation(value.getValue()); + } + + /** + * Regard the value as : int, date or string (default) + * @param value the type enumerated value. + */ + public void setType(Type value) { + this.type = Type.toType(value.getValue()); + } + + /** + * Initial value to set for a property if it is not + * already defined in the property file. + * For type date, an additional keyword is allowed: "now" + * @param value the default value. + */ + public void setDefault(String value) { + this.defaultValue = value; + } + + /** + * For int and date type only. If present, Values will + * be parsed and formatted accordingly. + * @param value the pattern to use. + */ + public void setPattern(String value) { + this.pattern = value; + } + + /** + * The unit of the value to be applied to date +/- operations. + * Valid Values are: + * <ul> + * <li>millisecond</li> + * <li>second</li> + * <li>minute</li> + * <li>hour</li> + * <li>day (default)</li> + * <li>week</li> + * <li>month</li> + * <li>year</li> + * </ul> + * This only applies to date types using a +/- operation. + * @param unit the unit enumerated value. + * @since Ant 1.5 + */ + public void setUnit(PropertyFile.Unit unit) { + field = unit.getCalendarField(); + } + + /** + * Apply the nested element to the properties. + * @param props the properties to apply the entry on. + * @throws BuildException if there is an error. + */ + protected void executeOn(Properties props) throws BuildException { + checkParameters(); + + if (operation == Operation.DELETE_OPER) { + props.remove(key); + return; + } + + // type may be null because it wasn't set + String oldValue = (String) props.get(key); + try { + if (type == Type.INTEGER_TYPE) { + executeInteger(oldValue); + } else if (type == Type.DATE_TYPE) { + executeDate(oldValue); + } else if (type == Type.STRING_TYPE) { + executeString(oldValue); + } else { + throw new BuildException("Unknown operation type: " + + type); + } + } catch (NullPointerException npe) { + // Default to string type + // which means do nothing + npe.printStackTrace(); + } + + if (newValue == null) { + newValue = ""; + } + + // Insert as a string by default + props.put(key, newValue); + } + + /** + * Handle operations for type <code>date</code>. + * + * @param oldValue the current value read from the property file or + * <code>null</code> if the <code>key</code> was + * not contained in the property file. + */ + private void executeDate(String oldValue) throws BuildException { + Calendar currentValue = Calendar.getInstance(); + + if (pattern == null) { + pattern = "yyyy/MM/dd HH:mm"; + } + DateFormat fmt = new SimpleDateFormat(pattern); + + String currentStringValue = getCurrentValue(oldValue); + if (currentStringValue == null) { + currentStringValue = DEFAULT_DATE_VALUE; + } + + if ("now".equals(currentStringValue)) { + currentValue.setTime(new Date()); + } else { + try { + currentValue.setTime(fmt.parse(currentStringValue)); + } catch (ParseException pe) { + // swallow + } + } + + if (operation != Operation.EQUALS_OPER) { + int offset = 0; + try { + offset = Integer.parseInt(value); + if (operation == Operation.DECREMENT_OPER) { + offset = -1 * offset; + } + } catch (NumberFormatException e) { + throw new BuildException("Value not an integer on " + key); + } + currentValue.add(field, offset); + } + + newValue = fmt.format(currentValue.getTime()); + } + + + /** + * Handle operations for type <code>int</code>. + * + * @param oldValue the current value read from the property file or + * <code>null</code> if the <code>key</code> was + * not contained in the property file. + */ + private void executeInteger(String oldValue) throws BuildException { + int currentValue = DEFAULT_INT_VALUE; + int newV = DEFAULT_INT_VALUE; + + + DecimalFormat fmt = (pattern != null) ? new DecimalFormat(pattern) + : new DecimalFormat(); + try { + String curval = getCurrentValue(oldValue); + if (curval != null) { + currentValue = fmt.parse(curval).intValue(); + } else { + currentValue = 0; + } + } catch (NumberFormatException nfe) { + // swallow + } catch (ParseException pe) { + // swallow + } + + if (operation == Operation.EQUALS_OPER) { + newV = currentValue; + } else { + int operationValue = 1; + if (value != null) { + try { + operationValue = fmt.parse(value).intValue(); + } catch (NumberFormatException nfe) { + // swallow + } catch (ParseException pe) { + // swallow + } + } + + if (operation == Operation.INCREMENT_OPER) { + newV = currentValue + operationValue; + } else if (operation == Operation.DECREMENT_OPER) { + newV = currentValue - operationValue; + } + } + + this.newValue = fmt.format(newV); + } + + /** + * Handle operations for type <code>string</code>. + * + * @param oldValue the current value read from the property file or + * <code>null</code> if the <code>key</code> was + * not contained in the property file. + */ + private void executeString(String oldValue) throws BuildException { + String newV = DEFAULT_STRING_VALUE; + + String currentValue = getCurrentValue(oldValue); + + if (currentValue == null) { + currentValue = DEFAULT_STRING_VALUE; + } + + if (operation == Operation.EQUALS_OPER) { + newV = currentValue; + } else if (operation == Operation.INCREMENT_OPER) { + newV = currentValue + value; + } + this.newValue = newV; + } + + /** + * Check if parameter combinations can be supported + * @todo make sure the 'unit' attribute is only specified on date + * fields + */ + private void checkParameters() throws BuildException { + if (type == Type.STRING_TYPE + && operation == Operation.DECREMENT_OPER) { + throw new BuildException("- is not supported for string " + + "properties (key:" + key + ")"); + } + if (value == null && defaultValue == null && operation != Operation.DELETE_OPER) { + throw new BuildException("\"value\" and/or \"default\" " + + "attribute must be specified (key:" + key + ")"); + } + if (key == null) { + throw new BuildException("key is mandatory"); + } + if (type == Type.STRING_TYPE && pattern != null) { + throw new BuildException("pattern is not supported for string " + + "properties (key:" + key + ")"); + } + } + + private String getCurrentValue(String oldValue) { + String ret = null; + if (operation == Operation.EQUALS_OPER) { + // If only value is specified, the property is set to it + // regardless of its previous value. + if (value != null && defaultValue == null) { + ret = value; + } + + // If only default is specified and the property previously + // existed in the property file, it is unchanged. + if (value == null && defaultValue != null && oldValue != null) { + ret = oldValue; + } + + // If only default is specified and the property did not + // exist in the property file, the property is set to default. + if (value == null && defaultValue != null && oldValue == null) { + ret = defaultValue; + } + + // If value and default are both specified and the property + // previously existed in the property file, the property + // is set to value. + if (value != null && defaultValue != null && oldValue != null) { + ret = value; + } + + // If value and default are both specified and the property + // did not exist in the property file, the property is set + // to default. + if (value != null && defaultValue != null && oldValue == null) { + ret = defaultValue; + } + } else { + ret = (oldValue == null) ? defaultValue : oldValue; + } + + return ret; + } + + /** + * Enumerated attribute with the values "+", "-", "=" + */ + public static class Operation extends EnumeratedAttribute { + + // Property type operations + /** + */ + public static final int INCREMENT_OPER = 0; + /** - */ + public static final int DECREMENT_OPER = 1; + /** = */ + public static final int EQUALS_OPER = 2; + /** del */ + public static final int DELETE_OPER = 3; + + /** {@inheritDoc}. */ + @Override + public String[] getValues() { + return new String[] {"+", "-", "=", "del"}; + } + + /** + * Convert string to index. + * @param oper the string to convert. + * @return the index. + */ + public static int toOperation(String oper) { + if ("+".equals(oper)) { + return INCREMENT_OPER; + } else if ("-".equals(oper)) { + return DECREMENT_OPER; + } else if ("del".equals(oper)) { + return DELETE_OPER; + } + return EQUALS_OPER; + } + } + + /** + * Enumerated attribute with the values "int", "date" and "string". + */ + public static class Type extends EnumeratedAttribute { + + // Property types + /** int */ + public static final int INTEGER_TYPE = 0; + /** date */ + public static final int DATE_TYPE = 1; + /** string */ + public static final int STRING_TYPE = 2; + + /** {@inheritDoc} */ + @Override + public String[] getValues() { + return new String[] {"int", "date", "string"}; + } + + /** + * Convert string to index. + * @param type the string to convert. + * @return the index. + */ + public static int toType(String type) { + if ("int".equals(type)) { + return INTEGER_TYPE; + } else if ("date".equals(type)) { + return DATE_TYPE; + } + return STRING_TYPE; + } + } + } + + /** + * Borrowed from Tstamp + * @todo share all this time stuff across many tasks as a datetime datatype + * @since Ant 1.5 + */ + public static class Unit extends EnumeratedAttribute { + + private static final String MILLISECOND = "millisecond"; + private static final String SECOND = "second"; + private static final String MINUTE = "minute"; + private static final String HOUR = "hour"; + private static final String DAY = "day"; + private static final String WEEK = "week"; + private static final String MONTH = "month"; + private static final String YEAR = "year"; + + private static final String[] UNITS + = {MILLISECOND, SECOND, MINUTE, HOUR, + DAY, WEEK, MONTH, YEAR }; + + private Map calendarFields = new HashMap(); + + /** no arg constructor */ + public Unit() { + calendarFields.put(MILLISECOND, + new Integer(Calendar.MILLISECOND)); + calendarFields.put(SECOND, new Integer(Calendar.SECOND)); + calendarFields.put(MINUTE, new Integer(Calendar.MINUTE)); + calendarFields.put(HOUR, new Integer(Calendar.HOUR_OF_DAY)); + calendarFields.put(DAY, new Integer(Calendar.DATE)); + calendarFields.put(WEEK, new Integer(Calendar.WEEK_OF_YEAR)); + calendarFields.put(MONTH, new Integer(Calendar.MONTH)); + calendarFields.put(YEAR, new Integer(Calendar.YEAR)); + } + + /** + * Convert the value to a Calendar field index. + * @return the calendar value. + */ + public int getCalendarField() { + String key = getValue().toLowerCase(); + Integer i = (Integer) calendarFields.get(key); + return i.intValue(); + } + + /** {@inheritDoc}. */ + @Override + public String[] getValues() { + return UNITS; + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/RenameExtensions.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/RenameExtensions.java new file mode 100644 index 00000000..c4acadb2 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/RenameExtensions.java @@ -0,0 +1,146 @@ +/* + * 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. + * + */ +/* + * Task to rename files based on extension. This task has the following + * properties which can be set: + * <ul> + * <li>fromExtension: </li> + * <li>toExtension: </li> + * <li>srcDir: </li> + * <li>replace: </li> + * </ul> + */ + +package org.apache.tools.ant.taskdefs.optional; + +import java.io.File; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.MatchingTask; +import org.apache.tools.ant.taskdefs.Move; +import org.apache.tools.ant.types.Mapper; + +/** + * + * @version 1.2 + * + * @deprecated since 1.5.x. + * Use <move> instead + */ +public class RenameExtensions extends MatchingTask { + + private String fromExtension = ""; + private String toExtension = ""; + private boolean replace = false; + private File srcDir; + + private Mapper.MapperType globType; + + + /** Creates new RenameExtensions */ + public RenameExtensions() { + super(); + globType = new Mapper.MapperType(); + globType.setValue("glob"); + } + + /** + * The string that files must end in to be renamed + * + * @param from the extension of files being renamed. + */ + public void setFromExtension(String from) { + fromExtension = from; + } + + /** + * The string that renamed files will end with on + * completion + * + * @param to the extension of the renamed files. + */ + public void setToExtension(String to) { + toExtension = to; + } + + /** + * store replace attribute - this determines whether the target file + * should be overwritten if present + * + * @param replace if true overwrite any target files that exist. + */ + public void setReplace(boolean replace) { + this.replace = replace; + } + + /** + * Set the source dir to find the files to be renamed. + * + * @param srcDir the source directory. + */ + public void setSrcDir(File srcDir) { + this.srcDir = srcDir; + } + + /** + * Executes the task. + * + * @throws BuildException is there is a problem in the task execution. + */ + public void execute() throws BuildException { + + // first off, make sure that we've got a from and to extension + if (fromExtension == null || toExtension == null || srcDir == null) { + throw new BuildException("srcDir, fromExtension and toExtension " + + "attributes must be set!"); + } + + log("DEPRECATED - The renameext task is deprecated. Use move instead.", + Project.MSG_WARN); + log("Replace this with:", Project.MSG_INFO); + log("<move todir=\"" + srcDir + "\" overwrite=\"" + replace + "\">", + Project.MSG_INFO); + log(" <fileset dir=\"" + srcDir + "\" />", Project.MSG_INFO); + log(" <mapper type=\"glob\"", Project.MSG_INFO); + log(" from=\"*" + fromExtension + "\"", Project.MSG_INFO); + log(" to=\"*" + toExtension + "\" />", Project.MSG_INFO); + log("</move>", Project.MSG_INFO); + log("using the same patterns on <fileset> as you\'ve used here", + Project.MSG_INFO); + + Move move = new Move(); + move.bindToOwner(this); + move.setOwningTarget(getOwningTarget()); + move.setTaskName(getTaskName()); + move.setLocation(getLocation()); + move.setTodir(srcDir); + move.setOverwrite(replace); + + fileset.setDir(srcDir); + move.addFileset(fileset); + + Mapper me = move.createMapper(); + me.setType(globType); + me.setFrom("*" + fromExtension); + me.setTo("*" + toExtension); + + move.execute(); + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ReplaceRegExp.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ReplaceRegExp.java new file mode 100644 index 00000000..d088096a --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ReplaceRegExp.java @@ -0,0 +1,533 @@ +/* + * 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.tools.ant.taskdefs.optional; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.RegularExpression; +import org.apache.tools.ant.types.Resource; +import org.apache.tools.ant.types.ResourceCollection; +import org.apache.tools.ant.types.Substitution; +import org.apache.tools.ant.types.resources.FileProvider; +import org.apache.tools.ant.types.resources.Union; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.regexp.Regexp; +import org.apache.tools.ant.util.regexp.RegexpUtil; + +/** + * Performs regular expression string replacements in a text + * file. The input file(s) must be able to be properly processed by + * a Reader instance. That is, they must be text only, no binary. + * + * The syntax of the regular expression depends on the implementation that + * you choose to use. The system property <code>ant.regexp.regexpimpl</code> + * will be the classname of the implementation that will be used (the default + * is <code>org.apache.tools.ant.util.regexp.JakartaOroRegexp</code> and + * requires the Jakarta Oro Package). + * + * <pre> + * Available implementations: + * + * org.apache.tools.ant.util.regexp.Jdk14RegexpRegexp (default) + * Uses Java's built-in regular expression package + * + * org.apache.tools.ant.util.regexp.JakartaOroRegexp + * Requires the jakarta-oro package + * + * org.apache.tools.ant.util.regexp.JakartaRegexpRegexp + * Requires the jakarta-regexp package + * + * Usage: + * + * Call Syntax: + * + * <replaceregexp file="file" + * match="pattern" + * replace="pattern" + * flags="options"? + * byline="true|false"? > + * regexp? + * substitution? + * fileset* + * </replaceregexp> + * + * NOTE: You must have either the file attribute specified, or at least one fileset subelement + * to operation on. You may not have the file attribute specified if you nest fileset elements + * inside this task. Also, you cannot specify both match and a regular expression subelement at + * the same time, nor can you specify the replace attribute and the substitution subelement at + * the same time. + * + * Attributes: + * + * file --> A single file to operation on (mutually exclusive + * with the fileset subelements) + * match --> The Regular expression to match + * replace --> The Expression replacement string + * flags --> The options to give to the replacement + * g = Substitute all occurrences. default is to replace only the first one + * i = Case insensitive match + * + * byline --> Should this file be processed a single line at a time (default is false) + * "true" indicates to perform replacement on a line by line basis + * "false" indicates to perform replacement on the whole file at once. + * + * Example: + * + * The following call could be used to replace an old property name in a ".properties" + * file with a new name. In the replace attribute, you can refer to any part of the + * match expression in parenthesis using backslash followed by a number like '\1'. + * + * <replaceregexp file="test.properties" + * match="MyProperty=(.*)" + * replace="NewProperty=\1" + * byline="true" /> + * + * </pre> + * + */ +public class ReplaceRegExp extends Task { + + private File file; + private String flags; + private boolean byline; + private Union resources; + private RegularExpression regex; + private Substitution subs; + + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + private boolean preserveLastModified = false; + + /** + * Encoding to assume for the files + */ + private String encoding = null; + + /** Default Constructor */ + public ReplaceRegExp() { + super(); + this.file = null; + this.flags = ""; + this.byline = false; + + this.regex = null; + this.subs = null; + } + + + /** + * file for which the regular expression should be replaced; + * required unless a nested fileset is supplied. + * @param file The file for which the reg exp should be replaced. + */ + public void setFile(File file) { + this.file = file; + } + + + /** + * the regular expression pattern to match in the file(s); + * required if no nested <regexp> is used + * @param match the match attribute. + */ + public void setMatch(String match) { + if (regex != null) { + throw new BuildException("Only one regular expression is allowed"); + } + + regex = new RegularExpression(); + regex.setPattern(match); + } + + + /** + * The substitution pattern to place in the file(s) in place + * of the regular expression. + * Required if no nested <substitution> is used + * @param replace the replace attribute + */ + + public void setReplace(String replace) { + if (subs != null) { + throw new BuildException("Only one substitution expression is " + + "allowed"); + } + + subs = new Substitution(); + subs.setExpression(replace); + } + + /** + * The flags to use when matching the regular expression. For more + * information, consult the Perl5 syntax. + * <ul> + * <li>g : Global replacement. Replace all occurrences found + * <li>i : Case Insensitive. Do not consider case in the match + * <li>m : Multiline. Treat the string as multiple lines of input, + * using "^" and "$" as the start or end of any line, respectively, + * rather than start or end of string. + * <li> s : Singleline. Treat the string as a single line of input, using + * "." to match any character, including a newline, which normally, + * it would not match. + *</ul> + * @param flags the flags attribute + */ + public void setFlags(String flags) { + this.flags = flags; + } + + + /** + * Process the file(s) one line at a time, executing the replacement + * on one line at a time. This is useful if you + * want to only replace the first occurrence of a regular expression on + * each line, which is not easy to do when processing the file as a whole. + * Defaults to <i>false</i>. + * + * @param byline the byline attribute as a string + * @deprecated since 1.6.x. + * Use setByLine(boolean). + */ + @Deprecated + public void setByLine(String byline) { + Boolean res = Boolean.valueOf(byline); + + if (res == null) { + res = Boolean.FALSE; + } + this.byline = res.booleanValue(); + } + + /** + * Process the file(s) one line at a time, executing the replacement + * on one line at a time. This is useful if you + * want to only replace the first occurrence of a regular expression on + * each line, which is not easy to do when processing the file as a whole. + * Defaults to <i>false</i>. + * + * @param byline the byline attribute + */ + public void setByLine(boolean byline) { + this.byline = byline; + } + + /** + * Specifies the encoding Ant expects the files to be in - + * defaults to the platforms default encoding. + * @param encoding the encoding attribute + * + * @since Ant 1.6 + */ + public void setEncoding(String encoding) { + this.encoding = encoding; + } + + /** + * list files to apply the replacement to + * @param set the fileset element + */ + public void addFileset(FileSet set) { + addConfigured(set); + } + + /** + * Support arbitrary file system based resource collections. + * + * @since Ant 1.8.0 + */ + public void addConfigured(ResourceCollection rc) { + if (!rc.isFilesystemOnly()) { + throw new BuildException("only filesystem resources are supported"); + } + if (resources == null) { + resources = new Union(); + } + resources.add(rc); + } + + /** + * A regular expression. + * You can use this element to refer to a previously + * defined regular expression datatype instance + * @return the regular expression object to be configured as an element + */ + public RegularExpression createRegexp() { + if (regex != null) { + throw new BuildException("Only one regular expression is allowed."); + } + + regex = new RegularExpression(); + return regex; + } + + + /** + * A substitution pattern. You can use this element to refer to a previously + * defined substitution pattern datatype instance. + * @return the substitution pattern object to be configured as an element + */ + public Substitution createSubstitution() { + if (subs != null) { + throw new BuildException("Only one substitution expression is " + + "allowed"); + } + + subs = new Substitution(); + return subs; + } + + /** + * Whether the file timestamp shall be preserved even if the file + * is modified. + * + * @since Ant 1.8.0 + */ + public void setPreserveLastModified(boolean b) { + preserveLastModified = b; + } + + /** + * Invoke a regular expression (r) on a string (input) using + * substitutions (s) for a matching regex. + * + * @param r a regular expression + * @param s a Substitution + * @param input the string to do the replacement on + * @param options The options for the regular expression + * @return the replacement result + */ + protected String doReplace(RegularExpression r, + Substitution s, + String input, + int options) { + String res = input; + Regexp regexp = r.getRegexp(getProject()); + + if (regexp.matches(input, options)) { + log("Found match; substituting", Project.MSG_DEBUG); + res = regexp.substitute(input, s.getExpression(getProject()), + options); + } + + return res; + } + + + /** + * Perform the replacement on a file + * + * @param f the file to perform the replacement on + * @param options the regular expressions options + * @exception IOException if an error occurs + */ + protected void doReplace(File f, int options) + throws IOException { + File temp = FILE_UTILS.createTempFile("replace", ".txt", null, true, true); + try { + boolean changes = false; + + InputStream is = new FileInputStream(f); + try { + Reader r = encoding != null ? new InputStreamReader(is, encoding) : new InputStreamReader(is); + OutputStream os = new FileOutputStream(temp); + try { + Writer w = encoding != null ? new OutputStreamWriter(os, encoding) : new OutputStreamWriter(os); + + log("Replacing pattern '" + regex.getPattern(getProject()) + + "' with '" + subs.getExpression(getProject()) + + "' in '" + f.getPath() + "'" + (byline ? " by line" : "") + + (flags.length() > 0 ? " with flags: '" + flags + "'" : "") + + ".", Project.MSG_VERBOSE); + + if (byline) { + r = new BufferedReader(r); + w = new BufferedWriter(w); + + StringBuffer linebuf = new StringBuffer(); + int c; + boolean hasCR = false; + + do { + c = r.read(); + + if (c == '\r') { + if (hasCR) { + // second CR -> EOL + possibly empty line + changes |= replaceAndWrite(linebuf.toString(), + w, options); + w.write('\r'); + + linebuf = new StringBuffer(); + // hasCR is still true (for the second one) + } else { + // first CR in this line + hasCR = true; + } + } else if (c == '\n') { + // LF -> EOL + changes |= replaceAndWrite(linebuf.toString(), + w, options); + if (hasCR) { + w.write('\r'); + hasCR = false; + } + w.write('\n'); + + linebuf = new StringBuffer(); + } else { // any other char + if ((hasCR) || (c < 0)) { + // Mac-style linebreak or EOF (or both) + changes |= replaceAndWrite(linebuf.toString(), + w, options); + if (hasCR) { + w.write('\r'); + hasCR = false; + } + + linebuf = new StringBuffer(); + } + + if (c >= 0) { + linebuf.append((char) c); + } + } + } while (c >= 0); + + } else { + changes = multilineReplace(r, w, options); + } + + r.close(); + w.close(); + + } finally { + os.close(); + } + } finally { + is.close(); + } + if (changes) { + log("File has changed; saving the updated file", Project.MSG_VERBOSE); + try { + long origLastModified = f.lastModified(); + FILE_UTILS.rename(temp, f); + if (preserveLastModified) { + FILE_UTILS.setFileLastModified(f, origLastModified); + } + temp = null; + } catch (IOException e) { + throw new BuildException("Couldn't rename temporary file " + + temp, e, getLocation()); + } + } else { + log("No change made", Project.MSG_DEBUG); + } + } finally { + if (temp != null) { + temp.delete(); + } + } + } + + + /** + * Execute the task + * + * @throws BuildException is there is a problem in the task execution. + */ + @Override + public void execute() throws BuildException { + if (regex == null) { + throw new BuildException("No expression to match."); + } + if (subs == null) { + throw new BuildException("Nothing to replace expression with."); + } + + if (file != null && resources != null) { + throw new BuildException("You cannot supply the 'file' attribute " + + "and resource collections at the same " + + "time."); + } + + int options = RegexpUtil.asOptions(flags); + + if (file != null && file.exists()) { + try { + doReplace(file, options); + } catch (IOException e) { + log("An error occurred processing file: '" + + file.getAbsolutePath() + "': " + e.toString(), + Project.MSG_ERR); + } + } else if (file != null) { + log("The following file is missing: '" + + file.getAbsolutePath() + "'", Project.MSG_ERR); + } + + if (resources != null) { + for (Resource r : resources) { + FileProvider fp = + r.as(FileProvider.class); + File f = fp.getFile(); + + if (f.exists()) { + try { + doReplace(f, options); + } catch (Exception e) { + log("An error occurred processing file: '" + + f.getAbsolutePath() + "': " + e.toString(), + Project.MSG_ERR); + } + } else { + log("The following file is missing: '" + + f.getAbsolutePath() + "'", Project.MSG_ERR); + } + } + } + } + + private boolean multilineReplace(Reader r, Writer w, int options) + throws IOException { + return replaceAndWrite(FileUtils.safeReadFully(r), w, options); + } + + private boolean replaceAndWrite(String s, Writer w, int options) + throws IOException { + String res = doReplace(regex, subs, s, options); + w.write(res); + return !res.equals(s); + } +} + + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Rpm.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Rpm.java new file mode 100644 index 00000000..b395a16c --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Rpm.java @@ -0,0 +1,364 @@ +/* + * 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.tools.ant.taskdefs.optional; + +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.Map; + +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.LogStreamHandler; +import org.apache.tools.ant.taskdefs.PumpStreamHandler; +import org.apache.tools.ant.taskdefs.condition.Os; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.FileUtils; + +/** + * Invokes the rpm tool to build a Linux installation file. + * + */ +public class Rpm extends Task { + + private static final String PATH1 = "PATH"; + private static final String PATH2 = "Path"; + private static final String PATH3 = "path"; + + /** + * the spec file + */ + private String specFile; + + /** + * the rpm top dir + */ + private File topDir; + + /** + * the rpm command to use + */ + private String command = "-bb"; + + /** + * The executable to use for building the packages. + * @since Ant 1.6 + */ + private String rpmBuildCommand = null; + + /** + * clean BUILD directory + */ + private boolean cleanBuildDir = false; + + /** + * remove spec file + */ + private boolean removeSpec = false; + + /** + * remove sources + */ + private boolean removeSource = 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; + + /** + * Halt on error return value from rpm build. + */ + private boolean failOnError = false; + + /** + * Don't show output of RPM build command on console. This does not affect + * the printing of output and error messages to files. + */ + private boolean quiet = false; + + /** + * Execute the task + * + * @throws BuildException is there is a problem in the task execution. + */ + public void execute() throws BuildException { + + Commandline toExecute = new Commandline(); + + toExecute.setExecutable(rpmBuildCommand == null + ? guessRpmBuildCommand() + : rpmBuildCommand); + if (topDir != null) { + toExecute.createArgument().setValue("--define"); + toExecute.createArgument().setValue("_topdir " + topDir); + } + + toExecute.createArgument().setLine(command); + + if (cleanBuildDir) { + toExecute.createArgument().setValue("--clean"); + } + if (removeSpec) { + toExecute.createArgument().setValue("--rmspec"); + } + if (removeSource) { + toExecute.createArgument().setValue("--rmsource"); + } + + toExecute.createArgument().setValue("SPECS/" + specFile); + + ExecuteStreamHandler streamhandler = null; + OutputStream outputstream = null; + OutputStream errorstream = null; + if (error == null && output == null) { + if (!quiet) { + streamhandler = new LogStreamHandler(this, Project.MSG_INFO, + Project.MSG_WARN); + } else { + streamhandler = new LogStreamHandler(this, Project.MSG_DEBUG, + Project.MSG_DEBUG); + } + } else { + if (output != null) { + try { + BufferedOutputStream bos + = new BufferedOutputStream(new FileOutputStream(output)); + outputstream = new PrintStream(bos); + } catch (IOException e) { + throw new BuildException(e, getLocation()); + } + } else if (!quiet) { + outputstream = new LogOutputStream(this, Project.MSG_INFO); + } else { + outputstream = new LogOutputStream(this, Project.MSG_DEBUG); + } + if (error != null) { + try { + BufferedOutputStream bos + = new BufferedOutputStream(new FileOutputStream(error)); + errorstream = new PrintStream(bos); + } catch (IOException e) { + throw new BuildException(e, getLocation()); + } + } else if (!quiet) { + errorstream = new LogOutputStream(this, Project.MSG_WARN); + } else { + errorstream = new LogOutputStream(this, Project.MSG_DEBUG); + } + streamhandler = new PumpStreamHandler(outputstream, errorstream); + } + + Execute exe = getExecute(toExecute, streamhandler); + try { + log("Building the RPM based on the " + specFile + " file"); + int returncode = exe.execute(); + if (Execute.isFailure(returncode)) { + String msg = "'" + toExecute.getExecutable() + + "' failed with exit code " + returncode; + if (failOnError) { + throw new BuildException(msg); + } + log(msg, Project.MSG_ERR); + } + } catch (IOException e) { + throw new BuildException(e, getLocation()); + } finally { + FileUtils.close(outputstream); + FileUtils.close(errorstream); + } + } + + /** + * The directory which will have the expected + * subdirectories, SPECS, SOURCES, BUILD, SRPMS ; optional. + * If this isn't specified, + * the <tt>baseDir</tt> value is used + * + * @param td the directory containing the normal RPM directories. + */ + public void setTopDir(File td) { + this.topDir = td; + } + + /** + * What command to issue to the rpm build tool; optional. + * The default is "-bb" + * @param c the command to use. + */ + public void setCommand(String c) { + this.command = c; + } + + /** + * The name of the spec File to use; required. + * @param sf the spec file name to use. + */ + public void setSpecFile(String sf) { + if ((sf == null) || (sf.trim().length() == 0)) { + throw new BuildException("You must specify a spec file", getLocation()); + } + this.specFile = sf; + } + + /** + * Flag (optional, default=false) to remove + * the generated files in the BUILD directory + * @param cbd a <code>boolean</code> value. + */ + public void setCleanBuildDir(boolean cbd) { + cleanBuildDir = cbd; + } + + /** + * Flag (optional, default=false) to remove the spec file from SPECS + * @param rs a <code>boolean</code> value. + */ + public void setRemoveSpec(boolean rs) { + removeSpec = rs; + } + + /** + * Flag (optional, default=false) + * to remove the sources after the build. + * See the <tt>--rmsource</tt> option of rpmbuild. + * @param rs a <code>boolean</code> value. + */ + public void setRemoveSource(boolean rs) { + removeSource = rs; + } + + /** + * Optional file to save stdout to. + * @param output the file to save stdout to. + */ + public void setOutput(File output) { + this.output = output; + } + + /** + * Optional file to save stderr to + * @param error the file to save error output to. + */ + public void setError(File error) { + this.error = error; + } + + /** + * The executable to run when building; optional. + * The default is <code>rpmbuild</code>. + * + * @since Ant 1.6 + * @param c the rpm build executable + */ + public void setRpmBuildCommand(String c) { + this.rpmBuildCommand = c; + } + + /** + * If <code>true</code>, stop the build process when the rpmbuild command + * exits with an error status. + * @param value <code>true</code> if it should halt, otherwise + * <code>false</code>. The default is <code>false</code>. + * + * @since Ant 1.6.3 + */ + public void setFailOnError(boolean value) { + failOnError = value; + } + + /** + * If true, output from the RPM build command will only be logged to DEBUG. + * @param value <code>false</code> if output should be logged, otherwise + * <code>true</code>. The default is <code>false</code>. + * + * @since Ant 1.6.3 + */ + public void setQuiet(boolean value) { + quiet = value; + } + + /** + * Checks whether <code>rpmbuild</code> is on the PATH and returns + * the absolute path to it - falls back to <code>rpm</code> + * otherwise. + * + * @return the command used to build RPM's + * + * @since 1.6 + */ + protected String guessRpmBuildCommand() { + Map/*<String, String>*/ env = Execute.getEnvironmentVariables(); + String path = (String) env.get(PATH1); + if (path == null) { + path = (String) env.get(PATH2); + if (path == null) { + path = (String) env.get(PATH3); + } + } + + if (path != null) { + Path p = new Path(getProject(), path); + String[] pElements = p.list(); + for (int i = 0; i < pElements.length; i++) { + File f = new File(pElements[i], + "rpmbuild" + + (Os.isFamily("dos") ? ".exe" : "")); + if (f.canRead()) { + return f.getAbsolutePath(); + } + } + } + + return "rpm"; + } + + /** + * Get the execute object. + * @param toExecute the command line to use. + * @param streamhandler the stream handler to use. + * @return the execute object. + * @since Ant 1.6.3 + */ + protected Execute getExecute(Commandline toExecute, + ExecuteStreamHandler streamhandler) { + Execute exe = new Execute(streamhandler, null); + + exe.setAntRun(getProject()); + if (topDir == null) { + topDir = getProject().getBaseDir(); + } + exe.setWorkingDirectory(topDir); + + exe.setCommandline(toExecute.getCommandline()); + return exe; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/SchemaValidate.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/SchemaValidate.java new file mode 100644 index 00000000..e57d6d22 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/SchemaValidate.java @@ -0,0 +1,529 @@ +/* + * 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.tools.ant.taskdefs.optional; + +import java.io.File; +import java.net.MalformedURLException; +import java.util.HashMap; +import java.util.Iterator; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.XmlConstants; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.XMLReader; + +/** + * Validate XML Schema documents. + * This task validates XML schema documents. It requires an XML parser + * that handles the relevant SAX, Xerces or JAXP options. + * + * To resolve remote referencies, Ant may need its proxy set up, using the + * setproxy task. + * + * Hands off most of the work to its parent, {@link XMLValidateTask} + * @since Ant1.7 + */ + +public class SchemaValidate extends XMLValidateTask { + + /** map of all declared schemas; we catch and complain about redefinitions */ + private HashMap schemaLocations = new HashMap(); + + /** full checking of a schema */ + private boolean fullChecking = true; + + /** + * flag to disable DTD support. Best left enabled. + */ + private boolean disableDTD = false; + + /** + * default URL for nonamespace schemas + */ + private SchemaLocation anonymousSchema; + + // Error strings + /** SAX1 not supported */ + public static final String ERROR_SAX_1 = "SAX1 parsers are not supported"; + + /** schema features not supported */ + public static final String ERROR_NO_XSD_SUPPORT + = "Parser does not support Xerces or JAXP schema features"; + + /** too many default schemas */ + public static final String ERROR_TOO_MANY_DEFAULT_SCHEMAS + = "Only one of defaultSchemaFile and defaultSchemaURL allowed"; + + /** unable to create parser */ + public static final String ERROR_PARSER_CREATION_FAILURE + = "Could not create parser"; + + /** adding schema */ + public static final String MESSAGE_ADDING_SCHEMA = "Adding schema "; + + /** Duplicate declaration of schema */ + public static final String ERROR_DUPLICATE_SCHEMA + = "Duplicate declaration of schema "; + + /** + * Called by the project to let the task initialize properly. The default + * implementation is a no-op. + * + * @throws BuildException if something goes wrong with the build + */ + public void init() throws BuildException { + super.init(); + //validating + setLenient(false); + } + + /** + * Turn on XSD support in Xerces. + * @return true on success, false on failure + */ + public boolean enableXercesSchemaValidation() { + try { + setFeature(XmlConstants.FEATURE_XSD, true); + //set the schema source for the doc + setNoNamespaceSchemaProperty(XmlConstants.PROPERTY_NO_NAMESPACE_SCHEMA_LOCATION); + } catch (BuildException e) { + log(e.toString(), Project.MSG_VERBOSE); + return false; + } + return true; + } + + /** + * set nonamespace handling up for xerces or other parsers + * @param property name of the property to set + */ + private void setNoNamespaceSchemaProperty(String property) { + String anonSchema = getNoNamespaceSchemaURL(); + if (anonSchema != null) { + setProperty(property, anonSchema); + } + } + + /** + * Set schema attributes in a JAXP 1.2 engine. + * @see <A href="http://java.sun.com/xml/jaxp/change-requests-11.html"> + * JAXP 1.2 Approved CHANGES</A> + * @return true on success, false on failure + */ + public boolean enableJAXP12SchemaValidation() { + try { + //enable XSD + setProperty(XmlConstants.FEATURE_JAXP12_SCHEMA_LANGUAGE, XmlConstants.URI_XSD); + //set the schema source for the doc + setNoNamespaceSchemaProperty(XmlConstants.FEATURE_JAXP12_SCHEMA_SOURCE); + } catch (BuildException e) { + log(e.toString(), Project.MSG_VERBOSE); + return false; + } + return true; + } + + /** + * add the schema + * @param location the schema location. + * @throws BuildException if there is no namespace, or if there already + * is a declaration of this schema with a different value + */ + public void addConfiguredSchema(SchemaLocation location) { + log("adding schema " + location, Project.MSG_DEBUG); + location.validateNamespace(); + SchemaLocation old = (SchemaLocation) schemaLocations.get(location.getNamespace()); + if (old != null && !old.equals(location)) { + throw new BuildException(ERROR_DUPLICATE_SCHEMA + location); + } + schemaLocations.put(location.getNamespace(), location); + } + + /** + * enable full schema checking. Slower but better. + * @param fullChecking a <code>boolean</code> value. + */ + public void setFullChecking(boolean fullChecking) { + this.fullChecking = fullChecking; + } + + /** + * create a schema location to hold the anonymous + * schema + */ + protected void createAnonymousSchema() { + if (anonymousSchema == null) { + anonymousSchema = new SchemaLocation(); + } + anonymousSchema.setNamespace("(no namespace)"); + } + + /** + * identify the URL of the default schema + * @param defaultSchemaURL the URL of the default schema. + */ + public void setNoNamespaceURL(String defaultSchemaURL) { + createAnonymousSchema(); + this.anonymousSchema.setUrl(defaultSchemaURL); + } + + /** + * identify a file containing the default schema + * @param defaultSchemaFile the location of the default schema. + */ + public void setNoNamespaceFile(File defaultSchemaFile) { + createAnonymousSchema(); + this.anonymousSchema.setFile(defaultSchemaFile); + } + + /** + * flag to disable DTD support. + * @param disableDTD a <code>boolean</code> value. + */ + public void setDisableDTD(boolean disableDTD) { + this.disableDTD = disableDTD; + } + + /** + * init the parser : load the parser class, and set features if necessary It + * is only after this that the reader is valid + * + * @throws BuildException if something went wrong + */ + protected void initValidator() { + super.initValidator(); + //validate the parser type + if (isSax1Parser()) { + throw new BuildException(ERROR_SAX_1); + } + + //enable schema + //setFeature(XmlConstants.FEATURE_VALIDATION, false); + setFeature(XmlConstants.FEATURE_NAMESPACES, true); + if (!enableXercesSchemaValidation() && !enableJAXP12SchemaValidation()) { + //couldnt use the xerces or jaxp calls + throw new BuildException(ERROR_NO_XSD_SUPPORT); + } + + //enable schema checking + setFeature(XmlConstants.FEATURE_XSD_FULL_VALIDATION, fullChecking); + + //turn off DTDs if desired + setFeatureIfSupported(XmlConstants.FEATURE_DISALLOW_DTD, disableDTD); + + //schema declarations go in next + addSchemaLocations(); + } + + /** + * Create a reader if the use of the class did not specify another one. + * The reason to not use {@link org.apache.tools.ant.util.JAXPUtils#getXMLReader()} was to + * create our own factory with our own options. + * @return a default XML parser + */ + protected XMLReader createDefaultReader() { + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setValidating(true); + factory.setNamespaceAware(true); + XMLReader reader = null; + try { + SAXParser saxParser = factory.newSAXParser(); + reader = saxParser.getXMLReader(); + } catch (ParserConfigurationException e) { + throw new BuildException(ERROR_PARSER_CREATION_FAILURE, e); + } catch (SAXException e) { + throw new BuildException(ERROR_PARSER_CREATION_FAILURE, e); + } + return reader; + } + + /** + * build a string list of all schema locations, then set the relevant + * property. + */ + protected void addSchemaLocations() { + Iterator it = schemaLocations.values().iterator(); + StringBuffer buffer = new StringBuffer(); + int count = 0; + while (it.hasNext()) { + if (count > 0) { + buffer.append(' '); + } + SchemaLocation schemaLocation = (SchemaLocation) it.next(); + String tuple = schemaLocation.getURIandLocation(); + buffer.append(tuple); + log("Adding schema " + tuple, Project.MSG_VERBOSE); + count++; + } + if (count > 0) { + setProperty(XmlConstants.PROPERTY_SCHEMA_LOCATION, buffer.toString()); + } + + } + + /** + * get the URL of the no namespace schema + * @return the schema URL + */ + protected String getNoNamespaceSchemaURL() { + if (anonymousSchema == null) { + return null; + } else { + return anonymousSchema.getSchemaLocationURL(); + } + } + + /** + * set a feature if it is supported, log at verbose level if + * not + * @param feature the feature. + * @param value a <code>boolean</code> value. + */ + protected void setFeatureIfSupported(String feature, boolean value) { + try { + getXmlReader().setFeature(feature, value); + } catch (SAXNotRecognizedException e) { + log("Not recognizied: " + feature, Project.MSG_VERBOSE); + } catch (SAXNotSupportedException e) { + log("Not supported: " + feature, Project.MSG_VERBOSE); + } + } + + /** + * handler called on successful file validation. + * + * @param fileProcessed number of files processed. + */ + protected void onSuccessfulValidation(int fileProcessed) { + log(fileProcessed + MESSAGE_FILES_VALIDATED, Project.MSG_VERBOSE); + } + + /** + * representation of a schema location. This is a URI plus either a file or + * a url + */ + public static class SchemaLocation { + private String namespace; + + private File file; + + private String url; + + /** No namespace URI */ + public static final String ERROR_NO_URI = "No namespace URI"; + + /** Both URL and File were given for schema */ + public static final String ERROR_TWO_LOCATIONS + = "Both URL and File were given for schema "; + + /** File not found */ + public static final String ERROR_NO_FILE = "File not found: "; + + /** Cannot make URL */ + public static final String ERROR_NO_URL_REPRESENTATION + = "Cannot make a URL of "; + + /** No location provided */ + public static final String ERROR_NO_LOCATION + = "No file or URL supplied for the schema "; + + /** No arg constructor */ + public SchemaLocation() { + } + + /** + * Get the namespace. + * @return the namespace. + */ + public String getNamespace() { + return namespace; + } + + /** + * set the namespace of this schema. Any URI + * @param namespace the namespace to use. + */ + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + /** + * Get the file. + * @return the file containing the schema. + */ + public File getFile() { + return file; + } + + /** + * identify a file that contains this namespace's schema. + * The file must exist. + * @param file the file contains the schema. + */ + public void setFile(File file) { + this.file = file; + } + + /** + * The URL containing the schema. + * @return the URL string. + */ + public String getUrl() { + return url; + } + + /** + * identify a URL that hosts the schema. + * @param url the URL string. + */ + public void setUrl(String url) { + this.url = url; + } + + /** + * get the URL of the schema + * @return a URL to the schema + * @throws BuildException if not + */ + public String getSchemaLocationURL() { + boolean hasFile = file != null; + boolean hasURL = isSet(url); + //error if both are empty, or both are set + if (!hasFile && !hasURL) { + throw new BuildException(ERROR_NO_LOCATION + namespace); + } + if (hasFile && hasURL) { + throw new BuildException(ERROR_TWO_LOCATIONS + namespace); + } + String schema = url; + if (hasFile) { + if (!file.exists()) { + throw new BuildException(ERROR_NO_FILE + file); + } + + try { + schema = FileUtils.getFileUtils().getFileURL(file).toString(); + } catch (MalformedURLException e) { + //this is almost implausible, but required handling + throw new BuildException(ERROR_NO_URL_REPRESENTATION + file, e); + } + } + return schema; + } + + /** + * validate the fields then create a "uri location" string + * + * @return string of uri and location + * @throws BuildException if there is an error. + */ + public String getURIandLocation() throws BuildException { + validateNamespace(); + StringBuffer buffer = new StringBuffer(); + buffer.append(namespace); + buffer.append(' '); + buffer.append(getSchemaLocationURL()); + return new String(buffer); + } + + /** + * assert that a namespace is valid + * @throws BuildException if not + */ + public void validateNamespace() { + if (!isSet(getNamespace())) { + throw new BuildException(ERROR_NO_URI); + } + } + + /** + * check that a property is set + * @param property string to check + * @return true if it is not null or empty + */ + private boolean isSet(String property) { + return property != null && property.length() != 0; + } + + /** + * equality test checks namespace, location and filename. All must match, + * @param o object to compare against + * @return true iff the objects are considered equal in value + */ + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SchemaLocation)) { + return false; + } + + final SchemaLocation schemaLocation = (SchemaLocation) o; + + if (file != null ? !file.equals(schemaLocation.file) : schemaLocation.file != null) { + return false; + } + if (namespace != null ? !namespace.equals(schemaLocation.namespace) + : schemaLocation.namespace != null) { + return false; + } + if (url != null ? !url.equals(schemaLocation.url) : schemaLocation.url != null) { + return false; + } + + return true; + } + + /** + * Generate a hashcode depending on the namespace, url and file name. + * @return the hashcode. + */ + public int hashCode() { + int result; + // CheckStyle:MagicNumber OFF + result = (namespace != null ? namespace.hashCode() : 0); + result = 29 * result + (file != null ? file.hashCode() : 0); + result = 29 * result + (url != null ? url.hashCode() : 0); + // CheckStyle:MagicNumber OFF + return result; + } + + /** + * Returns a string representation of the object for error messages + * and the like + * @return a string representation of the object. + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append(namespace != null ? namespace : "(anonymous)"); + buffer.append(' '); + buffer.append(url != null ? (url + " ") : ""); + buffer.append(file != null ? file.getAbsolutePath() : ""); + return buffer.toString(); + } + } //SchemaLocation +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Script.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Script.java new file mode 100644 index 00000000..2b8d9f65 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Script.java @@ -0,0 +1,133 @@ +/* + * 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.tools.ant.taskdefs.optional; + +import java.io.File; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.Reference; +import org.apache.tools.ant.util.ScriptRunnerHelper; + +/** + * Executes a script. + * + * @ant.task name="script" + */ +public class Script extends Task { + + private ScriptRunnerHelper helper = new ScriptRunnerHelper(); + + /** + * Set the project. + * @param project the project that this task belongs to. + */ + public void setProject(Project project) { + super.setProject(project); + helper.setProjectComponent(this); + } + + /** + * Run the script using the helper object. + * + * @exception BuildException if something goes wrong with the build + */ + public void execute() throws BuildException { + helper.getScriptRunner().executeScript("ANT"); + } + + /** + * Defines the manager. + * + * @param manager the scripting manager. + */ + public void setManager(String manager) { + helper.setManager(manager); + } + + /** + * Defines the language (required). + * + * @param language the scripting language name for the script. + */ + public void setLanguage(String language) { + helper.setLanguage(language); + } + + /** + * Load the script from an external file ; optional. + * + * @param fileName the name of the file containing the script source. + */ + public void setSrc(String fileName) { + helper.setSrc(new File(fileName)); + } + + /** + * Set the script text. + * + * @param text a component of the script text to be added. + */ + public void addText(String text) { + helper.addText(text); + } + + /** + * Set the classpath to be used when searching for classes and resources. + * + * @param classpath an Ant Path object containing the search path. + */ + public void setClasspath(Path classpath) { + helper.setClasspath(classpath); + } + + /** + * Classpath to be used when searching for classes and resources. + * + * @return an empty Path instance to be configured by Ant. + */ + public Path createClasspath() { + return helper.createClasspath(); + } + + /** + * Set the classpath by reference. + * + * @param r a Reference to a Path instance to be used as the classpath + * value. + */ + public void setClasspathRef(Reference r) { + helper.setClasspathRef(r); + } + + /** + * Set the setbeans attribute. + * If this is true, <script> will create variables in the + * script instance for all + * properties, targets and references of the current project. + * It this is false, only the project and self variables will + * be set. + * The default is true. + * @param setBeans the value to set. + */ + public void setSetBeans(boolean setBeans) { + helper.setSetBeans(setBeans); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java new file mode 100644 index 00000000..8d9a44a6 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java @@ -0,0 +1,650 @@ +/* + * 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.tools.ant.taskdefs.optional; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.net.URL; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.ErrorListener; +import javax.xml.transform.Source; +import javax.xml.transform.SourceLocator; +import javax.xml.transform.Templates; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.URIResolver; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.XSLTLiaison4; +import org.apache.tools.ant.taskdefs.XSLTLogger; +import org.apache.tools.ant.taskdefs.XSLTLoggerAware; +import org.apache.tools.ant.taskdefs.XSLTProcess; +import org.apache.tools.ant.types.Resource; +import org.apache.tools.ant.types.XMLCatalog; +import org.apache.tools.ant.types.resources.FileProvider; +import org.apache.tools.ant.types.resources.FileResource; +import org.apache.tools.ant.types.resources.URLProvider; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.JAXPUtils; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; + +/** + * Concrete liaison for XSLT processor implementing TraX. (ie JAXP 1.1) + * + * @since Ant 1.3 + */ +public class TraXLiaison implements XSLTLiaison4, ErrorListener, XSLTLoggerAware { + + /** + * Helper for transforming filenames to URIs. + * + * @since Ant 1.7 + */ + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** + * The current <code>Project</code> + */ + private Project project; + + /** + * the name of the factory implementation class to use + * or null for default JAXP lookup. + */ + private String factoryName = null; + + /** The trax TransformerFactory */ + private TransformerFactory tfactory = null; + + /** stylesheet to use for transformation */ + private Resource stylesheet; + + private XSLTLogger logger; + + /** possible resolver for publicIds */ + private EntityResolver entityResolver; + + /** transformer to use for processing files */ + private Transformer transformer; + + /** The In memory version of the stylesheet */ + private Templates templates; + + /** + * The modification time of the stylesheet from which the templates + * are read + */ + private long templatesModTime; + + /** possible resolver for URIs */ + private URIResolver uriResolver; + + /** transformer output properties */ + private final Vector outputProperties = new Vector(); + + /** stylesheet parameters */ + private final Hashtable<String, Object> params = new Hashtable<String, Object>(); + + /** factory attributes */ + private final Vector attributes = new Vector(); + + /** whether to suppress warnings */ + private boolean suppressWarnings = false; + + /** optional trace configuration. */ + private XSLTProcess.TraceConfiguration traceConfiguration = null; + + /** + * Constructor for TraXLiaison. + * @throws Exception never + */ + public TraXLiaison() throws Exception { + } + + /** + * Set the stylesheet file. + * @param stylesheet a <code>File</code> value + * @throws Exception on error + */ + public void setStylesheet(final File stylesheet) throws Exception { + final FileResource fr = new FileResource(); + fr.setProject(project); + fr.setFile(stylesheet); + setStylesheet(fr); + } + + /** + * Set the stylesheet file. + * @param stylesheet a {@link org.apache.tools.ant.types.Resource} value + * @throws Exception on error + */ + public void setStylesheet(final Resource stylesheet) throws Exception { + if (this.stylesheet != null) { + // resetting the stylesheet - reset transformer + transformer = null; + + // do we need to reset templates as well + if (!this.stylesheet.equals(stylesheet) + || (stylesheet.getLastModified() != templatesModTime)) { + templates = null; + } + } + this.stylesheet = stylesheet; + } + + /** + * Transform an input file. + * @param infile the file to transform + * @param outfile the result file + * @throws Exception on error + */ + public void transform(final File infile, final File outfile) throws Exception { + if (transformer == null) { + createTransformer(); + } + + InputStream fis = null; + OutputStream fos = null; + try { + fis = new BufferedInputStream(new FileInputStream(infile)); + fos = new BufferedOutputStream(new FileOutputStream(outfile)); + final StreamResult res = new StreamResult(fos); + // not sure what could be the need of this... + res.setSystemId(JAXPUtils.getSystemId(outfile)); + final Source src = getSource(fis, infile); + + // set parameters on each transformation, maybe something has changed + //(e.g. value of file name parameter) + setTransformationParameters(); + + transformer.transform(src, res); + } finally { + // make sure to close all handles, otherwise the garbage + // collector will close them...whenever possible and + // Windows may complain about not being able to delete files. + FileUtils.close(fis); + FileUtils.close(fos); + } + } + + /** + * Get the source instance from the stream and id of the file. + * @param is the stream containing the stylesheet data. + * @param infile the file that will be used for the systemid. + * @return the configured source instance matching the stylesheet. + * @throws ParserConfigurationException if a parser cannot be created which + * satisfies the requested configuration. + * @throws SAXException in case of problem detected by the SAX parser. + */ + private Source getSource(final InputStream is, final File infile) + throws ParserConfigurationException, SAXException { + // todo: is this comment still relevant ?? + // FIXME: need to use a SAXSource as the source for the transform + // so we can plug in our own entity resolver + Source src = null; + if (entityResolver != null) { + if (getFactory().getFeature(SAXSource.FEATURE)) { + final SAXParserFactory spFactory = SAXParserFactory.newInstance(); + spFactory.setNamespaceAware(true); + final XMLReader reader = spFactory.newSAXParser().getXMLReader(); + reader.setEntityResolver(entityResolver); + src = new SAXSource(reader, new InputSource(is)); + } else { + throw new IllegalStateException("xcatalog specified, but " + + "parser doesn't support SAX"); + } + } else { + // WARN: Don't use the StreamSource(File) ctor. It won't work with + // xalan prior to 2.2 because of systemid bugs. + src = new StreamSource(is); + } + src.setSystemId(JAXPUtils.getSystemId(infile)); + return src; + } + + private Source getSource(final InputStream is, final Resource resource) + throws ParserConfigurationException, SAXException { + // todo: is this comment still relevant ?? + // FIXME: need to use a SAXSource as the source for the transform + // so we can plug in our own entity resolver + Source src = null; + if (entityResolver != null) { + if (getFactory().getFeature(SAXSource.FEATURE)) { + final SAXParserFactory spFactory = SAXParserFactory.newInstance(); + spFactory.setNamespaceAware(true); + final XMLReader reader = spFactory.newSAXParser().getXMLReader(); + reader.setEntityResolver(entityResolver); + src = new SAXSource(reader, new InputSource(is)); + } else { + throw new IllegalStateException("xcatalog specified, but " + + "parser doesn't support SAX"); + } + } else { + // WARN: Don't use the StreamSource(File) ctor. It won't work with + // xalan prior to 2.2 because of systemid bugs. + src = new StreamSource(is); + } + // The line below is a hack: the system id must an URI, but it is not + // cleat to get the URI of an resource, so just set the name of the + // resource as a system id + src.setSystemId(resourceToURI(resource)); + return src; + } + + private String resourceToURI(final Resource resource) { + final FileProvider fp = resource.as(FileProvider.class); + if (fp != null) { + return FILE_UTILS.toURI(fp.getFile().getAbsolutePath()); + } + final URLProvider up = resource.as(URLProvider.class); + if (up != null) { + final URL u = up.getURL(); + return String.valueOf(u); + } else { + return resource.getName(); + } + } + + /** + * Read in templates from the stylesheet + */ + private void readTemplates() + throws IOException, TransformerConfigurationException, + ParserConfigurationException, SAXException { + + // Use a stream so that you can close it yourself quickly + // and avoid keeping the handle until the object is garbaged. + // (always keep control), otherwise you won't be able to delete + // the file quickly on windows. + InputStream xslStream = null; + try { + xslStream + = new BufferedInputStream(stylesheet.getInputStream()); + templatesModTime = stylesheet.getLastModified(); + final Source src = getSource(xslStream, stylesheet); + templates = getFactory().newTemplates(src); + } finally { + if (xslStream != null) { + xslStream.close(); + } + } + } + + /** + * Create a new transformer based on the liaison settings + * @throws Exception thrown if there is an error during creation. + * @see #setStylesheet(java.io.File) + * @see #addParam(java.lang.String, java.lang.String) + * @see #setOutputProperty(java.lang.String, java.lang.String) + */ + private void createTransformer() throws Exception { + if (templates == null) { + readTemplates(); + } + + transformer = templates.newTransformer(); + + // configure the transformer... + transformer.setErrorListener(this); + if (uriResolver != null) { + transformer.setURIResolver(uriResolver); + } + final int size = outputProperties.size(); + for (int i = 0; i < size; i++) { + final String[] pair = (String[]) outputProperties.elementAt(i); + transformer.setOutputProperty(pair[0], pair[1]); + } + + if (traceConfiguration != null) { + if ("org.apache.xalan.transformer.TransformerImpl" + .equals(transformer.getClass().getName())) { + try { + final Class traceSupport = + Class.forName("org.apache.tools.ant.taskdefs.optional." + + "Xalan2TraceSupport", true, + Thread.currentThread() + .getContextClassLoader()); + final XSLTTraceSupport ts = + (XSLTTraceSupport) traceSupport.newInstance(); + ts.configureTrace(transformer, traceConfiguration); + } catch (final Exception e) { + final String msg = "Failed to enable tracing because of " + e; + if (project != null) { + project.log(msg, Project.MSG_WARN); + } else { + System.err.println(msg); + } + } + } else { + final String msg = "Not enabling trace support for transformer" + + " implementation" + transformer.getClass().getName(); + if (project != null) { + project.log(msg, Project.MSG_WARN); + } else { + System.err.println(msg); + } + } + } + } + + /** + * Sets the parameters for the transformer. + */ + private void setTransformationParameters() { + for (final Enumeration enumeration = params.keys(); + enumeration.hasMoreElements();) { + final String name = (String) enumeration.nextElement(); + final Object value = params.get(name); + transformer.setParameter(name, value); + } + } + + /** + * return the Transformer factory associated to this liaison. + * @return the Transformer factory associated to this liaison. + * @throws BuildException thrown if there is a problem creating + * the factory. + * @see #setFactory(String) + * @since Ant 1.5.2 + */ + private TransformerFactory getFactory() throws BuildException { + if (tfactory != null) { + return tfactory; + } + // not initialized yet, so create the factory + if (factoryName == null) { + tfactory = TransformerFactory.newInstance(); + } else { + try { + Class clazz = null; + try { + clazz = + Class.forName(factoryName, true, + Thread.currentThread() + .getContextClassLoader()); + } catch (final ClassNotFoundException cnfe) { + final String msg = "Failed to load " + factoryName + + " via the configured classpath, will try" + + " Ant's classpath instead."; + if (logger != null) { + logger.log(msg); + } else if (project != null) { + project.log(msg, Project.MSG_WARN); + } else { + System.err.println(msg); + } + } + + if (clazz == null) { + clazz = Class.forName(factoryName); + } + tfactory = (TransformerFactory) clazz.newInstance(); + } catch (final Exception e) { + throw new BuildException(e); + } + } + + try { // #51668, #52382 + final Field _isNotSecureProcessing = tfactory.getClass().getDeclaredField("_isNotSecureProcessing"); + _isNotSecureProcessing.setAccessible(true); + _isNotSecureProcessing.set(tfactory, Boolean.TRUE); + } catch (final Exception x) { + if (project != null) { + project.log(x.toString(), Project.MSG_DEBUG); + } + } + + tfactory.setErrorListener(this); + + // specific attributes for the transformer + final int size = attributes.size(); + for (int i = 0; i < size; i++) { + final Object[] pair = (Object[]) attributes.elementAt(i); + tfactory.setAttribute((String) pair[0], pair[1]); + } + + if (uriResolver != null) { + tfactory.setURIResolver(uriResolver); + } + return tfactory; + } + + + /** + * Set the factory name to use instead of JAXP default lookup. + * @param name the fully qualified class name of the factory to use + * or null for the default JAXP look up mechanism. + * @since Ant 1.6 + */ + public void setFactory(final String name) { + factoryName = name; + } + + /** + * Set a custom attribute for the JAXP factory implementation. + * @param name the attribute name. + * @param value the value of the attribute, usually a boolean + * string or object. + * @since Ant 1.6 + */ + public void setAttribute(final String name, final Object value) { + final Object[] pair = new Object[]{name, value}; + attributes.addElement(pair); + } + + /** + * Set the output property for the current transformer. + * Note that the stylesheet must be set prior to calling + * this method. + * @param name the output property name. + * @param value the output property value. + * @since Ant 1.5 + * @since Ant 1.5 + */ + public void setOutputProperty(final String name, final String value) { + final String[] pair = new String[]{name, value}; + outputProperties.addElement(pair); + } + + /** + * Set the class to resolve entities during the transformation. + * @param aResolver the resolver class. + */ + public void setEntityResolver(final EntityResolver aResolver) { + entityResolver = aResolver; + } + + /** + * Set the class to resolve URIs during the transformation + * @param aResolver a <code>EntityResolver</code> value + */ + public void setURIResolver(final URIResolver aResolver) { + uriResolver = aResolver; + } + + /** + * Add a parameter. + * @param name the name of the parameter + * @param value the value of the parameter + */ + public void addParam(final String name, final String value) { + params.put(name, value); + } + + /** + * Add a parameter. + * @param name the name of the parameter + * @param value the value of the parameter + * @since Ant 1.9.3 + */ + public void addParam(final String name, final Object value) { + params.put(name, value); + } + + /** + * Set a logger. + * @param l a logger. + */ + public void setLogger(final XSLTLogger l) { + logger = l; + } + + /** + * Log an error. + * @param e the exception to log. + */ + public void error(final TransformerException e) { + logError(e, "Error"); + } + + /** + * Log a fatal error. + * @param e the exception to log. + */ + public void fatalError(final TransformerException e) { + logError(e, "Fatal Error"); + throw new BuildException("Fatal error during transformation using " + stylesheet + ": " + e.getMessageAndLocation(), e); + } + + /** + * Log a warning. + * @param e the exception to log. + */ + public void warning(final TransformerException e) { + if (!suppressWarnings) { + logError(e, "Warning"); + } + } + + private void logError(final TransformerException e, final String type) { + if (logger == null) { + return; + } + + final StringBuffer msg = new StringBuffer(); + final SourceLocator locator = e.getLocator(); + if (locator != null) { + final String systemid = locator.getSystemId(); + if (systemid != null) { + String url = systemid; + if (url.startsWith("file:")) { + url = FileUtils.getFileUtils().fromURI(url); + } + msg.append(url); + } else { + msg.append("Unknown file"); + } + final int line = locator.getLineNumber(); + if (line != -1) { + msg.append(":"); + msg.append(line); + final int column = locator.getColumnNumber(); + if (column != -1) { + msg.append(":"); + msg.append(column); + } + } + } + msg.append(": "); + msg.append(type); + msg.append("! "); + msg.append(e.getMessage()); + if (e.getCause() != null) { + msg.append(" Cause: "); + msg.append(e.getCause()); + } + + logger.log(msg.toString()); + } + + // kept for backwards compatibility + /** + * @param file the filename to use for the systemid + * @return the systemid + * @deprecated since 1.5.x. + * Use org.apache.tools.ant.util.JAXPUtils#getSystemId instead. + */ + @Deprecated + protected String getSystemId(final File file) { + return JAXPUtils.getSystemId(file); + } + + + /** + * Specific configuration for the TRaX liaison. + * @param xsltTask the XSLTProcess task instance from which this liaison + * is to be configured. + */ + public void configure(final XSLTProcess xsltTask) { + project = xsltTask.getProject(); + final XSLTProcess.Factory factory = xsltTask.getFactory(); + if (factory != null) { + setFactory(factory.getName()); + + // configure factory attributes + for (final Enumeration attrs = factory.getAttributes(); + attrs.hasMoreElements();) { + final XSLTProcess.Factory.Attribute attr = + (XSLTProcess.Factory.Attribute) attrs.nextElement(); + setAttribute(attr.getName(), attr.getValue()); + } + } + + final XMLCatalog xmlCatalog = xsltTask.getXMLCatalog(); + // use XMLCatalog as the entity resolver and URI resolver + if (xmlCatalog != null) { + setEntityResolver(xmlCatalog); + setURIResolver(xmlCatalog); + } + + + // configure output properties + for (final Enumeration props = xsltTask.getOutputProperties(); + props.hasMoreElements();) { + final XSLTProcess.OutputProperty prop + = (XSLTProcess.OutputProperty) props.nextElement(); + setOutputProperty(prop.getName(), prop.getValue()); + } + + suppressWarnings = xsltTask.getSuppressWarnings(); + + traceConfiguration = xsltTask.getTraceConfiguration(); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/XMLValidateTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/XMLValidateTask.java new file mode 100644 index 00000000..05c043df --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/XMLValidateTask.java @@ -0,0 +1,764 @@ +/* + * 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.tools.ant.taskdefs.optional; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Vector; + +import org.apache.tools.ant.AntClassLoader; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.DTDLocation; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.Reference; +import org.apache.tools.ant.types.XMLCatalog; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.JAXPUtils; +import org.apache.tools.ant.util.XmlConstants; +import org.xml.sax.EntityResolver; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.Parser; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.SAXParseException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.ParserAdapter; + +/** + * Checks XML files are valid (or only well formed). The + * task uses the SAX2 parser implementation provided by JAXP by default + * (probably the one that is used by Ant itself), but one can specify any + * SAX1/2 parser if needed. + * + */ +public class XMLValidateTask extends Task { + + /** + * helper for path -> URI and URI -> path conversions. + */ + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + protected static final String INIT_FAILED_MSG = + "Could not start xml validation: "; + + // ant task properties + // defaults + // CheckStyle:VisibilityModifier OFF - bc + protected boolean failOnError = true; + protected boolean warn = true; + protected boolean lenient = false; + protected String readerClassName = null; + + /** file to be validated */ + protected File file = null; + /** sets of file to be validated */ + protected Vector filesets = new Vector(); + protected Path classpath; + + /** + * the parser is viewed as a SAX2 XMLReader. If a SAX1 parser is specified, + * it's wrapped in an adapter that make it behave as a XMLReader. + * a more 'standard' way of doing this would be to use the JAXP1.1 SAXParser + * interface. + */ + protected XMLReader xmlReader = null; + // XMLReader used to validation process + protected ValidatorErrorHandler errorHandler = new ValidatorErrorHandler(); + // to report sax parsing errors + // CheckStyle:VisibilityModifier ON + + /** The vector to store all attributes (features) to be set on the parser. **/ + private Vector attributeList = new Vector(); + + /** + * List of properties. + */ + private final Vector propertyList = new Vector(); + + private XMLCatalog xmlCatalog = new XMLCatalog(); + /** Message for successful validation */ + public static final String MESSAGE_FILES_VALIDATED + = " file(s) have been successfully validated."; + + private AntClassLoader readerLoader = null; + + /** + * Specify how parser error are to be handled. + * Optional, default is <code>true</code>. + * <p> + * If set to <code>true</code> (default), throw a buildException if the + * parser yields an error. + * @param fail if set to <code>false</code> do not fail on error + */ + public void setFailOnError(boolean fail) { + failOnError = fail; + } + + /** + * Specify how parser error are to be handled. + * <p> + * If set to <code>true</code> (default), log a warn message for each SAX warn event. + * @param bool if set to <code>false</code> do not send warnings + */ + public void setWarn(boolean bool) { + warn = bool; + } + + /** + * Specify whether the parser should be validating. Default + * is <code>true</code>. + * <p> + * If set to false, the validation will fail only if the parsed document + * is not well formed XML. + * <p> + * this option is ignored if the specified class + * with {@link #setClassName(String)} is not a SAX2 XMLReader. + * @param bool if set to <code>false</code> only fail on malformed XML + */ + public void setLenient(boolean bool) { + lenient = bool; + } + + /** + * Specify the class name of the SAX parser to be used. (optional) + * @param className should be an implementation of SAX2 + * <code>org.xml.sax.XMLReader</code> or SAX2 <code>org.xml.sax.Parser</code>. + * <p> if className is an implementation of + * <code>org.xml.sax.Parser</code>, {@link #setLenient(boolean)}, + * will be ignored. + * <p> if not set, the default will be used. + * @see org.xml.sax.XMLReader + * @see org.xml.sax.Parser + */ + public void setClassName(String className) { + readerClassName = className; + } + + /** + * Specify the classpath to be searched to load the parser (optional) + * @param classpath the classpath to load the parser + */ + public void setClasspath(Path classpath) { + if (this.classpath == null) { + this.classpath = classpath; + } else { + this.classpath.append(classpath); + } + } + + /** + * @see #setClasspath + * @return the classpath created + */ + public Path createClasspath() { + if (this.classpath == null) { + this.classpath = new Path(getProject()); + } + return this.classpath.createPath(); + } + + /** + * Where to find the parser class; optional. + * @see #setClasspath + * @param r reference to a classpath defined elsewhere + */ + public void setClasspathRef(Reference r) { + createClasspath().setRefid(r); + } + + /** + * specify the file to be checked; optional. + * @param file the file to be checked + */ + public void setFile(File file) { + this.file = file; + } + + /** + * add an XMLCatalog as a nested element; optional. + * @param catalog XMLCatalog to use + */ + public void addConfiguredXMLCatalog(XMLCatalog catalog) { + xmlCatalog.addConfiguredXMLCatalog(catalog); + } + + /** + * specify a set of file to be checked + * @param set the fileset to check + */ + public void addFileset(FileSet set) { + filesets.addElement(set); + } + + /** + * Add an attribute nested element. This is used for setting arbitrary + * features of the SAX parser. + * Valid attributes + * <a href= + * "http://www.saxproject.org/apidoc/org/xml/sax/package-summary.html#package_description" + * >include</a> + * @return attribute created + * @since ant1.6 + */ + public Attribute createAttribute() { + final Attribute feature = new Attribute(); + attributeList.addElement(feature); + return feature; + } + + /** + * Creates a property. + * + * @return a property. + * @since ant 1.6.2 + */ + public Property createProperty() { + final Property prop = new Property(); + propertyList.addElement(prop); + return prop; + } + + /** + * Called by the project to let the task initialize properly. + * + * @exception BuildException if something goes wrong with the build + */ + public void init() throws BuildException { + super.init(); + xmlCatalog.setProject(getProject()); + } + + /** + * Create a DTD location record; optional. + * This stores the location of a DTD. The DTD is identified + * by its public Id. + * @return created DTD location + */ + public DTDLocation createDTD() { + DTDLocation dtdLocation = new DTDLocation(); + xmlCatalog.addDTD(dtdLocation); + return dtdLocation; + } + /** + * accessor to the xmlCatalog used in the task + * @return xmlCatalog reference + */ + protected EntityResolver getEntityResolver() { + return xmlCatalog; + } + + /** + * get the XML reader. Non-null only after {@link #initValidator()}. + * If the reader is an instance of {@link ParserAdapter} then + * the parser is a SAX1 parser, and you cannot call + * {@link #setFeature(String, boolean)} or {@link #setProperty(String, String)} + * on it. + * @return the XML reader or null. + */ + protected XMLReader getXmlReader() { + return xmlReader; + } + + /** + * execute the task + * @throws BuildException if <code>failonerror</code> is true and an error happens + */ + public void execute() throws BuildException { + try { + int fileProcessed = 0; + if (file == null && (filesets.size() == 0)) { + throw new BuildException( + "Specify at least one source - " + "a file or a fileset."); + } + + + + if (file != null) { + if (file.exists() && file.canRead() && file.isFile()) { + doValidate(file); + fileProcessed++; + } else { + String errorMsg = "File " + file + " cannot be read"; + if (failOnError) { + throw new BuildException(errorMsg); + } else { + log(errorMsg, Project.MSG_ERR); + } + } + } + + final int size = filesets.size(); + for (int i = 0; i < size; i++) { + + FileSet fs = (FileSet) filesets.elementAt(i); + DirectoryScanner ds = fs.getDirectoryScanner(getProject()); + String[] files = ds.getIncludedFiles(); + + for (int j = 0; j < files.length; j++) { + File srcFile = new File(fs.getDir(getProject()), files[j]); + doValidate(srcFile); + fileProcessed++; + } + } + onSuccessfulValidation(fileProcessed); + } finally { + cleanup(); + } + } + + /** + * handler called on successful file validation. + * @param fileProcessed number of files processed. + */ + protected void onSuccessfulValidation(int fileProcessed) { + log(fileProcessed + MESSAGE_FILES_VALIDATED); + } + + /** + * init the parser : + * load the parser class, and set features if necessary + * It is only after this that the reader is valid + * @throws BuildException if something went wrong + */ + protected void initValidator() { + + xmlReader = createXmlReader(); + + xmlReader.setEntityResolver(getEntityResolver()); + xmlReader.setErrorHandler(errorHandler); + + if (!isSax1Parser()) { + // turn validation on + if (!lenient) { + setFeature(XmlConstants.FEATURE_VALIDATION, true); + } + // set the feature from the attribute list + final int attSize = attributeList.size(); + for (int i = 0; i < attSize; i++) { + Attribute feature = (Attribute) attributeList.elementAt(i); + setFeature(feature.getName(), feature.getValue()); + + } + // Sets properties + final int propSize = propertyList.size(); + for (int i = 0; i < propSize; i++) { + final Property prop = (Property) propertyList.elementAt(i); + setProperty(prop.getName(), prop.getValue()); + } + } + } + + /** + * test that returns true if we are using a SAX1 parser. + * @return true when a SAX1 parser is in use + */ + protected boolean isSax1Parser() { + return (xmlReader instanceof ParserAdapter); + } + + /** + * create the XML reader. + * This is one by instantiating anything specified by {@link #readerClassName}, + * falling back to a default reader if not. + * If the returned reader is an instance of {@link ParserAdapter} then + * we have created and wrapped a SAX1 parser. + * @return the new XMLReader. + */ + protected XMLReader createXmlReader() { + Object reader = null; + if (readerClassName == null) { + reader = createDefaultReaderOrParser(); + } else { + + Class readerClass = null; + try { + // load the parser class + if (classpath != null) { + readerLoader = getProject().createClassLoader(classpath); + readerClass = Class.forName(readerClassName, true, + readerLoader); + } else { + readerClass = Class.forName(readerClassName); + } + + reader = readerClass.newInstance(); + } catch (ClassNotFoundException e) { + throw new BuildException(INIT_FAILED_MSG + readerClassName, e); + } catch (InstantiationException e) { + throw new BuildException(INIT_FAILED_MSG + readerClassName, e); + } catch (IllegalAccessException e) { + throw new BuildException(INIT_FAILED_MSG + readerClassName, e); + } + } + + // then check it implements XMLReader + XMLReader newReader; + if (reader instanceof XMLReader) { + newReader = (XMLReader) reader; + log( + "Using SAX2 reader " + reader.getClass().getName(), + Project.MSG_VERBOSE); + } else { + + // see if it is a SAX1 Parser + if (reader instanceof Parser) { + newReader = new ParserAdapter((Parser) reader); + log( + "Using SAX1 parser " + reader.getClass().getName(), + Project.MSG_VERBOSE); + } else { + throw new BuildException( + INIT_FAILED_MSG + + reader.getClass().getName() + + " implements nor SAX1 Parser nor SAX2 XMLReader."); + } + } + return newReader; + } + + /** + * Cleans up resources. + * + * @since Ant 1.8.0 + */ + protected void cleanup() { + if (readerLoader != null) { + readerLoader.cleanup(); + readerLoader = null; + } + } + + /** + * Returns a SAX-based XMLReader or a SAX-based Parser. + * @return reader or parser + */ + private Object createDefaultReaderOrParser() { + Object reader; + try { + reader = createDefaultReader(); + } catch (BuildException exc) { + reader = JAXPUtils.getParser(); + } + return reader; + } + + /** + * Create a reader if the use of the class did not specify another one. + * If a BuildException is thrown, the caller may revert to an alternate + * reader. + * @return a new reader. + * @throws BuildException if something went wrong + */ + protected XMLReader createDefaultReader() { + return JAXPUtils.getXMLReader(); + } + + /** + * Set a feature on the parser. + * @param feature the name of the feature to set + * @param value the value of the feature + * @throws BuildException if the feature was not supported + */ + protected void setFeature(String feature, boolean value) + throws BuildException { + log("Setting feature " + feature + "=" + value, Project.MSG_DEBUG); + try { + xmlReader.setFeature(feature, value); + } catch (SAXNotRecognizedException e) { + throw new BuildException( + "Parser " + + xmlReader.getClass().getName() + + " doesn't recognize feature " + + feature, + e, + getLocation()); + } catch (SAXNotSupportedException e) { + throw new BuildException( + "Parser " + + xmlReader.getClass().getName() + + " doesn't support feature " + + feature, + e, + getLocation()); + } + } + + /** + * Sets a property. + * + * @param name a property name + * @param value a property value. + * @throws BuildException if an error occurs. + * @throws BuildException if the property was not supported + */ + protected void setProperty(String name, String value) throws BuildException { + // Validates property + if (name == null || value == null) { + throw new BuildException("Property name and value must be specified."); + } + + try { + xmlReader.setProperty(name, value); + } catch (SAXNotRecognizedException e) { + throw new BuildException( + "Parser " + + xmlReader.getClass().getName() + + " doesn't recognize property " + + name, + e, + getLocation()); + } catch (SAXNotSupportedException e) { + throw new BuildException( + "Parser " + + xmlReader.getClass().getName() + + " doesn't support property " + + name, + e, + getLocation()); + } + } + + /** + * parse the file + * @param afile the file to validate. + * @return true if the file validates. + */ + protected boolean doValidate(File afile) { + //for every file, we have a new instance of the validator + initValidator(); + boolean result = true; + try { + log("Validating " + afile.getName() + "... ", Project.MSG_VERBOSE); + errorHandler.init(afile); + InputSource is = new InputSource(new FileInputStream(afile)); + String uri = FILE_UTILS.toURI(afile.getAbsolutePath()); + is.setSystemId(uri); + xmlReader.parse(is); + } catch (SAXException ex) { + log("Caught when validating: " + ex.toString(), Project.MSG_DEBUG); + if (failOnError) { + throw new BuildException( + "Could not validate document " + afile); + } + log("Could not validate document " + afile + ": " + ex.toString()); + result = false; + } catch (IOException ex) { + throw new BuildException( + "Could not validate document " + afile, + ex); + } + if (errorHandler.getFailure()) { + if (failOnError) { + throw new BuildException( + afile + " is not a valid XML document."); + } + result = false; + log(afile + " is not a valid XML document", Project.MSG_ERR); + } + return result; + } + + /** + * ValidatorErrorHandler role : + * <ul> + * <li> log SAX parse exceptions, + * <li> remember if an error occurred + * </ul> + */ + protected class ValidatorErrorHandler implements ErrorHandler { + + // CheckStyle:VisibilityModifier OFF - bc + protected File currentFile = null; + protected String lastErrorMessage = null; + protected boolean failed = false; + // CheckStyle:VisibilityModifier ON + /** + * initialises the class + * @param file file used + */ + public void init(File file) { + currentFile = file; + failed = false; + } + /** + * did an error happen during last parsing ? + * @return did an error happen during last parsing ? + */ + public boolean getFailure() { + return failed; + } + + /** + * record a fatal error + * @param exception the fatal error + */ + public void fatalError(SAXParseException exception) { + failed = true; + doLog(exception, Project.MSG_ERR); + } + /** + * receive notification of a recoverable error + * @param exception the error + */ + public void error(SAXParseException exception) { + failed = true; + doLog(exception, Project.MSG_ERR); + } + /** + * receive notification of a warning + * @param exception the warning + */ + public void warning(SAXParseException exception) { + // depending on implementation, XMLReader can yield hips of warning, + // only output then if user explicitly asked for it + if (warn) { + doLog(exception, Project.MSG_WARN); + } + } + + private void doLog(SAXParseException e, int logLevel) { + + log(getMessage(e), logLevel); + } + + private String getMessage(SAXParseException e) { + String sysID = e.getSystemId(); + if (sysID != null) { + String name = sysID; + if (sysID.startsWith("file:")) { + try { + name = FILE_UTILS.fromURI(sysID); + } catch (Exception ex) { + // if this is not a valid file: just use the uri + } + } + int line = e.getLineNumber(); + int col = e.getColumnNumber(); + return name + + (line == -1 + ? "" + : (":" + line + (col == -1 ? "" : (":" + col)))) + + ": " + + e.getMessage(); + } + return e.getMessage(); + } + } + + /** + * The class to create to set a feature of the parser. + * @since ant1.6 + */ + public static class Attribute { + /** The name of the attribute to set. + * + * Valid attributes <a href= + * "http://www.saxproject.org/apidoc/org/xml/sax/package-summary.html#package_description" + * >include.</a> + */ + private String attributeName = null; + + /** + * The value of the feature. + **/ + private boolean attributeValue; + + /** + * Set the feature name. + * @param name the name to set + */ + public void setName(String name) { + attributeName = name; + } + /** + * Set the feature value to true or false. + * @param value feature value + */ + public void setValue(boolean value) { + attributeValue = value; + } + + /** + * Gets the attribute name. + * @return the feature name + */ + public String getName() { + return attributeName; + } + + /** + * Gets the attribute value. + * @return the feature value + */ + public boolean getValue() { + return attributeValue; + } + } + + /** + * A Parser property. + * See <a href="http://xml.apache.org/xerces-j/properties.html"> + * XML parser properties</a> for usable properties + * @since ant 1.6.2 + */ + public static final class Property { + + private String name; + private String value; + /** + * accessor to the name of the property + * @return name of the property + */ + public String getName() { + return name; + } + /** + * setter for the name of the property + * @param name name of the property + */ + public void setName(String name) { + this.name = name; + } + + /** + * getter for the value of the property + * @return value of the property + */ + public String getValue() { + return value; + } + /** + * sets the value of the property + * @param value value of the property + */ + public void setValue(String value) { + this.value = value; + } + + } // Property + + + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/XSLTTraceSupport.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/XSLTTraceSupport.java new file mode 100644 index 00000000..8b684d83 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/XSLTTraceSupport.java @@ -0,0 +1,32 @@ +/* + * 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.tools.ant.taskdefs.optional; + +import javax.xml.transform.Transformer; + +import org.apache.tools.ant.taskdefs.XSLTProcess; + +/** + * Sets up trace support for a given transformer. + * + * @since Ant 1.8.0 + */ +public interface XSLTTraceSupport { + void configureTrace(Transformer t, XSLTProcess.TraceConfiguration conf); +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Xalan2TraceSupport.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Xalan2TraceSupport.java new file mode 100644 index 00000000..e5da0185 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Xalan2TraceSupport.java @@ -0,0 +1,54 @@ +/* + * 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.tools.ant.taskdefs.optional; + +import java.io.PrintWriter; +import java.util.TooManyListenersException; + +import javax.xml.transform.Transformer; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.XSLTProcess; +import org.apache.xalan.trace.PrintTraceListener; +import org.apache.xalan.transformer.TransformerImpl; + +/** + * Sets up trace support for a given transformer. + * + * @since Ant 1.8.0 + */ +public class Xalan2TraceSupport implements XSLTTraceSupport { + public void configureTrace(final Transformer t, + final XSLTProcess.TraceConfiguration conf) { + if (t instanceof TransformerImpl && conf != null) { + final PrintWriter w = new PrintWriter(conf.getOutputStream(), false); + final PrintTraceListener tl = new PrintTraceListener(w); + tl.m_traceElements = conf.getElements(); + tl.m_traceExtension = conf.getExtension(); + tl.m_traceGeneration = conf.getGeneration(); + tl.m_traceSelection = conf.getSelection(); + tl.m_traceTemplates = conf.getTemplates(); + try { + ((TransformerImpl) t).getTraceManager().addTraceListener(tl); + } catch (final TooManyListenersException tml) { + throw new BuildException(tml); + } + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheck.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheck.java new file mode 100644 index 00000000..f6a94b52 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheck.java @@ -0,0 +1,208 @@ +/* + * 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.tools.ant.taskdefs.optional.ccm; + + +import java.io.File; +import java.util.Vector; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.FileSet; + + +/** + * Class common to all check commands (checkout, checkin,checkin default task); + * @ant.task ignore="true" + */ +public class CCMCheck extends Continuus { + + private File file = null; + private String comment = null; + private String task = null; + + // CheckStyle:VisibilityModifier OFF - bc + + protected Vector filesets = new Vector(); + + // CheckStyle:VisibilityModifier ON + + /** Constructor for CCMCheck. */ + public CCMCheck() { + super(); + } + + /** + * Get the value of file. + * @return value of file. + */ + public File getFile() { + return file; + } + + /** + * Sets the path to the file that the command will operate on. + * @param v Value to assign to file. + */ + public void setFile(File v) { + log("working file " + v, Project.MSG_VERBOSE); + this.file = v; + } + + /** + * Get the value of comment. + * @return value of comment. + */ + public String getComment() { + return comment; + } + + /** + * Specifies a comment. + * @param v Value to assign to comment. + */ + public void setComment(String v) { + this.comment = v; + } + + + /** + * Get the value of task. + * @return value of task. + */ + public String getTask() { + return task; + } + + /** + * Specifies the task number used to check + * in the file (may use 'default'). + * @param v Value to assign to task. + */ + public void setTask(String v) { + this.task = v; + } + + + /** + * Adds a set of files to copy. + * @param set the set of files + */ + public void addFileset(FileSet set) { + filesets.addElement(set); + } + + + /** + * Executes the task. + * <p> + * Builds a command line to execute ccm and then calls Exec's run method + * to execute the command line. + * </p> + * @throws BuildException on error + */ + public void execute() throws BuildException { + + if (file == null && filesets.size() == 0) { + throw new BuildException( + "Specify at least one source - a file or a fileset."); + } + + if (file != null && file.exists() && file.isDirectory()) { + throw new BuildException("CCMCheck cannot be generated for directories"); + } + + if (file != null && filesets.size() > 0) { + throw new BuildException("Choose between file and fileset !"); + } + + if (getFile() != null) { + doit(); + return; + } + + int sizeofFileSet = filesets.size(); + for (int i = 0; i < sizeofFileSet; i++) { + FileSet fs = (FileSet) filesets.elementAt(i); + DirectoryScanner ds = fs.getDirectoryScanner(getProject()); + String[] srcFiles = ds.getIncludedFiles(); + for (int j = 0; j < srcFiles.length; j++) { + File src = new File(fs.getDir(getProject()), srcFiles[j]); + setFile(src); + doit(); + } + } + } + + /** + * check the file given by getFile(). + */ + private void doit() { + Commandline commandLine = new Commandline(); + + // build the command line from what we got the format is + // ccm co /t .. files + // as specified in the CCM.EXE help + + commandLine.setExecutable(getCcmCommand()); + commandLine.createArgument().setValue(getCcmAction()); + + checkOptions(commandLine); + + int result = run(commandLine); + if (Execute.isFailure(result)) { + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + } + + + /** + * Check the command line options. + */ + private void checkOptions(Commandline cmd) { + if (getComment() != null) { + cmd.createArgument().setValue(FLAG_COMMENT); + cmd.createArgument().setValue(getComment()); + } + + if (getTask() != null) { + cmd.createArgument().setValue(FLAG_TASK); + cmd.createArgument().setValue(getTask()); + } + + if (getFile() != null) { + cmd.createArgument().setValue(file.getAbsolutePath()); + } + } + + /** + * -comment flag -- comment to attach to the file + */ + public static final String FLAG_COMMENT = "/comment"; + + /** + * -task flag -- associate checkout task with task + */ + public static final String FLAG_TASK = "/task"; +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckin.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckin.java new file mode 100644 index 00000000..ff7472c9 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckin.java @@ -0,0 +1,39 @@ +/* + * 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.tools.ant.taskdefs.optional.ccm; + +import java.util.Date; + +/** + * Performs Continuus checkin command. + * + */ +public class CCMCheckin extends CCMCheck { + + /** + * Default constructor - setup checkin command + */ + public CCMCheckin() { + super(); + setCcmAction(COMMAND_CHECKIN); + setComment("Checkin " + new Date()); + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckinDefault.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckinDefault.java new file mode 100644 index 00000000..2fe2a2a1 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckinDefault.java @@ -0,0 +1,38 @@ +/* + * 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.tools.ant.taskdefs.optional.ccm; + +/** + * Performs Continuus Checkin Default task command. + * + * @ant.task name="ccmcheckintask" category="scm" + */ +public class CCMCheckinDefault extends CCMCheck { + + /** Constructor for CCMCheckinDefault. */ + public CCMCheckinDefault() { + super(); + setCcmAction(COMMAND_CHECKIN); + setTask(DEFAULT_TASK); + } + + /** The default task */ + public static final String DEFAULT_TASK = "default"; +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckout.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckout.java new file mode 100644 index 00000000..44bbc6bf --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckout.java @@ -0,0 +1,35 @@ +/* + * 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.tools.ant.taskdefs.optional.ccm; + +/** + * Performs Continuus checkout command. + * + */ +public class CCMCheckout extends CCMCheck { + + /** + * default constructor + */ + public CCMCheckout() { + super(); + setCcmAction(COMMAND_CHECKOUT); + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCreateTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCreateTask.java new file mode 100644 index 00000000..cda13a5c --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCreateTask.java @@ -0,0 +1,335 @@ +/* + * 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.tools.ant.taskdefs.optional.ccm; + + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.taskdefs.ExecuteStreamHandler; +import org.apache.tools.ant.types.Commandline; + + +/** + * Creates new Continuus ccm task and sets it as the default. + * + * @ant.task name="ccmcreatetask" category="scm" + */ +public class CCMCreateTask extends Continuus implements ExecuteStreamHandler { + + private String comment = null; + private String platform = null; + private String resolver = null; + private String release = null; + private String subSystem = null; + private String task = null; + + /** + * Constructor for CCMCreateTask. + */ + public CCMCreateTask() { + super(); + setCcmAction(COMMAND_CREATE_TASK); + } + + + /** + * Executes the task. + * <p> + * Builds a command line to execute ccm and then calls Exec's run method + * to execute the command line. + * </p> + * @throws BuildException on error + */ + public void execute() throws BuildException { + Commandline commandLine = new Commandline(); + int result = 0; + + // build the command line from what we got the format + // as specified in the CCM.EXE help + commandLine.setExecutable(getCcmCommand()); + commandLine.createArgument().setValue(getCcmAction()); + + checkOptions(commandLine); + + result = run(commandLine, this); + if (Execute.isFailure(result)) { + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + + //create task ok, set this task as the default one + Commandline commandLine2 = new Commandline(); + commandLine2.setExecutable(getCcmCommand()); + commandLine2.createArgument().setValue(COMMAND_DEFAULT_TASK); + commandLine2.createArgument().setValue(getTask()); + + log(commandLine.describeCommand(), Project.MSG_DEBUG); + + result = run(commandLine2); + if (result != 0) { + String msg = "Failed executing: " + commandLine2.toString(); + throw new BuildException(msg, getLocation()); + } + + } + + + /** + * Check the command line options. + */ + private void checkOptions(Commandline cmd) { + if (getComment() != null) { + cmd.createArgument().setValue(FLAG_COMMENT); + cmd.createArgument().setValue("\"" + getComment() + "\""); + } + + if (getPlatform() != null) { + cmd.createArgument().setValue(FLAG_PLATFORM); + cmd.createArgument().setValue(getPlatform()); + } // end of if () + + if (getResolver() != null) { + cmd.createArgument().setValue(FLAG_RESOLVER); + cmd.createArgument().setValue(getResolver()); + } // end of if () + + if (getSubSystem() != null) { + cmd.createArgument().setValue(FLAG_SUBSYSTEM); + cmd.createArgument().setValue("\"" + getSubSystem() + "\""); + } // end of if () + + if (getRelease() != null) { + cmd.createArgument().setValue(FLAG_RELEASE); + cmd.createArgument().setValue(getRelease()); + } // end of if () + } + + + /** + * Get the value of comment. + * @return value of comment. + */ + public String getComment() { + return comment; + } + + /** + * Specifies a comment. + * + * @param v Value to assign to comment. + */ + public void setComment(String v) { + this.comment = v; + } + + + /** + * Get the value of platform. + * @return value of platform. + */ + public String getPlatform() { + return platform; + } + + /** + * Specifies the target platform. + * + * @param v Value to assign to platform. + */ + public void setPlatform(String v) { + this.platform = v; + } + + + /** + * Get the value of resolver. + * @return value of resolver. + */ + public String getResolver() { + return resolver; + } + + /** + * Specifies the resolver. + * + * @param v Value to assign to resolver. + */ + public void setResolver(String v) { + this.resolver = v; + } + + + /** + * Get the value of release. + * @return value of release. + */ + public String getRelease() { + return release; + } + + /** + * Specify the CCM release. + * + * @param v Value to assign to release. + */ + public void setRelease(String v) { + this.release = v; + } + + /** + * Get the value of subSystem. + * @return value of subSystem. + */ + public String getSubSystem() { + return subSystem; + } + + /** + * Specifies the subsystem. + * + * @param v Value to assign to subSystem. + */ + public void setSubSystem(String v) { + this.subSystem = v; + } + + + /** + * Get the value of task. + * @return value of task. + */ + public String getTask() { + return task; + } + + /** + * Specifies the task number used to checkin + * the file (may use 'default'). + * + * @param v Value to assign to task. + */ + public void setTask(String v) { + this.task = v; + } + + /** + * /comment -- comments associated to the task + */ + public static final String FLAG_COMMENT = "/synopsis"; + + /** + * /platform flag -- target platform + */ + public static final String FLAG_PLATFORM = "/plat"; + + /** + * /resolver flag + */ + public static final String FLAG_RESOLVER = "/resolver"; + + /** + * /release flag + */ + public static final String FLAG_RELEASE = "/release"; + + /** + * /release flag + */ + public static final String FLAG_SUBSYSTEM = "/subsystem"; + + /** + * -task flag -- associate checkout task with task + */ + public static final String FLAG_TASK = "/task"; + + + // implementation of org.apache.tools.ant.taskdefs.ExecuteStreamHandler interface + + /** + * + * @throws IOException on error + */ + public void start() throws IOException { + } + + /** + * + */ + public void stop() { + } + + /** + * + * @param param1 the output stream + * @exception java.io.IOException on error + */ + public void setProcessInputStream(OutputStream param1) throws IOException { + } + + /** + * + * @param is the input stream + * @exception java.io.IOException on error + */ + public void setProcessErrorStream(InputStream is) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + String s = reader.readLine(); + if (s != null) { + log("err " + s, Project.MSG_DEBUG); + } // end of if () + } + + /** + * read the output stream to retrieve the new task number. + * @param is InputStream + * @throws IOException on error + */ + public void setProcessOutputStream(InputStream is) throws IOException { + + String buffer = ""; + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + buffer = reader.readLine(); + if (buffer != null) { + log("buffer:" + buffer, Project.MSG_DEBUG); + String taskstring = buffer.substring(buffer.indexOf(' ')).trim(); + taskstring = taskstring.substring(0, taskstring.lastIndexOf(' ')).trim(); + setTask(taskstring); + log("task is " + getTask(), Project.MSG_DEBUG); + } // end of if () + } catch (NullPointerException npe) { + log("error procession stream , null pointer exception", Project.MSG_ERR); + npe.printStackTrace(); + throw new BuildException(npe.getClass().getName()); + } catch (Exception e) { + log("error procession stream " + e.getMessage(), Project.MSG_ERR); + throw new BuildException(e.getMessage()); + } // end of try-catch + + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMReconfigure.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMReconfigure.java new file mode 100644 index 00000000..28f80645 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMReconfigure.java @@ -0,0 +1,159 @@ +/* + * 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.tools.ant.taskdefs.optional.ccm; + + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.types.Commandline; + + +/** + * Task allows to reconfigure a project, recursively or not + */ +public class CCMReconfigure extends Continuus { + + private String ccmProject = null; + private boolean recurse = false; + private boolean verbose = false; + + /** Constructor for CCMReconfigure. */ + public CCMReconfigure() { + super(); + setCcmAction(COMMAND_RECONFIGURE); + } + + + /** + * Executes the task. + * <p> + * Builds a command line to execute ccm and then calls Exec's run method + * to execute the command line. + * </p> + * @throws BuildException on error + */ + public void execute() throws BuildException { + Commandline commandLine = new Commandline(); + int result = 0; + + // build the command line from what we got the format + // as specified in the CCM.EXE help + commandLine.setExecutable(getCcmCommand()); + commandLine.createArgument().setValue(getCcmAction()); + + checkOptions(commandLine); + + result = run(commandLine); + if (Execute.isFailure(result)) { + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + } + + + /** + * Check the command line options. + */ + private void checkOptions(Commandline cmd) { + + if (isRecurse()) { + cmd.createArgument().setValue(FLAG_RECURSE); + } // end of if () + + if (isVerbose()) { + cmd.createArgument().setValue(FLAG_VERBOSE); + } // end of if () + + if (getCcmProject() != null) { + cmd.createArgument().setValue(FLAG_PROJECT); + cmd.createArgument().setValue(getCcmProject()); + } + + } + + /** + * Get the value of project. + * @return value of project. + */ + public String getCcmProject() { + return ccmProject; + } + + /** + * Sets the ccm project on which the operation is applied. + * @param v Value to assign to project. + */ + public void setCcmProject(String v) { + this.ccmProject = v; + } + + + /** + * Get the value of recurse. + * @return value of recurse. + */ + public boolean isRecurse() { + return recurse; + } + + /** + * If true, recurse on subproject (default false). + * + * @param v Value to assign to recurse. + */ + public void setRecurse(boolean v) { + this.recurse = v; + } + + + /** + * Get the value of verbose. + * @return value of verbose. + */ + public boolean isVerbose() { + return verbose; + } + + /** + * If true, do a verbose reconfigure operation (default false). + * @param v Value to assign to verbose. + */ + public void setVerbose(boolean v) { + this.verbose = v; + } + + + /** + * /recurse -- + */ + public static final String FLAG_RECURSE = "/recurse"; + + /** + * /recurse -- + */ + public static final String FLAG_VERBOSE = "/verbose"; + + + /** + * /project flag -- target project + */ + public static final String FLAG_PROJECT = "/project"; + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/Continuus.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/Continuus.java new file mode 100644 index 00000000..5618dd6a --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/Continuus.java @@ -0,0 +1,144 @@ +/* + * 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.tools.ant.taskdefs.optional.ccm; + +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.LogStreamHandler; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.util.FileUtils; + + +/** + * A base class for creating tasks for executing commands on Continuus 5.1. + * <p> + * The class extends the task as it operates by executing the ccm.exe program + * supplied with Continuus/Synergy. By default the task expects the ccm executable to be + * in the path, + * you can override this be specifying the ccmdir attribute. + * </p> + * + */ +public abstract class Continuus extends Task { + + private String ccmDir = ""; + private String ccmAction = ""; + + /** + * Get the value of ccmAction. + * @return value of ccmAction. + */ + public String getCcmAction() { + return ccmAction; + } + + /** + * Set the value of ccmAction. + * @param v Value to assign to ccmAction. + * @ant.attribute ignore="true" + */ + public void setCcmAction(String v) { + this.ccmAction = v; + } + + + /** + * Set the directory where the ccm executable is located. + * + * @param dir the directory containing the ccm executable + */ + public final void setCcmDir(String dir) { + ccmDir = FileUtils.translatePath(dir); + } + + /** + * Builds and returns the command string to execute ccm + * @return String containing path to the executable + */ + protected final String getCcmCommand() { + String toReturn = ccmDir; + if (!toReturn.equals("") && !toReturn.endsWith("/")) { + toReturn += "/"; + } + + toReturn += CCM_EXE; + + return toReturn; + } + + + /** + * Run the command. + * @param cmd the command line + * @param handler an execute stream handler + * @return the exit status of the command + */ + protected int run(Commandline cmd, ExecuteStreamHandler handler) { + try { + Execute exe = new Execute(handler); + exe.setAntRun(getProject()); + exe.setWorkingDirectory(getProject().getBaseDir()); + exe.setCommandline(cmd.getCommandline()); + return exe.execute(); + } catch (java.io.IOException e) { + throw new BuildException(e, getLocation()); + } + } + + /** + * Run the command. + * @param cmd the command line + * @return the exit status of the command + */ + protected int run(Commandline cmd) { + return run(cmd, new LogStreamHandler(this, Project.MSG_VERBOSE, Project.MSG_WARN)); + } + + /** + * Constant for the thing to execute + */ + private static final String CCM_EXE = "ccm"; + + /** + * The 'CreateTask' command + */ + public static final String COMMAND_CREATE_TASK = "create_task"; + /** + * The 'Checkout' command + */ + public static final String COMMAND_CHECKOUT = "co"; + /** + * The 'Checkin' command + */ + public static final String COMMAND_CHECKIN = "ci"; + /** + * The 'Reconfigure' command + */ + public static final String COMMAND_RECONFIGURE = "reconfigure"; + + /** + * The 'Reconfigure' command + */ + public static final String COMMAND_DEFAULT_TASK = "default_task"; + + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckin.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckin.java new file mode 100644 index 00000000..371d418b --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckin.java @@ -0,0 +1,343 @@ +/* + * 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.tools.ant.taskdefs.optional.clearcase; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.types.Commandline; + +/** + * Performs ClearCase checkin. + * + * <p> + * The following attributes are interpreted: + * <table border="1"> + * <tr> + * <th>Attribute</th> + * <th>Values</th> + * <th>Required</th> + * </tr> + * <tr> + * <td>viewpath</td> + * <td>Path to the ClearCase view file or directory that the command will operate on</td> + * <td>No</td> + * </tr> + * <tr> + * <td>comment</td> + * <td>Specify a comment. Only one of comment or cfile may be used.</td> + * <td>No</td> + * </tr> + * <tr> + * <td>commentfile</td> + * <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td> + * <td>No</td> + * </tr> + * <tr> + * <td>nowarn</td> + * <td>Suppress warning messages</td> + * <td>No</td> + * </tr> + * <tr> + * <td>preservetime</td> + * <td>Preserve the modification time</td> + * <td>No</td> + * </tr> + * <tr> + * <td>keepcopy</td> + * <td>Keeps a copy of the file with a .keep extension</td> + * <td>No</td> + * </tr> + * <tr> + * <td>identical</td> + * <td>Allows the file to be checked in even if it is identical to the original</td> + * <td>No</td> + * </tr> + * <tr> + * <td>failonerr</td> + * <td>Throw an exception if the command fails. Default is true</td> + * <td>No</td> + * </tr> + * </table> + * + */ +public class CCCheckin extends ClearCase { + private String mComment = null; + private String mCfile = null; + private boolean mNwarn = false; + private boolean mPtime = false; + private boolean mKeep = false; + private boolean mIdentical = true; + + /** + * Executes the task. + * <p> + * Builds a command line to execute cleartool and then calls Exec's run method + * to execute the command line. + * @throws BuildException if the command fails and failonerr is set to true + */ + public void execute() throws BuildException { + Commandline commandLine = new Commandline(); + Project aProj = getProject(); + int result = 0; + + // Default the viewpath to basedir if it is not specified + if (getViewPath() == null) { + setViewPath(aProj.getBaseDir().getPath()); + } + + // build the command line from what we got. the format is + // cleartool checkin [options...] [viewpath ...] + // as specified in the CLEARTOOL.EXE help + commandLine.setExecutable(getClearToolCommand()); + commandLine.createArgument().setValue(COMMAND_CHECKIN); + + checkOptions(commandLine); + + if (!getFailOnErr()) { + getProject().log("Ignoring any errors that occur for: " + + getViewPathBasename(), Project.MSG_VERBOSE); + } + result = run(commandLine); + if (Execute.isFailure(result) && getFailOnErr()) { + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + } + + + /** + * Check the command line options. + */ + private void checkOptions(Commandline cmd) { + if (getComment() != null) { + // -c + getCommentCommand(cmd); + } else { + if (getCommentFile() != null) { + // -cfile + getCommentFileCommand(cmd); + } else { + cmd.createArgument().setValue(FLAG_NOCOMMENT); + } + } + + if (getNoWarn()) { + // -nwarn + cmd.createArgument().setValue(FLAG_NOWARN); + } + + if (getPreserveTime()) { + // -ptime + cmd.createArgument().setValue(FLAG_PRESERVETIME); + } + + if (getKeepCopy()) { + // -keep + cmd.createArgument().setValue(FLAG_KEEPCOPY); + } + + if (getIdentical()) { + // -identical + cmd.createArgument().setValue(FLAG_IDENTICAL); + } + + // viewpath + cmd.createArgument().setValue(getViewPath()); + } + + + /** + * Sets the comment string. + * + * @param comment the comment string + */ + public void setComment(String comment) { + mComment = comment; + } + + /** + * Get comment string + * + * @return String containing the comment + */ + public String getComment() { + return mComment; + } + + /** + * Specifies a file containing a comment. + * + * @param cfile the path to the comment file + */ + public void setCommentFile(String cfile) { + mCfile = cfile; + } + + /** + * Get comment file + * + * @return String containing the path to the comment file + */ + public String getCommentFile() { + return mCfile; + } + + /** + * If true, suppress warning messages. + * + * @param nwarn the status to set the flag to + */ + public void setNoWarn(boolean nwarn) { + mNwarn = nwarn; + } + + /** + * Get nowarn flag status + * + * @return boolean containing status of nwarn flag + */ + public boolean getNoWarn() { + return mNwarn; + } + + /** + * If true, preserve the modification time. + * + * @param ptime the status to set the flag to + */ + public void setPreserveTime(boolean ptime) { + mPtime = ptime; + } + + /** + * Get preservetime flag status + * + * @return boolean containing status of preservetime flag + */ + public boolean getPreserveTime() { + return mPtime; + } + + /** + * If true, keeps a copy of the file with a .keep extension. + * + * @param keep the status to set the flag to + */ + public void setKeepCopy(boolean keep) { + mKeep = keep; + } + + /** + * Get keepcopy flag status + * + * @return boolean containing status of keepcopy flag + */ + public boolean getKeepCopy() { + return mKeep; + } + + /** + * If true, allows the file to be checked in even + * if it is identical to the original. + * + * @param identical the status to set the flag to + */ + public void setIdentical(boolean identical) { + mIdentical = identical; + } + + /** + * Get identical flag status + * + * @return boolean containing status of identical flag + */ + public boolean getIdentical() { + return mIdentical; + } + + + /** + * Get the 'comment' command + * + * @param cmd containing the command line string with or + * without the comment flag and string appended + */ + private void getCommentCommand(Commandline cmd) { + if (getComment() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENT); + cmd.createArgument().setValue(getComment()); + } + } + + /** + * Get the 'commentfile' command + * + * @param cmd containing the command line string with or + * without the commentfile flag and file appended + */ + private void getCommentFileCommand(Commandline cmd) { + if (getCommentFile() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENTFILE); + cmd.createArgument().setValue(getCommentFile()); + } + } + + + /** + * -c flag -- comment to attach to the file + */ + public static final String FLAG_COMMENT = "-c"; + /** + * -cfile flag -- file containing a comment to attach to the file + */ + public static final String FLAG_COMMENTFILE = "-cfile"; + /** + * -nc flag -- no comment is specified + */ + public static final String FLAG_NOCOMMENT = "-nc"; + /** + * -nwarn flag -- suppresses warning messages + */ + public static final String FLAG_NOWARN = "-nwarn"; + /** + * -ptime flag -- preserves the modification time + */ + public static final String FLAG_PRESERVETIME = "-ptime"; + /** + * -keep flag -- keeps a copy of the file with a .keep extension + */ + public static final String FLAG_KEEPCOPY = "-keep"; + /** + * -identical flag -- allows the file to be checked in even if it is identical to the original + */ + public static final String FLAG_IDENTICAL = "-identical"; + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckout.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckout.java new file mode 100644 index 00000000..0eaf09ac --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckout.java @@ -0,0 +1,516 @@ +/* + * 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.tools.ant.taskdefs.optional.clearcase; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.types.Commandline; + + +/** + * Performs ClearCase checkout. + * + * <p> + * The following attributes are interpreted: + * <table border="1"> + * <tr> + * <th>Attribute</th> + * <th>Values</th> + * <th>Required</th> + * </tr> + * <tr> + * <td>viewpath</td> + * <td>Path to the ClearCase view file or directory that the command will operate on</td> + * <td>No</td> + * <tr> + * <tr> + * <td>reserved</td> + * <td>Specifies whether to check out the file as reserved or not</td> + * <td>Yes</td> + * <tr> + * <tr> + * <td>out</td> + * <td>Creates a writable file under a different filename</td> + * <td>No</td> + * <tr> + * <tr> + * <td>nodata</td> + * <td>Checks out the file but does not create an editable file containing its data</td> + * <td>No</td> + * <tr> + * <tr> + * <td>branch</td> + * <td>Specify a branch to check out the file to</td> + * <td>No</td> + * <tr> + * <tr> + * <td>version</td> + * <td>Allows checkout of a version other than main latest</td> + * <td>No</td> + * <tr> + * <tr> + * <td>nowarn</td> + * <td>Suppress warning messages</td> + * <td>No</td> + * <tr> + * <tr> + * <td>comment</td> + * <td>Specify a comment. Only one of comment or cfile may be used.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>commentfile</td> + * <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>notco</td> + * <td>Fail if it's already checked out to the current view. Set to false to ignore it.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>failonerr</td> + * <td>Throw an exception if the command fails. Default is true</td> + * <td>No</td> + * <tr> + * </table> + * + */ +public class CCCheckout extends ClearCase { + private boolean mReserved = true; + private String mOut = null; + private boolean mNdata = false; + private String mBranch = null; + private boolean mVersion = false; + private boolean mNwarn = false; + private String mComment = null; + private String mCfile = null; + private boolean mNotco = true; + + /** + * Executes the task. + * <p> + * Builds a command line to execute cleartool and then calls Exec's run method + * to execute the command line. + * @throws BuildException if the command fails and failonerr is set to true + */ + public void execute() throws BuildException { + Commandline commandLine = new Commandline(); + Project aProj = getProject(); + int result = 0; + + // Default the viewpath to basedir if it is not specified + if (getViewPath() == null) { + setViewPath(aProj.getBaseDir().getPath()); + } + + // build the command line from what we got the format is + // cleartool checkout [options...] [viewpath ...] + // as specified in the CLEARTOOL.EXE help + commandLine.setExecutable(getClearToolCommand()); + commandLine.createArgument().setValue(COMMAND_CHECKOUT); + + checkOptions(commandLine); + /* + * If configured to not care about whether the element is + * already checked out to the current view. + * Then check to see if it is checked out. + */ + if (!getNotco() && lsCheckout()) { + getProject().log("Already checked out in this view: " + + getViewPathBasename(), Project.MSG_VERBOSE); + return; + } + if (!getFailOnErr()) { + getProject().log("Ignoring any errors that occur for: " + + getViewPathBasename(), Project.MSG_VERBOSE); + } + result = run(commandLine); + if (Execute.isFailure(result) && getFailOnErr()) { + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + } + + /** + * Check to see if the element is checked out in the current view. + */ + private boolean lsCheckout() { + Commandline cmdl = new Commandline(); + String result; + + // build the command line from what we got the format is + // cleartool lsco [options...] [viewpath ...] + // as specified in the CLEARTOOL.EXE help + cmdl.setExecutable(getClearToolCommand()); + cmdl.createArgument().setValue(COMMAND_LSCO); + cmdl.createArgument().setValue("-cview"); + cmdl.createArgument().setValue("-short"); + cmdl.createArgument().setValue("-d"); + // viewpath + cmdl.createArgument().setValue(getViewPath()); + + result = runS(cmdl); + + // System.out.println( "lsCheckout: " + result ); + + return (result != null && result.length() > 0) ? true : false; + } + /** + * Check the command line options. + */ + private void checkOptions(Commandline cmd) { + // ClearCase items + if (getReserved()) { + // -reserved + cmd.createArgument().setValue(FLAG_RESERVED); + } else { + // -unreserved + cmd.createArgument().setValue(FLAG_UNRESERVED); + } + + if (getOut() != null) { + // -out + getOutCommand(cmd); + } else { + if (getNoData()) { + // -ndata + cmd.createArgument().setValue(FLAG_NODATA); + } + + } + + if (getBranch() != null) { + // -branch + getBranchCommand(cmd); + } else { + if (getVersion()) { + // -version + cmd.createArgument().setValue(FLAG_VERSION); + } + + } + + if (getNoWarn()) { + // -nwarn + cmd.createArgument().setValue(FLAG_NOWARN); + } + + if (getComment() != null) { + // -c + getCommentCommand(cmd); + } else { + if (getCommentFile() != null) { + // -cfile + getCommentFileCommand(cmd); + } else { + cmd.createArgument().setValue(FLAG_NOCOMMENT); + } + } + + // viewpath + cmd.createArgument().setValue(getViewPath()); + + // Print out info about the notco option + // System.out.println( "Notco: " + (getNotco() ? "yes" : "no") ); + } + + /** + * If true, checks out the file as reserved. + * + * @param reserved the status to set the flag to + */ + public void setReserved(boolean reserved) { + mReserved = reserved; + } + + /** + * Get reserved flag status + * + * @return boolean containing status of reserved flag + */ + public boolean getReserved() { + return mReserved; + } + + /** + * If true, checkout fails if the element is already checked out to the current view. + * + * @param notco the status to set the flag to + * @since ant 1.6.1 + */ + public void setNotco(boolean notco) { + mNotco = notco; + } + + /** + * Get notco flag status + * + * @return boolean containing status of notco flag + * @since ant 1.6.1 + */ + public boolean getNotco() { + return mNotco; + } + + + /** + * Creates a writable file under a different filename. + * + * @param outf the path to the out file + */ + public void setOut(String outf) { + mOut = outf; + } + + /** + * Get out file + * + * @return String containing the path to the out file + */ + public String getOut() { + return mOut; + } + + /** + * If true, checks out the file but does not create an + * editable file containing its data. + * + * @param ndata the status to set the flag to + */ + public void setNoData(boolean ndata) { + mNdata = ndata; + } + + /** + * Get nodata flag status + * + * @return boolean containing status of ndata flag + */ + public boolean getNoData() { + return mNdata; + } + + /** + * Specify a branch to check out the file to. + * + * @param branch the name of the branch + */ + public void setBranch(String branch) { + mBranch = branch; + } + + /** + * Get branch name + * + * @return String containing the name of the branch + */ + public String getBranch() { + return mBranch; + } + + /** + * If true, allows checkout of a version other than main latest. + * + * @param version the status to set the flag to + */ + public void setVersion(boolean version) { + mVersion = version; + } + + /** + * Get version flag status + * + * @return boolean containing status of version flag + */ + public boolean getVersion() { + return mVersion; + } + + /** + * If true, warning messages are suppressed. + * + * @param nwarn the status to set the flag to + */ + public void setNoWarn(boolean nwarn) { + mNwarn = nwarn; + } + + /** + * Get nowarn flag status + * + * @return boolean containing status of nwarn flag + */ + public boolean getNoWarn() { + return mNwarn; + } + + /** + * Sets the comment string. + * + * @param comment the comment string + */ + public void setComment(String comment) { + mComment = comment; + } + + /** + * Get comment string + * + * @return String containing the comment + */ + public String getComment() { + return mComment; + } + + /** + * Specifies a file containing a comment. + * + * @param cfile the path to the comment file + */ + public void setCommentFile(String cfile) { + mCfile = cfile; + } + + /** + * Get comment file + * + * @return String containing the path to the comment file + */ + public String getCommentFile() { + return mCfile; + } + + /** + * Get the 'out' command + * + * @param cmd containing the command line string with or + * without the out flag and path appended + */ + private void getOutCommand(Commandline cmd) { + if (getOut() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_OUT); + cmd.createArgument().setValue(getOut()); + } + } + + /** + * Get the 'branch' command + * + * @param cmd containing the command line string with or + without the branch flag and name appended + */ + private void getBranchCommand(Commandline cmd) { + if (getBranch() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_BRANCH); + cmd.createArgument().setValue(getBranch()); + } + } + + + /** + * Get the 'comment' command + * + * @param cmd containing the command line string with or + * without the comment flag and string appended + */ + private void getCommentCommand(Commandline cmd) { + if (getComment() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENT); + cmd.createArgument().setValue(getComment()); + } + } + + /** + * Get the 'cfile' command + * + * @param cmd containing the command line string with or + * without the cfile flag and file appended + */ + private void getCommentFileCommand(Commandline cmd) { + if (getCommentFile() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENTFILE); + cmd.createArgument().setValue(getCommentFile()); + } + } + + /** + * -reserved flag -- check out the file as reserved + */ + public static final String FLAG_RESERVED = "-reserved"; + /** + * -reserved flag -- check out the file as unreserved + */ + public static final String FLAG_UNRESERVED = "-unreserved"; + /** + * -out flag -- create a writable file under a different filename + */ + public static final String FLAG_OUT = "-out"; + /** + * -ndata flag -- checks out the file but does not create an editable file containing its data + */ + public static final String FLAG_NODATA = "-ndata"; + /** + * -branch flag -- checks out the file on a specified branch + */ + public static final String FLAG_BRANCH = "-branch"; + /** + * -version flag -- allows checkout of a version that is not main latest + */ + public static final String FLAG_VERSION = "-version"; + /** + * -nwarn flag -- suppresses warning messages + */ + public static final String FLAG_NOWARN = "-nwarn"; + /** + * -c flag -- comment to attach to the file + */ + public static final String FLAG_COMMENT = "-c"; + /** + * -cfile flag -- file containing a comment to attach to the file + */ + public static final String FLAG_COMMENTFILE = "-cfile"; + /** + * -nc flag -- no comment is specified + */ + public static final String FLAG_NOCOMMENT = "-nc"; + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCLock.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCLock.java new file mode 100644 index 00000000..c273554e --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCLock.java @@ -0,0 +1,378 @@ +/* + * 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.tools.ant.taskdefs.optional.clearcase; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.types.Commandline; + + +/** + * TODO: + * comment field doesn't include all options yet + */ + + + +/** + * Performs a ClearCase Lock command. + * + * <p> + * The following attributes are interpreted: + * <table border="1"> + * <tr> + * <th>Attribute</th> + * <th>Values</th> + * <th>Required</th> + * </tr> + * <tr> + * <td>replace</td> + * <td>Specifies replacing an existing lock</td> + * <td>No</td> + * <tr> + * <tr> + * <td>nusers</td> + * <td>Specifies user(s) who can still modify the object/pname</td> + * <td>No</td> + * <tr> + * <tr> + * <td>obsolete</td> + * <td>Specifies that the object/pname should be marked obsolete</td> + * <td>No</td> + * <tr> + * <tr> + * <td>comment</td> + * <td>Specifies how to populate comments fields</td> + * <td>No</td> + * <tr> + * <tr> + * <td>pname</td> + * <td>Specifies the pathname to be locked.</td> + * <td>No</td> + * <tr> + * <td>objselect</td> + * <td>This variable is obsolete. Should use <i>objsel</i> instead.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>objsel</td> + * <td>Specifies the object(s) to be unlocked.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>failonerr</td> + * <td>Throw an exception if the command fails. Default is true</td> + * <td>No</td> + * <tr> + * </table> + * + */ +public class CCLock extends ClearCase { + private boolean mReplace = false; + private boolean mObsolete = false; + private String mComment = null; + private String mNusers = null; + private String mPname = null; + private String mObjselect = null; + + /** + * Executes the task. + * <p> + * Builds a command line to execute cleartool and then calls Exec's run method + * to execute the command line. + * @throws BuildException if the command fails and failonerr is set to true + */ + public void execute() throws BuildException { + Commandline commandLine = new Commandline(); + Project aProj = getProject(); + int result = 0; + + // Default the viewpath to basedir if it is not specified + if (getViewPath() == null) { + setViewPath(aProj.getBaseDir().getPath()); + } + + // build the command line from what we got the format is + // cleartool lock [options...] + // as specified in the CLEARTOOL.EXE help + commandLine.setExecutable(getClearToolCommand()); + commandLine.createArgument().setValue(COMMAND_LOCK); + + // Check the command line options + checkOptions(commandLine); + + // For debugging + // System.out.println(commandLine.toString()); + + if (!getFailOnErr()) { + getProject().log("Ignoring any errors that occur for: " + + getOpType(), Project.MSG_VERBOSE); + } + result = run(commandLine); + if (Execute.isFailure(result) && getFailOnErr()) { + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + } + + /** + * Check the command line options. + */ +private void checkOptions(Commandline cmd) { + // ClearCase items + if (getReplace()) { + // -replace + cmd.createArgument().setValue(FLAG_REPLACE); + } + if (getObsolete()) { + // -obsolete + cmd.createArgument().setValue(FLAG_OBSOLETE); + } else { + getNusersCommand(cmd); + } + getCommentCommand(cmd); + + if (getObjselect() == null && getPname() == null) { + throw new BuildException("Should select either an element " + + "(pname) or an object (objselect)"); + } + getPnameCommand(cmd); + // object selector + if (getObjselect() != null) { + cmd.createArgument().setValue(getObjselect()); + } +} + + /** + * If true, replace an existing lock. + * + * @param replace the status to set the flag to + */ + public void setReplace(boolean replace) { + mReplace = replace; + } + + /** + * Get replace flag status + * + * @return boolean containing status of replace flag + */ + public boolean getReplace() { + return mReplace; + } + + /** + * If true, mark object as obsolete. + * + * @param obsolete the status to set the flag to + */ + public void setObsolete(boolean obsolete) { + mObsolete = obsolete; + } + + /** + * Get obsolete flag status + * + * @return boolean containing status of obsolete flag + */ + public boolean getObsolete() { + return mObsolete; + } + + /** + * Sets the users who may continue to + * edit the object while it is locked. + * + * @param nusers users excluded from lock + */ + public void setNusers(String nusers) { + mNusers = nusers; + } + + /** + * Get nusers list + * + * @return String containing the list of users excluded from lock + */ + public String getNusers() { + return mNusers; + } + + /** + * Sets how comments should be written + * for the event record(s) + * + * @param comment comment method to use + */ + public void setComment(String comment) { + mComment = comment; + } + + /** + * Get comment method + * + * @return String containing the desired comment method + */ + public String getComment() { + return mComment; + } + + /** + * Sets the pathname to be locked + * + * @param pname pathname to be locked + */ + public void setPname(String pname) { + mPname = pname; + } + + /** + * Get the pathname to be locked + * + * @return String containing the pathname to be locked + */ + public String getPname() { + return mPname; + } + + /** + * Sets the object(s) to be locked + * + * @param objsel objects to be locked + * @since ant 1.6.1 + */ + public void setObjSel(String objsel) { + mObjselect = objsel; + } + + /** + * Sets the object(s) to be locked + * + * @param objselect objects to be locked + */ + public void setObjselect(String objselect) { + mObjselect = objselect; + } + + /** + * Get list of objects to be locked + * + * @return String containing the objects to be locked + */ + public String getObjselect() { + return mObjselect; + } + + /** + * Get the 'nusers' command + * + * @param cmd containing the command line string with or + * without the nusers flag and value appended + */ + private void getNusersCommand(Commandline cmd) { + if (getNusers() == null) { + return; + } else { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_NUSERS); + cmd.createArgument().setValue(getNusers()); + } + } + + /** + * Get the 'comment' command + * + * @param cmd containing the command line string with or without the + * comment flag and value appended + */ + private void getCommentCommand(Commandline cmd) { + if (getComment() == null) { + return; + } else { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENT); + cmd.createArgument().setValue(getComment()); + } + } + + /** + * Get the 'pname' command + * + * @param cmd containing the command line string with or + * without the pname flag and value appended + */ + private void getPnameCommand(Commandline cmd) { + if (getPname() == null) { + return; + } else { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_PNAME); + cmd.createArgument().setValue(getPname()); + } + } + + /** + * Return which object/pname is being operated on + * + * @return String containing the object/pname being worked on + */ + private String getOpType() { + + if (getPname() != null) { + return getPname(); + } else { + return getObjselect(); + } + } + + /** + * -replace flag -- replace existing lock on object(s) + */ + public static final String FLAG_REPLACE = "-replace"; + /** + * -nusers flag -- list of users to exclude from lock + */ + public static final String FLAG_NUSERS = "-nusers"; + /** + * -obsolete flag -- mark locked object as obsolete + */ + public static final String FLAG_OBSOLETE = "-obsolete"; + /** + * -comment flag -- method to use for commenting events + */ + public static final String FLAG_COMMENT = "-comment"; + /** + * -pname flag -- pathname to lock + */ + public static final String FLAG_PNAME = "-pname"; +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkattr.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkattr.java new file mode 100644 index 00000000..128ea16b --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkattr.java @@ -0,0 +1,425 @@ +/* + * 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.tools.ant.taskdefs.optional.clearcase; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.taskdefs.condition.Os; +import org.apache.tools.ant.types.Commandline; + +/** + * Task to perform mkattr command to ClearCase. + * <p> + * The following attributes are interpreted: + * <table border="1"> + * <tr> + * <th>Attribute</th> + * <th>Values</th> + * <th>Required</th> + * </tr> + * <tr> + * <td>viewpath</td> + * <td>Path to the ClearCase view file or directory that the command will operate on</td> + * <td>Yes</td> + * <tr> + * <tr> + * <td>replace</td> + * <td>Replace the value of the attribute if it already exists</td> + * <td>No</td> + * <tr> + * <tr> + * <td>recurse</td> + * <td>Process each subdirectory under viewpath</td> + * <td>No</td> + * <tr> + * <tr> + * <td>version</td> + * <td>Identify a specific version to attach the attribute to</td> + * <td>No</td> + * <tr> + * <tr> + * <td>typename</td> + * <td>Name of the attribute type</td> + * <td>Yes</td> + * <tr> + * <tr> + * <td>typevalue</td> + * <td>Value to attach to the attribute type</td> + * <td>Yes</td> + * <tr> + * <tr> + * <td>comment</td> + * <td>Specify a comment. Only one of comment or cfile may be used.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>commentfile</td> + * <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>failonerr</td> + * <td>Throw an exception if the command fails. Default is true</td> + * <td>No</td> + * <tr> + * </table> + * + */ +public class CCMkattr extends ClearCase { + private boolean mReplace = false; + private boolean mRecurse = false; + private String mVersion = null; + private String mTypeName = null; + private String mTypeValue = null; + private String mComment = null; + private String mCfile = null; + + /** + * Executes the task. + * <p> + * Builds a command line to execute cleartool and then calls Exec's run method + * to execute the command line. + * @throws BuildException if the command fails and failonerr is set to true + */ + public void execute() throws BuildException { + Commandline commandLine = new Commandline(); + Project aProj = getProject(); + int result = 0; + + // Check for required attributes + if (getTypeName() == null) { + throw new BuildException("Required attribute TypeName not specified"); + } + if (getTypeValue() == null) { + throw new BuildException("Required attribute TypeValue not specified"); + } + // Default the viewpath to basedir if it is not specified + if (getViewPath() == null) { + setViewPath(aProj.getBaseDir().getPath()); + } + + // build the command line from what we got. the format is + // cleartool mkattr [options...] [viewpath ...] + // as specified in the CLEARTOOL help + commandLine.setExecutable(getClearToolCommand()); + commandLine.createArgument().setValue(COMMAND_MKATTR); + + checkOptions(commandLine); + + if (!getFailOnErr()) { + getProject().log("Ignoring any errors that occur for: " + + getViewPathBasename(), Project.MSG_VERBOSE); + } + + // For debugging + // System.out.println(commandLine.toString()); + + result = run(commandLine); + if (Execute.isFailure(result) && getFailOnErr()) { + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + } + + + /** + * Check the command line options. + */ + private void checkOptions(Commandline cmd) { + if (getReplace()) { + // -replace + cmd.createArgument().setValue(FLAG_REPLACE); + } + + if (getRecurse()) { + // -recurse + cmd.createArgument().setValue(FLAG_RECURSE); + } + + if (getVersion() != null) { + // -version + getVersionCommand(cmd); + } + + if (getComment() != null) { + // -c + getCommentCommand(cmd); + } else { + if (getCommentFile() != null) { + // -cfile + getCommentFileCommand(cmd); + } else { + cmd.createArgument().setValue(FLAG_NOCOMMENT); + } + } + + if (getTypeName() != null) { + // type + getTypeCommand(cmd); + } + if (getTypeValue() != null) { + // type value + getTypeValueCommand(cmd); + } + // viewpath + cmd.createArgument().setValue(getViewPath()); + } + + + /** + * Set the replace flag + * + * @param replace the status to set the flag to + */ + public void setReplace(boolean replace) { + mReplace = replace; + } + + /** + * Get replace flag status + * + * @return boolean containing status of replace flag + */ + public boolean getReplace() { + return mReplace; + } + + /** + * Set recurse flag + * + * @param recurse the status to set the flag to + */ + public void setRecurse(boolean recurse) { + mRecurse = recurse; + } + + /** + * Get recurse flag status + * + * @return boolean containing status of recurse flag + */ + public boolean getRecurse() { + return mRecurse; + } + + /** + * Set the version flag + * + * @param version the status to set the flag to + */ + public void setVersion(String version) { + mVersion = version; + } + + /** + * Get version flag status + * + * @return boolean containing status of version flag + */ + public String getVersion() { + return mVersion; + } + + /** + * Set comment string + * + * @param comment the comment string + */ + public void setComment(String comment) { + mComment = comment; + } + + /** + * Get comment string + * + * @return String containing the comment + */ + public String getComment() { + return mComment; + } + + /** + * Set comment file + * + * @param cfile the path to the comment file + */ + public void setCommentFile(String cfile) { + mCfile = cfile; + } + + /** + * Get comment file + * + * @return String containing the path to the comment file + */ + public String getCommentFile() { + return mCfile; + } + + /** + * Set the attribute type-name + * + * @param tn the type name + */ + public void setTypeName(String tn) { + mTypeName = tn; + } + + /** + * Get attribute type-name + * + * @return String containing type name + */ + public String getTypeName() { + return mTypeName; + } + + /** + * Set the attribute type-value + * + * @param tv the type value + */ + public void setTypeValue(String tv) { + mTypeValue = tv; + } + + /** + * Get the attribute type-value + * + * @return String containing type value + */ + public String getTypeValue() { + return mTypeValue; + } + + + /** + * Get the 'version' command + * + * @param cmd CommandLine containing the command line string with or + * without the version flag and string appended + */ + private void getVersionCommand(Commandline cmd) { + if (getVersion() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_VERSION); + cmd.createArgument().setValue(getVersion()); + } + } + + /** + * Get the 'comment' command + * + * @param cmd containing the command line string with or + * without the comment flag and string appended + */ + private void getCommentCommand(Commandline cmd) { + if (getComment() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENT); + cmd.createArgument().setValue(getComment()); + } + } + + /** + * Get the 'commentfile' command + * + * @param cmd containing the command line string with or + * without the commentfile flag and file appended + */ + private void getCommentFileCommand(Commandline cmd) { + if (getCommentFile() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENTFILE); + cmd.createArgument().setValue(getCommentFile()); + } + } + + /** + * Get the attribute type-name + * + * @param cmd containing the command line string with or + * without the type-name + */ + private void getTypeCommand(Commandline cmd) { + String typenm = getTypeName(); + + if (typenm != null) { + cmd.createArgument().setValue(typenm); + } + } + + /** + * Get the attribute type-value + * + * @param cmd containing the command line string with or + * without the type-value + */ + private void getTypeValueCommand(Commandline cmd) { + String typevl = getTypeValue(); + + if (typevl != null) { + if (Os.isFamily("windows")) { + typevl = "\\\"" + typevl + "\\\""; // Windows quoting of the value + } else { + typevl = "\"" + typevl + "\""; + } + cmd.createArgument().setValue(typevl); + } + } + + /** + * -replace flag -- replace the existing value of the attribute + */ + public static final String FLAG_REPLACE = "-replace"; + /** + * -recurse flag -- process all subdirectories + */ + public static final String FLAG_RECURSE = "-recurse"; + /** + * -version flag -- attach attribute to specified version + */ + public static final String FLAG_VERSION = "-version"; + /** + * -c flag -- comment to attach to the element + */ + public static final String FLAG_COMMENT = "-c"; + /** + * -cfile flag -- file containing a comment to attach to the file + */ + public static final String FLAG_COMMENTFILE = "-cfile"; + /** + * -nc flag -- no comment is specified + */ + public static final String FLAG_NOCOMMENT = "-nc"; +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkbl.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkbl.java new file mode 100644 index 00000000..82c96005 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkbl.java @@ -0,0 +1,365 @@ +/* + * 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.tools.ant.taskdefs.optional.clearcase; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.types.Commandline; + +/** + * Task to CreateBaseline command to ClearCase. + * <p> + * The following attributes are interpreted: + * <table border="1"> + * <tr> + * <th>Attribute</th> + * <th>Values</th> + * <th>Required</th> + * </tr> + * <tr> + * <td>comment</td> + * <td>Specify a comment. Only one of comment or cfile may be +used.</td> + * <td>No</td> + * </tr> + * <tr> + * <td>commentfile</td> + * <td>Specify a file containing a comment. Only one of comment or +cfile may be used.</td> + * <td>No</td> + * </tr> + * <tr> + * <td>baselinerootname</td> + * <td>Specify the name to be associated with the baseline.</td> + * <td>Yes</td> + * </tr> + * <tr> + * <td>nowarn</td> + * <td>Suppress warning messages</td> + * <td>No</td> + * <tr> + * <tr> + * <td>identical</td> + * <td>Allows the baseline to be created even if it is identical to the +previous baseline.</td> + * <td>No</td> + * </tr> + * <tr> + * <td>full</td> + * <td>Creates a full baseline.</td> + * <td>No</td> + * </tr> + * <tr> + * <td>nlabel</td> + * <td>Allows the baseline to be created without a label.</td> + * <td>No</td> + * </tr> + * <tr> + * <td>failonerr</td> + * <td>Throw an exception if the command fails. Default is true</td> + * <td>No</td> + * <tr> + * </table> + * + */ +public class CCMkbl extends ClearCase { + private String mComment = null; + private String mCfile = null; + private String mBaselineRootName = null; + private boolean mNwarn = false; + private boolean mIdentical = true; + private boolean mFull = false; + private boolean mNlabel = false; + + + /** + * Executes the task. + * <p> + * Builds a command line to execute cleartool and then calls Exec's run method + * to execute the command line. + * @throws BuildException if the command fails and failonerr is set to true + */ + public void execute() throws BuildException { + Commandline commandLine = new Commandline(); + Project aProj = getProject(); + int result = 0; + + // Default the viewpath to basedir if it is not specified + if (getViewPath() == null) { + setViewPath(aProj.getBaseDir().getPath()); + } + + // build the command line from what we got. the format is + // cleartool checkin [options...] [viewpath ...] + // as specified in the CLEARTOOL.EXE help + commandLine.setExecutable(getClearToolCommand()); + commandLine.createArgument().setValue(COMMAND_MKBL); + + checkOptions(commandLine); + + if (!getFailOnErr()) { + getProject().log("Ignoring any errors that occur for: " + + getBaselineRootName(), Project.MSG_VERBOSE); + } + result = run(commandLine); + if (Execute.isFailure(result) && getFailOnErr()) { + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + } + + + /** + * Check the command line options. + */ + private void checkOptions(Commandline cmd) { + if (getComment() != null) { + // -c + getCommentCommand(cmd); + } else { + if (getCommentFile() != null) { + // -cfile + getCommentFileCommand(cmd); + } else { + cmd.createArgument().setValue(FLAG_NOCOMMENT); + } + } + + if (getIdentical()) { + // -identical + cmd.createArgument().setValue(FLAG_IDENTICAL); + } + + if (getFull()) { + // -full + cmd.createArgument().setValue(FLAG_FULL); + } else { + // -incremental + cmd.createArgument().setValue(FLAG_INCREMENTAL); + } + + if (getNlabel()) { + // -nlabel + cmd.createArgument().setValue(FLAG_NLABEL); + } + + // baseline_root_name + cmd.createArgument().setValue(getBaselineRootName()); + + } + + + /** + * Set comment string + * + * @param comment the comment string + */ + public void setComment(String comment) { + mComment = comment; + } + + /** + * Get comment string + * + * @return String containing the comment + */ + public String getComment() { + return mComment; + } + + /** + * Set comment file + * + * @param cfile the path to the comment file + */ + public void setCommentFile(String cfile) { + mCfile = cfile; + } + + /** + * Get comment file + * + * @return String containing the path to the comment file + */ + public String getCommentFile() { + return mCfile; + } + + /** + * Set baseline_root_name + * + * @param baselineRootName the name of the baseline + */ + public void setBaselineRootName(String baselineRootName) { + mBaselineRootName = baselineRootName; + } + + /** + * Get baseline_root_name + * + * @return String containing the name of the baseline + */ + public String getBaselineRootName() { + return mBaselineRootName; + } + + /** + + /** + * Set the nowarn flag + * + * @param nwarn the status to set the flag to + */ + public void setNoWarn(boolean nwarn) { + mNwarn = nwarn; + } + + /** + * Get nowarn flag status + * + * @return boolean containing status of nwarn flag + */ + public boolean getNoWarn() { + return mNwarn; + } + + /** + * Set the identical flag + * + * @param identical the status to set the flag to + */ + public void setIdentical(boolean identical) { + mIdentical = identical; + } + + /** + * Get identical flag status + * + * @return boolean containing status of identical flag + */ + public boolean getIdentical() { + return mIdentical; + } + + /** + * Set the full flag + * + * @param full the status to set the flag to + */ + public void setFull(boolean full) { + mFull = full; + } + + /** + * Get full flag status + * + * @return boolean containing status of full flag + */ + public boolean getFull() { + return mFull; + } + + /** + * Set the nlabel flag + * + * @param nlabel the status to set the flag to + */ + public void setNlabel(boolean nlabel) { + mNlabel = nlabel; + } + + /** + * Get nlabel status + * + * @return boolean containing status of nlabel flag + */ + public boolean getNlabel() { + return mNlabel; + } + + + /** + * Get the 'comment' command + * + * @param cmd containing the command line string with or + * without the comment flag and string appended + */ + private void getCommentCommand(Commandline cmd) { + if (getComment() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENT); + cmd.createArgument().setValue(getComment()); + } + } + + /** + * Get the 'commentfile' command + * + * @param cmd CommandLine containing the command line string with or + * without the commentfile flag and file appended + */ + private void getCommentFileCommand(Commandline cmd) { + if (getCommentFile() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENTFILE); + cmd.createArgument().setValue(getCommentFile()); + } + } + + + /** + * -c flag -- comment to attach to the file + */ + public static final String FLAG_COMMENT = "-c"; + /** + * -cfile flag -- file containing a comment to attach to the file + */ + public static final String FLAG_COMMENTFILE = "-cfile"; + /** + * -nc flag -- no comment is specified + */ + public static final String FLAG_NOCOMMENT = "-nc"; + /** + * -identical flag -- allows the file to be checked in even if it is identical to the original + */ + public static final String FLAG_IDENTICAL = "-identical"; + /** + * -incremental flag -- baseline to be created is incremental + */ + public static final String FLAG_INCREMENTAL = "-incremental"; + /** + * -full flag -- baseline to be created is full + */ + public static final String FLAG_FULL = "-full"; + /** + * -nlabel -- baseline to be created without a label + */ + public static final String FLAG_NLABEL = "-nlabel"; + + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkdir.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkdir.java new file mode 100644 index 00000000..4c89539f --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkdir.java @@ -0,0 +1,237 @@ +/* + * 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.tools.ant.taskdefs.optional.clearcase; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.types.Commandline; + +/** + * Performs ClearCase mkdir. + * + * <p> + * The following attributes are interpreted: + * <table border="1"> + * <tr> + * <th>Attribute</th> + * <th>Values</th> + * <th>Required</th> + * </tr> + * <tr> + * <td>viewpath</td> + * <td>Path to the ClearCase view directory that the command will operate on</td> + * <td>Yes</td> + * <tr> + * <tr> + * <td>comment</td> + * <td>Specify a comment. Only one of comment or cfile may be used.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>commentfile</td> + * <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>nocheckout</td> + * <td>Do not checkout after element creation</td> + * <td>No</td> + * <tr> + * <tr> + * <td>failonerr</td> + * <td>Throw an exception if the command fails. Default is true</td> + * <td>No</td> + * <tr> + * </table> + * + */ +public class CCMkdir extends ClearCase { + private String mComment = null; + private String mCfile = null; + private boolean mNoco = false; + + /** + * Executes the task. + * <p> + * Builds a command line to execute cleartool and then calls Exec's run method + * to execute the command line. + * @throws BuildException if the command fails and failonerr is set to true + */ + public void execute() throws BuildException { + Commandline commandLine = new Commandline(); + Project aProj = getProject(); + int result = 0; + + // Default the viewpath to basedir if it is not specified + if (getViewPath() == null) { + setViewPath(aProj.getBaseDir().getPath()); + } + + // build the command line from what we got. the format is + // cleartool mkelem [options...] [viewpath ...] + // as specified in the CLEARTOOL.EXE help + commandLine.setExecutable(getClearToolCommand()); + commandLine.createArgument().setValue(COMMAND_MKDIR); + + checkOptions(commandLine); + + if (!getFailOnErr()) { + getProject().log("Ignoring any errors that occur for: " + + getViewPathBasename(), Project.MSG_VERBOSE); + } + result = run(commandLine); + if (Execute.isFailure(result) && getFailOnErr()) { + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + } + + /** + * Check the command line options. + */ + private void checkOptions(Commandline cmd) { + if (getComment() != null) { + // -c + getCommentCommand(cmd); + } else { + if (getCommentFile() != null) { + // -cfile + getCommentFileCommand(cmd); + } else { + cmd.createArgument().setValue(FLAG_NOCOMMENT); + } + } + if (getNoCheckout()) { + // -nco + cmd.createArgument().setValue(FLAG_NOCHECKOUT); + } + // viewpath + cmd.createArgument().setValue(getViewPath()); + } + + /** + * Sets the comment string. + * + * @param comment the comment string + */ + public void setComment(String comment) { + mComment = comment; + } + + /** + * Get comment string + * + * @return String containing the comment + */ + public String getComment() { + return mComment; + } + + /** + * Specifies a file containing a comment. + * + * @param cfile the path to the comment file + */ + public void setCommentFile(String cfile) { + mCfile = cfile; + } + + /** + * Get comment file + * + * @return String containing the path to the comment file + */ + public String getCommentFile() { + return mCfile; + } + + /** + * If true, do not checkout element after creation. + * + * @param co the status to set the flag to + */ + public void setNoCheckout(boolean co) { + mNoco = co; + } + + /** + * Get no checkout flag status + * + * @return boolean containing status of noco flag + */ + public boolean getNoCheckout() { + return mNoco; + } + + + /** + * Get the 'comment' command + * + * @param cmd containing the command line string with or + * without the comment flag and string appended + */ + private void getCommentCommand(Commandline cmd) { + if (getComment() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENT); + cmd.createArgument().setValue(getComment()); + } + } + + /** + * Get the 'commentfile' command + * + * @param cmd containing the command line string with or + * without the commentfile flag and file appended + */ + private void getCommentFileCommand(Commandline cmd) { + if (getCommentFile() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENTFILE); + cmd.createArgument().setValue(getCommentFile()); + } + } + + /** + * -c flag -- comment to attach to the directory + */ + public static final String FLAG_COMMENT = "-c"; + /** + * -cfile flag -- file containing a comment to attach to the directory + */ + public static final String FLAG_COMMENTFILE = "-cfile"; + /** + * -nc flag -- no comment is specified + */ + public static final String FLAG_NOCOMMENT = "-nc"; + /** + * -nco flag -- do not checkout element after creation + */ + public static final String FLAG_NOCHECKOUT = "-nco"; +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkelem.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkelem.java new file mode 100644 index 00000000..94faa5a6 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkelem.java @@ -0,0 +1,424 @@ +/* + * 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.tools.ant.taskdefs.optional.clearcase; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.types.Commandline; + +/** + * Performs ClearCase mkelem. + * + * <p> + * The following attributes are interpreted: + * <table border="1"> + * <tr> + * <th>Attribute</th> + * <th>Values</th> + * <th>Required</th> + * </tr> + * <tr> + * <td>viewpath</td> + * <td>Path to the ClearCase view file or directory that the command will operate on</td> + * <td>Yes</td> + * <tr> + * <tr> + * <td>comment</td> + * <td>Specify a comment. Only one of comment or cfile may be used.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>commentfile</td> + * <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>nowarn</td> + * <td>Suppress warning messages</td> + * <td>No</td> + * <tr> + * <tr> + * <td>nocheckout</td> + * <td>Do not checkout after element creation</td> + * <td>No</td> + * <tr> + * <tr> + * <td>checkin</td> + * <td>Checkin element after creation</td> + * <td>No</td> + * <tr> + * <tr> + * <td>preservetime</td> + * <td>Preserve the modification time (for checkin)</td> + * <td>No</td> + * <tr> + * <tr> + * <td>master</td> + * <td>Assign mastership of the main branch to the current site</td> + * <td>No</td> + * <tr> + * <tr> + * <td>eltype</td> + * <td>Element type to use during element creation</td> + * <td>No</td> + * <tr> + * <tr> + * <td>failonerr</td> + * <td>Throw an exception if the command fails. Default is true</td> + * <td>No</td> + * <tr> + * </table> + * + */ +public class CCMkelem extends ClearCase { + private String mComment = null; + private String mCfile = null; + private boolean mNwarn = false; + private boolean mPtime = false; + private boolean mNoco = false; + private boolean mCheckin = false; + private boolean mMaster = false; + private String mEltype = null; + + /** + * Executes the task. + * <p> + * Builds a command line to execute cleartool and then calls Exec's run method + * to execute the command line. + * @throws BuildException if the command fails and failonerr is set to true + */ + public void execute() throws BuildException { + Commandline commandLine = new Commandline(); + Project aProj = getProject(); + int result = 0; + + // Default the viewpath to basedir if it is not specified + if (getViewPath() == null) { + setViewPath(aProj.getBaseDir().getPath()); + } + + // build the command line from what we got. the format is + // cleartool mkelem [options...] [viewpath ...] + // as specified in the CLEARTOOL.EXE help + commandLine.setExecutable(getClearToolCommand()); + commandLine.createArgument().setValue(COMMAND_MKELEM); + + checkOptions(commandLine); + + if (!getFailOnErr()) { + getProject().log("Ignoring any errors that occur for: " + + getViewPathBasename(), Project.MSG_VERBOSE); + } + result = run(commandLine); + if (Execute.isFailure(result) && getFailOnErr()) { + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + } + + + /** + * Check the command line options. + */ + private void checkOptions(Commandline cmd) { + if (getComment() != null) { + // -c + getCommentCommand(cmd); + } else { + if (getCommentFile() != null) { + // -cfile + getCommentFileCommand(cmd); + } else { + cmd.createArgument().setValue(FLAG_NOCOMMENT); + } + } + + if (getNoWarn()) { + // -nwarn + cmd.createArgument().setValue(FLAG_NOWARN); + } + /* + * Should choose either -ci or -nco. + */ + if (getNoCheckout() && getCheckin()) { + throw new BuildException("Should choose either [nocheckout | checkin]"); + } + if (getNoCheckout()) { + // -nco + cmd.createArgument().setValue(FLAG_NOCHECKOUT); + } + if (getCheckin()) { + // -ci + cmd.createArgument().setValue(FLAG_CHECKIN); + if (getPreserveTime()) { + // -ptime + cmd.createArgument().setValue(FLAG_PRESERVETIME); + } + } + if (getMaster()) { + // -master + cmd.createArgument().setValue(FLAG_MASTER); + } + if (getEltype() != null) { + // -eltype + getEltypeCommand(cmd); + } + // viewpath + cmd.createArgument().setValue(getViewPath()); + } + + /** + * Sets the comment string. + * + * @param comment the comment string + */ + public void setComment(String comment) { + mComment = comment; + } + + /** + * Get comment string + * + * @return String containing the comment + */ + public String getComment() { + return mComment; + } + + /** + * Specifies a file containing a comment. + * + * @param cfile the path to the comment file + */ + public void setCommentFile(String cfile) { + mCfile = cfile; + } + + /** + * Get comment file + * + * @return String containing the path to the comment file + */ + public String getCommentFile() { + return mCfile; + } + + /** + * If true, suppress warning messages. + * + * @param nwarn the status to set the flag to + */ + public void setNoWarn(boolean nwarn) { + mNwarn = nwarn; + } + + /** + * Get nowarn flag status + * + * @return boolean containing status of nwarn flag + */ + public boolean getNoWarn() { + return mNwarn; + } + + /** + * If true, preserve the modification time. + * + * @param ptime the status to set the flag to + */ + public void setPreserveTime(boolean ptime) { + mPtime = ptime; + } + + /** + * Get preservetime flag status + * + * @return boolean containing status of preservetime flag + */ + public boolean getPreserveTime() { + return mPtime; + } + + /** + * If true, do not checkout element after creation. + * + * @param co the status to set the flag to + */ + public void setNoCheckout(boolean co) { + mNoco = co; + } + + /** + * Get no checkout flag status + * + * @return boolean containing status of noco flag + */ + public boolean getNoCheckout() { + return mNoco; + } + + /** + * If true, checkin the element after creation + * + * @param ci the status to set the flag to + */ + public void setCheckin(boolean ci) { + mCheckin = ci; + } + + /** + * Get ci flag status + * + * @return boolean containing status of ci flag + */ + public boolean getCheckin() { + return mCheckin; + } + + /** + * If true, changes mastership of the main branch + * to the current site + * + * @param master the status to set the flag to + */ + public void setMaster(boolean master) { + mMaster = master; + } + + /** + * Get master flag status + * + * @return boolean containing status of master flag + */ + public boolean getMaster() { + return mMaster; + } + + /** + * Specifies the element type to use. + * + * @param eltype to create element + */ + public void setEltype(String eltype) { + mEltype = eltype; + } + + /** + * Get element type + * + * @return String containing the element type + */ + public String getEltype() { + return mEltype; + } + + + /** + * Get the 'comment' command + * + * @param cmd containing the command line string with or + * without the comment flag and string appended + */ + private void getCommentCommand(Commandline cmd) { + if (getComment() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENT); + cmd.createArgument().setValue(getComment()); + } + } + + /** + * Get the 'commentfile' command + * + * @param cmd containing the command line string with or + * without the commentfile flag and file appended + */ + private void getCommentFileCommand(Commandline cmd) { + if (getCommentFile() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENTFILE); + cmd.createArgument().setValue(getCommentFile()); + } + } + + /** + * Get the 'element type' command + * + * @param cmd containing the command line string with or + * without the comment flag and string appended + */ + private void getEltypeCommand(Commandline cmd) { + if (getEltype() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_ELTYPE); + cmd.createArgument().setValue(getEltype()); + } + } + + /** + * -c flag -- comment to attach to the file + */ + public static final String FLAG_COMMENT = "-c"; + /** + * -cfile flag -- file containing a comment to attach to the file + */ + public static final String FLAG_COMMENTFILE = "-cfile"; + /** + * -nc flag -- no comment is specified + */ + public static final String FLAG_NOCOMMENT = "-nc"; + /** + * -nwarn flag -- suppresses warning messages + */ + public static final String FLAG_NOWARN = "-nwarn"; + /** + * -ptime flag -- preserves the modification time on checkin + */ + public static final String FLAG_PRESERVETIME = "-ptime"; + /** + * -nco flag -- do not checkout element after creation + */ + public static final String FLAG_NOCHECKOUT = "-nco"; + /** + * -ci flag -- checkin element after creation + */ + public static final String FLAG_CHECKIN = "-ci"; + /** + * -master flag -- change mastership of main branch to current site + */ + public static final String FLAG_MASTER = "-master"; + /** + * -eltype flag -- element type to use during creation + */ + public static final String FLAG_ELTYPE = "-eltype"; +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklabel.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklabel.java new file mode 100644 index 00000000..e3d288df --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklabel.java @@ -0,0 +1,402 @@ +/* + * 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.tools.ant.taskdefs.optional.clearcase; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.types.Commandline; + +/** + * Task to perform mklabel command to ClearCase. + * <p> + * The following attributes are interpreted: + * <table border="1"> + * <tr> + * <th>Attribute</th> + * <th>Values</th> + * <th>Required</th> + * </tr> + * <tr> + * <td>viewpath</td> + * <td>Path to the ClearCase view file or directory that the command will operate on</td> + * <td>No</td> + * <tr> + * <tr> + * <td>replace</td> + * <td>Replace a label of the same type on the same branch</td> + * <td>No</td> + * <tr> + * <tr> + * <td>recurse</td> + * <td>Process each subdirectory under viewpath</td> + * <td>No</td> + * <tr> + * <tr> + * <td>version</td> + * <td>Identify a specific version to attach the label to</td> + * <td>No</td> + * <tr> + * <tr> + * <td>typename</td> + * <td>Name of the label type</td> + * <td>Yes</td> + * <tr> + * <tr> + * <td>vob</td> + * <td>Name of the VOB</td> + * <td>No</td> + * <tr> + * <tr> + * <td>comment</td> + * <td>Specify a comment. Only one of comment or cfile may be used.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>commentfile</td> + * <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>failonerr</td> + * <td>Throw an exception if the command fails. Default is true</td> + * <td>No</td> + * <tr> + * </table> + * + */ +public class CCMklabel extends ClearCase { + private boolean mReplace = false; + private boolean mRecurse = false; + private String mVersion = null; + private String mTypeName = null; + private String mVOB = null; + private String mComment = null; + private String mCfile = null; + + /** + * Executes the task. + * <p> + * Builds a command line to execute cleartool and then calls Exec's run method + * to execute the command line. + * @throws BuildException if the command fails and failonerr is set to true + */ + public void execute() throws BuildException { + Commandline commandLine = new Commandline(); + Project aProj = getProject(); + int result = 0; + + // Check for required attributes + if (getTypeName() == null) { + throw new BuildException("Required attribute TypeName not specified"); + } + + // Default the viewpath to basedir if it is not specified + if (getViewPath() == null) { + setViewPath(aProj.getBaseDir().getPath()); + } + + // build the command line from what we got. the format is + // cleartool mklabel [options...] [viewpath ...] + // as specified in the CLEARTOOL help + commandLine.setExecutable(getClearToolCommand()); + commandLine.createArgument().setValue(COMMAND_MKLABEL); + + checkOptions(commandLine); + + if (!getFailOnErr()) { + getProject().log("Ignoring any errors that occur for: " + + getViewPathBasename(), Project.MSG_VERBOSE); + } + result = run(commandLine); + if (Execute.isFailure(result) && getFailOnErr()) { + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + } + + + /** + * Check the command line options. + */ + private void checkOptions(Commandline cmd) { + if (getReplace()) { + // -replace + cmd.createArgument().setValue(FLAG_REPLACE); + } + + if (getRecurse()) { + // -recurse + cmd.createArgument().setValue(FLAG_RECURSE); + } + + if (getVersion() != null) { + // -version + getVersionCommand(cmd); + } + + if (getComment() != null) { + // -c + getCommentCommand(cmd); + } else { + if (getCommentFile() != null) { + // -cfile + getCommentFileCommand(cmd); + } else { + cmd.createArgument().setValue(FLAG_NOCOMMENT); + } + } + + if (getTypeName() != null) { + // type + getTypeCommand(cmd); + } + + // viewpath + cmd.createArgument().setValue(getViewPath()); + } + + + /** + * Set the replace flag + * + * @param replace the status to set the flag to + */ + public void setReplace(boolean replace) { + mReplace = replace; + } + + /** + * Get replace flag status + * + * @return boolean containing status of replace flag + */ + public boolean getReplace() { + return mReplace; + } + + /** + * Set recurse flag + * + * @param recurse the status to set the flag to + */ + public void setRecurse(boolean recurse) { + mRecurse = recurse; + } + + /** + * Get recurse flag status + * + * @return boolean containing status of recurse flag + */ + public boolean getRecurse() { + return mRecurse; + } + + /** + * Set the version flag + * + * @param version the status to set the flag to + */ + public void setVersion(String version) { + mVersion = version; + } + + /** + * Get version flag status + * + * @return boolean containing status of version flag + */ + public String getVersion() { + return mVersion; + } + + /** + * Set comment string + * + * @param comment the comment string + */ + public void setComment(String comment) { + mComment = comment; + } + + /** + * Get comment string + * + * @return String containing the comment + */ + public String getComment() { + return mComment; + } + + /** + * Set comment file + * + * @param cfile the path to the comment file + */ + public void setCommentFile(String cfile) { + mCfile = cfile; + } + + /** + * Get comment file + * + * @return String containing the path to the comment file + */ + public String getCommentFile() { + return mCfile; + } + + /** + * Set the type-name + * + * @param tn the type name + */ + public void setTypeName(String tn) { + mTypeName = tn; + } + + /** + * Get type-name + * + * @return String containing type name + */ + public String getTypeName() { + return mTypeName; + } + + /** + * Set the VOB name + * + * @param vob the VOB name + */ + public void setVOB(String vob) { + mVOB = vob; + } + + /** + * Get VOB name + * + * @return String containing VOB name + */ + public String getVOB() { + return mVOB; + } + + + /** + * Get the 'version' command + * + * @param cmd CommandLine containing the command line string with or + * without the version flag and string appended + */ + private void getVersionCommand(Commandline cmd) { + if (getVersion() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_VERSION); + cmd.createArgument().setValue(getVersion()); + } + } + + /** + * Get the 'comment' command + * + * @param cmd containing the command line string with or + * without the comment flag and string appended + */ + private void getCommentCommand(Commandline cmd) { + if (getComment() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENT); + cmd.createArgument().setValue(getComment()); + } + } + + /** + * Get the 'commentfile' command + * + * @param cmd containing the command line string with or + * without the commentfile flag and file appended + */ + private void getCommentFileCommand(Commandline cmd) { + if (getCommentFile() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENTFILE); + cmd.createArgument().setValue(getCommentFile()); + } + } + + /** + * Get the type-name + * + * @param cmd containing the command line string with or + * without the type-name + */ + private void getTypeCommand(Commandline cmd) { + String typenm = null; + + if (getTypeName() != null) { + typenm = getTypeName(); + if (getVOB() != null) { + typenm += "@" + getVOB(); + } + cmd.createArgument().setValue(typenm); + } + } + + + /** + * -replace flag -- replace another label of the same type + */ + public static final String FLAG_REPLACE = "-replace"; + /** + * -recurse flag -- process all subdirectories + */ + public static final String FLAG_RECURSE = "-recurse"; + /** + * -version flag -- attach label to specified version + */ + public static final String FLAG_VERSION = "-version"; + /** + * -c flag -- comment to attach to the file + */ + public static final String FLAG_COMMENT = "-c"; + /** + * -cfile flag -- file containing a comment to attach to the file + */ + public static final String FLAG_COMMENTFILE = "-cfile"; + /** + * -nc flag -- no comment is specified + */ + public static final String FLAG_NOCOMMENT = "-nc"; + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklbtype.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklbtype.java new file mode 100644 index 00000000..7bb7192e --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklbtype.java @@ -0,0 +1,440 @@ +/* + * 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.tools.ant.taskdefs.optional.clearcase; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.types.Commandline; + +/** + * Task to perform mklbtype command to ClearCase. + * <p> + * The following attributes are interpreted: + * <table border="1"> + * <tr> + * <th>Attribute</th> + * <th>Values</th> + * <th>Required</th> + * </tr> + * <tr> + * <td>typename</td> + * <td>Name of the label type to create</td> + * <td>Yes</td> + * <tr> + * <tr> + * <td>vob</td> + * <td>Name of the VOB</td> + * <td>No</td> + * <tr> + * <tr> + * <td>replace</td> + * <td>Replace an existing label definition of the same type</td> + * <td>No</td> + * <tr> + * <tr> + * <td>global</td> + * <td>Either global or ordinary can be specified, not both. + * Creates a label type that is global to the VOB or to + * VOBs that use this VOB</td> + * <td>No</td> + * <tr> + * <tr> + * <td>ordinary</td> + * <td>Either global or ordinary can be specified, not both. + * Creates a label type that can be used only in the current + * VOB. <B>Default</B></td> + * <td>No</td> + * <tr> + * <tr> + * <td>pbranch</td> + * <td>Allows the label type to be used once per branch in a given + * element's version tree</td> + * <td>No</td> + * <tr> + * <tr> + * <td>shared</td> + * <td>Sets the way mastership is checked by ClearCase. See ClearCase + * documentation for details</td> + * <td>No</td> + * <tr> + * <tr> + * <td>comment</td> + * <td>Specify a comment. Only one of comment or cfile may be used.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>commentfile</td> + * <td>Specify a file containing a comment. Only one of comment or + * cfile may be used.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>failonerr</td> + * <td>Throw an exception if the command fails. Default is true</td> + * <td>No</td> + * <tr> + * </table> + * + */ +public class CCMklbtype extends ClearCase { + private String mTypeName = null; + private String mVOB = null; + private String mComment = null; + private String mCfile = null; + private boolean mReplace = false; + private boolean mGlobal = false; + private boolean mOrdinary = true; + private boolean mPbranch = false; + private boolean mShared = false; + + /** + * Executes the task. + * <p> + * Builds a command line to execute cleartool and then calls Exec's run method + * to execute the command line. + * @throws BuildException if the command fails and failonerr is set to true + */ + public void execute() throws BuildException { + Commandline commandLine = new Commandline(); + int result = 0; + + // Check for required attributes + if (getTypeName() == null) { + throw new BuildException("Required attribute TypeName not specified"); + } + + // build the command line from what we got. the format is + // cleartool mklbtype [options...] type-selector... + // as specified in the CLEARTOOL help + commandLine.setExecutable(getClearToolCommand()); + commandLine.createArgument().setValue(COMMAND_MKLBTYPE); + + checkOptions(commandLine); + + if (!getFailOnErr()) { + getProject().log("Ignoring any errors that occur for: " + + getTypeSpecifier(), Project.MSG_VERBOSE); + } + result = run(commandLine); + if (Execute.isFailure(result) && getFailOnErr()) { + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + } + + + /** + * Check the command line options. + */ + private void checkOptions(Commandline cmd) { + if (getReplace()) { + // -replace + cmd.createArgument().setValue(FLAG_REPLACE); + } + + if (getOrdinary()) { + // -ordinary + cmd.createArgument().setValue(FLAG_ORDINARY); + } else { + if (getGlobal()) { + // -global + cmd.createArgument().setValue(FLAG_GLOBAL); + } + } + + if (getPbranch()) { + // -pbranch + cmd.createArgument().setValue(FLAG_PBRANCH); + } + + if (getShared()) { + // -shared + cmd.createArgument().setValue(FLAG_SHARED); + } + + if (getComment() != null) { + // -c + getCommentCommand(cmd); + } else { + if (getCommentFile() != null) { + // -cfile + getCommentFileCommand(cmd); + } else { + cmd.createArgument().setValue(FLAG_NOCOMMENT); + } + } + + // type-name@vob + cmd.createArgument().setValue(getTypeSpecifier()); + } + + + /** + * Set type-name string + * + * @param tn the type-name string + */ + public void setTypeName(String tn) { + mTypeName = tn; + } + + /** + * Get type-name string + * + * @return String containing the type-name + */ + public String getTypeName() { + return mTypeName; + } + + /** + * Set the VOB name + * + * @param vob the VOB name + */ + public void setVOB(String vob) { + mVOB = vob; + } + + /** + * Get VOB name + * + * @return String containing VOB name + */ + public String getVOB() { + return mVOB; + } + + /** + * Set the replace flag + * + * @param repl the status to set the flag to + */ + public void setReplace(boolean repl) { + mReplace = repl; + } + + /** + * Get replace flag status + * + * @return boolean containing status of replace flag + */ + public boolean getReplace() { + return mReplace; + } + + /** + * Set the global flag + * + * @param glob the status to set the flag to + */ + public void setGlobal(boolean glob) { + mGlobal = glob; + } + + /** + * Get global flag status + * + * @return boolean containing status of global flag + */ + public boolean getGlobal() { + return mGlobal; + } + + /** + * Set the ordinary flag + * + * @param ordinary the status to set the flag to + */ + public void setOrdinary(boolean ordinary) { + mOrdinary = ordinary; + } + + /** + * Get ordinary flag status + * + * @return boolean containing status of ordinary flag + */ + public boolean getOrdinary() { + return mOrdinary; + } + + /** + * Set the pbranch flag + * + * @param pbranch the status to set the flag to + */ + public void setPbranch(boolean pbranch) { + mPbranch = pbranch; + } + + /** + * Get pbranch flag status + * + * @return boolean containing status of pbranch flag + */ + public boolean getPbranch() { + return mPbranch; + } + + /** + * Set the shared flag + * + * @param shared the status to set the flag to + */ + public void setShared(boolean shared) { + mShared = shared; + } + + /** + * Get shared flag status + * + * @return boolean containing status of shared flag + */ + public boolean getShared() { + return mShared; + } + + /** + * Set comment string + * + * @param comment the comment string + */ + public void setComment(String comment) { + mComment = comment; + } + + /** + * Get comment string + * + * @return String containing the comment + */ + public String getComment() { + return mComment; + } + + /** + * Set comment file + * + * @param cfile the path to the comment file + */ + public void setCommentFile(String cfile) { + mCfile = cfile; + } + + /** + * Get comment file + * + * @return String containing the path to the comment file + */ + public String getCommentFile() { + return mCfile; + } + + + /** + * Get the 'comment' command + * + * @param cmd containing the command line string with or + * without the comment flag and string appended + */ + private void getCommentCommand(Commandline cmd) { + if (getComment() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENT); + cmd.createArgument().setValue(getComment()); + } + } + + /** + * Get the 'commentfile' command + * + * @param cmd containing the command line string with or + * without the commentfile flag and file appended + */ + private void getCommentFileCommand(Commandline cmd) { + if (getCommentFile() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENTFILE); + cmd.createArgument().setValue(getCommentFile()); + } + } + + /** + * Get the type-name specifier + * + * @return the 'type-name-specifier' command if the attribute was + * specified, otherwise an empty string + */ + private String getTypeSpecifier() { + String typenm = null; + + typenm = getTypeName(); + if (getVOB() != null) { + typenm += "@" + getVOB(); + } + + return typenm; + } + + + /** + * -replace flag -- replace existing label definition of the same type + */ + public static final String FLAG_REPLACE = "-replace"; + /** + * -global flag -- creates a label type that is global to the VOB or to VOBs that use this VOB + */ + public static final String FLAG_GLOBAL = "-global"; + /** + * -ordinary flag -- creates a label type that can be used only in the current VOB + */ + public static final String FLAG_ORDINARY = "-ordinary"; + /** + * -pbranch flag -- allows label type to be used once per branch + */ + public static final String FLAG_PBRANCH = "-pbranch"; + /** + * -shared flag -- sets the way mastership is checked by ClearCase + */ + public static final String FLAG_SHARED = "-shared"; + /** + * -c flag -- comment to attach to the file + */ + public static final String FLAG_COMMENT = "-c"; + /** + * -cfile flag -- file containing a comment to attach to the file + */ + public static final String FLAG_COMMENTFILE = "-cfile"; + /** + * -nc flag -- no comment is specified + */ + public static final String FLAG_NOCOMMENT = "-nc"; + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCRmtype.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCRmtype.java new file mode 100644 index 00000000..cef0c3a5 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCRmtype.java @@ -0,0 +1,373 @@ +/* + * 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.tools.ant.taskdefs.optional.clearcase; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.types.Commandline; + +/** + * Task to perform rmtype command to ClearCase. + * <p> + * The following attributes are interpreted: + * <table border="1"> + * <tr> + * <th>Attribute</th> + * <th>Values</th> + * <th>Required</th> + * </tr> + * <tr> + * <td>typekind</td> + * <td>The kind of type to create. Valid types are:<br> + * attype attribute type<br> + * brtype branch type<br> + * eltype element type<br> + * hltype hyperlink type<br> + * lbtype label type<br> + * trtype trigger type<br> + * </td> + * <td>Yes</td> + * <tr> + * <tr> + * <td>typename</td> + * <td>The name of the type to remove</td> + * <td>Yes</td> + * <tr> + * <tr> + * <td>vob</td> + * <td>Name of the VOB</td> + * <td>No</td> + * <tr> + * <tr> + * <td>ignore</td> + * <td>Used with trigger types only. Forces removal of trigger type + * even if a pre-operation trigger would prevent its removal</td> + * <td>No</td> + * <tr> + * <tr> + * <td>rmall</td> + * <td>Removes all instances of a type and the type object itself</td> + * <td>No</td> + * <tr> + * <tr> + * <td>comment</td> + * <td>Specify a comment. Only one of comment or cfile may be used.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>commentfile</td> + * <td>Specify a file containing a comment. Only one of comment or cfile + * may be used.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>failonerr</td> + * <td>Throw an exception if the command fails. Default is true</td> + * <td>No</td> + * <tr> + * </table> + * + */ +public class CCRmtype extends ClearCase { + private String mTypeKind = null; + private String mTypeName = null; + private String mVOB = null; + private String mComment = null; + private String mCfile = null; + private boolean mRmall = false; + private boolean mIgnore = false; + + /** + * Executes the task. + * <p> + * Builds a command line to execute cleartool and then calls Exec's run method + * to execute the command line. + * @throws BuildException if the command fails and failonerr is set to true + */ + public void execute() throws BuildException { + Commandline commandLine = new Commandline(); + int result = 0; + + // Check for required attributes + if (getTypeKind() == null) { + throw new BuildException("Required attribute TypeKind not specified"); + } + if (getTypeName() == null) { + throw new BuildException("Required attribute TypeName not specified"); + } + + // build the command line from what we got. the format is + // cleartool rmtype [options...] type-selector... + // as specified in the CLEARTOOL help + commandLine.setExecutable(getClearToolCommand()); + commandLine.createArgument().setValue(COMMAND_RMTYPE); + + checkOptions(commandLine); + + if (!getFailOnErr()) { + getProject().log("Ignoring any errors that occur for: " + + getTypeSpecifier(), Project.MSG_VERBOSE); + } + result = run(commandLine); + if (Execute.isFailure(result) && getFailOnErr()) { + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + } + + + /** + * Check the command line options. + */ + private void checkOptions(Commandline cmd) { + if (getIgnore()) { + // -ignore + cmd.createArgument().setValue(FLAG_IGNORE); + } + if (getRmAll()) { + // -rmall -force + cmd.createArgument().setValue(FLAG_RMALL); + cmd.createArgument().setValue(FLAG_FORCE); + } + if (getComment() != null) { + // -c + getCommentCommand(cmd); + } else { + if (getCommentFile() != null) { + // -cfile + getCommentFileCommand(cmd); + } else { + cmd.createArgument().setValue(FLAG_NOCOMMENT); + } + } + + // type-kind:type-name + cmd.createArgument().setValue(getTypeSpecifier()); + } + + /** + * Set the ignore flag + * + * @param ignore the status to set the flag to + */ + public void setIgnore(boolean ignore) { + mIgnore = ignore; + } + + /** + * Get ignore flag status + * + * @return boolean containing status of ignore flag + */ + public boolean getIgnore() { + return mIgnore; + } + + /** + * Set rmall flag + * + * @param rmall the status to set the flag to + */ + public void setRmAll(boolean rmall) { + mRmall = rmall; + } + + /** + * Get rmall flag status + * + * @return boolean containing status of rmall flag + */ + public boolean getRmAll() { + return mRmall; + } + + /** + * Set comment string + * + * @param comment the comment string + */ + public void setComment(String comment) { + mComment = comment; + } + + /** + * Get comment string + * + * @return String containing the comment + */ + public String getComment() { + return mComment; + } + + /** + * Set comment file + * + * @param cfile the path to the comment file + */ + public void setCommentFile(String cfile) { + mCfile = cfile; + } + + /** + * Get comment file + * + * @return String containing the path to the comment file + */ + public String getCommentFile() { + return mCfile; + } + + /** + * Set type-kind string + * + * @param tk the type-kind string + */ + public void setTypeKind(String tk) { + mTypeKind = tk; + } + + /** + * Get type-kind string + * + * @return String containing the type-kind + */ + public String getTypeKind() { + return mTypeKind; + } + + /** + * Set type-name string + * + * @param tn the type-name string + */ + public void setTypeName(String tn) { + mTypeName = tn; + } + + /** + * Get type-name string + * + * @return String containing the type-name + */ + public String getTypeName() { + return mTypeName; + } + + /** + * Set the VOB name + * + * @param vob the VOB name + */ + public void setVOB(String vob) { + mVOB = vob; + } + + /** + * Get VOB name + * + * @return String containing VOB name + */ + public String getVOB() { + return mVOB; + } + + /** + * Get the 'type-specifier' string + * + * @return the 'type-kind:type-name@vob' specifier + * + */ + private String getTypeSpecifier() { + String tkind = getTypeKind(); + String tname = getTypeName(); + String typeSpec = null; + + // Return the type-selector + typeSpec = tkind + ":" + tname; + if (getVOB() != null) { + typeSpec += "@" + getVOB(); + } + return typeSpec; + } + + /** + * Get the 'comment' command + * + * @param cmd containing the command line string with or + * without the comment flag and string appended + */ + private void getCommentCommand(Commandline cmd) { + if (getComment() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENT); + cmd.createArgument().setValue(getComment()); + } + } + + /** + * Get the 'commentfile' command + * + * @param cmd containing the command line string with or + * without the commentfile flag and file appended + */ + private void getCommentFileCommand(Commandline cmd) { + if (getCommentFile() != null) { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENTFILE); + cmd.createArgument().setValue(getCommentFile()); + } + } + + + /** + * -ignore flag -- ignore pre-trigger operations when removing a trigger type + */ + public static final String FLAG_IGNORE = "-ignore"; + /** + * -rmall flag -- removes all instances of a type and the type object itself + */ + public static final String FLAG_RMALL = "-rmall"; + /** + * -force flag -- suppresses confirmation prompts + */ + public static final String FLAG_FORCE = "-force"; + /** + * -c flag -- comment to attach to the file + */ + public static final String FLAG_COMMENT = "-c"; + /** + * -cfile flag -- file containing a comment to attach to the file + */ + public static final String FLAG_COMMENTFILE = "-cfile"; + /** + * -nc flag -- no comment is specified + */ + public static final String FLAG_NOCOMMENT = "-nc"; + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnCheckout.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnCheckout.java new file mode 100644 index 00000000..3c00e1af --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnCheckout.java @@ -0,0 +1,141 @@ +/* + * 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.tools.ant.taskdefs.optional.clearcase; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.types.Commandline; + +/** + * Performs ClearCase UnCheckout command. + * + * <p> + * The following attributes are interpreted: + * <table border="1"> + * <tr> + * <th>Attribute</th> + * <th>Values</th> + * <th>Required</th> + * </tr> + * <tr> + * <td>viewpath</td> + * <td>Path to the ClearCase view file or directory that the command will operate on</td> + * <td>No</td> + * <tr> + * <tr> + * <td>keepcopy</td> + * <td>Specifies whether to keep a copy of the file with a .keep extension or not</td> + * <td>No</td> + * <tr> + * <tr> + * <td>failonerr</td> + * <td>Throw an exception if the command fails. Default is true</td> + * <td>No</td> + * <tr> + * </table> + * + */ +public class CCUnCheckout extends ClearCase { + private boolean mKeep = false; + + /** + * Executes the task. + * <p> + * Builds a command line to execute cleartool and then calls Exec's run method + * to execute the command line. + * @throws BuildException if the command fails and failonerr is set to true + */ + public void execute() throws BuildException { + Commandline commandLine = new Commandline(); + Project aProj = getProject(); + int result = 0; + + // Default the viewpath to basedir if it is not specified + if (getViewPath() == null) { + setViewPath(aProj.getBaseDir().getPath()); + } + + // build the command line from what we got the format is + // cleartool uncheckout [options...] [viewpath ...] + // as specified in the CLEARTOOL.EXE help + commandLine.setExecutable(getClearToolCommand()); + commandLine.createArgument().setValue(COMMAND_UNCHECKOUT); + + checkOptions(commandLine); + + if (!getFailOnErr()) { + getProject().log("Ignoring any errors that occur for: " + + getViewPathBasename(), Project.MSG_VERBOSE); + } + result = run(commandLine); + if (Execute.isFailure(result) && getFailOnErr()) { + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + } + + + /** + * Check the command line options. + */ + private void checkOptions(Commandline cmd) { + // ClearCase items + if (getKeepCopy()) { + // -keep + cmd.createArgument().setValue(FLAG_KEEPCOPY); + } else { + // -rm + cmd.createArgument().setValue(FLAG_RM); + } + + // viewpath + cmd.createArgument().setValue(getViewPath()); + } + + /** + * If true, keep a copy of the file with a .keep extension. + * + * @param keep the status to set the flag to + */ + public void setKeepCopy(boolean keep) { + mKeep = keep; + } + + /** + * Get keepcopy flag status + * + * @return boolean containing status of keep flag + */ + public boolean getKeepCopy() { + return mKeep; + } + + + /** + * -keep flag -- keep a copy of the file with .keep extension + */ + public static final String FLAG_KEEPCOPY = "-keep"; + /** + * -rm flag -- remove the copy of the file + */ + public static final String FLAG_RM = "-rm"; + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnlock.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnlock.java new file mode 100644 index 00000000..4ca3e890 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnlock.java @@ -0,0 +1,260 @@ +/* + * 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.tools.ant.taskdefs.optional.clearcase; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.types.Commandline; + +/** + * TODO: + * comment field doesn't include all options yet + */ + +/** + * Performs a ClearCase Unlock command. + * + * <p> + * The following attributes are interpreted: + * <table border="1"> + * <tr> + * <th>Attribute</th> + * <th>Values</th> + * <th>Required</th> + * </tr> + * <tr> + * <td>comment</td> + * <td>Specifies how to populate comments fields</td> + * <td>No</td> + * <tr> + * <tr> + * <td>pname</td> + * <td>Specifies the object pathname to be unlocked.</td> + * <td>No</td> + * <tr> + * <td>objselect</td> + * <td>This variable is obsolete. Should use <i>objsel</i> instead.</td> + * <td>no</td> + * <tr> + * <tr> + * <td>objsel</td> + * <td>Specifies the object(s) to be unlocked.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>failonerr</td> + * <td>Throw an exception if the command fails. Default is true</td> + * <td>No</td> + * <tr> + * + * </table> + * + */ +public class CCUnlock extends ClearCase { + private String mComment = null; + private String mPname = null; + + /** + * Executes the task. + * <p> + * Builds a command line to execute cleartool and then calls Exec's run method + * to execute the command line. + * @throws BuildException if the command fails and failonerr is set to true + */ + public void execute() throws BuildException { + Commandline commandLine = new Commandline(); + Project aProj = getProject(); + int result = 0; + + // Default the viewpath to basedir if it is not specified + if (getViewPath() == null) { + setViewPath(aProj.getBaseDir().getPath()); + } + + // build the command line from what we got the format is + // cleartool lock [options...] + // as specified in the CLEARTOOL.EXE help + commandLine.setExecutable(getClearToolCommand()); + commandLine.createArgument().setValue(COMMAND_UNLOCK); + + // Check the command line options + checkOptions(commandLine); + + // For debugging + // System.out.println(commandLine.toString()); + + if (!getFailOnErr()) { + getProject().log("Ignoring any errors that occur for: " + + getOpType(), Project.MSG_VERBOSE); + } + result = run(commandLine); + if (Execute.isFailure(result) && getFailOnErr()) { + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + } + + /** + * Check the command line options. + */ + private void checkOptions(Commandline cmd) { + // ClearCase items + getCommentCommand(cmd); + + if (getObjSelect() == null && getPname() == null) { + throw new BuildException("Should select either an element " + + "(pname) or an object (objselect)"); + } + getPnameCommand(cmd); + // object selector + if (getObjSelect() != null) { + cmd.createArgument().setValue(getObjSelect()); + } + } + + /** + * Sets how comments should be written + * for the event record(s) + * + * @param comment comment method to use + */ + public void setComment(String comment) { + mComment = comment; + } + + /** + * Get comment method + * + * @return String containing the desired comment method + */ + public String getComment() { + return mComment; + } + + /** + * Sets the pathname to be locked + * + * @param pname pathname to be locked + */ + public void setPname(String pname) { + mPname = pname; + } + + /** + * Get the pathname to be locked + * + * @return String containing the pathname to be locked + */ + public String getPname() { + return mPname; + } + + /** + * Sets the object(s) to be locked + * + * @param objselect objects to be locked + */ + public void setObjselect(String objselect) { + setObjSelect(objselect); + } + + /** + * Sets the object(s) to be locked + * + * @param objsel objects to be locked + * @since ant 1.6.1 + */ + public void setObjSel(String objsel) { + setObjSelect(objsel); + } + + /** + * Get list of objects to be locked + * + * @return String containing the objects to be locked + */ + public String getObjselect() { + return getObjSelect(); + } + + /** + * Get the 'comment' command + * + * @param cmd containing the command line string with or without the + * comment flag and value appended + */ + private void getCommentCommand(Commandline cmd) { + if (getComment() == null) { + return; + } else { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_COMMENT); + cmd.createArgument().setValue(getComment()); + } + } + + /** + * Get the 'pname' command + * + * @param cmd containing the command line string with or without the + * pname flag and value appended + */ + private void getPnameCommand(Commandline cmd) { + if (getPname() == null) { + return; + } else { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_PNAME); + cmd.createArgument().setValue(getPname()); + } + } + + /** + * Return which object/pname is being operated on + * + * @return String containing the object/pname being worked on + */ + private String getOpType() { + + if (getPname() != null) { + return getPname(); + } else { + return getObjSelect(); + } + } + + /** + * -comment flag -- method to use for commenting events + */ + public static final String FLAG_COMMENT = "-comment"; + /** + * -pname flag -- pathname to lock + */ + public static final String FLAG_PNAME = "-pname"; +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUpdate.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUpdate.java new file mode 100644 index 00000000..712efdca --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUpdate.java @@ -0,0 +1,331 @@ +/* + * 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.tools.ant.taskdefs.optional.clearcase; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.types.Commandline; + +/** + * Performs a ClearCase Update command. + * + * <p> + * The following attributes are interpreted: + * <table border="1"> + * <tr> + * <th>Attribute</th> + * <th>Values</th> + * <th>Required</th> + * </tr> + * <tr> + * <td>viewpath</td> + * <td>Path to the ClearCase view file or directory that the command will operate on</td> + * <td>No</td> + * <tr> + * <tr> + * <td>graphical</td> + * <td>Displays a graphical dialog during the update</td> + * <td>No</td> + * <tr> + * <tr> + * <td>log</td> + * <td>Specifies a log file for ClearCase to write to</td> + * <td>No</td> + * <tr> + * <tr> + * <td>overwrite</td> + * <td>Specifies whether to overwrite hijacked files or not</td> + * <td>No</td> + * <tr> + * <tr> + * <td>rename</td> + * <td>Specifies that hijacked files should be renamed with a .keep extension</td> + * <td>No</td> + * <tr> + * <tr> + * <td>currenttime</td> + * <td>Specifies that modification time should be written as the current + * time. Either currenttime or preservetime can be specified.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>preservetime</td> + * <td>Specifies that modification time should preserved from the VOB + * time. Either currenttime or preservetime can be specified.</td> + * <td>No</td> + * <tr> + * <tr> + * <td>failonerr</td> + * <td>Throw an exception if the command fails. Default is true</td> + * <td>No</td> + * <tr> + * </table> + * + */ +public class CCUpdate extends ClearCase { + private boolean mGraphical = false; + private boolean mOverwrite = false; + private boolean mRename = false; + private boolean mCtime = false; + private boolean mPtime = false; + private String mLog = null; + + /** + * Executes the task. + * <p> + * Builds a command line to execute cleartool and then calls Exec's run method + * to execute the command line. + * @throws BuildException if the command fails and failonerr is set to true + */ + public void execute() throws BuildException { + Commandline commandLine = new Commandline(); + Project aProj = getProject(); + int result = 0; + + // Default the viewpath to basedir if it is not specified + if (getViewPath() == null) { + setViewPath(aProj.getBaseDir().getPath()); + } + + // build the command line from what we got the format is + // cleartool update [options...] [viewpath ...] + // as specified in the CLEARTOOL.EXE help + commandLine.setExecutable(getClearToolCommand()); + commandLine.createArgument().setValue(COMMAND_UPDATE); + + // Check the command line options + checkOptions(commandLine); + + // For debugging + getProject().log(commandLine.toString(), Project.MSG_DEBUG); + + if (!getFailOnErr()) { + getProject().log("Ignoring any errors that occur for: " + + getViewPathBasename(), Project.MSG_VERBOSE); + } + result = run(commandLine); + if (Execute.isFailure(result) && getFailOnErr()) { + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + } + + /** + * Check the command line options. + */ + private void checkOptions(Commandline cmd) { + // ClearCase items + if (getGraphical()) { + // -graphical + cmd.createArgument().setValue(FLAG_GRAPHICAL); + } else { + if (getOverwrite()) { + // -overwrite + cmd.createArgument().setValue(FLAG_OVERWRITE); + } else { + if (getRename()) { + // -rename + cmd.createArgument().setValue(FLAG_RENAME); + } else { + // -noverwrite + cmd.createArgument().setValue(FLAG_NOVERWRITE); + } + } + + if (getCurrentTime()) { + // -ctime + cmd.createArgument().setValue(FLAG_CURRENTTIME); + } else { + if (getPreserveTime()) { + // -ptime + cmd.createArgument().setValue(FLAG_PRESERVETIME); + } + } + + // -log logname + getLogCommand(cmd); + } + + // viewpath + cmd.createArgument().setValue(getViewPath()); + } + + /** + * If true, displays a graphical dialog during the update. + * + * @param graphical the status to set the flag to + */ + public void setGraphical(boolean graphical) { + mGraphical = graphical; + } + + /** + * Get graphical flag status + * + * @return boolean containing status of graphical flag + */ + public boolean getGraphical() { + return mGraphical; + } + + /** + * If true, overwrite hijacked files. + * + * @param ow the status to set the flag to + */ + public void setOverwrite(boolean ow) { + mOverwrite = ow; + } + + /** + * Get overwrite hijacked files status + * + * @return boolean containing status of overwrite flag + */ + public boolean getOverwrite() { + return mOverwrite; + } + + /** + * If true, hijacked files are renamed with a .keep extension. + * + * @param ren the status to set the flag to + */ + public void setRename(boolean ren) { + mRename = ren; + } + + /** + * Get rename hijacked files status + * + * @return boolean containing status of rename flag + */ + public boolean getRename() { + return mRename; + } + + /** + * If true, modification time should be written as the current time. + * Either currenttime or preservetime can be specified. + * + * @param ct the status to set the flag to + */ + public void setCurrentTime(boolean ct) { + mCtime = ct; + } + + /** + * Get current time status + * + * @return boolean containing status of current time flag + */ + public boolean getCurrentTime() { + return mCtime; + } + + /** + * If true, modification time should be preserved from the VOB time. + * Either currenttime or preservetime can be specified. + * + * @param pt the status to set the flag to + */ + public void setPreserveTime(boolean pt) { + mPtime = pt; + } + + /** + * Get preserve time status + * + * @return boolean containing status of preserve time flag + */ + public boolean getPreserveTime() { + return mPtime; + } + + /** + * Sets the log file where cleartool records + * the status of the command. + * + * @param log the path to the log file + */ + public void setLog(String log) { + mLog = log; + } + + /** + * Get log file + * + * @return String containing the path to the log file + */ + public String getLog() { + return mLog; + } + + + /** + * Get the 'log' command + * + * @param cmd containing the command line string with or without the log flag and path appended + */ + private void getLogCommand(Commandline cmd) { + if (getLog() == null) { + return; + } else { + /* Had to make two separate commands here because if a space is + inserted between the flag and the value, it is treated as a + Windows filename with a space and it is enclosed in double + quotes ("). This breaks clearcase. + */ + cmd.createArgument().setValue(FLAG_LOG); + cmd.createArgument().setValue(getLog()); + } + } + + /** + * -graphical flag -- display graphical dialog during update operation + */ + public static final String FLAG_GRAPHICAL = "-graphical"; + /** + * -log flag -- file to log status to + */ + public static final String FLAG_LOG = "-log"; + /** + * -overwrite flag -- overwrite hijacked files + */ + public static final String FLAG_OVERWRITE = "-overwrite"; + /** + * -noverwrite flag -- do not overwrite hijacked files + */ + public static final String FLAG_NOVERWRITE = "-noverwrite"; + /** + * -rename flag -- rename hijacked files with .keep extension + */ + public static final String FLAG_RENAME = "-rename"; + /** + * -ctime flag -- modified time is written as the current time + */ + public static final String FLAG_CURRENTTIME = "-ctime"; + /** + * -ptime flag -- modified time is written as the VOB time + */ + public static final String FLAG_PRESERVETIME = "-ptime"; + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/ClearCase.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/ClearCase.java new file mode 100644 index 00000000..d81e505c --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/ClearCase.java @@ -0,0 +1,242 @@ +/* + * 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.tools.ant.taskdefs.optional.clearcase; + +import java.io.File; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.ExecTask; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.taskdefs.LogStreamHandler; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.util.FileUtils; + + + +/** + * A base class for creating tasks for executing commands on ClearCase. + * <p> + * The class extends the 'exec' task as it operates by executing the cleartool program + * supplied with ClearCase. By default the task expects the cleartool executable to be + * in the path, * you can override this be specifying the cleartooldir attribute. + * </p> + * <p> + * This class provides set and get methods for the 'viewpath' and 'objselect' + * attribute. It also contains constants for the flags that can be passed to + * cleartool. + * </p> + * + */ +public abstract class ClearCase extends Task { + private String mClearToolDir = ""; + private String mviewPath = null; + private String mobjSelect = null; + private static int pcnt = 0; + private boolean mFailonerr = true; + /** + * Set the directory where the cleartool executable is located. + * + * @param dir the directory containing the cleartool executable + */ + public final void setClearToolDir(String dir) { + mClearToolDir = FileUtils.translatePath(dir); + } + + /** + * Builds and returns the command string to execute cleartool + * + * @return String containing path to the executable + */ + protected final String getClearToolCommand() { + String toReturn = mClearToolDir; + if (!toReturn.equals("") && !toReturn.endsWith("/")) { + toReturn += "/"; + } + + toReturn += CLEARTOOL_EXE; + + return toReturn; + } + + /** + * Set the path to the item in a ClearCase view to operate on. + * + * @param viewPath Path to the view directory or file + */ + public final void setViewPath(String viewPath) { + mviewPath = viewPath; + } + + /** + * Get the path to the item in a clearcase view + * + * @return mviewPath + */ + public String getViewPath() { + return mviewPath; + } + + /** + * Get the basename path of the item in a clearcase view + * + * @return basename + */ + public String getViewPathBasename() { + return (new File(mviewPath)).getName(); + } + + /** + * Set the object to operate on. + * + * @param objSelect object to operate on + */ + public final void setObjSelect(String objSelect) { + mobjSelect = objSelect; + } + + /** + * Get the object to operate on + * + * @return mobjSelect + */ + public String getObjSelect() { + return mobjSelect; + } + + /** + * Execute the given command are return success or failure + * @param cmd command line to execute + * @return the exit status of the subprocess or <code>INVALID</code> + */ + protected int run(Commandline cmd) { + try { + Project aProj = getProject(); + Execute exe + = new Execute(new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_WARN)); + exe.setAntRun(aProj); + exe.setWorkingDirectory(aProj.getBaseDir()); + exe.setCommandline(cmd.getCommandline()); + return exe.execute(); + } catch (java.io.IOException e) { + throw new BuildException(e, getLocation()); + } + } + + /** + * Execute the given command, and return it's output + * @param cmdline command line to execute + * @return output of the command line + */ + protected String runS(Commandline cmdline) { + String outV = "opts.cc.runS.output" + pcnt++; + ExecTask exe = new ExecTask(this); + Commandline.Argument arg = exe.createArg(); + + exe.setExecutable(cmdline.getExecutable()); + arg.setLine(Commandline.toString(cmdline.getArguments())); + exe.setOutputproperty(outV); + exe.execute(); + + return getProject().getProperty(outV); + } + /** + * If true, command will throw an exception on failure. + * + * @param failonerr the status to set the flag to + * @since ant 1.6.1 + */ + public void setFailOnErr(boolean failonerr) { + mFailonerr = failonerr; + } + + /** + * Get failonerr flag status + * + * @return boolean containing status of failonerr flag + * @since ant 1.6.1 + */ + public boolean getFailOnErr() { + return mFailonerr; + } + + /** + * Constant for the thing to execute + */ + private static final String CLEARTOOL_EXE = "cleartool"; + /** + * The 'Update' command + */ + public static final String COMMAND_UPDATE = "update"; + /** + * The 'Checkout' command + */ + public static final String COMMAND_CHECKOUT = "checkout"; + /** + * The 'Checkin' command + */ + public static final String COMMAND_CHECKIN = "checkin"; + /** + * The 'UndoCheckout' command + */ + public static final String COMMAND_UNCHECKOUT = "uncheckout"; + /** + * The 'Lock' command + */ + public static final String COMMAND_LOCK = "lock"; + /** + * The 'Unlock' command + */ + public static final String COMMAND_UNLOCK = "unlock"; + /** + * The 'Mkbl' command + */ + public static final String COMMAND_MKBL = "mkbl"; + /** + * The 'Mklabel' command + */ + public static final String COMMAND_MKLABEL = "mklabel"; + /** + * The 'Mklbtype' command + */ + public static final String COMMAND_MKLBTYPE = "mklbtype"; + /** + * The 'Rmtype' command + */ + public static final String COMMAND_RMTYPE = "rmtype"; + /** + * The 'LsCheckout' command + */ + public static final String COMMAND_LSCO = "lsco"; + /** + * The 'Mkelem' command + */ + public static final String COMMAND_MKELEM = "mkelem"; + /** + * The 'Mkattr' command + */ + public static final String COMMAND_MKATTR = "mkattr"; + /** + * The 'Mkdir' command + */ + public static final String COMMAND_MKDIR = "mkdir"; + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/AntAnalyzer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/AntAnalyzer.java new file mode 100644 index 00000000..341f6707 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/AntAnalyzer.java @@ -0,0 +1,144 @@ +/* + * 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.tools.ant.taskdefs.optional.depend; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.depend.AbstractAnalyzer; + +/** + * An analyzer which uses the depend task's bytecode classes to analyze + * dependencies + * + */ +public class AntAnalyzer extends AbstractAnalyzer { + /** + * Default constructor + */ + public AntAnalyzer() { + } + + /** + * Determine the dependencies of the configured root classes. + * + * @param files a vector to be populated with the files which contain + * the dependency classes + * @param classes a vector to be populated with the names of the + * dependency classes. + */ + protected void determineDependencies(Vector<File> files, Vector<String> classes) { + // we get the root classes and build up a set of + // classes upon which they depend + Hashtable<String, String> dependencies = new Hashtable<String, String>(); + Hashtable<File, File> containers = new Hashtable<File, File>(); + Hashtable<String, String> toAnalyze = new Hashtable<String, String>(); + for (Enumeration<String> e = getRootClasses(); e.hasMoreElements();) { + String classname = e.nextElement(); + toAnalyze.put(classname, classname); + } + + int count = 0; + int maxCount = isClosureRequired() ? MAX_LOOPS : 1; + Hashtable<String, String> analyzedDeps = null; + while (toAnalyze.size() != 0 && count++ < maxCount) { + analyzedDeps = new Hashtable<String, String>(); + for (Enumeration<String> e = toAnalyze.keys(); e.hasMoreElements();) { + String classname = e.nextElement(); + dependencies.put(classname, classname); + try { + File container = getClassContainer(classname); + if (container == null) { + continue; + } + containers.put(container, container); + + ZipFile zipFile = null; + InputStream inStream = null; + try { + if (container.getName().endsWith(".class")) { + inStream = new FileInputStream(container.getPath()); + } else { + zipFile = new ZipFile(container.getPath()); + String entryName + = classname.replace('.', '/') + ".class"; + ZipEntry entry = new ZipEntry(entryName); + inStream + = zipFile.getInputStream(entry); + } + ClassFile classFile = new ClassFile(); + classFile.read(inStream); + for (String dependency : classFile.getClassRefs()) { + analyzedDeps.put(dependency, dependency); + } + } finally { + FileUtils.close(inStream); + if (zipFile != null) { + zipFile.close(); + } + } + } catch (IOException ioe) { + // ignore + } + } + + toAnalyze.clear(); + + // now recover all the dependencies collected and add to the list. + for (String className : analyzedDeps.values()) { + if (!dependencies.containsKey(className)) { + toAnalyze.put(className, className); + } + } + } + + // pick up the last round of dependencies that were determined + for (String className : analyzedDeps.values()) { + dependencies.put(className, className); + } + + files.removeAllElements(); + for (File f : containers.keySet()) { + files.add(f); + } + + classes.removeAllElements(); + for (String dependency :dependencies.keySet()) { + classes.add(dependency); + } + } + + /** + * Indicate if this analyzer can determine dependent files. + * + * @return true if the analyzer provides dependency file information. + */ + protected boolean supportsFileDependencies() { + return true; + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFile.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFile.java new file mode 100644 index 00000000..858ce03e --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFile.java @@ -0,0 +1,121 @@ +/* + * 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.tools.ant.taskdefs.optional.depend; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Vector; + +import org.apache.tools.ant.taskdefs.optional.depend.constantpool.ClassCPInfo; +import org.apache.tools.ant.taskdefs.optional.depend.constantpool.ConstantPool; +import org.apache.tools.ant.taskdefs.optional.depend.constantpool.ConstantPoolEntry; + +/** + * A ClassFile object stores information about a Java class. The class may + * be read from a DataInputStream.and written to a DataOutputStream. These + * are usually streams from a Java class file or a class file component of a + * Jar file. + * + */ +public class ClassFile { + + /** The Magic Value that marks the start of a Java class file */ + private static final int CLASS_MAGIC = 0xCAFEBABE; + + /** This class' constant pool. */ + private ConstantPool constantPool; + + /** The class name for this class. */ + private String className; + + /** + * Read the class from a data stream. This method takes an InputStream + * as input and parses the class from the stream. <p> + * + * + * + * @param stream an InputStream from which the class will be read + * @exception IOException if there is a problem reading from the given + * stream. + * @exception ClassFormatError if the class cannot be parsed correctly + */ + public void read(InputStream stream) throws IOException, ClassFormatError { + DataInputStream classStream = new DataInputStream(stream); + + if (classStream.readInt() != CLASS_MAGIC) { + throw new ClassFormatError("No Magic Code Found " + + "- probably not a Java class file."); + } + + // right we have a good looking class file. + /* int minorVersion = */ classStream.readUnsignedShort(); + /* int majorVersion = */ classStream.readUnsignedShort(); + + // read the constant pool in and resolve it + constantPool = new ConstantPool(); + + constantPool.read(classStream); + constantPool.resolve(); + + /* int accessFlags = */ classStream.readUnsignedShort(); + int thisClassIndex = classStream.readUnsignedShort(); + /* int superClassIndex = */ classStream.readUnsignedShort(); + ClassCPInfo classInfo + = (ClassCPInfo) constantPool.getEntry(thisClassIndex); + className = classInfo.getClassName(); + } + + + /** + * Get the classes which this class references. + * + * @return a vector of class names which this class references + */ + public Vector<String> getClassRefs() { + + Vector<String> classRefs = new Vector<String>(); + + final int size = constantPool.size(); + for (int i = 0; i < size; ++i) { + ConstantPoolEntry entry = constantPool.getEntry(i); + + if (entry != null + && entry.getTag() == ConstantPoolEntry.CONSTANT_CLASS) { + ClassCPInfo classEntry = (ClassCPInfo) entry; + + if (!classEntry.getClassName().equals(className)) { + classRefs.add( + ClassFileUtils.convertSlashName(classEntry.getClassName())); + } + } + } + + return classRefs; + } + + /** + * Get the class' fully qualified name in dot format. + * + * @return the class name in dot format (eg. java.lang.Object) + */ + public String getFullClassName() { + return ClassFileUtils.convertSlashName(className); + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileIterator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileIterator.java new file mode 100644 index 00000000..92fc191b --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileIterator.java @@ -0,0 +1,33 @@ +/* + * 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.tools.ant.taskdefs.optional.depend; + +/** + * Iterator interface for iterating over a set of class files + * + */ +public interface ClassFileIterator { + + /** + * Get the next class file in the iteration + * + * @return the next class file in the iteration + */ + ClassFile getNextClassFile(); +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileUtils.java new file mode 100644 index 00000000..c6eec6cc --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileUtils.java @@ -0,0 +1,52 @@ +/* + * 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.tools.ant.taskdefs.optional.depend; + +/** + * Utility class file routines. This class provides a number of static + * utility methods to convert between the formats used in the Java class + * file format and those commonly used in Java programming. + * + * + */ +// CheckStyle:HideUtilityClassConstructorCheck OFF (bc) +public class ClassFileUtils { + + /** + * Convert a class name from class file slash notation to java source + * file dot notation. + * + * @param name the class name in slash notation org/apache/ant + * @return the class name in dot notation (eg. java.lang.Object). + */ + public static String convertSlashName(String name) { + return name.replace('\\', '.').replace('/', '.'); + } + + /** + * Convert a class name from java source file dot notation to class file + * slash notation.. + * + * @param dotName the class name in dot notation (eg. java.lang.Object). + * @return the class name in slash notation (eg. java/lang/Object). + */ + public static String convertDotName(String dotName) { + return dotName.replace('.', '/'); + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java new file mode 100644 index 00000000..cc65129f --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java @@ -0,0 +1,917 @@ +/* + * 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.tools.ant.taskdefs.optional.depend; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import org.apache.tools.ant.AntClassLoader; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.MatchingTask; +import org.apache.tools.ant.taskdefs.rmic.DefaultRmicAdapter; +import org.apache.tools.ant.taskdefs.rmic.WLRmic; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.Reference; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.depend.DependencyAnalyzer; + +/** + * Generates a dependency file for a given set of classes. + * + */ +public class Depend extends MatchingTask { + private static final int ONE_SECOND = 1000; + + /** + * A class (struct) user to manage information about a class + * + */ + private static class ClassFileInfo { + /** The file where the class file is stored in the file system */ + private File absoluteFile; + + /** The Java class name of this class */ + private String className; + + /** The source File containing this class */ + private File sourceFile; + + /** if user has been warned about this file not having a source file */ + private boolean isUserWarned = false; + } + + /** The path where source files exist */ + private Path srcPath; + + /** The path where compiled class files exist. */ + private Path destPath; + + /** The directory which contains the dependency cache. */ + private File cache; + + /** The list of source paths derived from the srcPath field. */ + private String[] srcPathList; + + /** + * A map which gives for every class a list of the class which it + * affects. + */ + private Hashtable affectedClassMap; + + /** A map which gives information about a class */ + private Hashtable classFileInfoMap; + + /** + * A map which gives the list of jars and classes from the classpath + * that a class depends upon + */ + private Hashtable classpathDependencies; + + /** The list of classes which are out of date. */ + private Hashtable outOfDateClasses; + + /** + * indicates that the dependency relationships should be extended beyond + * direct dependencies to include all classes. So if A directly affects + * B and B directly affects C, then A indirectly affects C. + */ + private boolean closure = false; + + /** + * flag to enable warning if we encounter RMI stubs + */ + private boolean warnOnRmiStubs = true; + + /** + * Flag which controls whether the reversed dependencies should be + * dumped to the log + */ + private boolean dump = false; + + /** The classpath to look for additional dependencies */ + private Path dependClasspath; + + /** constants used with the cache file */ + private static final String CACHE_FILE_NAME = "dependencies.txt"; + /** String Used to separate classnames in the dependency file */ + private static final String CLASSNAME_PREPEND = "||:"; + + /** + * Set the classpath to be used for this dependency check. + * + * @param classpath the classpath to be used when checking for + * dependencies on elements in the classpath + */ + public void setClasspath(Path classpath) { + if (dependClasspath == null) { + dependClasspath = classpath; + } else { + dependClasspath.append(classpath); + } + } + + /** + * Gets the classpath to be used for this dependency check. + * + * @return the current dependency classpath + */ + public Path getClasspath() { + return dependClasspath; + } + + /** + * Adds a classpath to be used for this dependency check. + * + * @return A path object to be configured by Ant + */ + public Path createClasspath() { + if (dependClasspath == null) { + dependClasspath = new Path(getProject()); + } + return dependClasspath.createPath(); + } + + /** + * Adds a reference to a classpath defined elsewhere. + * + * @param r a reference to a path object to be used as the depend + * classpath + */ + public void setClasspathRef(Reference r) { + createClasspath().setRefid(r); + } + + /** + * Flag to set to true if you want dependency issues with RMI + * stubs to appear at warning level. + * @param warnOnRmiStubs if true set dependency issues to appear at warning level. + * @since Ant1.7 + */ + public void setWarnOnRmiStubs(boolean warnOnRmiStubs) { + this.warnOnRmiStubs = warnOnRmiStubs; + } + + /** + * Read the dependencies from cache file + * + * @return a collection of class dependencies + * @exception IOException if the dependency file cannot be read + */ + private Hashtable readCachedDependencies(File depFile) throws IOException { + Hashtable dependencyMap = new Hashtable(); + + BufferedReader in = null; + try { + in = new BufferedReader(new FileReader(depFile)); + String line = null; + Vector dependencyList = null; + String className = null; + int prependLength = CLASSNAME_PREPEND.length(); + while ((line = in.readLine()) != null) { + if (line.startsWith(CLASSNAME_PREPEND)) { + dependencyList = new Vector(); + className = line.substring(prependLength); + dependencyMap.put(className, dependencyList); + } else { + dependencyList.addElement(line); + } + } + } finally { + FileUtils.close(in); + } + + return dependencyMap; + } + + /** + * Write the dependencies to cache file + * + * @param dependencyMap the map of dependencies to be written out. + * @exception IOException if the dependency file cannot be written out. + */ + private void writeCachedDependencies(Hashtable dependencyMap) + throws IOException { + if (cache != null) { + BufferedWriter pw = null; + try { + cache.mkdirs(); + File depFile = new File(cache, CACHE_FILE_NAME); + + pw = new BufferedWriter(new FileWriter(depFile)); + Enumeration e = dependencyMap.keys(); + while (e.hasMoreElements()) { + String className = (String) e.nextElement(); + + pw.write(CLASSNAME_PREPEND + className); + pw.newLine(); + + Vector dependencyList + = (Vector) dependencyMap.get(className); + int size = dependencyList.size(); + for (int x = 0; x < size; x++) { + pw.write(String.valueOf(dependencyList.elementAt(x))); + pw.newLine(); + } + } + } finally { + FileUtils.close(pw); + } + } + } + + /** + * Get the classpath for dependency checking. + * + * This method removes the dest dirs if it is given from the dependency classpath + */ + private Path getCheckClassPath() { + if (dependClasspath == null) { + return null; + } + + String[] destPathElements = destPath.list(); + String[] classpathElements = dependClasspath.list(); + String checkPath = ""; + for (int i = 0; i < classpathElements.length; ++i) { + String element = classpathElements[i]; + boolean inDestPath = false; + for (int j = 0; j < destPathElements.length && !inDestPath; ++j) { + inDestPath = destPathElements[j].equals(element); + } + if (!inDestPath) { + if (checkPath.length() == 0) { + checkPath = element; + } else { + checkPath += ":" + element; + } + } + } + + Path p = null; + if (checkPath.length() > 0) { + p = new Path(getProject(), checkPath); + } + + log("Classpath without dest dir is " + p, Project.MSG_DEBUG); + return p; + } + + /** + * Determine the dependencies between classes. Class dependencies are + * determined by examining the class references in a class file to other + * classes. + * + * This method sets up the following fields + * <ul> + * <li>affectedClassMap - the list of classes each class affects</li> + * <li>classFileInfoMap - information about each class</li> + * <li>classpathDependencies - the list of jars and classes from the + * classpath that each class depends upon.</li> + * </ul> + * + * If required, the dependencies are written to the cache. + * + * @exception IOException if either the dependencies cache or the class + * files cannot be read or written + */ + private void determineDependencies() throws IOException { + affectedClassMap = new Hashtable(); + classFileInfoMap = new Hashtable(); + boolean cacheDirty = false; + + Hashtable dependencyMap = new Hashtable(); + File cacheFile = null; + boolean cacheFileExists = true; + long cacheLastModified = Long.MAX_VALUE; + + // read the dependency cache from the disk + if (cache != null) { + cacheFile = new File(cache, CACHE_FILE_NAME); + cacheFileExists = cacheFile.exists(); + cacheLastModified = cacheFile.lastModified(); + if (cacheFileExists) { + dependencyMap = readCachedDependencies(cacheFile); + } + } + Enumeration classfileEnum = getClassFiles(destPath).elements(); + while (classfileEnum.hasMoreElements()) { + ClassFileInfo info = (ClassFileInfo) classfileEnum.nextElement(); + log("Adding class info for " + info.className, Project.MSG_DEBUG); + classFileInfoMap.put(info.className, info); + + Vector dependencyList = null; + + if (cache != null) { + // try to read the dependency info from the map if it is + // not out of date + if (cacheFileExists + && cacheLastModified > info.absoluteFile.lastModified()) { + // depFile exists and is newer than the class file + // need to get dependency list from the map. + dependencyList = (Vector) dependencyMap.get(info.className); + } + } + + if (dependencyList == null) { + // not cached - so need to read directly from the class file + DependencyAnalyzer analyzer = new AntAnalyzer(); + analyzer.addRootClass(info.className); + analyzer.addClassPath(destPath); + analyzer.setClosure(false); + dependencyList = new Vector(); + Enumeration depEnum = analyzer.getClassDependencies(); + while (depEnum.hasMoreElements()) { + Object o = depEnum.nextElement(); + dependencyList.addElement(o); + log("Class " + info.className + " depends on " + o, + Project.MSG_DEBUG); + } + cacheDirty = true; + dependencyMap.put(info.className, dependencyList); + } + + // This class depends on each class in the dependency list. For each + // one of those, add this class into their affected classes list + Enumeration depEnum = dependencyList.elements(); + while (depEnum.hasMoreElements()) { + String dependentClass = (String) depEnum.nextElement(); + + Hashtable affectedClasses + = (Hashtable) affectedClassMap.get(dependentClass); + if (affectedClasses == null) { + affectedClasses = new Hashtable(); + affectedClassMap.put(dependentClass, affectedClasses); + } + + affectedClasses.put(info.className, info); + log(dependentClass + " affects " + info.className, + Project.MSG_DEBUG); + } + } + + classpathDependencies = null; + Path checkPath = getCheckClassPath(); + if (checkPath != null) { + // now determine which jars each class depends upon + classpathDependencies = new Hashtable(); + AntClassLoader loader = null; + try { + loader = getProject().createClassLoader(checkPath); + + Hashtable classpathFileCache = new Hashtable(); + Object nullFileMarker = new Object(); + for (Enumeration e = dependencyMap.keys(); e.hasMoreElements();) { + String className = (String) e.nextElement(); + log("Determining classpath dependencies for " + className, + Project.MSG_DEBUG); + Vector dependencyList = (Vector) dependencyMap.get(className); + Hashtable dependencies = new Hashtable(); + classpathDependencies.put(className, dependencies); + Enumeration e2 = dependencyList.elements(); + while (e2.hasMoreElements()) { + String dependency = (String) e2.nextElement(); + log("Looking for " + dependency, Project.MSG_DEBUG); + Object classpathFileObject + = classpathFileCache.get(dependency); + if (classpathFileObject == null) { + classpathFileObject = nullFileMarker; + + if (!dependency.startsWith("java.") + && !dependency.startsWith("javax.")) { + URL classURL + = loader.getResource(dependency.replace('.', '/') + ".class"); + log("URL is " + classURL, Project.MSG_DEBUG); + if (classURL != null) { + if (classURL.getProtocol().equals("jar")) { + String jarFilePath = classURL.getFile(); + int classMarker = jarFilePath.indexOf('!'); + jarFilePath = jarFilePath.substring(0, classMarker); + if (jarFilePath.startsWith("file:")) { + classpathFileObject = new File( + FileUtils.getFileUtils().fromURI(jarFilePath)); + } else { + throw new IOException( + "Bizarre nested path in jar: protocol: " + + jarFilePath); + } + } else if (classURL.getProtocol().equals("file")) { + classpathFileObject = new File( + FileUtils.getFileUtils() + .fromURI(classURL.toExternalForm())); + } + log("Class " + className + + " depends on " + classpathFileObject + + " due to " + dependency, Project.MSG_DEBUG); + } + } else { + log("Ignoring base classlib dependency " + + dependency, Project.MSG_DEBUG); + } + classpathFileCache.put(dependency, classpathFileObject); + } + if (classpathFileObject != nullFileMarker) { + // we need to add this jar to the list for this class. + File jarFile = (File) classpathFileObject; + log("Adding a classpath dependency on " + jarFile, + Project.MSG_DEBUG); + dependencies.put(jarFile, jarFile); + } + } + } + } finally { + if (loader != null) { + loader.cleanup(); + } + } + } else { + log("No classpath to check", Project.MSG_DEBUG); + } + + // write the dependency cache to the disk + if (cache != null && cacheDirty) { + writeCachedDependencies(dependencyMap); + } + } + + /** + * Delete all the class files which are out of date, by way of their + * dependency on a class which is out of date + * + * @return the number of files deleted. + */ + private int deleteAllAffectedFiles() { + int count = 0; + for (Enumeration e = outOfDateClasses.elements(); e.hasMoreElements();) { + String className = (String) e.nextElement(); + count += deleteAffectedFiles(className); + ClassFileInfo classInfo + = (ClassFileInfo) classFileInfoMap.get(className); + if (classInfo != null && classInfo.absoluteFile.exists()) { + if (classInfo.sourceFile == null) { + warnOutOfDateButNotDeleted(classInfo, className, className); + } else { + classInfo.absoluteFile.delete(); + count++; + } + } + } + return count; + } + + /** + * Delete all the class files of classes which depend on the given class + * + * @param className the name of the class whose dependent classes will be + * deleted + * @return the number of class files removed + */ + private int deleteAffectedFiles(String className) { + int count = 0; + + Hashtable affectedClasses = (Hashtable) affectedClassMap.get(className); + if (affectedClasses == null) { + return count; + } + for (Enumeration e = affectedClasses.keys(); e.hasMoreElements();) { + String affectedClass = (String) e.nextElement(); + ClassFileInfo affectedClassInfo + = (ClassFileInfo) affectedClasses.get(affectedClass); + + if (!affectedClassInfo.absoluteFile.exists()) { + continue; + } + + if (affectedClassInfo.sourceFile == null) { + warnOutOfDateButNotDeleted(affectedClassInfo, affectedClass, className); + continue; + } + + log("Deleting file " + affectedClassInfo.absoluteFile.getPath() + + " since " + className + " out of date", Project.MSG_VERBOSE); + + affectedClassInfo.absoluteFile.delete(); + count++; + if (closure) { + count += deleteAffectedFiles(affectedClass); + } else { + // without closure we may delete an inner class but not the + // top level class which would not trigger a recompile. + + if (affectedClass.indexOf("$") == -1) { + continue; + } + // need to delete the main class + String topLevelClassName + = affectedClass.substring(0, affectedClass.indexOf("$")); + log("Top level class = " + topLevelClassName, + Project.MSG_VERBOSE); + ClassFileInfo topLevelClassInfo + = (ClassFileInfo) classFileInfoMap.get(topLevelClassName); + if (topLevelClassInfo != null + && topLevelClassInfo.absoluteFile.exists()) { + log("Deleting file " + + topLevelClassInfo.absoluteFile.getPath() + + " since one of its inner classes was removed", + Project.MSG_VERBOSE); + topLevelClassInfo.absoluteFile.delete(); + count++; + if (closure) { + count += deleteAffectedFiles(topLevelClassName); + } + } + } + } + return count; + } + + /** + * warn when a class is out of date, but not deleted as its source is unknown. + * MSG_WARN is the normal level, but we downgrade to MSG_VERBOSE for RMI files + * if {@link #warnOnRmiStubs is false} + * @param affectedClassInfo info about the affectd class + * @param affectedClass the name of the affected .class file + * @param className the file that is triggering the out of dateness + */ + private void warnOutOfDateButNotDeleted( + ClassFileInfo affectedClassInfo, String affectedClass, + String className) { + if (affectedClassInfo.isUserWarned) { + return; + } + int level = Project.MSG_WARN; + if (!warnOnRmiStubs) { + //downgrade warnings on RMI stublike classes, as they are generated + //by rmic, so there is no need to tell the user that their source is + //missing. + if (isRmiStub(affectedClass, className)) { + level = Project.MSG_VERBOSE; + } + } + log("The class " + affectedClass + " in file " + + affectedClassInfo.absoluteFile.getPath() + + " is out of date due to " + className + + " but has not been deleted because its source file" + + " could not be determined", level); + affectedClassInfo.isUserWarned = true; + } + + /** + * test for being an RMI stub + * @param affectedClass class being tested + * @param className possible origin of the RMI stub + * @return whether the class affectedClass is a RMI stub + */ + private boolean isRmiStub(String affectedClass, String className) { + return isStub(affectedClass, className, DefaultRmicAdapter.RMI_STUB_SUFFIX) + || isStub(affectedClass, className, DefaultRmicAdapter.RMI_SKEL_SUFFIX) + || isStub(affectedClass, className, WLRmic.RMI_STUB_SUFFIX) + || isStub(affectedClass, className, WLRmic.RMI_SKEL_SUFFIX); + } + + private boolean isStub(String affectedClass, String baseClass, String suffix) { + return (baseClass + suffix).equals(affectedClass); + } + + /** + * Dump the dependency information loaded from the classes to the Ant log + */ + private void dumpDependencies() { + log("Reverse Dependency Dump for " + affectedClassMap.size() + + " classes:", Project.MSG_DEBUG); + + Enumeration classEnum = affectedClassMap.keys(); + while (classEnum.hasMoreElements()) { + String className = (String) classEnum.nextElement(); + log(" Class " + className + " affects:", Project.MSG_DEBUG); + Hashtable affectedClasses + = (Hashtable) affectedClassMap.get(className); + Enumeration affectedClassEnum = affectedClasses.keys(); + while (affectedClassEnum.hasMoreElements()) { + String affectedClass = (String) affectedClassEnum.nextElement(); + ClassFileInfo info + = (ClassFileInfo) affectedClasses.get(affectedClass); + log(" " + affectedClass + " in " + + info.absoluteFile.getPath(), Project.MSG_DEBUG); + } + } + + if (classpathDependencies != null) { + log("Classpath file dependencies (Forward):", Project.MSG_DEBUG); + + Enumeration classpathEnum = classpathDependencies.keys(); + while (classpathEnum.hasMoreElements()) { + String className = (String) classpathEnum.nextElement(); + log(" Class " + className + " depends on:", Project.MSG_DEBUG); + Hashtable dependencies + = (Hashtable) classpathDependencies.get(className); + + Enumeration classpathFileEnum = dependencies.elements(); + while (classpathFileEnum.hasMoreElements()) { + File classpathFile = (File) classpathFileEnum.nextElement(); + log(" " + classpathFile.getPath(), Project.MSG_DEBUG); + } + } + } + } + + private void determineOutOfDateClasses() { + outOfDateClasses = new Hashtable(); + for (int i = 0; i < srcPathList.length; i++) { + File srcDir = getProject().resolveFile(srcPathList[i]); + if (srcDir.exists()) { + DirectoryScanner ds = this.getDirectoryScanner(srcDir); + String[] files = ds.getIncludedFiles(); + scanDir(srcDir, files); + } + } + + // now check classpath file dependencies + if (classpathDependencies == null) { + return; + } + + Enumeration classpathDepsEnum = classpathDependencies.keys(); + while (classpathDepsEnum.hasMoreElements()) { + String className = (String) classpathDepsEnum.nextElement(); + if (outOfDateClasses.containsKey(className)) { + continue; + } + ClassFileInfo info + = (ClassFileInfo) classFileInfoMap.get(className); + + // if we have no info about the class - it may have been deleted already and we + // are using cached info. + if (info != null) { + Hashtable dependencies + = (Hashtable) classpathDependencies.get(className); + for (Enumeration e2 = dependencies.elements(); e2.hasMoreElements();) { + File classpathFile = (File) e2.nextElement(); + if (classpathFile.lastModified() + > info.absoluteFile.lastModified()) { + log("Class " + className + + " is out of date with respect to " + + classpathFile, Project.MSG_DEBUG); + outOfDateClasses.put(className, className); + break; + } + } + } + } + } + + /** + * Does the work. + * + * @exception BuildException Thrown in case of an unrecoverable error. + */ + public void execute() throws BuildException { + try { + long start = System.currentTimeMillis(); + if (srcPath == null) { + throw new BuildException("srcdir attribute must be set", + getLocation()); + } + + srcPathList = srcPath.list(); + if (srcPathList.length == 0) { + throw new BuildException("srcdir attribute must be non-empty", + getLocation()); + } + + if (destPath == null) { + destPath = srcPath; + } + + if (cache != null && cache.exists() && !cache.isDirectory()) { + throw new BuildException("The cache, if specified, must " + + "point to a directory"); + } + + if (cache != null && !cache.exists()) { + cache.mkdirs(); + } + + determineDependencies(); + if (dump) { + dumpDependencies(); + } + determineOutOfDateClasses(); + int count = deleteAllAffectedFiles(); + + long duration = (System.currentTimeMillis() - start) / ONE_SECOND; + + final int summaryLogLevel; + if (count > 0) { + summaryLogLevel = Project.MSG_INFO; + } else { + summaryLogLevel = Project.MSG_DEBUG; + } + + log("Deleted " + count + " out of date files in " + + duration + " seconds", summaryLogLevel); + } catch (Exception e) { + throw new BuildException(e); + } + } + + /** + * Scans the directory looking for source files that are newer than + * their class files. The results are returned in the class variable + * compileList + * + * @param srcDir the source directory + * @param files the names of the files in the source dir which are to be + * checked. + */ + protected void scanDir(File srcDir, String[] files) { + + for (int i = 0; i < files.length; i++) { + File srcFile = new File(srcDir, files[i]); + if (files[i].endsWith(".java")) { + String filePath = srcFile.getPath(); + String className + = filePath.substring(srcDir.getPath().length() + 1, + filePath.length() - ".java".length()); + className = ClassFileUtils.convertSlashName(className); + ClassFileInfo info + = (ClassFileInfo) classFileInfoMap.get(className); + if (info == null) { + // there was no class file. add this class to the list + outOfDateClasses.put(className, className); + } else { + if (srcFile.lastModified() + > info.absoluteFile.lastModified()) { + outOfDateClasses.put(className, className); + } + } + } + } + } + + + /** + * Get the list of class files we are going to analyse. + * + * @param classLocations a path structure containing all the directories + * where classes can be found. + * @return a vector containing the classes to analyse. + */ + private Vector getClassFiles(Path classLocations) { + // break the classLocations into its components. + String[] classLocationsList = classLocations.list(); + + Vector classFileList = new Vector(); + + for (int i = 0; i < classLocationsList.length; ++i) { + File dir = new File(classLocationsList[i]); + if (dir.isDirectory()) { + addClassFiles(classFileList, dir, dir); + } + } + + return classFileList; + } + + /** + * Find the source file for a given class + * + * @param classname the classname in slash format. + * @param sourceFileKnownToExist if not null, a file already known to exist + * (saves call to .exists()) + */ + private File findSourceFile(String classname, File sourceFileKnownToExist) { + String sourceFilename; + int innerIndex = classname.indexOf("$"); + if (innerIndex != -1) { + sourceFilename = classname.substring(0, innerIndex) + ".java"; + } else { + sourceFilename = classname + ".java"; + } + + // search the various source path entries + for (int i = 0; i < srcPathList.length; ++i) { + File sourceFile = new File(srcPathList[i], sourceFilename); + if (sourceFile.equals(sourceFileKnownToExist) || sourceFile.exists()) { + return sourceFile; + } + } + return null; + } + + /** + * Add the list of class files from the given directory to the class + * file vector, including any subdirectories. + * + * @param classFileList a list of ClassFileInfo objects for all the + * files in the directory tree + * @param dir the directory tree to be searched, recursively, for class + * files + * @param root the root of the source tree. This is used to determine + * the absolute class name from the relative position in the + * source tree + */ + private void addClassFiles(Vector classFileList, File dir, File root) { + String[] filesInDir = dir.list(); + + if (filesInDir == null) { + return; + } + int length = filesInDir.length; + + int rootLength = root.getPath().length(); + File sourceFileKnownToExist = null; // speed optimization + for (int i = 0; i < length; ++i) { + File file = new File(dir, filesInDir[i]); + if (filesInDir[i].endsWith(".class")) { + ClassFileInfo info = new ClassFileInfo(); + info.absoluteFile = file; + String relativeName = file.getPath().substring( + rootLength + 1, + file.getPath().length() - ".class".length()); + info.className + = ClassFileUtils.convertSlashName(relativeName); + info.sourceFile = sourceFileKnownToExist = findSourceFile( + relativeName, sourceFileKnownToExist); + classFileList.addElement(info); + } else { + addClassFiles(classFileList, file, root); + } + } + } + + + /** + * Set the directories path to find the Java source files. + * + * @param srcPath the source path + */ + public void setSrcdir(Path srcPath) { + this.srcPath = srcPath; + } + + /** + * Set the destination directory where the compiled Java files exist. + * + * @param destPath the destination areas where build files are written + */ + public void setDestDir(Path destPath) { + this.destPath = destPath; + } + + /** + * Sets the dependency cache file. + * + * @param cache the dependency cache file + */ + public void setCache(File cache) { + this.cache = cache; + } + + /** + * If true, transitive dependencies are followed until the + * closure of the dependency set if reached. + * When not set, the depend task will only follow + * direct dependencies between classes. + * + * @param closure indicate if dependency closure is required. + */ + public void setClosure(boolean closure) { + this.closure = closure; + } + + /** + * If true, the dependency information will be written + * to the debug level log. + * + * @param dump set to true to dump dependency information to the log + */ + public void setDump(boolean dump) { + this.dump = dump; + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/DirectoryIterator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/DirectoryIterator.java new file mode 100644 index 00000000..ebf244a5 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/DirectoryIterator.java @@ -0,0 +1,164 @@ +/* + * 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.tools.ant.taskdefs.optional.depend; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Stack; +import java.util.Vector; + +/** + * An iterator which iterates through the contents of a java directory. The + * iterator should be created with the directory at the root of the Java + * namespace. + * + */ +public class DirectoryIterator implements ClassFileIterator { + + /** + * This is a stack of current iterators supporting the depth first + * traversal of the directory tree. + */ + private Stack enumStack; + + /** + * The current directory iterator. As directories encounter lower level + * directories, the current iterator is pushed onto the iterator stack + * and a new iterator over the sub directory becomes the current + * directory. This implements a depth first traversal of the directory + * namespace. + */ + private Enumeration currentEnum; + + /** + * Creates a directory iterator. The directory iterator is created to + * scan the root directory. If the changeInto flag is given, then the + * entries returned will be relative to this directory and not the + * current directory. + * + * @param rootDirectory the root if the directory namespace which is to + * be iterated over + * @param changeInto if true then the returned entries will be relative + * to the rootDirectory and not the current directory. + * @exception IOException if there is a problem reading the directory + * information. + */ + public DirectoryIterator(File rootDirectory, boolean changeInto) + throws IOException { + super(); + + enumStack = new Stack(); + + Vector filesInRoot = getDirectoryEntries(rootDirectory); + + currentEnum = filesInRoot.elements(); + } + + /** + * Get a vector covering all the entries (files and subdirectories in a + * directory). + * + * @param directory the directory to be scanned. + * @return a vector containing File objects for each entry in the + * directory. + */ + private Vector getDirectoryEntries(File directory) { + Vector files = new Vector(); + + // File[] filesInDir = directory.listFiles(); + String[] filesInDir = directory.list(); + + if (filesInDir != null) { + int length = filesInDir.length; + + for (int i = 0; i < length; ++i) { + files.addElement(new File(directory, filesInDir[i])); + } + } + + return files; + } + + /** + * Template method to allow subclasses to supply elements for the + * iteration. The directory iterator maintains a stack of iterators + * covering each level in the directory hierarchy. The current iterator + * covers the current directory being scanned. If the next entry in that + * directory is a subdirectory, the current iterator is pushed onto the + * stack and a new iterator is created for the subdirectory. If the + * entry is a file, it is returned as the next element and the iterator + * remains valid. If there are no more entries in the current directory, + * the topmost iterator on the stack is popped off to become the + * current iterator. + * + * @return the next ClassFile in the iteration. + */ + public ClassFile getNextClassFile() { + ClassFile nextElement = null; + + try { + while (nextElement == null) { + if (currentEnum.hasMoreElements()) { + File element = (File) currentEnum.nextElement(); + + if (element.isDirectory()) { + + // push the current iterator onto the stack and then + // iterate through this directory. + enumStack.push(currentEnum); + + Vector files = getDirectoryEntries(element); + + currentEnum = files.elements(); + } else { + + // we have a file. create a stream for it + FileInputStream inFileStream + = new FileInputStream(element); + + if (element.getName().endsWith(".class")) { + + // create a data input stream from the jar + // input stream + ClassFile javaClass = new ClassFile(); + + javaClass.read(inFileStream); + + nextElement = javaClass; + } + } + } else { + // this iterator is exhausted. Can we pop one off the stack + if (enumStack.empty()) { + break; + } else { + currentEnum = (Enumeration) enumStack.pop(); + } + } + } + } catch (IOException e) { + nextElement = null; + } + + return nextElement; + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/JarFileIterator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/JarFileIterator.java new file mode 100644 index 00000000..bc315d2e --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/JarFileIterator.java @@ -0,0 +1,89 @@ +/* + * 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.tools.ant.taskdefs.optional.depend; + +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * A class file iterator which iterates through the contents of a Java jar + * file. + * + */ +public class JarFileIterator implements ClassFileIterator { + /** The jar stream from the jar file being iterated over*/ + private ZipInputStream jarStream; + + /** + * Construct an iterator over a jar stream + * + * @param stream the basic input stream from which the Jar is received + * @exception IOException if the jar stream cannot be created + */ + public JarFileIterator(InputStream stream) throws IOException { + super(); + + jarStream = new ZipInputStream(stream); + } + + /** + * Get the next ClassFile object from the jar + * + * @return a ClassFile object describing the class from the jar + */ + public ClassFile getNextClassFile() { + ZipEntry jarEntry; + ClassFile nextElement = null; + + try { + jarEntry = jarStream.getNextEntry(); + + while (nextElement == null && jarEntry != null) { + String entryName = jarEntry.getName(); + + if (!jarEntry.isDirectory() && entryName.endsWith(".class")) { + + // create a data input stream from the jar input stream + ClassFile javaClass = new ClassFile(); + + javaClass.read(jarStream); + + nextElement = javaClass; + } else { + + jarEntry = jarStream.getNextEntry(); + } + } + } catch (IOException e) { + String message = e.getMessage(); + String text = e.getClass().getName(); + + if (message != null) { + text += ": " + message; + } + + throw new RuntimeException("Problem reading JAR file: " + text); + } + + return nextElement; + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ClassCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ClassCPInfo.java new file mode 100644 index 00000000..8abbfc82 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ClassCPInfo.java @@ -0,0 +1,93 @@ +/* + * 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.tools.ant.taskdefs.optional.depend.constantpool; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * The constant pool entry which stores class information. + * + */ +public class ClassCPInfo extends ConstantPoolEntry { + + /** + * The class' name. This will be only valid if the entry has been + * resolved against the constant pool. + */ + private String className; + + /** + * The index into the constant pool where this class' name is stored. If + * the class name is changed, this entry is invalid until this entry is + * connected to a constant pool. + */ + private int index; + + /** + * Constructor. Sets the tag value for this entry to type Class + */ + public ClassCPInfo() { + super(CONSTANT_CLASS, 1); + } + + /** + * Read the entry from a stream. + * + * @param cpStream the stream containing the constant pool entry to be + * read. + * @exception IOException thrown if there is a problem reading the entry + * from the stream. + */ + public void read(DataInputStream cpStream) throws IOException { + index = cpStream.readUnsignedShort(); + className = "unresolved"; + } + + /** + * Generate a string readable version of this entry + * + * @return string representation of this constant pool entry + */ + public String toString() { + return "Class Constant Pool Entry for " + className + "[" + index + "]"; + } + + /** + * Resolve this class info against the given constant pool. + * + * @param constantPool the constant pool with which to resolve the + * class. + */ + public void resolve(ConstantPool constantPool) { + className = ((Utf8CPInfo) constantPool.getEntry(index)).getValue(); + + super.resolve(constantPool); + } + + /** + * Get the class name of this entry. + * + * @return the class' name. + */ + public String getClassName() { + return className; + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantCPInfo.java new file mode 100644 index 00000000..6103422e --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantCPInfo.java @@ -0,0 +1,63 @@ +/* + * 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.tools.ant.taskdefs.optional.depend.constantpool; + +/** + * A Constant Pool entry which represents a constant value. + * + */ +public abstract class ConstantCPInfo extends ConstantPoolEntry { + + /** + * The entry's untyped value. Each subclass interprets the constant + * value based on the subclass's type. The value here must be + * compatible. + */ + private Object value; + + /** + * Initialise the constant entry. + * + * @param tagValue the constant pool entry type to be used. + * @param entries the number of constant pool entry slots occupied by + * this entry. + */ + protected ConstantCPInfo(int tagValue, int entries) { + super(tagValue, entries); + } + + /** + * Get the value of the constant. + * + * @return the value of the constant (untyped). + */ + public Object getValue() { + return value; + } + + /** + * Set the constant value. + * + * @param newValue the new untyped value of this constant. + */ + public void setValue(Object newValue) { + value = newValue; + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPool.java new file mode 100644 index 00000000..67d366b8 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPool.java @@ -0,0 +1,358 @@ +/* + * 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.tools.ant.taskdefs.optional.depend.constantpool; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * The constant pool of a Java class. The constant pool is a collection of + * constants used in a Java class file. It stores strings, constant values, + * class names, method names, field names etc. + * + * @see <a href="http://java.sun.com/docs/books/vmspec/">The Java Virtual + * Machine Specification</a> + */ +public class ConstantPool { + + /** The entries in the constant pool. */ + private final List<ConstantPoolEntry> entries = new ArrayList<ConstantPoolEntry>(); + + /** + * A Hashtable of UTF8 entries - used to get constant pool indexes of + * the UTF8 values quickly + */ + private final Map<String, Integer> utf8Indexes = new HashMap<String, Integer>(); + + /** Initialise the constant pool. */ + public ConstantPool() { + // The zero index is never present in the constant pool itself so + // we add a null entry for it + entries.add(null); + } + + /** + * Read the constant pool from a class input stream. + * + * @param classStream the DataInputStream of a class file. + * @exception IOException if there is a problem reading the constant pool + * from the stream + */ + public void read(DataInputStream classStream) throws IOException { + int numEntries = classStream.readUnsignedShort(); + + for (int i = 1; i < numEntries;) { + ConstantPoolEntry nextEntry + = ConstantPoolEntry.readEntry(classStream); + + i += nextEntry.getNumEntries(); + + addEntry(nextEntry); + } + } + + /** + * Get the size of the constant pool. + * + * @return the size of the constant pool + */ + public int size() { + return entries.size(); + } + + /** + * Add an entry to the constant pool. + * + * @param entry the new entry to be added to the constant pool. + * @return the index into the constant pool at which the entry is + * stored. + */ + public int addEntry(ConstantPoolEntry entry) { + int index = entries.size(); + + entries.add(entry); + + int numSlots = entry.getNumEntries(); + + // add null entries for any additional slots required. + for (int j = 0; j < numSlots - 1; ++j) { + entries.add(null); + } + + if (entry instanceof Utf8CPInfo) { + Utf8CPInfo utf8Info = (Utf8CPInfo) entry; + + utf8Indexes.put(utf8Info.getValue(), new Integer(index)); + } + + return index; + } + + /** + * Resolve the entries in the constant pool. Resolution of the constant + * pool involves transforming indexes to other constant pool entries + * into the actual data for that entry. + */ + public void resolve() { + for (ConstantPoolEntry poolInfo : entries) { + if (poolInfo != null && !poolInfo.isResolved()) { + poolInfo.resolve(this); + } + } + } + + + /** + * Get an constant pool entry at a particular index. + * + * @param index the index into the constant pool. + * @return the constant pool entry at that index. + */ + public ConstantPoolEntry getEntry(int index) { + return entries.get(index); + } + + /** + * Get the index of a given UTF8 constant pool entry. + * + * @param value the string value of the UTF8 entry. + * @return the index at which the given string occurs in the constant + * pool or -1 if the value does not occur. + */ + public int getUTF8Entry(String value) { + int index = -1; + Integer indexInteger = utf8Indexes.get(value); + + if (indexInteger != null) { + index = indexInteger.intValue(); + } + + return index; + } + + /** + * Get the index of a given CONSTANT_CLASS entry in the constant pool. + * + * @param className the name of the class for which the class entry + * index is required. + * @return the index at which the given class entry occurs in the + * constant pool or -1 if the value does not occur. + */ + public int getClassEntry(String className) { + int index = -1; + + final int size = entries.size(); + for (int i = 0; i < size && index == -1; ++i) { + Object element = entries.get(i); + + if (element instanceof ClassCPInfo) { + ClassCPInfo classinfo = (ClassCPInfo) element; + + if (classinfo.getClassName().equals(className)) { + index = i; + } + } + } + + return index; + } + + /** + * Get the index of a given constant value entry in the constant pool. + * + * @param constantValue the constant value for which the index is + * required. + * @return the index at which the given value entry occurs in the + * constant pool or -1 if the value does not occur. + */ + public int getConstantEntry(Object constantValue) { + int index = -1; + + final int size = entries.size(); + for (int i = 0; i < size && index == -1; ++i) { + Object element = entries.get(i); + + if (element instanceof ConstantCPInfo) { + ConstantCPInfo constantEntry = (ConstantCPInfo) element; + + if (constantEntry.getValue().equals(constantValue)) { + index = i; + } + } + } + + return index; + } + + /** + * Get the index of a given CONSTANT_METHODREF entry in the constant + * pool. + * + * @param methodClassName the name of the class which contains the + * method being referenced. + * @param methodName the name of the method being referenced. + * @param methodType the type descriptor of the method being referenced. + * @return the index at which the given method ref entry occurs in the + * constant pool or -1 if the value does not occur. + */ + public int getMethodRefEntry(String methodClassName, String methodName, + String methodType) { + int index = -1; + + final int size = entries.size(); + for (int i = 0; i < size && index == -1; ++i) { + Object element = entries.get(i); + + if (element instanceof MethodRefCPInfo) { + MethodRefCPInfo methodRefEntry = (MethodRefCPInfo) element; + + if (methodRefEntry.getMethodClassName().equals(methodClassName) + && methodRefEntry.getMethodName().equals(methodName) + && methodRefEntry.getMethodType().equals(methodType)) { + index = i; + } + } + } + + return index; + } + + /** + * Get the index of a given CONSTANT_INTERFACEMETHODREF entry in the + * constant pool. + * + * @param interfaceMethodClassName the name of the interface which + * contains the method being referenced. + * @param interfaceMethodName the name of the method being referenced. + * @param interfaceMethodType the type descriptor of the method being + * referenced. + * @return the index at which the given method ref entry occurs in the + * constant pool or -1 if the value does not occur. + */ + public int getInterfaceMethodRefEntry(String interfaceMethodClassName, + String interfaceMethodName, + String interfaceMethodType) { + int index = -1; + + final int size = entries.size(); + for (int i = 0; i < size && index == -1; ++i) { + Object element = entries.get(i); + + if (element instanceof InterfaceMethodRefCPInfo) { + InterfaceMethodRefCPInfo interfaceMethodRefEntry + = (InterfaceMethodRefCPInfo) element; + + if (interfaceMethodRefEntry.getInterfaceMethodClassName().equals( + interfaceMethodClassName) + && interfaceMethodRefEntry.getInterfaceMethodName().equals( + interfaceMethodName) + && interfaceMethodRefEntry.getInterfaceMethodType().equals( + interfaceMethodType)) { + index = i; + } + } + } + + return index; + } + + /** + * Get the index of a given CONSTANT_FIELDREF entry in the constant + * pool. + * + * @param fieldClassName the name of the class which contains the field + * being referenced. + * @param fieldName the name of the field being referenced. + * @param fieldType the type descriptor of the field being referenced. + * @return the index at which the given field ref entry occurs in the + * constant pool or -1 if the value does not occur. + */ + public int getFieldRefEntry(String fieldClassName, String fieldName, + String fieldType) { + int index = -1; + + final int size = entries.size(); + for (int i = 0; i < size && index == -1; ++i) { + Object element = entries.get(i); + + if (element instanceof FieldRefCPInfo) { + FieldRefCPInfo fieldRefEntry = (FieldRefCPInfo) element; + + if (fieldRefEntry.getFieldClassName().equals(fieldClassName) + && fieldRefEntry.getFieldName().equals(fieldName) + && fieldRefEntry.getFieldType().equals(fieldType)) { + index = i; + } + } + } + + return index; + } + + /** + * Get the index of a given CONSTANT_NAMEANDTYPE entry in the constant + * pool. + * + * @param name the name + * @param type the type + * @return the index at which the given NameAndType entry occurs in the + * constant pool or -1 if the value does not occur. + */ + public int getNameAndTypeEntry(String name, String type) { + int index = -1; + + final int size = entries.size(); + for (int i = 0; i < size && index == -1; ++i) { + Object element = entries.get(i); + + if (element instanceof NameAndTypeCPInfo) { + NameAndTypeCPInfo nameAndTypeEntry + = (NameAndTypeCPInfo) element; + + if (nameAndTypeEntry.getName().equals(name) + && nameAndTypeEntry.getType().equals(type)) { + index = i; + } + } + } + + return index; + } + + /** + * Dump the constant pool to a string. + * + * @return the constant pool entries as strings + */ + public String toString() { + StringBuilder sb = new StringBuilder("\n"); + final int size = entries.size(); + + for (int i = 0; i < size; ++i) { + sb.append("[" + i + "] = " + getEntry(i) + "\n"); + } + + return sb.toString(); + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPoolEntry.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPoolEntry.java new file mode 100644 index 00000000..26a0d094 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPoolEntry.java @@ -0,0 +1,242 @@ +/* + * 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.tools.ant.taskdefs.optional.depend.constantpool; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * An entry in the constant pool. This class contains a representation of the + * constant pool entries. It is an abstract base class for all the different + * forms of constant pool entry. + * + * @see ConstantPool + */ +public abstract class ConstantPoolEntry { + + /** Tag value for UTF8 entries. */ + public static final int CONSTANT_UTF8 = 1; + + /** Tag value for Integer entries. */ + public static final int CONSTANT_INTEGER = 3; + + /** Tag value for Float entries. */ + public static final int CONSTANT_FLOAT = 4; + + /** Tag value for Long entries. */ + public static final int CONSTANT_LONG = 5; + + /** Tag value for Double entries. */ + public static final int CONSTANT_DOUBLE = 6; + + /** Tag value for Class entries. */ + public static final int CONSTANT_CLASS = 7; + + /** Tag value for String entries. */ + public static final int CONSTANT_STRING = 8; + + /** Tag value for Field Reference entries. */ + public static final int CONSTANT_FIELDREF = 9; + + /** Tag value for Method Reference entries. */ + public static final int CONSTANT_METHODREF = 10; + + /** Tag value for Interface Method Reference entries. */ + public static final int CONSTANT_INTERFACEMETHODREF = 11; + + /** Tag value for Name and Type entries. */ + public static final int CONSTANT_NAMEANDTYPE = 12; + + /** Tag value for Method Handle entries */ + public static final int CONSTANT_METHODHANDLE = 15; + + /** Tag value for Method Type entries */ + public static final int CONSTANT_METHODTYPE = 16; + + /** Tag value for InvokeDynamic entries*/ + public static final int CONSTANT_INVOKEDYNAMIC = 18; + + /** + * This entry's tag which identifies the type of this constant pool + * entry. + */ + private int tag; + + /** + * The number of slots in the constant pool, occupied by this entry. + */ + private int numEntries; + + /** + * A flag which indicates if this entry has been resolved or not. + */ + private boolean resolved; + + /** + * Initialise the constant pool entry. + * + * @param tagValue the tag value which identifies which type of constant + * pool entry this is. + * @param entries the number of constant pool entry slots this entry + * occupies. + */ + public ConstantPoolEntry(int tagValue, int entries) { + tag = tagValue; + numEntries = entries; + resolved = false; + } + + /** + * Read a constant pool entry from a stream. This is a factory method + * which reads a constant pool entry form a stream and returns the + * appropriate subclass for the entry. + * + * @param cpStream the stream from which the constant pool entry is to + * be read. + * @return the appropriate ConstantPoolEntry subclass representing the + * constant pool entry from the stream. + * @exception IOException if the constant pool entry cannot be read + * from the stream + */ + public static ConstantPoolEntry readEntry(DataInputStream cpStream) + throws IOException { + ConstantPoolEntry cpInfo = null; + int cpTag = cpStream.readUnsignedByte(); + + switch (cpTag) { + + case CONSTANT_UTF8: + cpInfo = new Utf8CPInfo(); + + break; + case CONSTANT_INTEGER: + cpInfo = new IntegerCPInfo(); + + break; + case CONSTANT_FLOAT: + cpInfo = new FloatCPInfo(); + + break; + case CONSTANT_LONG: + cpInfo = new LongCPInfo(); + + break; + case CONSTANT_DOUBLE: + cpInfo = new DoubleCPInfo(); + + break; + case CONSTANT_CLASS: + cpInfo = new ClassCPInfo(); + + break; + case CONSTANT_STRING: + cpInfo = new StringCPInfo(); + + break; + case CONSTANT_FIELDREF: + cpInfo = new FieldRefCPInfo(); + + break; + case CONSTANT_METHODREF: + cpInfo = new MethodRefCPInfo(); + + break; + case CONSTANT_INTERFACEMETHODREF: + cpInfo = new InterfaceMethodRefCPInfo(); + + break; + case CONSTANT_NAMEANDTYPE: + cpInfo = new NameAndTypeCPInfo(); + + break; + case CONSTANT_METHODHANDLE: + cpInfo = new MethodHandleCPInfo(); + + break; + case CONSTANT_METHODTYPE: + cpInfo = new MethodTypeCPInfo(); + + break; + case CONSTANT_INVOKEDYNAMIC: + cpInfo = new InvokeDynamicCPInfo(); + + break; + default: + throw new ClassFormatError("Invalid Constant Pool entry Type " + + cpTag); + } + + cpInfo.read(cpStream); + + return cpInfo; + } + + /** + * Indicates whether this entry has been resolved. In general a constant + * pool entry can reference another constant pool entry by its index + * value. Resolution involves replacing this index value with the + * constant pool entry at that index. + * + * @return true if this entry has been resolved. + */ + public boolean isResolved() { + return resolved; + } + + /** + * Resolve this constant pool entry with respect to its dependents in + * the constant pool. + * + * @param constantPool the constant pool of which this entry is a member + * and against which this entry is to be resolved. + */ + public void resolve(ConstantPool constantPool) { + resolved = true; + } + + /** + * read a constant pool entry from a class stream. + * + * @param cpStream the DataInputStream which contains the constant pool + * entry to be read. + * @exception IOException if there is a problem reading the entry from + * the stream. + */ + public abstract void read(DataInputStream cpStream) throws IOException; + + /** + * Get the Entry's type tag. + * + * @return The Tag value of this entry + */ + public int getTag() { + return tag; + } + + /** + * Get the number of Constant Pool Entry slots within the constant pool + * occupied by this entry. + * + * @return the number of slots used. + */ + public final int getNumEntries() { + return numEntries; + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/DoubleCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/DoubleCPInfo.java new file mode 100644 index 00000000..a21c0d66 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/DoubleCPInfo.java @@ -0,0 +1,58 @@ +/* + * 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.tools.ant.taskdefs.optional.depend.constantpool; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * The constant pool entry subclass used to represent double constant + * values. + * + */ +public class DoubleCPInfo extends ConstantCPInfo { + /** + * Constructor + */ + public DoubleCPInfo() { + super(CONSTANT_DOUBLE, 2); + } + + /** + * read a constant pool entry from a class stream. + * + * @param cpStream the DataInputStream which contains the constant pool + * entry to be read. + * @exception IOException if there is a problem reading the entry from the + * stream. + */ + public void read(DataInputStream cpStream) throws IOException { + setValue(new Double(cpStream.readDouble())); + } + + /** + * Print a readable version of the constant pool entry. + * + * @return the string representation of this constant pool entry. + */ + public String toString() { + return "Double Constant Pool Entry: " + getValue(); + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FieldRefCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FieldRefCPInfo.java new file mode 100644 index 00000000..06c0925d --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FieldRefCPInfo.java @@ -0,0 +1,130 @@ +/* + * 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.tools.ant.taskdefs.optional.depend.constantpool; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * A FieldRef CP Info + * + */ +public class FieldRefCPInfo extends ConstantPoolEntry { + /** Name of the field's class */ + private String fieldClassName; + /** name of the field in that class */ + private String fieldName; + /** The type of the field */ + private String fieldType; + /** Index into the constant pool for the class */ + private int classIndex; + /** Index into the constant pool for the name and type entry */ + private int nameAndTypeIndex; + + /** Constructor. */ + public FieldRefCPInfo() { + super(CONSTANT_FIELDREF, 1); + } + + /** + * read a constant pool entry from a class stream. + * + * @param cpStream the DataInputStream which contains the constant pool + * entry to be read. + * @exception IOException if there is a problem reading the entry from + * the stream. + */ + public void read(DataInputStream cpStream) throws IOException { + classIndex = cpStream.readUnsignedShort(); + nameAndTypeIndex = cpStream.readUnsignedShort(); + } + + /** + * Resolve this constant pool entry with respect to its dependents in + * the constant pool. + * + * @param constantPool the constant pool of which this entry is a member + * and against which this entry is to be resolved. + */ + public void resolve(ConstantPool constantPool) { + ClassCPInfo fieldClass + = (ClassCPInfo) constantPool.getEntry(classIndex); + + fieldClass.resolve(constantPool); + + fieldClassName = fieldClass.getClassName(); + + NameAndTypeCPInfo nt + = (NameAndTypeCPInfo) constantPool.getEntry(nameAndTypeIndex); + + nt.resolve(constantPool); + + fieldName = nt.getName(); + fieldType = nt.getType(); + + super.resolve(constantPool); + } + + /** + * Print a readable version of the constant pool entry. + * + * @return the string representation of this constant pool entry. + */ + public String toString() { + String value; + + if (isResolved()) { + value = "Field : Class = " + fieldClassName + ", name = " + + fieldName + ", type = " + fieldType; + } else { + value = "Field : Class index = " + classIndex + + ", name and type index = " + nameAndTypeIndex; + } + + return value; + } + + /** + * Gets the name of the class defining the field + * + * @return the name of the class defining the field + */ + public String getFieldClassName() { + return fieldClassName; + } + + /** + * Get the name of the field + * + * @return the field's name + */ + public String getFieldName() { + return fieldName; + } + + /** + * Get the type of the field + * + * @return the field's type in string format + */ + public String getFieldType() { + return fieldType; + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FloatCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FloatCPInfo.java new file mode 100644 index 00000000..532b6725 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FloatCPInfo.java @@ -0,0 +1,56 @@ +/* + * 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.tools.ant.taskdefs.optional.depend.constantpool; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * A Float CP Info + * + */ +public class FloatCPInfo extends ConstantCPInfo { + + /** Constructor. */ + public FloatCPInfo() { + super(CONSTANT_FLOAT, 1); + } + + /** + * read a constant pool entry from a class stream. + * + * @param cpStream the DataInputStream which contains the constant pool + * entry to be read. + * @exception IOException if there is a problem reading the entry from + * the stream. + */ + public void read(DataInputStream cpStream) throws IOException { + setValue(new Float(cpStream.readFloat())); + } + + /** + * Print a readable version of the constant pool entry. + * + * @return the string representation of this constant pool entry. + */ + public String toString() { + return "Float Constant Pool Entry: " + getValue(); + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/IntegerCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/IntegerCPInfo.java new file mode 100644 index 00000000..3beaa8cd --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/IntegerCPInfo.java @@ -0,0 +1,56 @@ +/* + * 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.tools.ant.taskdefs.optional.depend.constantpool; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * An Integer CP Info + * + */ +public class IntegerCPInfo extends ConstantCPInfo { + + /** Constructor. */ + public IntegerCPInfo() { + super(CONSTANT_INTEGER, 1); + } + + /** + * read a constant pool entry from a class stream. + * + * @param cpStream the DataInputStream which contains the constant pool + * entry to be read. + * @exception IOException if there is a problem reading the entry from + * the stream. + */ + public void read(DataInputStream cpStream) throws IOException { + setValue(new Integer(cpStream.readInt())); + } + + /** + * Print a readable version of the constant pool entry. + * + * @return the string representation of this constant pool entry. + */ + public String toString() { + return "Integer Constant Pool Entry: " + getValue(); + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/InterfaceMethodRefCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/InterfaceMethodRefCPInfo.java new file mode 100644 index 00000000..fbc23c1c --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/InterfaceMethodRefCPInfo.java @@ -0,0 +1,137 @@ +/* + * 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.tools.ant.taskdefs.optional.depend.constantpool; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * A InterfaceMethodRef CP Info + * + */ +public class InterfaceMethodRefCPInfo extends ConstantPoolEntry { + /** the class name of the class defining the interface method */ + private String interfaceMethodClassName; + /** the name of the interface nmethod */ + private String interfaceMethodName; + /** the method signature of the interface method */ + private String interfaceMethodType; + /** + * the index into the constant pool of the class entry for the interface + * class + */ + private int classIndex; + /** + * the index into the constant pool of the name and type entry + * describing the method + */ + private int nameAndTypeIndex; + + /** Constructor. */ + public InterfaceMethodRefCPInfo() { + super(CONSTANT_INTERFACEMETHODREF, 1); + } + + /** + * read a constant pool entry from a class stream. + * + * @param cpStream the DataInputStream which contains the constant pool + * entry to be read. + * @exception IOException if there is a problem reading the entry from + * the stream. + */ + public void read(DataInputStream cpStream) throws IOException { + classIndex = cpStream.readUnsignedShort(); + nameAndTypeIndex = cpStream.readUnsignedShort(); + } + + /** + * Resolve this constant pool entry with respect to its dependents in + * the constant pool. + * + * @param constantPool the constant pool of which this entry is a member + * and against which this entry is to be resolved. + */ + public void resolve(ConstantPool constantPool) { + ClassCPInfo interfaceMethodClass + = (ClassCPInfo) constantPool.getEntry(classIndex); + + interfaceMethodClass.resolve(constantPool); + + interfaceMethodClassName = interfaceMethodClass.getClassName(); + + NameAndTypeCPInfo nt + = (NameAndTypeCPInfo) constantPool.getEntry(nameAndTypeIndex); + + nt.resolve(constantPool); + + interfaceMethodName = nt.getName(); + interfaceMethodType = nt.getType(); + + super.resolve(constantPool); + } + + /** + * Print a readable version of the constant pool entry. + * + * @return the string representation of this constant pool entry. + */ + public String toString() { + String value; + + if (isResolved()) { + value = "InterfaceMethod : Class = " + interfaceMethodClassName + + ", name = " + interfaceMethodName + ", type = " + + interfaceMethodType; + } else { + value = "InterfaceMethod : Class index = " + classIndex + + ", name and type index = " + nameAndTypeIndex; + } + + return value; + } + + /** + * Gets the name of the class defining the interface method + * + * @return the name of the class defining the interface method + */ + public String getInterfaceMethodClassName() { + return interfaceMethodClassName; + } + + /** + * Get the name of the interface method + * + * @return the name of the interface method + */ + public String getInterfaceMethodName() { + return interfaceMethodName; + } + + /** + * Gets the type of the interface method + * + * @return the interface method's type signature + */ + public String getInterfaceMethodType() { + return interfaceMethodType; + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/InvokeDynamicCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/InvokeDynamicCPInfo.java new file mode 100644 index 00000000..3795db75 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/InvokeDynamicCPInfo.java @@ -0,0 +1,85 @@ +/* + * 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.tools.ant.taskdefs.optional.depend.constantpool; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * An InvokeDynamic CP Info + * + */ +public class InvokeDynamicCPInfo extends ConstantCPInfo { + + /** Index into the bootstrap methods for the class */ + private int bootstrapMethodAttrIndex; + /** the value of the method descriptor pointed to */ + private int nameAndTypeIndex; + /** the name and type CP info pointed to */ + private NameAndTypeCPInfo nameAndTypeCPInfo; + /** */ + /** Constructor. */ + public InvokeDynamicCPInfo() { + super(CONSTANT_INVOKEDYNAMIC, 1); + } + + /** + * read a constant pool entry from a class stream. + * + * @param cpStream the DataInputStream which contains the constant pool + * entry to be read. + * @exception java.io.IOException if there is a problem reading the entry from + * the stream. + */ + public void read(DataInputStream cpStream) throws IOException { + bootstrapMethodAttrIndex = cpStream.readUnsignedShort(); + nameAndTypeIndex = cpStream.readUnsignedShort(); + } + + /** + * Print a readable version of the constant pool entry. + * + * @return the string representation of this constant pool entry. + */ + public String toString() { + String value; + if (isResolved()) { + value = "Name = " + nameAndTypeCPInfo.getName() + ", type = " + nameAndTypeCPInfo.getType(); + } else { + value = "BootstrapMethodAttrIndex inx = " + bootstrapMethodAttrIndex + + "NameAndType index = " + nameAndTypeIndex; + } + + return value; + } + /** + * Resolve this constant pool entry with respect to its dependents in + * the constant pool. + * + * @param constantPool the constant pool of which this entry is a member + * and against which this entry is to be resolved. + */ + public void resolve(ConstantPool constantPool) { + nameAndTypeCPInfo + = (NameAndTypeCPInfo) constantPool.getEntry(nameAndTypeIndex); + nameAndTypeCPInfo.resolve(constantPool); + super.resolve(constantPool); + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/LongCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/LongCPInfo.java new file mode 100644 index 00000000..e854f04f --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/LongCPInfo.java @@ -0,0 +1,56 @@ +/* + * 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.tools.ant.taskdefs.optional.depend.constantpool; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * A Long CP Info + * + */ +public class LongCPInfo extends ConstantCPInfo { + + /** Constructor. */ + public LongCPInfo() { + super(CONSTANT_LONG, 2); + } + + /** + * read a constant pool entry from a class stream. + * + * @param cpStream the DataInputStream which contains the constant pool + * entry to be read. + * @exception IOException if there is a problem reading the entry from + * the stream. + */ + public void read(DataInputStream cpStream) throws IOException { + setValue(new Long(cpStream.readLong())); + } + + /** + * Print a readable version of the constant pool entry. + * + * @return the string representation of this constant pool entry. + */ + public String toString() { + return "Long Constant Pool Entry: " + getValue(); + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodHandleCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodHandleCPInfo.java new file mode 100644 index 00000000..e11e3aab --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodHandleCPInfo.java @@ -0,0 +1,107 @@ +/* + * 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.tools.ant.taskdefs.optional.depend.constantpool; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * A MethodHandle CP Info + * + */ +public class MethodHandleCPInfo extends ConstantPoolEntry { + private ConstantPoolEntry reference; + + /** reference kind **/ + private ReferenceKind referenceKind; + /** Must be a valid index into the constant pool tabel. */ + private int referenceIndex; + /** + * the index into the constant pool which defined the name and type + * signature of the method + */ + private int nameAndTypeIndex; + public enum ReferenceKind { + REF_getField(1), + REF_getStatic(2), + REF_putField(3), + REF_putStatic(4), + REF_invokeVirtual(5), + REF_invokeStatic(6), + REF_invokeSpecial(7), + REF_newInvokeSpecial(8), + REF_invokeInterface(9); + private final int referenceKind; + ReferenceKind(int referenceKind) { + this.referenceKind = referenceKind; + } + + } + /** Constructor. */ + public MethodHandleCPInfo() { + super(CONSTANT_METHODHANDLE, 1); + } + + /** + * read a constant pool entry from a class stream. + * + * @param cpStream the DataInputStream which contains the constant pool + * entry to be read. + * @exception java.io.IOException if there is a problem reading the entry from + * the stream. + */ + public void read(DataInputStream cpStream) throws IOException { + referenceKind = ReferenceKind.values()[cpStream.readUnsignedByte() - 1]; + + referenceIndex = cpStream.readUnsignedShort(); + } + + /** + * Print a readable version of the constant pool entry. + * + * @return the string representation of this constant pool entry. + */ + public String toString() { + String value; + + if (isResolved()) { + value = "MethodHandle : " + reference.toString(); + } else { + value = "MethodHandle : Reference kind = " + referenceKind + + "Reference index = " + referenceIndex; + } + + return value; + } + + /** + * Resolve this constant pool entry with respect to its dependents in + * the constant pool. + * + * @param constantPool the constant pool of which this entry is a member + * and against which this entry is to be resolved. + */ + public void resolve(ConstantPool constantPool) { + reference = constantPool.getEntry(referenceIndex); + reference.resolve(constantPool); + super.resolve(constantPool); + } + + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodRefCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodRefCPInfo.java new file mode 100644 index 00000000..6b335212 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodRefCPInfo.java @@ -0,0 +1,133 @@ +/* + * 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.tools.ant.taskdefs.optional.depend.constantpool; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * A MethodRef CP Info + * + */ +public class MethodRefCPInfo extends ConstantPoolEntry { + /** the name of the class defining this method */ + private String methodClassName; + /** the name of the method */ + private String methodName; + /** the method's type descriptor */ + private String methodType; + /** The index into the constant pool which defines the class of this method. */ + private int classIndex; + /** + * the index into the constant pool which defined the name and type + * signature of the method + */ + private int nameAndTypeIndex; + + /** Constructor. */ + public MethodRefCPInfo() { + super(CONSTANT_METHODREF, 1); + } + + /** + * read a constant pool entry from a class stream. + * + * @param cpStream the DataInputStream which contains the constant pool + * entry to be read. + * @exception IOException if there is a problem reading the entry from + * the stream. + */ + public void read(DataInputStream cpStream) throws IOException { + classIndex = cpStream.readUnsignedShort(); + nameAndTypeIndex = cpStream.readUnsignedShort(); + } + + /** + * Print a readable version of the constant pool entry. + * + * @return the string representation of this constant pool entry. + */ + public String toString() { + String value; + + if (isResolved()) { + value = "Method : Class = " + methodClassName + ", name = " + + methodName + ", type = " + methodType; + } else { + value = "Method : Class index = " + classIndex + + ", name and type index = " + nameAndTypeIndex; + } + + return value; + } + + /** + * Resolve this constant pool entry with respect to its dependents in + * the constant pool. + * + * @param constantPool the constant pool of which this entry is a member + * and against which this entry is to be resolved. + */ + public void resolve(ConstantPool constantPool) { + ClassCPInfo methodClass + = (ClassCPInfo) constantPool.getEntry(classIndex); + + methodClass.resolve(constantPool); + + methodClassName = methodClass.getClassName(); + + NameAndTypeCPInfo nt + = (NameAndTypeCPInfo) constantPool.getEntry(nameAndTypeIndex); + + nt.resolve(constantPool); + + methodName = nt.getName(); + methodType = nt.getType(); + + super.resolve(constantPool); + } + + /** + * Get the name of the class defining the method + * + * @return the name of the class defining this method + */ + public String getMethodClassName() { + return methodClassName; + } + + /** + * Get the name of the method. + * + * @return the name of the method. + */ + public String getMethodName() { + return methodName; + } + + /** + * Get the type signature of the method. + * + * @return the type signature of the method. + */ + public String getMethodType() { + return methodType; + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodTypeCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodTypeCPInfo.java new file mode 100644 index 00000000..d3c35cee --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodTypeCPInfo.java @@ -0,0 +1,82 @@ +/* + * 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.tools.ant.taskdefs.optional.depend.constantpool; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * A Method Type CP Info + * + */ +public class MethodTypeCPInfo extends ConstantCPInfo { + + /** Index into the constant pool for the class */ + private int methodDescriptorIndex; + /** the value of the method descriptor pointed to */ + private String methodDescriptor; + /** Constructor. */ + public MethodTypeCPInfo() { + super(CONSTANT_METHODTYPE, 1); + } + + /** + * read a constant pool entry from a class stream. + * + * @param cpStream the DataInputStream which contains the constant pool + * entry to be read. + * @exception java.io.IOException if there is a problem reading the entry from + * the stream. + */ + @Override + public void read(final DataInputStream cpStream) throws IOException { + methodDescriptorIndex = cpStream.readUnsignedShort(); + } + + /** + * Resolve this constant pool entry with respect to its dependents in + * the constant pool. + * + * @param constantPool the constant pool of which this entry is a member + * and against which this entry is to be resolved. + */ + @Override + public void resolve(final ConstantPool constantPool) { + final Utf8CPInfo methodClass + = (Utf8CPInfo) constantPool.getEntry(methodDescriptorIndex); + methodClass.resolve(constantPool); + methodDescriptor = methodClass.getValue(); + super.resolve(constantPool); + } + /** + * Print a readable version of the constant pool entry. + * + * @return the string representation of this constant pool entry. + */ + @Override + public String toString() { + if (!isResolved()) { + return "MethodDescriptorIndex: " + methodDescriptorIndex; + } else { + return "MethodDescriptor: " + methodDescriptor; + + } + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/NameAndTypeCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/NameAndTypeCPInfo.java new file mode 100644 index 00000000..47f454d2 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/NameAndTypeCPInfo.java @@ -0,0 +1,112 @@ +/* + * 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.tools.ant.taskdefs.optional.depend.constantpool; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * A NameAndType CP Info + * + */ +public class NameAndTypeCPInfo extends ConstantPoolEntry { + + /** Constructor. */ + public NameAndTypeCPInfo() { + super(CONSTANT_NAMEANDTYPE, 1); + } + + /** + * read a constant pool entry from a class stream. + * + * @param cpStream the DataInputStream which contains the constant pool + * entry to be read. + * @exception IOException if there is a problem reading the entry from + * the stream. + */ + public void read(DataInputStream cpStream) throws IOException { + nameIndex = cpStream.readUnsignedShort(); + descriptorIndex = cpStream.readUnsignedShort(); + } + + /** + * Print a readable version of the constant pool entry. + * + * @return the string representation of this constant pool entry. + */ + public String toString() { + String value; + + if (isResolved()) { + value = "Name = " + name + ", type = " + type; + } else { + value = "Name index = " + nameIndex + + ", descriptor index = " + descriptorIndex; + } + + return value; + } + + /** + * Resolve this constant pool entry with respect to its dependents in + * the constant pool. + * + * @param constantPool the constant pool of which this entry is a member + * and against which this entry is to be resolved. + */ + public void resolve(ConstantPool constantPool) { + name = ((Utf8CPInfo) constantPool.getEntry(nameIndex)).getValue(); + type = ((Utf8CPInfo) constantPool.getEntry(descriptorIndex)).getValue(); + + super.resolve(constantPool); + } + + /** + * Get the name component of this entry + * + * @return the name of this name and type entry + */ + public String getName() { + return name; + } + + /** + * Get the type signature of this entry + * + * @return the type signature of this entry + */ + public String getType() { + return type; + } + + /** the name component of this entry */ + private String name; + /** the type component of this entry */ + private String type; + /** + * the index into the constant pool at which the name component's string + * value is stored + */ + private int nameIndex; + /** + * the index into the constant pool where the type descriptor string is + * stored. + */ + private int descriptorIndex; +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/StringCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/StringCPInfo.java new file mode 100644 index 00000000..bc9ee24b --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/StringCPInfo.java @@ -0,0 +1,74 @@ +/* + * 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.tools.ant.taskdefs.optional.depend.constantpool; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * A String Constant Pool Entry. The String info contains an index into the + * constant pool where a UTF8 string is stored. + * + */ +public class StringCPInfo extends ConstantCPInfo { + + /** Constructor. */ + public StringCPInfo() { + super(CONSTANT_STRING, 1); + } + + /** + * read a constant pool entry from a class stream. + * + * @param cpStream the DataInputStream which contains the constant pool + * entry to be read. + * @exception IOException if there is a problem reading the entry from + * the stream. + */ + public void read(DataInputStream cpStream) throws IOException { + index = cpStream.readUnsignedShort(); + + setValue("unresolved"); + } + + /** + * Print a readable version of the constant pool entry. + * + * @return the string representation of this constant pool entry. + */ + public String toString() { + return "String Constant Pool Entry for " + + getValue() + "[" + index + "]"; + } + + /** + * Resolve this constant pool entry with respect to its dependents in + * the constant pool. + * + * @param constantPool the constant pool of which this entry is a member + * and against which this entry is to be resolved. + */ + public void resolve(ConstantPool constantPool) { + setValue(((Utf8CPInfo) constantPool.getEntry(index)).getValue()); + super.resolve(constantPool); + } + + /** the index into the constant pool containing the string's content */ + private int index; +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/Utf8CPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/Utf8CPInfo.java new file mode 100644 index 00000000..5471ccde --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/Utf8CPInfo.java @@ -0,0 +1,67 @@ +/* + * 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.tools.ant.taskdefs.optional.depend.constantpool; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * A UTF8 Constant Pool Entry. + * + */ +public class Utf8CPInfo extends ConstantPoolEntry { + /** The String value of the UTF-8 entry */ + private String value; + + /** Constructor. */ + public Utf8CPInfo() { + super(CONSTANT_UTF8, 1); + } + + /** + * read a constant pool entry from a class stream. + * + * @param cpStream the DataInputStream which contains the constant pool + * entry to be read. + * @exception IOException if there is a problem reading the entry from + * the stream. + */ + public void read(DataInputStream cpStream) throws IOException { + value = cpStream.readUTF(); + } + + /** + * Print a readable version of the constant pool entry. + * + * @return the string representation of this constant pool entry. + */ + public String toString() { + return "UTF8 Value = " + value; + } + + /** + * Get the string value of the UTF-8 entry + * + * @return the UTF-8 value as a Java string + */ + public String getValue() { + return value; + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandDeploymentTool.java new file mode 100644 index 00000000..4eefaebf --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandDeploymentTool.java @@ -0,0 +1,559 @@ +/* + * 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.tools.ant.taskdefs.optional.ejb; + + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Vector; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.ExecTask; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.taskdefs.ExecuteStreamHandler; +import org.apache.tools.ant.taskdefs.Java; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.Path; + + +/** + * BorlandDeploymentTool is dedicated to the Borland Application Server 4.5 and 4.5.1 + * This task generates and compiles the stubs and skeletons for all ejb described into the + * Deployment Descriptor, builds the jar file including the support files and verify + * whether the produced jar is valid or not. + * The supported options are: + * <ul> + * <li>debug (boolean) : turn on the debug mode for generation of + * stubs and skeletons (default:false)</li> + * <li>verify (boolean) : turn on the verification at the end of the jar + * production (default:true) </li> + * <li>verifyargs (String) : add optional argument to verify command + * (see vbj com.inprise.ejb.util.Verify)</li> + * <li>basdtd (String) : location of the BAS DTD </li> + * <li>generateclient (boolean) : turn on the client jar file generation </li> + * <li>version (int) : tell what is the Borland appserver version 4 or 5 </li> + * </ul> + * + *<PRE> + * + * <ejbjar srcdir="${build.classes}" + * basejarname="vsmp" + * descriptordir="${rsc.dir}/hrmanager"> + * <borland destdir="tstlib"> + * <classpath refid="classpath" /> + * </borland> + * <include name="**\ejb-jar.xml"/> + * <support dir="${build.classes}"> + * <include name="demo\smp\*.class"/> + * <include name="demo\helper\*.class"/> + * </support> + * </ejbjar> + *</PRE> + * + */ +public class BorlandDeploymentTool extends GenericDeploymentTool + implements ExecuteStreamHandler { + /** Borland 1.1 ejb id */ + public static final String PUBLICID_BORLAND_EJB + = "-//Inprise Corporation//DTD Enterprise JavaBeans 1.1//EN"; + + protected static final String DEFAULT_BAS45_EJB11_DTD_LOCATION + = "/com/inprise/j2ee/xml/dtds/ejb-jar.dtd"; + + protected static final String DEFAULT_BAS_DTD_LOCATION + = "/com/inprise/j2ee/xml/dtds/ejb-inprise.dtd"; + + protected static final String BAS_DD = "ejb-inprise.xml"; + protected static final String BES_DD = "ejb-borland.xml"; + + + /** Java2iiop executable **/ + protected static final String JAVA2IIOP = "java2iiop"; + + /** Verify class */ + protected static final String VERIFY = "com.inprise.ejb.util.Verify"; + + /** Instance variable that stores the suffix for the borland jarfile. */ + private String jarSuffix = "-ejb.jar"; + + /** Instance variable that stores the location of the borland DTD file. */ + private String borlandDTD; + + /** Instance variable that determines whether the debug mode is on */ + private boolean java2iiopdebug = false; + + /** store additional param for java2iiop command used to build EJB Stubs */ + private String java2iioparams = null; + + /** Instance variable that determines whether the client jar file is generated */ + private boolean generateclient = false; + + /** Borland Enterprise Server = version 5 */ + static final int BES = 5; + /** Borland Application Server or Inprise Application Server = version 4 */ + static final int BAS = 4; + + /** borland appserver version 4 or 5 */ + private int version = BAS; + + + /** + * Instance variable that determines whether it is necessary to verify the + * produced jar + */ + private boolean verify = true; + private String verifyArgs = ""; + + private Hashtable genfiles = new Hashtable(); + + /** + * set the debug mode for java2iiop (default false) + * @param debug the setting to use. + **/ + public void setDebug(boolean debug) { + this.java2iiopdebug = debug; + } + + /** + * set the verify mode for the produced jar (default true) + * @param verify the setting to use. + **/ + public void setVerify(boolean verify) { + this.verify = verify; + } + + + /** + * Setter used to store the suffix for the generated borland jar file. + * @param inString the string to use as the suffix. + */ + public void setSuffix(String inString) { + this.jarSuffix = inString; + } + + + /** + * sets some additional args to send to verify command + * @param args additional command line parameters + */ + public void setVerifyArgs(String args) { + this.verifyArgs = args; + } + + /** + * Setter used to store the location of the borland DTD. This can be a file on the system + * or a resource on the classpath. + * @param inString the string to use as the DTD location. + */ + public void setBASdtd(String inString) { + this.borlandDTD = inString; + } + + + /** + * setter used to store whether the task will include the generate client task. + * (see : BorlandGenerateClient task) + * @param b if true generate the client task. + */ + public void setGenerateclient(boolean b) { + this.generateclient = b; + } + + /** + * setter used to store the borland appserver version [4 or 5] + * @param version app server version 4 or 5 + */ + public void setVersion(int version) { + this.version = version; + } + + /** + * If filled, the params are added to the java2iiop command. + * (ex: -no_warn_missing_define) + * @param params additional params for java2iiop + */ + public void setJava2iiopParams(String params) { + this.java2iioparams = params; + } + + + /** + * Get the borland descriptor handler. + * @param srcDir the source directory. + * @return the descriptor. + */ + protected DescriptorHandler getBorlandDescriptorHandler(final File srcDir) { + DescriptorHandler handler = + new DescriptorHandler(getTask(), srcDir) { + protected void processElement() { + if (currentElement.equals("type-storage")) { + // Get the filename of vendor specific descriptor + String fileNameWithMETA = currentText; + //trim the META_INF\ off of the file name + String fileName + = fileNameWithMETA.substring(META_DIR.length(), + fileNameWithMETA.length()); + File descriptorFile = new File(srcDir, fileName); + + ejbFiles.put(fileNameWithMETA, descriptorFile); + } + } + }; + handler.registerDTD(PUBLICID_BORLAND_EJB, + borlandDTD == null ? DEFAULT_BAS_DTD_LOCATION : borlandDTD); + + for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) { + EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next(); + handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation()); + } + return handler; + } + + /** + * Add any vendor specific files which should be included in the + * EJB Jar. + * @param ejbFiles the map to add the files to. + * @param ddPrefix the prefix to use. + */ + protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) { + + //choose the right vendor DD + if (!(version == BES || version == BAS)) { + throw new BuildException("version " + version + " is not supported"); + } + + String dd = (version == BES ? BES_DD : BAS_DD); + + log("vendor file : " + ddPrefix + dd, Project.MSG_DEBUG); + + File borlandDD = new File(getConfig().descriptorDir, ddPrefix + dd); + if (borlandDD.exists()) { + log("Borland specific file found " + borlandDD, Project.MSG_VERBOSE); + ejbFiles.put(META_DIR + dd , borlandDD); + } else { + log("Unable to locate borland deployment descriptor. " + + "It was expected to be in " + + borlandDD.getPath(), Project.MSG_WARN); + return; + } + } + + /** + * Get the vendor specific name of the Jar that will be output. The modification date + * of this jar will be checked against the dependent bean classes. + */ + File getVendorOutputJarFile(String baseName) { + return new File(getDestDir(), baseName + jarSuffix); + } + + /** + * Verify the produced jar file by invoking the Borland verify tool + * @param sourceJar java.io.File representing the produced jar file + */ + private void verifyBorlandJar(File sourceJar) { + if (version == BAS) { + verifyBorlandJarV4(sourceJar); + return; + } + if (version == BES) { + verifyBorlandJarV5(sourceJar); + return; + } + log("verify jar skipped because the version is invalid [" + + version + "]", Project.MSG_WARN); + } + + /** + * Verify the produced jar file by invoking the Borland iastool tool + * @param sourceJar java.io.File representing the produced jar file + */ + private void verifyBorlandJarV5(File sourceJar) { + log("verify BES " + sourceJar, Project.MSG_INFO); + try { + ExecTask execTask = null; + execTask = new ExecTask(getTask()); + execTask.setDir(new File(".")); + execTask.setExecutable("iastool"); + //classpath + if (getCombinedClasspath() != null) { + execTask.createArg().setValue("-VBJclasspath"); + execTask.createArg().setValue(getCombinedClasspath().toString()); + } + + if (java2iiopdebug) { + execTask.createArg().setValue("-debug"); + } + execTask.createArg().setValue("-verify"); + execTask.createArg().setValue("-src"); + // ejb jar file to verify + execTask.createArg().setValue(sourceJar.getPath()); + log("Calling iastool", Project.MSG_VERBOSE); + execTask.execute(); + } catch (Exception e) { + // Have to catch this because of the semantics of calling main() + String msg = "Exception while calling generateclient Details: " + + e.toString(); + throw new BuildException(msg, e); + } + } + + /** + * Verify the produced jar file by invoking the Borland verify tool + * @param sourceJar java.io.File representing the produced jar file + */ + private void verifyBorlandJarV4(File sourceJar) { + org.apache.tools.ant.taskdefs.Java javaTask = null; + log("verify BAS " + sourceJar, Project.MSG_INFO); + try { + String args = verifyArgs; + args += " " + sourceJar.getPath(); + + javaTask = new Java(getTask()); + javaTask.setTaskName("verify"); + javaTask.setClassname(VERIFY); + Commandline.Argument arguments = javaTask.createArg(); + arguments.setLine(args); + Path classpath = getCombinedClasspath(); + if (classpath != null) { + javaTask.setClasspath(classpath); + javaTask.setFork(true); + } + + log("Calling " + VERIFY + " for " + sourceJar.toString(), + Project.MSG_VERBOSE); + javaTask.execute(); + } catch (Exception e) { + //TO DO : delete the file if it is not a valid file. + String msg = "Exception while calling " + VERIFY + " Details: " + + e.toString(); + throw new BuildException(msg, e); + } + } + + + /** + * Generate the client jar corresponding to the jar file passed as parameter + * the method uses the BorlandGenerateClient task. + * @param sourceJar java.io.File representing the produced jar file + */ + private void generateClient(File sourceJar) { + getTask().getProject().addTaskDefinition("internal_bas_generateclient", + org.apache.tools.ant.taskdefs.optional.ejb.BorlandGenerateClient.class); + + org.apache.tools.ant.taskdefs.optional.ejb.BorlandGenerateClient gentask = null; + log("generate client for " + sourceJar, Project.MSG_INFO); + try { + Project project = getTask().getProject(); + gentask + = (BorlandGenerateClient) project.createTask("internal_bas_generateclient"); + gentask.setEjbjar(sourceJar); + gentask.setDebug(java2iiopdebug); + Path classpath = getCombinedClasspath(); + if (classpath != null) { + gentask.setClasspath(classpath); + } + gentask.setVersion(version); + gentask.setTaskName("generate client"); + gentask.execute(); + } catch (Exception e) { + //TO DO : delete the file if it is not a valid file. + String msg = "Exception while calling " + VERIFY + " Details: " + + e.toString(); + throw new BuildException(msg, e); + } + } + + /** + * Generate stubs & skeleton for each home found into the DD + * Add all the generate class file into the ejb files + * @param ithomes : iterator on home class + */ + private void buildBorlandStubs(Iterator ithomes) { + Execute execTask = null; + + execTask = new Execute(this); + Project project = getTask().getProject(); + execTask.setAntRun(project); + execTask.setWorkingDirectory(project.getBaseDir()); + + Commandline commandline = new Commandline(); + commandline.setExecutable(JAVA2IIOP); + //debug ? + if (java2iiopdebug) { + commandline.createArgument().setValue("-VBJdebug"); + } + //set the classpath + commandline.createArgument().setValue("-VBJclasspath"); + commandline.createArgument().setPath(getCombinedClasspath()); + //list file + commandline.createArgument().setValue("-list_files"); + //no TIE classes + commandline.createArgument().setValue("-no_tie"); + + if (java2iioparams != null) { + log("additional " + java2iioparams + " to java2iiop ", 0); + commandline.createArgument().setLine(java2iioparams); + } + + + //root dir + commandline.createArgument().setValue("-root_dir"); + commandline.createArgument().setValue(getConfig().srcDir.getAbsolutePath()); + //compiling order + commandline.createArgument().setValue("-compile"); + //add the home class + while (ithomes.hasNext()) { + commandline.createArgument().setValue(ithomes.next().toString()); + } + + try { + log("Calling java2iiop", Project.MSG_VERBOSE); + log(commandline.describeCommand(), Project.MSG_DEBUG); + execTask.setCommandline(commandline.getCommandline()); + int result = execTask.execute(); + if (Execute.isFailure(result)) { + String msg = "Failed executing java2iiop (ret code is " + + result + ")"; + throw new BuildException(msg, getTask().getLocation()); + } + } catch (java.io.IOException e) { + log("java2iiop exception :" + e.getMessage(), Project.MSG_ERR); + throw new BuildException(e, getTask().getLocation()); + } + } + + /** + * Method used to encapsulate the writing of the JAR file. Iterates over the + * filenames/java.io.Files in the Hashtable stored on the instance variable + * ejbFiles. + * @param baseName the base name. + * @param jarFile the jar file to write to. + * @param files the files to write to the jar. + * @param publicId the id to use. + * @throws BuildException if there is an error. + */ + protected void writeJar(String baseName, File jarFile, Hashtable files, String publicId) + throws BuildException { + //build the home classes list. + Vector homes = new Vector(); + Iterator it = files.keySet().iterator(); + while (it.hasNext()) { + String clazz = (String) it.next(); + if (clazz.endsWith("Home.class")) { + //remove .class extension + String home = toClass(clazz); + homes.add(home); + log(" Home " + home, Project.MSG_VERBOSE); + } + } + + buildBorlandStubs(homes.iterator()); + + //add the gen files to the collection + files.putAll(genfiles); + + super.writeJar(baseName, jarFile, files, publicId); + + if (verify) { + verifyBorlandJar(jarFile); + } + + if (generateclient) { + generateClient(jarFile); + } + genfiles.clear(); + } + + /** + * convert a class file name : A/B/C/toto.class + * into a class name: A.B.C.toto + */ + private String toClass(String filename) { + //remove the .class + String classname = filename.substring(0, filename.lastIndexOf(".class")); + classname = classname.replace('\\', '.'); + return classname; + } + + /** + * convert a file name : A/B/C/toto.java + * into a class name: A/B/C/toto.class + */ + private String toClassFile(String filename) { + //remove the .class + String classfile = filename.substring(0, filename.lastIndexOf(".java")); + classfile = classfile + ".class"; + return classfile; + } + + // implementation of org.apache.tools.ant.taskdefs.ExecuteStreamHandler interface + + /** {@inheritDoc}. */ + public void start() throws IOException { } + /** {@inheritDoc}. */ + public void stop() { } + /** {@inheritDoc}. */ + public void setProcessInputStream(OutputStream param1) throws IOException { } + + /** + * Set the output stream of the process. + * @param is the input stream. + * @throws IOException if there is an error. + */ + public void setProcessOutputStream(InputStream is) throws IOException { + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + String javafile; + while ((javafile = reader.readLine()) != null) { + if (javafile.endsWith(".java")) { + String classfile = toClassFile(javafile); + String key = classfile.substring( + getConfig().srcDir.getAbsolutePath().length() + 1); + genfiles.put(key, new File(classfile)); + } + } + reader.close(); + } catch (Exception e) { + String msg = "Exception while parsing java2iiop output. Details: " + e.toString(); + throw new BuildException(msg, e); + } + } + + /** + * Set the error stream of the process. + * @param is the input stream. + * @throws IOException if there is an error. + */ + public void setProcessErrorStream(InputStream is) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + String s = reader.readLine(); + if (s != null) { + log("[java2iiop] " + s, Project.MSG_ERR); + } + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandGenerateClient.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandGenerateClient.java new file mode 100644 index 00000000..dd46269d --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandGenerateClient.java @@ -0,0 +1,313 @@ +/* + * 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.tools.ant.taskdefs.optional.ejb; + +import java.io.File; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.ExecTask; +import org.apache.tools.ant.taskdefs.Java; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.Reference; + +/** + * Generates a Borland Application Server 4.5 client JAR using as + * input the EJB JAR file. + * + * Two mode are available: java mode (default) and fork mode. With the fork mode, + * it is impossible to add classpath to the command line. + * + * @ant.task name="blgenclient" category="ejb" + */ +public class BorlandGenerateClient extends Task { + static final String JAVA_MODE = "java"; + static final String FORK_MODE = "fork"; + + // CheckStyle:VisibilityModifier OFF - bc + /** debug the generateclient task */ + boolean debug = false; + + /** hold the ejbjar file name */ + File ejbjarfile = null; + + /** hold the client jar file name */ + File clientjarfile = null; + + /** hold the classpath */ + Path classpath; + + /** hold the mode (java|fork) */ + String mode = FORK_MODE; + + /** hold the version */ + int version = BorlandDeploymentTool.BAS; + // CheckStyle:VisibilityModifier ON + + /** + * Set the version attribute. + * @param version the value to use. + */ + public void setVersion(int version) { + this.version = version; + } + + /** + * Command launching mode: java or fork. + * @param s the mode to use. + */ + public void setMode(String s) { + mode = s; + } + + /** + * If true, turn on the debug mode for each of the Borland tools launched. + * @param debug a <code>boolean</code> value. + */ + public void setDebug(boolean debug) { + this.debug = debug; + } + + /** + * EJB JAR file. + * @param ejbfile the file to use. + */ + public void setEjbjar(File ejbfile) { + ejbjarfile = ejbfile; + } + + /** + * Client JAR file name. + * @param clientjar the file to use. + */ + public void setClientjar(File clientjar) { + clientjarfile = clientjar; + } + + /** + * Path to use for classpath. + * @param classpath the path to use. + */ + public void setClasspath(Path classpath) { + if (this.classpath == null) { + this.classpath = classpath; + } else { + this.classpath.append(classpath); + } + } + + /** + * Adds path to the classpath. + * @return a path to be configured as a nested element. + */ + public Path createClasspath() { + if (this.classpath == null) { + this.classpath = new Path(getProject()); + } + return this.classpath.createPath(); + } + + /** + * Reference to existing path, to use as a classpath. + * @param r the reference to use. + */ + public void setClasspathRef(Reference r) { + createClasspath().setRefid(r); + } + + + /** + * Do the work. + * + * The work is actually done by creating a separate JVM to run a java task. + * + * @exception BuildException if something goes wrong with the build + */ + public void execute() throws BuildException { + if (ejbjarfile == null || ejbjarfile.isDirectory()) { + throw new BuildException("invalid ejb jar file."); + } + + if (clientjarfile == null || clientjarfile.isDirectory()) { + log("invalid or missing client jar file.", Project.MSG_VERBOSE); + String ejbjarname = ejbjarfile.getAbsolutePath(); + //clientname = ejbjarfile+client.jar + String clientname = ejbjarname.substring(0, ejbjarname.lastIndexOf(".")); + clientname = clientname + "client.jar"; + clientjarfile = new File(clientname); + } + + if (mode == null) { + log("mode is null default mode is java"); + setMode(JAVA_MODE); + } + + if (!(version == BorlandDeploymentTool.BES + || version == BorlandDeploymentTool.BAS)) { + throw new BuildException("version " + version + + " is not supported"); + } + + log("client jar file is " + clientjarfile); + + if (mode.equalsIgnoreCase(FORK_MODE)) { + executeFork(); + } else { + executeJava(); + } // end of else + } + + /** + * launch the generate client using java api. + * @throws BuildException if there is an error. + */ + protected void executeJava() throws BuildException { + try { + if (version == BorlandDeploymentTool.BES) { + throw new BuildException("java mode is supported only for " + + "previous version <=" + BorlandDeploymentTool.BAS); + } + + log("mode : java"); + + Java execTask = null; + execTask = new Java(this); + + execTask.setDir(new File(".")); + execTask.setClassname("com.inprise.server.commandline.EJBUtilities"); + //classpath + //add at the end of the classpath + //the system classpath in order to find the tools.jar file + execTask.setClasspath(classpath.concatSystemClasspath()); + + execTask.setFork(true); + execTask.createArg().setValue("generateclient"); + if (debug) { + execTask.createArg().setValue("-trace"); + } + + execTask.createArg().setValue("-short"); + execTask.createArg().setValue("-jarfile"); + // ejb jar file + execTask.createArg().setValue(ejbjarfile.getAbsolutePath()); + //client jar file + execTask.createArg().setValue("-single"); + execTask.createArg().setValue("-clientjarfile"); + execTask.createArg().setValue(clientjarfile.getAbsolutePath()); + + log("Calling EJBUtilities", Project.MSG_VERBOSE); + execTask.execute(); + + } catch (Exception e) { + // Have to catch this because of the semantics of calling main() + String msg = "Exception while calling generateclient Details: " + e.toString(); + throw new BuildException(msg, e); + } + } + + /** + * launch the generate client using system api. + * @throws BuildException if there is an error. + */ + protected void executeFork() throws BuildException { + if (version == BorlandDeploymentTool.BAS) { + executeForkV4(); + } + if (version == BorlandDeploymentTool.BES) { + executeForkV5(); + } + } + + /** + * launch the generate client using system api. + * @throws BuildException if there is an error. + */ + protected void executeForkV4() throws BuildException { + try { + + log("mode : fork " + BorlandDeploymentTool.BAS, Project.MSG_DEBUG); + + ExecTask execTask = new ExecTask(this); + + execTask.setDir(new File(".")); + execTask.setExecutable("iastool"); + execTask.createArg().setValue("generateclient"); + if (debug) { + execTask.createArg().setValue("-trace"); + } + + execTask.createArg().setValue("-short"); + execTask.createArg().setValue("-jarfile"); + // ejb jar file + execTask.createArg().setValue(ejbjarfile.getAbsolutePath()); + //client jar file + execTask.createArg().setValue("-single"); + execTask.createArg().setValue("-clientjarfile"); + execTask.createArg().setValue(clientjarfile.getAbsolutePath()); + + log("Calling iastool", Project.MSG_VERBOSE); + execTask.execute(); + } catch (Exception e) { + // Have to catch this because of the semantics of calling main() + String msg = "Exception while calling generateclient Details: " + + e.toString(); + throw new BuildException(msg, e); + } + + } + + /** + * launch the generate client using system api. + * @throws BuildException if there is an error. + */ + protected void executeForkV5() throws BuildException { + try { + log("mode : fork " + BorlandDeploymentTool.BES, Project.MSG_DEBUG); + ExecTask execTask = new ExecTask(this); + + execTask.setDir(new File(".")); + + execTask.setExecutable("iastool"); + if (debug) { + execTask.createArg().setValue("-debug"); + } + execTask.createArg().setValue("-genclient"); + execTask.createArg().setValue("-jars"); + // ejb jar file + execTask.createArg().setValue(ejbjarfile.getAbsolutePath()); + //client jar file + execTask.createArg().setValue("-target"); + execTask.createArg().setValue(clientjarfile.getAbsolutePath()); + //classpath + execTask.createArg().setValue("-cp"); + execTask.createArg().setValue(classpath.toString()); + log("Calling iastool", Project.MSG_VERBOSE); + execTask.execute(); + } catch (Exception e) { + // Have to catch this because of the semantics of calling main() + String msg = "Exception while calling generateclient Details: " + + e.toString(); + throw new BuildException(msg, e); + } + + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/DescriptorHandler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/DescriptorHandler.java new file mode 100644 index 00000000..158cba93 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/DescriptorHandler.java @@ -0,0 +1,390 @@ +/* + * 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.tools.ant.taskdefs.optional.ejb; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Hashtable; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.xml.sax.AttributeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * Inner class used by EjbJar to facilitate the parsing of deployment + * descriptors and the capture of appropriate information. Extends + * HandlerBase so it only implements the methods needed. During parsing + * creates a hashtable consisting of entries mapping the name it should be + * inserted into an EJB jar as to a File representing the file on disk. This + * list can then be accessed through the getFiles() method. + */ +public class DescriptorHandler extends org.xml.sax.HandlerBase { + private static final int DEFAULT_HASH_TABLE_SIZE = 10; + private static final int STATE_LOOKING_EJBJAR = 1; + private static final int STATE_IN_EJBJAR = 2; + private static final int STATE_IN_BEANS = 3; + private static final int STATE_IN_SESSION = 4; + private static final int STATE_IN_ENTITY = 5; + private static final int STATE_IN_MESSAGE = 6; + + private Task owningTask; + + private String publicId = null; + + /** + * Bunch of constants used for storing entries in a hashtable, and for + * constructing the filenames of various parts of the ejb jar. + */ + private static final String EJB_REF = "ejb-ref"; + private static final String EJB_LOCAL_REF = "ejb-local-ref"; + private static final String HOME_INTERFACE = "home"; + private static final String REMOTE_INTERFACE = "remote"; + private static final String LOCAL_HOME_INTERFACE = "local-home"; + private static final String LOCAL_INTERFACE = "local"; + private static final String BEAN_CLASS = "ejb-class"; + private static final String PK_CLASS = "prim-key-class"; + private static final String EJB_NAME = "ejb-name"; + private static final String EJB_JAR = "ejb-jar"; + private static final String ENTERPRISE_BEANS = "enterprise-beans"; + private static final String ENTITY_BEAN = "entity"; + private static final String SESSION_BEAN = "session"; + private static final String MESSAGE_BEAN = "message-driven"; + + /** + * The state of the parsing + */ + private int parseState = STATE_LOOKING_EJBJAR; + + // CheckStyle:VisibilityModifier OFF - bc + /** + * Instance variable used to store the name of the current element being + * processed by the SAX parser. Accessed by the SAX parser call-back methods + * startElement() and endElement(). + */ + protected String currentElement = null; + + /** + * The text of the current element + */ + protected String currentText = null; + + /** + * Instance variable that stores the names of the files as they will be + * put into the jar file, mapped to File objects Accessed by the SAX + * parser call-back method characters(). + */ + protected Hashtable ejbFiles = null; + + /** + * Instance variable that stores the value found in the <ejb-name> element + */ + protected String ejbName = null; + + private Hashtable fileDTDs = new Hashtable(); + + private Hashtable resourceDTDs = new Hashtable(); + + private boolean inEJBRef = false; + + private Hashtable urlDTDs = new Hashtable(); + // CheckStyle:VisibilityModifier OFF - bc + + /** + * The directory containing the bean classes and interfaces. This is + * used for performing dependency file lookups. + */ + private File srcDir; + + /** + * Constructor for DescriptorHandler. + * @param task the task that owns this descriptor + * @param srcDir the source directory + */ + public DescriptorHandler(Task task, File srcDir) { + this.owningTask = task; + this.srcDir = srcDir; + } + + /** + * Register a dtd with a location. + * The location is one of a filename, a resource name in the classpath, or + * a URL. + * @param publicId the public identity of the dtd + * @param location the location of the dtd + */ + public void registerDTD(String publicId, String location) { + if (location == null) { + return; + } + + File fileDTD = new File(location); + if (!fileDTD.exists()) { + // resolve relative to project basedir + fileDTD = owningTask.getProject().resolveFile(location); + } + + if (fileDTD.exists()) { + if (publicId != null) { + fileDTDs.put(publicId, fileDTD); + owningTask.log("Mapped publicId " + publicId + " to file " + + fileDTD, Project.MSG_VERBOSE); + } + return; + } + + if (getClass().getResource(location) != null) { + if (publicId != null) { + resourceDTDs.put(publicId, location); + owningTask.log("Mapped publicId " + publicId + " to resource " + + location, Project.MSG_VERBOSE); + } + } + + try { + if (publicId != null) { + URL urldtd = new URL(location); + urlDTDs.put(publicId, urldtd); + } + } catch (java.net.MalformedURLException e) { + //ignored + } + + } + + /** + * Resolve the entity. + * @see org.xml.sax.EntityResolver#resolveEntity(String, String). + * @param publicId The public identifier, or <code>null</code> + * if none is available. + * @param systemId The system identifier provided in the XML + * document. Will not be <code>null</code>. + * @return an inputsource for this identifier + * @throws SAXException if there is a problem. + */ + public InputSource resolveEntity(String publicId, String systemId) + throws SAXException { + this.publicId = publicId; + + File dtdFile = (File) fileDTDs.get(publicId); + if (dtdFile != null) { + try { + owningTask.log("Resolved " + publicId + " to local file " + + dtdFile, Project.MSG_VERBOSE); + return new InputSource(new FileInputStream(dtdFile)); + } catch (FileNotFoundException ex) { + // ignore + } + } + + String dtdResourceName = (String) resourceDTDs.get(publicId); + if (dtdResourceName != null) { + InputStream is = this.getClass().getResourceAsStream(dtdResourceName); + if (is != null) { + owningTask.log("Resolved " + publicId + " to local resource " + + dtdResourceName, Project.MSG_VERBOSE); + return new InputSource(is); + } + } + + URL dtdUrl = (URL) urlDTDs.get(publicId); + if (dtdUrl != null) { + try { + InputStream is = dtdUrl.openStream(); + owningTask.log("Resolved " + publicId + " to url " + + dtdUrl, Project.MSG_VERBOSE); + return new InputSource(is); + } catch (IOException ioe) { + //ignore + } + } + + owningTask.log("Could not resolve ( publicId: " + publicId + + ", systemId: " + systemId + ") to a local entity", Project.MSG_INFO); + + return null; + } + + /** + * Getter method that returns the set of files to include in the EJB jar. + * @return the map of files + */ + public Hashtable getFiles() { + return (ejbFiles == null) ? new Hashtable() : ejbFiles; + } + + /** + * Get the publicId of the DTD + * @return the public id + */ + public String getPublicId() { + return publicId; + } + + /** + * Getter method that returns the value of the <ejb-name> element. + * @return the ejb name + */ + public String getEjbName() { + return ejbName; + } + + /** + * SAX parser call-back method that is used to initialize the values of some + * instance variables to ensure safe operation. + * @throws SAXException on error + */ + public void startDocument() throws SAXException { + this.ejbFiles = new Hashtable(DEFAULT_HASH_TABLE_SIZE, 1); + this.currentElement = null; + inEJBRef = false; + } + + + /** + * SAX parser call-back method that is invoked when a new element is entered + * into. Used to store the context (attribute name) in the currentAttribute + * instance variable. + * @param name The name of the element being entered. + * @param attrs Attributes associated to the element. + * @throws SAXException on error + */ + public void startElement(String name, AttributeList attrs) + throws SAXException { + this.currentElement = name; + currentText = ""; + if (name.equals(EJB_REF) || name.equals(EJB_LOCAL_REF)) { + inEJBRef = true; + } else if (parseState == STATE_LOOKING_EJBJAR && name.equals(EJB_JAR)) { + parseState = STATE_IN_EJBJAR; + } else if (parseState == STATE_IN_EJBJAR && name.equals(ENTERPRISE_BEANS)) { + parseState = STATE_IN_BEANS; + } else if (parseState == STATE_IN_BEANS && name.equals(SESSION_BEAN)) { + parseState = STATE_IN_SESSION; + } else if (parseState == STATE_IN_BEANS && name.equals(ENTITY_BEAN)) { + parseState = STATE_IN_ENTITY; + } else if (parseState == STATE_IN_BEANS && name.equals(MESSAGE_BEAN)) { + parseState = STATE_IN_MESSAGE; + } + } + + + /** + * SAX parser call-back method that is invoked when an element is exited. + * Used to blank out (set to the empty string, not nullify) the name of + * the currentAttribute. A better method would be to use a stack as an + * instance variable, however since we are only interested in leaf-node + * data this is a simpler and workable solution. + * @param name The name of the attribute being exited. Ignored + * in this implementation. + * @throws SAXException on error + */ + public void endElement(String name) throws SAXException { + processElement(); + currentText = ""; + this.currentElement = ""; + if (name.equals(EJB_REF) || name.equals(EJB_LOCAL_REF)) { + inEJBRef = false; + } else if (parseState == STATE_IN_ENTITY && name.equals(ENTITY_BEAN)) { + parseState = STATE_IN_BEANS; + } else if (parseState == STATE_IN_SESSION && name.equals(SESSION_BEAN)) { + parseState = STATE_IN_BEANS; + } else if (parseState == STATE_IN_MESSAGE && name.equals(MESSAGE_BEAN)) { + parseState = STATE_IN_BEANS; + } else if (parseState == STATE_IN_BEANS && name.equals(ENTERPRISE_BEANS)) { + parseState = STATE_IN_EJBJAR; + } else if (parseState == STATE_IN_EJBJAR && name.equals(EJB_JAR)) { + parseState = STATE_LOOKING_EJBJAR; + } + } + + /** + * SAX parser call-back method invoked whenever characters are located within + * an element. currentAttribute (modified by startElement and endElement) + * tells us whether we are in an interesting element (one of the up to four + * classes of an EJB). If so then converts the classname from the format + * org.apache.tools.ant.Parser to the convention for storing such a class, + * org/apache/tools/ant/Parser.class. This is then resolved into a file + * object under the srcdir which is stored in a Hashtable. + * @param ch A character array containing all the characters in + * the element, and maybe others that should be ignored. + * @param start An integer marking the position in the char + * array to start reading from. + * @param length An integer representing an offset into the + * char array where the current data terminates. + * @throws SAXException on error + */ + public void characters(char[] ch, int start, int length) + throws SAXException { + + currentText += new String(ch, start, length); + } + + + /** + * Called when an endelement is seen. + * This may be overridden in derived classes. + * This updates the ejbfiles if the element is an interface or a bean class. + * This updates the ejbname if the element is an ejb name. + */ + protected void processElement() { + if (inEJBRef + || (parseState != STATE_IN_ENTITY + && parseState != STATE_IN_SESSION + && parseState != STATE_IN_MESSAGE)) { + return; + } + + if (currentElement.equals(HOME_INTERFACE) + || currentElement.equals(REMOTE_INTERFACE) + || currentElement.equals(LOCAL_INTERFACE) + || currentElement.equals(LOCAL_HOME_INTERFACE) + || currentElement.equals(BEAN_CLASS) + || currentElement.equals(PK_CLASS)) { + + // Get the filename into a String object + File classFile = null; + String className = currentText.trim(); + + // If it's a primitive wrapper then we shouldn't try and put + // it into the jar, so ignore it. + if (!className.startsWith("java.") + && !className.startsWith("javax.")) { + // Translate periods into path separators, add .class to the + // name, create the File object and add it to the Hashtable. + className = className.replace('.', File.separatorChar); + className += ".class"; + classFile = new File(srcDir, className); + ejbFiles.put(className, classFile); + } + } + + // Get the value of the <ejb-name> tag. Only the first occurrence. + if (currentElement.equals(EJB_NAME)) { + if (ejbName == null) { + ejbName = currentText.trim(); + } + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EJBDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EJBDeploymentTool.java new file mode 100644 index 00000000..6ed8e34a --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EJBDeploymentTool.java @@ -0,0 +1,61 @@ +/* + * 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.tools.ant.taskdefs.optional.ejb; + + + +import javax.xml.parsers.SAXParser; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; + + +/** + * The interface to implement for deployment tools. + */ +public interface EJBDeploymentTool { + /** + * Process a deployment descriptor, generating the necessary vendor specific + * deployment files. + * + * @param descriptorFilename the name of the deployment descriptor + * @param saxParser a SAX parser which can be used to parse the deployment descriptor. + * @throws BuildException if there is an error. + */ + void processDescriptor(String descriptorFilename, SAXParser saxParser) + throws BuildException; + + /** + * Called to validate that the tool parameters have been configured. + * @throws BuildException if there is an error. + */ + void validateConfigured() throws BuildException; + + /** + * Set the task which owns this tool + * @param task the task. + */ + void setTask(Task task); + + /** + * Configure this tool for use in the ejbjar task. + * @param config contains configuration state. + */ + void configure(EjbJar.Config config); +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java new file mode 100644 index 00000000..e9b7ed42 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java @@ -0,0 +1,630 @@ +/* + * 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.tools.ant.taskdefs.optional.ejb; + +// Standard java imports +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.MatchingTask; +import org.apache.tools.ant.types.EnumeratedAttribute; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Path; +import org.xml.sax.SAXException; + +/** + * Provides automated EJB JAR file creation. + * <p> + * Extends the + * MatchingTask class provided in the default ant distribution to provide a + * directory scanning EJB jarfile generator. + * </p> + * + * <p> + * The task works by taking the deployment descriptors one at a time and + * parsing them to locate the names of the classes which should be placed in + * the jar. The classnames are translated to java.io.Files by replacing + * periods with File.separatorChar and resolving the generated filename as a + * relative path under the srcDir attribute. All necessary files are then + * assembled into a jarfile. One jarfile is constructed for each deployment + * descriptor found. + * </p> + * + * */ +public class EjbJar extends MatchingTask { + + /** + * Inner class used to record information about the location of a local DTD + */ + public static class DTDLocation + extends org.apache.tools.ant.types.DTDLocation { + } + + /** + * A class which contains the configuration state of the ejbjar task. + * This state is passed to the deployment tools for configuration + */ + static class Config { + // CheckStyle:VisibilityModifier OFF - bc + /** + * Stores a handle to the directory under which to search for class + * files + */ + public File srcDir; + + /** + * Stores a handle to the directory under which to search for + * deployment descriptors + */ + public File descriptorDir; + + /** Instance variable that marks the end of the 'basename' */ + public String baseNameTerminator = "-"; + + /** Stores a handle to the destination EJB Jar file */ + public String baseJarName; + + /** + * Instance variable that determines whether to use a package structure + * of a flat directory as the destination for the jar files. + */ + public boolean flatDestDir = false; + + /** + * The classpath to use when loading classes + */ + public Path classpath; + + /** + * A Fileset of support classes + */ + public List supportFileSets = new ArrayList(); + + /** + * The list of configured DTD locations + */ + public ArrayList dtdLocations = new ArrayList(); + + /** + * The naming scheme used to determine the generated jar name + * from the descriptor information + */ + public NamingScheme namingScheme; + + /** + * The Manifest file + */ + public File manifest; + + /** + * The dependency analyzer to use to add additional classes to the jar + */ + public String analyzer; + // CheckStyle:VisibilityModifier ON + } + + /** + * An EnumeratedAttribute class for handling different EJB jar naming + * schemes + */ + public static class NamingScheme extends EnumeratedAttribute { + /** + * Naming scheme where generated jar is determined from the ejb-name in + * the deployment descriptor + */ + public static final String EJB_NAME = "ejb-name"; + + /** + * Naming scheme where the generated jar name is based on the + * name of the directory containing the deployment descriptor + */ + public static final String DIRECTORY = "directory"; + + /** + * Naming scheme where the generated jar name is based on the name of + * the deployment descriptor file + */ + public static final String DESCRIPTOR = "descriptor"; + + /** + * Naming scheme where the generated jar is named by the basejarname + * attribute + */ + public static final String BASEJARNAME = "basejarname"; + + /** + * Gets the values of the NamingScheme + * + * @return an array of the values of this attribute class. + */ + public String[] getValues() { + return new String[] {EJB_NAME, DIRECTORY, DESCRIPTOR, BASEJARNAME}; + } + } + + /** + * CMP versions supported + * valid CMP versions are 1.0 and 2.0 + * @since ant 1.6 + */ + public static class CMPVersion extends EnumeratedAttribute { + /** 1.0 value */ + public static final String CMP1_0 = "1.0"; + /** 2.0 value */ + public static final String CMP2_0 = "2.0"; + /** {@inheritDoc}. */ + public String[] getValues() { + return new String[]{ + CMP1_0, + CMP2_0, + }; + } + } + /** + * The config which is built by this task and used by the various deployment + * tools to access the configuration of the ejbjar task + */ + private Config config = new Config(); + + + /** + * Stores a handle to the directory to put the Jar files in. This is + * only used by the generic deployment descriptor tool which is created + * if no other deployment descriptor tools are provided. Normally each + * deployment tool will specify the desitination dir itself. + */ + private File destDir; + + /** Instance variable that stores the suffix for the generated jarfile. */ + private String genericJarSuffix = "-generic.jar"; + + /** Instance variable that stores the CMP version for the jboss jarfile. */ + private String cmpVersion = CMPVersion.CMP1_0; + + /** The list of deployment tools we are going to run. */ + private ArrayList deploymentTools = new ArrayList(); + + /** + * Add a deployment tool to the list of deployment tools that will be + * processed + * + * @param deploymentTool a deployment tool instance to which descriptors + * will be passed for processing. + */ + protected void addDeploymentTool(EJBDeploymentTool deploymentTool) { + deploymentTool.setTask(this); + deploymentTools.add(deploymentTool); + } + + /** + * Adds a deployment tool for Weblogic server. + * + * @return the deployment tool instance to be configured. + */ + public WeblogicDeploymentTool createWeblogic() { + WeblogicDeploymentTool tool = new WeblogicDeploymentTool(); + addDeploymentTool(tool); + return tool; + } + + /** + * Adds a deployment tool for Websphere 4.0 server. + * + * @return the deployment tool instance to be configured. + */ + public WebsphereDeploymentTool createWebsphere() { + WebsphereDeploymentTool tool = new WebsphereDeploymentTool(); + addDeploymentTool(tool); + return tool; + } + + /** + * Adds a deployment tool for Borland server. + * + * @return the deployment tool instance to be configured. + */ + public BorlandDeploymentTool createBorland() { + log("Borland deployment tools", Project.MSG_VERBOSE); + + BorlandDeploymentTool tool = new BorlandDeploymentTool(); + tool.setTask(this); + deploymentTools.add(tool); + return tool; + } + + /** + * Adds a deployment tool for iPlanet Application Server. + * + * @return the deployment tool instance to be configured. + */ + public IPlanetDeploymentTool createIplanet() { + log("iPlanet Application Server deployment tools", Project.MSG_VERBOSE); + + IPlanetDeploymentTool tool = new IPlanetDeploymentTool(); + addDeploymentTool(tool); + return tool; + } + + /** + * Adds a deployment tool for JBoss server. + * + * @return the deployment tool instance to be configured. + */ + public JbossDeploymentTool createJboss() { + JbossDeploymentTool tool = new JbossDeploymentTool(); + addDeploymentTool(tool); + return tool; + } + + /** + * Adds a deployment tool for JOnAS server. + * + * @return the deployment tool instance to be configured. + */ + public JonasDeploymentTool createJonas() { + log("JOnAS deployment tools", Project.MSG_VERBOSE); + + JonasDeploymentTool tool = new JonasDeploymentTool(); + addDeploymentTool(tool); + return tool; + } + + /** + * Adds a deployment tool for Weblogic when using the Toplink + * Object-Relational mapping. + * + * @return the deployment tool instance to be configured. + */ + public WeblogicTOPLinkDeploymentTool createWeblogictoplink() { + log("The <weblogictoplink> element is no longer required. Please use " + + "the <weblogic> element and set newCMP=\"true\"", + Project.MSG_INFO); + WeblogicTOPLinkDeploymentTool tool + = new WeblogicTOPLinkDeploymentTool(); + addDeploymentTool(tool); + return tool; + } + + /** + * Adds to the classpath used to locate the super classes and + * interfaces of the classes that will make up the EJB JAR. + * + * @return the path to be configured. + */ + public Path createClasspath() { + if (config.classpath == null) { + config.classpath = new Path(getProject()); + } + return config.classpath.createPath(); + } + + /** + * Create a DTD location record. This stores the location of a DTD. The + * DTD is identified by its public Id. The location may either be a file + * location or a resource location. + * + * @return the DTD location object to be configured by Ant + */ + public DTDLocation createDTD() { + DTDLocation dtdLocation = new DTDLocation(); + config.dtdLocations.add(dtdLocation); + + return dtdLocation; + } + + /** + * Adds a fileset for support elements. + * + * @return a fileset which can be populated with support files. + */ + public FileSet createSupport() { + FileSet supportFileSet = new FileSet(); + config.supportFileSets.add(supportFileSet); + return supportFileSet; + } + + + /** + * Set the Manifest file to use when jarring. As of EJB 1.1, manifest + * files are no longer used to configure the EJB. However, they still + * have a vital importance if the EJB is intended to be packaged in an + * EAR file. By adding "Class-Path" settings to a Manifest file, the EJB + * can look for classes inside the EAR file itself, allowing for easier + * deployment. This is outlined in the J2EE specification, and all J2EE + * components are meant to support it. + * + * @param manifest the manifest to be used in the EJB jar + */ + public void setManifest(File manifest) { + config.manifest = manifest; + } + + /** + * Sets the source directory, which is the directory that + * contains the classes that will be added to the EJB jar. Typically + * this will include the home and remote interfaces and the bean class. + * + * @param inDir the source directory. + */ + public void setSrcdir(File inDir) { + config.srcDir = inDir; + } + + /** + * Set the descriptor directory. The descriptor directory contains the + * EJB deployment descriptors. These are XML files that declare the + * properties of a bean in a particular deployment scenario. Such + * properties include, for example, the transactional nature of the bean + * and the security access control to the bean's methods. + * + * @param inDir the directory containing the deployment descriptors. + */ + public void setDescriptordir(File inDir) { + config.descriptorDir = inDir; + } + + /** + * Set the analyzer to use when adding in dependencies to the JAR. + * + * @param analyzer the name of the dependency analyzer or a class. + */ + public void setDependency(String analyzer) { + config.analyzer = analyzer; + } + + /** + * Set the base name of the EJB JAR that is to be created if it is not + * to be determined from the name of the deployment descriptor files. + * + * @param inValue the basename that will be used when writing the jar + * file containing the EJB + */ + public void setBasejarname(String inValue) { + config.baseJarName = inValue; + if (config.namingScheme == null) { + config.namingScheme = new NamingScheme(); + config.namingScheme.setValue(NamingScheme.BASEJARNAME); + } else if (!config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME)) { + throw new BuildException("The basejarname attribute is not " + + "compatible with the " + + config.namingScheme.getValue() + " naming scheme"); + } + } + + /** + * Set the naming scheme used to determine the name of the generated jars + * from the deployment descriptor + * + * @param namingScheme the naming scheme to be used + */ + public void setNaming(NamingScheme namingScheme) { + config.namingScheme = namingScheme; + if (!config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME) + && config.baseJarName != null) { + throw new BuildException("The basejarname attribute is not " + + "compatible with the " + + config.namingScheme.getValue() + " naming scheme"); + } + } + + /** + * Gets the destination directory. + * + * @return destination directory + * @since ant 1.6 + */ + public File getDestdir() { + return this.destDir; + } + + /** + * Set the destination directory. The EJB jar files will be written into + * this directory. The jar files that exist in this directory are also + * used when determining if the contents of the jar file have changed. + * Note that this parameter is only used if no deployment tools are + * specified. Typically each deployment tool will specify its own + * destination directory. + * + * @param inDir the destination directory in which to generate jars + */ + public void setDestdir(File inDir) { + this.destDir = inDir; + } + + /** + * Gets the CMP version. + * + * @return CMP version + * @since ant 1.6 + */ + public String getCmpversion() { + return this.cmpVersion; + } + + /** + * Sets the CMP version. + * + * @param version CMP version. + * Must be either <code>1.0</code> or <code>2.0</code>.<br/> + * Default is <code>1.0</code>.<br/> + * Initially, only the JBoss implementation does something specific for CMP 2.0.<br/> + * @since ant 1.6 + */ + public void setCmpversion(CMPVersion version) { + this.cmpVersion = version.getValue(); + } + + /** + * Set the classpath to use when resolving classes for inclusion in the jar. + * + * @param classpath the classpath to use. + */ + public void setClasspath(Path classpath) { + config.classpath = classpath; + } + + /** + * Controls whether the + * destination JARs are written out in the destination directory with + * the same hierarchical structure from which the deployment descriptors + * have been read. If this is set to true the generated EJB jars are + * written into the root of the destination directory, otherwise they + * are written out in the same relative position as the deployment + * descriptors in the descriptor directory. + * + * @param inValue the new value of the flatdestdir flag. + */ + public void setFlatdestdir(boolean inValue) { + config.flatDestDir = inValue; + } + + /** + * Set the suffix for the generated jar file. When generic jars are + * generated, they have a suffix which is appended to the the bean name + * to create the name of the jar file. Note that this suffix includes + * the extension fo te jar file and should therefore end with an + * appropriate extension such as .jar or .ear + * + * @param inString the string to use as the suffix. + */ + public void setGenericjarsuffix(String inString) { + this.genericJarSuffix = inString; + } + + /** + * The string which terminates the bean name. + * The convention used by this task is + * that bean descriptors are named as the BeanName with some suffix. The + * baseNameTerminator string separates the bean name and the suffix and + * is used to determine the bean name. + * + * @param inValue a string which marks the end of the basename. + */ + public void setBasenameterminator(String inValue) { + config.baseNameTerminator = inValue; + } + + /** + * Validate the config that has been configured from the build file + * + * @throws BuildException if the config is not valid + */ + private void validateConfig() throws BuildException { + if (config.srcDir == null) { + throw new BuildException("The srcDir attribute must be specified"); + } + + if (config.descriptorDir == null) { + config.descriptorDir = config.srcDir; + } + + if (config.namingScheme == null) { + config.namingScheme = new NamingScheme(); + config.namingScheme.setValue(NamingScheme.DESCRIPTOR); + } else if (config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME) + && config.baseJarName == null) { + throw new BuildException("The basejarname attribute must " + + "be specified with the basejarname naming scheme"); + } + } + + /** + * Invoked by Ant after the task is prepared, when it is ready to execute + * this task. + * + * This will configure all of the nested deployment tools to allow them to + * process the jar. If no deployment tools have been configured a generic + * tool is created to handle the jar. + * + * A parser is configured and then each descriptor found is passed to all + * the deployment tool elements for processing. + * + * @exception BuildException thrown whenever a problem is + * encountered that cannot be recovered from, to signal to ant + * that a major problem occurred within this task. + */ + public void execute() throws BuildException { + validateConfig(); + + if (deploymentTools.size() == 0) { + GenericDeploymentTool genericTool = new GenericDeploymentTool(); + genericTool.setTask(this); + genericTool.setDestdir(destDir); + genericTool.setGenericJarSuffix(genericJarSuffix); + deploymentTools.add(genericTool); + } + + for (Iterator i = deploymentTools.iterator(); i.hasNext();) { + EJBDeploymentTool tool = (EJBDeploymentTool) i.next(); + tool.configure(config); + tool.validateConfigured(); + } + + try { + // Create the parser using whatever parser the system dictates + SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); + saxParserFactory.setValidating(true); + SAXParser saxParser = saxParserFactory.newSAXParser(); + + + DirectoryScanner ds = getDirectoryScanner(config.descriptorDir); + ds.scan(); + String[] files = ds.getIncludedFiles(); + + log(files.length + " deployment descriptors located.", + Project.MSG_VERBOSE); + + // Loop through the files. Each file represents one deployment + // descriptor, and hence one bean in our model. + for (int index = 0; index < files.length; ++index) { + // process the deployment descriptor in each tool + for (Iterator i = deploymentTools.iterator(); i.hasNext();) { + EJBDeploymentTool tool = (EJBDeploymentTool) i.next(); + tool.processDescriptor(files[index], saxParser); + } + } + } catch (SAXException se) { + String msg = "SAXException while creating parser." + + " Details: " + + se.getMessage(); + throw new BuildException(msg, se); + } catch (ParserConfigurationException pce) { + String msg = "ParserConfigurationException while creating parser. " + + "Details: " + pce.getMessage(); + throw new BuildException(msg, pce); + } + } // end of execute() + +} + + + + + + + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java new file mode 100644 index 00000000..069bdfcf --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java @@ -0,0 +1,953 @@ +/* + * 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.tools.ant.taskdefs.optional.ejb; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Set; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; + +import javax.xml.parsers.SAXParser; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Location; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.depend.DependencyAnalyzer; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + + +/** + * A deployment tool which creates generic EJB jars. Generic jars contains + * only those classes and META-INF entries specified in the EJB 1.1 standard + * + * This class is also used as a framework for the creation of vendor specific + * deployment tools. A number of template methods are provided through which the + * vendor specific tool can hook into the EJB creation process. + * + */ +public class GenericDeploymentTool implements EJBDeploymentTool { + /** The default buffer byte size to use for IO */ + public static final int DEFAULT_BUFFER_SIZE = 1024; + /** The level to use for compression */ + public static final int JAR_COMPRESS_LEVEL = 9; + + /** The standard META-INF directory in jar files */ + protected static final String META_DIR = "META-INF/"; + + /** The standard MANIFEST file */ + protected static final String MANIFEST = META_DIR + "MANIFEST.MF"; + + /** Name for EJB Deployment descriptor within EJB jars */ + protected static final String EJB_DD = "ejb-jar.xml"; + + /** A dependency analyzer name to find ancestor classes */ + public static final String ANALYZER_SUPER = "super"; + /** A dependency analyzer name to find all related classes */ + public static final String ANALYZER_FULL = "full"; + /** A dependency analyzer name for no analyzer */ + public static final String ANALYZER_NONE = "none"; + + /** The default analyzer */ + public static final String DEFAULT_ANALYZER = ANALYZER_SUPER; + + /** The analyzer class for the super analyzer */ + public static final String ANALYZER_CLASS_SUPER + = "org.apache.tools.ant.util.depend.bcel.AncestorAnalyzer"; + /** The analyzer class for the super analyzer */ + public static final String ANALYZER_CLASS_FULL + = "org.apache.tools.ant.util.depend.bcel.FullAnalyzer"; + + /** + * The configuration from the containing task. This config combined + * with the settings of the individual attributes here constitues the + * complete config for this deployment tool. + */ + private EjbJar.Config config; + + /** Stores a handle to the directory to put the Jar files in */ + private File destDir; + + /** The classpath to use with this deployment tool. This is appended to + any paths from the ejbjar task itself.*/ + private Path classpath; + + /** Instance variable that stores the suffix for the generated jarfile. */ + private String genericJarSuffix = "-generic.jar"; + + /** + * The task to which this tool belongs. This is used to access services + * provided by the ant core, such as logging. + */ + private Task task; + + /** + * The classloader generated from the given classpath to load + * the super classes and super interfaces. + */ + private ClassLoader classpathLoader = null; + + /** + * Set of files have been loaded into the EJB jar + */ + private Set addedfiles; + + /** + * Handler used to parse the EJB XML descriptor + */ + private DescriptorHandler handler; + + /** + * Dependency analyzer used to collect class dependencies + */ + private DependencyAnalyzer dependencyAnalyzer; + + /** No arg constructor */ + public GenericDeploymentTool() { + } + + + /** + * Set the destination directory; required. + * @param inDir the destination directory. + */ + public void setDestdir(File inDir) { + this.destDir = inDir; + } + + /** + * Get the destination directory. + * + * @return the destination directory into which EJB jars are to be written + */ + protected File getDestDir() { + return destDir; + } + + + /** + * Set the task which owns this tool + * + * @param task the Task to which this deployment tool is associated. + */ + public void setTask(Task task) { + this.task = task; + } + + /** + * Get the task for this tool. + * + * @return the Task instance this tool is associated with. + */ + protected Task getTask() { + return task; + } + + /** + * Get the basename terminator. + * + * @return an ejbjar task configuration + */ + protected EjbJar.Config getConfig() { + return config; + } + + /** + * Indicate if this build is using the base jar name. + * + * @return true if the name of the generated jar is coming from the + * basejarname attribute + */ + protected boolean usingBaseJarName() { + return config.baseJarName != null; + } + + /** + * Set the suffix for the generated jar file. + * @param inString the string to use as the suffix. + */ + public void setGenericJarSuffix(String inString) { + this.genericJarSuffix = inString; + } + + /** + * Add the classpath for the user classes + * + * @return a Path instance to be configured by Ant. + */ + public Path createClasspath() { + if (classpath == null) { + classpath = new Path(task.getProject()); + } + return classpath.createPath(); + } + + /** + * Set the classpath to be used for this compilation. + * + * @param classpath the classpath to be used for this build. + */ + public void setClasspath(Path classpath) { + this.classpath = classpath; + } + + /** + * Get the classpath by combining the one from the surrounding task, if any + * and the one from this tool. + * + * @return the combined classpath + */ + protected Path getCombinedClasspath() { + Path combinedPath = classpath; + if (config.classpath != null) { + if (combinedPath == null) { + combinedPath = config.classpath; + } else { + combinedPath.append(config.classpath); + } + } + + return combinedPath; + } + + /** + * Log a message to the Ant output. + * + * @param message the message to be logged. + * @param level the severity of this message. + */ + protected void log(String message, int level) { + getTask().log(message, level); + } + + /** + * Get the build file location associated with this element's task. + * + * @return the task's location instance. + */ + protected Location getLocation() { + return getTask().getLocation(); + } + + private void createAnalyzer() { + String analyzer = config.analyzer; + if (analyzer == null) { + analyzer = DEFAULT_ANALYZER; + } + + if (analyzer.equals(ANALYZER_NONE)) { + return; + } + + String analyzerClassName = null; + if (analyzer.equals(ANALYZER_SUPER)) { + analyzerClassName = ANALYZER_CLASS_SUPER; + } else if (analyzer.equals(ANALYZER_FULL)) { + analyzerClassName = ANALYZER_CLASS_FULL; + } else { + analyzerClassName = analyzer; + } + + try { + Class analyzerClass = Class.forName(analyzerClassName); + dependencyAnalyzer + = (DependencyAnalyzer) analyzerClass.newInstance(); + dependencyAnalyzer.addClassPath(new Path(task.getProject(), + config.srcDir.getPath())); + dependencyAnalyzer.addClassPath(config.classpath); + } catch (NoClassDefFoundError e) { + dependencyAnalyzer = null; + task.log("Unable to load dependency analyzer: " + analyzerClassName + + " - dependent class not found: " + e.getMessage(), + Project.MSG_WARN); + } catch (Exception e) { + dependencyAnalyzer = null; + task.log("Unable to load dependency analyzer: " + analyzerClassName + + " - exception: " + e.getMessage(), + Project.MSG_WARN); + } + } + + + /** + * Configure this tool for use in the ejbjar task. + * + * @param config the configuration from the surrounding ejbjar task. + */ + public void configure(EjbJar.Config config) { + this.config = config; + + createAnalyzer(); + classpathLoader = null; + } + + /** + * Utility method that encapsulates the logic of adding a file entry to + * a .jar file. Used by execute() to add entries to the jar file as it is + * constructed. + * @param jStream A JarOutputStream into which to write the + * jar entry. + * @param inputFile A File from which to read the + * contents the file being added. + * @param logicalFilename A String representing the name, including + * all relevant path information, that should be stored for the entry + * being added. + * @throws BuildException if there is a problem. + */ + protected void addFileToJar(JarOutputStream jStream, + File inputFile, + String logicalFilename) + throws BuildException { + FileInputStream iStream = null; + try { + if (!addedfiles.contains(logicalFilename)) { + iStream = new FileInputStream(inputFile); + // Create the zip entry and add it to the jar file + ZipEntry zipEntry = new ZipEntry(logicalFilename.replace('\\', '/')); + jStream.putNextEntry(zipEntry); + + // Create the file input stream, and buffer everything over + // to the jar output stream + byte[] byteBuffer = new byte[2 * DEFAULT_BUFFER_SIZE]; + int count = 0; + do { + jStream.write(byteBuffer, 0, count); + count = iStream.read(byteBuffer, 0, byteBuffer.length); + } while (count != -1); + + //add it to list of files in jar + addedfiles.add(logicalFilename); + } + } catch (IOException ioe) { + log("WARNING: IOException while adding entry " + + logicalFilename + " to jarfile from " + + inputFile.getPath() + " " + ioe.getClass().getName() + + "-" + ioe.getMessage(), Project.MSG_WARN); + } finally { + // Close up the file input stream for the class file + if (iStream != null) { + try { + iStream.close(); + } catch (IOException closeException) { + // ignore + } + } + } + } + + /** + * Get a descriptionHandler. + * @param srcDir the source directory. + * @return a handler. + */ + protected DescriptorHandler getDescriptorHandler(File srcDir) { + DescriptorHandler h = new DescriptorHandler(getTask(), srcDir); + + registerKnownDTDs(h); + + // register any DTDs supplied by the user + for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) { + EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next(); + h.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation()); + } + return h; + } + + /** + * Register the locations of all known DTDs. + * + * vendor-specific subclasses should override this method to define + * the vendor-specific locations of the EJB DTDs + * @param handler no used in this class. + */ + protected void registerKnownDTDs(DescriptorHandler handler) { + // none to register for generic + } + + /** {@inheritDoc}. */ + public void processDescriptor(String descriptorFileName, SAXParser saxParser) { + + checkConfiguration(descriptorFileName, saxParser); + + try { + handler = getDescriptorHandler(config.srcDir); + + // Retrive the files to be added to JAR from EJB descriptor + Hashtable ejbFiles = parseEjbFiles(descriptorFileName, saxParser); + + // Add any support classes specified in the build file + addSupportClasses(ejbFiles); + + // Determine the JAR filename (without filename extension) + String baseName = getJarBaseName(descriptorFileName); + + String ddPrefix = getVendorDDPrefix(baseName, descriptorFileName); + + File manifestFile = getManifestFile(ddPrefix); + if (manifestFile != null) { + ejbFiles.put(MANIFEST, manifestFile); + } + + + + // First the regular deployment descriptor + ejbFiles.put(META_DIR + EJB_DD, + new File(config.descriptorDir, descriptorFileName)); + + // now the vendor specific files, if any + addVendorFiles(ejbFiles, ddPrefix); + + // add any dependent files + checkAndAddDependants(ejbFiles); + + // Lastly create File object for the Jar files. If we are using + // a flat destination dir, then we need to redefine baseName! + if (config.flatDestDir && baseName.length() != 0) { + int startName = baseName.lastIndexOf(File.separator); + if (startName == -1) { + startName = 0; + } + + int endName = baseName.length(); + baseName = baseName.substring(startName, endName); + } + + File jarFile = getVendorOutputJarFile(baseName); + + + // Check to see if we need a build and start doing the work! + if (needToRebuild(ejbFiles, jarFile)) { + // Log that we are going to build... + log("building " + + jarFile.getName() + + " with " + + String.valueOf(ejbFiles.size()) + + " files", + Project.MSG_INFO); + + // Use helper method to write the jarfile + String publicId = getPublicId(); + writeJar(baseName, jarFile, ejbFiles, publicId); + + } else { + // Log that the file is up to date... + log(jarFile.toString() + " is up to date.", + Project.MSG_VERBOSE); + } + + } catch (SAXException se) { + String msg = "SAXException while parsing '" + + descriptorFileName + + "'. This probably indicates badly-formed XML." + + " Details: " + + se.getMessage(); + throw new BuildException(msg, se); + } catch (IOException ioe) { + String msg = "IOException while parsing'" + + descriptorFileName + + "'. This probably indicates that the descriptor" + + " doesn't exist. Details: " + + ioe.getMessage(); + throw new BuildException(msg, ioe); + } + } + + /** + * This method is called as the first step in the processDescriptor method + * to allow vendor-specific subclasses to validate the task configuration + * prior to processing the descriptor. If the configuration is invalid, + * a BuildException should be thrown. + * + * @param descriptorFileName String representing the file name of an EJB + * descriptor to be processed + * @param saxParser SAXParser which may be used to parse the XML + * descriptor + * @throws BuildException if there is a problem. + */ + protected void checkConfiguration(String descriptorFileName, + SAXParser saxParser) throws BuildException { + + /* + * For the GenericDeploymentTool, do nothing. Vendor specific + * subclasses should throw a BuildException if the configuration is + * invalid for their server. + */ + } + + /** + * This method returns a list of EJB files found when the specified EJB + * descriptor is parsed and processed. + * + * @param descriptorFileName String representing the file name of an EJB + * descriptor to be processed + * @param saxParser SAXParser which may be used to parse the XML + * descriptor + * @return Hashtable of EJB class (and other) files to be + * added to the completed JAR file + * @throws SAXException Any SAX exception, possibly wrapping another + * exception + * @throws IOException An IOException from the parser, possibly from a + * the byte stream or character stream + */ + protected Hashtable parseEjbFiles(String descriptorFileName, SAXParser saxParser) + throws IOException, SAXException { + FileInputStream descriptorStream = null; + Hashtable ejbFiles = null; + + try { + + /* Parse the ejb deployment descriptor. While it may not + * look like much, we use a SAXParser and an inner class to + * get hold of all the classfile names for the descriptor. + */ + descriptorStream + = new FileInputStream(new File(config.descriptorDir, descriptorFileName)); + saxParser.parse(new InputSource(descriptorStream), handler); + + ejbFiles = handler.getFiles(); + + } finally { + if (descriptorStream != null) { + try { + descriptorStream.close(); + } catch (IOException closeException) { + // ignore + } + } + } + + return ejbFiles; + } + + /** + * Adds any classes the user specifies using <i>support</i> nested elements + * to the <code>ejbFiles</code> Hashtable. + * + * @param ejbFiles Hashtable of EJB classes (and other) files that will be + * added to the completed JAR file + */ + protected void addSupportClasses(Hashtable ejbFiles) { + // add in support classes if any + Project project = task.getProject(); + for (Iterator i = config.supportFileSets.iterator(); i.hasNext();) { + FileSet supportFileSet = (FileSet) i.next(); + File supportBaseDir = supportFileSet.getDir(project); + DirectoryScanner supportScanner = supportFileSet.getDirectoryScanner(project); + supportScanner.scan(); + String[] supportFiles = supportScanner.getIncludedFiles(); + for (int j = 0; j < supportFiles.length; ++j) { + ejbFiles.put(supportFiles[j], new File(supportBaseDir, supportFiles[j])); + } + } + } + + + /** + * Using the EJB descriptor file name passed from the <code>ejbjar</code> + * task, this method returns the "basename" which will be used to name the + * completed JAR file. + * + * @param descriptorFileName String representing the file name of an EJB + * descriptor to be processed + * @return The "basename" which will be used to name the + * completed JAR file + */ + protected String getJarBaseName(String descriptorFileName) { + + String baseName = ""; + + // Work out what the base name is + if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.BASEJARNAME)) { + String canonicalDescriptor = descriptorFileName.replace('\\', '/'); + int index = canonicalDescriptor.lastIndexOf('/'); + if (index != -1) { + baseName = descriptorFileName.substring(0, index + 1); + } + baseName += config.baseJarName; + } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) { + int lastSeparatorIndex = descriptorFileName.lastIndexOf(File.separator); + int endBaseName = -1; + if (lastSeparatorIndex != -1) { + endBaseName = descriptorFileName.indexOf(config.baseNameTerminator, + lastSeparatorIndex); + } else { + endBaseName = descriptorFileName.indexOf(config.baseNameTerminator); + } + + if (endBaseName != -1) { + baseName = descriptorFileName.substring(0, endBaseName); + } else { + throw new BuildException("Unable to determine jar name " + + "from descriptor \"" + descriptorFileName + "\""); + } + } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DIRECTORY)) { + File descriptorFile = new File(config.descriptorDir, descriptorFileName); + String path = descriptorFile.getAbsolutePath(); + int lastSeparatorIndex + = path.lastIndexOf(File.separator); + if (lastSeparatorIndex == -1) { + throw new BuildException("Unable to determine directory name holding descriptor"); + } + String dirName = path.substring(0, lastSeparatorIndex); + int dirSeparatorIndex = dirName.lastIndexOf(File.separator); + if (dirSeparatorIndex != -1) { + dirName = dirName.substring(dirSeparatorIndex + 1); + } + + baseName = dirName; + } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.EJB_NAME)) { + baseName = handler.getEjbName(); + } + return baseName; + } + + /** + * Get the prefix for vendor deployment descriptors. + * + * This will contain the path and the start of the descriptor name, + * depending on the naming scheme + * @param baseName the base name to use. + * @param descriptorFileName the file name to use. + * @return the prefix. + */ + public String getVendorDDPrefix(String baseName, String descriptorFileName) { + String ddPrefix = null; + + if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) { + ddPrefix = baseName + config.baseNameTerminator; + } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.BASEJARNAME) + || config.namingScheme.getValue().equals(EjbJar.NamingScheme.EJB_NAME) + || config.namingScheme.getValue().equals(EjbJar.NamingScheme.DIRECTORY)) { + String canonicalDescriptor = descriptorFileName.replace('\\', '/'); + int index = canonicalDescriptor.lastIndexOf('/'); + if (index == -1) { + ddPrefix = ""; + } else { + ddPrefix = descriptorFileName.substring(0, index + 1); + } + } + return ddPrefix; + } + + /** + * Add any vendor specific files which should be included in the + * EJB Jar. + * @param ejbFiles a hashtable entryname -> file. + * @param ddPrefix a prefix to use. + */ + protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) { + // nothing to add for generic tool. + } + + + /** + * Get the vendor specific name of the Jar that will be output. The modification date + * of this jar will be checked against the dependent bean classes. + * @param baseName the basename to use. + */ + File getVendorOutputJarFile(String baseName) { + return new File(destDir, baseName + genericJarSuffix); + } + + /** + * This method checks the timestamp on each file listed in the <code> + * ejbFiles</code> and compares them to the timestamp on the <code>jarFile + * </code>. If the <code>jarFile</code>'s timestamp is more recent than + * each EJB file, <code>true</code> is returned. Otherwise, <code>false + * </code> is returned. + * TODO: find a way to check the manifest-file, that is found by naming convention + * + * @param ejbFiles Hashtable of EJB classes (and other) files that will be + * added to the completed JAR file + * @param jarFile JAR file which will contain all of the EJB classes (and + * other) files + * @return boolean indicating whether or not the <code>jarFile</code> + * is up to date + */ + protected boolean needToRebuild(Hashtable ejbFiles, File jarFile) { + if (jarFile.exists()) { + long lastBuild = jarFile.lastModified(); + + Iterator fileIter = ejbFiles.values().iterator(); + + // Loop through the files seeing if any has been touched + // more recently than the destination jar. + while (fileIter.hasNext()) { + File currentFile = (File) fileIter.next(); + if (lastBuild < currentFile.lastModified()) { + log("Build needed because " + currentFile.getPath() + " is out of date", + Project.MSG_VERBOSE); + return true; + } + } + return false; + } + + return true; + } + + /** + * Returns the Public ID of the DTD specified in the EJB descriptor. Not + * every vendor-specific <code>DeploymentTool</code> will need to reference + * this value or may want to determine this value in a vendor-specific way. + * + * @return Public ID of the DTD specified in the EJB descriptor. + */ + protected String getPublicId() { + return handler.getPublicId(); + } + + /** + * Get the manifest file to use for building the generic jar. + * + * If the file does not exist the global manifest from the config is used + * otherwise the default Ant manifest will be used. + * + * @param prefix the prefix where to llook for the manifest file based on + * the naming convention. + * + * @return the manifest file or null if the manifest file does not exist + */ + protected File getManifestFile(String prefix) { + File manifestFile + = new File(getConfig().descriptorDir, prefix + "manifest.mf"); + if (manifestFile.exists()) { + return manifestFile; + } + + if (config.manifest != null) { + return config.manifest; + } + return null; + } + + /** + * Method used to encapsulate the writing of the JAR file. Iterates over the + * filenames/java.io.Files in the Hashtable stored on the instance variable + * ejbFiles. + * @param baseName the base name to use. + * @param jarfile the jar file to write to. + * @param files the files to write to the jar. + * @param publicId the id to use. + * @throws BuildException if there is a problem. + */ + protected void writeJar(String baseName, File jarfile, Hashtable files, + String publicId) throws BuildException { + + JarOutputStream jarStream = null; + try { + // clean the addedfiles set + if (addedfiles == null) { + addedfiles = new HashSet(); + } else { + addedfiles.clear(); + } + + /* If the jarfile already exists then whack it and recreate it. + * Should probably think of a more elegant way to handle this + * so that in case of errors we don't leave people worse off + * than when we started =) + */ + if (jarfile.exists()) { + jarfile.delete(); + } + jarfile.getParentFile().mkdirs(); + jarfile.createNewFile(); + + InputStream in = null; + Manifest manifest = null; + try { + File manifestFile = (File) files.get(MANIFEST); + if (manifestFile != null && manifestFile.exists()) { + in = new FileInputStream(manifestFile); + } else { + String defaultManifest = "/org/apache/tools/ant/defaultManifest.mf"; + in = this.getClass().getResourceAsStream(defaultManifest); + if (in == null) { + throw new BuildException("Could not find " + + "default manifest: " + defaultManifest); + } + } + + manifest = new Manifest(in); + } catch (IOException e) { + throw new BuildException ("Unable to read manifest", e, getLocation()); + } finally { + if (in != null) { + in.close(); + } + } + + // Create the streams necessary to write the jarfile + + jarStream = new JarOutputStream(new FileOutputStream(jarfile), manifest); + jarStream.setMethod(JarOutputStream.DEFLATED); + + // Loop through all the class files found and add them to the jar + for (Iterator entryIterator = files.keySet().iterator(); entryIterator.hasNext();) { + String entryName = (String) entryIterator.next(); + if (entryName.equals(MANIFEST)) { + continue; + } + + File entryFile = (File) files.get(entryName); + + log("adding file '" + entryName + "'", + Project.MSG_VERBOSE); + + addFileToJar(jarStream, entryFile, entryName); + + // See if there are any inner classes for this class and add them in if there are + InnerClassFilenameFilter flt = new InnerClassFilenameFilter(entryFile.getName()); + File entryDir = entryFile.getParentFile(); + String[] innerfiles = entryDir.list(flt); + if (innerfiles != null) { + for (int i = 0, n = innerfiles.length; i < n; i++) { + + //get and clean up innerclass name + int entryIndex = entryName.lastIndexOf(entryFile.getName()) - 1; + if (entryIndex < 0) { + entryName = innerfiles[i]; + } else { + entryName = entryName.substring(0, entryIndex) + + File.separatorChar + innerfiles[i]; + } + // link the file + entryFile = new File(config.srcDir, entryName); + + log("adding innerclass file '" + entryName + "'", + Project.MSG_VERBOSE); + + addFileToJar(jarStream, entryFile, entryName); + + } + } + } + } catch (IOException ioe) { + String msg = "IOException while processing ejb-jar file '" + + jarfile.toString() + + "'. Details: " + + ioe.getMessage(); + throw new BuildException(msg, ioe); + } finally { + if (jarStream != null) { + try { + jarStream.close(); + } catch (IOException closeException) { + // ignore + } + } + } + } // end of writeJar + + + /** + * Add all available classes, that depend on Remote, Home, Bean, PK + * @param checkEntries files, that are extracted from the deployment descriptor + * @throws BuildException if there is a problem. + */ + protected void checkAndAddDependants(Hashtable checkEntries) + throws BuildException { + + if (dependencyAnalyzer == null) { + return; + } + + dependencyAnalyzer.reset(); + + Iterator i = checkEntries.keySet().iterator(); + while (i.hasNext()) { + String entryName = (String) i.next(); + if (entryName.endsWith(".class")) { + String className = entryName.substring(0, + entryName.length() - ".class".length()); + className = className.replace(File.separatorChar, '/'); + className = className.replace('/', '.'); + + dependencyAnalyzer.addRootClass(className); + } + } + + Enumeration e = dependencyAnalyzer.getClassDependencies(); + + while (e.hasMoreElements()) { + String classname = (String) e.nextElement(); + String location + = classname.replace('.', File.separatorChar) + ".class"; + File classFile = new File(config.srcDir, location); + if (classFile.exists()) { + checkEntries.put(location, classFile); + log("dependent class: " + classname + " - " + classFile, + Project.MSG_VERBOSE); + } + } + } + + + /** + * Returns a Classloader object which parses the passed in generic EjbJar classpath. + * The loader is used to dynamically load classes from javax.ejb.* and the classes + * being added to the jar. + * @return a classloader. + */ + protected ClassLoader getClassLoaderForBuild() { + if (classpathLoader != null) { + return classpathLoader; + } + + Path combinedClasspath = getCombinedClasspath(); + + // only generate a new ClassLoader if we have a classpath + if (combinedClasspath == null) { + classpathLoader = getClass().getClassLoader(); + } else { + // Memory leak in line below + classpathLoader + = getTask().getProject().createClassLoader(combinedClasspath); + } + + return classpathLoader; + } + + /** + * Called to validate that the tool parameters have been configured. + * + * @throws BuildException If the Deployment Tool's configuration isn't + * valid + */ + public void validateConfigured() throws BuildException { + if ((destDir == null) || (!destDir.isDirectory())) { + String msg = "A valid destination directory must be specified " + + "using the \"destdir\" attribute."; + throw new BuildException(msg, getLocation()); + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetDeploymentTool.java new file mode 100644 index 00000000..cbc8526a --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetDeploymentTool.java @@ -0,0 +1,403 @@ +/* + * 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.tools.ant.taskdefs.optional.ejb; + +import java.io.File; +import java.io.IOException; +import java.util.Hashtable; +import java.util.Iterator; + +import javax.xml.parsers.SAXParser; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.xml.sax.SAXException; + +/** + * This class is used to generate iPlanet Application Server (iAS) 6.0 stubs and + * skeletons and build an EJB Jar file. It is designed to be used with the Ant + * <code>ejbjar</code> task. If only stubs and skeletons need to be generated + * (in other words, if no JAR file needs to be created), refer to the + * <code>iplanet-ejbc</code> task and the <code>IPlanetEjbcTask</code> class. + * <p> + * The following attributes may be specified by the user: + * <ul> + * <li><i>destdir</i> -- The base directory into which the generated JAR + * files will be written. Each JAR file is written + * in directories which correspond to their location + * within the "descriptordir" namespace. This is a + * required attribute. + * <li><i>classpath</i> -- The classpath used when generating EJB stubs and + * skeletons. This is an optional attribute (if + * omitted, the classpath specified in the "ejbjar" + * parent task will be used). If specified, the + * classpath elements will be prepended to the + * classpath specified in the parent "ejbjar" task. + * Note that nested "classpath" elements may also be + * used. + * <li><i>keepgenerated</i> -- Indicates whether or not the Java source + * files which are generated by ejbc will be + * saved or automatically deleted. If "yes", + * the source files will be retained. This is + * an optional attribute (if omitted, it + * defaults to "no"). + * <li><i>debug</i> -- Indicates whether or not the ejbc utility should + * log additional debugging statements to the standard + * output. If "yes", the additional debugging statements + * will be generated (if omitted, it defaults to "no"). + * <li><i>iashome</i> -- May be used to specify the "home" directory for + * this iPlanet Application server installation. This + * is used to find the ejbc utility if it isn't + * included in the user's system path. This is an + * optional attribute (if specified, it should refer + * to the <code>[install-location]/iplanet/ias6/ias + * </code> directory). If omitted, the ejbc utility + * must be on the user's system path. + * <li><i>suffix</i> -- String value appended to the JAR filename when + * creating each JAR. This attribute is not required + * (if omitted, it defaults to ".jar"). + * </ul> + * <p> + * For each EJB descriptor found in the "ejbjar" parent task, this deployment + * tool will locate the three classes that comprise the EJB. If these class + * files cannot be located in the specified <code>srcdir</code> directory, the + * task will fail. The task will also attempt to locate the EJB stubs and + * skeletons in this directory. If found, the timestamps on the stubs and + * skeletons will be checked to ensure they are up to date. Only if these files + * cannot be found or if they are out of date will ejbc be called. + * + * @see IPlanetEjbc + */ +public class IPlanetDeploymentTool extends GenericDeploymentTool { + + /* Attributes set by the Ant build file */ + private File iashome; + private String jarSuffix = ".jar"; + private boolean keepgenerated = false; + private boolean debug = false; + + /* + * Filenames of the standard EJB descriptor (which is passed to this class + * from the parent "ejbjar" task) and the iAS-specific EJB descriptor + * (whose name is determined by this class). Both filenames are relative + * to the directory specified by the "srcdir" attribute in the ejbjar task. + */ + private String descriptorName; + private String iasDescriptorName; + + /* + * The displayName variable stores the value of the "display-name" element + * from the standard EJB descriptor. As a future enhancement to this task, + * we may determine the name of the EJB JAR file using this display-name, + * but this has not be implemented yet. + */ + private String displayName; + + /* + * Regardless of the name of the iAS-specific EJB descriptor file, it will + * written in the completed JAR file as "ias-ejb-jar.xml". This is the + * naming convention implemented by iAS. + */ + private static final String IAS_DD = "ias-ejb-jar.xml"; + + /** + * Setter method used to store the "home" directory of the user's iAS + * installation. The directory specified should typically be + * <code>[install-location]/iplanet/ias6/ias</code>. + * + * @param iashome The home directory for the user's iAS installation. + */ + public void setIashome(File iashome) { + this.iashome = iashome; + } + + /** + * Setter method used to specify whether the Java source files generated by + * the ejbc utility should be saved or automatically deleted. + * + * @param keepgenerated boolean which, if <code>true</code>, indicates that + * Java source files generated by ejbc for the stubs + * and skeletons should be kept. + */ + public void setKeepgenerated(boolean keepgenerated) { + this.keepgenerated = keepgenerated; + } + + /** + * Sets whether or not debugging output will be generated when ejbc is + * executed. + * + * @param debug A boolean indicating if debugging output should be generated + */ + public void setDebug(boolean debug) { + this.debug = debug; + } + + /** + * Setter method used to specify the filename suffix (for example, ".jar") + * for the JAR files to be created. + * + * @param jarSuffix The string to use as the JAR filename suffix. + */ + public void setSuffix(String jarSuffix) { + this.jarSuffix = jarSuffix; + } + + /** + * Since iAS doesn't generate a "generic" JAR as part of its processing, + * this attribute is ignored and a warning message is displayed to the user. + * + * @param inString the string to use as the suffix. This parameter is + * ignored. + */ + @Override + public void setGenericJarSuffix(String inString) { + log("Since a generic JAR file is not created during processing, the " + + "iPlanet Deployment Tool does not support the " + + "\"genericjarsuffix\" attribute. It will be ignored.", + Project.MSG_WARN); + } + + /** {@inheritDoc}. */ + @Override + public void processDescriptor(String descriptorName, SAXParser saxParser) { + this.descriptorName = descriptorName; + this.iasDescriptorName = null; + + log("iPlanet Deployment Tool processing: " + descriptorName + " (and " + + getIasDescriptorName() + ")", Project.MSG_VERBOSE); + + super.processDescriptor(descriptorName, saxParser); + } + + /** + * Verifies that the user selections are valid. + * + * @param descriptorFileName String representing the file name of an EJB + * descriptor to be processed + * @param saxParser SAXParser which may be used to parse the XML + * descriptor + * @throws BuildException If the user selections are invalid. + */ + @Override + protected void checkConfiguration(String descriptorFileName, + SAXParser saxParser) throws BuildException { + + int startOfName = descriptorFileName.lastIndexOf(File.separatorChar) + 1; + String stdXml = descriptorFileName.substring(startOfName); + if (stdXml.equals(EJB_DD) && (getConfig().baseJarName == null)) { + String msg = "No name specified for the completed JAR file. The EJB" + + " descriptor should be prepended with the JAR " + + "name or it should be specified using the " + + "attribute \"basejarname\" in the \"ejbjar\" task."; + throw new BuildException(msg, getLocation()); + } + + File iasDescriptor = new File(getConfig().descriptorDir, + getIasDescriptorName()); + if ((!iasDescriptor.exists()) || (!iasDescriptor.isFile())) { + String msg = "The iAS-specific EJB descriptor (" + + iasDescriptor + ") was not found."; + throw new BuildException(msg, getLocation()); + } + + if ((iashome != null) && (!iashome.isDirectory())) { + String msg = "If \"iashome\" is specified, it must be a valid " + + "directory (it was set to " + iashome + ")."; + throw new BuildException(msg, getLocation()); + } + } + + /** + * This method returns a list of EJB files found when the specified EJB + * descriptor is parsed and processed. + * + * @param descriptorFileName String representing the file name of an EJB + * descriptor to be processed + * @param saxParser SAXParser which may be used to parse the XML + * descriptor + * @return Hashtable of EJB class (and other) files to be + * added to the completed JAR file + * @throws IOException An IOException from the parser, possibly from + * the byte stream or character stream + * @throws SAXException Any SAX exception, possibly wrapping another + * exception + */ + @Override + protected Hashtable parseEjbFiles(String descriptorFileName, + SAXParser saxParser) throws IOException, SAXException { + + Hashtable files; + + /* Build and populate an instance of the ejbc utility */ + IPlanetEjbc ejbc = new IPlanetEjbc( + new File(getConfig().descriptorDir, + descriptorFileName), + new File(getConfig().descriptorDir, + getIasDescriptorName()), + getConfig().srcDir, + getCombinedClasspath().toString(), + saxParser); + ejbc.setRetainSource(keepgenerated); + ejbc.setDebugOutput(debug); + if (iashome != null) { + ejbc.setIasHomeDir(iashome); + } + if (getConfig().dtdLocations != null) { + for (Iterator i = getConfig().dtdLocations.iterator(); + i.hasNext();) { + EjbJar.DTDLocation dtdLocation = + (EjbJar.DTDLocation) i.next(); + ejbc.registerDTD(dtdLocation.getPublicId(), + dtdLocation.getLocation()); + } + } + + /* Execute the ejbc utility -- stubs/skeletons are rebuilt, if needed */ + try { + ejbc.execute(); + } catch (IPlanetEjbc.EjbcException e) { + throw new BuildException("An error has occurred while trying to " + + "execute the iAS ejbc utility", e, getLocation()); + } + + displayName = ejbc.getDisplayName(); + files = ejbc.getEjbFiles(); + + /* Add CMP descriptors to the list of EJB files */ + String[] cmpDescriptors = ejbc.getCmpDescriptors(); + if (cmpDescriptors.length > 0) { + File baseDir = getConfig().descriptorDir; + + int endOfPath = descriptorFileName.lastIndexOf(File.separator); + String relativePath = descriptorFileName.substring(0, endOfPath + 1); + + for (int i = 0; i < cmpDescriptors.length; i++) { + int endOfCmp = cmpDescriptors[i].lastIndexOf('/'); + String cmpDescriptor = cmpDescriptors[i].substring(endOfCmp + 1); + + File cmpFile = new File(baseDir, relativePath + cmpDescriptor); + if (!cmpFile.exists()) { + throw new BuildException("The CMP descriptor file (" + + cmpFile + ") could not be found.", getLocation()); + } + files.put(cmpDescriptors[i], cmpFile); + } + } + + return files; + } + + /** + * Add the iAS-specific EJB descriptor to the list of files which will be + * written to the JAR file. + * + * @param ejbFiles Hashtable of EJB class (and other) files to be added to + * the completed JAR file. + * @param ddPrefix not used + */ + @Override + protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) { + ejbFiles.put(META_DIR + IAS_DD, new File(getConfig().descriptorDir, + getIasDescriptorName())); + } + + /** + * Get the name of the Jar that will be written. The modification date + * of this jar will be checked against the dependent bean classes. + * + * @param baseName String name of the EJB JAR file to be written (without + * a filename extension). + * + * @return File representing the JAR file which will be written. + */ + @Override + File getVendorOutputJarFile(String baseName) { + File jarFile = new File(getDestDir(), baseName + jarSuffix); + log("JAR file name: " + jarFile.toString(), Project.MSG_VERBOSE); + return jarFile; + } + + /** + * The iAS ejbc utility doesn't require the Public ID of the descriptor's + * DTD for it to process correctly--this method always returns <code>null + * </code>. + * + * @return <code>null</code>. + */ + @Override + protected String getPublicId() { + return null; + } + + /** + * Determines the name of the iAS-specific EJB descriptor using the + * specified standard EJB descriptor name. In general, the standard + * descriptor will be named "[basename]-ejb-jar.xml", and this method will + * return "[basename]-ias-ejb-jar.xml". + * + * @return The name of the iAS-specific EJB descriptor file. + */ + private String getIasDescriptorName() { + + /* Only calculate the descriptor name once */ + if (iasDescriptorName != null) { + return iasDescriptorName; + } + + String path = ""; // Directory path of the EJB descriptor + String basename; // Filename appearing before name terminator + String remainder; // Filename appearing after the name terminator + + /* Find the end of the standard descriptor's relative path */ + int startOfFileName = descriptorName.lastIndexOf(File.separatorChar); + if (startOfFileName != -1) { + path = descriptorName.substring(0, startOfFileName + 1); + } + + /* Check to see if the standard name is used (there's no basename) */ + if (descriptorName.substring(startOfFileName + 1).equals(EJB_DD)) { + basename = ""; + remainder = EJB_DD; + + } else { + int endOfBaseName = descriptorName.indexOf( + getConfig().baseNameTerminator, + startOfFileName); + /* + * Check for the odd case where the terminator and/or filename + * extension aren't found. These will ensure "ias-" appears at the + * end of the name and before the '.' (if present). + */ + if (endOfBaseName < 0) { + endOfBaseName = descriptorName.lastIndexOf('.') - 1; + if (endOfBaseName < 0) { + endOfBaseName = descriptorName.length() - 1; + } + } + + basename = descriptorName.substring(startOfFileName + 1, + endOfBaseName + 1); + remainder = descriptorName.substring(endOfBaseName + 1); + } + + iasDescriptorName = path + basename + "ias-" + remainder; + return iasDescriptorName; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbc.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbc.java new file mode 100644 index 00000000..ed799d33 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbc.java @@ -0,0 +1,1495 @@ +/* + * 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.tools.ant.taskdefs.optional.ejb; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.xml.sax.AttributeList; +import org.xml.sax.HandlerBase; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * Compiles EJB stubs and skeletons for the iPlanet Application + * Server (iAS). The class will read a standard EJB descriptor (as well as an + * EJB descriptor specific to iPlanet Application Server) to identify one or + * more EJBs to process. It will search for EJB "source" classes (the remote +; * interface, home interface, and EJB implementation class) and the EJB stubs + * and skeletons in the specified destination directory. Only if the stubs and + * skeletons cannot be found or if they're out of date will the iPlanet + * Application Server ejbc utility be run. + * <p> + * Because this class (and it's assorted inner classes) may be bundled into the + * iPlanet Application Server distribution at some point (and removed from the + * Ant distribution), the class has been written to be independent of all + * Ant-specific classes. It is also for this reason (and to avoid cluttering + * the Apache Ant source files) that this utility has been packaged into a + * single source file. + * <p> + * For more information on Ant Tasks for iPlanet Application Server, see the + * <code>IPlanetDeploymentTool</code> and <code>IPlanetEjbcTask</code> classes. + * + * @see IPlanetDeploymentTool + * @see IPlanetEjbcTask + * @ant.task ignore="true" + */ +public class IPlanetEjbc { + + private static final int MIN_NUM_ARGS = 2; + private static final int MAX_NUM_ARGS = 8; + private static final int NUM_CLASSES_WITH_IIOP = 15; + private static final int NUM_CLASSES_WITHOUT_IIOP = 9; + + /* Constants used for the "beantype" attribute */ + private static final String ENTITY_BEAN = "entity"; + private static final String STATELESS_SESSION = "stateless"; + private static final String STATEFUL_SESSION = "stateful"; + + /* Filenames of the standard EJB descriptor and the iAS-specific descriptor */ + private File stdDescriptor; + private File iasDescriptor; + + /* + * Directory where "source" EJB files are stored and where stubs and + * skeletons will also be written. + */ + private File destDirectory; + + /* Classpath used when the iAS ejbc is called */ + private String classpath; + private String[] classpathElements; + + /* Options passed to the iAS ejbc */ + private boolean retainSource = false; + private boolean debugOutput = false; + + /* iAS installation directory (used if ejbc isn't on user's PATH) */ + private File iasHomeDir; + + /* Parser and handler used to process both EJB descriptor files */ + private SAXParser parser; + private EjbcHandler handler = new EjbcHandler(); + + /* + * This Hashtable maintains a list of EJB class files processed by the ejbc + * utility (both "source" class files as well as stubs and skeletons). The + * key for the Hashtable is a String representing the path to the class file + * (relative to the destination directory). The value for the Hashtable is + * a File object which reference the actual class file. + */ + private Hashtable ejbFiles = new Hashtable(); + + /* Value of the display-name element read from the standard EJB descriptor */ + private String displayName; + + /** + * Constructs an instance which may be used to process EJB descriptors and + * generate EJB stubs and skeletons, if needed. + * + * @param stdDescriptor File referencing a standard EJB descriptor. + * @param iasDescriptor File referencing an iAS-specific EJB descriptor. + * @param destDirectory File referencing the base directory where both + * EJB "source" files are found and where stubs and + * skeletons will be written. + * @param classpath String representation of the classpath to be used + * by the iAS ejbc utility. + * @param parser SAXParser to be used to process both of the EJB + * descriptors. + * @todo classpathElements is not needed here, its never used + * (at least IDEA tells me so! :) + */ + public IPlanetEjbc(File stdDescriptor, + File iasDescriptor, + File destDirectory, + String classpath, + SAXParser parser) { + this.stdDescriptor = stdDescriptor; + this.iasDescriptor = iasDescriptor; + this.destDirectory = destDirectory; + this.classpath = classpath; + this.parser = parser; + + /* + * Parse the classpath into it's individual elements and store the + * results in the "classpathElements" instance variable. + */ + List elements = new ArrayList(); + if (classpath != null) { + StringTokenizer st = new StringTokenizer(classpath, + File.pathSeparator); + while (st.hasMoreTokens()) { + elements.add(st.nextToken()); + } + classpathElements + = (String[]) elements.toArray(new String[elements.size()]); + } + } + + /** + * If true, the Java source files which are generated by the + * ejbc process are retained. + * + * @param retainSource A boolean indicating if the Java source files for + * the stubs and skeletons should be retained. + * @todo This is not documented in the HTML. On purpose? + */ + public void setRetainSource(boolean retainSource) { + this.retainSource = retainSource; + } + + /** + * If true, enables debugging output when ejbc is executed. + * + * @param debugOutput A boolean indicating if debugging output should be + * generated + */ + public void setDebugOutput(boolean debugOutput) { + this.debugOutput = debugOutput; + } + + /** + * Registers the location of a local DTD file or resource. By registering + * a local DTD, EJB descriptors can be parsed even when the remote servers + * which contain the "public" DTDs cannot be accessed. + * + * @param publicID The public DTD identifier found in an XML document. + * @param location The file or resource name for the appropriate DTD stored + * on the local machine. + */ + public void registerDTD(String publicID, String location) { + handler.registerDTD(publicID, location); + } + + /** + * May be used to specify the "home" directory for this iAS installation. + * The directory specified should typically be + * <code>[install-location]/iplanet/ias6/ias</code>. + * + * @param iasHomeDir The home directory for the user's iAS installation. + */ + public void setIasHomeDir(File iasHomeDir) { + this.iasHomeDir = iasHomeDir; + } + + /** + * Returns a Hashtable which contains a list of EJB class files processed by + * the ejbc utility (both "source" class files as well as stubs and + * skeletons). The key for the Hashtable is a String representing the path + * to the class file (relative to the destination directory). The value for + * the Hashtable is a File object which reference the actual class file. + * + * @return The list of EJB files processed by the ejbc utility. + */ + public Hashtable getEjbFiles() { + return ejbFiles; + } + + /** + * Returns the display-name element read from the standard EJB descriptor. + * + * @return The EJB-JAR display name. + */ + public String getDisplayName() { + return displayName; + } + + /** + * Returns the list of CMP descriptors referenced in the EJB descriptors. + * + * @return An array of CMP descriptors. + */ + public String[] getCmpDescriptors() { + List returnList = new ArrayList(); + + EjbInfo[] ejbs = handler.getEjbs(); + + for (int i = 0; i < ejbs.length; i++) { + List descriptors = (List) ejbs[i].getCmpDescriptors(); + returnList.addAll(descriptors); + } + + return (String[]) returnList.toArray(new String[returnList.size()]); + } + + /** + * Main application method for the iPlanet Application Server ejbc utility. + * If the application is run with no commandline arguments, a usage + * statement is printed for the user. + * + * @param args The commandline arguments passed to the application. + */ + public static void main(String[] args) { + File stdDescriptor; + File iasDescriptor; + File destDirectory = null; + String classpath = null; + SAXParser parser = null; + boolean debug = false; + boolean retainSource = false; + IPlanetEjbc ejbc; + + if ((args.length < MIN_NUM_ARGS) || (args.length > MAX_NUM_ARGS)) { + usage(); + return; + } + + stdDescriptor = new File(args[args.length - 2]); + iasDescriptor = new File(args[args.length - 1]); + + for (int i = 0; i < args.length - 2; i++) { + if (args[i].equals("-classpath")) { + classpath = args[++i]; + } else if (args[i].equals("-d")) { + destDirectory = new File(args[++i]); + } else if (args[i].equals("-debug")) { + debug = true; + } else if (args[i].equals("-keepsource")) { + retainSource = true; + } else { + usage(); + return; + } + } + + /* If the -classpath flag isn't specified, use the system classpath */ + if (classpath == null) { + Properties props = System.getProperties(); + classpath = props.getProperty("java.class.path"); + } + + /* + * If the -d flag isn't specified, use the working directory as the + * destination directory + */ + if (destDirectory == null) { + Properties props = System.getProperties(); + destDirectory = new File(props.getProperty("user.dir")); + } + + /* Construct a SAXParser used to process the descriptors */ + SAXParserFactory parserFactory = SAXParserFactory.newInstance(); + parserFactory.setValidating(true); + try { + parser = parserFactory.newSAXParser(); + } catch (Exception e) { + // SAXException or ParserConfigurationException may be thrown + System.out.println("An exception was generated while trying to "); + System.out.println("create a new SAXParser."); + e.printStackTrace(); + return; + } + + /* Build and populate an instance of the ejbc utility */ + ejbc = new IPlanetEjbc(stdDescriptor, iasDescriptor, destDirectory, + classpath, parser); + ejbc.setDebugOutput(debug); + ejbc.setRetainSource(retainSource); + + /* Execute the ejbc utility -- stubs/skeletons are rebuilt, if needed */ + try { + ejbc.execute(); + } catch (IOException e) { + System.out.println("An IOException has occurred while reading the " + + "XML descriptors (" + e.getMessage() + ")."); + return; + } catch (SAXException e) { + System.out.println("A SAXException has occurred while reading the " + + "XML descriptors (" + e.getMessage() + ")."); + return; + } catch (IPlanetEjbc.EjbcException e) { + System.out.println("An error has occurred while executing the ejbc " + + "utility (" + e.getMessage() + ")."); + return; + } + } + + /** + * Print a usage statement. + */ + private static void usage() { + System.out.println("java org.apache.tools.ant.taskdefs.optional.ejb.IPlanetEjbc \\"); + System.out.println(" [OPTIONS] [EJB 1.1 descriptor] [iAS EJB descriptor]"); + System.out.println(""); + System.out.println("Where OPTIONS are:"); + System.out.println(" -debug -- for additional debugging output"); + System.out.println(" -keepsource -- to retain Java source files generated"); + System.out.println(" -classpath [classpath] -- classpath used for compilation"); + System.out.println(" -d [destination directory] -- directory for compiled classes"); + System.out.println(""); + System.out.println("If a classpath is not specified, the system classpath"); + System.out.println("will be used. If a destination directory is not specified,"); + System.out.println("the current working directory will be used (classes will"); + System.out.println("still be placed in subfolders which correspond to their"); + System.out.println("package name)."); + System.out.println(""); + System.out.println("The EJB home interface, remote interface, and implementation"); + System.out.println("class must be found in the destination directory. In"); + System.out.println("addition, the destination will look for the stubs and skeletons"); + System.out.println("in the destination directory to ensure they are up to date."); + } + + /** + * Compiles the stub and skeletons for the specified EJBs, if they need to + * be updated. + * + * @throws EjbcException If the ejbc utility cannot be correctly configured + * or if one or more of the EJB "source" classes + * cannot be found in the destination directory + * @throws IOException If the parser encounters a problem reading the XML + * file + * @throws SAXException If the parser encounters a problem processing the + * XML descriptor (it may wrap another exception) + */ + public void execute() throws EjbcException, IOException, SAXException { + + checkConfiguration(); // Throws EjbcException if unsuccessful + + EjbInfo[] ejbs = getEjbs(); // Returns list of EJBs for processing + + for (int i = 0; i < ejbs.length; i++) { + log("EJBInfo..."); + log(ejbs[i].toString()); + } + + for (int i = 0; i < ejbs.length; i++) { + EjbInfo ejb = ejbs[i]; + + ejb.checkConfiguration(destDirectory); // Throws EjbcException + + if (ejb.mustBeRecompiled(destDirectory)) { + log(ejb.getName() + " must be recompiled using ejbc."); + + String[] arguments = buildArgumentList(ejb); + callEjbc(arguments); + + } else { + log(ejb.getName() + " is up to date."); + } + } + } + + /** + * Executes the iPlanet Application Server ejbc command-line utility. + * + * @param arguments Command line arguments to be passed to the ejbc utility. + */ + private void callEjbc(String[] arguments) { + + /* Concatenate all of the command line arguments into a single String */ + StringBuffer args = new StringBuffer(); + for (int i = 0; i < arguments.length; i++) { + args.append(arguments[i]).append(" "); + } + + /* If an iAS home directory is specified, prepend it to the commmand */ + String command; + if (iasHomeDir == null) { + command = ""; + } else { + command = iasHomeDir.toString() + File.separator + "bin" + + File.separator; + } + command += "ejbc "; + + log(command + args); + + /* + * Use the Runtime object to execute an external command. Use the + * RedirectOutput inner class to direct the standard and error output + * from the command to the JRE's standard output + */ + try { + Process p = Runtime.getRuntime().exec(command + args); + RedirectOutput output = new RedirectOutput(p.getInputStream()); + RedirectOutput error = new RedirectOutput(p.getErrorStream()); + output.start(); + error.start(); + p.waitFor(); + p.destroy(); + } catch (IOException e) { + log("An IOException has occurred while trying to execute ejbc."); + e.printStackTrace(); + } catch (InterruptedException e) { + // Do nothing + } + } + + /** + * Verifies that the user selections are valid. + * + * @throws EjbcException If the user selections are invalid. + */ + protected void checkConfiguration() throws EjbcException { + + String msg = ""; + + if (stdDescriptor == null) { + msg += "A standard XML descriptor file must be specified. "; + } + if (iasDescriptor == null) { + msg += "An iAS-specific XML descriptor file must be specified. "; + } + if (classpath == null) { + msg += "A classpath must be specified. "; + } + if (parser == null) { + msg += "An XML parser must be specified. "; + } + + if (destDirectory == null) { + msg += "A destination directory must be specified. "; + } else if (!destDirectory.exists()) { + msg += "The destination directory specified does not exist. "; + } else if (!destDirectory.isDirectory()) { + msg += "The destination specified is not a directory. "; + } + + if (msg.length() > 0) { + throw new EjbcException(msg); + } + } + + /** + * Parses the EJB descriptors and returns a list of EJBs which may need to + * be compiled. + * + * @return An array of objects which describe the EJBs to be + * processed. + * @throws IOException If the parser encounters a problem reading the XML + * files + * @throws SAXException If the parser encounters a problem processing the + * XML descriptor (it may wrap another exception) + */ + private EjbInfo[] getEjbs() throws IOException, SAXException { + EjbInfo[] ejbs = null; + + /* + * The EJB information is gathered from the standard XML EJB descriptor + * and the iAS-specific XML EJB descriptor using a SAX parser. + */ + + parser.parse(stdDescriptor, handler); + parser.parse(iasDescriptor, handler); + ejbs = handler.getEjbs(); + + return ejbs; + } + + /** + * Based on this object's instance variables as well as the EJB to be + * processed, the correct flags and parameters are set for the ejbc + * command-line utility. + * @param ejb The EJB for which stubs and skeletons will be compiled. + * @return An array of Strings which are the command-line parameters for + * for the ejbc utility. + */ + private String[] buildArgumentList(EjbInfo ejb) { + + List arguments = new ArrayList(); + + /* OPTIONAL COMMAND LINE PARAMETERS */ + + if (debugOutput) { + arguments.add("-debug"); + } + + /* No beantype flag is needed for an entity bean */ + if (ejb.getBeantype().equals(STATELESS_SESSION)) { + arguments.add("-sl"); + } else if (ejb.getBeantype().equals(STATEFUL_SESSION)) { + arguments.add("-sf"); + } + + if (ejb.getIiop()) { + arguments.add("-iiop"); + } + + if (ejb.getCmp()) { + arguments.add("-cmp"); + } + + if (retainSource) { + arguments.add("-gs"); + } + + if (ejb.getHasession()) { + arguments.add("-fo"); + } + + /* REQUIRED COMMAND LINE PARAMETERS */ + + arguments.add("-classpath"); + arguments.add(classpath); + + arguments.add("-d"); + arguments.add(destDirectory.toString()); + + arguments.add(ejb.getHome().getQualifiedClassName()); + arguments.add(ejb.getRemote().getQualifiedClassName()); + arguments.add(ejb.getImplementation().getQualifiedClassName()); + + /* Convert the List into an Array and return it */ + return (String[]) arguments.toArray(new String[arguments.size()]); + } + + /** + * Convenience method used to print messages to the user if debugging + * messages are enabled. + * + * @param msg The String to print to standard output. + */ + private void log(String msg) { + if (debugOutput) { + System.out.println(msg); + } + } + + + /* Inner classes follow */ + + + /** + * This inner class is used to signal any problems during the execution of + * the ejbc compiler. + * + */ + public class EjbcException extends Exception { + + /** + * Constructs an exception with the given descriptive message. + * + * @param msg Description of the exception which has occurred. + */ + public EjbcException(String msg) { + super(msg); + } + } // End of EjbcException inner class + + + /** + * This inner class is an XML document handler that can be used to parse EJB + * descriptors (both the standard EJB descriptor as well as the iAS-specific + * descriptor that stores additional values for iAS). Once the descriptors + * have been processed, the list of EJBs found can be obtained by calling + * the <code>getEjbs()</code> method. + * + * @see IPlanetEjbc.EjbInfo + */ + private class EjbcHandler extends HandlerBase { + /** EJB 1.1 ID */ + private static final String PUBLICID_EJB11 = + "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN"; + /** IPlanet ID */ + private static final String PUBLICID_IPLANET_EJB_60 = + "-//Sun Microsystems, Inc.//DTD iAS Enterprise JavaBeans 1.0//EN"; + /** EJB 1.1 location */ + private static final String DEFAULT_IAS60_EJB11_DTD_LOCATION = + "ejb-jar_1_1.dtd"; + /** IAS60 location */ + private static final String DEFAULT_IAS60_DTD_LOCATION = + "IASEjb_jar_1_0.dtd"; + + /* + * Two Maps are used to track local DTDs that will be used in case the + * remote copies of these DTDs cannot be accessed. The key for the Map + * is the DTDs public ID and the value is the local location for the DTD + */ + private Map resourceDtds = new HashMap(); + private Map fileDtds = new HashMap(); + + private Map ejbs = new HashMap(); // List of EJBs found in XML + private EjbInfo currentEjb; // One item within the Map + private boolean iasDescriptor = false; // Is doc iAS or EJB descriptor + + private String currentLoc = ""; // Tracks current element + private String currentText; // Tracks current text data + private String ejbType; // "session" or "entity" + + /** + * Constructs a new instance of the handler and registers local copies + * of the standard EJB 1.1 descriptor DTD as well as iAS's EJB + * descriptor DTD. + */ + public EjbcHandler() { + registerDTD(PUBLICID_EJB11, DEFAULT_IAS60_EJB11_DTD_LOCATION); + registerDTD(PUBLICID_IPLANET_EJB_60, DEFAULT_IAS60_DTD_LOCATION); + } + + /** + * Returns the list of EJB objects found during the processing of the + * standard EJB 1.1 descriptor and iAS-specific EJB descriptor. + * + * @return An array of EJBs which were found during the descriptor + * parsing. + */ + public EjbInfo[] getEjbs() { + return (EjbInfo[]) ejbs.values().toArray(new EjbInfo[ejbs.size()]); + } + + /** + * Returns the value of the display-name element found in the standard + * EJB 1.1 descriptor. + * + * @return String display-name value. + */ + public String getDisplayName() { + return displayName; + } + + /** + * Registers a local DTD that will be used when parsing an EJB + * descriptor. When the DTD's public identifier is found in an XML + * document, the parser will reference the local DTD rather than the + * remote DTD. This enables XML documents to be processed even when the + * public DTD isn't available. + * + * @param publicID The DTD's public identifier. + * @param location The location of the local DTD copy -- the location + * may either be a resource found on the classpath or a + * local file. + */ + public void registerDTD(String publicID, String location) { + log("Registering: " + location); + if ((publicID == null) || (location == null)) { + return; + } + + if (ClassLoader.getSystemResource(location) != null) { + log("Found resource: " + location); + resourceDtds.put(publicID, location); + } else { + File dtdFile = new File(location); + if (dtdFile.exists() && dtdFile.isFile()) { + log("Found file: " + location); + fileDtds.put(publicID, location); + } + } + } + + /** + * Resolves an external entity found during XML processing. If a public + * ID is found that has been registered with the handler, an <code> + * InputSource</code> will be returned which refers to the local copy. + * If the public ID hasn't been registered or if an error occurs, the + * superclass implementation is used. + * + * @param publicId The DTD's public identifier. + * @param systemId The location of the DTD, as found in the XML document. + */ + public InputSource resolveEntity(String publicId, String systemId) + throws SAXException { + InputStream inputStream = null; + + + try { + + /* Search the resource Map and (if not found) file Map */ + + String location = (String) resourceDtds.get(publicId); + if (location != null) { + inputStream + = ClassLoader.getSystemResource(location).openStream(); + } else { + location = (String) fileDtds.get(publicId); + if (location != null) { + inputStream = new FileInputStream(location); + } + } + } catch (IOException e) { + return super.resolveEntity(publicId, systemId); + } + + if (inputStream == null) { + return super.resolveEntity(publicId, systemId); + } else { + return new InputSource(inputStream); + } + } + + /** + * Receive notification that the start of an XML element has been found. + * + * @param name String name of the element found. + * @param atts AttributeList of the attributes included with the element + * (if any). + * @throws SAXException If the parser cannot process the document. + */ + public void startElement(String name, AttributeList atts) + throws SAXException { + + /* + * I need to "push" the element onto the String (currentLoc) which + * always represents the current location in the XML document. + */ + currentLoc += "\\" + name; + + /* A new element has started, so reset the text being captured */ + currentText = ""; + + if (currentLoc.equals("\\ejb-jar")) { + iasDescriptor = false; + } else if (currentLoc.equals("\\ias-ejb-jar")) { + iasDescriptor = true; + } + + if ((name.equals("session")) || (name.equals("entity"))) { + ejbType = name; + } + } + + /** + * Receive notification that character data has been found in the XML + * document + * + * @param ch Array of characters which have been found in the document. + * @param start Starting index of the data found in the document. + * @param len The number of characters found in the document. + * @throws SAXException If the parser cannot process the document. + */ + public void characters(char[] ch, int start, int len) + throws SAXException { + + currentText += new String(ch).substring(start, start + len); + } + + /** + * Receive notification that the end of an XML element has been found. + * + * @param name String name of the element. + * @throws SAXException If the parser cannot process the document. + */ + public void endElement(String name) throws SAXException { + + /* + * If this is a standard EJB 1.1 descriptor, we are looking for one + * set of data, while if this is an iAS-specific descriptor, we're + * looking for different set of data. Hand the processing off to + * the appropriate method. + */ + if (iasDescriptor) { + iasCharacters(currentText); + } else { + stdCharacters(currentText); + } + + /* + * I need to "pop" the element off the String (currentLoc) which + * always represents my current location in the XML document. + */ + + int nameLength = name.length() + 1; // Add one for the "\" + int locLength = currentLoc.length(); + + currentLoc = currentLoc.substring(0, locLength - nameLength); + } + + /** + * Receive notification that character data has been found in a standard + * EJB 1.1 descriptor. We're interested in retrieving the home + * interface, remote interface, implementation class, the type of bean, + * and if the bean uses CMP. + * + * @param value String data found in the XML document. + */ + private void stdCharacters(String value) { + + if (currentLoc.equals("\\ejb-jar\\display-name")) { + displayName = value; + return; + } + + String base = "\\ejb-jar\\enterprise-beans\\" + ejbType; + + if (currentLoc.equals(base + "\\ejb-name")) { + currentEjb = (EjbInfo) ejbs.get(value); + if (currentEjb == null) { + currentEjb = new EjbInfo(value); + ejbs.put(value, currentEjb); + } + } else if (currentLoc.equals(base + "\\home")) { + currentEjb.setHome(value); + } else if (currentLoc.equals(base + "\\remote")) { + currentEjb.setRemote(value); + } else if (currentLoc.equals(base + "\\ejb-class")) { + currentEjb.setImplementation(value); + } else if (currentLoc.equals(base + "\\prim-key-class")) { + currentEjb.setPrimaryKey(value); + } else if (currentLoc.equals(base + "\\session-type")) { + currentEjb.setBeantype(value); + } else if (currentLoc.equals(base + "\\persistence-type")) { + currentEjb.setCmp(value); + } + } + + /** + * Receive notification that character data has been found in an + * iAS-specific descriptor. We're interested in retrieving data + * indicating whether the bean must support RMI/IIOP access, whether + * the bean must provide highly available stubs and skeletons (in the + * case of stateful session beans), and if this bean uses additional + * CMP XML descriptors (in the case of entity beans with CMP). + * + * @param value String data found in the XML document. + */ + private void iasCharacters(String value) { + String base = "\\ias-ejb-jar\\enterprise-beans\\" + ejbType; + + if (currentLoc.equals(base + "\\ejb-name")) { + currentEjb = (EjbInfo) ejbs.get(value); + if (currentEjb == null) { + currentEjb = new EjbInfo(value); + ejbs.put(value, currentEjb); + } + } else if (currentLoc.equals(base + "\\iiop")) { + currentEjb.setIiop(value); + } else if (currentLoc.equals(base + "\\failover-required")) { + currentEjb.setHasession(value); + } else if (currentLoc.equals(base + "\\persistence-manager" + + "\\properties-file-location")) { + currentEjb.addCmpDescriptor(value); + } + } + } // End of EjbcHandler inner class + + + /** + * This inner class represents an EJB that will be compiled using ejbc. + * + */ + private class EjbInfo { + private String name; // EJB's display name + private Classname home; // EJB's home interface name + private Classname remote; // EJB's remote interface name + private Classname implementation; // EJB's implementation class + private Classname primaryKey; // EJB's primary key class + private String beantype = "entity"; // or "stateful" or "stateless" + private boolean cmp = false; // Does this EJB support CMP? + private boolean iiop = false; // Does this EJB support IIOP? + private boolean hasession = false; // Does this EJB require failover? + private List cmpDescriptors = new ArrayList(); // CMP descriptor list + + /** + * Construct a new EJBInfo object with the given name. + * + * @param name The display name for the EJB. + */ + public EjbInfo(String name) { + this.name = name; + } + + /** + * Returns the display name of the EJB. If a display name has not been + * set, it returns the EJB implementation classname (if the + * implementation class is not set, it returns "[unnamed]"). + * + * @return The display name for the EJB. + */ + public String getName() { + if (name == null) { + if (implementation == null) { + return "[unnamed]"; + } else { + return implementation.getClassName(); + } + } + return name; + } + + /* + * Below are getter's and setter's for each of the instance variables. + * Note that (in addition to supporting setters with the same type as + * the instance variable) a setter is provided with takes a String + * argument -- this are provided so the XML document handler can set + * the EJB values using the Strings it parses. + */ + + public void setHome(String home) { + setHome(new Classname(home)); + } + + public void setHome(Classname home) { + this.home = home; + } + + public Classname getHome() { + return home; + } + + public void setRemote(String remote) { + setRemote(new Classname(remote)); + } + + public void setRemote(Classname remote) { + this.remote = remote; + } + + public Classname getRemote() { + return remote; + } + + public void setImplementation(String implementation) { + setImplementation(new Classname(implementation)); + } + + public void setImplementation(Classname implementation) { + this.implementation = implementation; + } + + public Classname getImplementation() { + return implementation; + } + + public void setPrimaryKey(String primaryKey) { + setPrimaryKey(new Classname(primaryKey)); + } + + public void setPrimaryKey(Classname primaryKey) { + this.primaryKey = primaryKey; + } + + public Classname getPrimaryKey() { + return primaryKey; + } + + public void setBeantype(String beantype) { + this.beantype = beantype.toLowerCase(); + } + + public String getBeantype() { + return beantype; + } + + public void setCmp(boolean cmp) { + this.cmp = cmp; + } + + public void setCmp(String cmp) { + setCmp(cmp.equals("Container")); + } + + public boolean getCmp() { + return cmp; + } + + public void setIiop(boolean iiop) { + this.iiop = iiop; + } + + public void setIiop(String iiop) { + setIiop(iiop.equals("true")); + } + + public boolean getIiop() { + return iiop; + } + + public void setHasession(boolean hasession) { + this.hasession = hasession; + } + + public void setHasession(String hasession) { + setHasession(hasession.equals("true")); + } + + public boolean getHasession() { + return hasession; + } + + public void addCmpDescriptor(String descriptor) { + cmpDescriptors.add(descriptor); + } + + public List getCmpDescriptors() { + return cmpDescriptors; + } + + /** + * Verifies that the EJB is valid--if it is invalid, an exception is + * thrown + * + * + * @param buildDir The directory where the EJB remote interface, home + * interface, and implementation class must be found. + * @throws EjbcException If the EJB is invalid. + */ + private void checkConfiguration(File buildDir) throws EjbcException { + + /* Check that the specified instance variables are valid */ + if (home == null) { + throw new EjbcException("A home interface was not found " + + "for the " + name + " EJB."); + } + if (remote == null) { + throw new EjbcException("A remote interface was not found " + + "for the " + name + " EJB."); + } + if (implementation == null) { + throw new EjbcException("An EJB implementation class was not " + + "found for the " + name + " EJB."); + } + + if ((!beantype.equals(ENTITY_BEAN)) + && (!beantype.equals(STATELESS_SESSION)) + && (!beantype.equals(STATEFUL_SESSION))) { + throw new EjbcException("The beantype found (" + beantype + ") " + + "isn't valid in the " + name + " EJB."); + } + + if (cmp && (!beantype.equals(ENTITY_BEAN))) { + System.out.println("CMP stubs and skeletons may not be generated" + + " for a Session Bean -- the \"cmp\" attribute will be" + + " ignoredfor the " + name + " EJB."); + } + + if (hasession && (!beantype.equals(STATEFUL_SESSION))) { + System.out.println("Highly available stubs and skeletons may " + + "only be generated for a Stateful Session Bean -- the " + + "\"hasession\" attribute will be ignored for the " + + name + " EJB."); + } + + /* Check that the EJB "source" classes all exist */ + if (!remote.getClassFile(buildDir).exists()) { + throw new EjbcException("The remote interface " + + remote.getQualifiedClassName() + " could not be " + + "found."); + } + if (!home.getClassFile(buildDir).exists()) { + throw new EjbcException("The home interface " + + home.getQualifiedClassName() + " could not be " + + "found."); + } + if (!implementation.getClassFile(buildDir).exists()) { + throw new EjbcException("The EJB implementation class " + + implementation.getQualifiedClassName() + " could " + + "not be found."); + } + } + + /** + * Determines if the ejbc utility needs to be run or not. If the stubs + * and skeletons can all be found in the destination directory AND all + * of their timestamps are more recent than the EJB source classes + * (home, remote, and implementation classes), the method returns + * <code>false</code>. Otherwise, the method returns <code>true</code>. + * + * @param destDir The directory where the EJB source classes, stubs and + * skeletons are located. + * @return A boolean indicating whether or not the ejbc utility needs to + * be run to bring the stubs and skeletons up to date. + */ + public boolean mustBeRecompiled(File destDir) { + + long sourceModified = sourceClassesModified(destDir); + + long destModified = destClassesModified(destDir); + + return (destModified < sourceModified); + } + + /** + * Examines each of the EJB source classes (home, remote, and + * implementation) and returns the modification timestamp for the + * "oldest" class. + * + * @param classpath The classpath to be used to find the source EJB + * classes. If <code>null</code>, the system classpath + * is used. + * @return The modification timestamp for the "oldest" EJB source class. + * @throws BuildException If one of the EJB source classes cannot be + * found on the classpath. + */ + private long sourceClassesModified(File buildDir) { + long latestModified; // The timestamp of the "newest" class + long modified; // Timestamp for a given class + File remoteFile; // File for the remote interface class + File homeFile; // File for the home interface class + File implFile; // File for the EJB implementation class + File pkFile; // File for the EJB primary key class + + /* Check the timestamp on the remote interface */ + remoteFile = remote.getClassFile(buildDir); + modified = remoteFile.lastModified(); + if (modified == -1) { + System.out.println("The class " + + remote.getQualifiedClassName() + " couldn't " + + "be found on the classpath"); + return -1; + } + latestModified = modified; + + /* Check the timestamp on the home interface */ + homeFile = home.getClassFile(buildDir); + modified = homeFile.lastModified(); + if (modified == -1) { + System.out.println("The class " + + home.getQualifiedClassName() + " couldn't be " + + "found on the classpath"); + return -1; + } + latestModified = Math.max(latestModified, modified); + + /* Check the timestamp of the primary key class */ + if (primaryKey != null) { + pkFile = primaryKey.getClassFile(buildDir); + modified = pkFile.lastModified(); + if (modified == -1) { + System.out.println("The class " + + primaryKey.getQualifiedClassName() + "couldn't be " + + "found on the classpath"); + return -1; + } + latestModified = Math.max(latestModified, modified); + } else { + pkFile = null; + } + + /* Check the timestamp on the EJB implementation class. + * + * Note that if ONLY the implementation class has changed, it's not + * necessary to rebuild the EJB stubs and skeletons. For this + * reason, we ensure the file exists (using lastModified above), but + * we DON'T compare it's timestamp with the timestamps of the home + * and remote interfaces (because it's irrelevant in determining if + * ejbc must be run) + */ + implFile = implementation.getClassFile(buildDir); + modified = implFile.lastModified(); + if (modified == -1) { + System.out.println("The class " + + implementation.getQualifiedClassName() + + " couldn't be found on the classpath"); + return -1; + } + + String pathToFile = remote.getQualifiedClassName(); + pathToFile = pathToFile.replace('.', File.separatorChar) + ".class"; + ejbFiles.put(pathToFile, remoteFile); + + pathToFile = home.getQualifiedClassName(); + pathToFile = pathToFile.replace('.', File.separatorChar) + ".class"; + ejbFiles.put(pathToFile, homeFile); + + pathToFile = implementation.getQualifiedClassName(); + pathToFile = pathToFile.replace('.', File.separatorChar) + ".class"; + ejbFiles.put(pathToFile, implFile); + + if (pkFile != null) { + pathToFile = primaryKey.getQualifiedClassName(); + pathToFile = pathToFile.replace('.', File.separatorChar) + ".class"; + ejbFiles.put(pathToFile, pkFile); + } + + return latestModified; + } + + /** + * Examines each of the EJB stubs and skeletons in the destination + * directory and returns the modification timestamp for the "oldest" + * class. If one of the stubs or skeletons cannot be found, <code>-1 + * </code> is returned. + * + * @param dest The directory in which the EJB stubs and skeletons are + * stored. + * @return The modification timestamp for the "oldest" EJB stub or + * skeleton. If one of the classes cannot be found, <code>-1 + * </code> is returned. + * @throws BuildException If the canonical path of the destination + * directory cannot be found. + */ + private long destClassesModified(File destDir) { + String[] classnames = classesToGenerate(); // List of all stubs & skels + long destClassesModified = new Date().getTime(); // Earliest mod time + boolean allClassesFound = true; // Has each been found? + + /* + * Loop through each stub/skeleton class that must be generated, and + * determine (if all exist) which file has the most recent timestamp + */ + for (int i = 0; i < classnames.length; i++) { + + String pathToClass = + classnames[i].replace('.', File.separatorChar) + ".class"; + File classFile = new File(destDir, pathToClass); + + /* + * Add each stub/skeleton class to the list of EJB files. Note + * that each class is added even if it doesn't exist now. + */ + ejbFiles.put(pathToClass, classFile); + + allClassesFound = allClassesFound && classFile.exists(); + + if (allClassesFound) { + long fileMod = classFile.lastModified(); + + /* Keep track of the oldest modification timestamp */ + destClassesModified = Math.min(destClassesModified, fileMod); + } + } + + return (allClassesFound) ? destClassesModified : -1; + } + + /** + * Builds an array of class names which represent the stubs and + * skeletons which need to be generated for a given EJB. The class + * names are fully qualified. Nine classes are generated for all EJBs + * while an additional six classes are generated for beans requiring + * RMI/IIOP access. + * + * @return An array of Strings representing the fully-qualified class + * names for the stubs and skeletons to be generated. + */ + private String[] classesToGenerate() { + String[] classnames = (iiop) + ? new String[NUM_CLASSES_WITH_IIOP] + : new String[NUM_CLASSES_WITHOUT_IIOP]; + + final String remotePkg = remote.getPackageName() + "."; + final String remoteClass = remote.getClassName(); + final String homePkg = home.getPackageName() + "."; + final String homeClass = home.getClassName(); + final String implPkg = implementation.getPackageName() + "."; + final String implFullClass = implementation.getQualifiedWithUnderscores(); + int index = 0; + + classnames[index++] = implPkg + "ejb_fac_" + implFullClass; + classnames[index++] = implPkg + "ejb_home_" + implFullClass; + classnames[index++] = implPkg + "ejb_skel_" + implFullClass; + classnames[index++] = remotePkg + "ejb_kcp_skel_" + remoteClass; + classnames[index++] = homePkg + "ejb_kcp_skel_" + homeClass; + classnames[index++] = remotePkg + "ejb_kcp_stub_" + remoteClass; + classnames[index++] = homePkg + "ejb_kcp_stub_" + homeClass; + classnames[index++] = remotePkg + "ejb_stub_" + remoteClass; + classnames[index++] = homePkg + "ejb_stub_" + homeClass; + + if (!iiop) { + return classnames; + } + + classnames[index++] = "org.omg.stub." + remotePkg + "_" + + remoteClass + "_Stub"; + classnames[index++] = "org.omg.stub." + homePkg + "_" + + homeClass + "_Stub"; + classnames[index++] = "org.omg.stub." + remotePkg + + "_ejb_RmiCorbaBridge_" + + remoteClass + "_Tie"; + classnames[index++] = "org.omg.stub." + homePkg + + "_ejb_RmiCorbaBridge_" + + homeClass + "_Tie"; + + classnames[index++] = remotePkg + "ejb_RmiCorbaBridge_" + + remoteClass; + classnames[index++] = homePkg + "ejb_RmiCorbaBridge_" + homeClass; + + return classnames; + } + + /** + * Convenience method which creates a String representation of all the + * instance variables of an EjbInfo object. + * + * @return A String representing the EjbInfo instance. + */ + public String toString() { + String s = "EJB name: " + name + + "\n\r home: " + home + + "\n\r remote: " + remote + + "\n\r impl: " + implementation + + "\n\r primaryKey: " + primaryKey + + "\n\r beantype: " + beantype + + "\n\r cmp: " + cmp + + "\n\r iiop: " + iiop + + "\n\r hasession: " + hasession; + + Iterator i = cmpDescriptors.iterator(); + while (i.hasNext()) { + s += "\n\r CMP Descriptor: " + i.next(); + } + + return s; + } + + } // End of EjbInfo inner class + + /** + * Convenience class used to represent the fully qualified name of a Java + * class. It provides an easy way to retrieve components of the class name + * in a format that is convenient for building iAS stubs and skeletons. + * + */ + private static class Classname { + private String qualifiedName; // Fully qualified name of the Java class + private String packageName; // Name of the package for this class + private String className; // Name of the class without the package + + /** + * This constructor builds an object which represents the name of a Java + * class. + * + * @param qualifiedName String representing the fully qualified class + * name of the Java class. + */ + public Classname(String qualifiedName) { + if (qualifiedName == null) { + return; + } + + this.qualifiedName = qualifiedName; + + int index = qualifiedName.lastIndexOf('.'); + if (index == -1) { + className = qualifiedName; + packageName = ""; + } else { + packageName = qualifiedName.substring(0, index); + className = qualifiedName.substring(index + 1); + } + } + + /** + * Gets the fully qualified name of the Java class. + * + * @return String representing the fully qualified class name. + */ + public String getQualifiedClassName() { + return qualifiedName; + } + + /** + * Gets the package name for the Java class. + * + * @return String representing the package name for the class. + */ + public String getPackageName() { + return packageName; + } + + /** + * Gets the Java class name without the package structure. + * + * @return String representing the name for the class. + */ + public String getClassName() { + return className; + } + + /** + * Gets the fully qualified name of the Java class with underscores + * separating the components of the class name rather than periods. + * This format is used in naming some of the stub and skeleton classes + * for the iPlanet Application Server. + * + * @return String representing the fully qualified class name using + * underscores instead of periods. + */ + public String getQualifiedWithUnderscores() { + return qualifiedName.replace('.', '_'); + } + + /** + * Returns a File which references the class relative to the specified + * directory. Note that the class file may or may not exist. + * + * @param directory A File referencing the base directory containing + * class files. + * @return File referencing this class. + */ + public File getClassFile(File directory) { + String pathToFile = qualifiedName.replace('.', File.separatorChar) + + ".class"; + return new File(directory, pathToFile); + } + + /** + * String representation of this class name. It returns the fully + * qualified class name. + * + * @return String representing the fully qualified class name. + */ + public String toString() { + return getQualifiedClassName(); + } + } // End of Classname inner class + + + /** + * Thread class used to redirect output from an <code>InputStream</code> to + * the JRE standard output. This class may be used to redirect output from + * an external process to the standard output. + * + */ + private static class RedirectOutput extends Thread { + + private InputStream stream; // Stream to read and redirect to standard output + + /** + * Constructs a new instance that will redirect output from the + * specified stream to the standard output. + * + * @param stream InputStream which will be read and redirected to the + * standard output. + */ + public RedirectOutput(InputStream stream) { + this.stream = stream; + } + + /** + * Reads text from the input stream and redirects it to standard output + * using a separate thread. + */ + public void run() { + BufferedReader reader = new BufferedReader( + new InputStreamReader(stream)); + String text; + try { + while ((text = reader.readLine()) != null) { + System.out.println(text); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + reader.close(); + } catch (IOException e) { + // Do nothing + } + } + } + } // End of RedirectOutput inner class + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbcTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbcTask.java new file mode 100644 index 00000000..ee5dc854 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbcTask.java @@ -0,0 +1,321 @@ +/* + * 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.tools.ant.taskdefs.optional.ejb; + +import java.io.File; +import java.io.IOException; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.Path; +import org.xml.sax.SAXException; + +/** + * Compiles EJB stubs and skeletons for the iPlanet Application Server. + * The EJBs to be processed are specified by the EJB 1.1 standard XML + * descriptor, and additional attributes are obtained from the iPlanet Application + * Server-specific XML descriptor. Since the XML descriptors can include + * multiple EJBs, this is a convenient way of specifying many EJBs in a single + * Ant task. The following attributes are allowed: + * <ul> + * <li><i>ejbdescriptor</i> -- Standard EJB 1.1 XML descriptor (typically + * titled "ejb-jar.xml"). This attribute is + * required. + * <li><i>iasdescriptor</i> -- EJB XML descriptor for iPlanet Application + * Server (typically titled "ias-ejb-jar.xml). + * This attribute is required. + * <li><i>dest</i> -- The is the base directory where the RMI stubs and + * skeletons are written. In addition, the class files + * for each bean (home interface, remote interface, and + * EJB implementation) must be found in this directory. + * This attribute is required. + * <li><i>classpath</i> -- The classpath used when generating EJB stubs and + * skeletons. This is an optional attribute (if + * omitted, the classpath specified when Ant was + * started will be used). Nested "classpath" + * elements may also be used. + * <li><i>keepgenerated</i> -- Indicates whether or not the Java source + * files which are generated by ejbc will be + * saved or automatically deleted. If "yes", + * the source files will be retained. This is + * an optional attribute (if omitted, it + * defaults to "no"). + * <li><i>debug</i> -- Indicates whether or not the ejbc utility should + * log additional debugging statements to the standard + * output. If "yes", the additional debugging statements + * will be generated (if omitted, it defaults to "no"). + * <li><i>iashome</i> -- May be used to specify the "home" directory for + * this iPlanet Application Server installation. This + * is used to find the ejbc utility if it isn't + * included in the user's system path. This is an + * optional attribute (if specified, it should refer + * to the <code>[install-location]/iplanet/ias6/ias + * </code> directory). If omitted, the ejbc utility + * must be on the user's system path. + * </ul> + * <p> + * For each EJB specified, this task will locate the three classes that comprise + * the EJB. If these class files cannot be located in the <code>dest</code> + * directory, the task will fail. The task will also attempt to locate the EJB + * stubs and skeletons in this directory. If found, the timestamps on the + * stubs and skeletons will be checked to ensure they are up to date. Only if + * these files cannot be found or if they are out of date will ejbc be called + * to generate new stubs and skeletons. + * + * @see IPlanetEjbc + * + * @ant.task name="iplanet-ejbc" category="ejb" + */ +public class IPlanetEjbcTask extends Task { + + /* Attributes set by the Ant build file */ + private File ejbdescriptor; + private File iasdescriptor; + private File dest; + private Path classpath; + private boolean keepgenerated = false; + private boolean debug = false; + private File iashome; + + /** + * Sets the location of the standard XML EJB descriptor. Typically, this + * file is named "ejb-jar.xml". + * + * @param ejbdescriptor The name and location of the EJB descriptor. + */ + public void setEjbdescriptor(File ejbdescriptor) { + this.ejbdescriptor = ejbdescriptor; + } + + /** + * Sets the location of the iAS-specific XML EJB descriptor. Typically, + * this file is named "ias-ejb-jar.xml". + * + * @param iasdescriptor The name and location of the iAS-specific EJB + * descriptor. + */ + public void setIasdescriptor (File iasdescriptor) { + this.iasdescriptor = iasdescriptor; + } + + /** + * Sets the destination directory where the EJB source classes must exist + * and where the stubs and skeletons will be written. The destination + * directory must exist before this task is executed. + * + * @param dest The directory where the compiled classes will be written. + */ + public void setDest(File dest) { + this.dest = dest; + } + + /** + * Sets the classpath to be used when compiling the EJB stubs and skeletons. + * + * @param classpath The classpath to be used. + */ + public void setClasspath(Path classpath) { + if (this.classpath == null) { + this.classpath = classpath; + } else { + this.classpath.append(classpath); + } + } + + /** + * Adds to the classpath used when compiling the EJB stubs and skeletons. + * @return the class path. + */ + public Path createClasspath() { + if (classpath == null) { + classpath = new Path(getProject()); + } + return classpath.createPath(); + } + + /** + * If true, the Java source files which are generated by ejbc will be saved . + * + * @param keepgenerated A boolean indicating if the Java source files for + * the stubs and skeletons should be retained. + */ + public void setKeepgenerated(boolean keepgenerated) { + this.keepgenerated = keepgenerated; + } + + /** + * If true, debugging output will be generated when ejbc is + * executed. + * + * @param debug A boolean indicating if debugging output should be generated + */ + public void setDebug(boolean debug) { + this.debug = debug; + } + + /** + * May be used to specify the "home" directory for this iAS installation. + * The directory specified should typically be + * <code>[install-location]/iplanet/ias6/ias</code>. + * + * @param iashome The home directory for the user's iAS installation. + */ + public void setIashome(File iashome) { + this.iashome = iashome; + } + + /** + * Does the work. + * @throws BuildException if there is a problem. + */ + public void execute() throws BuildException { + checkConfiguration(); + + executeEjbc(getParser()); + } + + /** + * Verifies that the user selections are valid. + * + * @throws BuildException If the user selections are invalid. + */ + private void checkConfiguration() throws BuildException { + + if (ejbdescriptor == null) { + String msg = "The standard EJB descriptor must be specified using " + + "the \"ejbdescriptor\" attribute."; + throw new BuildException(msg, getLocation()); + } + if ((!ejbdescriptor.exists()) || (!ejbdescriptor.isFile())) { + String msg = "The standard EJB descriptor (" + ejbdescriptor + + ") was not found or isn't a file."; + throw new BuildException(msg, getLocation()); + } + + if (iasdescriptor == null) { + String msg = "The iAS-speific XML descriptor must be specified using" + + " the \"iasdescriptor\" attribute."; + throw new BuildException(msg, getLocation()); + } + if ((!iasdescriptor.exists()) || (!iasdescriptor.isFile())) { + String msg = "The iAS-specific XML descriptor (" + iasdescriptor + + ") was not found or isn't a file."; + throw new BuildException(msg, getLocation()); + } + + if (dest == null) { + String msg = "The destination directory must be specified using " + + "the \"dest\" attribute."; + throw new BuildException(msg, getLocation()); + } + if ((!dest.exists()) || (!dest.isDirectory())) { + String msg = "The destination directory (" + dest + ") was not " + + "found or isn't a directory."; + throw new BuildException(msg, getLocation()); + } + + if ((iashome != null) && (!iashome.isDirectory())) { + String msg = "If \"iashome\" is specified, it must be a valid " + + "directory (it was set to " + iashome + ")."; + throw new BuildException(msg, getLocation()); + } + } + + /** + * Returns a SAXParser that may be used to process the XML descriptors. + * + * @return Parser which may be used to process the EJB descriptors. + * @throws BuildException If the parser cannot be created or configured. + */ + private SAXParser getParser() throws BuildException { + + SAXParser saxParser = null; + try { + SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); + saxParserFactory.setValidating(true); + saxParser = saxParserFactory.newSAXParser(); + } catch (SAXException e) { + String msg = "Unable to create a SAXParser: " + e.getMessage(); + throw new BuildException(msg, e, getLocation()); + } catch (ParserConfigurationException e) { + String msg = "Unable to create a SAXParser: " + e.getMessage(); + throw new BuildException(msg, e, getLocation()); + } + + return saxParser; + } + + /** + * Executes the EJBc utility using the SAXParser provided. + * + * @param saxParser SAXParser that may be used to process the EJB + * descriptors + * @throws BuildException If there is an error reading or parsing the XML + * descriptors + */ + private void executeEjbc(SAXParser saxParser) throws BuildException { + IPlanetEjbc ejbc = new IPlanetEjbc(ejbdescriptor, + iasdescriptor, + dest, + getClasspath().toString(), + saxParser); + ejbc.setRetainSource(keepgenerated); + ejbc.setDebugOutput(debug); + if (iashome != null) { + ejbc.setIasHomeDir(iashome); + } + + try { + ejbc.execute(); + } catch (IOException e) { + String msg = "An IOException occurred while trying to read the XML " + + "descriptor file: " + e.getMessage(); + throw new BuildException(msg, e, getLocation()); + } catch (SAXException e) { + String msg = "A SAXException occurred while trying to read the XML " + + "descriptor file: " + e.getMessage(); + throw new BuildException(msg, e, getLocation()); + } catch (IPlanetEjbc.EjbcException e) { + String msg = "An exception occurred while trying to run the ejbc " + + "utility: " + e.getMessage(); + throw new BuildException(msg, e, getLocation()); + } + } + + /** + * Returns the CLASSPATH to be used when calling EJBc. If no user CLASSPATH + * is specified, the System classpath is returned instead. + * + * @return Path The classpath to be used for EJBc. + */ + private Path getClasspath() { + Path cp = null; + if (classpath == null) { + cp = (new Path(getProject())).concatSystemClasspath("last"); + } else { + cp = classpath.concatSystemClasspath("ignore"); + } + + return cp; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/InnerClassFilenameFilter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/InnerClassFilenameFilter.java new file mode 100644 index 00000000..e92d9879 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/InnerClassFilenameFilter.java @@ -0,0 +1,54 @@ +/* + * 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.tools.ant.taskdefs.optional.ejb; + +import java.io.File; +import java.io.FilenameFilter; + +/** + * A filename filter for inner class files of a particular class. + */ +public class InnerClassFilenameFilter implements FilenameFilter { + private String baseClassName; + + /** + * Constructor of filter. + * @param baseclass the class to filter inner classes on. + */ + InnerClassFilenameFilter(String baseclass) { + int extidx = baseclass.lastIndexOf(".class"); + if (extidx == -1) { + extidx = baseclass.length() - 1; + } + baseClassName = baseclass.substring(0, extidx); + } + + /** + * Check if the file name passes the filter. + * @param dir not used. + * @param filename the filename to filter on. + * @return true if the filename is an inner class of the base class. + */ + public boolean accept(File dir, String filename) { + if ((filename.lastIndexOf(".") != filename.lastIndexOf(".class")) + || (filename.indexOf(baseClassName + "$") != 0)) { + return false; + } + return true; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JbossDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JbossDeploymentTool.java new file mode 100644 index 00000000..79f4574e --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JbossDeploymentTool.java @@ -0,0 +1,111 @@ +/* + * 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.tools.ant.taskdefs.optional.ejb; + +import java.io.File; +import java.util.Hashtable; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; + +/** + * The deployment tool to add the jboss specific deployment descriptor to the ejb jar file. + * Jboss only requires one additional file jboss.xml and does not require any additional + * compilation. + * + * @version 1.0 + * @see EjbJar#createJboss + */ +public class JbossDeploymentTool extends GenericDeploymentTool { + protected static final String JBOSS_DD = "jboss.xml"; + protected static final String JBOSS_CMP10D = "jaws.xml"; + protected static final String JBOSS_CMP20D = "jbosscmp-jdbc.xml"; + + /** Instance variable that stores the suffix for the jboss jarfile. */ + private String jarSuffix = ".jar"; + + /** + * Setter used to store the suffix for the generated JBoss jar file. + * @param inString the string to use as the suffix. + */ + public void setSuffix(String inString) { + jarSuffix = inString; + } + + /** + * Add any vendor specific files which should be included in the + * EJB Jar. + * @param ejbFiles the hashtable of files to populate. + * @param ddPrefix the prefix to use. + */ + protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) { + File jbossDD = new File(getConfig().descriptorDir, ddPrefix + JBOSS_DD); + if (jbossDD.exists()) { + ejbFiles.put(META_DIR + JBOSS_DD, jbossDD); + } else { + log("Unable to locate jboss deployment descriptor. " + + "It was expected to be in " + jbossDD.getPath(), + Project.MSG_WARN); + return; + } + String descriptorFileName = JBOSS_CMP10D; + if (EjbJar.CMPVersion.CMP2_0.equals(getParent().getCmpversion())) { + descriptorFileName = JBOSS_CMP20D; + } + File jbossCMPD + = new File(getConfig().descriptorDir, ddPrefix + descriptorFileName); + + if (jbossCMPD.exists()) { + ejbFiles.put(META_DIR + descriptorFileName, jbossCMPD); + } else { + log("Unable to locate jboss cmp descriptor. " + + "It was expected to be in " + + jbossCMPD.getPath(), Project.MSG_VERBOSE); + return; + } + } + + /** + * Get the vendor specific name of the Jar that will be output. The modification date + * of this jar will be checked against the dependent bean classes. + */ + File getVendorOutputJarFile(String baseName) { + if (getDestDir() == null && getParent().getDestdir() == null) { + throw new BuildException("DestDir not specified"); + } + if (getDestDir() == null) { + return new File(getParent().getDestdir(), baseName + jarSuffix); + } else { + return new File(getDestDir(), baseName + jarSuffix); + } + } + + /** + * Called to validate that the tool parameters have been configured. + * + * @throws BuildException If the Deployment Tool's configuration isn't + * valid + * @since ant 1.6 + */ + public void validateConfigured() throws BuildException { + } + + private EjbJar getParent() { + return (EjbJar) this.getTask(); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JonasDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JonasDeploymentTool.java new file mode 100644 index 00000000..81fe8059 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JonasDeploymentTool.java @@ -0,0 +1,835 @@ +/* + * 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.tools.ant.taskdefs.optional.ejb; + +import java.io.File; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Hashtable; + +import javax.xml.parsers.SAXParser; + +import org.apache.tools.ant.AntClassLoader; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Java; +import org.apache.tools.ant.types.Path; + +/** + * The deployment tool to add the jonas specific deployment descriptors to the + * ejb JAR file. JONAS only requires one additional file jonas-ejb-jar.xml. + * + * @version 1.0 + * @see EjbJar#createJonas + */ +public class JonasDeploymentTool extends GenericDeploymentTool { + + /** Public Id of the standard deployment descriptor DTD. */ + protected static final String EJB_JAR_1_1_PUBLIC_ID + = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN"; + protected static final String EJB_JAR_2_0_PUBLIC_ID + = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"; + + /** Public Id of the JOnAS-specific deployment descriptor DTD. */ + protected static final String JONAS_EJB_JAR_2_4_PUBLIC_ID + = "-//ObjectWeb//DTD JOnAS 2.4//EN"; + protected static final String JONAS_EJB_JAR_2_5_PUBLIC_ID + = "-//ObjectWeb//DTD JOnAS 2.5//EN"; + + /** RMI ORB. */ + protected static final String RMI_ORB = "RMI"; + + /** JEREMIE ORB. */ + protected static final String JEREMIE_ORB = "JEREMIE"; + + /** DAVID ORB. */ + protected static final String DAVID_ORB = "DAVID"; + + /** + * Name of the standard deployment descriptor DTD (these files are stored in + * the ${JONAS_ROOT}/xml directory). + */ + protected static final String EJB_JAR_1_1_DTD = "ejb-jar_1_1.dtd"; + protected static final String EJB_JAR_2_0_DTD = "ejb-jar_2_0.dtd"; + + /** + * Name of the JOnAS-specific deployment descriptor DTD (these files are + * stored in the ${JONAS_ROOT}/xml directory). + */ + protected static final String JONAS_EJB_JAR_2_4_DTD + = "jonas-ejb-jar_2_4.dtd"; + protected static final String JONAS_EJB_JAR_2_5_DTD + = "jonas-ejb-jar_2_5.dtd"; + + /** Default JOnAS deployment descriptor name. */ + protected static final String JONAS_DD = "jonas-ejb-jar.xml"; + + /** GenIC class name (JOnAS 2.5) */ + protected static final String GENIC_CLASS = + "org.objectweb.jonas_ejb.genic.GenIC"; + + /** Old GenIC class name (JOnAS 2.4.x). */ + protected static final String OLD_GENIC_CLASS_1 = + "org.objectweb.jonas_ejb.tools.GenWholeIC"; + + /** Old GenIC class name. */ + protected static final String OLD_GENIC_CLASS_2 = + "org.objectweb.jonas_ejb.tools.GenIC"; + + /** + * Filename of the standard EJB descriptor (which is passed to this class + * from the parent "ejbjar" task). This file is relative to the directory + * specified by the "srcdir" attribute in the ejbjar task. + */ + private String descriptorName; + + /** + * Filename of the JOnAS-specific EJB descriptor (which is passed to this + * class from the parent "ejbjar" task). This file is relative to the + * directory specified by the "srcdir" attribute in the ejbjar task. + */ + private String jonasDescriptorName; + + /* ------------- */ + /* GenIC options */ + /* ------------- */ + + /** + * Temporary output directory used by GenIC. + */ + private File outputdir; + + /** + * <code>true</code> if the intermediate Java source files generated by + * GenIC must be deleted or not. The default is <code>false</code> + */ + private boolean keepgenerated = false; + + /** + * <code>true</code> if the generated source files must not be compiled via + * the java and rmi compilers. The default is <code>false</code>. + */ + private boolean nocompil = false; + + /** + * <code>true</code> if the XML deployment descriptors must be parsed + * without validation. The default is <code>false</code>. + */ + private boolean novalidation = false; + + /** + * Java compiler to use. The default is the value of + * <code>build.compiler</code> property. + */ + private String javac; + + /** Options to pass to the java compiler. */ + private String javacopts; + + /** Options to pass to the rmi compiler. */ + private String rmicopts; + + /** + * Whether or not the RMI skeleton and stub must be modified to + * implement the implicit propagation of the security context (the + * transactional context is always provided). The default is + * <code>false</code>. + */ + private boolean secpropag = false; + + /** + * <code>true</code> if the GenIC call must be verbose. The default + * is <code>false</code>. + */ + private boolean verbose = false; + + /** Additional args to send to GenIC. */ + private String additionalargs; + + /* ------------- */ + /* other options */ + /* ------------- */ + + /** JOnAS root directory. */ + private File jonasroot; + + /** + * <code>true</code> if the generic JAR file used as input to GenIC must be + * retained. The default is <code>false</code>. + */ + private boolean keepgeneric = false; + + /** Stores the suffix for the JOnAS JAR file. The default is '.jar'. */ + private String suffix = ".jar"; + + /** + * ORB to use (RMI, JEREMIE or DAVID). If omitted, it defaults to the one + * present in classpath. If specified, the corresponding JOnAS JAR is + * automatically added to the classpath. + */ + private String orb; + + /** + * <code>true</code> if GenIC must not be run on the EJB JAR. + * The default is <code>false</code>. + */ + private boolean nogenic = false; + + /* -------------------- */ + /* GenIC options setter */ + /* -------------------- */ + + /** + * Sets the <code>keepgenerated</code> flag. + * + * @param aBoolean <code>true</code> if the flag must be set. + */ + public void setKeepgenerated(boolean aBoolean) { + keepgenerated = aBoolean; + } + + /** + * Sets the additional arguments. + * + * @param aString additional args. + */ + public void setAdditionalargs(String aString) { + additionalargs = aString; + } + + /** + * Sets the <code>nocompil</code> flag. + * + * @param aBoolean <code>true</code> if the flag must be set. + */ + public void setNocompil(boolean aBoolean) { + nocompil = aBoolean; + } + + /** + * Sets the <code>novalidation</code> flag. + * + * @param aBoolean <code>true</code> if the flag must be set. + */ + public void setNovalidation(boolean aBoolean) { + novalidation = aBoolean; + } + + /** + * Sets the java compiler to use. + * + * @param aString the java compiler. + */ + public void setJavac(String aString) { + javac = aString; + } + + /** + * Set the options to pass to the java compiler. + * + * @param aString the options. + */ + public void setJavacopts(String aString) { + javacopts = aString; + } + + /** + * Set the options to pass to the rmi compiler. + * + * @param aString the options. + */ + public void setRmicopts(String aString) { + rmicopts = aString; + } + + /** + * Sets the <code>secpropag</code> flag. + * + * @param aBoolean <code>true</code> if the flag must be set. + */ + public void setSecpropag(boolean aBoolean) { + secpropag = aBoolean; + } + + /** + * Sets the <code>verbose</code> flag. + * + * @param aBoolean <code>true</code> if the flag must be set. + */ + public void setVerbose(boolean aBoolean) { + verbose = aBoolean; + } + + /* -------------------- */ + /* other options setter */ + /* -------------------- */ + + /** + * Set the JOnAS root directory. + * + * @param aFile the JOnAS root directory. + */ + public void setJonasroot(File aFile) { + jonasroot = aFile; + } + + /** + * Sets the <code>keepgeneric</code> flag. + * + * @param aBoolean <code>true</code> if the flag must be set. + */ + public void setKeepgeneric(boolean aBoolean) { + keepgeneric = aBoolean; + } + + /** + * Sets the jar suffix. + * + * @param aString the string to use as the suffix. + */ + public void setJarsuffix(String aString) { + suffix = aString; + } + + /** + * Sets the <code>orb</code> to construct classpath. + * + * @param aString 'RMI', 'JEREMIE', or 'DAVID'. + */ + public void setOrb(String aString) { + orb = aString; + } + + /** + * Sets the <code>nogenic</code> flag. + * + * @param aBoolean <code>true</code> if the flag must be set. + */ + public void setNogenic(boolean aBoolean) { + nogenic = aBoolean; + } + + /* ------------- */ + /* other methods */ + /* ------------- */ + + /** {@inheritDoc}. */ + public void processDescriptor(String aDescriptorName, SAXParser saxParser) { + + descriptorName = aDescriptorName; + + log("JOnAS Deployment Tool processing: " + descriptorName, + Project.MSG_VERBOSE); + + super.processDescriptor(descriptorName, saxParser); + + if (outputdir != null) { + // the method deleteOnExit() do not work because the directory is not empty + log("Deleting temp output directory '" + outputdir + "'.", Project.MSG_VERBOSE); + deleteAllFiles(outputdir); + } + } + + /** {@inheritDoc}. */ + protected void writeJar(String baseName, File jarfile, Hashtable ejbFiles, String publicId) + throws BuildException { + + // create the generic jar first + File genericJarFile = super.getVendorOutputJarFile(baseName); + super.writeJar(baseName, genericJarFile, ejbFiles, publicId); + + // GenIC call on generic jar + addGenICGeneratedFiles(genericJarFile, ejbFiles); + + // create the real jar + super.writeJar(baseName, getVendorOutputJarFile(baseName), ejbFiles, publicId); + + if (!keepgeneric) { + log("Deleting generic JAR " + genericJarFile.toString(), Project.MSG_VERBOSE); + genericJarFile.delete(); + } + } + + /** {@inheritDoc}. */ + protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) { + + // JOnAS-specific descriptor deployment + jonasDescriptorName = getJonasDescriptorName(); + File jonasDD = new File(getConfig().descriptorDir, jonasDescriptorName); + + if (jonasDD.exists()) { + ejbFiles.put(META_DIR + JONAS_DD, jonasDD); + } else { + log("Unable to locate the JOnAS deployment descriptor. It was expected to be in: " + + jonasDD.getPath() + ".", Project.MSG_WARN); + } + } + + /** {@inheritDoc}. */ + protected File getVendorOutputJarFile(String baseName) { + return new File(getDestDir(), baseName + suffix); + } + + /** + * Determines the name of the JOnAS-specific EJB descriptor using the + * specified standard EJB descriptor name. In general, the standard + * descriptor will be named "[basename]-ejb-jar.xml", and this method will + * return "[basename]-jonas-ejb-jar.xml" or "jonas-[basename].xml" + * + * @return The name of the JOnAS-specific EJB descriptor file. + */ + private String getJonasDescriptorName() { + + // descriptorName = <path><basename><basenameterminator><remainder> + // examples = /org/objectweb/fooAppli/foo/Foo-ejb-jar.xml + // examples = /org/objectweb/fooAppli/foo/Foo.xml (JOnAS convention) + + String jonasDN; // JOnAS-specific DD + boolean jonasConvention = false; // true if the JOnAS convention is used for the DD + String path; // Directory path of the EJB descriptor + String fileName; // EJB descriptor file name + String baseName; // Filename appearing before name terminator + String remainder; // Filename appearing after the name terminator + + int startOfFileName = descriptorName.lastIndexOf(File.separatorChar); + if (startOfFileName != -1) { + // extract path info + path = descriptorName.substring(0, startOfFileName + 1); + fileName = descriptorName.substring(startOfFileName + 1); + } else { + // descriptorName is just a file without path + path = ""; + fileName = descriptorName; + } + + if (fileName.startsWith(EJB_DD)) { + return path + JONAS_DD; + } + + int endOfBaseName = descriptorName.indexOf(getConfig().baseNameTerminator, startOfFileName); + + /* + * Check for the odd case where the terminator and/or filename + * extension aren't found. These will ensure "jonas-" appears at the + * end of the name and before the '.' (if present). + */ + if (endOfBaseName < 0) { + // baseNameTerminator not found: the descriptor use the + // JOnAS naming convention, ie [Foo.xml,jonas-Foo.xml] and + // not [Foo<baseNameTerminator>-ejb-jar.xml, + // Foo<baseNameTerminator>-jonas-ejb-jar.xml]. + endOfBaseName = descriptorName.lastIndexOf('.') - 1; + if (endOfBaseName < 0) { + // no . found + endOfBaseName = descriptorName.length() - 1; + } + + jonasConvention = true; + } + + baseName = descriptorName.substring(startOfFileName + 1, endOfBaseName + 1); + remainder = descriptorName.substring(endOfBaseName + 1); + + if (jonasConvention) { + jonasDN = path + "jonas-" + baseName + ".xml"; + } else { + jonasDN = path + baseName + "jonas-" + remainder; + } + + log("Standard EJB descriptor name: " + descriptorName, Project.MSG_VERBOSE); + log("JOnAS-specific descriptor name: " + jonasDN, Project.MSG_VERBOSE); + + return jonasDN; + } + + /** {@inheritDoc}. */ + protected String getJarBaseName(String descriptorFileName) { + + String baseName = null; + + if (getConfig().namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) { + + // try to find JOnAS specific convention name + if (descriptorFileName.indexOf(getConfig().baseNameTerminator) == -1) { + + // baseNameTerminator not found: the descriptor use the + // JOnAS naming convention, ie [Foo.xml,jonas-Foo.xml] and + // not [Foo<baseNameTerminator>-ejb-jar.xml, + // Foo<baseNameTerminator>-jonas-ejb-jar.xml]. + + String aCanonicalDescriptor = descriptorFileName.replace('\\', '/'); + int lastSeparatorIndex = aCanonicalDescriptor.lastIndexOf('/'); + int endOfBaseName; + + if (lastSeparatorIndex != -1) { + endOfBaseName = descriptorFileName.indexOf(".xml", lastSeparatorIndex); + } else { + endOfBaseName = descriptorFileName.indexOf(".xml"); + } + + if (endOfBaseName != -1) { + baseName = descriptorFileName.substring(0, endOfBaseName); + } + } + } + + if (baseName == null) { + // else get standard baseName + baseName = super.getJarBaseName(descriptorFileName); + } + + log("JAR base name: " + baseName, Project.MSG_VERBOSE); + + return baseName; + } + + /** {@inheritDoc}. */ + protected void registerKnownDTDs(DescriptorHandler handler) { + handler.registerDTD(EJB_JAR_1_1_PUBLIC_ID, + jonasroot + File.separator + "xml" + File.separator + EJB_JAR_1_1_DTD); + handler.registerDTD(EJB_JAR_2_0_PUBLIC_ID, + jonasroot + File.separator + "xml" + File.separator + EJB_JAR_2_0_DTD); + + handler.registerDTD(JONAS_EJB_JAR_2_4_PUBLIC_ID, + jonasroot + File.separator + "xml" + File.separator + JONAS_EJB_JAR_2_4_DTD); + handler.registerDTD(JONAS_EJB_JAR_2_5_PUBLIC_ID, + jonasroot + File.separator + "xml" + File.separator + JONAS_EJB_JAR_2_5_DTD); + } + + /** + * Add to the given hashtable all the file generated by GenIC. + * + * @param genericJarFile jar file. + * @param ejbFiles the hashtable. + */ + private void addGenICGeneratedFiles( + File genericJarFile, Hashtable ejbFiles) { + Java genicTask = null; // GenIC task + String genicClass = null; // GenIC class (3 are supported for various + // versions + if (nogenic) { + return; + } + + genicTask = new Java(getTask()); + genicTask.setTaskName("genic"); + genicTask.setFork(true); + + // jonasroot + genicTask.createJvmarg().setValue("-Dinstall.root=" + jonasroot); + + // java policy file + String jonasConfigDir = jonasroot + File.separator + "config"; + File javaPolicyFile = new File(jonasConfigDir, "java.policy"); + if (javaPolicyFile.exists()) { + genicTask.createJvmarg().setValue("-Djava.security.policy=" + + javaPolicyFile.toString()); + } + + // outputdir + try { + outputdir = createTempDir(); + } catch (IOException aIOException) { + String msg = "Cannot create temp dir: " + aIOException.getMessage(); + throw new BuildException(msg, aIOException); + } + log("Using temporary output directory: " + outputdir, Project.MSG_VERBOSE); + + genicTask.createArg().setValue("-d"); + genicTask.createArg().setFile(outputdir); + + // work around a bug of GenIC 2.5 + String key; + File f; + Enumeration keys = ejbFiles.keys(); + while (keys.hasMoreElements()) { + key = (String) keys.nextElement(); + f = new File(outputdir + File.separator + key); + f.getParentFile().mkdirs(); + } + log("Worked around a bug of GenIC 2.5.", Project.MSG_VERBOSE); + + // classpath + Path classpath = getCombinedClasspath(); + if (classpath == null) { + classpath = new Path(getTask().getProject()); + } + classpath.append(new Path(classpath.getProject(), jonasConfigDir)); + classpath.append(new Path(classpath.getProject(), outputdir.toString())); + + // try to create the classpath for the correct ORB + if (orb != null) { + String orbJar = jonasroot + File.separator + "lib" + + File.separator + orb + "_jonas.jar"; + classpath.append(new Path(classpath.getProject(), orbJar)); + } + log("Using classpath: " + classpath.toString(), Project.MSG_VERBOSE); + genicTask.setClasspath(classpath); + + // class name (search in the classpath provided for the ejbjar element) + genicClass = getGenicClassName(classpath); + if (genicClass == null) { + log("Cannot find GenIC class in classpath.", Project.MSG_ERR); + throw new BuildException("GenIC class not found, please check the classpath."); + } else { + log("Using '" + genicClass + "' GenIC class." , Project.MSG_VERBOSE); + genicTask.setClassname(genicClass); + } + + // keepgenerated + if (keepgenerated) { + genicTask.createArg().setValue("-keepgenerated"); + } + + // nocompil + if (nocompil) { + genicTask.createArg().setValue("-nocompil"); + } + + // novalidation + if (novalidation) { + genicTask.createArg().setValue("-novalidation"); + } + + // javac + if (javac != null) { + genicTask.createArg().setValue("-javac"); + genicTask.createArg().setLine(javac); + } + + // javacopts + if (javacopts != null && !javacopts.equals("")) { + genicTask.createArg().setValue("-javacopts"); + genicTask.createArg().setLine(javacopts); + } + + // rmicopts + if (rmicopts != null && !rmicopts.equals("")) { + genicTask.createArg().setValue("-rmicopts"); + genicTask.createArg().setLine(rmicopts); + } + + // secpropag + if (secpropag) { + genicTask.createArg().setValue("-secpropag"); + } + + // verbose + if (verbose) { + genicTask.createArg().setValue("-verbose"); + } + + // additionalargs + if (additionalargs != null) { + genicTask.createArg().setValue(additionalargs); + } + + // the generated classes must not be added in the generic JAR! + // is that buggy on old JOnAS (2.4) ?? + genicTask.createArg().setValue("-noaddinjar"); + + // input file to process by GenIC + genicTask.createArg().setValue(genericJarFile.getPath()); + + // calling GenIC task + log("Calling " + genicClass + " for " + getConfig().descriptorDir + + File.separator + descriptorName + ".", Project.MSG_VERBOSE); + + if (genicTask.executeJava() != 0) { + + // the method deleteOnExit() do not work because the directory is not empty + log("Deleting temp output directory '" + outputdir + "'.", Project.MSG_VERBOSE); + deleteAllFiles(outputdir); + + if (!keepgeneric) { + log("Deleting generic JAR " + genericJarFile.toString(), + Project.MSG_VERBOSE); + genericJarFile.delete(); + } + + throw new BuildException("GenIC reported an error."); + } + + // add the generated files to the ejbFiles + addAllFiles(outputdir, "", ejbFiles); + } + + /** + * Get the GenIC class name to use in the given classpath. + * + * @param classpath classpath where the GenIC class must be searched. + * @return the GenIC class name. Return <code>null</code> if the class name + * cannot be found. + */ + String getGenicClassName(Path classpath) { + + log("Looking for GenIC class in classpath: " + + classpath.toString(), Project.MSG_VERBOSE); + + AntClassLoader cl = null; + + try { + cl = classpath.getProject().createClassLoader(classpath); + + try { + cl.loadClass(JonasDeploymentTool.GENIC_CLASS); + log("Found GenIC class '" + JonasDeploymentTool.GENIC_CLASS + + "' in classpath.", Project.MSG_VERBOSE); + return JonasDeploymentTool.GENIC_CLASS; + + } catch (ClassNotFoundException cnf1) { + log("GenIC class '" + JonasDeploymentTool.GENIC_CLASS + + "' not found in classpath.", + Project.MSG_VERBOSE); + } + + try { + cl.loadClass(JonasDeploymentTool.OLD_GENIC_CLASS_1); + log("Found GenIC class '" + + JonasDeploymentTool.OLD_GENIC_CLASS_1 + + "' in classpath.", Project.MSG_VERBOSE); + return JonasDeploymentTool.OLD_GENIC_CLASS_1; + + } catch (ClassNotFoundException cnf2) { + log("GenIC class '" + JonasDeploymentTool.OLD_GENIC_CLASS_1 + + "' not found in classpath.", + Project.MSG_VERBOSE); + } + + try { + cl.loadClass(JonasDeploymentTool.OLD_GENIC_CLASS_2); + log("Found GenIC class '" + + JonasDeploymentTool.OLD_GENIC_CLASS_2 + + "' in classpath.", Project.MSG_VERBOSE); + return JonasDeploymentTool.OLD_GENIC_CLASS_2; + + } catch (ClassNotFoundException cnf3) { + log("GenIC class '" + JonasDeploymentTool.OLD_GENIC_CLASS_2 + + "' not found in classpath.", + Project.MSG_VERBOSE); + } + } finally { + if (cl != null) { + cl.cleanup(); + } + } + return null; + } + + /** + * Verify the configuration. + * @param descriptorFileName the name of the descriptor file. + * @param saxParser not used. + * @throws BuildException if there is an error. + */ + protected void checkConfiguration(String descriptorFileName, + SAXParser saxParser) throws BuildException { + + // jonasroot + if (jonasroot == null) { + throw new BuildException("The jonasroot attribut is not set."); + } else if (!jonasroot.isDirectory()) { + throw new BuildException("The jonasroot attribut '" + jonasroot + + "' is not a valid directory."); + } + + // orb + if (orb != null && !orb.equals(RMI_ORB) && !orb.equals(JEREMIE_ORB) + && !orb.equals(DAVID_ORB)) { + throw new BuildException("The orb attribut '" + orb + + "' is not valid (must be either " + + RMI_ORB + ", " + JEREMIE_ORB + " or " + DAVID_ORB + ")."); + } + + // additionalargs + if (additionalargs != null && additionalargs.equals("")) { + throw new BuildException("Empty additionalargs attribut."); + } + + // javac + if (javac != null && javac.equals("")) { + throw new BuildException("Empty javac attribut."); + } + } + + /* ----------------------------------------------------------------------------------- */ + /* utilitary methods */ + /* ----------------------------------------------------------------------------------- */ + + /** + * Create a temporary directory for GenIC output. + * + * @return the temp directory. + * @throws BuildException if a temp directory cannot be created. + */ + private File createTempDir() throws IOException { + File tmpDir = File.createTempFile("genic", null, null); + tmpDir.delete(); + if (!tmpDir.mkdir()) { + throw new IOException("Cannot create the temporary directory '" + tmpDir + "'."); + } + return tmpDir; + } + + /** + * Delete a file. If the file is a directory, delete recursivly all the + * files inside. + * + * @param aFile file to delete. + */ + private void deleteAllFiles(File aFile) { + if (aFile.isDirectory()) { + File[] someFiles = aFile.listFiles(); + + for (int i = 0; i < someFiles.length; i++) { + deleteAllFiles(someFiles[i]); + } + } + aFile.delete(); + } + + /** + * Add a file to the a given hashtable. If the file is a directory, add + * recursivly all the files inside to the hashtable. + * + * @param file the file to add. + * @param rootDir the current sub-directory to scan. + * @param hashtable the hashtable where to add the files. + */ + private void addAllFiles(File file, String rootDir, Hashtable hashtable) { + + if (!file.exists()) { + throw new IllegalArgumentException(); + } + + String newRootDir; + if (file.isDirectory()) { + File[] files = file.listFiles(); + for (int i = 0; i < files.length; i++) { + if (rootDir.length() > 0) { + newRootDir = rootDir + File.separator + files[i].getName(); + } else { + newRootDir = files[i].getName(); + } + addAllFiles(files[i], newRootDir, hashtable); + } + } else { + hashtable.put(rootDir, file); + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicDeploymentTool.java new file mode 100644 index 00000000..550f59ce --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicDeploymentTool.java @@ -0,0 +1,932 @@ +/* + * 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.tools.ant.taskdefs.optional.ejb; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Vector; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.apache.tools.ant.AntClassLoader; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Java; +import org.apache.tools.ant.types.Environment; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.FileUtils; +import org.xml.sax.InputSource; + +/** + The weblogic element is used to control the weblogic.ejbc compiler for + generating weblogic EJB jars. Prior to Ant 1.3, the method of locating CMP + descriptors was to use the ejbjar naming convention. So if your ejb-jar was + called, Customer-ejb-jar.xml, your weblogic descriptor was called Customer- + weblogic-ejb-jar.xml and your CMP descriptor had to be Customer-weblogic-cmp- + rdbms-jar.xml. In addition, the <type-storage> element in the weblogic + descriptor had to be set to the standard name META-INF/weblogic-cmp-rdbms- + jar.xml, as that is where the CMP descriptor was mapped to in the generated + jar. +*/ +public class WeblogicDeploymentTool extends GenericDeploymentTool { + /** EJB11 id */ + public static final String PUBLICID_EJB11 + = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN"; + /** EJB20 id */ + public static final String PUBLICID_EJB20 + = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"; + /** Weblogic 5.1.0 id */ + public static final String PUBLICID_WEBLOGIC_EJB510 + = "-//BEA Systems, Inc.//DTD WebLogic 5.1.0 EJB//EN"; + /** Weblogic 6.0.0 id */ + public static final String PUBLICID_WEBLOGIC_EJB600 + = "-//BEA Systems, Inc.//DTD WebLogic 6.0.0 EJB//EN"; + /** Weblogic 7.0.0 id */ + public static final String PUBLICID_WEBLOGIC_EJB700 + = "-//BEA Systems, Inc.//DTD WebLogic 7.0.0 EJB//EN"; + + /** Weblogic 5.1 dtd location */ + protected static final String DEFAULT_WL51_EJB11_DTD_LOCATION + = "/weblogic/ejb/deployment/xml/ejb-jar.dtd"; + /** Weblogic 6.0 ejb 1.1 dtd location */ + protected static final String DEFAULT_WL60_EJB11_DTD_LOCATION + = "/weblogic/ejb20/dd/xml/ejb11-jar.dtd"; + /** Weblogic 6.0 ejb 2.0 dtd location */ + protected static final String DEFAULT_WL60_EJB20_DTD_LOCATION + = "/weblogic/ejb20/dd/xml/ejb20-jar.dtd"; + + protected static final String DEFAULT_WL51_DTD_LOCATION + = "/weblogic/ejb/deployment/xml/weblogic-ejb-jar.dtd"; + protected static final String DEFAULT_WL60_51_DTD_LOCATION + = "/weblogic/ejb20/dd/xml/weblogic510-ejb-jar.dtd"; + protected static final String DEFAULT_WL60_DTD_LOCATION + = "/weblogic/ejb20/dd/xml/weblogic600-ejb-jar.dtd"; + protected static final String DEFAULT_WL70_DTD_LOCATION + = "/weblogic/ejb20/dd/xml/weblogic700-ejb-jar.dtd"; + + protected static final String DEFAULT_COMPILER = "default"; + + protected static final String WL_DD = "weblogic-ejb-jar.xml"; + protected static final String WL_CMP_DD = "weblogic-cmp-rdbms-jar.xml"; + + protected static final String COMPILER_EJB11 = "weblogic.ejbc"; + protected static final String COMPILER_EJB20 = "weblogic.ejbc20"; + + /** File utilities instance for copying jars */ + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** Instance variable that stores the suffix for the weblogic jarfile. */ + private String jarSuffix = ".jar"; + + /** Instance variable that stores the location of the weblogic DTD file. */ + private String weblogicDTD; + + /** Instance variable that stores the location of the ejb 1.1 DTD file. */ + private String ejb11DTD; + + /** Instance variable that determines whether generic ejb jars are kept. */ + private boolean keepgenerated = false; + + /** + * Instance variable that stores the fully qualified classname of the + * weblogic EJBC compiler + */ + private String ejbcClass = null; + + private String additionalArgs = ""; + + /** + * additional args to pass to the spawned jvm + */ + private String additionalJvmArgs = ""; + + private boolean keepGeneric = false; + + private String compiler = null; + + private boolean alwaysRebuild = true; + + /** controls whether ejbc is run on the generated jar */ + private boolean noEJBC = false; + + /** Indicates if the old CMP location convention is to be used. */ + private boolean newCMP = false; + + /** The classpath to the weblogic classes. */ + private Path wlClasspath = null; + + /** System properties for the JVM. */ + private Vector sysprops = new Vector(); + + /** + * The weblogic.StdoutSeverityLevel to use when running the JVM that + * executes ejbc. Set to 16 to avoid the warnings about EJB Home and + * Remotes being in the classpath + */ + private Integer jvmDebugLevel = null; + + private File outputDir; + + /** + * Add a nested sysproperty element. + * @param sysp the element to add. + */ + public void addSysproperty(Environment.Variable sysp) { + sysprops.add(sysp); + } + + + /** + * Get the classpath to the weblogic classpaths. + * @return the classpath to configure. + */ + public Path createWLClasspath() { + if (wlClasspath == null) { + wlClasspath = new Path(getTask().getProject()); + } + return wlClasspath.createPath(); + } + + /** + * If set ejbc will use this directory as the output + * destination rather than a jar file. This allows for the + * generation of "exploded" jars. + * @param outputDir the directory to be used. + */ + public void setOutputDir(File outputDir) { + this.outputDir = outputDir; + } + + + /** + * Optional classpath to WL6.0. + * Weblogic 6.0 will give a warning if the home and remote interfaces + * of a bean are on the system classpath used to run weblogic.ejbc. + * In that case, the standard weblogic classes should be set with + * this attribute (or equivalent nested element) and the + * home and remote interfaces located with the standard classpath + * attribute. + * @param wlClasspath the path to be used. + */ + public void setWLClasspath(Path wlClasspath) { + this.wlClasspath = wlClasspath; + } + + + /** + * The compiler (switch <code>-compiler</code>) to use; optional. + * This allows for the selection of a different compiler + * to be used for the compilation of the generated Java + * files. This could be set, for example, to Jikes to + * compile with the Jikes compiler. If this is not set + * and the <code>build.compiler</code> property is set + * to jikes, the Jikes compiler will be used. If this + * is not desired, the value "<code>default</code>" + * may be given to use the default compiler. + * @param compiler the compiler to be used. + */ + public void setCompiler(String compiler) { + this.compiler = compiler; + } + + + /** + * Set the rebuild flag to false to only update changes in the jar rather + * than rerunning ejbc; optional, default true. + * This flag controls whether weblogic.ejbc is always + * invoked to build the jar file. In certain circumstances, + * such as when only a bean class has been changed, the jar + * can be generated by merely replacing the changed classes + * and not rerunning ejbc. Setting this to false will reduce + * the time to run ejbjar. + * @param rebuild a <code>boolean</code> value. + */ + public void setRebuild(boolean rebuild) { + this.alwaysRebuild = rebuild; + } + + + /** + * Sets the weblogic.StdoutSeverityLevel to use when running the JVM that + * executes ejbc; optional. Set to 16 to avoid the warnings about EJB Home and + * Remotes being in the classpath + * @param jvmDebugLevel the value to use. + */ + public void setJvmDebugLevel(Integer jvmDebugLevel) { + this.jvmDebugLevel = jvmDebugLevel; + } + + + /** + * Get the debug level. + * @return the jvm debug level (may be null). + */ + public Integer getJvmDebugLevel() { + return jvmDebugLevel; + } + + + + /** + * Setter used to store the suffix for the generated weblogic jar file. + * + * @param inString the string to use as the suffix. + */ + public void setSuffix(String inString) { + this.jarSuffix = inString; + } + + + /** + * controls whether the generic file used as input to + * ejbc is retained; defaults to false + * + * @param inValue true for keep generic + */ + public void setKeepgeneric(boolean inValue) { + this.keepGeneric = inValue; + } + + + /** + * Controls whether weblogic will keep the generated Java + * files used to build the class files added to the + * jar. This can be useful when debugging; default is false. + * + * @param inValue either 'true' or 'false' + */ + public void setKeepgenerated(String inValue) { + this.keepgenerated = Boolean.valueOf(inValue).booleanValue(); + } + + + /** + * Any optional extra arguments pass to the weblogic.ejbc + * tool. + * @param args extra arguments to pass to the ejbc tool. + */ + public void setArgs(String args) { + this.additionalArgs = args; + } + + + /** + * Set any additional arguments to pass to the weblogic JVM; optional. + * @param args the arguments to be passed to the JVM + */ + public void setJvmargs(String args) { + this.additionalJvmArgs = args; + } + + /** + * Set the classname of the ejbc compiler; optional + * Normally ejbjar determines + * the appropriate class based on the DTD used for the EJB. The EJB 2.0 compiler + * featured in weblogic 6 has, however, been deprecated in version 7. When + * using with version 7 this attribute should be set to + * "weblogic.ejbc" to avoid the deprecation warning. + * @param ejbcClass the name of the class to use. + */ + public void setEjbcClass(String ejbcClass) { + this.ejbcClass = ejbcClass; + } + + + /** + * Get the ejbc compiler class. + * @return the name of the ejbc compiler class. + */ + public String getEjbcClass() { + return ejbcClass; + } + + + /** + * <b>Deprecated</b>. Defines the location of the ejb-jar DTD in + * the weblogic class hierarchy. Should not be needed, and the + * nested <dtd> element is recommended when it is. + * + * @param inString the string to use as the DTD location. + */ + public void setWeblogicdtd(String inString) { + setEJBdtd(inString); + } + + + /** + * <b>Deprecated</b>. Defines the location of weblogic DTD in + * the weblogic class hierarchy. Should not be needed, and the + * nested <dtd> element is recommended when it is. + * + * @param inString the string to use as the DTD location. + */ + public void setWLdtd(String inString) { + this.weblogicDTD = inString; + } + + + /** + * <b>Deprecated</b>. Defines the location of Sun's EJB DTD in + * the weblogic class hierarchy. Should not be needed, and the + * nested <dtd> element is recommended when it is. + * + * @param inString the string to use as the DTD location. + */ + public void setEJBdtd(String inString) { + this.ejb11DTD = inString; + } + + + /** + * Set the value of the oldCMP scheme. This is an antonym for newCMP + * @ant.attribute ignore="true' + * @param oldCMP a <code>boolean</code> value. + */ + public void setOldCMP(boolean oldCMP) { + this.newCMP = !oldCMP; + } + + + /** + * If this is set to true, the new method for locating + * CMP descriptors will be used; optional, default false. + * <P> + * The old CMP scheme locates the + * weblogic CMP descriptor based on the naming convention where the + * weblogic CMP file is expected to be named with the bean name as the + * prefix. Under this scheme the name of the CMP descriptor does not match + * the name actually used in the main weblogic EJB descriptor. Also, + * descriptors which contain multiple CMP references could not be used. + * @param newCMP a <code>boolean</code> value. + */ + public void setNewCMP(boolean newCMP) { + this.newCMP = newCMP; + } + + + /** + * Do not EJBC the jar after it has been put together; + * optional, default false + * @param noEJBC a <code>boolean</code> value. + */ + public void setNoEJBC(boolean noEJBC) { + this.noEJBC = noEJBC; + } + + + /** + * Register the DTDs. + * @param handler the handler to use. + */ + protected void registerKnownDTDs(DescriptorHandler handler) { + // register all the known DTDs + handler.registerDTD(PUBLICID_EJB11, DEFAULT_WL51_EJB11_DTD_LOCATION); + handler.registerDTD(PUBLICID_EJB11, DEFAULT_WL60_EJB11_DTD_LOCATION); + handler.registerDTD(PUBLICID_EJB11, ejb11DTD); + handler.registerDTD(PUBLICID_EJB20, DEFAULT_WL60_EJB20_DTD_LOCATION); + } + + + /** + * Get the weblogic descriptor handler. + * @param srcDir the source directory. + * @return the descriptor. + */ + protected DescriptorHandler getWeblogicDescriptorHandler(final File srcDir) { + DescriptorHandler handler = + new DescriptorHandler(getTask(), srcDir) { + protected void processElement() { + if (currentElement.equals("type-storage")) { + // Get the filename of vendor specific descriptor + String fileNameWithMETA = currentText; + //trim the META_INF\ off of the file name + String fileName + = fileNameWithMETA.substring(META_DIR.length(), + fileNameWithMETA.length()); + File descriptorFile = new File(srcDir, fileName); + + ejbFiles.put(fileNameWithMETA, descriptorFile); + } + } + }; + + handler.registerDTD(PUBLICID_WEBLOGIC_EJB510, DEFAULT_WL51_DTD_LOCATION); + handler.registerDTD(PUBLICID_WEBLOGIC_EJB510, DEFAULT_WL60_51_DTD_LOCATION); + handler.registerDTD(PUBLICID_WEBLOGIC_EJB600, DEFAULT_WL60_DTD_LOCATION); + handler.registerDTD(PUBLICID_WEBLOGIC_EJB700, DEFAULT_WL70_DTD_LOCATION); + handler.registerDTD(PUBLICID_WEBLOGIC_EJB510, weblogicDTD); + handler.registerDTD(PUBLICID_WEBLOGIC_EJB600, weblogicDTD); + + for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) { + EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next(); + + handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation()); + } + return handler; + } + + + /** + * Add any vendor specific files which should be included in the EJB Jar. + * @param ejbFiles the hash table to be populated. + * @param ddPrefix the prefix to use. + */ + protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) { + File weblogicDD = new File(getConfig().descriptorDir, ddPrefix + WL_DD); + + if (weblogicDD.exists()) { + ejbFiles.put(META_DIR + WL_DD, + weblogicDD); + } else { + log("Unable to locate weblogic deployment descriptor. " + + "It was expected to be in " + + weblogicDD.getPath(), Project.MSG_WARN); + return; + } + + if (!newCMP) { + log("The old method for locating CMP files has been DEPRECATED.", Project.MSG_VERBOSE); + log("Please adjust your weblogic descriptor and set " + + "newCMP=\"true\" to use the new CMP descriptor " + + "inclusion mechanism. ", Project.MSG_VERBOSE); + // The the weblogic cmp deployment descriptor + File weblogicCMPDD = new File(getConfig().descriptorDir, ddPrefix + WL_CMP_DD); + + if (weblogicCMPDD.exists()) { + ejbFiles.put(META_DIR + WL_CMP_DD, + weblogicCMPDD); + } + } else { + // now that we have the weblogic descriptor, we parse the file + // to find other descriptors needed to deploy the bean. + // this could be the weblogic-cmp-rdbms.xml or any other O/R + // mapping tool descriptors. + try { + File ejbDescriptor = (File) ejbFiles.get(META_DIR + EJB_DD); + SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); + + saxParserFactory.setValidating(true); + + SAXParser saxParser = saxParserFactory.newSAXParser(); + DescriptorHandler handler + = getWeblogicDescriptorHandler(ejbDescriptor.getParentFile()); + + saxParser.parse(new InputSource + (new FileInputStream(weblogicDD)), + handler); + + Hashtable ht = handler.getFiles(); + Enumeration e = ht.keys(); + + while (e.hasMoreElements()) { + String key = (String) e.nextElement(); + + ejbFiles.put(key, ht.get(key)); + } + } catch (Exception e) { + String msg = "Exception while adding Vendor specific files: " + e.toString(); + + throw new BuildException(msg, e); + } + } + } + + + /** + * Get the vendor specific name of the Jar that will be output. The + * modification date of this jar will be checked against the dependent + * bean classes. + */ + File getVendorOutputJarFile(String baseName) { + return new File(getDestDir(), baseName + jarSuffix); + } + + + /** + * Helper method invoked by execute() for each WebLogic jar to be built. + * Encapsulates the logic of constructing a java task for calling + * weblogic.ejbc and executing it. + * + * @param sourceJar java.io.File representing the source (EJB1.1) jarfile. + * @param destJar java.io.File representing the destination, WebLogic + * jarfile. + */ + private void buildWeblogicJar(File sourceJar, File destJar, String publicId) { + Java javaTask = null; + + if (noEJBC) { + try { + FILE_UTILS.copyFile(sourceJar, destJar); + if (!keepgenerated) { + sourceJar.delete(); + } + return; + } catch (IOException e) { + throw new BuildException("Unable to write EJB jar", e); + } + } + + String ejbcClassName = ejbcClass; + + try { + javaTask = new Java(getTask()); + javaTask.setTaskName("ejbc"); + + javaTask.createJvmarg().setLine(additionalJvmArgs); + if (!(sysprops.isEmpty())) { + for (Enumeration en = sysprops.elements(); en.hasMoreElements();) { + Environment.Variable entry + = (Environment.Variable) en.nextElement(); + javaTask.addSysproperty(entry); + } + } + + if (getJvmDebugLevel() != null) { + javaTask.createJvmarg().setLine(" -Dweblogic.StdoutSeverityLevel=" + jvmDebugLevel); + } + + if (ejbcClassName == null) { + // try to determine it from publicId + if (PUBLICID_EJB11.equals(publicId)) { + ejbcClassName = COMPILER_EJB11; + } else if (PUBLICID_EJB20.equals(publicId)) { + ejbcClassName = COMPILER_EJB20; + } else { + log("Unrecognized publicId " + publicId + + " - using EJB 1.1 compiler", Project.MSG_WARN); + ejbcClassName = COMPILER_EJB11; + } + } + + javaTask.setClassname(ejbcClassName); + javaTask.createArg().setLine(additionalArgs); + if (keepgenerated) { + javaTask.createArg().setValue("-keepgenerated"); + } + if (compiler == null) { + // try to use the compiler specified by build.compiler. + // Right now we are just going to allow Jikes + String buildCompiler + = getTask().getProject().getProperty("build.compiler"); + + if (buildCompiler != null && buildCompiler.equals("jikes")) { + javaTask.createArg().setValue("-compiler"); + javaTask.createArg().setValue("jikes"); + } + } else { + if (!compiler.equals(DEFAULT_COMPILER)) { + javaTask.createArg().setValue("-compiler"); + javaTask.createArg().setLine(compiler); + } + } + + Path combinedClasspath = getCombinedClasspath(); + if (wlClasspath != null && combinedClasspath != null + && combinedClasspath.toString().trim().length() > 0) { + javaTask.createArg().setValue("-classpath"); + javaTask.createArg().setPath(combinedClasspath); + } + + javaTask.createArg().setValue(sourceJar.getPath()); + if (outputDir == null) { + javaTask.createArg().setValue(destJar.getPath()); + } else { + javaTask.createArg().setValue(outputDir.getPath()); + } + + Path classpath = wlClasspath; + + if (classpath == null) { + classpath = getCombinedClasspath(); + } + + javaTask.setFork(true); + if (classpath != null) { + javaTask.setClasspath(classpath); + } + + log("Calling " + ejbcClassName + " for " + sourceJar.toString(), + Project.MSG_VERBOSE); + + if (javaTask.executeJava() != 0) { + throw new BuildException("Ejbc reported an error"); + } + } catch (Exception e) { + // Have to catch this because of the semantics of calling main() + String msg = "Exception while calling " + ejbcClassName + + ". Details: " + e.toString(); + + throw new BuildException(msg, e); + } + } + + + /** + * Method used to encapsulate the writing of the JAR file. Iterates over + * the filenames/java.io.Files in the Hashtable stored on the instance + * variable ejbFiles. + * @param baseName the base name. + * @param jarFile the jar file to populate. + * @param files the hash table of files to write. + * @param publicId the id to use. + * @throws BuildException if there is a problem. + */ + protected void writeJar(String baseName, File jarFile, Hashtable files, + String publicId) throws BuildException { + // need to create a generic jar first. + File genericJarFile = super.getVendorOutputJarFile(baseName); + + super.writeJar(baseName, genericJarFile, files, publicId); + + if (alwaysRebuild || isRebuildRequired(genericJarFile, jarFile)) { + buildWeblogicJar(genericJarFile, jarFile, publicId); + } + if (!keepGeneric) { + log("deleting generic jar " + genericJarFile.toString(), + Project.MSG_VERBOSE); + genericJarFile.delete(); + } + } + + + /** + * Called to validate that the tool parameters have been configured. + * @throws BuildException if there is an error. + */ + public void validateConfigured() throws BuildException { + super.validateConfigured(); + } + + + /** + * Helper method to check to see if a weblogic EBJ1.1 jar needs to be + * rebuilt using ejbc. Called from writeJar it sees if the "Bean" classes + * are the only thing that needs to be updated and either updates the Jar + * with the Bean classfile or returns true, saying that the whole weblogic + * jar needs to be regened with ejbc. This allows faster build times for + * working developers. <p> + * + * The way weblogic ejbc works is it creates wrappers for the publicly + * defined methods as they are exposed in the remote interface. If the + * actual bean changes without changing the the method signatures then + * only the bean classfile needs to be updated and the rest of the + * weblogic jar file can remain the same. If the Interfaces, ie. the + * method signatures change or if the xml deployment descriptors changed, + * the whole jar needs to be rebuilt with ejbc. This is not strictly true + * for the xml files. If the JNDI name changes then the jar doesn't have to + * be rebuild, but if the resources references change then it does. At + * this point the weblogic jar gets rebuilt if the xml files change at + * all. + * + * @param genericJarFile java.io.File The generic jar file. + * @param weblogicJarFile java.io.File The weblogic jar file to check to + * see if it needs to be rebuilt. + * @return true if the jar needs to be rebuilt. + */ + // CheckStyle:MethodLength OFF - this will no be fixed + protected boolean isRebuildRequired(File genericJarFile, File weblogicJarFile) { + boolean rebuild = false; + + JarFile genericJar = null; + JarFile wlJar = null; + File newWLJarFile = null; + JarOutputStream newJarStream = null; + ClassLoader genericLoader = null; + + try { + log("Checking if weblogic Jar needs to be rebuilt for jar " + weblogicJarFile.getName(), + Project.MSG_VERBOSE); + // Only go forward if the generic and the weblogic file both exist + if (genericJarFile.exists() && genericJarFile.isFile() + && weblogicJarFile.exists() && weblogicJarFile.isFile()) { + //open jar files + genericJar = new JarFile(genericJarFile); + wlJar = new JarFile(weblogicJarFile); + + Hashtable genericEntries = new Hashtable(); + Hashtable wlEntries = new Hashtable(); + Hashtable replaceEntries = new Hashtable(); + + //get the list of generic jar entries + for (Enumeration e = genericJar.entries(); e.hasMoreElements();) { + JarEntry je = (JarEntry) e.nextElement(); + + genericEntries.put(je.getName().replace('\\', '/'), je); + } + //get the list of weblogic jar entries + for (Enumeration e = wlJar.entries(); e.hasMoreElements();) { + JarEntry je = (JarEntry) e.nextElement(); + + wlEntries.put(je.getName(), je); + } + + //Cycle Through generic and make sure its in weblogic + genericLoader = getClassLoaderFromJar(genericJarFile); + + for (Enumeration e = genericEntries.keys(); e.hasMoreElements();) { + String filepath = (String) e.nextElement(); + + if (wlEntries.containsKey(filepath)) { + // File name/path match + + // Check files see if same + JarEntry genericEntry = (JarEntry) genericEntries.get(filepath); + JarEntry wlEntry = (JarEntry) wlEntries.get(filepath); + + if ((genericEntry.getCrc() != wlEntry.getCrc()) + || (genericEntry.getSize() != wlEntry.getSize())) { + + if (genericEntry.getName().endsWith(".class")) { + //File are different see if its an object or an interface + String classname + = genericEntry.getName() + .replace(File.separatorChar, '.') + .replace('/', '.'); + + classname = classname.substring(0, classname.lastIndexOf(".class")); + + Class genclass = genericLoader.loadClass(classname); + + if (genclass.isInterface()) { + //Interface changed rebuild jar. + log("Interface " + genclass.getName() + + " has changed", Project.MSG_VERBOSE); + rebuild = true; + break; + } else { + //Object class Changed update it. + replaceEntries.put(filepath, genericEntry); + } + } else { + // is it the manifest. If so ignore it + if (!genericEntry.getName().equals("META-INF/MANIFEST.MF")) { + //File other then class changed rebuild + log("Non class file " + genericEntry.getName() + + " has changed", Project.MSG_VERBOSE); + rebuild = true; + break; + } + } + } + } else { + // a file doesn't exist rebuild + + log("File " + filepath + " not present in weblogic jar", + Project.MSG_VERBOSE); + rebuild = true; + break; + } + } + + if (!rebuild) { + log("No rebuild needed - updating jar", Project.MSG_VERBOSE); + newWLJarFile = new File(weblogicJarFile.getAbsolutePath() + ".temp"); + if (newWLJarFile.exists()) { + newWLJarFile.delete(); + } + + newJarStream = new JarOutputStream(new FileOutputStream(newWLJarFile)); + newJarStream.setLevel(0); + + //Copy files from old weblogic jar + for (Enumeration e = wlEntries.elements(); e.hasMoreElements();) { + byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; + int bytesRead; + InputStream is; + JarEntry je = (JarEntry) e.nextElement(); + + if (je.getCompressedSize() == -1 + || je.getCompressedSize() == je.getSize()) { + newJarStream.setLevel(0); + } else { + newJarStream.setLevel(JAR_COMPRESS_LEVEL); + } + + // Update with changed Bean class + if (replaceEntries.containsKey(je.getName())) { + log("Updating Bean class from generic Jar " + + je.getName(), Project.MSG_VERBOSE); + // Use the entry from the generic jar + je = (JarEntry) replaceEntries.get(je.getName()); + is = genericJar.getInputStream(je); + } else { + //use fle from original weblogic jar + + is = wlJar.getInputStream(je); + } + newJarStream.putNextEntry(new JarEntry(je.getName())); + + while ((bytesRead = is.read(buffer)) != -1) { + newJarStream.write(buffer, 0, bytesRead); + } + is.close(); + } + } else { + log("Weblogic Jar rebuild needed due to changed " + + "interface or XML", Project.MSG_VERBOSE); + } + } else { + rebuild = true; + } + } catch (ClassNotFoundException cnfe) { + String cnfmsg = "ClassNotFoundException while processing ejb-jar file" + + ". Details: " + + cnfe.getMessage(); + + throw new BuildException(cnfmsg, cnfe); + } catch (IOException ioe) { + String msg = "IOException while processing ejb-jar file " + + ". Details: " + + ioe.getMessage(); + + throw new BuildException(msg, ioe); + } finally { + // need to close files and perhaps rename output + if (genericJar != null) { + try { + genericJar.close(); + } catch (IOException closeException) { + // empty + } + } + + if (wlJar != null) { + try { + wlJar.close(); + } catch (IOException closeException) { + // empty + } + } + + if (newJarStream != null) { + try { + newJarStream.close(); + } catch (IOException closeException) { + // empty + } + + try { + FILE_UTILS.rename(newWLJarFile, weblogicJarFile); + } catch (IOException renameException) { + log(renameException.getMessage(), Project.MSG_WARN); + rebuild = true; + } + } + if (genericLoader != null + && genericLoader instanceof AntClassLoader) { + AntClassLoader loader = (AntClassLoader) genericLoader; + loader.cleanup(); + } + } + + return rebuild; + } + + + /** + * Helper method invoked by isRebuildRequired to get a ClassLoader for a + * Jar File passed to it. + * + * @param classjar java.io.File representing jar file to get classes from. + * @return the classloader for the jarfile. + * @throws IOException if there is a problem. + */ + protected ClassLoader getClassLoaderFromJar(File classjar) throws IOException { + Path lookupPath = new Path(getTask().getProject()); + + lookupPath.setLocation(classjar); + + Path classpath = getCombinedClasspath(); + + if (classpath != null) { + lookupPath.append(classpath); + } + + return getTask().getProject().createClassLoader(lookupPath); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicTOPLinkDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicTOPLinkDeploymentTool.java new file mode 100644 index 00000000..0752bbec --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicTOPLinkDeploymentTool.java @@ -0,0 +1,113 @@ +/* + * 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.tools.ant.taskdefs.optional.ejb; + +import java.io.File; +import java.util.Hashtable; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; + +/** + * Deployment tool for Weblogic TOPLink. + */ +public class WeblogicTOPLinkDeploymentTool extends WeblogicDeploymentTool { + + private static final String TL_DTD_LOC + = "http://www.objectpeople.com/tlwl/dtd/toplink-cmp_2_5_1.dtd"; + + private String toplinkDescriptor; + private String toplinkDTD; + + /** + * Setter used to store the name of the toplink descriptor. + * @param inString the string to use as the descriptor name. + */ + public void setToplinkdescriptor(String inString) { + this.toplinkDescriptor = inString; + } + + /** + * Setter used to store the location of the toplink DTD file. + * This is expected to be an URL (file or otherwise). If running + * this on NT using a file URL, the safest thing would be to not use a + * drive spec in the URL and make sure the file resides on the drive that + * ANT is running from. This will keep the setting in the build XML + * platform independent. + * + * @param inString the string to use as the DTD location. + */ + public void setToplinkdtd(String inString) { + this.toplinkDTD = inString; + } + + /** + * Get the descriptor handler. + * @param srcDir the source file. + * @return the descriptor handler. + */ + protected DescriptorHandler getDescriptorHandler(File srcDir) { + DescriptorHandler handler = super.getDescriptorHandler(srcDir); + if (toplinkDTD != null) { + handler.registerDTD("-//The Object People, Inc.//" + + "DTD TOPLink for WebLogic CMP 2.5.1//EN", toplinkDTD); + } else { + handler.registerDTD("-//The Object People, Inc.//" + + "DTD TOPLink for WebLogic CMP 2.5.1//EN", TL_DTD_LOC); + } + return handler; + } + + /** + * Add any vendor specific files which should be included in the + * EJB Jar. + * @param ejbFiles the hashtable to add files to. + * @param ddPrefix the prefix to use. + */ + protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) { + super.addVendorFiles(ejbFiles, ddPrefix); + // Then the toplink deployment descriptor + + // Setup a naming standard here?. + + + File toplinkDD = new File(getConfig().descriptorDir, ddPrefix + toplinkDescriptor); + + if (toplinkDD.exists()) { + ejbFiles.put(META_DIR + toplinkDescriptor, + toplinkDD); + } else { + log("Unable to locate toplink deployment descriptor. " + + "It was expected to be in " + + toplinkDD.getPath(), Project.MSG_WARN); + } + } + + /** + * Called to validate that the tool parameters have been configured. + * @throws BuildException if there is an error. + */ + public void validateConfigured() throws BuildException { + super.validateConfigured(); + if (toplinkDescriptor == null) { + throw new BuildException("The toplinkdescriptor attribute must " + + "be specified"); + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WebsphereDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WebsphereDeploymentTool.java new file mode 100644 index 00000000..d15f9f52 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WebsphereDeploymentTool.java @@ -0,0 +1,897 @@ +/* + * 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.tools.ant.taskdefs.optional.ejb; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; + +import org.apache.tools.ant.AntClassLoader; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Java; +import org.apache.tools.ant.types.Environment; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.FileUtils; + +/** + * Websphere deployment tool that augments the ejbjar task. + * Searches for the websphere specific deployment descriptors and + * adds them to the final ejb jar file. Websphere has two specific descriptors for session + * beans: + * <ul> + * <li>ibm-ejb-jar-bnd.xmi</li> + * <li>ibm-ejb-jar-ext.xmi</li> + * </ul> + * and another two for container managed entity beans: + * <ul> + * <li>Map.mapxmi</li> + * <li>Schema.dbxmi</li> + * </ul> + * In terms of WebSphere, the generation of container code and stubs is + * called <code>deployment</code>. This step can be performed by the websphere + * element as part of the jar generation process. If the switch + * <code>ejbdeploy</code> is on, the ejbdeploy tool from the websphere toolset + * is called for every ejb-jar. Unfortunately, this step only works, if you + * use the ibm jdk. Otherwise, the rmic (called by ejbdeploy) throws a + * ClassFormatError. Be sure to switch ejbdeploy off, if run ant with + * sun jdk. + * + */ +public class WebsphereDeploymentTool extends GenericDeploymentTool { + + /** ID for ejb 1.1 */ + public static final String PUBLICID_EJB11 + = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN"; + /** ID for ejb 2.0 */ + public static final String PUBLICID_EJB20 + = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"; + /** Schema directory */ + protected static final String SCHEMA_DIR = "Schema/"; + + protected static final String WAS_EXT = "ibm-ejb-jar-ext.xmi"; + protected static final String WAS_BND = "ibm-ejb-jar-bnd.xmi"; + protected static final String WAS_CMP_MAP = "Map.mapxmi"; + protected static final String WAS_CMP_SCHEMA = "Schema.dbxmi"; + + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** Instance variable that stores the suffix for the websphere jarfile. */ + private String jarSuffix = ".jar"; + + /** Instance variable that stores the location of the ejb 1.1 DTD file. */ + private String ejb11DTD; + + /** Instance variable that determines whether generic ejb jars are kept. */ + + private boolean keepGeneric = false; + + private boolean alwaysRebuild = true; + + private boolean ejbdeploy = true; + + /** Indicates if the old CMP location convention is to be used. */ + private boolean newCMP = false; + + /** The classpath to the websphere classes. */ + private Path wasClasspath = null; + + /** The DB Vendor name, the EJB is persisted against */ + private String dbVendor; + + /** The name of the database to create. (For top-down mapping only) */ + private String dbName; + + /** The name of the schema to create. (For top-down mappings only) */ + private String dbSchema; + + /** true - Only generate the deployment code, do not run RMIC or Javac */ + private boolean codegen; + + /** true - Only output error messages, suppress informational messages */ + private boolean quiet = true; + + /** true - Disable the validation steps */ + private boolean novalidate; + + /** true - Disable warning and informational messages */ + private boolean nowarn; + + /** true - Disable informational messages */ + private boolean noinform; + + /** true - Enable internal tracing */ + private boolean trace; + + /** Additional options for RMIC */ + private String rmicOptions; + + /** true- Use the WebSphere 3.5 compatible mapping rules */ + private boolean use35MappingRules; + + /** the scratchdir for the ejbdeploy operation */ + private String tempdir = "_ejbdeploy_temp"; + + /** the home directory for websphere */ + private File websphereHome; + + /** + * Get the classpath to the websphere classpaths. + * @return the websphere classpath. + */ + public Path createWASClasspath() { + if (wasClasspath == null) { + wasClasspath = new Path(getTask().getProject()); + } + return wasClasspath.createPath(); + } + + + /** + * Set the websphere classpath. + * @param wasClasspath the websphere classpath. + */ + public void setWASClasspath(Path wasClasspath) { + this.wasClasspath = wasClasspath; + } + + + /** Sets the DB Vendor for the Entity Bean mapping ; optional. + * <p> + * Valid options can be obtained by running the following command: + * <code> + * <WAS_HOME>/bin/EJBDeploy.[sh/bat] -help + * </code> + * </p> + * <p> + * This is also used to determine the name of the Map.mapxmi and + * Schema.dbxmi files, for example Account-DB2UDB_V81-Map.mapxmi + * and Account-DB2UDB_V81-Schema.dbxmi. + * </p> + * + * @param dbvendor database vendor type + */ + public void setDbvendor(String dbvendor) { + this.dbVendor = dbvendor; + } + + + /** + * Sets the name of the Database to create; optional. + * + * @param dbName name of the database + */ + public void setDbname(String dbName) { + this.dbName = dbName; + } + + + /** + * Sets the name of the schema to create; optional. + * + * @param dbSchema name of the schema + */ + public void setDbschema(String dbSchema) { + this.dbSchema = dbSchema; + } + + + /** + * Flag, default false, to only generate the deployment + * code, do not run RMIC or Javac + * + * @param codegen option + */ + public void setCodegen(boolean codegen) { + this.codegen = codegen; + } + + + /** + * Flag, default true, to only output error messages. + * + * @param quiet option + */ + public void setQuiet(boolean quiet) { + this.quiet = quiet; + } + + + /** + * Flag to disable the validation steps; optional, default false. + * + * @param novalidate option + */ + public void setNovalidate(boolean novalidate) { + this.novalidate = novalidate; + } + + + /** + * Flag to disable warning and informational messages; optional, default false. + * + * @param nowarn option + */ + public void setNowarn(boolean nowarn) { + this.nowarn = nowarn; + } + + + /** + * Flag to disable informational messages; optional, default false. + * + * @param noinform if true disables informational messages + */ + public void setNoinform(boolean noinform) { + this.noinform = noinform; + } + + + /** + * Flag to enable internal tracing when set, optional, default false. + * + * @param trace a <code>boolean</code> value. + */ + public void setTrace(boolean trace) { + this.trace = trace; + } + + /** + * Set the rmic options. + * + * @param options the options to use. + */ + public void setRmicoptions(String options) { + this.rmicOptions = options; + } + + /** + * Flag to use the WebSphere 3.5 compatible mapping rules ; optional, default false. + * + * @param attr a <code>boolean</code> value. + */ + public void setUse35(boolean attr) { + use35MappingRules = attr; + } + + + /** + * Set the rebuild flag to false to only update changes in the jar rather + * than rerunning ejbdeploy; optional, default true. + * @param rebuild a <code>boolean</code> value. + */ + public void setRebuild(boolean rebuild) { + this.alwaysRebuild = rebuild; + } + + + /** + * String value appended to the basename of the deployment + * descriptor to create the filename of the WebLogic EJB + * jar file. Optional, default '.jar'. + * @param inString the string to use as the suffix. + */ + public void setSuffix(String inString) { + this.jarSuffix = inString; + } + + + /** + * This controls whether the generic file used as input to + * ejbdeploy is retained; optional, default false. + * @param inValue either 'true' or 'false'. + */ + public void setKeepgeneric(boolean inValue) { + this.keepGeneric = inValue; + } + + + /** + * Decide, whether ejbdeploy should be called or not; + * optional, default true. + * + * @param ejbdeploy a <code>boolean</code> value. + */ + public void setEjbdeploy(boolean ejbdeploy) { + this.ejbdeploy = ejbdeploy; + } + + + /** + * Setter used to store the location of the Sun's Generic EJB DTD. This + * can be a file on the system or a resource on the classpath. + * + * @param inString the string to use as the DTD location. + */ + public void setEJBdtd(String inString) { + this.ejb11DTD = inString; + } + + + /** + * Set the value of the oldCMP scheme. This is an antonym for newCMP + * @ant.attribute ignore="true" + * @param oldCMP a <code>boolean</code> value. + */ + public void setOldCMP(boolean oldCMP) { + this.newCMP = !oldCMP; + } + + + /** + * Set the value of the newCMP scheme. The old CMP scheme locates the + * websphere CMP descriptor based on the naming convention where the + * websphere CMP file is expected to be named with the bean name as the + * prefix. Under this scheme the name of the CMP descriptor does not match + * the name actually used in the main websphere EJB descriptor. Also, + * descriptors which contain multiple CMP references could not be used. + * @param newCMP a <code>boolean</code> value. + */ + public void setNewCMP(boolean newCMP) { + this.newCMP = newCMP; + } + + + /** + * The directory, where ejbdeploy will write temporary files; + * optional, defaults to '_ejbdeploy_temp'. + * @param tempdir the directory name to use. + */ + public void setTempdir(String tempdir) { + this.tempdir = tempdir; + } + + + /** {@inheritDoc}. */ + protected DescriptorHandler getDescriptorHandler(File srcDir) { + DescriptorHandler handler = new DescriptorHandler(getTask(), srcDir); + // register all the DTDs, both the ones that are known and + // any supplied by the user + handler.registerDTD(PUBLICID_EJB11, ejb11DTD); + + for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) { + EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next(); + + handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation()); + } + + return handler; + } + + + /** + * Get a description handler. + * @param srcDir the source directory. + * @return the handler. + */ + protected DescriptorHandler getWebsphereDescriptorHandler(final File srcDir) { + DescriptorHandler handler = + new DescriptorHandler(getTask(), srcDir) { + protected void processElement() { + } + }; + + for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) { + EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next(); + + handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation()); + } + return handler; + } + + + /** + * Add any vendor specific files which should be included in the EJB Jar. + * @param ejbFiles a hashtable entryname -> file. + * @param baseName a prefix to use. + */ + protected void addVendorFiles(Hashtable ejbFiles, String baseName) { + + String ddPrefix = (usingBaseJarName() ? "" : baseName); + String dbPrefix = (dbVendor == null) ? "" : dbVendor + "-"; + + // Get the Extensions document + File websphereEXT = new File(getConfig().descriptorDir, ddPrefix + WAS_EXT); + + if (websphereEXT.exists()) { + ejbFiles.put(META_DIR + WAS_EXT, + websphereEXT); + } else { + log("Unable to locate websphere extensions. " + + "It was expected to be in " + + websphereEXT.getPath(), Project.MSG_VERBOSE); + } + + File websphereBND = new File(getConfig().descriptorDir, ddPrefix + WAS_BND); + + if (websphereBND.exists()) { + ejbFiles.put(META_DIR + WAS_BND, + websphereBND); + } else { + log("Unable to locate websphere bindings. " + + "It was expected to be in " + + websphereBND.getPath(), Project.MSG_VERBOSE); + } + + if (!newCMP) { + log("The old method for locating CMP files has been DEPRECATED.", + Project.MSG_VERBOSE); + log("Please adjust your websphere descriptor and set " + + "newCMP=\"true\" to use the new CMP descriptor " + + "inclusion mechanism. ", Project.MSG_VERBOSE); + } else { + // We attempt to put in the MAP and Schema files of CMP beans + try { + // Add the Map file + File websphereMAP = new File(getConfig().descriptorDir, + ddPrefix + dbPrefix + WAS_CMP_MAP); + + if (websphereMAP.exists()) { + ejbFiles.put(META_DIR + WAS_CMP_MAP, + websphereMAP); + } else { + log("Unable to locate the websphere Map: " + + websphereMAP.getPath(), Project.MSG_VERBOSE); + } + + File websphereSchema = new File(getConfig().descriptorDir, + ddPrefix + dbPrefix + WAS_CMP_SCHEMA); + + if (websphereSchema.exists()) { + ejbFiles.put(META_DIR + SCHEMA_DIR + WAS_CMP_SCHEMA, + websphereSchema); + } else { + log("Unable to locate the websphere Schema: " + + websphereSchema.getPath(), Project.MSG_VERBOSE); + } + // Theres nothing else to see here...keep moving sonny + } catch (Exception e) { + String msg = "Exception while adding Vendor specific files: " + + e.toString(); + + throw new BuildException(msg, e); + } + } + } + + + /** + * Get the vendor specific name of the Jar that will be output. The + * modification date of this jar will be checked against the dependent + * bean classes. + */ + File getVendorOutputJarFile(String baseName) { + return new File(getDestDir(), baseName + jarSuffix); + } + + + /** + * Gets the options for the EJB Deploy operation + * + * @return String + */ + protected String getOptions() { + // Set the options + StringBuffer options = new StringBuffer(); + + if (dbVendor != null) { + options.append(" -dbvendor ").append(dbVendor); + } + if (dbName != null) { + options.append(" -dbname \"").append(dbName).append("\""); + } + + if (dbSchema != null) { + options.append(" -dbschema \"").append(dbSchema).append("\""); + } + + if (codegen) { + options.append(" -codegen"); + } + + if (quiet) { + options.append(" -quiet"); + } + + if (novalidate) { + options.append(" -novalidate"); + } + + if (nowarn) { + options.append(" -nowarn"); + } + + if (noinform) { + options.append(" -noinform"); + } + + if (trace) { + options.append(" -trace"); + } + + if (use35MappingRules) { + options.append(" -35"); + } + + if (rmicOptions != null) { + options.append(" -rmic \"").append(rmicOptions).append("\""); + } + + return options.toString(); + } + + + /** + * Helper method invoked by execute() for each websphere jar to be built. + * Encapsulates the logic of constructing a java task for calling + * websphere.ejbdeploy and executing it. + * + * @param sourceJar java.io.File representing the source (EJB1.1) jarfile. + * @param destJar java.io.File representing the destination, websphere + * jarfile. + */ + private void buildWebsphereJar(File sourceJar, File destJar) { + try { + if (ejbdeploy) { + Java javaTask = new Java(getTask()); + // Set the JvmArgs + javaTask.createJvmarg().setValue("-Xms64m"); + javaTask.createJvmarg().setValue("-Xmx128m"); + + // Set the Environment variable + Environment.Variable var = new Environment.Variable(); + + var.setKey("websphere.lib.dir"); + File libdir = new File(websphereHome, "lib"); + var.setValue(libdir.getAbsolutePath()); + javaTask.addSysproperty(var); + + // Set the working directory + javaTask.setDir(websphereHome); + + // Set the Java class name + javaTask.setTaskName("ejbdeploy"); + javaTask.setClassname("com.ibm.etools.ejbdeploy.EJBDeploy"); + + javaTask.createArg().setValue(sourceJar.getPath()); + javaTask.createArg().setValue(tempdir); + javaTask.createArg().setValue(destJar.getPath()); + javaTask.createArg().setLine(getOptions()); + if (getCombinedClasspath() != null + && getCombinedClasspath().toString().length() > 0) { + javaTask.createArg().setValue("-cp"); + javaTask.createArg().setValue(getCombinedClasspath().toString()); + } + + Path classpath = wasClasspath; + + if (classpath == null) { + classpath = getCombinedClasspath(); + } + + javaTask.setFork(true); + if (classpath != null) { + javaTask.setClasspath(classpath); + } + + log("Calling websphere.ejbdeploy for " + sourceJar.toString(), + Project.MSG_VERBOSE); + + javaTask.execute(); + } + } catch (Exception e) { + // Have to catch this because of the semantics of calling main() + String msg = "Exception while calling ejbdeploy. Details: " + e.toString(); + + throw new BuildException(msg, e); + } + } + + /** {@inheritDoc}. */ + protected void writeJar(String baseName, File jarFile, Hashtable files, String publicId) + throws BuildException { + if (ejbdeploy) { + // create the -generic.jar, if required + File genericJarFile = super.getVendorOutputJarFile(baseName); + + super.writeJar(baseName, genericJarFile, files, publicId); + + // create the output .jar, if required + if (alwaysRebuild || isRebuildRequired(genericJarFile, jarFile)) { + buildWebsphereJar(genericJarFile, jarFile); + } + if (!keepGeneric) { + log("deleting generic jar " + genericJarFile.toString(), + Project.MSG_VERBOSE); + genericJarFile.delete(); + } + } else { + // create the "undeployed" output .jar, if required + super.writeJar(baseName, jarFile, files, publicId); + } + } + + + /** + * Called to validate that the tool parameters have been configured. + * @throws BuildException if there is an error. + */ + public void validateConfigured() throws BuildException { + super.validateConfigured(); + if (ejbdeploy) { + String home = getTask().getProject().getProperty("websphere.home"); + if (home == null) { + throw new BuildException("The 'websphere.home' property must " + + "be set when 'ejbdeploy=true'"); + } + websphereHome = getTask().getProject().resolveFile(home); + } + } + + + /** + * Helper method to check to see if a websphere EBJ1.1 jar needs to be + * rebuilt using ejbdeploy. Called from writeJar it sees if the "Bean" + * classes are the only thing that needs to be updated and either updates + * the Jar with the Bean classfile or returns true, saying that the whole + * websphere jar needs to be regened with ejbdeploy. This allows faster + * build times for working developers. <p> + * + * The way websphere ejbdeploy works is it creates wrappers for the + * publicly defined methods as they are exposed in the remote interface. + * If the actual bean changes without changing the the method signatures + * then only the bean classfile needs to be updated and the rest of the + * websphere jar file can remain the same. If the Interfaces, ie. the + * method signatures change or if the xml deployment descriptors changed, + * the whole jar needs to be rebuilt with ejbdeploy. This is not strictly + * true for the xml files. If the JNDI name changes then the jar doesn't + * have to be rebuild, but if the resources references change then it + * does. At this point the websphere jar gets rebuilt if the xml files + * change at all. + * + * @param genericJarFile java.io.File The generic jar file. + * @param websphereJarFile java.io.File The websphere jar file to check to + * see if it needs to be rebuilt. + * @return true if a rebuild is required. + */ + // CheckStyle:MethodLength OFF - this will no be fixed + protected boolean isRebuildRequired(File genericJarFile, File websphereJarFile) { + boolean rebuild = false; + + JarFile genericJar = null; + JarFile wasJar = null; + File newwasJarFile = null; + JarOutputStream newJarStream = null; + ClassLoader genericLoader = null; + + try { + log("Checking if websphere Jar needs to be rebuilt for jar " + + websphereJarFile.getName(), Project.MSG_VERBOSE); + // Only go forward if the generic and the websphere file both exist + if (genericJarFile.exists() && genericJarFile.isFile() + && websphereJarFile.exists() && websphereJarFile.isFile()) { + //open jar files + genericJar = new JarFile(genericJarFile); + wasJar = new JarFile(websphereJarFile); + + Hashtable genericEntries = new Hashtable(); + Hashtable wasEntries = new Hashtable(); + Hashtable replaceEntries = new Hashtable(); + + //get the list of generic jar entries + for (Enumeration e = genericJar.entries(); e.hasMoreElements();) { + JarEntry je = (JarEntry) e.nextElement(); + + genericEntries.put(je.getName().replace('\\', '/'), je); + } + //get the list of websphere jar entries + for (Enumeration e = wasJar.entries(); e.hasMoreElements();) { + JarEntry je = (JarEntry) e.nextElement(); + + wasEntries.put(je.getName(), je); + } + + //Cycle Through generic and make sure its in websphere + genericLoader = getClassLoaderFromJar(genericJarFile); + + for (Enumeration e = genericEntries.keys(); e.hasMoreElements();) { + String filepath = (String) e.nextElement(); + + if (wasEntries.containsKey(filepath)) { + // File name/path match + // Check files see if same + JarEntry genericEntry = (JarEntry) genericEntries.get(filepath); + JarEntry wasEntry = (JarEntry) wasEntries.get(filepath); + + if ((genericEntry.getCrc() != wasEntry.getCrc()) + || (genericEntry.getSize() != wasEntry.getSize())) { + + if (genericEntry.getName().endsWith(".class")) { + //File are different see if its an object or an interface + String classname + = genericEntry.getName().replace(File.separatorChar, '.'); + + classname = classname.substring(0, classname.lastIndexOf(".class")); + + Class genclass = genericLoader.loadClass(classname); + + if (genclass.isInterface()) { + //Interface changed rebuild jar. + log("Interface " + genclass.getName() + + " has changed", Project.MSG_VERBOSE); + rebuild = true; + break; + } else { + //Object class Changed update it. + replaceEntries.put(filepath, genericEntry); + } + } else { + // is it the manifest. If so ignore it + if (!genericEntry.getName().equals("META-INF/MANIFEST.MF")) { + //File other then class changed rebuild + log("Non class file " + genericEntry.getName() + + " has changed", Project.MSG_VERBOSE); + rebuild = true; + } + break; + } + } + } else { + // a file doesn't exist rebuild + + log("File " + filepath + " not present in websphere jar", + Project.MSG_VERBOSE); + rebuild = true; + break; + } + } + + if (!rebuild) { + log("No rebuild needed - updating jar", Project.MSG_VERBOSE); + newwasJarFile = new File(websphereJarFile.getAbsolutePath() + ".temp"); + if (newwasJarFile.exists()) { + newwasJarFile.delete(); + } + + newJarStream = new JarOutputStream(new FileOutputStream(newwasJarFile)); + newJarStream.setLevel(0); + + //Copy files from old websphere jar + for (Enumeration e = wasEntries.elements(); e.hasMoreElements();) { + byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; + int bytesRead; + InputStream is; + JarEntry je = (JarEntry) e.nextElement(); + + if (je.getCompressedSize() == -1 + || je.getCompressedSize() == je.getSize()) { + newJarStream.setLevel(0); + } else { + newJarStream.setLevel(JAR_COMPRESS_LEVEL); + } + + // Update with changed Bean class + if (replaceEntries.containsKey(je.getName())) { + log("Updating Bean class from generic Jar " + je.getName(), + Project.MSG_VERBOSE); + // Use the entry from the generic jar + je = (JarEntry) replaceEntries.get(je.getName()); + is = genericJar.getInputStream(je); + } else { + //use fle from original websphere jar + + is = wasJar.getInputStream(je); + } + newJarStream.putNextEntry(new JarEntry(je.getName())); + + while ((bytesRead = is.read(buffer)) != -1) { + newJarStream.write(buffer, 0, bytesRead); + } + is.close(); + } + } else { + log("websphere Jar rebuild needed due to changed " + + "interface or XML", Project.MSG_VERBOSE); + } + } else { + rebuild = true; + } + } catch (ClassNotFoundException cnfe) { + String cnfmsg = "ClassNotFoundException while processing ejb-jar file" + + ". Details: " + + cnfe.getMessage(); + + throw new BuildException(cnfmsg, cnfe); + } catch (IOException ioe) { + String msg = "IOException while processing ejb-jar file " + + ". Details: " + + ioe.getMessage(); + + throw new BuildException(msg, ioe); + } finally { + // need to close files and perhaps rename output + if (genericJar != null) { + try { + genericJar.close(); + } catch (IOException closeException) { + // Ignore + } + } + + if (wasJar != null) { + try { + wasJar.close(); + } catch (IOException closeException) { + // Ignore + } + } + + if (newJarStream != null) { + try { + newJarStream.close(); + } catch (IOException closeException) { + // Ignore + } + + try { + FILE_UTILS.rename(newwasJarFile, websphereJarFile); + } catch (IOException renameException) { + log(renameException.getMessage(), Project.MSG_WARN); + rebuild = true; + } + } + if (genericLoader != null + && genericLoader instanceof AntClassLoader) { + AntClassLoader loader = (AntClassLoader) genericLoader; + loader.cleanup(); + } + } + + return rebuild; + } + + + /** + * Helper method invoked by isRebuildRequired to get a ClassLoader for a + * Jar File passed to it. + * + * @param classjar java.io.File representing jar file to get classes from. + * @return a classloader for the jar file. + * @throws IOException if there is an error. + */ + protected ClassLoader getClassLoaderFromJar(File classjar) throws IOException { + Path lookupPath = new Path(getTask().getProject()); + + lookupPath.setLocation(classjar); + + Path classpath = getCombinedClasspath(); + + if (classpath != null) { + lookupPath.append(classpath); + } + + return getTask().getProject().createClassLoader(lookupPath); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatability.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatability.java new file mode 100644 index 00000000..2c06daf1 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatability.java @@ -0,0 +1,57 @@ +/* + * 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.tools.ant.taskdefs.optional.extension; + +/** + * Enum used in (@link Extension) to indicate the compatibility + * of one extension to another. See (@link Extension) for instances + * of object. + * + * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + * This file is from excalibur.extension package. Dont edit this file + * directly as there is no unit tests to make sure it is operational + * in ant. Edit file in excalibur and run tests there before changing + * ants file. + * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + * + * @see Extension + */ +public final class Compatability { + /** + * A string representation of compatibility level. + */ + private final String name; + + /** + * Create a compatibility enum with specified name. + * + * @param name the name of compatibility level + */ + Compatability(final String name) { + this.name = name; + } + + /** + * Return name of compatibility level. + * + * @return the name of compatibility level + */ + public String toString() { + return name; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatibility.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatibility.java new file mode 100644 index 00000000..bb28cd6b --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatibility.java @@ -0,0 +1,57 @@ +/* + * 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.tools.ant.taskdefs.optional.extension; + +/** + * Enum used in (@link Extension) to indicate the compatibility + * of one extension to another. See (@link Extension) for instances + * of object. + * + * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + * This file is from excalibur.extension package. Dont edit this file + * directly as there is no unit tests to make sure it is operational + * in ant. Edit file in excalibur and run tests there before changing + * ants file. + * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + * + * @see Extension + */ +public final class Compatibility { + /** + * A string representation of compatibility level. + */ + private final String name; + + /** + * Create a compatibility enum with specified name. + * + * @param name the name of compatibility level + */ + Compatibility(final String name) { + this.name = name; + } + + /** + * Return name of compatibility level. + * + * @return the name of compatibility level + */ + public String toString() { + return name; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/DeweyDecimal.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/DeweyDecimal.java new file mode 100644 index 00000000..2edc2a78 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/DeweyDecimal.java @@ -0,0 +1,54 @@ +/* + * 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.tools.ant.taskdefs.optional.extension; + + +/** + * Utility class to contain version numbers in "Dewey Decimal" + * syntax. Numbers in the "Dewey Decimal" syntax consist of positive + * decimal integers separated by periods ".". For example, "2.0" or + * "1.2.3.4.5.6.7". This allows an extensible number to be used to + * represent major, minor, micro, etc versions. The version number + * must begin with a number. + * + * Original Implementation moved to org.apache.tools.ant.util.DeweyDecimal + * @deprecated use org.apache.tools.ant.util.DeweyDecimal instead. + * Deprecated since ant 1.8 + */ +public final class DeweyDecimal extends org.apache.tools.ant.util.DeweyDecimal { + + /** + * Construct a DeweyDecimal from an array of integer components. + * + * @param components an array of integer components. + */ + public DeweyDecimal(final int[] components) { + super(components); + } + + /** + * Construct a DeweyDecimal from string in DeweyDecimal format. + * + * @param string the string in dewey decimal format + * @exception NumberFormatException if string is malformed + */ + public DeweyDecimal(final String string) + throws NumberFormatException { + super(string); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Extension.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Extension.java new file mode 100644 index 00000000..d13d2f4e --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Extension.java @@ -0,0 +1,690 @@ +/* + * 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.tools.ant.taskdefs.optional.extension; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import org.apache.tools.ant.util.DeweyDecimal; +import org.apache.tools.ant.util.StringUtils; + +/** + * <p>Utility class that represents either an available "Optional Package" + * (formerly known as "Standard Extension") as described in the manifest + * of a JAR file, or the requirement for such an optional package.</p> + * + * <p>For more information about optional packages, see the document + * <em>Optional Package Versioning</em> in the documentation bundle for your + * Java2 Standard Edition package, in file + * <code>guide/extensions/versioning.html</code>.</p> + * + */ +public final class Extension { + /** + * Manifest Attribute Name object for EXTENSION_LIST. + */ + public static final Attributes.Name EXTENSION_LIST + = new Attributes.Name("Extension-List"); + + /** + * <code>Name</code> object for <code>Optional-Extension-List</code> + * manifest attribute used for declaring optional dependencies on + * installed extensions. Note that the dependencies declared by this method + * are not required for the library to operate but if present will be used. + * It is NOT part of the official "Optional Package" specification. + * + * @see <a href="http://java.sun.com/j2se/1.3/docs/guide/extensions/spec.html#dependency"> + * Installed extension dependency</a> + */ + public static final Attributes.Name OPTIONAL_EXTENSION_LIST + = new Attributes.Name("Optional-Extension-List"); + + /** + * Manifest Attribute Name object for EXTENSION_NAME. + */ + public static final Attributes.Name EXTENSION_NAME = + new Attributes.Name("Extension-Name"); + /** + * Manifest Attribute Name object for SPECIFICATION_VERSION. + */ + public static final Attributes.Name SPECIFICATION_VERSION + = Attributes.Name.SPECIFICATION_VERSION; + + /** + * Manifest Attribute Name object for SPECIFICATION_VENDOR. + */ + public static final Attributes.Name SPECIFICATION_VENDOR + = Attributes.Name.SPECIFICATION_VENDOR; + + /** + * Manifest Attribute Name object for IMPLEMENTATION_VERSION. + */ + public static final Attributes.Name IMPLEMENTATION_VERSION + = Attributes.Name.IMPLEMENTATION_VERSION; + + /** + * Manifest Attribute Name object for IMPLEMENTATION_VENDOR. + */ + public static final Attributes.Name IMPLEMENTATION_VENDOR + = Attributes.Name.IMPLEMENTATION_VENDOR; + + /** + * Manifest Attribute Name object for IMPLEMENTATION_URL. + */ + public static final Attributes.Name IMPLEMENTATION_URL + = new Attributes.Name("Implementation-URL"); + + /** + * Manifest Attribute Name object for IMPLEMENTATION_VENDOR_ID. + */ + public static final Attributes.Name IMPLEMENTATION_VENDOR_ID + = new Attributes.Name("Implementation-Vendor-Id"); + + /** + * Enum indicating that extension is compatible with other extension. + */ + public static final Compatibility COMPATIBLE + = new Compatibility("COMPATIBLE"); + + /** + * Enum indicating that extension requires an upgrade + * of specification to be compatible with other extension. + */ + public static final Compatibility REQUIRE_SPECIFICATION_UPGRADE + = new Compatibility("REQUIRE_SPECIFICATION_UPGRADE"); + + /** + * Enum indicating that extension requires a vendor + * switch to be compatible with other extension. + */ + public static final Compatibility REQUIRE_VENDOR_SWITCH + = new Compatibility("REQUIRE_VENDOR_SWITCH"); + + /** + * Enum indicating that extension requires an upgrade + * of implementation to be compatible with other extension. + */ + public static final Compatibility REQUIRE_IMPLEMENTATION_UPGRADE + = new Compatibility("REQUIRE_IMPLEMENTATION_UPGRADE"); + + /** + * Enum indicating that extension is incompatible with + * other extension in ways other than other enums + * indicate). For example the other extension may have + * a different ID. + */ + public static final Compatibility INCOMPATIBLE + = new Compatibility("INCOMPATIBLE"); + + /** + * The name of the optional package being made available, or required. + */ + private String extensionName; + + /** + * The version number (dotted decimal notation) of the specification + * to which this optional package conforms. + */ + private DeweyDecimal specificationVersion; + + /** + * The name of the company or organization that originated the + * specification to which this optional package conforms. + */ + private String specificationVendor; + + /** + * The unique identifier of the company that produced the optional + * package contained in this JAR file. + */ + private String implementationVendorID; + + /** + * The name of the company or organization that produced this + * implementation of this optional package. + */ + private String implementationVendor; + + /** + * The version number (dotted decimal notation) for this implementation + * of the optional package. + */ + private DeweyDecimal implementationVersion; + + /** + * The URL from which the most recent version of this optional package + * can be obtained if it is not already installed. + */ + private String implementationURL; + + /** + * Return an array of <code>Extension</code> objects representing optional + * packages that are available in the JAR file associated with the + * specified <code>Manifest</code>. If there are no such optional + * packages, a zero-length array is returned. + * + * @param manifest Manifest to be parsed + * @return the "available" extensions in specified manifest + */ + public static Extension[] getAvailable(final Manifest manifest) { + if (null == manifest) { + return new Extension[ 0 ]; + } + + final ArrayList results = new ArrayList(); + + final Attributes mainAttributes = manifest.getMainAttributes(); + if (null != mainAttributes) { + final Extension extension = getExtension("", mainAttributes); + if (null != extension) { + results.add(extension); + } + } + + final Map entries = manifest.getEntries(); + final Iterator keys = entries.keySet().iterator(); + while (keys.hasNext()) { + final String key = (String) keys.next(); + final Attributes attributes = (Attributes) entries.get(key); + final Extension extension = getExtension("", attributes); + if (null != extension) { + results.add(extension); + } + } + + return (Extension[]) results.toArray(new Extension[results.size()]); + } + + /** + * Return the set of <code>Extension</code> objects representing optional + * packages that are required by the application contained in the JAR + * file associated with the specified <code>Manifest</code>. If there + * are no such optional packages, a zero-length list is returned. + * + * @param manifest Manifest to be parsed + * @return the dependencies that are specified in manifest + */ + public static Extension[] getRequired(final Manifest manifest) { + return getListed(manifest, Attributes.Name.EXTENSION_LIST); + } + + /** + * Return the set of <code>Extension</code> objects representing "Optional + * Packages" that the application declares they will use if present. If + * there are no such optional packages, a zero-length list is returned. + * + * @param manifest Manifest to be parsed + * @return the optional dependencies that are specified in manifest + */ + public static Extension[] getOptions(final Manifest manifest) { + return getListed(manifest, OPTIONAL_EXTENSION_LIST); + } + + /** + * Add Extension to the specified manifest Attributes. + * + * @param attributes the attributes of manifest to add to + * @param extension the extension + */ + public static void addExtension(final Extension extension, + final Attributes attributes) { + addExtension(extension, "", attributes); + } + + /** + * Add Extension to the specified manifest Attributes. + * Use the specified prefix so that dependencies can added + * with a prefix such as "java3d-" etc. + * + * @param attributes the attributes of manifest to add to + * @param extension the extension + * @param prefix the name to prefix to extension + */ + public static void addExtension(final Extension extension, + final String prefix, + final Attributes attributes) { + attributes.putValue(prefix + EXTENSION_NAME, + extension.getExtensionName()); + + final String specificationVendor = extension.getSpecificationVendor(); + if (null != specificationVendor) { + attributes.putValue(prefix + SPECIFICATION_VENDOR, + specificationVendor); + } + + final DeweyDecimal specificationVersion + = extension.getSpecificationVersion(); + if (null != specificationVersion) { + attributes.putValue(prefix + SPECIFICATION_VERSION, + specificationVersion.toString()); + } + + final String implementationVendorID + = extension.getImplementationVendorID(); + if (null != implementationVendorID) { + attributes.putValue(prefix + IMPLEMENTATION_VENDOR_ID, + implementationVendorID); + } + + final String implementationVendor = extension.getImplementationVendor(); + if (null != implementationVendor) { + attributes.putValue(prefix + IMPLEMENTATION_VENDOR, + implementationVendor); + } + + final DeweyDecimal implementationVersion + = extension.getImplementationVersion(); + if (null != implementationVersion) { + attributes.putValue(prefix + IMPLEMENTATION_VERSION, + implementationVersion.toString()); + } + + final String implementationURL = extension.getImplementationURL(); + if (null != implementationURL) { + attributes.putValue(prefix + IMPLEMENTATION_URL, + implementationURL); + } + } + + /** + * The constructor to create Extension object. + * Note that every component is allowed to be specified + * but only the extensionName is mandatory. + * + * @param extensionName the name of extension. + * @param specificationVersion the specification Version of extension. + * @param specificationVendor the specification Vendor of extension. + * @param implementationVersion the implementation Version of extension. + * @param implementationVendor the implementation Vendor of extension. + * @param implementationVendorId the implementation VendorId of extension. + * @param implementationURL the implementation URL of extension. + */ + public Extension(final String extensionName, + final String specificationVersion, + final String specificationVendor, + final String implementationVersion, + final String implementationVendor, + final String implementationVendorId, + final String implementationURL) { + this.extensionName = extensionName; + this.specificationVendor = specificationVendor; + + if (null != specificationVersion) { + try { + this.specificationVersion + = new DeweyDecimal(specificationVersion); + } catch (final NumberFormatException nfe) { + final String error = "Bad specification version format '" + + specificationVersion + "' in '" + extensionName + + "'. (Reason: " + nfe + ")"; + throw new IllegalArgumentException(error); + } + } + + this.implementationURL = implementationURL; + this.implementationVendor = implementationVendor; + this.implementationVendorID = implementationVendorId; + + if (null != implementationVersion) { + try { + this.implementationVersion + = new DeweyDecimal(implementationVersion); + } catch (final NumberFormatException nfe) { + final String error = "Bad implementation version format '" + + implementationVersion + "' in '" + extensionName + + "'. (Reason: " + nfe + ")"; + throw new IllegalArgumentException(error); + } + } + + if (null == this.extensionName) { + throw new NullPointerException("extensionName property is null"); + } + } + + /** + * Get the name of the extension. + * + * @return the name of the extension + */ + public String getExtensionName() { + return extensionName; + } + + /** + * Get the vendor of the extensions specification. + * + * @return the vendor of the extensions specification. + */ + public String getSpecificationVendor() { + return specificationVendor; + } + + /** + * Get the version of the extensions specification. + * + * @return the version of the extensions specification. + */ + public DeweyDecimal getSpecificationVersion() { + return specificationVersion; + } + + /** + * Get the url of the extensions implementation. + * + * @return the url of the extensions implementation. + */ + public String getImplementationURL() { + return implementationURL; + } + + /** + * Get the vendor of the extensions implementation. + * + * @return the vendor of the extensions implementation. + */ + public String getImplementationVendor() { + return implementationVendor; + } + + /** + * Get the vendorID of the extensions implementation. + * + * @return the vendorID of the extensions implementation. + */ + public String getImplementationVendorID() { + return implementationVendorID; + } + + /** + * Get the version of the extensions implementation. + * + * @return the version of the extensions implementation. + */ + public DeweyDecimal getImplementationVersion() { + return implementationVersion; + } + + /** + * Return a Compatibility enum indicating the relationship of this + * <code>Extension</code> with the specified <code>Extension</code>. + * + * @param required Description of the required optional package + * @return the enum indicating the compatibility (or lack thereof) + * of specified extension + */ + public Compatibility getCompatibilityWith(final Extension required) { + // Extension Name must match + if (!extensionName.equals(required.getExtensionName())) { + return INCOMPATIBLE; + } + + // Available specification version must be >= required + final DeweyDecimal requiredSpecificationVersion + = required.getSpecificationVersion(); + if (null != requiredSpecificationVersion) { + if (null == specificationVersion + || !isCompatible(specificationVersion, requiredSpecificationVersion)) { + return REQUIRE_SPECIFICATION_UPGRADE; + } + } + + // Implementation Vendor ID must match + final String requiredImplementationVendorID + = required.getImplementationVendorID(); + if (null != requiredImplementationVendorID) { + if (null == implementationVendorID + || !implementationVendorID.equals(requiredImplementationVendorID)) { + return REQUIRE_VENDOR_SWITCH; + } + } + + // Implementation version must be >= required + final DeweyDecimal requiredImplementationVersion + = required.getImplementationVersion(); + if (null != requiredImplementationVersion) { + if (null == implementationVersion + || !isCompatible(implementationVersion, requiredImplementationVersion)) { + return REQUIRE_IMPLEMENTATION_UPGRADE; + } + } + + // This available optional package satisfies the requirements + return COMPATIBLE; + } + + /** + * Return <code>true</code> if the specified <code>Extension</code> + * (which represents an optional package required by an application) + * is satisfied by this <code>Extension</code> (which represents an + * optional package that is already installed. Otherwise, return + * <code>false</code>. + * + * @param required Description of the required optional package + * @return true if the specified extension is compatible with this extension + */ + public boolean isCompatibleWith(final Extension required) { + return (COMPATIBLE == getCompatibilityWith(required)); + } + + /** + * Return a String representation of this object. + * + * @return string representation of object. + */ + public String toString() { + final String brace = ": "; + + final StringBuffer sb = new StringBuffer(EXTENSION_NAME.toString()); + sb.append(brace); + sb.append(extensionName); + sb.append(StringUtils.LINE_SEP); + + if (null != specificationVersion) { + sb.append(SPECIFICATION_VERSION); + sb.append(brace); + sb.append(specificationVersion); + sb.append(StringUtils.LINE_SEP); + } + + if (null != specificationVendor) { + sb.append(SPECIFICATION_VENDOR); + sb.append(brace); + sb.append(specificationVendor); + sb.append(StringUtils.LINE_SEP); + } + + if (null != implementationVersion) { + sb.append(IMPLEMENTATION_VERSION); + sb.append(brace); + sb.append(implementationVersion); + sb.append(StringUtils.LINE_SEP); + } + + if (null != implementationVendorID) { + sb.append(IMPLEMENTATION_VENDOR_ID); + sb.append(brace); + sb.append(implementationVendorID); + sb.append(StringUtils.LINE_SEP); + } + + if (null != implementationVendor) { + sb.append(IMPLEMENTATION_VENDOR); + sb.append(brace); + sb.append(implementationVendor); + sb.append(StringUtils.LINE_SEP); + } + + if (null != implementationURL) { + sb.append(IMPLEMENTATION_URL); + sb.append(brace); + sb.append(implementationURL); + sb.append(StringUtils.LINE_SEP); + } + + return sb.toString(); + } + + /** + * Return <code>true</code> if the first version number is greater than + * or equal to the second; otherwise return <code>false</code>. + * + * @param first First version number (dotted decimal) + * @param second Second version number (dotted decimal) + */ + private boolean isCompatible(final DeweyDecimal first, + final DeweyDecimal second) { + return first.isGreaterThanOrEqual(second); + } + + /** + * Retrieve all the extensions listed under a particular key + * (Usually EXTENSION_LIST or OPTIONAL_EXTENSION_LIST). + * + * @param manifest the manifest to extract extensions from + * @param listKey the key used to get list (Usually + * EXTENSION_LIST or OPTIONAL_EXTENSION_LIST) + * @return the list of listed extensions + */ + private static Extension[] getListed(final Manifest manifest, + final Attributes.Name listKey) { + final ArrayList results = new ArrayList(); + final Attributes mainAttributes = manifest.getMainAttributes(); + + if (null != mainAttributes) { + getExtension(mainAttributes, results, listKey); + } + + final Map entries = manifest.getEntries(); + final Iterator keys = entries.keySet().iterator(); + while (keys.hasNext()) { + final String key = (String) keys.next(); + final Attributes attributes = (Attributes) entries.get(key); + getExtension(attributes, results, listKey); + } + + return (Extension[]) results.toArray(new Extension[results.size()]); + } + + /** + * Add required optional packages defined in the specified + * attributes entry, if any. + * + * @param attributes Attributes to be parsed + * @param required list to add required optional packages to + * @param listKey the key to use to lookup list, usually EXTENSION_LIST + * or OPTIONAL_EXTENSION_LIST + */ + private static void getExtension(final Attributes attributes, + final ArrayList required, + final Attributes.Name listKey) { + final String names = attributes.getValue(listKey); + if (null == names) { + return; + } + + final String[] extensions = split(names, " "); + for (int i = 0; i < extensions.length; i++) { + final String prefix = extensions[ i ] + "-"; + final Extension extension = getExtension(prefix, attributes); + + if (null != extension) { + required.add(extension); + } + } + } + + /** + * Splits the string on every token into an array of strings. + * + * @param string the string + * @param onToken the token + * @return the resultant array + */ + private static String[] split(final String string, + final String onToken) { + final StringTokenizer tokenizer = new StringTokenizer(string, onToken); + final String[] result = new String[ tokenizer.countTokens() ]; + + for (int i = 0; i < result.length; i++) { + result[ i ] = tokenizer.nextToken(); + } + + return result; + } + + /** + * Extract an Extension from Attributes. + * Prefix indicates the prefix checked for each string. + * Usually the prefix is <em>"<extension>-"</em> if looking for a + * <b>Required</b> extension. If you are looking for an + * <b>Available</b> extension + * then the prefix is <em>""</em>. + * + * @param prefix the prefix for each attribute name + * @param attributes Attributes to searched + * @return the new Extension object, or null + */ + private static Extension getExtension(final String prefix, + final Attributes attributes) { + //WARNING: We trim the values of all the attributes because + //Some extension declarations are badly defined (ie have spaces + //after version or vendorID) + final String nameKey = prefix + EXTENSION_NAME; + final String name = getTrimmedString(attributes.getValue(nameKey)); + if (null == name) { + return null; + } + + final String specVendorKey = prefix + SPECIFICATION_VENDOR; + final String specVendor + = getTrimmedString(attributes.getValue(specVendorKey)); + final String specVersionKey = prefix + SPECIFICATION_VERSION; + final String specVersion + = getTrimmedString(attributes.getValue(specVersionKey)); + + final String impVersionKey = prefix + IMPLEMENTATION_VERSION; + final String impVersion + = getTrimmedString(attributes.getValue(impVersionKey)); + final String impVendorKey = prefix + IMPLEMENTATION_VENDOR; + final String impVendor + = getTrimmedString(attributes.getValue(impVendorKey)); + final String impVendorIDKey = prefix + IMPLEMENTATION_VENDOR_ID; + final String impVendorId + = getTrimmedString(attributes.getValue(impVendorIDKey)); + final String impURLKey = prefix + IMPLEMENTATION_URL; + final String impURL = getTrimmedString(attributes.getValue(impURLKey)); + + return new Extension(name, specVersion, specVendor, impVersion, + impVendor, impVendorId, impURL); + } + + /** + * Trim the supplied string if the string is non-null + * + * @param value the string to trim or null + * @return the trimmed string or null + */ + private static String getTrimmedString(final String value) { + return null == value ? null : value.trim(); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionAdapter.java new file mode 100644 index 00000000..b3cfddc2 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionAdapter.java @@ -0,0 +1,215 @@ +/* + * 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.tools.ant.taskdefs.optional.extension; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.types.DataType; +import org.apache.tools.ant.types.Reference; +import org.apache.tools.ant.util.DeweyDecimal; + +/** + * Simple class that represents an Extension and conforms to Ants + * patterns. + * + * @ant.datatype name="extension" + */ +public class ExtensionAdapter extends DataType { + /** + * The name of the optional package being made available, or required. + */ + private String extensionName; + + /** + * The version number (dotted decimal notation) of the specification + * to which this optional package conforms. + */ + private DeweyDecimal specificationVersion; + + /** + * The name of the company or organization that originated the + * specification to which this optional package conforms. + */ + private String specificationVendor; + + /** + * The unique identifier of the company that produced the optional + * package contained in this JAR file. + */ + private String implementationVendorID; + + /** + * The name of the company or organization that produced this + * implementation of this optional package. + */ + private String implementationVendor; + + /** + * The version number (dotted decimal notation) for this implementation + * of the optional package. + */ + private DeweyDecimal implementationVersion; + + /** + * The URL from which the most recent version of this optional package + * can be obtained if it is not already installed. + */ + private String implementationURL; + + /** + * Set the name of extension. + * + * @param extensionName the name of extension + */ + public void setExtensionName(final String extensionName) { + verifyNotAReference(); + this.extensionName = extensionName; + } + + /** + * Set the specificationVersion of extension. + * + * @param specificationVersion the specificationVersion of extension + */ + public void setSpecificationVersion(final String specificationVersion) { + verifyNotAReference(); + this.specificationVersion = new DeweyDecimal(specificationVersion); + } + + /** + * Set the specificationVendor of extension. + * + * @param specificationVendor the specificationVendor of extension + */ + public void setSpecificationVendor(final String specificationVendor) { + verifyNotAReference(); + this.specificationVendor = specificationVendor; + } + + /** + * Set the implementationVendorID of extension. + * + * @param implementationVendorID the implementationVendorID of extension + */ + public void setImplementationVendorId(final String implementationVendorID) { + verifyNotAReference(); + this.implementationVendorID = implementationVendorID; + } + + /** + * Set the implementationVendor of extension. + * + * @param implementationVendor the implementationVendor of extension + */ + public void setImplementationVendor(final String implementationVendor) { + verifyNotAReference(); + this.implementationVendor = implementationVendor; + } + + /** + * Set the implementationVersion of extension. + * + * @param implementationVersion the implementationVersion of extension + */ + public void setImplementationVersion(final String implementationVersion) { + verifyNotAReference(); + this.implementationVersion = new DeweyDecimal(implementationVersion); + } + + /** + * Set the implementationURL of extension. + * + * @param implementationURL the implementationURL of extension + */ + public void setImplementationUrl(final String implementationURL) { + verifyNotAReference(); + this.implementationURL = implementationURL; + } + + /** + * Makes this instance in effect a reference to another ExtensionAdapter + * instance. + * + * <p>You must not set another attribute or nest elements inside + * this element if you make it a reference.</p> + * + * @param reference the reference to which this instance is associated + * @exception BuildException if this instance already has been configured. + */ + public void setRefid(final Reference reference) + throws BuildException { + if (null != extensionName + || null != specificationVersion + || null != specificationVendor + || null != implementationVersion + || null != implementationVendorID + || null != implementationVendor + || null != implementationURL) { + throw tooManyAttributes(); + } + super.setRefid(reference); + } + + private void verifyNotAReference() + throws BuildException { + if (isReference()) { + throw tooManyAttributes(); + } + } + + /** + * Convert this adpater object into an extension object. + * + * @return the extension object + */ + Extension toExtension() + throws BuildException { + if (isReference()) { + return ((ExtensionAdapter) getCheckedRef()).toExtension(); + } + dieOnCircularReference(); + if (null == extensionName) { + final String message = "Extension is missing name."; + throw new BuildException(message); + } + + String specificationVersionString = null; + if (null != specificationVersion) { + specificationVersionString = specificationVersion.toString(); + } + String implementationVersionString = null; + if (null != implementationVersion) { + implementationVersionString = implementationVersion.toString(); + } + return new Extension(extensionName, + specificationVersionString, + specificationVendor, + implementationVersionString, + implementationVendor, + implementationVendorID, + implementationURL); + } + + /** + * a debug toString method. + * @return the extension in a string. + * @see java.lang.Object#toString() + */ + public String toString() { + return "{" + toExtension().toString() + "}"; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionResolver.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionResolver.java new file mode 100644 index 00000000..a73282ec --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionResolver.java @@ -0,0 +1,43 @@ +/* + * 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.tools.ant.taskdefs.optional.extension; + +import java.io.File; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; + +/** + * Interface to locate a File that satisfies extension. + * + */ +public interface ExtensionResolver { + /** + * Attempt to locate File that satisfies + * extension via resolver. + * + * @param extension the extension + * @param project the Ant project instance + * @return the File satisfying extension, null + * if can not resolve extension + * @throws BuildException if error occurs attempting to + * resolve extension + */ + File resolve(Extension extension, Project project) + throws BuildException; +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionSet.java new file mode 100644 index 00000000..5aba37c9 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionSet.java @@ -0,0 +1,153 @@ +/* + * 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.tools.ant.taskdefs.optional.extension; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Stack; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.types.DataType; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Reference; + +/** + * The Extension set lists a set of "Optional Packages" / + * "Extensions". + * + * @ant.datatype name="extension-set" + */ +public class ExtensionSet + extends DataType { + /** + * ExtensionAdapter objects representing extensions. + */ + private final ArrayList extensions = new ArrayList(); + + /** + * Filesets specifying all the extensions wanted. + */ + private final ArrayList extensionsFilesets = new ArrayList(); + + /** + * Adds an extension that this library requires. + * + * @param extensionAdapter an extension that this library requires. + */ + public void addExtension(final ExtensionAdapter extensionAdapter) { + if (isReference()) { + throw noChildrenAllowed(); + } + setChecked(false); + extensions.add(extensionAdapter); + } + + /** + * Adds a set of files about which extensions data will be extracted. + * + * @param fileSet a set of files about which extensions data will be extracted. + */ + public void addLibfileset(final LibFileSet fileSet) { + if (isReference()) { + throw noChildrenAllowed(); + } + setChecked(false); + extensionsFilesets.add(fileSet); + } + + /** + * Adds a set of files about which extensions data will be extracted. + * + * @param fileSet a set of files about which extensions data will be extracted. + */ + public void addFileset(final FileSet fileSet) { + if (isReference()) { + throw noChildrenAllowed(); + } + setChecked(false); + extensionsFilesets.add(fileSet); + } + + /** + * Extract a set of Extension objects from the ExtensionSet. + * + * @param proj the project instance. + * @return an array containing the Extensions from this set + * @throws BuildException if an error occurs + */ + public Extension[] toExtensions(final Project proj) + throws BuildException { + if (isReference()) { + return ((ExtensionSet) getCheckedRef()).toExtensions(proj); + } + dieOnCircularReference(); + final ArrayList extensionsList = ExtensionUtil.toExtensions(extensions); + ExtensionUtil.extractExtensions(proj, extensionsList, extensionsFilesets); + return (Extension[]) extensionsList.toArray(new Extension[extensionsList.size()]); + } + + /** + * Makes this instance in effect a reference to another ExtensionSet + * instance. + * + * <p>You must not set another attribute or nest elements inside + * this element if you make it a reference.</p> + * + * @param reference the reference to which this instance is associated + * @exception BuildException if this instance already has been configured. + */ + @Override + public void setRefid(final Reference reference) + throws BuildException { + if (!extensions.isEmpty() || !extensionsFilesets.isEmpty()) { + throw tooManyAttributes(); + } + super.setRefid(reference); + } + + @Override + protected synchronized void dieOnCircularReference(Stack stk, Project p) + throws BuildException { + if (isChecked()) { + return; + } + if (isReference()) { + super.dieOnCircularReference(stk, p); + } else { + for (Iterator i = extensions.iterator(); i.hasNext();) { + pushAndInvokeCircularReferenceCheck((ExtensionAdapter) i.next(), + stk, p); + } + for (Iterator i = extensionsFilesets.iterator(); i.hasNext();) { + pushAndInvokeCircularReferenceCheck((FileSet) i.next(), stk, p); + } + setChecked(true); + } + } + + /** + * @see java.lang.Object#toString() + * @return the extensions in a string. + */ + @Override + public String toString() { + return "ExtensionSet" + Arrays.asList(toExtensions(getProject())); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionUtil.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionUtil.java new file mode 100644 index 00000000..089c7894 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionUtil.java @@ -0,0 +1,215 @@ +/* + * 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.tools.ant.taskdefs.optional.extension; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.types.FileSet; + +/** + * A set of useful methods relating to extensions. + * + */ +public final class ExtensionUtil { + /** + * Class is not meant to be instantiated. + */ + private ExtensionUtil() { + //all methods static + } + + /** + * Convert a list of extensionAdapter objects to extensions. + * + * @param adapters the list of ExtensionAdapterss to add to convert + * @throws BuildException if an error occurs + */ + static ArrayList toExtensions(final List adapters) + throws BuildException { + final ArrayList results = new ArrayList(); + + final int size = adapters.size(); + for (int i = 0; i < size; i++) { + final ExtensionAdapter adapter = + (ExtensionAdapter) adapters.get(i); + final Extension extension = adapter.toExtension(); + results.add(extension); + } + + return results; + } + + /** + * Generate a list of extensions from a specified fileset. + * + * @param libraries the list to add extensions to + * @param fileset the filesets containing librarys + * @throws BuildException if an error occurs + */ + static void extractExtensions(final Project project, + final List libraries, + final List fileset) + throws BuildException { + if (!fileset.isEmpty()) { + final Extension[] extensions = getExtensions(project, + fileset); + for (int i = 0; i < extensions.length; i++) { + libraries.add(extensions[ i ]); + } + } + } + + /** + * Retrieve extensions from the specified libraries. + * + * @param libraries the filesets for libraries + * @return the extensions contained in libraries + * @throws BuildException if failing to scan libraries + */ + private static Extension[] getExtensions(final Project project, + final List libraries) + throws BuildException { + final ArrayList extensions = new ArrayList(); + final Iterator iterator = libraries.iterator(); + while (iterator.hasNext()) { + final FileSet fileSet = (FileSet) iterator.next(); + + boolean includeImpl = true; + boolean includeURL = true; + + if (fileSet instanceof LibFileSet) { + LibFileSet libFileSet = (LibFileSet) fileSet; + includeImpl = libFileSet.isIncludeImpl(); + includeURL = libFileSet.isIncludeURL(); + } + + final DirectoryScanner scanner = fileSet.getDirectoryScanner(project); + final File basedir = scanner.getBasedir(); + final String[] files = scanner.getIncludedFiles(); + for (int i = 0; i < files.length; i++) { + final File file = new File(basedir, files[ i ]); + loadExtensions(file, extensions, includeImpl, includeURL); + } + } + return (Extension[]) extensions.toArray(new Extension[extensions.size()]); + } + + /** + * Load list of available extensions from specified file. + * + * @param file the file + * @param extensionList the list to add available extensions to + * @throws BuildException if there is an error + */ + private static void loadExtensions(final File file, + final List extensionList, + final boolean includeImpl, + final boolean includeURL) + throws BuildException { + try { + final JarFile jarFile = new JarFile(file); + final Extension[] extensions = + Extension.getAvailable(jarFile.getManifest()); + for (int i = 0; i < extensions.length; i++) { + final Extension extension = extensions[ i ]; + addExtension(extensionList, extension, includeImpl, includeURL); + } + } catch (final Exception e) { + throw new BuildException(e.getMessage(), e); + } + } + + /** + * Add extension to list. + * If extension should not have implementation details but + * does strip them. If extension should not have url but does + * then strip it. + * + * @param extensionList the list of extensions to add to + * @param originalExtension the extension + * @param includeImpl false to exclude implementation details + * @param includeURL false to exclude implementation URL + */ + private static void addExtension(final List extensionList, + final Extension originalExtension, + final boolean includeImpl, + final boolean includeURL) { + Extension extension = originalExtension; + if (!includeURL + && null != extension.getImplementationURL()) { + extension = + new Extension(extension.getExtensionName(), + extension.getSpecificationVersion().toString(), + extension.getSpecificationVendor(), + extension.getImplementationVersion().toString(), + extension.getImplementationVendor(), + extension.getImplementationVendorID(), + null); + } + + final boolean hasImplAttributes = + null != extension.getImplementationURL() + || null != extension.getImplementationVersion() + || null != extension.getImplementationVendorID() + || null != extension.getImplementationVendor(); + + if (!includeImpl && hasImplAttributes) { + extension = + new Extension(extension.getExtensionName(), + extension.getSpecificationVersion().toString(), + extension.getSpecificationVendor(), + null, + null, + null, + extension.getImplementationURL()); + } + + extensionList.add(extension); + } + + /** + * Retrieve manifest for specified file. + * + * @param file the file + * @return the manifest + * @throws BuildException if errror occurs (file doesn't exist, + * file not a jar, manifest doesn't exist in file) + */ + static Manifest getManifest(final File file) + throws BuildException { + try { + final JarFile jarFile = new JarFile(file); + Manifest m = jarFile.getManifest(); + if (m == null) { + throw new BuildException(file + " doesn't have a MANIFEST"); + } + return m; + } catch (final IOException ioe) { + throw new BuildException(ioe.getMessage(), ioe); + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtraAttribute.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtraAttribute.java new file mode 100644 index 00000000..d52bec41 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtraAttribute.java @@ -0,0 +1,83 @@ +/* + * 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.tools.ant.taskdefs.optional.extension; + +import org.apache.tools.ant.BuildException; + +/** + * Simple holder for extra attributes in main section of manifest. + * + * @todo Refactor this and all the other parameter, sysproperty, + * property etc into a single class in framework + */ +public class ExtraAttribute { + private String name; + private String value; + + /** + * Set the name of the parameter. + * + * @param name the name of parameter + */ + public void setName(final String name) { + this.name = name; + } + + /** + * Set the value of the parameter. + * + * @param value the parameter value + */ + public void setValue(final String value) { + this.value = value; + } + + /** + * Retrieve name of parameter. + * + * @return the name of parameter. + */ + String getName() { + return name; + } + + /** + * Retrieve the value of parameter. + * + * @return the value of parameter. + */ + String getValue() { + return value; + } + + /** + * Make sure that neither the name or the value + * is null. + * + * @throws BuildException if the attribute is invalid. + */ + public void validate() throws BuildException { + if (null == name) { + final String message = "Missing name from parameter."; + throw new BuildException(message); + } else if (null == value) { + final String message = "Missing value from parameter " + name + "."; + throw new BuildException(message); + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibAvailableTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibAvailableTask.java new file mode 100644 index 00000000..cebcf0d5 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibAvailableTask.java @@ -0,0 +1,157 @@ +/* + * 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.tools.ant.taskdefs.optional.extension; + +import java.io.File; +import java.util.Iterator; +import java.util.Vector; +import java.util.jar.Manifest; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; + +/** + * Checks whether an extension is present in a fileset or an extensionSet. + * + * @ant.task name="jarlib-available" + */ +public class JarLibAvailableTask extends Task { + /** + * The library to display information about. + */ + private File libraryFile; + + /** + * Filesets specifying all the librarys + * to display information about. + */ + private final Vector extensionFileSets = new Vector(); + + /** + * The name of the property to set if extension is available. + */ + private String propertyName; + + /** + * The extension that is required. + */ + private ExtensionAdapter requiredExtension; + + /** + * The name of property to set if extensions are available. + * + * @param property The name of property to set if extensions is available. + */ + public void setProperty(final String property) { + this.propertyName = property; + } + + /** + * The JAR library to check. + * + * @param file The jar library to check. + */ + public void setFile(final File file) { + this.libraryFile = file; + } + + /** + * Set the Extension looking for. + * + * @param extension Set the Extension looking for. + */ + public void addConfiguredExtension(final ExtensionAdapter extension) { + if (null != requiredExtension) { + final String message = "Can not specify extension to " + + "search for multiple times."; + throw new BuildException(message); + } + requiredExtension = extension; + } + + /** + * Adds a set of extensions to search in. + * + * @param extensionSet a set of extensions to search in. + */ + public void addConfiguredExtensionSet(final ExtensionSet extensionSet) { + extensionFileSets.addElement(extensionSet); + } + + /** + * Execute the task. + * + * @throws BuildException if something goes wrong. + */ + public void execute() throws BuildException { + validate(); + + final Extension test = requiredExtension.toExtension(); + + // Check if list of files to check has been specified + if (!extensionFileSets.isEmpty()) { + final Iterator iterator = extensionFileSets.iterator(); + while (iterator.hasNext()) { + final ExtensionSet extensionSet + = (ExtensionSet) iterator.next(); + final Extension[] extensions = + extensionSet.toExtensions(getProject()); + for (int i = 0; i < extensions.length; i++) { + final Extension extension = extensions[ i ]; + if (extension.isCompatibleWith(test)) { + getProject().setNewProperty(propertyName, "true"); + } + } + } + } else { + final Manifest manifest = ExtensionUtil.getManifest(libraryFile); + final Extension[] extensions = Extension.getAvailable(manifest); + for (int i = 0; i < extensions.length; i++) { + final Extension extension = extensions[ i ]; + if (extension.isCompatibleWith(test)) { + getProject().setNewProperty(propertyName, "true"); + } + } + } + } + + /** + * Validate the tasks parameters. + * + * @throws BuildException if invalid parameters found + */ + private void validate() throws BuildException { + if (null == requiredExtension) { + final String message = "Extension element must be specified."; + throw new BuildException(message); + } + + if (null == libraryFile && extensionFileSets.isEmpty()) { + final String message = "File attribute not specified."; + throw new BuildException(message); + } + if (null != libraryFile && !libraryFile.exists()) { + final String message = "File '" + libraryFile + "' does not exist."; + throw new BuildException(message); + } + if (null != libraryFile && !libraryFile.isFile()) { + final String message = "\'" + libraryFile + "\' is not a file."; + throw new BuildException(message); + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibDisplayTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibDisplayTask.java new file mode 100644 index 00000000..da12cd02 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibDisplayTask.java @@ -0,0 +1,119 @@ +/* + * 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.tools.ant.taskdefs.optional.extension; + +import java.io.File; +import java.util.Iterator; +import java.util.Vector; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.FileSet; + +/** + * Displays the "Optional Package" and "Package Specification" information + * contained within the specified JARs. + * + * <p>Prior to JDK1.3, an "Optional Package" was known as an Extension. + * The specification for this mechanism is available in the JDK1.3 + * documentation in the directory + * $JDK_HOME/docs/guide/extensions/versioning.html. Alternatively it is + * available online at <a href="http://java.sun.com/j2se/1.3/docs/guide/extensions/versioning.html"> + * http://java.sun.com/j2se/1.3/docs/guide/extensions/versioning.html</a>.</p> + * + * @ant.task name="jarlib-display" + */ +public class JarLibDisplayTask extends Task { + /** + * The library to display information about. + */ + private File libraryFile; + + /** + * Filesets specifying all the librarys + * to display information about. + */ + private final Vector libraryFileSets = new Vector(); + + /** + * The JAR library to display information for. + * + * @param file The jar library to display information for. + */ + public void setFile(final File file) { + this.libraryFile = file; + } + + /** + * Adds a set of files about which library data will be displayed. + * + * @param fileSet a set of files about which library data will be displayed. + */ + public void addFileset(final FileSet fileSet) { + libraryFileSets.addElement(fileSet); + } + + /** + * Execute the task. + * + * @throws BuildException if the task fails. + */ + public void execute() throws BuildException { + validate(); + + final LibraryDisplayer displayer = new LibraryDisplayer(); + // Check if list of files to check has been specified + if (!libraryFileSets.isEmpty()) { + final Iterator iterator = libraryFileSets.iterator(); + while (iterator.hasNext()) { + final FileSet fileSet = (FileSet) iterator.next(); + final DirectoryScanner scanner + = fileSet.getDirectoryScanner(getProject()); + final File basedir = scanner.getBasedir(); + final String[] files = scanner.getIncludedFiles(); + for (int i = 0; i < files.length; i++) { + final File file = new File(basedir, files[ i ]); + displayer.displayLibrary(file); + } + } + } else { + displayer.displayLibrary(libraryFile); + } + } + + /** + * Validate the tasks parameters. + * + * @throws BuildException if invalid parameters found + */ + private void validate() throws BuildException { + if (null == libraryFile && libraryFileSets.isEmpty()) { + final String message = "File attribute not specified."; + throw new BuildException(message); + } + if (null != libraryFile && !libraryFile.exists()) { + final String message = "File '" + libraryFile + "' does not exist."; + throw new BuildException(message); + } + if (null != libraryFile && !libraryFile.isFile()) { + final String message = "\'" + libraryFile + "\' is not a file."; + throw new BuildException(message); + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibManifestTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibManifestTask.java new file mode 100644 index 00000000..5afc57f1 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibManifestTask.java @@ -0,0 +1,296 @@ +/* + * 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.tools.ant.taskdefs.optional.extension; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.MagicNames; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; + +/** + * Generates a manifest that declares all the dependencies. + * The dependencies are determined by looking in the + * specified path and searching for Extension / "Optional Package" + * specifications in the manifests of the jars. + * + * <p>Prior to JDK1.3, an "Optional Package" was known as an Extension. + * The specification for this mechanism is available in the JDK1.3 + * documentation in the directory + * $JDK_HOME/docs/guide/extensions/versioning.html. Alternatively it is + * available online at <a href="http://java.sun.com/j2se/1.3/docs/guide/extensions/versioning.html"> + * http://java.sun.com/j2se/1.3/docs/guide/extensions/versioning.html</a>.</p> + * + * @ant.task name="jarlib-manifest" + */ +public final class JarLibManifestTask extends Task { + /** + * Version of manifest spec that task generates. + */ + private static final String MANIFEST_VERSION = "1.0"; + + /** + * "Created-By" string used when creating manifest. + */ + private static final String CREATED_BY = "Created-By"; + + /** + * The library to display information about. + */ + private File destFile; + + /** + * The extension supported by this library (if any). + */ + private Extension extension; + + /** + * ExtensionAdapter objects representing + * dependencies required by library. + */ + private final ArrayList dependencies = new ArrayList(); + + /** + * ExtensionAdapter objects representing optional + * dependencies required by library. + */ + private final ArrayList optionals = new ArrayList(); + + /** + * Extra attributes the user specifies for main section + * in manifest. + */ + private final ArrayList extraAttributes = new ArrayList(); + + /** + * The location where generated manifest is placed. + * + * @param destFile The location where generated manifest is placed. + */ + public void setDestfile(final File destFile) { + this.destFile = destFile; + } + + /** + * Adds an extension that this library implements. + * + * @param extensionAdapter an extension that this library implements. + * + * @throws BuildException if there is multiple extensions detected + * in the library. + */ + public void addConfiguredExtension(final ExtensionAdapter extensionAdapter) + throws BuildException { + if (null != extension) { + throw new BuildException("Can not have multiple extensions defined in one library."); + } + extension = extensionAdapter.toExtension(); + } + + /** + * Adds a set of extensions that this library requires. + * + * @param extensionSet a set of extensions that this library requires. + */ + public void addConfiguredDepends(final ExtensionSet extensionSet) { + dependencies.add(extensionSet); + } + + /** + * Adds a set of extensions that this library optionally requires. + * + * @param extensionSet a set of extensions that this library optionally requires. + */ + public void addConfiguredOptions(final ExtensionSet extensionSet) { + optionals.add(extensionSet); + } + + /** + * Adds an attribute that is to be put in main section of manifest. + * + * @param attribute an attribute that is to be put in main section of manifest. + */ + public void addConfiguredAttribute(final ExtraAttribute attribute) { + extraAttributes.add(attribute); + } + + /** + * Execute the task. + * + * @throws BuildException if the task fails. + */ + public void execute() throws BuildException { + validate(); + + final Manifest manifest = new Manifest(); + final Attributes attributes = manifest.getMainAttributes(); + + attributes.put(Attributes.Name.MANIFEST_VERSION, MANIFEST_VERSION); + attributes.putValue(CREATED_BY, "Apache Ant " + + getProject().getProperty(MagicNames.ANT_VERSION)); + + appendExtraAttributes(attributes); + + if (null != extension) { + Extension.addExtension(extension, attributes); + } + + //Add all the dependency data to manifest for dependencies + final ArrayList depends = toExtensions(dependencies); + appendExtensionList(attributes, Extension.EXTENSION_LIST, "lib", depends.size()); + appendLibraryList(attributes, "lib", depends); + + // Add all the dependency data to manifest for "optional" + //dependencies + final ArrayList option = toExtensions(optionals); + appendExtensionList(attributes, Extension.OPTIONAL_EXTENSION_LIST, "opt", option.size()); + appendLibraryList(attributes, "opt", option); + + try { + log("Generating manifest " + destFile.getAbsoluteFile(), Project.MSG_INFO); + writeManifest(manifest); + } catch (final IOException ioe) { + throw new BuildException(ioe.getMessage(), ioe); + } + } + + /** + * Validate the tasks parameters. + * + * @throws BuildException if invalid parameters found + */ + private void validate() throws BuildException { + if (null == destFile) { + throw new BuildException("Destfile attribute not specified."); + } + if (destFile.exists() && !destFile.isFile()) { + throw new BuildException(destFile + " is not a file."); + } + } + + /** + * Add any extra attributes to the manifest. + * + * @param attributes the manifest section to write + * attributes to + */ + private void appendExtraAttributes(final Attributes attributes) { + final Iterator iterator = extraAttributes.iterator(); + while (iterator.hasNext()) { + final ExtraAttribute attribute = + (ExtraAttribute) iterator.next(); + attributes.putValue(attribute.getName(), + attribute.getValue()); + } + } + + /** + * Write out manifest to destfile. + * + * @param manifest the manifest + * @throws IOException if error writing file + */ + private void writeManifest(final Manifest manifest) throws IOException { + FileOutputStream output = null; + try { + output = new FileOutputStream(destFile); + manifest.write(output); + output.flush(); + } finally { + if (null != output) { + try { + output.close(); + } catch (IOException e) { + // ignore + } + } + } + } + + /** + * Append specified extensions to specified attributes. + * Use the extensionKey to list the extensions, usually "Extension-List:" + * for required dependencies and "Optional-Extension-List:" for optional + * dependencies. NOTE: "Optional" dependencies are not part of the + * specification. + * + * @param attributes the attributes to add extensions to + * @param extensions the list of extensions + * @throws BuildException if an error occurs + */ + private void appendLibraryList(final Attributes attributes, final String listPrefix, + final ArrayList extensions) throws BuildException { + final int size = extensions.size(); + for (int i = 0; i < size; i++) { + final Extension ext = (Extension) extensions.get(i); + final String prefix = listPrefix + i + "-"; + Extension.addExtension(ext, prefix, attributes); + } + } + + /** + * Append an attribute such as "Extension-List: lib0 lib1 lib2" + * using specified prefix and counting up to specified size. + * Also use specified extensionKey so that can generate list of + * optional dependencies as well. + * + * @param size the number of librarys to list + * @param listPrefix the prefix for all librarys + * @param attributes the attributes to add key-value to + * @param extensionKey the key to use + */ + private void appendExtensionList(final Attributes attributes, + final Attributes.Name extensionKey, final String listPrefix, final int size) { + final StringBuffer sb = new StringBuffer(); + for (int i = 0; i < size; i++) { + sb.append(listPrefix); + sb.append(i); + sb.append(' '); + } + //add in something like + //"Extension-List: javahelp java3d" + attributes.put(extensionKey, sb.toString()); + } + + /** + * Convert a list of ExtensionSet objects to extensions. + * + * @param extensionSets the list of ExtensionSets to add to list + * @throws BuildException if an error occurs + */ + private ArrayList toExtensions(final ArrayList extensionSets) throws BuildException { + final ArrayList results = new ArrayList(); + + final int size = extensionSets.size(); + for (int i = 0; i < size; i++) { + final ExtensionSet set = (ExtensionSet) extensionSets.get(i); + final Extension[] extensions = set.toExtensions(getProject()); + for (int j = 0; j < extensions.length; j++) { + results.add(extensions[ j ]); + } + } + return results; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibResolveTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibResolveTask.java new file mode 100644 index 00000000..c13194fa --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibResolveTask.java @@ -0,0 +1,268 @@ +/* + * 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.tools.ant.taskdefs.optional.extension; + +import java.io.File; +import java.util.ArrayList; +import java.util.jar.Manifest; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.optional.extension.resolvers.AntResolver; +import org.apache.tools.ant.taskdefs.optional.extension.resolvers.LocationResolver; +import org.apache.tools.ant.taskdefs.optional.extension.resolvers.URLResolver; + +/** + * Tries to locate a JAR to satisfy an extension and place + * location of JAR into property. + * + * @ant.task name="jarlib-resolve" + */ +public class JarLibResolveTask extends Task { + /** + * The name of the property in which the location of + * library is stored. + */ + private String propertyName; + + /** + * The extension that is required. + */ + private Extension requiredExtension; + + /** + * The set of resolvers to use to attempt to locate library. + */ + private final ArrayList resolvers = new ArrayList(); + + /** + * Flag to indicate that you should check that + * the librarys resolved actually contain + * extension and if they don't then raise + * an exception. + */ + private boolean checkExtension = true; + + /** + * Flag indicating whether or not you should + * throw a BuildException if you cannot resolve + * library. + */ + private boolean failOnError = true; + + /** + * The name of the property in which the location of + * library is stored. + * + * @param property The name of the property in which the location of + * library is stored. + */ + public void setProperty(final String property) { + this.propertyName = property; + } + + /** + * Check nested libraries for extensions + * + * @param checkExtension if true, libraries returned by nested + * resolvers should be checked to see if they supply extension. + */ + public void setCheckExtension(final boolean checkExtension) { + this.checkExtension = checkExtension; + } + + /** + * Set whether to fail if error. + * + * @param failOnError if true, failure to locate library should fail build. + */ + public void setFailOnError(final boolean failOnError) { + this.failOnError = failOnError; + } + + /** + * Adds location resolver to look for a library in a location + * relative to project directory. + * + * @param loc the resolver location to search. + */ + public void addConfiguredLocation(final LocationResolver loc) { + resolvers.add(loc); + } + + /** + * Adds a URL resolver to download a library from a URL + * to a local file. + * + * @param url the URL resolver from which to download the library + */ + public void addConfiguredUrl(final URLResolver url) { + resolvers.add(url); + } + + /** + * Adds Ant resolver to run an Ant build file to generate a library. + * + * @param ant the AntResolver to generate the library. + */ + public void addConfiguredAnt(final AntResolver ant) { + resolvers.add(ant); + } + + /** + * Set the Extension looking for. + * + * @param extension Set the Extension looking for. + */ + public void addConfiguredExtension(final ExtensionAdapter extension) { + if (null != requiredExtension) { + final String message = "Can not specify extension to " + + "resolve multiple times."; + throw new BuildException(message); + } + requiredExtension = extension.toExtension(); + } + + /** + * Execute the task. + * + * @throws BuildException if the task fails. + */ + public void execute() throws BuildException { + validate(); + + getProject().log("Resolving extension: " + requiredExtension, Project.MSG_VERBOSE); + + String candidate = getProject().getProperty(propertyName); + + if (null != candidate) { + final String message = "Property Already set to: " + candidate; + if (failOnError) { + throw new BuildException(message); + } + getProject().log(message, Project.MSG_ERR); + return; + } + + final int size = resolvers.size(); + for (int i = 0; i < size; i++) { + final ExtensionResolver resolver = + (ExtensionResolver) resolvers.get(i); + + getProject().log("Searching for extension using Resolver:" + resolver, + Project.MSG_VERBOSE); + + try { + final File file = resolver.resolve(requiredExtension, getProject()); + try { + checkExtension(file); + return; + } catch (final BuildException be) { + final String message = "File " + file + " returned by " + + "resolver failed to satisfy extension due to: " + be.getMessage(); + getProject().log(message, Project.MSG_WARN); + } + } catch (final BuildException be) { + final String message = "Failed to resolve extension to file " + "using resolver " + + resolver + " due to: " + be; + getProject().log(message, Project.MSG_WARN); + } + } + missingExtension(); + } + + /** + * Utility method that will throw a {@link BuildException} + * if {@link #failOnError} is true else it just displays + * a warning. + */ + private void missingExtension() { + final String message = "Unable to resolve extension to a file"; + if (failOnError) { + throw new BuildException(message); + } + getProject().log(message, Project.MSG_ERR); + } + + /** + * Check if specified file satisfies extension. + * If it does then set the relevant property + * else throw a BuildException. + * + * @param file the candidate library + * @throws BuildException if library does not satisfy extension + */ + private void checkExtension(final File file) { + if (!file.exists()) { + throw new BuildException("File " + file + " does not exist"); + } + if (!file.isFile()) { + throw new BuildException("File " + file + " is not a file"); + } + if (!checkExtension) { + getProject().log("Setting property to " + file + + " without verifying library satisfies extension", Project.MSG_VERBOSE); + setLibraryProperty(file); + } else { + getProject().log("Checking file " + file + " to see if it satisfies extension", + Project.MSG_VERBOSE); + final Manifest manifest = ExtensionUtil.getManifest(file); + final Extension[] extensions = Extension.getAvailable(manifest); + for (int i = 0; i < extensions.length; i++) { + final Extension extension = extensions[ i ]; + if (extension.isCompatibleWith(requiredExtension)) { + setLibraryProperty(file); + return; + } + } + final String message = "File " + file + " skipped as it " + + "does not satisfy extension"; + getProject().log(message, Project.MSG_VERBOSE); + throw new BuildException(message); + } + } + + /** + * Utility method to set the appropriate property + * to indicate that specified file satisfies library + * requirements. + * + * @param file the library + */ + private void setLibraryProperty(final File file) { + getProject().setNewProperty(propertyName, file.getAbsolutePath()); + } + + /** + * Validate the tasks parameters. + * + * @throws BuildException if invalid parameters found + */ + private void validate() throws BuildException { + if (null == propertyName) { + final String message = "Property attribute must be specified."; + throw new BuildException(message); + } + + if (null == requiredExtension) { + final String message = "Extension element must be specified."; + throw new BuildException(message); + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibFileSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibFileSet.java new file mode 100644 index 00000000..b21719e5 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibFileSet.java @@ -0,0 +1,117 @@ +/* + * 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.tools.ant.taskdefs.optional.extension; + +import org.apache.tools.ant.types.FileSet; + +/** + * LibFileSet represents a fileset containing libraries. + * Associated with the libraries is data pertaining to + * how they are to be handled when building manifests. + * + */ +public class LibFileSet + extends FileSet { + /** + * Flag indicating whether should include the + * "Implementation-URL" attribute in manifest. + * Defaults to false. + */ + private boolean includeURL; + + /** + * Flag indicating whether should include the + * "Implementation-*" attributes in manifest. + * Defaults to false. + */ + private boolean includeImpl; + + /** + * String that is the base URL for the librarys + * when constructing the "Implementation-URL" + * attribute. For instance setting the base to + * "http://jakarta.apache.org/avalon/libs/" and then + * including the library "excalibur-cli-1.0.jar" in the + * fileset will result in the "Implementation-URL" attribute + * being set to "http://jakarta.apache.org/avalon/libs/excalibur-cli-1.0.jar" + * + * Note this is only used if the library does not define + * "Implementation-URL" itself. + * + * Note that this also implies includeURL=true + */ + private String urlBase; + + /** + * Flag indicating whether should include the + * "Implementation-URL" attribute in manifest. + * Defaults to false. + * + * @param includeURL the flag + */ + public void setIncludeUrl(boolean includeURL) { + this.includeURL = includeURL; + } + + /** + * Flag indicating whether should include the + * "Implementation-*" attributes in manifest. + * Defaults to false. + * + * @param includeImpl the flag + */ + public void setIncludeImpl(boolean includeImpl) { + this.includeImpl = includeImpl; + } + + /** + * Set the url base for fileset. + * + * @param urlBase the base url + */ + public void setUrlBase(String urlBase) { + this.urlBase = urlBase; + } + + /** + * Get the includeURL flag. + * + * @return the includeURL flag. + */ + boolean isIncludeURL() { + return includeURL; + } + + /** + * Get the includeImpl flag. + * + * @return the includeImpl flag. + */ + boolean isIncludeImpl() { + return includeImpl; + } + + /** + * Get the urlbase. + * + * @return the urlbase. + */ + String getUrlBase() { + return urlBase; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibraryDisplayer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibraryDisplayer.java new file mode 100644 index 00000000..b0ee4f81 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibraryDisplayer.java @@ -0,0 +1,150 @@ +/* + * 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.tools.ant.taskdefs.optional.extension; + +import java.io.File; +import java.text.ParseException; +import java.util.jar.Manifest; + +import org.apache.tools.ant.BuildException; + +/** + * Utility class to output the information in a jar relating + * to "Optional Packages" (formely known as "Extensions") + * and Package Specifications. + * + */ +class LibraryDisplayer { + /** + * Display the extensions and specifications contained + * within specified file. + * + * @param file the file + * @throws BuildException if fail to read file + */ + void displayLibrary(final File file) + throws BuildException { + final Manifest manifest = ExtensionUtil.getManifest(file); + displayLibrary(file, manifest); + } + + /** + * Display the extensions and specifications contained + * within specified file. + * + * @param file the file to use while reporting + * @param manifest the manifest of file + * @throws BuildException if fail to read file + */ + void displayLibrary(final File file, + final Manifest manifest) + throws BuildException { + final Extension[] available = Extension.getAvailable(manifest); + final Extension[] required = Extension.getRequired(manifest); + final Extension[] options = Extension.getOptions(manifest); + final Specification[] specifications = getSpecifications(manifest); + + if (0 == available.length && 0 == required.length && 0 == options.length + && 0 == specifications.length) { + return; + } + + final String message = "File: " + file; + final int size = message.length(); + printLine(size); + System.out.println(message); + printLine(size); + if (0 != available.length) { + System.out.println("Extensions Supported By Library:"); + for (int i = 0; i < available.length; i++) { + final Extension extension = available[ i ]; + System.out.println(extension.toString()); + } + } + + if (0 != required.length) { + System.out.println("Extensions Required By Library:"); + for (int i = 0; i < required.length; i++) { + final Extension extension = required[ i ]; + System.out.println(extension.toString()); + } + } + + if (0 != options.length) { + System.out.println("Extensions that will be used by Library if present:"); + for (int i = 0; i < options.length; i++) { + final Extension extension = options[ i ]; + System.out.println(extension.toString()); + } + } + + if (0 != specifications.length) { + System.out.println("Specifications Supported By Library:"); + for (int i = 0; i < specifications.length; i++) { + final Specification specification = specifications[ i ]; + displaySpecification(specification); + } + } + } + + /** + * Print out a line of '-'s equal to specified size. + * + * @param size the number of dashes to printout + */ + private void printLine(final int size) { + for (int i = 0; i < size; i++) { + System.out.print("-"); + } + System.out.println(); + } + + /** + * Get specifications from manifest. + * + * @param manifest the manifest + * @return the specifications or null if none + * @throws BuildException if malformed specification sections + */ + private Specification[] getSpecifications(final Manifest manifest) + throws BuildException { + try { + return Specification.getSpecifications(manifest); + } catch (final ParseException pe) { + throw new BuildException(pe.getMessage(), pe); + } + } + + /** + * Print out specification details. + * + * @param specification the specification + */ + private void displaySpecification(final Specification specification) { + final String[] sections = specification.getSections(); + if (null != sections) { + final StringBuffer sb = new StringBuffer("Sections: "); + for (int i = 0; i < sections.length; i++) { + sb.append(" "); + sb.append(sections[ i ]); + } + System.out.println(sb); + } + System.out.println(specification.toString()); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Specification.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Specification.java new file mode 100644 index 00000000..1e4bb7b3 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Specification.java @@ -0,0 +1,605 @@ +/* + * 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.tools.ant.taskdefs.optional.extension; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Map; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import org.apache.tools.ant.util.DeweyDecimal; +import org.apache.tools.ant.util.StringUtils; + +/** + * <p>Utility class that represents either an available "Optional Package" + * (formerly known as "Standard Extension") as described in the manifest + * of a JAR file, or the requirement for such an optional package.</p> + * + * <p>For more information about optional packages, see the document + * <em>Optional Package Versioning</em> in the documentation bundle for your + * Java2 Standard Edition package, in file + * <code>guide/extensions/versioning.html</code>.</p> + * + */ +public final class Specification { + + private static final String MISSING = "Missing "; + + /** + * Manifest Attribute Name object for SPECIFICATION_TITLE. + */ + public static final Attributes.Name SPECIFICATION_TITLE + = Attributes.Name.SPECIFICATION_TITLE; + + /** + * Manifest Attribute Name object for SPECIFICATION_VERSION. + */ + public static final Attributes.Name SPECIFICATION_VERSION + = Attributes.Name.SPECIFICATION_VERSION; + + /** + * Manifest Attribute Name object for SPECIFICATION_VENDOR. + */ + public static final Attributes.Name SPECIFICATION_VENDOR + = Attributes.Name.SPECIFICATION_VENDOR; + + /** + * Manifest Attribute Name object for IMPLEMENTATION_TITLE. + */ + public static final Attributes.Name IMPLEMENTATION_TITLE + = Attributes.Name.IMPLEMENTATION_TITLE; + + /** + * Manifest Attribute Name object for IMPLEMENTATION_VERSION. + */ + public static final Attributes.Name IMPLEMENTATION_VERSION + = Attributes.Name.IMPLEMENTATION_VERSION; + + /** + * Manifest Attribute Name object for IMPLEMENTATION_VENDOR. + */ + public static final Attributes.Name IMPLEMENTATION_VENDOR + = Attributes.Name.IMPLEMENTATION_VENDOR; + + /** + * Enum indicating that extension is compatible with other Package + * Specification. + */ + public static final Compatibility COMPATIBLE = + new Compatibility("COMPATIBLE"); + + /** + * Enum indicating that extension requires an upgrade + * of specification to be compatible with other Package Specification. + */ + public static final Compatibility REQUIRE_SPECIFICATION_UPGRADE = + new Compatibility("REQUIRE_SPECIFICATION_UPGRADE"); + + /** + * Enum indicating that extension requires a vendor + * switch to be compatible with other Package Specification. + */ + public static final Compatibility REQUIRE_VENDOR_SWITCH = + new Compatibility("REQUIRE_VENDOR_SWITCH"); + + /** + * Enum indicating that extension requires an upgrade + * of implementation to be compatible with other Package Specification. + */ + public static final Compatibility REQUIRE_IMPLEMENTATION_CHANGE = + new Compatibility("REQUIRE_IMPLEMENTATION_CHANGE"); + + /** + * This enum indicates that an extension is incompatible with + * other Package Specification in ways other than other enums + * indicate. For example, the other Package Specification + * may have a different ID. + */ + public static final Compatibility INCOMPATIBLE = + new Compatibility("INCOMPATIBLE"); + + /** + * The name of the Package Specification. + */ + private String specificationTitle; + + /** + * The version number (dotted decimal notation) of the specification + * to which this optional package conforms. + */ + private DeweyDecimal specificationVersion; + + /** + * The name of the company or organization that originated the + * specification to which this specification conforms. + */ + private String specificationVendor; + + /** + * The title of implementation. + */ + private String implementationTitle; + + /** + * The name of the company or organization that produced this + * implementation of this specification. + */ + private String implementationVendor; + + /** + * The version string for implementation. The version string is + * opaque. + */ + private String implementationVersion; + + /** + * The sections of jar that the specification applies to. + */ + private String[] sections; + + /** + * Return an array of <code>Package Specification</code> objects. + * If there are no such optional packages, a zero-length array is returned. + * + * @param manifest Manifest to be parsed + * @return the Package Specifications extensions in specified manifest + * @throws ParseException if the attributes of the specifications cannot + * be parsed according to their expected formats. + */ + public static Specification[] getSpecifications(final Manifest manifest) + throws ParseException { + if (null == manifest) { + return new Specification[ 0 ]; + } + + final ArrayList results = new ArrayList(); + + final Map entries = manifest.getEntries(); + final Iterator keys = entries.keySet().iterator(); + while (keys.hasNext()) { + final String key = (String) keys.next(); + final Attributes attributes = (Attributes) entries.get(key); + final Specification specification + = getSpecification(key, attributes); + if (null != specification) { + results.add(specification); + } + } + + final ArrayList trimmedResults = removeDuplicates(results); + return (Specification[]) trimmedResults.toArray(new Specification[trimmedResults.size()]); + } + + /** + * The constructor to create Package Specification object. + * Note that every component is allowed to be specified + * but only the specificationTitle is mandatory. + * + * @param specificationTitle the name of specification. + * @param specificationVersion the specification Version. + * @param specificationVendor the specification Vendor. + * @param implementationTitle the title of implementation. + * @param implementationVersion the implementation Version. + * @param implementationVendor the implementation Vendor. + */ + public Specification(final String specificationTitle, + final String specificationVersion, + final String specificationVendor, + final String implementationTitle, + final String implementationVersion, + final String implementationVendor) { + this(specificationTitle, specificationVersion, specificationVendor, + implementationTitle, implementationVersion, implementationVendor, + null); + } + + /** + * The constructor to create Package Specification object. + * Note that every component is allowed to be specified + * but only the specificationTitle is mandatory. + * + * @param specificationTitle the name of specification. + * @param specificationVersion the specification Version. + * @param specificationVendor the specification Vendor. + * @param implementationTitle the title of implementation. + * @param implementationVersion the implementation Version. + * @param implementationVendor the implementation Vendor. + * @param sections the sections/packages that Specification applies to. + */ + public Specification(final String specificationTitle, + final String specificationVersion, + final String specificationVendor, + final String implementationTitle, + final String implementationVersion, + final String implementationVendor, + final String[] sections) { + this.specificationTitle = specificationTitle; + this.specificationVendor = specificationVendor; + + if (null != specificationVersion) { + try { + this.specificationVersion + = new DeweyDecimal(specificationVersion); + } catch (final NumberFormatException nfe) { + final String error = "Bad specification version format '" + + specificationVersion + "' in '" + specificationTitle + + "'. (Reason: " + nfe + ")"; + throw new IllegalArgumentException(error); + } + } + + this.implementationTitle = implementationTitle; + this.implementationVendor = implementationVendor; + this.implementationVersion = implementationVersion; + + if (null == this.specificationTitle) { + throw new NullPointerException("specificationTitle"); + } + + String[] copy = null; + if (null != sections) { + copy = new String[ sections.length ]; + System.arraycopy(sections, 0, copy, 0, sections.length); + } + this.sections = copy; + } + + /** + * Get the title of the specification. + * + * @return the title of specification + */ + public String getSpecificationTitle() { + return specificationTitle; + } + + /** + * Get the vendor of the specification. + * + * @return the vendor of the specification. + */ + public String getSpecificationVendor() { + return specificationVendor; + } + + /** + * Get the title of the specification. + * + * @return the title of the specification. + */ + public String getImplementationTitle() { + return implementationTitle; + } + + /** + * Get the version of the specification. + * + * @return the version of the specification. + */ + public DeweyDecimal getSpecificationVersion() { + return specificationVersion; + } + + /** + * Get the vendor of the extensions implementation. + * + * @return the vendor of the extensions implementation. + */ + public String getImplementationVendor() { + return implementationVendor; + } + + /** + * Get the version of the implementation. + * + * @return the version of the implementation. + */ + public String getImplementationVersion() { + return implementationVersion; + } + + /** + * Return an array containing sections to which specification applies + * or null if relevant to no sections. + * + * @return an array containing sections to which specification applies + * or null if relevant to no sections. + */ + public String[] getSections() { + if (null == sections) { + return null; + } + final String[] newSections = new String[ sections.length ]; + System.arraycopy(sections, 0, newSections, 0, sections.length); + return newSections; + } + + /** + * Return a Compatibility enum indicating the relationship of this + * <code>Package Specification</code> with the specified + * <code>Extension</code>. + * + * @param other the other specification + * @return the enum indicating the compatibility (or lack thereof) + * of specified Package Specification + */ + public Compatibility getCompatibilityWith(final Specification other) { + // Specification Name must match + if (!specificationTitle.equals(other.getSpecificationTitle())) { + return INCOMPATIBLE; + } + + // Available specification version must be >= required + final DeweyDecimal otherSpecificationVersion + = other.getSpecificationVersion(); + if (null != specificationVersion) { + if (null == otherSpecificationVersion + || !isCompatible(specificationVersion, otherSpecificationVersion)) { + return REQUIRE_SPECIFICATION_UPGRADE; + } + } + + // Implementation Vendor ID must match + final String otherImplementationVendor + = other.getImplementationVendor(); + if (null != implementationVendor) { + if (null == otherImplementationVendor + || !implementationVendor.equals(otherImplementationVendor)) { + return REQUIRE_VENDOR_SWITCH; + } + } + + // Implementation version must be >= required + final String otherImplementationVersion + = other.getImplementationVersion(); + if (null != implementationVersion) { + if (null == otherImplementationVersion + || !implementationVersion.equals(otherImplementationVersion)) { + return REQUIRE_IMPLEMENTATION_CHANGE; + } + } + + // This available optional package satisfies the requirements + return COMPATIBLE; + } + + /** + * Return <code>true</code> if the specified <code>package</code> + * is satisfied by this <code>Specification</code>. Otherwise, return + * <code>false</code>. + * + * @param other the specification + * @return true if the specification is compatible with this specification + */ + public boolean isCompatibleWith(final Specification other) { + return (COMPATIBLE == getCompatibilityWith(other)); + } + + /** + * Return a String representation of this object. + * + * @return string representation of object. + */ + public String toString() { + final String brace = ": "; + + final StringBuffer sb + = new StringBuffer(SPECIFICATION_TITLE.toString()); + sb.append(brace); + sb.append(specificationTitle); + sb.append(StringUtils.LINE_SEP); + + if (null != specificationVersion) { + sb.append(SPECIFICATION_VERSION); + sb.append(brace); + sb.append(specificationVersion); + sb.append(StringUtils.LINE_SEP); + } + + if (null != specificationVendor) { + sb.append(SPECIFICATION_VENDOR); + sb.append(brace); + sb.append(specificationVendor); + sb.append(StringUtils.LINE_SEP); + } + + if (null != implementationTitle) { + sb.append(IMPLEMENTATION_TITLE); + sb.append(brace); + sb.append(implementationTitle); + sb.append(StringUtils.LINE_SEP); + } + + if (null != implementationVersion) { + sb.append(IMPLEMENTATION_VERSION); + sb.append(brace); + sb.append(implementationVersion); + sb.append(StringUtils.LINE_SEP); + } + + if (null != implementationVendor) { + sb.append(IMPLEMENTATION_VENDOR); + sb.append(brace); + sb.append(implementationVendor); + sb.append(StringUtils.LINE_SEP); + } + + return sb.toString(); + } + + /** + * Return <code>true</code> if the first version number is greater than + * or equal to the second; otherwise return <code>false</code>. + * + * @param first First version number (dotted decimal) + * @param second Second version number (dotted decimal) + */ + private boolean isCompatible(final DeweyDecimal first, + final DeweyDecimal second) { + return first.isGreaterThanOrEqual(second); + } + + /** + * Combine all specifications objects that are identical except + * for the sections. + * + * <p>Note this is very inefficent and should probably be fixed + * in the future.</p> + * + * @param list the array of results to trim + * @return an array list with all duplicates removed + */ + private static ArrayList removeDuplicates(final ArrayList list) { + final ArrayList results = new ArrayList(); + final ArrayList sections = new ArrayList(); + while (list.size() > 0) { + final Specification specification = (Specification) list.remove(0); + final Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + final Specification other = (Specification) iterator.next(); + if (isEqual(specification, other)) { + final String[] otherSections = other.getSections(); + if (null != otherSections) { + sections.addAll(Arrays.asList(otherSections)); + } + iterator.remove(); + } + } + + final Specification merged = + mergeInSections(specification, sections); + results.add(merged); + //Reset list of sections + sections.clear(); + } + + return results; + } + + /** + * Test if two specifications are equal except for their sections. + * + * @param specification one specificaiton + * @param other the ohter specification + * @return true if two specifications are equal except for their + * sections, else false + */ + private static boolean isEqual(final Specification specification, + final Specification other) { + return + specification.getSpecificationTitle().equals(other.getSpecificationTitle()) + && specification.getSpecificationVersion().isEqual(other.getSpecificationVersion()) + && specification.getSpecificationVendor().equals(other.getSpecificationVendor()) + && specification.getImplementationTitle().equals(other.getImplementationTitle()) + && specification.getImplementationVersion().equals(other.getImplementationVersion()) + && specification.getImplementationVendor().equals(other.getImplementationVendor()); + } + + /** + * Merge the specified sections into specified section and return result. + * If no sections to be added then just return original specification. + * + * @param specification the specification + * @param sectionsToAdd the list of sections to merge + * @return the merged specification + */ + private static Specification mergeInSections(final Specification specification, + final ArrayList sectionsToAdd) { + if (0 == sectionsToAdd.size()) { + return specification; + } + sectionsToAdd.addAll(Arrays.asList(specification.getSections())); + + final String[] sections = + (String[]) sectionsToAdd.toArray(new String[sectionsToAdd.size()]); + + return new Specification(specification.getSpecificationTitle(), + specification.getSpecificationVersion().toString(), + specification.getSpecificationVendor(), + specification.getImplementationTitle(), + specification.getImplementationVersion(), + specification.getImplementationVendor(), + sections); + } + + /** + * Trim the supplied string if the string is non-null + * + * @param value the string to trim or null + * @return the trimmed string or null + */ + private static String getTrimmedString(final String value) { + return value == null ? null : value.trim(); + } + + /** + * Extract an Package Specification from Attributes. + * + * @param attributes Attributes to searched + * @return the new Specification object, or null + */ + private static Specification getSpecification(final String section, + final Attributes attributes) + throws ParseException { + //WARNING: We trim the values of all the attributes because + //Some extension declarations are badly defined (ie have spaces + //after version or vendor) + final String name + = getTrimmedString(attributes.getValue(SPECIFICATION_TITLE)); + if (null == name) { + return null; + } + + final String specVendor + = getTrimmedString(attributes.getValue(SPECIFICATION_VENDOR)); + if (null == specVendor) { + throw new ParseException(MISSING + SPECIFICATION_VENDOR, 0); + } + + final String specVersion + = getTrimmedString(attributes.getValue(SPECIFICATION_VERSION)); + if (null == specVersion) { + throw new ParseException(MISSING + SPECIFICATION_VERSION, 0); + } + + final String impTitle + = getTrimmedString(attributes.getValue(IMPLEMENTATION_TITLE)); + if (null == impTitle) { + throw new ParseException(MISSING + IMPLEMENTATION_TITLE, 0); + } + + final String impVersion + = getTrimmedString(attributes.getValue(IMPLEMENTATION_VERSION)); + if (null == impVersion) { + throw new ParseException(MISSING + IMPLEMENTATION_VERSION, 0); + } + + final String impVendor + = getTrimmedString(attributes.getValue(IMPLEMENTATION_VENDOR)); + if (null == impVendor) { + throw new ParseException(MISSING + IMPLEMENTATION_VENDOR, 0); + } + + return new Specification(name, specVersion, specVendor, + impTitle, impVersion, impVendor, + new String[]{section}); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/AntResolver.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/AntResolver.java new file mode 100644 index 00000000..6284679f --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/AntResolver.java @@ -0,0 +1,117 @@ +/* + * 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.tools.ant.taskdefs.optional.extension.resolvers; + +import java.io.File; +import java.io.IOException; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Ant; +import org.apache.tools.ant.taskdefs.optional.extension.Extension; +import org.apache.tools.ant.taskdefs.optional.extension.ExtensionResolver; + +/** + * Resolver that just returns s specified location. + * + */ +public class AntResolver implements ExtensionResolver { + private File antfile; + private File destfile; + private String target; + + /** + * Sets the ant file + * @param antfile the ant file to set + */ + public void setAntfile(final File antfile) { + this.antfile = antfile; + } + + /** + * Sets the destination file + * @param destfile the destination file + */ + public void setDestfile(final File destfile) { + this.destfile = destfile; + } + + /** + * Sets the target + * @param target the target + */ + public void setTarget(final String target) { + this.target = target; + } + + /** + * Returns the resolved file + * @param extension the extension + * @param project the project + * @return the file resolved + * @throws BuildException if the file cannot be resolved + */ + public File resolve(final Extension extension, + final Project project) throws BuildException { + validate(); + + final Ant ant = new Ant(); + ant.setProject(project); + ant.setInheritAll(false); + ant.setAntfile(antfile.getName()); + + try { + final File dir = + antfile.getParentFile().getCanonicalFile(); + ant.setDir(dir); + } catch (final IOException ioe) { + throw new BuildException(ioe.getMessage(), ioe); + } + + if (null != target) { + ant.setTarget(target); + } + + ant.execute(); + + return destfile; + } + + /* + * Validates URL + */ + private void validate() { + if (null == antfile) { + final String message = "Must specify Buildfile"; + throw new BuildException(message); + } + + if (null == destfile) { + final String message = "Must specify destination file"; + throw new BuildException(message); + } + } + + /** + * Returns a string representation + * @return the string representation + */ + public String toString() { + return "Ant[" + antfile + "==>" + destfile + "]"; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/LocationResolver.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/LocationResolver.java new file mode 100644 index 00000000..e2fec022 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/LocationResolver.java @@ -0,0 +1,65 @@ +/* + * 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.tools.ant.taskdefs.optional.extension.resolvers; + +import java.io.File; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.optional.extension.Extension; +import org.apache.tools.ant.taskdefs.optional.extension.ExtensionResolver; + +/** + * Resolver that just returns s specified location. + * + */ +public class LocationResolver implements ExtensionResolver { + private String location; + + /** + * Sets the location for this resolver + * @param location the location + */ + public void setLocation(final String location) { + this.location = location; + } + + /** + * Returns the resolved file + * @param extension the extension + * @param project the project + * @return the file resolved + * @throws BuildException if no location is set + */ + public File resolve(final Extension extension, + final Project project) throws BuildException { + if (null == location) { + final String message = "No location specified for resolver"; + throw new BuildException(message); + } + + return project.resolveFile(location); + } + /** + * Returns a string representation of the Location + * @return the string representation + */ + public String toString() { + return "Location[" + location + "]"; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/URLResolver.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/URLResolver.java new file mode 100644 index 00000000..d693b899 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/URLResolver.java @@ -0,0 +1,133 @@ +/* + * 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.tools.ant.taskdefs.optional.extension.resolvers; + +import java.io.File; +import java.net.URL; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Get; +import org.apache.tools.ant.taskdefs.optional.extension.Extension; +import org.apache.tools.ant.taskdefs.optional.extension.ExtensionResolver; + +/** + * Resolver that just returns s specified location. + * + */ +public class URLResolver implements ExtensionResolver { + private File destfile; + private File destdir; + private URL url; + + /** + * Sets the URL + * @param url the url + */ + public void setUrl(final URL url) { + this.url = url; + } + + /** + * Sets the destination file + * @param destfile the destination file + */ + public void setDestfile(final File destfile) { + this.destfile = destfile; + } + + /** + * Sets the destination directory + * @param destdir the destination directory + */ + public void setDestdir(final File destdir) { + this.destdir = destdir; + } + + /** + * Returns the file resolved from URL and directory + * @param extension the extension + * @param project the project + * @return file the file resolved + * @throws BuildException if the URL is invalid + */ + public File resolve(final Extension extension, + final Project project) throws BuildException { + validate(); + + final File file = getDest(); + + final Get get = new Get(); + get.setProject(project); + get.setDest(file); + get.setSrc(url); + get.execute(); + + return file; + } + + /* + * Gets the destination file + */ + private File getDest() { + File result; + if (null != destfile) { + result = destfile; + } else { + final String file = url.getFile(); + String filename; + if (null == file || file.length() <= 1) { + filename = "default.file"; + } else { + int index = file.lastIndexOf('/'); + if (-1 == index) { + index = 0; + } + filename = file.substring(index); + } + result = new File(destdir, filename); + } + return result; + } + + /* + * Validates URL + */ + private void validate() { + if (null == url) { + final String message = "Must specify URL"; + throw new BuildException(message); + } + + if (null == destdir && null == destfile) { + final String message = "Must specify destination file or directory"; + throw new BuildException(message); + } else if (null != destdir && null != destfile) { + final String message = "Must not specify both destination file or directory"; + throw new BuildException(message); + } + } + + /** + * Returns a string representation of the URL + * @return the string representation + */ + public String toString() { + return "URL[" + url + "]"; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/i18n/Translate.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/i18n/Translate.java new file mode 100644 index 00000000..82731fe9 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/i18n/Translate.java @@ -0,0 +1,632 @@ +/* + * 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.tools.ant.taskdefs.optional.i18n; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.Hashtable; +import java.util.Locale; +import java.util.Vector; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.MatchingTask; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.LineTokenizer; + +/** + * Translates text embedded in files using Resource Bundle files. + * Since ant 1.6 preserves line endings + * + */ +public class Translate extends MatchingTask { + /** + * search a bundle matching the specified language, the country and the variant + */ + private static final int BUNDLE_SPECIFIED_LANGUAGE_COUNTRY_VARIANT = 0; + /** + * search a bundle matching the specified language, and the country + */ + private static final int BUNDLE_SPECIFIED_LANGUAGE_COUNTRY = 1; + /** + * search a bundle matching the specified language only + */ + private static final int BUNDLE_SPECIFIED_LANGUAGE = 2; + /** + * search a bundle matching nothing special + */ + private static final int BUNDLE_NOMATCH = 3; + /** + * search a bundle matching the language, the country and the variant + * of the current locale of the computer + */ + private static final int BUNDLE_DEFAULT_LANGUAGE_COUNTRY_VARIANT = 4; + /** + * search a bundle matching the language, and the country + * of the current locale of the computer + */ + private static final int BUNDLE_DEFAULT_LANGUAGE_COUNTRY = 5; + /** + * search a bundle matching the language only + * of the current locale of the computer + */ + private static final int BUNDLE_DEFAULT_LANGUAGE = 6; + /** + * number of possibilities for the search + */ + private static final int BUNDLE_MAX_ALTERNATIVES = BUNDLE_DEFAULT_LANGUAGE + 1; + /** + * Family name of resource bundle + */ + private String bundle; + + /** + * Locale specific language of the resource bundle + */ + private String bundleLanguage; + + /** + * Locale specific country of the resource bundle + */ + private String bundleCountry; + + /** + * Locale specific variant of the resource bundle + */ + private String bundleVariant; + + /** + * Destination directory + */ + private File toDir; + + /** + * Source file encoding scheme + */ + private String srcEncoding; + + /** + * Destination file encoding scheme + */ + private String destEncoding; + + /** + * Resource Bundle file encoding scheme, defaults to srcEncoding + */ + private String bundleEncoding; + + /** + * Starting token to identify keys + */ + private String startToken; + + /** + * Ending token to identify keys + */ + private String endToken; + + /** + * Whether or not to create a new destination file. + * Defaults to <code>false</code>. + */ + private boolean forceOverwrite; + + /** + * Vector to hold source file sets. + */ + private Vector filesets = new Vector(); + + /** + * Holds key value pairs loaded from resource bundle file + */ + private Hashtable resourceMap = new Hashtable(); + /** + + * Used to resolve file names. + */ + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** + * Last Modified Timestamp of resource bundle file being used. + */ + private long[] bundleLastModified = new long[BUNDLE_MAX_ALTERNATIVES]; + + /** + * Last Modified Timestamp of source file being used. + */ + private long srcLastModified; + + /** + * Last Modified Timestamp of destination file being used. + */ + private long destLastModified; + + /** + * Has at least one file from the bundle been loaded? + */ + private boolean loaded = false; + + /** + * Sets Family name of resource bundle; required. + * @param bundle family name of resource bundle + */ + public void setBundle(String bundle) { + this.bundle = bundle; + } + + /** + * Sets locale specific language of resource bundle; optional. + * @param bundleLanguage language of the bundle + */ + public void setBundleLanguage(String bundleLanguage) { + this.bundleLanguage = bundleLanguage; + } + + /** + * Sets locale specific country of resource bundle; optional. + * @param bundleCountry country of the bundle + */ + public void setBundleCountry(String bundleCountry) { + this.bundleCountry = bundleCountry; + } + + /** + * Sets locale specific variant of resource bundle; optional. + * @param bundleVariant locale variant of resource bundle + */ + public void setBundleVariant(String bundleVariant) { + this.bundleVariant = bundleVariant; + } + + /** + * Sets Destination directory; required. + * @param toDir destination directory + */ + public void setToDir(File toDir) { + this.toDir = toDir; + } + + /** + * Sets starting token to identify keys; required. + * @param startToken starting token to identify keys + */ + public void setStartToken(String startToken) { + this.startToken = startToken; + } + + /** + * Sets ending token to identify keys; required. + * @param endToken ending token to identify keys + */ + public void setEndToken(String endToken) { + this.endToken = endToken; + } + + /** + * Sets source file encoding scheme; optional, + * defaults to encoding of local system. + * @param srcEncoding source file encoding + */ + public void setSrcEncoding(String srcEncoding) { + this.srcEncoding = srcEncoding; + } + + /** + * Sets destination file encoding scheme; optional. Defaults to source file + * encoding + * @param destEncoding destination file encoding scheme + */ + public void setDestEncoding(String destEncoding) { + this.destEncoding = destEncoding; + } + + /** + * Sets Resource Bundle file encoding scheme; optional. Defaults to source file + * encoding + * @param bundleEncoding bundle file encoding scheme + */ + public void setBundleEncoding(String bundleEncoding) { + this.bundleEncoding = bundleEncoding; + } + + /** + * Whether or not to overwrite existing file irrespective of + * whether it is newer than the source file as well as the + * resource bundle file. + * Defaults to false. + * @param forceOverwrite whether or not to overwrite existing files + */ + public void setForceOverwrite(boolean forceOverwrite) { + this.forceOverwrite = forceOverwrite; + } + + /** + * Adds a set of files to translate as a nested fileset element. + * @param set the fileset to be added + */ + public void addFileset(FileSet set) { + filesets.addElement(set); + } + + /** + * Check attributes values, load resource map and translate + * @throws BuildException if the required attributes are not set + * Required : <ul> + * <li>bundle</li> + * <li>starttoken</li> + * <li>endtoken</li> + * </ul> + */ + public void execute() throws BuildException { + if (bundle == null) { + throw new BuildException("The bundle attribute must be set.", + getLocation()); + } + + if (startToken == null) { + throw new BuildException("The starttoken attribute must be set.", + getLocation()); + } + + if (endToken == null) { + throw new BuildException("The endtoken attribute must be set.", + getLocation()); + } + + if (bundleLanguage == null) { + Locale l = Locale.getDefault(); + bundleLanguage = l.getLanguage(); + } + + if (bundleCountry == null) { + bundleCountry = Locale.getDefault().getCountry(); + } + + if (bundleVariant == null) { + Locale l = new Locale(bundleLanguage, bundleCountry); + bundleVariant = l.getVariant(); + } + + if (toDir == null) { + throw new BuildException("The todir attribute must be set.", + getLocation()); + } + + if (!toDir.exists()) { + toDir.mkdirs(); + } else if (toDir.isFile()) { + throw new BuildException(toDir + " is not a directory"); + } + + if (srcEncoding == null) { + srcEncoding = System.getProperty("file.encoding"); + } + + if (destEncoding == null) { + destEncoding = srcEncoding; + } + + if (bundleEncoding == null) { + bundleEncoding = srcEncoding; + } + + loadResourceMaps(); + + translate(); + } + + /** + * Load resource maps based on resource bundle encoding scheme. + * The resource bundle lookup searches for resource files with various + * suffixes on the basis of (1) the desired locale and (2) the default + * locale (basebundlename), in the following order from lower-level + * (more specific) to parent-level (less specific): + * + * basebundlename + "_" + language1 + "_" + country1 + "_" + variant1 + * basebundlename + "_" + language1 + "_" + country1 + * basebundlename + "_" + language1 + * basebundlename + * basebundlename + "_" + language2 + "_" + country2 + "_" + variant2 + * basebundlename + "_" + language2 + "_" + country2 + * basebundlename + "_" + language2 + * + * To the generated name, a ".properties" string is appeneded and + * once this file is located, it is treated just like a properties file + * but with bundle encoding also considered while loading. + */ + private void loadResourceMaps() throws BuildException { + Locale locale = new Locale(bundleLanguage, + bundleCountry, + bundleVariant); + String language = locale.getLanguage().length() > 0 + ? "_" + locale.getLanguage() : ""; + String country = locale.getCountry().length() > 0 + ? "_" + locale.getCountry() : ""; + String variant = locale.getVariant().length() > 0 + ? "_" + locale.getVariant() : ""; + String bundleFile = bundle + language + country + variant; + processBundle(bundleFile, BUNDLE_SPECIFIED_LANGUAGE_COUNTRY_VARIANT, false); + + bundleFile = bundle + language + country; + processBundle(bundleFile, BUNDLE_SPECIFIED_LANGUAGE_COUNTRY, false); + + bundleFile = bundle + language; + processBundle(bundleFile, BUNDLE_SPECIFIED_LANGUAGE, false); + + bundleFile = bundle; + processBundle(bundleFile, BUNDLE_NOMATCH, false); + + //Load default locale bundle files + //using default file encoding scheme. + locale = Locale.getDefault(); + + language = locale.getLanguage().length() > 0 + ? "_" + locale.getLanguage() : ""; + country = locale.getCountry().length() > 0 + ? "_" + locale.getCountry() : ""; + variant = locale.getVariant().length() > 0 + ? "_" + locale.getVariant() : ""; + bundleEncoding = System.getProperty("file.encoding"); + + bundleFile = bundle + language + country + variant; + processBundle(bundleFile, BUNDLE_DEFAULT_LANGUAGE_COUNTRY_VARIANT, false); + + bundleFile = bundle + language + country; + processBundle(bundleFile, BUNDLE_DEFAULT_LANGUAGE_COUNTRY, false); + + bundleFile = bundle + language; + processBundle(bundleFile, BUNDLE_DEFAULT_LANGUAGE, true); + } + + /** + * Process each file that makes up this bundle. + */ + private void processBundle(final String bundleFile, final int i, + final boolean checkLoaded) throws BuildException { + final File propsFile = getProject().resolveFile(bundleFile + ".properties"); + FileInputStream ins = null; + try { + ins = new FileInputStream(propsFile); + loaded = true; + bundleLastModified[i] = propsFile.lastModified(); + log("Using " + propsFile, Project.MSG_DEBUG); + loadResourceMap(ins); + } catch (IOException ioe) { + log(propsFile + " not found.", Project.MSG_DEBUG); + //if all resource files associated with this bundle + //have been scanned for and still not able to + //find a single resrouce file, throw exception + if (!loaded && checkLoaded) { + throw new BuildException(ioe.getMessage(), getLocation()); + } + } + } + + /** + * Load resourceMap with key value pairs. Values of existing keys + * are not overwritten. Bundle's encoding scheme is used. + */ + private void loadResourceMap(FileInputStream ins) throws BuildException { + try { + BufferedReader in = null; + InputStreamReader isr = new InputStreamReader(ins, bundleEncoding); + in = new BufferedReader(isr); + String line = null; + while ((line = in.readLine()) != null) { + //So long as the line isn't empty and isn't a comment... + if (line.trim().length() > 1 && '#' != line.charAt(0) && '!' != line.charAt(0)) { + //Legal Key-Value separators are :, = and white space. + int sepIndex = line.indexOf('='); + if (-1 == sepIndex) { + sepIndex = line.indexOf(':'); + } + if (-1 == sepIndex) { + for (int k = 0; k < line.length(); k++) { + if (Character.isSpaceChar(line.charAt(k))) { + sepIndex = k; + break; + } + } + } + //Only if we do have a key is there going to be a value + if (-1 != sepIndex) { + String key = line.substring(0, sepIndex).trim(); + String value = line.substring(sepIndex + 1).trim(); + //Handle line continuations, if any + while (value.endsWith("\\")) { + value = value.substring(0, value.length() - 1); + line = in.readLine(); + if (line != null) { + value = value + line.trim(); + } else { + break; + } + } + if (key.length() > 0) { + //Has key already been loaded into resourceMap? + if (resourceMap.get(key) == null) { + resourceMap.put(key, value); + } + } + } + } + } + if (in != null) { + in.close(); + } + } catch (IOException ioe) { + throw new BuildException(ioe.getMessage(), getLocation()); + } + } + + /** + * Reads source file line by line using the source encoding and + * searches for keys that are sandwiched between the startToken + * and endToken. The values for these keys are looked up from + * the hashtable and substituted. If the hashtable doesn't + * contain the key, they key itself is used as the value. + * Detination files and directories are created as needed. + * The destination file is overwritten only if + * the forceoverwritten attribute is set to true if + * the source file or any associated bundle resource file is + * newer than the destination file. + */ + private void translate() throws BuildException { + int filesProcessed = 0; + final int size = filesets.size(); + for (int i = 0; i < size; i++) { + FileSet fs = (FileSet) filesets.elementAt(i); + DirectoryScanner ds = fs.getDirectoryScanner(getProject()); + String[] srcFiles = ds.getIncludedFiles(); + for (int j = 0; j < srcFiles.length; j++) { + try { + File dest = FILE_UTILS.resolveFile(toDir, srcFiles[j]); + //Make sure parent dirs exist, else, create them. + try { + File destDir = new File(dest.getParent()); + if (!destDir.exists()) { + destDir.mkdirs(); + } + } catch (Exception e) { + log("Exception occurred while trying to check/create " + + " parent directory. " + e.getMessage(), + Project.MSG_DEBUG); + } + destLastModified = dest.lastModified(); + File src = FILE_UTILS.resolveFile(ds.getBasedir(), srcFiles[j]); + srcLastModified = src.lastModified(); + //Check to see if dest file has to be recreated + boolean needsWork = forceOverwrite + || destLastModified < srcLastModified; + if (!needsWork) { + for (int icounter = 0; icounter < BUNDLE_MAX_ALTERNATIVES; icounter++) { + needsWork = (destLastModified < bundleLastModified[icounter]); + if (needsWork) { + break; + } + } + } + if (needsWork) { + log("Processing " + srcFiles[j], + Project.MSG_DEBUG); + translateOneFile(src, dest); + ++filesProcessed; + } else { + log("Skipping " + srcFiles[j] + + " as destination file is up to date", + Project.MSG_VERBOSE); + } + } catch (IOException ioe) { + throw new BuildException(ioe.getMessage(), getLocation()); + } + } + } + log("Translation performed on " + filesProcessed + " file(s).", Project.MSG_DEBUG); + } + + private void translateOneFile(File src, File dest) throws IOException { + BufferedWriter out = null; + BufferedReader in = null; + try { + FileOutputStream fos = new FileOutputStream(dest); + out = new BufferedWriter(new OutputStreamWriter(fos, destEncoding)); + FileInputStream fis = new FileInputStream(src); + in = new BufferedReader(new InputStreamReader(fis, srcEncoding)); + String line; + LineTokenizer lineTokenizer = new LineTokenizer(); + lineTokenizer.setIncludeDelims(true); + line = lineTokenizer.getToken(in); + while ((line) != null) { + // 2003-02-21 new replace algorithm by tbee (tbee@tbee.org) + // because it wasn't able to replace something like "@aaa;@bbb;" + + // is there a startToken + // and there is still stuff following the startToken + int startIndex = line.indexOf(startToken); + while (startIndex >= 0 + && (startIndex + startToken.length()) <= line.length()) { + // the new value, this needs to be here + // because it is required to calculate the next position to + // search from at the end of the loop + String replace = null; + + // we found a starttoken, is there an endtoken following? + // start at token+tokenlength because start and end + // token may be identical + int endIndex = line.indexOf(endToken, startIndex + + startToken.length()); + if (endIndex < 0) { + startIndex += 1; + } else { + // grab the token + String token = line.substring(startIndex + + startToken.length(), + endIndex); + + // If there is a white space or = or :, then + // it isn't to be treated as a valid key. + boolean validToken = true; + for (int k = 0; k < token.length() && validToken; k++) { + char c = token.charAt(k); + if (c == ':' || c == '=' + || Character.isSpaceChar(c)) { + validToken = false; + } + } + if (!validToken) { + startIndex += 1; + } else { + // find the replace string + if (resourceMap.containsKey(token)) { + replace = (String) resourceMap.get(token); + } else { + log("Replacement string missing for: " + token, + Project.MSG_VERBOSE); + replace = startToken + token + endToken; + } + + + // generate the new line + line = line.substring(0, startIndex) + replace + + line.substring(endIndex + endToken.length()); + + // set start position for next search + startIndex += replace.length(); + } + } + + // find next starttoken + startIndex = line.indexOf(startToken, startIndex); + } + out.write(line); + line = lineTokenizer.getToken(in); + } + } finally { + FileUtils.close(in); + FileUtils.close(out); + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/image/Image.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/image/Image.java new file mode 100644 index 00000000..162e3756 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/image/Image.java @@ -0,0 +1,421 @@ +/* + * 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.tools.ant.taskdefs.optional.image; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Locale; +import java.util.Vector; + +import javax.media.jai.JAI; +import javax.media.jai.PlanarImage; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.MatchingTask; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Mapper; +import org.apache.tools.ant.types.optional.image.Draw; +import org.apache.tools.ant.types.optional.image.ImageOperation; +import org.apache.tools.ant.types.optional.image.Rotate; +import org.apache.tools.ant.types.optional.image.Scale; +import org.apache.tools.ant.types.optional.image.TransformOperation; +import org.apache.tools.ant.util.FileNameMapper; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.IdentityMapper; + +import com.sun.media.jai.codec.FileSeekableStream; + +/** + * A MatchingTask which relies on <a + * href="http://java.sun.com/products/java-media/jai">JAI (Java + * Advanced Imaging)</a> to perform image manipulation operations on + * existing images. The operations are represented as ImageOperation + * DataType objects. The operations are arranged to conform to the + * Chaining Model of JAI. Check out the <a + * href="http://java.sun.com/products/java-media/jai/forDevelopers/jai1_0_1guide-unc/"> + * JAI Programming Guide</a>. + * + * @see org.apache.tools.ant.types.optional.image.ImageOperation + * @see org.apache.tools.ant.types.DataType + */ +public class Image extends MatchingTask { + // CheckStyle:VisibilityModifier OFF - bc + protected Vector instructions = new Vector(); + protected boolean overwrite = false; + protected Vector filesets = new Vector(); + protected File srcDir = null; + protected File destDir = null; + + // CheckStyle:MemberNameCheck OFF - bc + + //cannot remove underscores due to protected visibility >:( + protected String str_encoding = "JPEG"; + protected boolean garbage_collect = false; + + private boolean failonerror = true; + + // CheckStyle:MemberNameCheck ON + + // CheckStyle:VisibilityModifier ON + + private Mapper mapperElement = null; + + /** + * Add a set of files to be deleted. + * @param set the FileSet to add. + */ + public void addFileset(FileSet set) { + filesets.addElement(set); + } + + /** + * Set whether to fail on error. + * If false, note errors to the output but keep going. + * @param failonerror true or false. + */ + public void setFailOnError(boolean failonerror) { + this.failonerror = failonerror; + } + + /** + * Set the source dir to find the image files. + * @param srcDir the directory in which the image files reside. + */ + public void setSrcdir(File srcDir) { + this.srcDir = srcDir; + } + + /** + * Set the image encoding type. <a + * href="http://java.sun.com/products/java-media/jai/forDevelopers/jai1_0_1guide-unc/Encode.doc.html#56610"> + * See this table in the JAI Programming Guide</a>. + * @param encoding the String image encoding. + */ + public void setEncoding(String encoding) { + str_encoding = encoding; + } + + /** + * Set whether to overwrite a file if there is a naming conflict. + * @param overwrite whether to overwrite. + */ + public void setOverwrite(boolean overwrite) { + this.overwrite = overwrite; + } + + /** + * Set whether to invoke Garbage Collection after each image processed. + * Defaults to false. + * @param gc whether to invoke the garbage collector. + */ + public void setGc(boolean gc) { + garbage_collect = gc; + } + + /** + * Set the destination directory for manipulated images. + * @param destDir The destination directory. + */ + public void setDestDir(File destDir) { + this.destDir = destDir; + } + + /** + * Add an ImageOperation to chain. + * @param instr The ImageOperation to append to the chain. + */ + public void addImageOperation(ImageOperation instr) { + instructions.add(instr); + } + + /** + * Add a Rotate ImageOperation to the chain. + * @param instr The Rotate operation to add to the chain. + * @see org.apache.tools.ant.types.optional.image.Rotate + */ + public void addRotate(Rotate instr) { + instructions.add(instr); + } + + /** + * Add a Scale ImageOperation to the chain. + * @param instr The Scale operation to add to the chain. + * @see org.apache.tools.ant.types.optional.image.Scale + */ + public void addScale(Scale instr) { + instructions.add(instr); + } + + /** + * Add a Draw ImageOperation to the chain. DrawOperation + * DataType objects can be nested inside the Draw object. + * @param instr The Draw operation to add to the chain. + * @see org.apache.tools.ant.types.optional.image.Draw + * @see org.apache.tools.ant.types.optional.image.DrawOperation + */ + public void addDraw(Draw instr) { + instructions.add(instr); + } + + /** + * Add an ImageOperation to chain. + * @param instr The ImageOperation to append to the chain. + * @since Ant 1.7 + */ + public void add(ImageOperation instr) { + addImageOperation(instr); + } + + /** + * Defines the mapper to map source to destination files. + * @return a mapper to be configured + * @exception BuildException if more than one mapper is defined + * @since Ant 1.8.0 + */ + public Mapper createMapper() throws BuildException { + if (mapperElement != null) { + throw new BuildException("Cannot define more than one mapper", + getLocation()); + } + mapperElement = new Mapper(getProject()); + return mapperElement; + } + + /** + * Add a nested filenamemapper. + * @param fileNameMapper the mapper to add. + * @since Ant 1.8.0 + */ + public void add(FileNameMapper fileNameMapper) { + createMapper().add(fileNameMapper); + } + + /** + * Executes all the chained ImageOperations on the files inside + * the directory. + * @since Ant 1.8.0 + */ + public int processDir(final File srcDir, final String[] srcNames, + final File dstDir, final FileNameMapper mapper) { + int writeCount = 0; + + for (int i = 0; i < srcNames.length; ++i) { + final String srcName = srcNames[i]; + final File srcFile = new File(srcDir, srcName).getAbsoluteFile(); + + final String[] dstNames = mapper.mapFileName(srcName); + if (dstNames == null) { + log(srcFile + " skipped, don't know how to handle it", + Project.MSG_VERBOSE); + continue; + } + + for (int j = 0; j < dstNames.length; ++j){ + + final String dstName = dstNames[j]; + final File dstFile = new File(dstDir, dstName).getAbsoluteFile(); + + if (dstFile.exists()){ + // avoid overwriting unless necessary + if(!overwrite + && srcFile.lastModified() <= dstFile.lastModified()) { + + log(srcFile + " omitted as " + dstFile + + " is up to date.", Project.MSG_VERBOSE); + + // don't overwrite the file + continue; + } + + // avoid extra work while overwriting + if (!srcFile.equals(dstFile)){ + dstFile.delete(); + } + } + processFile(srcFile, dstFile); + ++writeCount; + } + } + + // run the garbage collector if wanted + if (garbage_collect) { + System.gc(); + } + + return writeCount; + } + + /** + * Executes all the chained ImageOperations on the file + * specified. + * @param file The file to be processed. + * @deprecated this method isn't used anymore + */ + public void processFile(File file) { + processFile(file, new File(destDir == null + ? srcDir : destDir, file.getName())); + } + + /** + * Executes all the chained ImageOperations on the file + * specified. + * @param file The file to be processed. + * @param newFile The file to write to. + * @since Ant 1.8.0 + */ + public void processFile(File file, File newFile) { + try { + log("Processing File: " + file.getAbsolutePath()); + + FileSeekableStream input = null; + PlanarImage image = null; + try { + input = new FileSeekableStream(file); + image = JAI.create("stream", input); + final int size = instructions.size(); + for (int i = 0; i < size; i++) { + Object instr = instructions.elementAt(i); + if (instr instanceof TransformOperation) { + image = ((TransformOperation) instr) + .executeTransformOperation(image); + } else { + log("Not a TransformOperation: " + instr); + } + } + } finally { + FileUtils.close(input); + } + + File dstParent = newFile.getParentFile(); + if (!dstParent.isDirectory() + && !(dstParent.mkdirs() || dstParent.isDirectory())) { + throw new BuildException("Failed to create parent directory " + + dstParent); + } + + if ((overwrite && newFile.exists()) && (!newFile.equals(file))) { + newFile.delete(); + } + + FileOutputStream stream = null; + try { + stream = new FileOutputStream(newFile); + + JAI.create("encode", image, stream, + str_encoding.toUpperCase(Locale.ENGLISH), + null); + stream.flush(); + } finally { + FileUtils.close(stream); + } + } catch (IOException err) { + if (!file.equals(newFile)){ + newFile.delete(); + } + if (!failonerror) { + log("Error processing file: " + err); + } else { + throw new BuildException(err); + } + } catch (java.lang.RuntimeException rerr) { + if (!file.equals(newFile)){ + newFile.delete(); + } + if (!failonerror) { + log("Error processing file: " + rerr); + } else { + throw new BuildException(rerr); + } + } + } + + /** + * Executes the Task. + * @throws BuildException on error. + */ + public void execute() throws BuildException { + + validateAttributes(); + + try { + File dest = destDir != null ? destDir : srcDir; + + int writeCount = 0; + + // build mapper + final FileNameMapper mapper; + if (mapperElement==null){ + mapper = new IdentityMapper(); + } else { + mapper = mapperElement.getImplementation(); + } + + // deal with specified srcDir + if (srcDir != null) { + final DirectoryScanner ds = super.getDirectoryScanner(srcDir); + + final String[] files = ds.getIncludedFiles(); + writeCount += processDir(srcDir, files, dest, mapper); + } + // deal with the filesets + final int size = filesets.size(); + for (int i = 0; i < size; i++) { + final FileSet fs = (FileSet) filesets.elementAt(i); + final DirectoryScanner ds = + fs.getDirectoryScanner(getProject()); + final String[] files = ds.getIncludedFiles(); + final File fromDir = fs.getDir(getProject()); + writeCount += processDir(fromDir, files, dest, mapper); + } + + if (writeCount>0){ + log("Processed " + writeCount + + (writeCount == 1 ? " image." : " images.")); + } + + } catch (Exception err) { + err.printStackTrace(); + throw new BuildException(err.getMessage()); + } + } + + /** + * Ensure we have a consistent and legal set of attributes, and set + * any internal flags necessary based on different combinations + * of attributes. + * @throws BuildException on error. + */ + protected void validateAttributes() throws BuildException { + if (srcDir == null && filesets.size() == 0) { + throw new BuildException("Specify at least one source" + + "--a srcDir or a fileset."); + } + if (srcDir == null && destDir == null) { + throw new BuildException("Specify the destDir, or the srcDir."); + } + if (str_encoding.equalsIgnoreCase("jpg")) { + str_encoding = "JPEG"; + } else if (str_encoding.equalsIgnoreCase("tif")) { + str_encoding = "TIFF"; + } + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/AbstractHotDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/AbstractHotDeploymentTool.java new file mode 100644 index 00000000..fdbde749 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/AbstractHotDeploymentTool.java @@ -0,0 +1,193 @@ +/* + * 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.tools.ant.taskdefs.optional.j2ee; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.types.Path; + +/** + * Abstract class to support vendor-specific hot deployment tools. + * This class will validate boilerplate attributes. + * + * Subclassing this class for a vendor specific tool involves the + * following. + * <ol><li>Implement the <code>isActionValid()<code> method to insure the + * action supplied as the "action" attribute of ServerDeploy is valid. + * <li>Implement the <code>validateAttributes()</code> method to insure + * all required attributes are supplied, and are in the correct format. + * <li>Add a <code>add<TOOL></code> method to the ServerDeploy + * class. This method will be called when Ant encounters a + * <code>add<TOOL></code> task nested in the + * <code>serverdeploy</code> task. + * <li>Define the <code>deploy</code> method. This method should perform + * whatever task it takes to hot-deploy the component. IE: spawn a JVM and + * run class, exec a native executable, run Java code... + * + * @see org.apache.tools.ant.taskdefs.optional.j2ee.HotDeploymentTool + * @see org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy + */ +public abstract class AbstractHotDeploymentTool implements HotDeploymentTool { + /** The parent task **/ + private ServerDeploy task; + + /** The classpath passed to the JVM on execution. **/ + private Path classpath; + + /** The username for the deployment server. **/ + private String userName; + + /** The password for the deployment server. **/ + private String password; + + /** The address of the deployment server **/ + private String server; + + /** + * Add a classpath as a nested element. + * @return A Path object representing the classpath to be used. + */ + public Path createClasspath() { + if (classpath == null) { + classpath = new Path(task.getProject()); + } + return classpath.createPath(); + } + + /** + * Determines if the "action" attribute defines a valid action. + * <p>Subclasses should determine if the action passed in is + * supported by the vendor's deployment tool. + * <p>Actions may by "deploy", "delete", etc... It all depends + * on the tool. + * @return true if the "action" attribute is valid, false if not. + */ + protected abstract boolean isActionValid(); + + /** + * Validates the passed in attributes. + * Subclasses should chain to this super-method to insure + * validation of boilerplate attributes. + * <p>Only the "action" attribute is required in the + * base class. Subclasses should check attributes accordingly. + * @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete. + */ + public void validateAttributes() throws BuildException { + if (task.getAction() == null) { + throw new BuildException("The \"action\" attribute must be set"); + } + + if (!isActionValid()) { + throw new BuildException("Invalid action \"" + task.getAction() + "\" passed"); + } + + if (classpath == null) { + throw new BuildException("The classpath attribute must be set"); + } + } + + /** + * Perform the actual deployment. + * It's up to the subclasses to implement the actual behavior. + * @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete. + */ + public abstract void deploy() throws BuildException; + + /** + * Sets the parent task. + * @param task a ServerDeploy object representing the parent task. + * @ant.attribute ignore="true" + */ + public void setTask(ServerDeploy task) { + this.task = task; + } + + /** + * Returns the task field, a ServerDeploy object. + * @return An ServerDeploy representing the parent task. + */ + protected ServerDeploy getTask() { + return task; + } + + /** + * gets the classpath field. + * @return A Path representing the "classpath" attribute. + */ + public Path getClasspath() { + return classpath; + } + + /** + * The classpath to be passed to the JVM running the tool; + * optional depending upon the tool. + * The classpath may also be supplied as a nested element. + * @param classpath A Path object representing the "classpath" attribute. + */ + public void setClasspath(Path classpath) { + this.classpath = classpath; + } + + /** + * Returns the userName field. + * @return A String representing the "userName" attribute. + */ + public String getUserName() { + return userName; + } + + /** + * The user with privileges to deploy applications to the server; optional. + * @param userName A String representing the "userName" attribute. + */ + public void setUserName(String userName) { + this.userName = userName; + } + + /** + * Returns the password field. + * @return A String representing the "password" attribute. + */ + public String getPassword() { + return password; + } + + /** + * The password of the user; optional. + * @param password A String representing the "password" attribute. + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * Returns the server field. + * @return A String representing the "server" attribute. + */ + public String getServer() { + return server; + } + + /** + * The address or URL for the server where the component will be deployed. + * @param server A String representing the "server" attribute. + */ + public void setServer(String server) { + this.server = server; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/GenericHotDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/GenericHotDeploymentTool.java new file mode 100644 index 00000000..5a5abbab --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/GenericHotDeploymentTool.java @@ -0,0 +1,140 @@ +/* + * 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.tools.ant.taskdefs.optional.j2ee; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.Java; +import org.apache.tools.ant.types.Commandline; + +/** + * A generic tool for J2EE server hot deployment. + * <p>The simple implementation spawns a JVM with the supplied + * class name, jvm args, and arguments. + * + * @see org.apache.tools.ant.taskdefs.optional.j2ee.HotDeploymentTool + * @see org.apache.tools.ant.taskdefs.optional.j2ee.AbstractHotDeploymentTool + * @see org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy + */ +public class GenericHotDeploymentTool extends AbstractHotDeploymentTool { + /** A Java task used to run the deployment tool **/ + private Java java; + + /** The fully qualified class name of the deployment tool **/ + private String className; + + /** List of valid actions **/ + private static final String[] VALID_ACTIONS = {ACTION_DEPLOY}; + + /** + * Add a nested argument element to hand to the deployment tool; optional. + * @return A Commandline.Argument object representing the + * command line argument being passed when the deployment + * tool is run. IE: "-user=mark", "-password=venture"... + */ + public Commandline.Argument createArg() { + return java.createArg(); + } + + /** + * Add a nested argument element to hand to the JVM running the + * deployment tool. + * Creates a nested arg element. + * @return A Commandline.Argument object representing the + * JVM command line argument being passed when the deployment + * tool is run. IE: "-ms64m", "-mx128m"... + */ + public Commandline.Argument createJvmarg() { + return java.createJvmarg(); + } + + /** + * Determines if the "action" attribute defines a valid action. + * <p>Subclasses should determine if the action passed in is + * supported by the vendor's deployment tool. + * For this generic implementation, the only valid action is "deploy" + * @return true if the "action" attribute is valid, false if not. + */ + protected boolean isActionValid() { + return (getTask().getAction().equals(VALID_ACTIONS[0])); + } + + /** + * Sets the parent task. + * @param task An ServerDeploy object representing the parent task. + * @ant.attribute ignored="true" + */ + public void setTask(ServerDeploy task) { + super.setTask(task); + java = new Java(task); + } + + /** + * Perform the actual deployment. + * For this generic implementation, a JVM is spawned using the + * supplied classpath, classname, JVM args, and command line arguments. + * @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete. + */ + public void deploy() throws BuildException { + java.setClassname(className); + java.setClasspath(getClasspath()); + java.setFork(true); + java.setFailonerror(true); + java.execute(); + } + + /** + * Validates the passed in attributes. + * Ensures the className and arguments attribute have been set. + * @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete. + */ + public void validateAttributes() throws BuildException { + super.validateAttributes(); + + if (className == null) { + throw new BuildException("The classname attribute must be set"); + } + } + + /** + * The name of the class to execute to perform + * deployment; required. + * Example: "com.foobar.tools.deploy.DeployTool" + * @param className The fully qualified class name of the class + * to perform deployment. + */ + public void setClassName(String className) { + this.className = className; + } + + /** + * get the java attribute. + * @return the java attribute. + */ + public Java getJava() { + return java; + } + + /** + * Get the classname attribute. + * @return the classname value. + */ + public String getClassName() { + return className; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/HotDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/HotDeploymentTool.java new file mode 100644 index 00000000..16c55d8e --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/HotDeploymentTool.java @@ -0,0 +1,61 @@ +/* + * 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.tools.ant.taskdefs.optional.j2ee; + +import org.apache.tools.ant.BuildException; + +/** + * An interface for vendor-specific "hot" deployment tools. + * + * @see org.apache.tools.ant.taskdefs.optional.j2ee.AbstractHotDeploymentTool + * @see org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy + */ +public interface HotDeploymentTool { + /** The delete action String **/ + String ACTION_DELETE = "delete"; + + /** The deploy action String **/ + String ACTION_DEPLOY = "deploy"; + + /** The list action String **/ + String ACTION_LIST = "list"; + + /** The undeploy action String **/ + String ACTION_UNDEPLOY = "undeploy"; + + /** The update action String **/ + String ACTION_UPDATE = "update"; + + /** + * Validates the passed in attributes. + * @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete. + */ + void validateAttributes() throws BuildException; + + /** + * Perform the actual deployment. + * @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete. + */ + void deploy() throws BuildException; + + /** + * Sets the parent task. + * @param task A ServerDeploy object representing the parent task. + */ + void setTask(ServerDeploy task); +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/JonasHotDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/JonasHotDeploymentTool.java new file mode 100644 index 00000000..c7a33a19 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/JonasHotDeploymentTool.java @@ -0,0 +1,252 @@ +/* + * 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.tools.ant.taskdefs.optional.j2ee; + +import java.io.File; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.Java; +import org.apache.tools.ant.types.Path; + +/** + * An Ant wrapper task for the weblogic.deploy tool. This is used + * to hot-deploy J2EE applications to a running WebLogic server. + * This is <b>not</b> the same as creating the application + * archive. This task assumes the archive (EAR, JAR, or WAR) file + * has been assembled and is supplied as the "source" attribute. + * <p> + * + * In the end, this task assembles the commandline parameters and + * runs the weblogic.deploy tool in a separate JVM. + * + *@see org.apache.tools.ant.taskdefs.optional.j2ee.HotDeploymentTool + *@see org.apache.tools.ant.taskdefs.optional.j2ee.AbstractHotDeploymentTool + *@see org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy + */ +public class JonasHotDeploymentTool extends GenericHotDeploymentTool implements HotDeploymentTool { + + /** + * Description of the Field + */ + protected static final String DEFAULT_ORB = "RMI"; + + /** + * The classname of the tool to run * + */ + private static final String JONAS_DEPLOY_CLASS_NAME = "org.objectweb.jonas.adm.JonasAdmin"; + + /** + * All the valid actions that weblogic.deploy permits * + */ + private static final String[] VALID_ACTIONS + = {ACTION_DELETE, ACTION_DEPLOY, ACTION_LIST, ACTION_UNDEPLOY, ACTION_UPDATE}; + + /** + * Description of the Field + */ + private File jonasroot; + + /** + * Description of the Field + */ + private String orb = null; + + /** + * Description of the Field + */ + private String davidHost; + + /** + * Description of the Field + */ + private int davidPort; + + + /** + * Set the host for the David ORB; required if + * ORB==david. + * + *@param inValue The new davidhost value + */ + public void setDavidhost(final String inValue) { + davidHost = inValue; + } + + + /** + * Set the port for the David ORB; required if + * ORB==david. + * + *@param inValue The new davidport value + */ + public void setDavidport(final int inValue) { + davidPort = inValue; + } + + + /** + * set the jonas root directory (-Dinstall.root=). This + * element is required. + * + *@param inValue The new jonasroot value + */ + public void setJonasroot(final File inValue) { + jonasroot = inValue; + } + + + /** + * + * Choose your ORB : RMI, JEREMIE, DAVID, ...; optional. + * If omitted, it defaults + * to the one present in classpath. The corresponding JOnAS JAR is + * automatically added to the classpath. If your orb is DAVID (RMI/IIOP) you must + * specify davidhost and davidport properties. + * + *@param inValue RMI, JEREMIE, DAVID,... + */ + public void setOrb(final String inValue) { + orb = inValue; + } + + + /** + * gets the classpath field. + * + *@return A Path representing the "classpath" attribute. + */ + public Path getClasspath() { + + Path aClassPath = super.getClasspath(); + + if (aClassPath == null) { + aClassPath = new Path(getTask().getProject()); + } + if (orb != null) { + String aOrbJar = new File(jonasroot, "lib/" + orb + "_jonas.jar").toString(); + String aConfigDir = new File(jonasroot, "config/").toString(); + Path aJOnASOrbPath = new Path(aClassPath.getProject(), + aOrbJar + File.pathSeparator + aConfigDir); + aClassPath.append(aJOnASOrbPath); + } + return aClassPath; + } + + + /** + * Validates the passed in attributes. <p> + * + * The rules are: + * <ol> + * <li> If action is "deploy" or "update" the "application" + * and "source" attributes must be supplied. + * <li> If action is "delete" or "undeploy" the + * "application" attribute must be supplied. + * + *@exception BuildException Description + * of Exception + */ + public void validateAttributes() throws BuildException { + // super.validateAttributes(); // don't want to call this method + + Java java = getJava(); + + String action = getTask().getAction(); + if (action == null) { + throw new BuildException("The \"action\" attribute must be set"); + } + + if (!isActionValid()) { + throw new BuildException("Invalid action \"" + action + "\" passed"); + } + + if (getClassName() == null) { + setClassName(JONAS_DEPLOY_CLASS_NAME); + } + + if (jonasroot == null || jonasroot.isDirectory()) { + java.createJvmarg().setValue("-Dinstall.root=" + jonasroot); + java.createJvmarg().setValue("-Djava.security.policy=" + jonasroot + + "/config/java.policy"); + + if ("DAVID".equals(orb)) { + java.createJvmarg().setValue("-Dorg.omg.CORBA.ORBClass" + + "=org.objectweb.david.libs.binding.orbs.iiop.IIOPORB"); + java.createJvmarg().setValue("-Dorg.omg.CORBA.ORBSingletonClass=" + + "org.objectweb.david.libs.binding.orbs.ORBSingletonClass"); + java.createJvmarg().setValue("-Djavax.rmi.CORBA.StubClass=" + + "org.objectweb.david.libs.stub_factories.rmi.StubDelegate"); + java.createJvmarg().setValue("-Djavax.rmi.CORBA.PortableRemoteObjectClass=" + + "org.objectweb.david.libs.binding.rmi.ORBPortableRemoteObjectDelegate"); + java.createJvmarg().setValue("-Djavax.rmi.CORBA.UtilClass=" + + "org.objectweb.david.libs.helpers.RMIUtilDelegate"); + java.createJvmarg().setValue("-Ddavid.CosNaming.default_method=0"); + java.createJvmarg().setValue("-Ddavid.rmi.ValueHandlerClass=" + + "com.sun.corba.se.internal.io.ValueHandlerImpl"); + if (davidHost != null) { + java.createJvmarg().setValue("-Ddavid.CosNaming.default_host=" + + davidHost); + } + if (davidPort != 0) { + java.createJvmarg().setValue("-Ddavid.CosNaming.default_port=" + + davidPort); + } + } + } + + if (getServer() != null) { + java.createArg().setLine("-n " + getServer()); + } + + if (action.equals(ACTION_DEPLOY) + || action.equals(ACTION_UPDATE) + || action.equals("redeploy")) { + java.createArg().setLine("-a " + getTask().getSource()); + } else if (action.equals(ACTION_DELETE) || action.equals(ACTION_UNDEPLOY)) { + java.createArg().setLine("-r " + getTask().getSource()); + } else if (action.equals(ACTION_LIST)) { + java.createArg().setValue("-l"); + } + } + + + /** + * Determines if the action supplied is valid. <p> + * + * Valid actions are contained in the static array + * VALID_ACTIONS + * + *@return true if the action attribute is valid, false if + * not. + */ + protected boolean isActionValid() { + boolean valid = false; + + String action = getTask().getAction(); + + for (int i = 0; i < VALID_ACTIONS.length; i++) { + if (action.equals(VALID_ACTIONS[i])) { + valid = true; + break; + } + } + + return valid; + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/ServerDeploy.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/ServerDeploy.java new file mode 100644 index 00000000..8965b8e8 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/ServerDeploy.java @@ -0,0 +1,153 @@ +/* + * 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.tools.ant.taskdefs.optional.j2ee; + +import java.io.File; +import java.util.Enumeration; +import java.util.Vector; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; + +/** + * Controls hot deployment tools for J2EE servers. + * + * This class is used as a framework for the creation of vendor specific + * hot deployment tools. + * + * @see org.apache.tools.ant.taskdefs.optional.j2ee.HotDeploymentTool + * @see org.apache.tools.ant.taskdefs.optional.j2ee.AbstractHotDeploymentTool + * @see org.apache.tools.ant.taskdefs.optional.j2ee.GenericHotDeploymentTool + * @see org.apache.tools.ant.taskdefs.optional.j2ee.WebLogicHotDeploymentTool + */ +public class ServerDeploy extends Task { + /** The action to be performed. IE: "deploy", "delete", etc... **/ + private String action; + + /** The source (fully-qualified path) to the component being deployed **/ + private File source; + + /** The vendor specific tool for deploying the component **/ + private Vector vendorTools = new Vector(); + + /////////////////////////////////////////////////////////////////////////// + // + // Place vendor specific tool creations here. + // + /////////////////////////////////////////////////////////////////////////// + + /** + * Creates a generic deployment tool. + * <p>Ant calls this method on creation to handle embedded "generic" elements + * in the ServerDeploy task. + * @param tool An instance of GenericHotDeployment tool, passed in by Ant. + */ + public void addGeneric(GenericHotDeploymentTool tool) { + tool.setTask(this); + vendorTools.addElement(tool); + } + + /** + * Creates a WebLogic deployment tool, for deployment to WebLogic servers. + * <p>Ant calls this method on creation to handle embedded "weblogic" elements + * in the ServerDeploy task. + * @param tool An instance of WebLogicHotDeployment tool, passed in by Ant. + */ + public void addWeblogic(WebLogicHotDeploymentTool tool) { + tool.setTask(this); + vendorTools.addElement(tool); + } + + /** + * Creates a JOnAS deployment tool, for deployment to JOnAS servers. + * <p>Ant calls this method on creation to handle embedded "jonas" elements + * in the ServerDeploy task. + * @param tool An instance of JonasHotDeployment tool, passed in by Ant. + */ + public void addJonas(JonasHotDeploymentTool tool) { + tool.setTask(this); + vendorTools.addElement(tool); + } + + + /////////////////////////////////////////////////////////////////////////// + // + // Execute method + // + /////////////////////////////////////////////////////////////////////////// + + /** + * Execute the task. + * <p>This method calls the deploy() method on each of the vendor-specific tools + * in the <code>vendorTools</code> collection. This performs the actual + * process of deployment on each tool. + * @exception org.apache.tools.ant.BuildException if the attributes + * are invalid or incomplete, or a failure occurs in the deployment process. + */ + public void execute() throws BuildException { + for (Enumeration e = vendorTools.elements(); + e.hasMoreElements();) { + HotDeploymentTool tool = (HotDeploymentTool) e.nextElement(); + tool.validateAttributes(); + tool.deploy(); + } + } + + /////////////////////////////////////////////////////////////////////////// + // + // Set/get methods + // + /////////////////////////////////////////////////////////////////////////// + + /** + * Returns the action field. + * @return A string representing the "action" attribute. + */ + public String getAction() { + return action; + } + + /** + * The action to be performed, usually "deploy"; required. + * Some tools support additional actions, such as "delete", "list", "undeploy", "update"... + * @param action A String representing the "action" attribute. + */ + public void setAction(String action) { + this.action = action; + } + + /** + * Returns the source field (the path/filename of the component to be + * deployed. + * @return A File object representing the "source" attribute. + */ + public File getSource() { + return source; + } + + /** + * The filename of the component to be deployed; optional + * depending upon the tool and the action. + * @param source String representing the "source" attribute. + */ + public void setSource(File source) { + this.source = source; + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/WebLogicHotDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/WebLogicHotDeploymentTool.java new file mode 100644 index 00000000..da875092 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/WebLogicHotDeploymentTool.java @@ -0,0 +1,247 @@ +/* + * 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.tools.ant.taskdefs.optional.j2ee; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.Java; + +/** + * An Ant wrapper task for the weblogic.deploy tool. This is used to + * hot-deploy J2EE applications to a running WebLogic server. + * This is <b>not</b> the same as creating the application archive. + * This task assumes the archive (EAR, JAR, or WAR) file has been + * assembled and is supplied as the "source" attribute. + * <p>In the end, this task assembles the commandline parameters + * and runs the weblogic.deploy tool in a separate JVM. + * + * @see org.apache.tools.ant.taskdefs.optional.j2ee.HotDeploymentTool + * @see org.apache.tools.ant.taskdefs.optional.j2ee.AbstractHotDeploymentTool + * @see org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy + */ +public class WebLogicHotDeploymentTool extends AbstractHotDeploymentTool + implements HotDeploymentTool { + private static final int STRING_BUFFER_SIZE = 1024; + /** The classname of the tool to run **/ + private static final String WEBLOGIC_DEPLOY_CLASS_NAME = "weblogic.deploy"; + + /** All the valid actions that weblogic.deploy permits **/ + private static final String[] VALID_ACTIONS + = {ACTION_DELETE, ACTION_DEPLOY, ACTION_LIST, ACTION_UNDEPLOY, ACTION_UPDATE}; + + /** Represents the "-debug" flag from weblogic.deploy **/ + private boolean debug; + + /** The application name that is being deployed **/ + private String application; + + /** The component name:target(s) for the "-component" argument of weblogic.deploy **/ + private String component; + + /** + * Perform the actual deployment. + * For this implementation, a JVM is spawned and the weblogic.deploy + * tools is executed. + * @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete. + */ + public void deploy() { + Java java = new Java(getTask()); + java.setFork(true); + java.setFailonerror(true); + java.setClasspath(getClasspath()); + + java.setClassname(WEBLOGIC_DEPLOY_CLASS_NAME); + java.createArg().setLine(getArguments()); + java.execute(); + } + + /** + * Validates the passed in attributes. + * <p>The rules are: + * <ol><li>If action is "deploy" or "update" the "application" and "source" + * attributes must be supplied. + * <li>If action is "delete" or "undeploy" the "application" attribute must + * be supplied. + * @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete + */ + public void validateAttributes() throws BuildException { + super.validateAttributes(); + + String action = getTask().getAction(); + + // check that the password has been set + if ((getPassword() == null)) { + throw new BuildException("The password attribute must be set."); + } + + // check for missing application on deploy & update + if ((action.equals(ACTION_DEPLOY) || action.equals(ACTION_UPDATE)) + && application == null) { + throw new BuildException("The application attribute must be set " + + "if action = " + action); + } + + // check for missing source on deploy & update + if ((action.equals(ACTION_DEPLOY) || action.equals(ACTION_UPDATE)) + && getTask().getSource() == null) { + throw new BuildException("The source attribute must be set if " + + "action = " + action); + } + + // check for missing application on delete & undeploy + if ((action.equals(ACTION_DELETE) || action.equals(ACTION_UNDEPLOY)) + && application == null) { + throw new BuildException("The application attribute must be set if " + + "action = " + action); + } + } + + /** + * Builds the arguments to pass to weblogic.deploy according to the + * supplied action. + * @return A String containing the arguments for the weblogic.deploy tool. + * @throws BuildException if there is an error. + */ + public String getArguments() throws BuildException { + String action = getTask().getAction(); + String args = null; + + if (action.equals(ACTION_DEPLOY) || action.equals(ACTION_UPDATE)) { + args = buildDeployArgs(); + } else if (action.equals(ACTION_DELETE) || action.equals(ACTION_UNDEPLOY)) { + args = buildUndeployArgs(); + } else if (action.equals(ACTION_LIST)) { + args = buildListArgs(); + } + + return args; + } + + /** + * Determines if the action supplied is valid. + * <p>Valid actions are contained in the static array VALID_ACTIONS + * @return true if the action attribute is valid, false if not. + */ + protected boolean isActionValid() { + boolean valid = false; + + String action = getTask().getAction(); + + for (int i = 0; i < VALID_ACTIONS.length; i++) { + if (action.equals(VALID_ACTIONS[i])) { + valid = true; + break; + } + } + + return valid; + } + + /** + * Builds the prefix arguments to pass to weblogic.deploy. + * These arguments are generic across all actions. + * @return A StringBuffer containing the prefix arguments. + * The action-specific build methods will append to this StringBuffer. + */ + protected StringBuffer buildArgsPrefix() { + ServerDeploy task = getTask(); + // constructs the "-url <url> -debug <action> <password>" portion + // of the commmand line + return new StringBuffer(STRING_BUFFER_SIZE) + .append((getServer() != null) + ? "-url " + getServer() + : "") + .append(" ") + .append(debug ? "-debug " : "") + .append((getUserName() != null) + ? "-username " + getUserName() + : "") + .append(" ") + .append(task.getAction()).append(" ") + .append(getPassword()).append(" "); + } + + /** + * Builds the arguments to pass to weblogic.deploy for deployment actions + * ("deploy" and "update"). + * @return A String containing the full argument string for weblogic.deploy. + */ + protected String buildDeployArgs() { + String args = buildArgsPrefix() + .append(application).append(" ") + .append(getTask().getSource()) + .toString(); + + if (component != null) { + args = "-component " + component + " " + args; + } + + return args; + } + + /** + * Builds the arguments to pass to weblogic.deploy for undeployment actions + * ("undeploy" and "delete"). + * @return A String containing the full argument string for weblogic.deploy. + */ + protected String buildUndeployArgs() { + return buildArgsPrefix() + .append(application).append(" ") + .toString(); + } + + /** + * Builds the arguments to pass to weblogic.deploy for the list action + * @return A String containing the full argument string for weblogic.deploy. + */ + protected String buildListArgs() { + return buildArgsPrefix() + .toString(); + } + + /** + * If set to true, additional information will be + * printed during the deployment process; optional. + * @param debug A boolean representing weblogic.deploy "-debug" flag. + */ + public void setDebug(boolean debug) { + this.debug = debug; + } + + /** + * The name of the application being deployed; required. + * @param application A String representing the application portion of the + * weblogic.deploy command line. + */ + public void setApplication(String application) { + this.application = application; + } + + /** + * the component string for the deployment targets; optional. + * It is in the form <code><component>:<target1>,<target2>...</code> + * Where component is the archive name (minus the .jar, .ear, .war + * extension). Targets are the servers where the components will be deployed + + * @param component A String representing the value of the "-component" + * argument of the weblogic.deploy command line argument. + */ + public void setComponent(String component) { + this.component = component; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJDoc.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJDoc.java new file mode 100644 index 00000000..a4dc0f48 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJDoc.java @@ -0,0 +1,226 @@ +/* + * 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.tools.ant.taskdefs.optional.javacc; + +import java.io.File; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Hashtable; + +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.LogStreamHandler; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.CommandlineJava; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.JavaEnvUtils; + +/** + * Runs the JJDoc compiler compiler. + * + */ +public class JJDoc extends Task { + + // keys to optional attributes + private static final String OUTPUT_FILE = "OUTPUT_FILE"; + private static final String TEXT = "TEXT"; + private static final String ONE_TABLE = "ONE_TABLE"; + + private final Hashtable optionalAttrs = new Hashtable(); + + private String outputFile = null; + private boolean plainText = false; + + private static final String DEFAULT_SUFFIX_HTML = ".html"; + private static final String DEFAULT_SUFFIX_TEXT = ".txt"; + + // required attributes + private File targetFile = null; + private File javaccHome = null; + + private CommandlineJava cmdl = new CommandlineJava(); + + private String maxMemory = null; + + /** + * Sets the TEXT BNF documentation option. + * @param plainText a <code>boolean</code> value. + */ + public void setText(boolean plainText) { + optionalAttrs.put(TEXT, plainText ? Boolean.TRUE : Boolean.FALSE); + this.plainText = plainText; + } + + /** + * Sets the ONE_TABLE documentation option. + * @param oneTable a <code>boolean</code> value. + */ + public void setOnetable(boolean oneTable) { + optionalAttrs.put(ONE_TABLE, oneTable ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * The outputfile to write the generated BNF documentation file to. + * If not set, the file is written with the same name as + * the JavaCC grammar file with a suffix .html or .txt. + * @param outputFile the name of the output file. + */ + public void setOutputfile(String outputFile) { + this.outputFile = outputFile; + } + + /** + * The javacc grammar file to process. + * @param target the grammar file. + */ + public void setTarget(File target) { + this.targetFile = target; + } + + /** + * The directory containing the JavaCC distribution. + * @param javaccHome the home directory. + */ + public void setJavacchome(File javaccHome) { + this.javaccHome = javaccHome; + } + + /** + * Corresponds -Xmx. + * + * @param max max memory parameter. + * @since Ant 1.8.3 + */ + public void setMaxmemory(String max) { + maxMemory = max; + } + + /** + * Constructor + */ + public JJDoc() { + cmdl.setVm(JavaEnvUtils.getJreExecutable("java")); + } + + /** + * Do the task. + * @throws BuildException if there is an error. + */ + public void execute() throws BuildException { + + // load command line with optional attributes + Enumeration iter = optionalAttrs.keys(); + while (iter.hasMoreElements()) { + String name = (String) iter.nextElement(); + Object value = optionalAttrs.get(name); + cmdl.createArgument() + .setValue("-" + name + ":" + value.toString()); + } + + if (targetFile == null || !targetFile.isFile()) { + throw new BuildException("Invalid target: " + targetFile); + } + + if (outputFile != null) { + cmdl.createArgument() .setValue("-" + OUTPUT_FILE + ":" + + outputFile.replace('\\', '/')); + } + + // use the directory containing the target as the output directory + File javaFile = new File(createOutputFileName(targetFile, outputFile, + plainText)); + + if (javaFile.exists() + && targetFile.lastModified() < javaFile.lastModified()) { + log("Target is already built - skipping (" + targetFile + ")", + Project.MSG_VERBOSE); + return; + } + + cmdl.createArgument().setValue(targetFile.getAbsolutePath()); + + final Path classpath = cmdl.createClasspath(getProject()); + final File javaccJar = JavaCC.getArchiveFile(javaccHome); + classpath.createPathElement().setPath(javaccJar.getAbsolutePath()); + classpath.addJavaRuntime(); + + cmdl.setClassname(JavaCC.getMainClass(classpath, + JavaCC.TASKDEF_TYPE_JJDOC)); + + cmdl.setMaxmemory(maxMemory); + final Commandline.Argument arg = cmdl.createVmArgument(); + arg.setValue("-Dinstall.root=" + javaccHome.getAbsolutePath()); + + final Execute process = + new Execute(new LogStreamHandler(this, + Project.MSG_INFO, + Project.MSG_INFO), + null); + log(cmdl.describeCommand(), Project.MSG_VERBOSE); + process.setCommandline(cmdl.getCommandline()); + + try { + if (process.execute() != 0) { + throw new BuildException("JJDoc failed."); + } + } catch (IOException e) { + throw new BuildException("Failed to launch JJDoc", e); + } + } + + private String createOutputFileName(File destFile, String optionalOutputFile, + boolean plain) { + String suffix = DEFAULT_SUFFIX_HTML; + String javaccFile = destFile.getAbsolutePath().replace('\\', '/'); + + if (plain) { + suffix = DEFAULT_SUFFIX_TEXT; + } + + if ((optionalOutputFile == null) || optionalOutputFile.equals("")) { + int filePos = javaccFile.lastIndexOf("/"); + + if (filePos >= 0) { + javaccFile = javaccFile.substring(filePos + 1); + } + + int suffixPos = javaccFile.lastIndexOf('.'); + + if (suffixPos == -1) { + optionalOutputFile = javaccFile + suffix; + } else { + String currentSuffix = javaccFile.substring(suffixPos); + + if (currentSuffix.equals(suffix)) { + optionalOutputFile = javaccFile + suffix; + } else { + optionalOutputFile = javaccFile.substring(0, suffixPos) + + suffix; + } + } + } else { + optionalOutputFile = optionalOutputFile.replace('\\', '/'); + } + + return (getProject().getBaseDir() + "/" + optionalOutputFile) + .replace('\\', '/'); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJTree.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJTree.java new file mode 100644 index 00000000..f5d126ee --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJTree.java @@ -0,0 +1,416 @@ +/* + * 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.tools.ant.taskdefs.optional.javacc; + +import java.io.File; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Hashtable; + +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.LogStreamHandler; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.CommandlineJava; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.JavaEnvUtils; + +/** + * Runs the JJTree compiler compiler. + * + */ +public class JJTree extends Task { + + // keys to optional attributes + private static final String OUTPUT_FILE = "OUTPUT_FILE"; + private static final String BUILD_NODE_FILES = "BUILD_NODE_FILES"; + private static final String MULTI = "MULTI"; + private static final String NODE_DEFAULT_VOID = "NODE_DEFAULT_VOID"; + private static final String NODE_FACTORY = "NODE_FACTORY"; + private static final String NODE_SCOPE_HOOK = "NODE_SCOPE_HOOK"; + private static final String NODE_USES_PARSER = "NODE_USES_PARSER"; + private static final String STATIC = "STATIC"; + private static final String VISITOR = "VISITOR"; + + private static final String NODE_PACKAGE = "NODE_PACKAGE"; + private static final String VISITOR_EXCEPTION = "VISITOR_EXCEPTION"; + private static final String NODE_PREFIX = "NODE_PREFIX"; + + private final Hashtable optionalAttrs = new Hashtable(); + + private String outputFile = null; + + private static final String DEFAULT_SUFFIX = ".jj"; + + // required attributes + private File outputDirectory = null; + private File targetFile = null; + private File javaccHome = null; + + private CommandlineJava cmdl = new CommandlineJava(); + + private String maxMemory = null; + + /** + * Sets the BUILD_NODE_FILES grammar option. + * @param buildNodeFiles a <code>boolean</code> value. + */ + public void setBuildnodefiles(boolean buildNodeFiles) { + optionalAttrs.put(BUILD_NODE_FILES, buildNodeFiles ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the MULTI grammar option. + * @param multi a <code>boolean</code> value. + */ + public void setMulti(boolean multi) { + optionalAttrs.put(MULTI, multi ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the NODE_DEFAULT_VOID grammar option. + * @param nodeDefaultVoid a <code>boolean</code> value. + */ + public void setNodedefaultvoid(boolean nodeDefaultVoid) { + optionalAttrs.put(NODE_DEFAULT_VOID, nodeDefaultVoid ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the NODE_FACTORY grammar option. + * @param nodeFactory a <code>boolean</code> value. + */ + public void setNodefactory(boolean nodeFactory) { + optionalAttrs.put(NODE_FACTORY, nodeFactory ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the NODE_SCOPE_HOOK grammar option. + * @param nodeScopeHook a <code>boolean</code> value. + */ + public void setNodescopehook(boolean nodeScopeHook) { + optionalAttrs.put(NODE_SCOPE_HOOK, nodeScopeHook ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the NODE_USES_PARSER grammar option. + * @param nodeUsesParser a <code>boolean</code> value. + */ + public void setNodeusesparser(boolean nodeUsesParser) { + optionalAttrs.put(NODE_USES_PARSER, nodeUsesParser ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the STATIC grammar option. + * @param staticParser a <code>boolean</code> value. + */ + public void setStatic(boolean staticParser) { + optionalAttrs.put(STATIC, staticParser ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the VISITOR grammar option. + * @param visitor a <code>boolean</code> value. + */ + public void setVisitor(boolean visitor) { + optionalAttrs.put(VISITOR, visitor ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the NODE_PACKAGE grammar option. + * @param nodePackage the option to use. + */ + public void setNodepackage(String nodePackage) { + optionalAttrs.put(NODE_PACKAGE, nodePackage); + } + + /** + * Sets the VISITOR_EXCEPTION grammar option. + * @param visitorException the option to use. + */ + public void setVisitorException(String visitorException) { + optionalAttrs.put(VISITOR_EXCEPTION, visitorException); + } + + /** + * Sets the NODE_PREFIX grammar option. + * @param nodePrefix the option to use. + */ + public void setNodeprefix(String nodePrefix) { + optionalAttrs.put(NODE_PREFIX, nodePrefix); + } + + /** + * The directory to write the generated JavaCC grammar and node files to. + * If not set, the files are written to the directory + * containing the grammar file. + * @param outputDirectory the output directory. + */ + public void setOutputdirectory(File outputDirectory) { + this.outputDirectory = outputDirectory; + } + + /** + * The outputfile to write the generated JavaCC grammar file to. + * If not set, the file is written with the same name as + * the JJTree grammar file with a suffix .jj. + * @param outputFile the output file name. + */ + public void setOutputfile(String outputFile) { + this.outputFile = outputFile; + } + + /** + * The jjtree grammar file to process. + * @param targetFile the grammar file. + */ + public void setTarget(File targetFile) { + this.targetFile = targetFile; + } + + /** + * The directory containing the JavaCC distribution. + * @param javaccHome the directory containing JavaCC. + */ + public void setJavacchome(File javaccHome) { + this.javaccHome = javaccHome; + } + + /** + * Corresponds -Xmx. + * + * @param max max memory parameter. + * @since Ant 1.8.3 + */ + public void setMaxmemory(String max) { + maxMemory = max; + } + + /** + * Constructor + */ + public JJTree() { + cmdl.setVm(JavaEnvUtils.getJreExecutable("java")); + } + + /** + * Run the task. + * @throws BuildException on error. + */ + public void execute() throws BuildException { + + // load command line with optional attributes + Enumeration iter = optionalAttrs.keys(); + while (iter.hasMoreElements()) { + String name = (String) iter.nextElement(); + Object value = optionalAttrs.get(name); + cmdl.createArgument().setValue("-" + name + ":" + value.toString()); + } + + if (targetFile == null || !targetFile.isFile()) { + throw new BuildException("Invalid target: " + targetFile); + } + + File javaFile = null; + + // use the directory containing the target as the output directory + if (outputDirectory == null) { + // convert backslashes to slashes, otherwise jjtree will + // put this as comments and this seems to confuse javacc + cmdl.createArgument().setValue("-OUTPUT_DIRECTORY:" + + getDefaultOutputDirectory()); + + javaFile = new File(createOutputFileName(targetFile, outputFile, + null)); + } else { + if (!outputDirectory.isDirectory()) { + throw new BuildException("'outputdirectory' " + outputDirectory + + " is not a directory."); + } + + // convert backslashes to slashes, otherwise jjtree will + // put this as comments and this seems to confuse javacc + cmdl.createArgument().setValue("-OUTPUT_DIRECTORY:" + + outputDirectory.getAbsolutePath() + .replace('\\', '/')); + + javaFile = new File(createOutputFileName(targetFile, outputFile, + outputDirectory + .getPath())); + } + + if (javaFile.exists() + && targetFile.lastModified() < javaFile.lastModified()) { + log("Target is already built - skipping (" + targetFile + ")", + Project.MSG_VERBOSE); + return; + } + + if (outputFile != null) { + cmdl.createArgument().setValue("-" + OUTPUT_FILE + ":" + + outputFile.replace('\\', '/')); + } + + cmdl.createArgument().setValue(targetFile.getAbsolutePath()); + + final Path classpath = cmdl.createClasspath(getProject()); + final File javaccJar = JavaCC.getArchiveFile(javaccHome); + classpath.createPathElement().setPath(javaccJar.getAbsolutePath()); + classpath.addJavaRuntime(); + + cmdl.setClassname(JavaCC.getMainClass(classpath, + JavaCC.TASKDEF_TYPE_JJTREE)); + + cmdl.setMaxmemory(maxMemory); + final Commandline.Argument arg = cmdl.createVmArgument(); + arg.setValue("-Dinstall.root=" + javaccHome.getAbsolutePath()); + + final Execute process = + new Execute(new LogStreamHandler(this, + Project.MSG_INFO, + Project.MSG_INFO), + null); + log(cmdl.describeCommand(), Project.MSG_VERBOSE); + process.setCommandline(cmdl.getCommandline()); + + try { + if (process.execute() != 0) { + throw new BuildException("JJTree failed."); + } + } catch (IOException e) { + throw new BuildException("Failed to launch JJTree", e); + } + } + + private String createOutputFileName(File destFile, String optionalOutputFile, + String outputDir) { + optionalOutputFile = validateOutputFile(optionalOutputFile, + outputDir); + String jjtreeFile = destFile.getAbsolutePath().replace('\\', '/'); + + if ((optionalOutputFile == null) || optionalOutputFile.equals("")) { + int filePos = jjtreeFile.lastIndexOf("/"); + + if (filePos >= 0) { + jjtreeFile = jjtreeFile.substring(filePos + 1); + } + + int suffixPos = jjtreeFile.lastIndexOf('.'); + + if (suffixPos == -1) { + optionalOutputFile = jjtreeFile + DEFAULT_SUFFIX; + } else { + String currentSuffix = jjtreeFile.substring(suffixPos); + + if (currentSuffix.equals(DEFAULT_SUFFIX)) { + optionalOutputFile = jjtreeFile + DEFAULT_SUFFIX; + } else { + optionalOutputFile = jjtreeFile.substring(0, suffixPos) + + DEFAULT_SUFFIX; + } + } + } + + if ((outputDir == null) || outputDir.equals("")) { + outputDir = getDefaultOutputDirectory(); + } + + return (outputDir + "/" + optionalOutputFile).replace('\\', '/'); + } + + /** + * When running JJTree from an Ant taskdesk the -OUTPUT_DIRECTORY must + * always be set. But when -OUTPUT_DIRECTORY is set, -OUTPUT_FILE is + * handled as if relative of this -OUTPUT_DIRECTORY. Thus when the + * -OUTPUT_FILE is absolute or contains a drive letter we have a problem. + * + * @param destFile + * @param outputDir + * @return validation file, relative if possible; <tt>null</tt> if not set + * @throws BuildException + */ + private String validateOutputFile(String destFile, + String outputDir) + throws BuildException { + if (destFile == null) { + return null; + } + + if ((outputDir == null) + && (destFile.startsWith("/") || destFile.startsWith("\\"))) { + String relativeOutputFile = makeOutputFileRelative(destFile); + setOutputfile(relativeOutputFile); + + return relativeOutputFile; + } + + String root = getRoot(new File(destFile)).getAbsolutePath(); + + if ((root.length() > 1) + && destFile.startsWith(root.substring(0, root.length() - 1))) { + throw new BuildException("Drive letter in 'outputfile' not " + + "supported: " + destFile); + } + + return destFile; + } + + private String makeOutputFileRelative(String destFile) { + StringBuffer relativePath = new StringBuffer(); + String defaultOutputDirectory = getDefaultOutputDirectory(); + int nextPos = defaultOutputDirectory.indexOf('/'); + int startPos = nextPos + 1; + + while (startPos > -1 && startPos < defaultOutputDirectory.length()) { + relativePath.append("/.."); + nextPos = defaultOutputDirectory.indexOf('/', startPos); + + if (nextPos == -1) { + startPos = nextPos; + } else { + startPos = nextPos + 1; + } + } + + relativePath.append(destFile); + + return relativePath.toString(); + } + + private String getDefaultOutputDirectory() { + return getProject().getBaseDir().getAbsolutePath().replace('\\', '/'); + } + + /** + * Determine root directory for a given file. + * + * @param file + * @return file's root directory + */ + private File getRoot(File file) { + File root = file.getAbsoluteFile(); + + while (root.getParent() != null) { + root = root.getParentFile(); + } + + return root; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JavaCC.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JavaCC.java new file mode 100644 index 00000000..219cc9c8 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JavaCC.java @@ -0,0 +1,579 @@ +/* + * 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.tools.ant.taskdefs.optional.javacc; + +import java.io.File; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.Hashtable; + +import org.apache.tools.ant.AntClassLoader; +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.types.Commandline; +import org.apache.tools.ant.types.CommandlineJava; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.JavaEnvUtils; + +/** + * JavaCC compiler compiler task. + * + */ +public class JavaCC extends Task { + + // keys to optional attributes + private static final String LOOKAHEAD = "LOOKAHEAD"; + private static final String CHOICE_AMBIGUITY_CHECK = "CHOICE_AMBIGUITY_CHECK"; + private static final String OTHER_AMBIGUITY_CHECK = "OTHER_AMBIGUITY_CHECK"; + + private static final String STATIC = "STATIC"; + private static final String DEBUG_PARSER = "DEBUG_PARSER"; + private static final String DEBUG_LOOKAHEAD = "DEBUG_LOOKAHEAD"; + private static final String DEBUG_TOKEN_MANAGER = "DEBUG_TOKEN_MANAGER"; + private static final String OPTIMIZE_TOKEN_MANAGER = "OPTIMIZE_TOKEN_MANAGER"; + private static final String ERROR_REPORTING = "ERROR_REPORTING"; + private static final String JAVA_UNICODE_ESCAPE = "JAVA_UNICODE_ESCAPE"; + private static final String UNICODE_INPUT = "UNICODE_INPUT"; + private static final String IGNORE_CASE = "IGNORE_CASE"; + private static final String COMMON_TOKEN_ACTION = "COMMON_TOKEN_ACTION"; + private static final String USER_TOKEN_MANAGER = "USER_TOKEN_MANAGER"; + private static final String USER_CHAR_STREAM = "USER_CHAR_STREAM"; + private static final String BUILD_PARSER = "BUILD_PARSER"; + private static final String BUILD_TOKEN_MANAGER = "BUILD_TOKEN_MANAGER"; + private static final String SANITY_CHECK = "SANITY_CHECK"; + private static final String FORCE_LA_CHECK = "FORCE_LA_CHECK"; + private static final String CACHE_TOKENS = "CACHE_TOKENS"; + private static final String KEEP_LINE_COLUMN = "KEEP_LINE_COLUMN"; + private static final String JDK_VERSION = "JDK_VERSION"; + + private final Hashtable optionalAttrs = new Hashtable(); + + // required attributes + private File outputDirectory = null; + private File targetFile = null; + private File javaccHome = null; + + private CommandlineJava cmdl = new CommandlineJava(); + + protected static final int TASKDEF_TYPE_JAVACC = 1; + protected static final int TASKDEF_TYPE_JJTREE = 2; + protected static final int TASKDEF_TYPE_JJDOC = 3; + + protected static final String[] ARCHIVE_LOCATIONS = + new String[] { + "JavaCC.zip", + "bin/lib/JavaCC.zip", + "bin/lib/javacc.jar", + "javacc.jar", // used by jpackage for JavaCC 3.x + }; + + protected static final int[] ARCHIVE_LOCATIONS_VS_MAJOR_VERSION = + new int[] { + 1, + 2, + 3, + 3, + }; + + protected static final String COM_PACKAGE = "COM.sun.labs."; + protected static final String COM_JAVACC_CLASS = "javacc.Main"; + protected static final String COM_JJTREE_CLASS = "jjtree.Main"; + protected static final String COM_JJDOC_CLASS = "jjdoc.JJDocMain"; + + protected static final String ORG_PACKAGE_3_0 = "org.netbeans.javacc."; + protected static final String ORG_PACKAGE_3_1 = "org.javacc."; + protected static final String ORG_JAVACC_CLASS = "parser.Main"; + protected static final String ORG_JJTREE_CLASS = COM_JJTREE_CLASS; + protected static final String ORG_JJDOC_CLASS = COM_JJDOC_CLASS; + + private String maxMemory = null; + + /** + * Sets the LOOKAHEAD grammar option. + * @param lookahead an <code>int</code> value. + */ + public void setLookahead(int lookahead) { + optionalAttrs.put(LOOKAHEAD, new Integer(lookahead)); + } + + /** + * Sets the CHOICE_AMBIGUITY_CHECK grammar option. + * @param choiceAmbiguityCheck an <code>int</code> value. + */ + public void setChoiceambiguitycheck(int choiceAmbiguityCheck) { + optionalAttrs.put(CHOICE_AMBIGUITY_CHECK, new Integer(choiceAmbiguityCheck)); + } + + /** + * Sets the OTHER_AMBIGUITY_CHECK grammar option. + * @param otherAmbiguityCheck an <code>int</code> value. + */ + public void setOtherambiguityCheck(int otherAmbiguityCheck) { + optionalAttrs.put(OTHER_AMBIGUITY_CHECK, new Integer(otherAmbiguityCheck)); + } + + /** + * Sets the STATIC grammar option. + * @param staticParser a <code>boolean</code> value. + */ + public void setStatic(boolean staticParser) { + optionalAttrs.put(STATIC, staticParser ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the DEBUG_PARSER grammar option. + * @param debugParser a <code>boolean</code> value. + */ + public void setDebugparser(boolean debugParser) { + optionalAttrs.put(DEBUG_PARSER, debugParser ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the DEBUG_LOOKAHEAD grammar option. + * @param debugLookahead a <code>boolean</code> value. + */ + public void setDebuglookahead(boolean debugLookahead) { + optionalAttrs.put(DEBUG_LOOKAHEAD, debugLookahead ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the DEBUG_TOKEN_MANAGER grammar option. + * @param debugTokenManager a <code>boolean</code> value. + */ + public void setDebugtokenmanager(boolean debugTokenManager) { + optionalAttrs.put(DEBUG_TOKEN_MANAGER, debugTokenManager ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the OPTIMIZE_TOKEN_MANAGER grammar option. + * @param optimizeTokenManager a <code>boolean</code> value. + */ + public void setOptimizetokenmanager(boolean optimizeTokenManager) { + optionalAttrs.put(OPTIMIZE_TOKEN_MANAGER, + optimizeTokenManager ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the ERROR_REPORTING grammar option. + * @param errorReporting a <code>boolean</code> value. + */ + public void setErrorreporting(boolean errorReporting) { + optionalAttrs.put(ERROR_REPORTING, errorReporting ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the JAVA_UNICODE_ESCAPE grammar option. + * @param javaUnicodeEscape a <code>boolean</code> value. + */ + public void setJavaunicodeescape(boolean javaUnicodeEscape) { + optionalAttrs.put(JAVA_UNICODE_ESCAPE, javaUnicodeEscape ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the UNICODE_INPUT grammar option. + * @param unicodeInput a <code>boolean</code> value. + */ + public void setUnicodeinput(boolean unicodeInput) { + optionalAttrs.put(UNICODE_INPUT, unicodeInput ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the IGNORE_CASE grammar option. + * @param ignoreCase a <code>boolean</code> value. + */ + public void setIgnorecase(boolean ignoreCase) { + optionalAttrs.put(IGNORE_CASE, ignoreCase ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the COMMON_TOKEN_ACTION grammar option. + * @param commonTokenAction a <code>boolean</code> value. + */ + public void setCommontokenaction(boolean commonTokenAction) { + optionalAttrs.put(COMMON_TOKEN_ACTION, commonTokenAction ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the USER_TOKEN_MANAGER grammar option. + * @param userTokenManager a <code>boolean</code> value. + */ + public void setUsertokenmanager(boolean userTokenManager) { + optionalAttrs.put(USER_TOKEN_MANAGER, userTokenManager ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the USER_CHAR_STREAM grammar option. + * @param userCharStream a <code>boolean</code> value. + */ + public void setUsercharstream(boolean userCharStream) { + optionalAttrs.put(USER_CHAR_STREAM, userCharStream ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the BUILD_PARSER grammar option. + * @param buildParser a <code>boolean</code> value. + */ + public void setBuildparser(boolean buildParser) { + optionalAttrs.put(BUILD_PARSER, buildParser ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the BUILD_TOKEN_MANAGER grammar option. + * @param buildTokenManager a <code>boolean</code> value. + */ + public void setBuildtokenmanager(boolean buildTokenManager) { + optionalAttrs.put(BUILD_TOKEN_MANAGER, buildTokenManager ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the SANITY_CHECK grammar option. + * @param sanityCheck a <code>boolean</code> value. + */ + public void setSanitycheck(boolean sanityCheck) { + optionalAttrs.put(SANITY_CHECK, sanityCheck ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the FORCE_LA_CHECK grammar option. + * @param forceLACheck a <code>boolean</code> value. + */ + public void setForcelacheck(boolean forceLACheck) { + optionalAttrs.put(FORCE_LA_CHECK, forceLACheck ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the CACHE_TOKENS grammar option. + * @param cacheTokens a <code>boolean</code> value. + */ + public void setCachetokens(boolean cacheTokens) { + optionalAttrs.put(CACHE_TOKENS, cacheTokens ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the KEEP_LINE_COLUMN grammar option. + * @param keepLineColumn a <code>boolean</code> value. + */ + public void setKeeplinecolumn(boolean keepLineColumn) { + optionalAttrs.put(KEEP_LINE_COLUMN, keepLineColumn ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets the JDK_VERSION option. + * @param jdkVersion the version to use. + * @since Ant1.7 + */ + public void setJDKversion(String jdkVersion) { + optionalAttrs.put(JDK_VERSION, jdkVersion); + } + + /** + * The directory to write the generated files to. + * If not set, the files are written to the directory + * containing the grammar file. + * @param outputDirectory the output directory. + */ + public void setOutputdirectory(File outputDirectory) { + this.outputDirectory = outputDirectory; + } + + /** + * The grammar file to process. + * @param targetFile the grammar file. + */ + public void setTarget(File targetFile) { + this.targetFile = targetFile; + } + + /** + * The directory containing the JavaCC distribution. + * @param javaccHome the directory. + */ + public void setJavacchome(File javaccHome) { + this.javaccHome = javaccHome; + } + + /** + * Corresponds -Xmx. + * + * @param max max memory parameter. + * @since Ant 1.8.3 + */ + public void setMaxmemory(String max) { + maxMemory = max; + } + + /** + * Constructor + */ + public JavaCC() { + cmdl.setVm(JavaEnvUtils.getJreExecutable("java")); + } + + /** + * Run the task. + * @throws BuildException on error. + */ + public void execute() throws BuildException { + + // load command line with optional attributes + Enumeration iter = optionalAttrs.keys(); + while (iter.hasMoreElements()) { + String name = (String) iter.nextElement(); + Object value = optionalAttrs.get(name); + cmdl.createArgument().setValue("-" + name + ":" + value.toString()); + } + + // check the target is a file + if (targetFile == null || !targetFile.isFile()) { + throw new BuildException("Invalid target: " + targetFile); + } + + // use the directory containing the target as the output directory + if (outputDirectory == null) { + outputDirectory = new File(targetFile.getParent()); + } else if (!outputDirectory.isDirectory()) { + throw new BuildException("Outputdir not a directory."); + } + cmdl.createArgument().setValue("-OUTPUT_DIRECTORY:" + + outputDirectory.getAbsolutePath()); + + // determine if the generated java file is up-to-date + final File javaFile = getOutputJavaFile(outputDirectory, targetFile); + if (javaFile.exists() + && targetFile.lastModified() < javaFile.lastModified()) { + log("Target is already built - skipping (" + targetFile + ")", + Project.MSG_VERBOSE); + return; + } + cmdl.createArgument().setValue(targetFile.getAbsolutePath()); + + final Path classpath = cmdl.createClasspath(getProject()); + final File javaccJar = JavaCC.getArchiveFile(javaccHome); + classpath.createPathElement().setPath(javaccJar.getAbsolutePath()); + classpath.addJavaRuntime(); + + cmdl.setClassname(JavaCC.getMainClass(classpath, + JavaCC.TASKDEF_TYPE_JAVACC)); + + cmdl.setMaxmemory(maxMemory); + final Commandline.Argument arg = cmdl.createVmArgument(); + arg.setValue("-Dinstall.root=" + javaccHome.getAbsolutePath()); + + Execute.runCommand(this, cmdl.getCommandline()); + } + + /** + * Helper method to retrieve the path used to store the JavaCC.zip + * or javacc.jar which is different from versions. + * + * @param home the javacc home path directory. + * @throws BuildException thrown if the home directory is invalid + * or if the archive could not be found despite attempts to do so. + * @return the file object pointing to the JavaCC archive. + */ + protected static File getArchiveFile(File home) throws BuildException { + return new File(home, + ARCHIVE_LOCATIONS[getArchiveLocationIndex(home)]); + } + + /** + * Helper method to retrieve main class which is different from versions. + * @param home the javacc home path directory. + * @param type the taskdef. + * @throws BuildException thrown if the home directory is invalid + * or if the archive could not be found despite attempts to do so. + * @return the main class for the taskdef. + */ + protected static String getMainClass(File home, int type) + throws BuildException { + + Path p = new Path(null); + p.createPathElement().setLocation(getArchiveFile(home)); + p.addJavaRuntime(); + return getMainClass(p, type); + } + + /** + * Helper method to retrieve main class which is different from versions. + * @param path classpath to search in. + * @param type the taskdef. + * @throws BuildException thrown if the home directory is invalid + * or if the archive could not be found despite attempts to do so. + * @return the main class for the taskdef. + * @since Ant 1.7 + */ + protected static String getMainClass(Path path, int type) + throws BuildException { + String packagePrefix = null; + String mainClass = null; + + AntClassLoader l = null; + try { + l = AntClassLoader.newAntClassLoader(null, null, + path + .concatSystemClasspath("ignore"), + true); + String javaccClass = COM_PACKAGE + COM_JAVACC_CLASS; + InputStream is = l.getResourceAsStream(javaccClass.replace('.', '/') + + ".class"); + if (is != null) { + packagePrefix = COM_PACKAGE; + switch (type) { + case TASKDEF_TYPE_JAVACC: + mainClass = COM_JAVACC_CLASS; + + break; + + case TASKDEF_TYPE_JJTREE: + mainClass = COM_JJTREE_CLASS; + + break; + + case TASKDEF_TYPE_JJDOC: + mainClass = COM_JJDOC_CLASS; + + break; + default: + // Fall Through + } + } else { + javaccClass = ORG_PACKAGE_3_1 + ORG_JAVACC_CLASS; + is = l.getResourceAsStream(javaccClass.replace('.', '/') + + ".class"); + if (is != null) { + packagePrefix = ORG_PACKAGE_3_1; + } else { + javaccClass = ORG_PACKAGE_3_0 + ORG_JAVACC_CLASS; + is = l.getResourceAsStream(javaccClass.replace('.', '/') + + ".class"); + if (is != null) { + packagePrefix = ORG_PACKAGE_3_0; + } + } + + if (is != null) { + switch (type) { + case TASKDEF_TYPE_JAVACC: + mainClass = ORG_JAVACC_CLASS; + + break; + + case TASKDEF_TYPE_JJTREE: + mainClass = ORG_JJTREE_CLASS; + + break; + + case TASKDEF_TYPE_JJDOC: + mainClass = ORG_JJDOC_CLASS; + + break; + default: + // Fall Through + } + } + } + + if (packagePrefix == null) { + throw new BuildException("failed to load JavaCC"); + } + if (mainClass == null) { + throw new BuildException("unknown task type " + type); + } + return packagePrefix + mainClass; + } finally { + if (l != null) { + l.cleanup(); + } + } + } + + /** + * Helper method to determine the archive location index. + * + * @param home the javacc home path directory. + * @throws BuildException thrown if the home directory is invalid + * or if the archive could not be found despite attempts to do so. + * @return the archive location index + */ + private static int getArchiveLocationIndex(File home) + throws BuildException { + + if (home == null || !home.isDirectory()) { + throw new BuildException("JavaCC home must be a valid directory."); + } + + for (int i = 0; i < ARCHIVE_LOCATIONS.length; i++) { + File f = new File(home, ARCHIVE_LOCATIONS[i]); + + if (f.exists()) { + return i; + } + } + + throw new BuildException("Could not find a path to JavaCC.zip " + + "or javacc.jar from '" + home + "'."); + } + + /** + * Helper method to determine the major version number of JavaCC. + * + * @param home the javacc home path directory. + * @throws BuildException thrown if the home directory is invalid + * or if the archive could not be found despite attempts to do so. + * @return a the major version number + */ + protected static int getMajorVersionNumber(File home) + throws BuildException { + + return + ARCHIVE_LOCATIONS_VS_MAJOR_VERSION[getArchiveLocationIndex(home)]; + } + + /** + * Determines the output Java file to be generated by the given grammar + * file. + * + */ + private File getOutputJavaFile(File outputdir, File srcfile) { + String path = srcfile.getPath(); + + // Extract file's base-name + int startBasename = path.lastIndexOf(File.separator); + if (startBasename != -1) { + path = path.substring(startBasename + 1); + } + + // Replace the file's extension with '.java' + int startExtn = path.lastIndexOf('.'); + if (startExtn != -1) { + path = path.substring(0, startExtn) + ".java"; + } else { + path += ".java"; + } + + // Change the directory + if (outputdir != null) { + path = outputdir + File.separator + path; + } + + return new File(path); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/Gcjh.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/Gcjh.java new file mode 100644 index 00000000..712bb76a --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/Gcjh.java @@ -0,0 +1,89 @@ +/* + * 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.tools.ant.taskdefs.optional.javah; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.taskdefs.optional.Javah; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.JavaEnvUtils; + +/** + * Adapter to the native gcjh compiler. + * + * @since Ant 1.8.2 + */ +public class Gcjh implements JavahAdapter { + + public static final String IMPLEMENTATION_NAME = "gcjh"; + + /** + * Performs the actual compilation. + */ + public boolean compile(Javah javah) throws BuildException { + Commandline cmd = setupGcjhCommand(javah); + try { + Execute.runCommand(javah, cmd.getCommandline()); + return true; + } catch (BuildException e) { + if (e.getMessage().indexOf("failed with return code") == -1) { + throw e; + } + } + return false; + } + + private Commandline setupGcjhCommand(Javah javah) { + Commandline cmd = new Commandline(); + cmd.setExecutable(JavaEnvUtils.getJdkExecutable("gcjh")); + + if (javah.getDestdir() != null) { + cmd.createArgument().setValue("-d"); + cmd.createArgument().setFile(javah.getDestdir()); + } + + if (javah.getOutputfile() != null) { + cmd.createArgument().setValue("-o"); + cmd.createArgument().setFile(javah.getOutputfile()); + } + + Path cp = new Path(javah.getProject()); + if (javah.getBootclasspath() != null) { + cp.append(javah.getBootclasspath()); + } + cp = cp.concatSystemBootClasspath("ignore"); + if (javah.getClasspath() != null) { + cp.append(javah.getClasspath()); + } + if (cp.size() > 0) { + cmd.createArgument().setValue("--classpath"); + cmd.createArgument().setPath(cp); + } + + if (!javah.getOld()) { + cmd.createArgument().setValue("-jni"); + } + + cmd.addArguments(javah.getCurrentArgs()); + + javah.logAndAddFiles(cmd); + return cmd; + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapter.java new file mode 100644 index 00000000..121fa429 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapter.java @@ -0,0 +1,38 @@ +/* + * 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.tools.ant.taskdefs.optional.javah; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.optional.Javah; + +/** + * Interface for different backend implementations of the Javah task. + * + * @since Ant 1.6.3 + */ +public interface JavahAdapter { + /** + * Performs the actual compilation. + * @param javah the calling javah task. + * @return true if the compilation was successful. + * @throws BuildException if there is an error. + * @since Ant 1.6.3 + */ + boolean compile(Javah javah) throws BuildException; +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapterFactory.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapterFactory.java new file mode 100644 index 00000000..d98b4276 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapterFactory.java @@ -0,0 +1,120 @@ +/* + * 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.tools.ant.taskdefs.optional.javah; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.ProjectComponent; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.ClasspathUtils; +import org.apache.tools.ant.util.JavaEnvUtils; + +/** + * Creates the JavahAdapter based on the user choice and + * potentially the VM vendor. + * + * @since Ant 1.6.3 + */ +// CheckStyle:HideUtilityClassConstructorCheck OFF (bc) +public class JavahAdapterFactory { + + /** + * Determines the default choice of adapter based on the VM + * vendor. + * + * @return the default choice of adapter based on the VM + * vendor + */ + public static String getDefault() { + if (JavaEnvUtils.isKaffe()) { + return Kaffeh.IMPLEMENTATION_NAME; + } else if (JavaEnvUtils.isGij()) { + return Gcjh.IMPLEMENTATION_NAME; + } + return SunJavah.IMPLEMENTATION_NAME; + } + + /** + * Creates the JavahAdapter based on the user choice and + * potentially the VM vendor. + * + * @param choice the user choice (if any). + * @param log a ProjectComponent instance used to access Ant's + * logging system. + * @return The adapter to use. + * @throws BuildException if there is an error. + */ + public static JavahAdapter getAdapter(String choice, + ProjectComponent log) + throws BuildException { + return getAdapter(choice, log, null); + } + + /** + * Creates the JavahAdapter based on the user choice and + * potentially the VM vendor. + * + * @param choice the user choice (if any). + * @param log a ProjectComponent instance used to access Ant's + * logging system. + * @param classpath the classpath to use when looking up an + * adapter class + * @return The adapter to use. + * @throws BuildException if there is an error. + * @since Ant 1.8.0 + */ + public static JavahAdapter getAdapter(String choice, + ProjectComponent log, + Path classpath) + throws BuildException { + if ((JavaEnvUtils.isKaffe() && choice == null) + || Kaffeh.IMPLEMENTATION_NAME.equals(choice)) { + return new Kaffeh(); + } else if ((JavaEnvUtils.isGij() && choice == null) + || Gcjh.IMPLEMENTATION_NAME.equals(choice)) { + return new Gcjh(); + } else if (SunJavah.IMPLEMENTATION_NAME.equals(choice)) { + return new SunJavah(); + } else if (choice != null) { + return resolveClassName(choice, + // Memory leak in line below + log.getProject() + .createClassLoader(classpath)); + } + + // This default has been good enough until Ant 1.6.3, so stick + // with it + return new SunJavah(); + } + + /** + * Tries to resolve the given classname into a javah adapter. + * Throws a fit if it can't. + * + * @param className The fully qualified classname to be created. + * @param loader the classloader to use + * @throws BuildException This is the fit that is thrown if className + * isn't an instance of JavahAdapter. + */ + private static JavahAdapter resolveClassName(String className, + ClassLoader loader) + throws BuildException { + return (JavahAdapter) ClasspathUtils.newInstance(className, + loader != null ? loader : + JavahAdapterFactory.class.getClassLoader(), JavahAdapter.class); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/Kaffeh.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/Kaffeh.java new file mode 100644 index 00000000..d37f7717 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/Kaffeh.java @@ -0,0 +1,94 @@ +/* + * 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.tools.ant.taskdefs.optional.javah; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.taskdefs.optional.Javah; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.JavaEnvUtils; + +/** + * Adapter to the native kaffeh compiler. + * + * @since Ant 1.6.3 + */ +public class Kaffeh implements JavahAdapter { + + /** the name of the javah adapter - kaffeh */ + public static final String IMPLEMENTATION_NAME = "kaffeh"; + + /** + * Performs the actual compilation. + * @param javah the calling javah task. + * @return true if the compilation was successful. + * @throws BuildException if there is an error. + * @since Ant 1.6.3 + */ + public boolean compile(Javah javah) throws BuildException { + Commandline cmd = setupKaffehCommand(javah); + try { + Execute.runCommand(javah, cmd.getCommandline()); + return true; + } catch (BuildException e) { + if (e.getMessage().indexOf("failed with return code") == -1) { + throw e; + } + } + return false; + } + + private Commandline setupKaffehCommand(Javah javah) { + Commandline cmd = new Commandline(); + cmd.setExecutable(JavaEnvUtils.getJdkExecutable("kaffeh")); + + if (javah.getDestdir() != null) { + cmd.createArgument().setValue("-d"); + cmd.createArgument().setFile(javah.getDestdir()); + } + + if (javah.getOutputfile() != null) { + cmd.createArgument().setValue("-o"); + cmd.createArgument().setFile(javah.getOutputfile()); + } + + Path cp = new Path(javah.getProject()); + if (javah.getBootclasspath() != null) { + cp.append(javah.getBootclasspath()); + } + cp = cp.concatSystemBootClasspath("ignore"); + if (javah.getClasspath() != null) { + cp.append(javah.getClasspath()); + } + if (cp.size() > 0) { + cmd.createArgument().setValue("-classpath"); + cmd.createArgument().setPath(cp); + } + + if (!javah.getOld()) { + cmd.createArgument().setValue("-jni"); + } + + cmd.addArguments(javah.getCurrentArgs()); + + javah.logAndAddFiles(cmd); + return cmd; + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/SunJavah.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/SunJavah.java new file mode 100644 index 00000000..0b1655ad --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/SunJavah.java @@ -0,0 +1,123 @@ +/* + * 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.tools.ant.taskdefs.optional.javah; + +import java.io.File; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.launch.Locator; +import org.apache.tools.ant.taskdefs.ExecuteJava; +import org.apache.tools.ant.taskdefs.optional.Javah; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.Path; + + +/** + * Adapter to com.sun.tools.javah.oldjavah.Main or com.sun.tools.javah.Main. + * + * @since Ant 1.6.3 + */ +public class SunJavah implements JavahAdapter { + + /** the name of the javah adapter - sun */ + public static final String IMPLEMENTATION_NAME = "sun"; + + /** + * Performs the actual compilation. + * @param javah the calling javah task. + * @return true if the compilation was successful. + * @throws BuildException if there is an error. + * @since Ant 1.6.3 + */ + public boolean compile(Javah javah) throws BuildException { + Commandline cmd = setupJavahCommand(javah); + ExecuteJava ej = new ExecuteJava(); + Class c = null; + try { + try { + // first search for the "old" javah class in 1.4.2 tools.jar + c = Class.forName("com.sun.tools.javah.oldjavah.Main"); + } catch (ClassNotFoundException cnfe) { + // assume older than 1.4.2 tools.jar + c = Class.forName("com.sun.tools.javah.Main"); + } + } catch (ClassNotFoundException ex) { + throw new BuildException( + "Can't load javah", ex, javah.getLocation()); + } + cmd.setExecutable(c.getName()); + ej.setJavaCommand(cmd); + File f = Locator.getClassSource(c); + if (f != null) { + ej.setClasspath(new Path(javah.getProject(), f.getPath())); + } + return ej.fork(javah) == 0; + } + + private Commandline setupJavahCommand(Javah javah) { + Commandline cmd = new Commandline(); + + if (javah.getDestdir() != null) { + cmd.createArgument().setValue("-d"); + cmd.createArgument().setFile(javah.getDestdir()); + } + + if (javah.getOutputfile() != null) { + cmd.createArgument().setValue("-o"); + cmd.createArgument().setFile(javah.getOutputfile()); + } + + if (javah.getClasspath() != null) { + cmd.createArgument().setValue("-classpath"); + cmd.createArgument().setPath(javah.getClasspath()); + } + + if (javah.getVerbose()) { + cmd.createArgument().setValue("-verbose"); + } + if (javah.getOld()) { + cmd.createArgument().setValue("-old"); + } + if (javah.getForce()) { + cmd.createArgument().setValue("-force"); + } + if (javah.getStubs() && !javah.getOld()) { + throw new BuildException( + "stubs only available in old mode.", javah.getLocation()); + } + + if (javah.getStubs()) { + cmd.createArgument().setValue("-stubs"); + } + Path bcp = new Path(javah.getProject()); + if (javah.getBootclasspath() != null) { + bcp.append(javah.getBootclasspath()); + } + bcp = bcp.concatSystemBootClasspath("ignore"); + if (bcp.size() > 0) { + cmd.createArgument().setValue("-bootclasspath"); + cmd.createArgument().setPath(bcp); + } + + cmd.addArguments(javah.getCurrentArgs()); + + javah.logAndAddFiles(cmd); + return cmd; + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jdepend/JDependTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jdepend/JDependTask.java new file mode 100644 index 00000000..21f03fe7 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jdepend/JDependTask.java @@ -0,0 +1,684 @@ +/* + * 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.tools.ant.taskdefs.optional.jdepend; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Map; +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.ExecuteWatchdog; +import org.apache.tools.ant.taskdefs.LogStreamHandler; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.CommandlineJava; +import org.apache.tools.ant.types.EnumeratedAttribute; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.PatternSet; +import org.apache.tools.ant.types.Reference; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.LoaderUtils; + +/** + * Runs JDepend tests. + * + * <p>JDepend is a tool to generate design quality metrics for each Java package. + * It has been initially created by Mike Clark. JDepend can be found at <a + * href="http://www.clarkware.com/software/JDepend.html">http://www.clarkware.com/software/JDepend.html</a>. + * + * The current implementation spawn a new Java VM. + * + */ +public class JDependTask extends Task { + //private CommandlineJava commandline = new CommandlineJava(); + + // required attributes + private Path sourcesPath; // Deprecated! + private Path classesPath; // Use this going forward + + // optional attributes + private File outputFile; + private File dir; + private Path compileClasspath; + private boolean haltonerror = false; + private boolean fork = false; + private Long timeout = null; + + private String jvm = null; + private String format = "text"; + private PatternSet defaultPatterns = new PatternSet(); + + private static Constructor packageFilterC; + private static Method setFilter; + + private boolean includeRuntime = false; + private Path runtimeClasses = null; + + static { + try { + Class packageFilter = + Class.forName("jdepend.framework.PackageFilter"); + packageFilterC = + packageFilter.getConstructor(new Class[] {java.util.Collection.class}); + setFilter = + jdepend.textui.JDepend.class.getDeclaredMethod("setFilter", + new Class[] {packageFilter}); + } catch (Throwable t) { + if (setFilter == null) { + packageFilterC = null; + } + } + } + + /** + * If true, + * include jdepend.jar in the forked VM. + * + * @param b include ant run time yes or no + * @since Ant 1.6 + */ + public void setIncluderuntime(boolean b) { + includeRuntime = b; + } + + /** + * Set the timeout value (in milliseconds). + * + * <p>If the operation is running for more than this value, the jdepend + * will be canceled. (works only when in 'fork' mode).</p> + * @param value the maximum time (in milliseconds) allowed before + * declaring the test as 'timed-out' + * @see #setFork(boolean) + */ + public void setTimeout(Long value) { + timeout = value; + } + + /** + * @return the timeout value + */ + public Long getTimeout() { + return timeout; + } + + /** + * The output file name. + * + * @param outputFile the output file name + */ + public void setOutputFile(File outputFile) { + this.outputFile = outputFile; + } + + /** + * @return the output file name + */ + public File getOutputFile() { + return outputFile; + } + + /** + * Whether or not to halt on failure. Default: false. + * @param haltonerror the value to set + */ + public void setHaltonerror(boolean haltonerror) { + this.haltonerror = haltonerror; + } + + /** + * @return the value of the haltonerror attribute + */ + public boolean getHaltonerror() { + return haltonerror; + } + + /** + * If true, forks into a new JVM. Default: false. + * + * @param value <tt>true</tt> if a JVM should be forked, + * otherwise <tt>false<tt> + */ + public void setFork(boolean value) { + fork = value; + } + + /** + * @return the value of the fork attribute + */ + public boolean getFork() { + return fork; + } + + /** + * The command used to invoke a forked Java Virtual Machine. + * + * Default is <tt>java</tt>. Ignored if no JVM is forked. + * @param value the new VM to use instead of <tt>java</tt> + * @see #setFork(boolean) + */ + public void setJvm(String value) { + jvm = value; + + } + + /** + * Adds a path to source code to analyze. + * @return a source path + * @deprecated since 1.6.x. + */ + public Path createSourcespath() { + if (sourcesPath == null) { + sourcesPath = new Path(getProject()); + } + return sourcesPath.createPath(); + } + + /** + * Gets the sourcepath. + * @return the sources path + * @deprecated since 1.6.x. + */ + public Path getSourcespath() { + return sourcesPath; + } + + /** + * Adds a path to class code to analyze. + * @return a classes path + */ + public Path createClassespath() { + if (classesPath == null) { + classesPath = new Path(getProject()); + } + return classesPath.createPath(); + } + + /** + * Gets the classespath. + * @return the classes path + */ + public Path getClassespath() { + return classesPath; + } + + /** + * The directory to invoke the VM in. Ignored if no JVM is forked. + * @param dir the directory to invoke the JVM from. + * @see #setFork(boolean) + */ + public void setDir(File dir) { + this.dir = dir; + } + + /** + * @return the dir attribute + */ + public File getDir() { + return dir; + } + + /** + * Set the classpath to be used for this compilation. + * @param classpath a class path to be used + */ + public void setClasspath(Path classpath) { + if (compileClasspath == null) { + compileClasspath = classpath; + } else { + compileClasspath.append(classpath); + } + } + + /** + * Gets the classpath to be used for this compilation. + * @return the class path used for compilation + */ + public Path getClasspath() { + return compileClasspath; + } + + /** + * Adds a path to the classpath. + * @return a classpath + */ + public Path createClasspath() { + if (compileClasspath == null) { + compileClasspath = new Path(getProject()); + } + return compileClasspath.createPath(); + } + + /** + * Create a new JVM argument. Ignored if no JVM is forked. + * @param commandline the commandline to create the argument on + * @return create a new JVM argument so that any argument can + * be passed to the JVM. + * @see #setFork(boolean) + */ + public Commandline.Argument createJvmarg(CommandlineJava commandline) { + return commandline.createVmArgument(); + } + + /** + * Adds a reference to a classpath defined elsewhere. + * @param r a classpath reference + */ + public void setClasspathRef(Reference r) { + createClasspath().setRefid(r); + } + + /** + * add a name entry on the exclude list + * @return a pattern for the excludes + */ + public PatternSet.NameEntry createExclude() { + return defaultPatterns.createExclude(); + } + + /** + * @return the excludes patterns + */ + public PatternSet getExcludes() { + return defaultPatterns; + } + + /** + * The format to write the output in, "xml" or "text". + * + * @param ea xml or text + */ + public void setFormat(FormatAttribute ea) { + format = ea.getValue(); + } + + /** + * A class for the enumerated attribute format, + * values are xml and text. + * @see EnumeratedAttribute + */ + public static class FormatAttribute extends EnumeratedAttribute { + private String [] formats = new String[]{"xml", "text"}; + + /** + * @return the enumerated values + */ + public String[] getValues() { + return formats; + } + } + + /** + * No problems with this test. + */ + private static final int SUCCESS = 0; + /** + * An error occurred. + */ + private static final int ERRORS = 1; + + /** + * Search for the given resource and add the directory or archive + * that contains it to the classpath. + * + * <p>Doesn't work for archives in JDK 1.1 as the URL returned by + * getResource doesn't contain the name of the archive.</p> + * + * @param resource resource that one wants to lookup + * @since Ant 1.6 + */ + private void addClasspathEntry(String resource) { + /* + * pre Ant 1.6 this method used to call getClass().getResource + * while Ant 1.6 will call ClassLoader.getResource(). + * + * The difference is that Class.getResource expects a leading + * slash for "absolute" resources and will strip it before + * delegating to ClassLoader.getResource - so we now have to + * emulate Class's behavior. + */ + if (resource.startsWith("/")) { + resource = resource.substring(1); + } else { + resource = "org/apache/tools/ant/taskdefs/optional/jdepend/" + + resource; + } + + File f = LoaderUtils.getResourceSource(getClass().getClassLoader(), + resource); + if (f != null) { + log("Found " + f.getAbsolutePath(), Project.MSG_DEBUG); + runtimeClasses.createPath().setLocation(f); + } else { + log("Couldn\'t find " + resource, Project.MSG_DEBUG); + } + } + + /** + * execute the task + * + * @exception BuildException if an error occurs + */ + public void execute() throws BuildException { + + CommandlineJava commandline = new CommandlineJava(); + + if ("text".equals(format)) { + commandline.setClassname("jdepend.textui.JDepend"); + } else + if ("xml".equals(format)) { + commandline.setClassname("jdepend.xmlui.JDepend"); + } + + if (jvm != null) { + commandline.setVm(jvm); + } + if (getSourcespath() == null && getClassespath() == null) { + throw new BuildException("Missing classespath required argument"); + } else if (getClassespath() == null) { + String msg = + "sourcespath is deprecated in JDepend >= 2.5 " + + "- please convert to classespath"; + log(msg); + } + + // execute the test and get the return code + int exitValue = JDependTask.ERRORS; + boolean wasKilled = false; + if (!getFork()) { + exitValue = executeInVM(commandline); + } else { + ExecuteWatchdog watchdog = createWatchdog(); + exitValue = executeAsForked(commandline, watchdog); + // null watchdog means no timeout, you'd better not check with null + if (watchdog != null) { + wasKilled = watchdog.killedProcess(); + } + } + + // if there is an error/failure and that it should halt, stop + // everything otherwise just log a statement + boolean errorOccurred = exitValue == JDependTask.ERRORS || wasKilled; + + if (errorOccurred) { + String errorMessage = "JDepend FAILED" + + (wasKilled ? " - Timed out" : ""); + + if (getHaltonerror()) { + throw new BuildException(errorMessage, getLocation()); + } else { + log(errorMessage, Project.MSG_ERR); + } + } + } + + // this comment extract from JUnit Task may also apply here + // "in VM is not very nice since it could probably hang the + // whole build. IMHO this method should be avoided and it would be best + // to remove it in future versions. TBD. (SBa)" + + /** + * Execute inside VM. + * + * @param commandline the command line + * @return the return value of the mvm + * @exception BuildException if an error occurs + */ + public int executeInVM(CommandlineJava commandline) throws BuildException { + jdepend.textui.JDepend jdepend; + + if ("xml".equals(format)) { + jdepend = new jdepend.xmlui.JDepend(); + } else { + jdepend = new jdepend.textui.JDepend(); + } + + FileWriter fw = null; + PrintWriter pw = null; + if (getOutputFile() != null) { + try { + fw = new FileWriter(getOutputFile().getPath()); + } catch (IOException e) { + String msg = "JDepend Failed when creating the output file: " + + e.getMessage(); + log(msg); + throw new BuildException(msg); + } + pw = new PrintWriter(fw); + jdepend.setWriter(pw); + log("Output to be stored in " + getOutputFile().getPath()); + } + + + try { + if (getClassespath() != null) { + // This is the new, better way - use classespath instead + // of sourcespath. The code is currently the same - you + // need class files in a directory to use this or jar files. + String[] cP = getClassespath().list(); + for (int i = 0; i < cP.length; i++) { + File f = new File(cP[i]); + // not necessary as JDepend would fail, but why loose + // some time? + if (!f.exists()) { + String msg = "\"" + + f.getPath() + + "\" does not represent a valid" + + " file or directory. JDepend would fail."; + log(msg); + throw new BuildException(msg); + } + try { + jdepend.addDirectory(f.getPath()); + } catch (IOException e) { + String msg = + "JDepend Failed when adding a class directory: " + + e.getMessage(); + log(msg); + throw new BuildException(msg); + } + } + + } else if (getSourcespath() != null) { + + // This is the old way and is deprecated - classespath is + // the right way to do this and is above + String[] sP = getSourcespath().list(); + for (int i = 0; i < sP.length; i++) { + File f = new File(sP[i]); + + // not necessary as JDepend would fail, but why loose + // some time? + if (!f.exists() || !f.isDirectory()) { + String msg = "\"" + + f.getPath() + + "\" does not represent a valid" + + " directory. JDepend would fail."; + log(msg); + throw new BuildException(msg); + } + try { + jdepend.addDirectory(f.getPath()); + } catch (IOException e) { + String msg = + "JDepend Failed when adding a source directory: " + + e.getMessage(); + log(msg); + throw new BuildException(msg); + } + } + } + + // This bit turns <exclude> child tags into patters to ignore + String[] patterns = defaultPatterns.getExcludePatterns(getProject()); + if (patterns != null && patterns.length > 0) { + if (setFilter != null) { + Vector v = new Vector(); + for (int i = 0; i < patterns.length; i++) { + v.addElement(patterns[i]); + } + try { + Object o = packageFilterC.newInstance(new Object[] {v}); + setFilter.invoke(jdepend, new Object[] {o}); + } catch (Throwable e) { + log("excludes will be ignored as JDepend doesn't like me: " + + e.getMessage(), Project.MSG_WARN); + } + } else { + log("Sorry, your version of JDepend doesn't support excludes", + Project.MSG_WARN); + } + } + + jdepend.analyze(); + if (pw.checkError()) { + throw new IOException("Encountered an error writing JDepend" + + " output"); + } + } catch (IOException ex) { + throw new BuildException(ex); + } finally { + FileUtils.close(pw); + FileUtils.close(fw); + } + return SUCCESS; + } + + + /** + * Execute the task by forking a new JVM. The command will block until + * it finishes. To know if the process was destroyed or not, use the + * <tt>killedProcess()</tt> method of the watchdog class. + * @param commandline the commandline for forked jvm + * @param watchdog the watchdog in charge of cancelling the test if it + * exceeds a certain amount of time. Can be <tt>null</tt>. + * @return the result of running the jdepend + * @throws BuildException in case of error + */ + // JL: comment extracted from JUnitTask (and slightly modified) + public int executeAsForked(CommandlineJava commandline, + ExecuteWatchdog watchdog) throws BuildException { + runtimeClasses = new Path(getProject()); + addClasspathEntry("/jdepend/textui/JDepend.class"); + + // if not set, auto-create the ClassPath from the project + createClasspath(); + + // not sure whether this test is needed but cost nothing to put. + // hope it will be reviewed by anybody competent + if (getClasspath().toString().length() > 0) { + createJvmarg(commandline).setValue("-classpath"); + createJvmarg(commandline).setValue(getClasspath().toString()); + } + + if (includeRuntime) { + Map/*<String, String>*/ env = Execute.getEnvironmentVariables(); + String cp = (String) env.get("CLASSPATH"); + if (cp != null) { + commandline.createClasspath(getProject()).createPath() + .append(new Path(getProject(), cp)); + } + log("Implicitly adding " + runtimeClasses + " to CLASSPATH", + Project.MSG_VERBOSE); + commandline.createClasspath(getProject()).createPath() + .append(runtimeClasses); + } + + if (getOutputFile() != null) { + // having a space between the file and its path causes commandline + // to add quotes around the argument thus making JDepend not taking + // it into account. Thus we split it in two + commandline.createArgument().setValue("-file"); + commandline.createArgument().setValue(outputFile.getPath()); + // we have to find a cleaner way to put this output + } + + if (getSourcespath() != null) { + // This is deprecated - use classespath in the future + String[] sP = getSourcespath().list(); + for (int i = 0; i < sP.length; i++) { + File f = new File(sP[i]); + + // not necessary as JDepend would fail, but why loose + // some time? + if (!f.exists() || !f.isDirectory()) { + throw new BuildException("\"" + f.getPath() + + "\" does not represent a valid" + + " directory. JDepend would" + + " fail."); + } + commandline.createArgument().setValue(f.getPath()); + } + } + + if (getClassespath() != null) { + // This is the new way - use classespath - code is the + // same for now + String[] cP = getClassespath().list(); + for (int i = 0; i < cP.length; i++) { + File f = new File(cP[i]); + // not necessary as JDepend would fail, but why loose + // some time? + if (!f.exists()) { + throw new BuildException("\"" + f.getPath() + + "\" does not represent a valid" + + " file or directory. JDepend would" + + " fail."); + } + commandline.createArgument().setValue(f.getPath()); + } + } + + Execute execute = new Execute(new LogStreamHandler(this, + Project.MSG_INFO, Project.MSG_WARN), watchdog); + execute.setCommandline(commandline.getCommandline()); + if (getDir() != null) { + execute.setWorkingDirectory(getDir()); + execute.setAntRun(getProject()); + } + + if (getOutputFile() != null) { + log("Output to be stored in " + getOutputFile().getPath()); + } + log(commandline.describeCommand(), Project.MSG_VERBOSE); + try { + return execute.execute(); + } catch (IOException e) { + throw new BuildException("Process fork failed.", e, getLocation()); + } + } + + /** + * @return <tt>null</tt> if there is a timeout value, otherwise the + * watchdog instance. + * @throws BuildException in case of error + */ + protected ExecuteWatchdog createWatchdog() throws BuildException { + if (getTimeout() == null) { + return null; + } + return new ExecuteWatchdog(getTimeout().longValue()); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/ClassNameReader.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/ClassNameReader.java new file mode 100644 index 00000000..20e9fc51 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/ClassNameReader.java @@ -0,0 +1,139 @@ +/* + * 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.tools.ant.taskdefs.optional.jlink; + +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Reads just enough of a class file to determine the class' full name. + * + * <p>Extremely minimal constant pool implementation, mainly to support extracting + * strings from a class file. + */ +class ConstantPool { + // CheckStyle:VisibilityModifier OFF - bc + static final + byte UTF8 = 1, UNUSED = 2, INTEGER = 3, FLOAT = 4, LONG = 5, DOUBLE = 6, + CLASS = 7, STRING = 8, FIELDREF = 9, METHODREF = 10, + INTERFACEMETHODREF = 11, NAMEANDTYPE = 12; + + byte[] types; + + Object[] values; + // CheckStyle:VisibilityModifier ON + + /** + * Create a constant pool. + * @param data the data input containing the class. + * @throws IOException if there is an error. + */ + ConstantPool(DataInput data) throws IOException { + super(); + + int count = data.readUnsignedShort(); + types = new byte [ count ]; + values = new Object [ count ]; + // read in all constant pool entries. + for (int i = 1; i < count; i++) { + byte type = data.readByte(); + types[i] = type; + switch (type) { + case UTF8 : + values[i] = data.readUTF(); + break; + + case UNUSED : + break; + + case INTEGER : + values[i] = new Integer(data.readInt()); + break; + + case FLOAT : + values[i] = new Float(data.readFloat()); + break; + + case LONG : + values[i] = new Long(data.readLong()); + ++i; + break; + + case DOUBLE : + values[i] = new Double(data.readDouble()); + ++i; + break; + + case CLASS : + case STRING : + values[i] = new Integer(data.readUnsignedShort()); + break; + + case FIELDREF : + case METHODREF : + case INTERFACEMETHODREF : + case NAMEANDTYPE : + values[i] = new Integer(data.readInt()); + break; + default: + // Do nothing + } + } + } +} + +/** + * Provides a quick and dirty way to determine the true name of a class + * given just an InputStream. Reads in just enough to perform this + * minimal task only. + */ +public class ClassNameReader extends Object { + private static final int CLASS_MAGIC_NUMBER = 0xCAFEBABE; + + /** + * Get the class name of a class in an input stream. + * + * @param input an <code>InputStream</code> value + * @return the name of the class + * @exception IOException if an error occurs + */ + public static String getClassName(InputStream input) throws IOException { + DataInputStream data = new DataInputStream(input); + // verify this is a valid class file. + int cookie = data.readInt(); + if (cookie != CLASS_MAGIC_NUMBER) { + return null; + } + /* int version = */ data.readInt(); + // read the constant pool. + ConstantPool constants = new ConstantPool(data); + Object[] values = constants.values; + // read access flags and class index. + /* int accessFlags = */ data.readUnsignedShort(); + int classIndex = data.readUnsignedShort(); + Integer stringIndex = (Integer) values[classIndex]; + String className = (String) values[stringIndex.intValue()]; + return className; + } + + +} + + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/JlinkTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/JlinkTask.java new file mode 100644 index 00000000..f5767e67 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/JlinkTask.java @@ -0,0 +1,183 @@ +/* + * 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.tools.ant.taskdefs.optional.jlink; + +import java.io.File; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.MatchingTask; +import org.apache.tools.ant.types.Path; + +/** + * This class defines objects that can link together various jar and + * zip files. + * + * <p>It is basically a wrapper for the jlink code written originally + * by <a href="mailto:beard@netscape.com">Patrick Beard</a>. The + * classes org.apache.tools.ant.taskdefs.optional.jlink.Jlink and + * org.apache.tools.ant.taskdefs.optional.jlink.ClassNameReader + * support this class.</p> + * + * <p>For example: + * <code> + * <pre> + * <jlink compress="false" outfile="out.jar"/> + * <mergefiles> + * <pathelement path="${build.dir}/mergefoo.jar"/> + * <pathelement path="${build.dir}/mergebar.jar"/> + * </mergefiles> + * <addfiles> + * <pathelement path="${build.dir}/mac.jar"/> + * <pathelement path="${build.dir}/pc.zip"/> + * </addfiles> + * </jlink> + * </pre> + * </code> + * + * @ant.task ignore="true" + */ +public class JlinkTask extends MatchingTask { + + /** + * The output file for this run of jlink. Usually a jar or zip file. + * @param outfile the output file + */ + public void setOutfile(File outfile) { + this.outfile = outfile; + } + + /** + * Establishes the object that contains the files to + * be merged into the output. + * @return a path to be configured + */ + public Path createMergefiles() { + if (this.mergefiles == null) { + this.mergefiles = new Path(getProject()); + } + return this.mergefiles.createPath(); + } + + /** + * Sets the files to be merged into the output. + * @param mergefiles a path + */ + public void setMergefiles(Path mergefiles) { + if (this.mergefiles == null) { + this.mergefiles = mergefiles; + } else { + this.mergefiles.append(mergefiles); + } + } + + /** + * Establishes the object that contains the files to + * be added to the output. + * @return a path to be configured + */ + public Path createAddfiles() { + if (this.addfiles == null) { + this.addfiles = new Path(getProject()); + } + return this.addfiles.createPath(); + } + + /** + * Sets the files to be added into the output. + * @param addfiles a path + */ + public void setAddfiles(Path addfiles) { + if (this.addfiles == null) { + this.addfiles = addfiles; + } else { + this.addfiles.append(addfiles); + } + } + + /** + * Defines whether or not the output should be compacted. + * @param compress a <code>boolean</code> value + */ + public void setCompress(boolean compress) { + this.compress = compress; + } + + /** + * Does the adding and merging. + * @throws BuildException on error + */ + public void execute() throws BuildException { + //Be sure everything has been set. + if (outfile == null) { + throw new BuildException("outfile attribute is required! " + + "Please set."); + } + if (!haveAddFiles() && !haveMergeFiles()) { + throw new BuildException("addfiles or mergefiles required! " + + "Please set."); + } + log("linking: " + outfile.getPath()); + log("compression: " + compress, Project.MSG_VERBOSE); + jlink linker = new jlink(); + linker.setOutfile(outfile.getPath()); + linker.setCompression(compress); + if (haveMergeFiles()) { + log("merge files: " + mergefiles.toString(), Project.MSG_VERBOSE); + linker.addMergeFiles(mergefiles.list()); + } + if (haveAddFiles()) { + log("add files: " + addfiles.toString(), Project.MSG_VERBOSE); + linker.addAddFiles(addfiles.list()); + } + try { + linker.link(); + } catch (Exception ex) { + throw new BuildException(ex, getLocation()); + } + } + + private boolean haveAddFiles() { + return haveEntries(addfiles); + } + + private boolean haveMergeFiles() { + return haveEntries(mergefiles); + } + + private boolean haveEntries(Path p) { + if (p == null) { + return false; + } + if (p.size() > 0) { + return true; + } + return false; + } + + private File outfile = null; + + private Path mergefiles = null; + + private Path addfiles = null; + + private boolean compress = false; + +} + + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java new file mode 100644 index 00000000..499cca27 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java @@ -0,0 +1,458 @@ +/* + * 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. + * + */ +/** + * jlink.java links together multiple .jar files Original code by Patrick + * Beard. Modifications to work with ANT by Matthew Kuperus Heun. + * + */ +package org.apache.tools.ant.taskdefs.optional.jlink; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.Vector; +import java.util.zip.CRC32; +import java.util.zip.Deflater; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +import org.apache.tools.ant.util.FileUtils; + +// CheckStyle:TypeNameCheck OFF - bc +/** + * jlink links together multiple .jar files. + */ +public class jlink { + private static final int BUFFER_SIZE = 8192; + private static final int VECTOR_INIT_SIZE = 10; + + private String outfile = null; + + private Vector mergefiles = new Vector(VECTOR_INIT_SIZE); + + private Vector addfiles = new Vector(VECTOR_INIT_SIZE); + + private boolean compression = false; + + // CheckStyle:VisibilityModifier OFF - bc + + byte[] buffer = new byte[BUFFER_SIZE]; + + // CheckStyle:VisibilityModifier OFF - bc + + /** The file that will be created by this instance of jlink. + * @param outfile the file to create. + */ + public void setOutfile(String outfile) { + if (outfile == null) { + return; + } + this.outfile = outfile; + } + + + /** + * Adds a file to be merged into the output. + * @param fileToMerge the file to merge into the output. + */ + public void addMergeFile(String fileToMerge) { + if (fileToMerge == null) { + return; + } + mergefiles.addElement(fileToMerge); + } + + + /** Adds a file to be added into the output. + * @param fileToAdd the file to add to the output. + */ + public void addAddFile(String fileToAdd) { + if (fileToAdd == null) { + return; + } + addfiles.addElement(fileToAdd); + } + + + /** + * Adds several files to be merged into the output. + * @param filesToMerge an array of files to merge into the output. + */ + public void addMergeFiles(String[] filesToMerge) { + if (filesToMerge == null) { + return; + } + for (int i = 0; i < filesToMerge.length; i++) { + addMergeFile(filesToMerge[i]); + } + } + + + /** + * Adds several file to be added into the output. + * @param filesToAdd an array of files to add to the output. + */ + public void addAddFiles(String[] filesToAdd) { + if (filesToAdd == null) { + return; + } + for (int i = 0; i < filesToAdd.length; i++) { + addAddFile(filesToAdd[i]); + } + } + + + /** + * Determines whether output will be compressed. + * @param compress if true use compression. + */ + public void setCompression(boolean compress) { + this.compression = compress; + } + + + /** + * Performs the linking of files. Addfiles are added to the output as-is. + * For example, a jar file is added to the output as a jar file. However, + * mergefiles are first examined for their type. If it is a jar or zip + * file, the contents will be extracted from the mergefile and entered + * into the output. If a zip or jar file is encountered in a subdirectory + * it will be added, not merged. If a directory is encountered, it becomes + * the root entry of all the files below it. Thus, you can provide + * multiple, disjoint directories, as addfiles: they will all be added in + * a rational manner to outfile. + * @throws Exception on error. + */ + public void link() throws Exception { + ZipOutputStream output = new ZipOutputStream(new FileOutputStream(outfile)); + + if (compression) { + output.setMethod(ZipOutputStream.DEFLATED); + output.setLevel(Deflater.DEFAULT_COMPRESSION); + } else { + output.setMethod(ZipOutputStream.STORED); + } + + Enumeration merges = mergefiles.elements(); + + while (merges.hasMoreElements()) { + String path = (String) merges.nextElement(); + File f = new File(path); + + if (f.getName().endsWith(".jar") || f.getName().endsWith(".zip")) { + //Do the merge + mergeZipJarContents(output, f); + } else { + //Add this file to the addfiles Vector and add it + //later at the top level of the output file. + addAddFile(path); + } + } + + Enumeration adds = addfiles.elements(); + + while (adds.hasMoreElements()) { + String name = (String) adds.nextElement(); + File f = new File(name); + + if (f.isDirectory()) { + //System.out.println("in jlink: adding directory contents of " + f.getPath()); + addDirContents(output, f, f.getName() + '/', compression); + } else { + addFile(output, f, "", compression); + } + } + FileUtils.close(output); + } + + + /** + * The command line entry point for jlink. + * @param args an array of arguments + */ + public static void main(String[] args) { + // jlink output input1 ... inputN + if (args.length < 2) { + System.out.println("usage: jlink output input1 ... inputN"); + System.exit(1); + } + jlink linker = new jlink(); + + linker.setOutfile(args[0]); + // To maintain compatibility with the command-line version, + // we will only add files to be merged. + for (int i = 1; i < args.length; i++) { + linker.addMergeFile(args[i]); + } + try { + linker.link(); + } catch (Exception ex) { + System.err.print(ex.getMessage()); + } + } + + + /* + * Actually performs the merging of f into the output. + * f should be a zip or jar file. + */ + private void mergeZipJarContents(ZipOutputStream output, File f) throws IOException { + //Check to see that the file with name "name" exists. + if (!f.exists()) { + return; + } + ZipFile zipf = new ZipFile(f); + Enumeration entries = zipf.entries(); + + while (entries.hasMoreElements()) { + ZipEntry inputEntry = (ZipEntry) entries.nextElement(); + //Ignore manifest entries. They're bound to cause conflicts between + //files that are being merged. User should supply their own + //manifest file when doing the merge. + String inputEntryName = inputEntry.getName(); + int index = inputEntryName.indexOf("META-INF"); + + if (index < 0) { + //META-INF not found in the name of the entry. Go ahead and process it. + try { + output.putNextEntry(processEntry(zipf, inputEntry)); + } catch (ZipException ex) { + //If we get here, it could be because we are trying to put a + //directory entry that already exists. + //For example, we're trying to write "com", but a previous + //entry from another mergefile was called "com". + //In that case, just ignore the error and go on to the + //next entry. + String mess = ex.getMessage(); + + if (mess.indexOf("duplicate") >= 0) { + //It was the duplicate entry. + continue; + } else { + // I hate to admit it, but we don't know what happened + // here. Throw the Exception. + throw ex; + } + } + + InputStream in = zipf.getInputStream(inputEntry); + int len = buffer.length; + int count = -1; + + while ((count = in.read(buffer, 0, len)) > 0) { + output.write(buffer, 0, count); + } + in.close(); + output.closeEntry(); + } + } + zipf.close(); + } + + + /* + * Adds contents of a directory to the output. + */ + private void addDirContents(ZipOutputStream output, File dir, String prefix, + boolean compress) throws IOException { + String[] contents = dir.list(); + + for (int i = 0; i < contents.length; ++i) { + String name = contents[i]; + File file = new File(dir, name); + + if (file.isDirectory()) { + addDirContents(output, file, prefix + name + '/', compress); + } else { + addFile(output, file, prefix, compress); + } + } + } + + + /* + * Gets the name of an entry in the file. This is the real name + * which for a class is the name of the package with the class + * name appended. + */ + private String getEntryName(File file, String prefix) { + String name = file.getName(); + + if (!name.endsWith(".class")) { + // see if the file is in fact a .class file, and determine its actual name. + InputStream input = null; + try { + input = new FileInputStream(file); + String className = ClassNameReader.getClassName(input); + + if (className != null) { + return className.replace('.', '/') + ".class"; + } + } catch (IOException ioe) { + //do nothing + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException e) { + //do nothing + } + } + } + } + System.out.println("From " + file.getPath() + " and prefix " + prefix + + ", creating entry " + prefix + name); + return (prefix + name); + } + + + /* + * Adds a file to the output stream. + */ + private void addFile(ZipOutputStream output, File file, String prefix, + boolean compress) throws IOException { + //Make sure file exists + if (!file.exists()) { + return; + } + ZipEntry entry = new ZipEntry(getEntryName(file, prefix)); + + entry.setTime(file.lastModified()); + entry.setSize(file.length()); + if (!compress) { + entry.setCrc(calcChecksum(file)); + } + FileInputStream input = new FileInputStream(file); + + addToOutputStream(output, input, entry); + } + + + /* + * A convenience method that several other methods might call. + */ + private void addToOutputStream(ZipOutputStream output, InputStream input, + ZipEntry ze) throws IOException { + try { + output.putNextEntry(ze); + } catch (ZipException zipEx) { + //This entry already exists. So, go with the first one. + input.close(); + return; + } + + int numBytes = -1; + + while ((numBytes = input.read(buffer)) > 0) { + output.write(buffer, 0, numBytes); + } + output.closeEntry(); + input.close(); + } + + + /* + * A method that does the work on a given entry in a mergefile. + * The big deal is to set the right parameters in the ZipEntry + * on the output stream. + */ + private ZipEntry processEntry(ZipFile zip, ZipEntry inputEntry) { + /* + First, some notes. + On MRJ 2.2.2, getting the size, compressed size, and CRC32 from the + ZipInputStream does not work for compressed (deflated) files. Those calls return -1. + For uncompressed (stored) files, those calls do work. + However, using ZipFile.getEntries() works for both compressed and + uncompressed files. + + Now, from some simple testing I did, it seems that the value of CRC-32 is + independent of the compression setting. So, it should be easy to pass this + information on to the output entry. + */ + String name = inputEntry.getName(); + + if (!(inputEntry.isDirectory() || name.endsWith(".class"))) { + try { + InputStream input = zip.getInputStream(zip.getEntry(name)); + String className = ClassNameReader.getClassName(input); + + input.close(); + if (className != null) { + name = className.replace('.', '/') + ".class"; + } + } catch (IOException ioe) { + //do nothing + } + } + ZipEntry outputEntry = new ZipEntry(name); + + outputEntry.setTime(inputEntry.getTime()); + outputEntry.setExtra(inputEntry.getExtra()); + outputEntry.setComment(inputEntry.getComment()); + outputEntry.setTime(inputEntry.getTime()); + if (compression) { + outputEntry.setMethod(ZipEntry.DEFLATED); + //Note, don't need to specify size or crc for compressed files. + } else { + outputEntry.setMethod(ZipEntry.STORED); + outputEntry.setCrc(inputEntry.getCrc()); + outputEntry.setSize(inputEntry.getSize()); + } + return outputEntry; + } + + + /* + * Necessary in the case where you add a entry that + * is not compressed. + */ + private long calcChecksum(File f) throws IOException { + BufferedInputStream in = new BufferedInputStream(new FileInputStream(f)); + + return calcChecksum(in); + } + + + /* + * Necessary in the case where you add a entry that + * is not compressed. + */ + private long calcChecksum(InputStream in) throws IOException { + CRC32 crc = new CRC32(); + int len = buffer.length; + int count = -1; + int haveRead = 0; + + while ((count = in.read(buffer, 0, len)) > 0) { + haveRead += count; + crc.update(buffer, 0, count); + } + in.close(); + return crc.getValue(); + } + + +} + + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/Jasper41Mangler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/Jasper41Mangler.java new file mode 100644 index 00000000..609938c9 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/Jasper41Mangler.java @@ -0,0 +1,93 @@ +/* + * 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.tools.ant.taskdefs.optional.jsp; + +import java.io.File; + +/** + * this class implements the name mangling rules of the jasper in tomcat4.1.x + * which is likely to remain for some time + * @see "org.apache.jasper.JspCompilationContext" + */ +public class Jasper41Mangler implements JspMangler { + + + /** + * map from a jsp file to a java filename; does not do packages + * + * @param jspFile file + * @return java filename + */ + public String mapJspToJavaName(File jspFile) { + String jspUri = jspFile.getAbsolutePath(); + int start = jspUri.lastIndexOf(File.separatorChar) + 1; + int end = jspUri.length(); + StringBuffer modifiedClassName; + modifiedClassName = new StringBuffer(jspUri.length() - start); + if (!Character.isJavaIdentifierStart(jspUri.charAt(start)) + || jspUri.charAt(start) == '_') { + // If the first char is not a start of Java identifier or is _ + // prepend a '_'. + modifiedClassName.append('_'); + } + for (int i = start; i < end; i++) { + char ch = jspUri.charAt(i); + if (Character.isJavaIdentifierPart(ch)) { + modifiedClassName.append(ch); + } else if (ch == '.') { + modifiedClassName.append('_'); + } else { + modifiedClassName.append(mangleChar(ch)); + } + } + return modifiedClassName.toString(); + } + + /** + * Mangle the specified character to create a legal Java class name. + */ + private static String mangleChar(char ch) { + // CheckStyle:MagicNumber OFF + String s = Integer.toHexString(ch); + int nzeros = 5 - s.length(); + char[] result = new char[6]; + result[0] = '_'; + for (int i = 1; i <= nzeros; i++) { + result[i] = '0'; + } + for (int i = nzeros + 1, j = 0; i < 6; i++, j++) { + result[i] = s.charAt(j); + } + return new String(result); + // CheckStyle:MagicNumber ON + } + + + /** + * taking in the substring representing the path relative to the source dir + * return a new string representing the destination path + * @param path not used. + * @return null as this is not implemented. + * @todo + */ + public String mapPath(String path) { + return null; + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspC.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspC.java new file mode 100644 index 00000000..4274bf0d --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspC.java @@ -0,0 +1,709 @@ +/* + * 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.tools.ant.taskdefs.optional.jsp; + +import java.io.File; +import java.util.Date; +import java.util.Enumeration; +import java.util.Vector; + +import org.apache.tools.ant.AntClassLoader; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.MatchingTask; +import org.apache.tools.ant.taskdefs.optional.jsp.compilers.JspCompilerAdapter; +import org.apache.tools.ant.taskdefs.optional.jsp.compilers.JspCompilerAdapterFactory; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.Reference; + +/** + * Runs a JSP compiler. + * + * <p> This task takes the given jsp files and compiles them into java + * files. It is then up to the user to compile the java files into classes. + * + * <p> The task requires the srcdir and destdir attributes to be + * set. This Task is a MatchingTask, so the files to be compiled can be + * specified using includes/excludes attributes or nested include/exclude + * elements. Optional attributes are verbose (set the verbosity level passed + * to jasper), package (name of the destination package for generated java + * classes and classpath (the classpath to use when running the jsp + * compiler). + * <p> This task supports the nested elements classpath (A Path) and + * classpathref (A Reference) which can be used in preference to the + * attribute classpath, if the jsp compiler is not already in the ant + * classpath. + * + * <p><h4>Usage</h4> + * <pre> + * <jspc srcdir="${basedir}/src/war" + * destdir="${basedir}/gensrc" + * package="com.i3sp.jsp" + * verbose="9"> + * <include name="**\/*.jsp" /> + * </jspc> + * </pre> + * + * <p> Large Amount of cutting and pasting from the Javac task... + * @since 1.5 + */ +public class JspC extends MatchingTask { + private Path classpath; + private Path compilerClasspath; + private Path src; + private File destDir; + private String packageName; + /** name of the compiler to use */ + private String compilerName = "jasper"; + + /** + * -ieplugin <clsid> Java Plugin classid for Internet Explorer + */ + private String iepluginid; + private boolean mapped; + private int verbose = 0; + // CheckStyle:VisibilityModifier OFF - bc + protected Vector compileList = new Vector(); + Vector javaFiles = new Vector(); + + /** + * flag to control action on execution trouble + */ + protected boolean failOnError = true; + + /** + * -uriroot <dir> The root directory that uri files should be resolved + * against, + */ + private File uriroot; + + /** + * -webinc <file> Creates partial servlet mappings for the -webapp option + */ + private File webinc; + + /** + * -webxml <file> Creates a complete web.xml when using the -webapp option. + */ + + private File webxml; + + /** + * web apps + */ + protected WebAppParameter webApp; + + + + private static final String FAIL_MSG + = "Compile failed, messages should have been provided."; + + // CheckStyle:VisibilityModifier ON + + /** + * Set the path for source JSP files. + * @param srcDir the source path. + */ + public void setSrcDir(Path srcDir) { + if (src == null) { + src = srcDir; + } else { + src.append(srcDir); + } + } + + /** + * Get the source dir. + * @return the source path. + */ + public Path getSrcDir() { + return src; + } + + /** + * Set the destination directory into which the JSP source + * files should be compiled. + * @param destDir the destination directory. + */ + public void setDestdir(File destDir) { + this.destDir = destDir; + } + + /** + * Get the destination directory. + * @return the directory. + */ + public File getDestdir() { + return destDir; + } + + /** + * Set the name of the package the compiled jsp files should be in. + * @param pkg the name of the package. + */ + public void setPackage(String pkg) { + this.packageName = pkg; + } + + /** + * Get the name of the package. + * @return the package. + */ + public String getPackage() { + return packageName; + } + + /** + * Set the verbose level of the compiler + * @param i the verbose level to use. + */ + public void setVerbose(int i) { + verbose = i; + } + + /** + * Get the verbose level. + * @return the level. + */ + public int getVerbose() { + return verbose; + } + + /** + * Whether or not the build should halt if compilation fails. + * Defaults to <code>true</code>. + * @param fail a <code>boolean</code> value. + */ + public void setFailonerror(boolean fail) { + failOnError = fail; + } + /** + * Gets the failonerror flag. + * @return the flag. + */ + public boolean getFailonerror() { + return failOnError; + } + + /** + * Get the IE CLASSID value. + * @return the value. + */ + public String getIeplugin() { + return iepluginid; + } + /** + * Java Plugin CLASSID for Internet Explorer + * @param iepluginid the id to use. + */ + public void setIeplugin(String iepluginid) { + this.iepluginid = iepluginid; + } + + /** + * If true, generate separate write() calls for each HTML line + * in the JSP. + * @return mapping status + */ + public boolean isMapped() { + return mapped; + } + + /** + * If true, generate separate write() calls for each HTML line + * in the JSP. + * @param mapped a <code>boolean</code> value. + */ + public void setMapped(boolean mapped) { + this.mapped = mapped; + } + + /** + * The URI context of relative URI references in the JSP pages. + * If it does not exist then it is derived from the location + * of the file relative to the declared or derived value of uriroot. + * + * @param uribase The new Uribase value + */ + public void setUribase(File uribase) { + log("Uribase is currently an unused parameter", Project.MSG_WARN); + } + + /** + * Get the uri base value. + * @return the value. + */ + public File getUribase() { + return uriroot; + } + + /** + * The root directory that uri files should be resolved + * against. (Default is the directory jspc is invoked from) + * + * @param uriroot The new Uribase value + */ + public void setUriroot(File uriroot) { + this.uriroot = uriroot; + } + + /** + * Get the uri root value. + * @return the value. + */ + public File getUriroot() { + return uriroot; + } + + + /** + * Set the classpath to be used for this compilation. + * @param cp the path to be used. + */ + public void setClasspath(Path cp) { + if (classpath == null) { + classpath = cp; + } else { + classpath.append(cp); + } + } + + /** + * Adds a path to the classpath. + * @return a path to be configured. + */ + public Path createClasspath() { + if (classpath == null) { + classpath = new Path(getProject()); + } + return classpath.createPath(); + } + + /** + * Adds a reference to a classpath defined elsewhere + * @param r a reference to a classpath. + */ + public void setClasspathRef(Reference r) { + createClasspath().setRefid(r); + } + + /** + * Get the classpath. + * @return the classpath. + */ + public Path getClasspath() { + return classpath; + } + + /** + * Set the classpath to be used to find this compiler adapter + * @param cp the compiler classpath. + */ + public void setCompilerclasspath(Path cp) { + if (compilerClasspath == null) { + compilerClasspath = cp; + } else { + compilerClasspath.append(cp); + } + } + + /** + * get the classpath used to find the compiler adapter + * @return the compiler classpath. + */ + public Path getCompilerclasspath() { + return compilerClasspath; + } + + /** + * Support nested compiler classpath, used to locate compiler adapter + * @return a path to be configured. + */ + public Path createCompilerclasspath() { + if (compilerClasspath == null) { + compilerClasspath = new Path(getProject()); + } + return compilerClasspath.createPath(); + } + + /** + * Filename for web.xml. + * + * @param webxml The new Webxml value + */ + public void setWebxml(File webxml) { + this.webxml = webxml; + } + + /** + * Filename for web.xml. + * @return The filename for web.xml. + */ + public File getWebxml() { + return this.webxml; + } + + /** + * output filename for the fraction of web.xml that lists + * servlets. + * @param webinc The new Webinc value + */ + public void setWebinc(File webinc) { + this.webinc = webinc; + } + + /** + * Get the webinc attribute. + * @return the webinc attribute. + */ + public File getWebinc() { + return this.webinc; + } + + /** + * Adds a single webapp. + * + * @param webappParam add a web app parameter + * @throws BuildException if more than one webapp is specified. + */ + public void addWebApp(WebAppParameter webappParam) + throws BuildException { + //demand create vector of filesets + if (webApp == null) { + webApp = webappParam; + } else { + throw new BuildException("Only one webapp can be specified"); + } + } + + /** + * Get the web app. + * @return the web app attribute. + */ + public WebAppParameter getWebApp() { + return webApp; + } + + /** + * Class name of a JSP compiler adapter. + * @param compiler the compiler class name. + */ + public void setCompiler(String compiler) { + this.compilerName = compiler; + } + + /** + * get the list of files to compile + * @return the list of files. + */ + public Vector getCompileList() { + return compileList; + } + + /** + * execute by building up a list of files that + * have changed and hand them off to a jsp compiler + * @throws BuildException on error. + */ + public void execute() + throws BuildException { + + // make sure that we've got a destdir + if (destDir == null) { + throw new BuildException("destdir attribute must be set!", + getLocation()); + } + + if (!destDir.isDirectory()) { + throw new BuildException("destination directory \"" + destDir + + "\" does not exist or is not a directory", + getLocation()); + } + + File dest = getActualDestDir(); + + AntClassLoader al = null; + try { + //bind to a compiler + JspCompilerAdapter compiler = + JspCompilerAdapterFactory + .getCompiler(compilerName, this, + al = getProject().createClassLoader(compilerClasspath)); + + //if we are a webapp, hand off to the compiler, which had + //better handle it + if (webApp != null) { + doCompilation(compiler); + return; + } + + // make sure that we've got a srcdir + if (src == null) { + throw new BuildException("srcdir attribute must be set!", + getLocation()); + } + String [] list = src.list(); + if (list.length == 0) { + throw new BuildException("srcdir attribute must be set!", + getLocation()); + } + + + // if the compiler does its own dependency stuff, we just + // call it right now + if (compiler.implementsOwnDependencyChecking()) { + doCompilation(compiler); + return; + } + + //the remainder of this method is only for compilers that + //need their dependency work done + JspMangler mangler = compiler.createMangler(); + + // scan source directories and dest directory to build up both copy + // lists and compile lists + resetFileLists(); + int filecount = 0; + for (int i = 0; i < list.length; i++) { + File srcDir = getProject().resolveFile(list[i]); + if (!srcDir.exists()) { + throw new BuildException("srcdir \"" + srcDir.getPath() + + "\" does not exist!", + getLocation()); + } + DirectoryScanner ds = this.getDirectoryScanner(srcDir); + String[] files = ds.getIncludedFiles(); + filecount = files.length; + scanDir(srcDir, dest, mangler, files); + } + + // compile the source files + + log("compiling " + compileList.size() + " files", + Project.MSG_VERBOSE); + + if (compileList.size() > 0) { + + log("Compiling " + compileList.size() + " source file" + + (compileList.size() == 1 ? "" : "s") + + " to " + + dest); + doCompilation(compiler); + + } else { + if (filecount == 0) { + log("there were no files to compile", Project.MSG_INFO); + } else { + log("all files are up to date", Project.MSG_VERBOSE); + } + } + } finally { + if (al != null) { + al.cleanup(); + } + } + } + + /** + * calculate where the files will end up: + * this is destDir or it id destDir + the package name + */ + private File getActualDestDir() { + File dest = null; + if (packageName == null) { + dest = destDir; + } else { + String path = destDir.getPath() + File.separatorChar + + packageName.replace('.', File.separatorChar); + dest = new File(path); + } + return dest; + } + + /** + * do the compile + */ + private void doCompilation(JspCompilerAdapter compiler) + throws BuildException { + // now we need to populate the compiler adapter + compiler.setJspc(this); + + // finally, lets execute the compiler!! + if (!compiler.execute()) { + if (failOnError) { + throw new BuildException(FAIL_MSG, getLocation()); + } else { + log(FAIL_MSG, Project.MSG_ERR); + } + } + } + + /** + * Clear the list of files to be compiled and copied.. + */ + protected void resetFileLists() { + compileList.removeAllElements(); + } + + /** + * Scans the directory looking for source files to be compiled. + * The results are returned in the class variable compileList + * @param srcDir the source directory. + * @param dest the destination directory. + * @param mangler the jsp filename mangler. + * @param files the file names to mangle. + */ + protected void scanDir(File srcDir, File dest, JspMangler mangler, + String[] files) { + + long now = (new Date()).getTime(); + + for (int i = 0; i < files.length; i++) { + String filename = files[i]; + File srcFile = new File(srcDir, filename); + File javaFile = mapToJavaFile(mangler, srcFile, srcDir, dest); + if (javaFile == null) { + continue; + } + + if (srcFile.lastModified() > now) { + log("Warning: file modified in the future: " + filename, + Project.MSG_WARN); + } + boolean shouldCompile = false; + shouldCompile = isCompileNeeded(srcFile, javaFile); + if (shouldCompile) { + compileList.addElement(srcFile.getAbsolutePath()); + javaFiles.addElement(javaFile); + } + } + } + + /** + * Test whether or not compilation is needed. A return value of + * <code>true<code> means yes, <code>false</code> means + * our tests do not indicate this, but as the TLDs are + * not used for dependency checking this is not guaranteed. + * The current tests are + * <ol> + * <li>no dest file + * <li>dest file out of date w.r.t source + * <li>dest file zero bytes long + * </ol> + * @param srcFile JSP source file + * @param javaFile JSP dest file + * @return true if a compile is definitely needed. + * + */ + private boolean isCompileNeeded(File srcFile, File javaFile) { + boolean shouldCompile = false; + if (!javaFile.exists()) { + shouldCompile = true; + log("Compiling " + srcFile.getPath() + + " because java file " + javaFile.getPath() + + " does not exist", Project.MSG_VERBOSE); + } else { + if (srcFile.lastModified() > javaFile.lastModified()) { + shouldCompile = true; + log("Compiling " + srcFile.getPath() + + " because it is out of date with respect to " + + javaFile.getPath(), + Project.MSG_VERBOSE); + } else { + if (javaFile.length() == 0) { + shouldCompile = true; + log("Compiling " + srcFile.getPath() + + " because java file " + javaFile.getPath() + + " is empty", Project.MSG_VERBOSE); + } + } + } + return shouldCompile; + } + + + /** + * get a filename from our jsp file. + * @param mangler the jsp filename manager. + * @param srcFile the source file. + * @param srcDir the source directory. + * @param dest the destination directory. + * @return the filename. + * @todo support packages and subdirs + */ + protected File mapToJavaFile(JspMangler mangler, File srcFile, File srcDir, + File dest) { + if (!srcFile.getName().endsWith(".jsp")) { + return null; + } + String javaFileName = mangler.mapJspToJavaName(srcFile); + // String srcFileDir=srcFile.getParent(); + return new File(dest, javaFileName); + } + + /** + * delete any java output files that are empty + * this is to get around a little defect in jasper: when it + * fails, it leaves incomplete files around. + */ + public void deleteEmptyJavaFiles() { + if (javaFiles != null) { + Enumeration e = javaFiles.elements(); + while (e.hasMoreElements()) { + File file = (File) e.nextElement(); + if (file.exists() && file.length() == 0) { + log("deleting empty output file " + file); + file.delete(); + } + } + } + } + + /** + * static inner class used as a parameter element + */ + public static class WebAppParameter { + + /** + * the sole option + */ + private File directory; + + /** + * query current directory + * @return the directory. + */ + public File getDirectory() { + return directory; + } + + /** + * set directory; alternate syntax + * @param directory the base dir. + */ + public void setBaseDir(File directory) { + this.directory = directory; + } + //end inner class + } + + + //end class +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspMangler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspMangler.java new file mode 100644 index 00000000..f62492c3 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspMangler.java @@ -0,0 +1,47 @@ +/* + * 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.tools.ant.taskdefs.optional.jsp; + +import java.io.File; + +/** + * This is an interface to the Mangler service that jspc needs to map + * JSP file names to java files. + * Note the complete lack of correlation + * with Jasper's mangler interface. + */ +public interface JspMangler { + + + /** + * map from a jsp file to a java filename; does not do packages + * + * @param jspFile file + * @return java filename + */ + String mapJspToJavaName(File jspFile); + + /** + * taking in the substring representing the path relative to the source dir + * return a new string representing the destination path + * @param path the path to map. + * @return the mapped path. + */ + String mapPath(String path); + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspNameMangler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspNameMangler.java new file mode 100644 index 00000000..6e08e7d5 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspNameMangler.java @@ -0,0 +1,155 @@ +/* + * 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.tools.ant.taskdefs.optional.jsp; +import java.io.File; + +import org.apache.tools.ant.util.StringUtils; + +/** + * This is a class derived from the Jasper code + * (org.apache.jasper.compiler.CommandLineCompiler) to map from a JSP filename + * to a valid Java classname. + * + */ +public class JspNameMangler implements JspMangler { + + // CheckStyle:ConstantNameCheck OFF - bc + + /** + * this is the list of keywords which can not be used as classnames + */ + public static final String[] keywords = { + "assert", + "abstract", "boolean", "break", "byte", + "case", "catch", "char", "class", + "const", "continue", "default", "do", + "double", "else", "extends", "final", + "finally", "float", "for", "goto", + "if", "implements", "import", + "instanceof", "int", "interface", + "long", "native", "new", "package", + "private", "protected", "public", + "return", "short", "static", "super", + "switch", "synchronized", "this", + "throw", "throws", "transient", + "try", "void", "volatile", "while" + }; + + // CheckStyle:ConstantNameCheck ON + + /** + * map from a jsp file to a java filename; does not do packages + * + * @param jspFile file + * @return java filename + */ + public String mapJspToJavaName(File jspFile) { + return mapJspToBaseName(jspFile) + ".java"; + } + + + /** + * map from a jsp file to a base name; does not deal with extensions + * + * @param jspFile jspFile file + * @return exensionless potentially remapped name + */ + private String mapJspToBaseName(File jspFile) { + String className; + className = stripExtension(jspFile); + + // since we don't mangle extensions like the servlet does, + // we need to check for keywords as class names + for (int i = 0; i < keywords.length; ++i) { + if (className.equals(keywords[i])) { + className += "%"; + break; + } + } + + // Fix for invalid characters. If you think of more add to the list. + StringBuffer modifiedClassName = new StringBuffer(className.length()); + // first char is more restrictive than the rest + char firstChar = className.charAt(0); + if (Character.isJavaIdentifierStart(firstChar)) { + modifiedClassName.append(firstChar); + } else { + modifiedClassName.append(mangleChar(firstChar)); + } + // this is the rest + for (int i = 1; i < className.length(); i++) { + char subChar = className.charAt(i); + if (Character.isJavaIdentifierPart(subChar)) { + modifiedClassName.append(subChar); + } else { + modifiedClassName.append(mangleChar(subChar)); + } + } + return modifiedClassName.toString(); + } + + + /** + * get short filename from file + * + * @param jspFile file in + * @return file without any jsp extension + */ + private String stripExtension(File jspFile) { + return StringUtils.removeSuffix(jspFile.getName(), ".jsp"); + } + + + /** + * definition of the char escaping algorithm + * + * @param ch char to mangle + * @return mangled string; 5 digit hex value + */ + private static String mangleChar(char ch) { + // CheckStyle:MagicNumber OFF + if (ch == File.separatorChar) { + ch = '/'; + } + String s = Integer.toHexString(ch); + int nzeros = 5 - s.length(); + char[] result = new char[6]; + result[0] = '_'; + for (int i = 1; i <= nzeros; ++i) { + result[i] = '0'; + } + int resultIndex = 0; + for (int i = nzeros + 1; i < 6; ++i) { + result[i] = s.charAt(resultIndex++); + } + return new String(result); + // CheckStyle:MagicNumber ON + } + + /** + * taking in the substring representing the path relative to the source dir + * return a new string representing the destination path + * not supported, as jasper in tomcat4.0 doesn't either + * @param path not used + * @return null always. + */ + public String mapPath(String path) { + return null; + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/WLJspc.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/WLJspc.java new file mode 100644 index 00000000..45a427ad --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/WLJspc.java @@ -0,0 +1,334 @@ +/* + * 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.tools.ant.taskdefs.optional.jsp; + +//apache/ant imports +import java.io.File; +import java.util.Date; +import java.util.StringTokenizer; +import java.util.Vector; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Java; +import org.apache.tools.ant.taskdefs.MatchingTask; +import org.apache.tools.ant.types.Path; + +/** + * Precompiles JSP's using WebLogic's JSP compiler (weblogic.jspc). + * + * Tested only on Weblogic 4.5.1 - NT4.0 and Solaris 5.7 + * + * required attributes + * src : root of source tree for JSP, ie, the document root for your weblogic server + * dest : root of destination directory, what you have set as + * WorkingDir in the weblogic properties + * package : start package name under which your JSP's would be compiled + * + * other attributes + * classpath + * + * A classpath should be set which contains the weblogic classes as well as all + * application classes referenced by the JSP. The system classpath is also + * appended when the jspc is called, so you may choose to put everything in + * the classpath while calling Ant. However, since presumably the JSP's will + * reference classes being build by Ant, it would be better to explicitly add + * the classpath in the task + * + * The task checks timestamps on the JSP's and the generated classes, and compiles + * only those files that have changed. + * + * It follows the weblogic naming convention of putting classes in + * <b> _dirName/_fileName.class for dirname/fileName.jsp </b> + * + * Limitation: It compiles the files thru the Classic compiler only. + * Limitation: Since it is my experience that weblogic jspc throws out of + * memory error on being given too many files at one go, it is + * called multiple times with one jsp file each. + * + * <pre> + * example + * <target name="jspcompile" depends="compile"> + * <wljspc src="c:\\weblogic\\myserver\\public_html" + * dest="c:\\weblogic\\myserver\\serverclasses" package="myapp.jsp"> + * <classpath> + * <pathelement location="${weblogic.classpath}" /> + * <pathelement path="${compile.dest}" /> + * </classpath> + * + * </wljspc> + * </target> + * </pre> + * + */ + +public class WLJspc extends MatchingTask { + //TODO Test on other versions of weblogic + //TODO add more attributes to the task, to take care of all jspc options + //TODO Test on Unix + + /** root of compiled files tree */ + private File destinationDirectory; + + /** root of source files tree */ + private File sourceDirectory; + + /** package under which resultant classes will reside */ + private String destinationPackage; + + /** classpath used to compile the jsp files. */ + private Path compileClasspath; + + //private String compilerPath; //fully qualified name for the compiler executable + + private String pathToPackage = ""; + private Vector filesToDo = new Vector(); + + /** + * Run the task. + * @throws BuildException if there is an error. + */ + public void execute() throws BuildException { + if (!destinationDirectory.isDirectory()) { + throw new BuildException("destination directory " + + destinationDirectory.getPath() + " is not valid"); + } + + if (!sourceDirectory.isDirectory()) { + throw new BuildException("src directory " + + sourceDirectory.getPath() + " is not valid"); + } + + if (destinationPackage == null) { + throw new BuildException("package attribute must be present.", + getLocation()); + } + + + pathToPackage + = this.destinationPackage.replace('.', File.separatorChar); + // get all the files in the sourceDirectory + DirectoryScanner ds = super.getDirectoryScanner(sourceDirectory); + + //use the systemclasspath as well, to include the ant jar + if (compileClasspath == null) { + compileClasspath = new Path(getProject()); + } + + compileClasspath = compileClasspath.concatSystemClasspath(); + String[] files = ds.getIncludedFiles(); + + //Weblogic.jspc calls System.exit() ... have to fork + // Therefore, takes loads of time + // Can pass directories at a time (*.jsp) but easily runs out of + // memory on hefty dirs (even on a Sun) + Java helperTask = new Java(this); + helperTask.setFork(true); + helperTask.setClassname("weblogic.jspc"); + helperTask.setTaskName(getTaskName()); + // CheckStyle:MagicNumber OFF + String[] args = new String[12]; + // CheckStyle:MagicNumber ON + + File jspFile = null; + String parents = ""; + int j = 0; + //TODO this array stuff is a remnant of prev trials.. gotta remove. + args[j++] = "-d"; + args[j++] = destinationDirectory.getAbsolutePath().trim(); + args[j++] = "-docroot"; + args[j++] = sourceDirectory.getAbsolutePath().trim(); + args[j++] = "-keepgenerated"; + //Call compiler as class... dont want to fork again + //Use classic compiler -- can be parameterised? + args[j++] = "-compilerclass"; + args[j++] = "sun.tools.javac.Main"; + //Weblogic jspc does not seem to work unless u explicitly set this... + // Does not take the classpath from the env.... + // Am i missing something about the Java task?? + args[j++] = "-classpath"; + args[j++] = compileClasspath.toString(); + + this.scanDir(files); + log("Compiling " + filesToDo.size() + " JSP files"); + + final int size = filesToDo.size(); + for (int i = 0; i < size; i++) { + //TODO + // All this to get package according to weblogic standards + // Can be written better... this is too hacky! + // Careful.. similar code in scanDir , but slightly different!! + String filename = (String) filesToDo.elementAt(i); + jspFile = new File(filename); + args[j] = "-package"; + parents = jspFile.getParent(); + if ((parents != null) && (!("").equals(parents))) { + parents = this.replaceString(parents, File.separator, "_."); + args[j + 1] = destinationPackage + "." + "_" + parents; + } else { + args[j + 1] = destinationPackage; + } + + + args[j + 2] = sourceDirectory + File.separator + filename; + helperTask.clearArgs(); + + // CheckStyle:MagicNumber OFF + for (int x = 0; x < j + 3; x++) { + helperTask.createArg().setValue(args[x]); + } + // CheckStyle:MagicNumber ON + + helperTask.setClasspath(compileClasspath); + if (helperTask.executeJava() != 0) { + log(filename + " failed to compile", Project.MSG_WARN); + } + } + } + + + + /** + * Set the classpath to be used for this compilation. + * @param classpath the classpath to use. + */ + public void setClasspath(Path classpath) { + if (compileClasspath == null) { + compileClasspath = classpath; + } else { + compileClasspath.append(classpath); + } + } + + /** + * Maybe creates a nested classpath element. + * @return a path to be configured. + */ + public Path createClasspath() { + if (compileClasspath == null) { + compileClasspath = new Path(getProject()); + } + return compileClasspath; + } + + /** + * Set the directory containing the source jsp's + * + * + * @param dirName the directory containg the source jsp's + */ + public void setSrc(File dirName) { + + sourceDirectory = dirName; + } + + /** + * Set the directory containing the source jsp's + * + * + * @param dirName the directory containg the source jsp's + */ + public void setDest(File dirName) { + + destinationDirectory = dirName; + } + + /** + * Set the package under which the compiled classes go + * + * @param packageName the package name for the classes + */ + public void setPackage(String packageName) { + + destinationPackage = packageName; + } + + /** + * Scan the array of files and add the jsp + * files that need to be compiled to the filesToDo field. + * @param files the files to scan. + */ + protected void scanDir(String[] files) { + + long now = (new Date()).getTime(); + File jspFile = null; + String parents = null; + String pack = ""; + for (int i = 0; i < files.length; i++) { + File srcFile = new File(this.sourceDirectory, files[i]); + //TODO + // All this to convert source to destination directory according + // to weblogic standards Can be written better... this is too hacky! + jspFile = new File(files[i]); + parents = jspFile.getParent(); + + if ((parents != null) && (!("").equals(parents))) { + parents = this.replaceString(parents, File.separator, "_/"); + pack = pathToPackage + File.separator + "_" + parents; + } else { + pack = pathToPackage; + } + + String filePath = pack + File.separator + "_"; + int startingIndex = files[i].lastIndexOf(File.separator) != -1 + ? files[i].lastIndexOf(File.separator) + 1 : 0; + int endingIndex = files[i].indexOf(".jsp"); + if (endingIndex == -1) { + log("Skipping " + files[i] + ". Not a JSP", + Project.MSG_VERBOSE); + continue; + } + + filePath += files[i].substring(startingIndex, endingIndex); + filePath += ".class"; + File classFile = new File(this.destinationDirectory, filePath); + + if (srcFile.lastModified() > now) { + log("Warning: file modified in the future: " + + files[i], Project.MSG_WARN); + } + if (srcFile.lastModified() > classFile.lastModified()) { + filesToDo.addElement(files[i]); + log("Recompiling File " + files[i], Project.MSG_VERBOSE); + } + } + } + + + /** + * Replace occurrences of a string with a replacement string. + * @param inpString the string to convert. + * @param escapeChars the string to replace. + * @param replaceChars the string to place. + * @return the converted string. + */ + protected String replaceString(String inpString, String escapeChars, + String replaceChars) { + String localString = ""; + int numTokens = 0; + StringTokenizer st = new StringTokenizer(inpString, escapeChars, true); + numTokens = st.countTokens(); + for (int i = 0; i < numTokens; i++) { + String test = st.nextToken(); + test = (test.equals(escapeChars) ? replaceChars : test); + localString += test; + } + return localString; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/DefaultJspCompilerAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/DefaultJspCompilerAdapter.java new file mode 100644 index 00000000..5c4d0e3b --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/DefaultJspCompilerAdapter.java @@ -0,0 +1,153 @@ +/* + * 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.tools.ant.taskdefs.optional.jsp.compilers; + +import java.io.File; +import java.util.Enumeration; +import java.util.Vector; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.optional.jsp.JspC; +import org.apache.tools.ant.types.CommandlineJava; + +/** + * This is the default implementation for the JspCompilerAdapter interface. + * This is currently very light on the ground since only one compiler type is + * supported. + * + */ +public abstract class DefaultJspCompilerAdapter + implements JspCompilerAdapter { + + private static String lSep = System.getProperty("line.separator"); + + /** + * Logs the compilation parameters, adds the files to compile and logs the + * "niceSourceList" + * @param jspc the compiler task for logging + * @param compileList the list of files to compile + * @param cmd the command line used + */ + protected void logAndAddFilesToCompile(JspC jspc, + Vector compileList, + CommandlineJava cmd) { + jspc.log("Compilation " + cmd.describeJavaCommand(), + Project.MSG_VERBOSE); + + StringBuffer niceSourceList = new StringBuffer("File"); + if (compileList.size() != 1) { + niceSourceList.append("s"); + } + niceSourceList.append(" to be compiled:"); + + niceSourceList.append(lSep); + + Enumeration e = compileList.elements(); + while (e.hasMoreElements()) { + String arg = (String) e.nextElement(); + cmd.createArgument().setValue(arg); + niceSourceList.append(" "); + niceSourceList.append(arg); + niceSourceList.append(lSep); + } + + jspc.log(niceSourceList.toString(), Project.MSG_VERBOSE); + } + + // CheckStyle:VisibilityModifier OFF - bc + + /** + * our owner + */ + protected JspC owner; + + // CheckStyle:VisibilityModifier ON + + /** + * set the owner + * @param owner the owner JspC compiler + */ + public void setJspc(JspC owner) { + this.owner = owner; + } + + /** get the owner + * @return the owner; should never be null + */ + public JspC getJspc() { + return owner; + } + + + /** + * add an argument oneple to the argument list, if the value aint null + * @param cmd the command line + * @param argument The argument + */ + protected void addArg(CommandlineJava cmd, String argument) { + if (argument != null && argument.length() != 0) { + cmd.createArgument().setValue(argument); + } + } + + + /** + * add an argument tuple to the argument list, if the value aint null + * @param cmd the command line + * @param argument The argument + * @param value the parameter + */ + protected void addArg(CommandlineJava cmd, String argument, String value) { + if (value != null) { + cmd.createArgument().setValue(argument); + cmd.createArgument().setValue(value); + } + } + + /** + * add an argument tuple to the arg list, if the file parameter aint null + * @param cmd the command line + * @param argument The argument + * @param file the parameter + */ + protected void addArg(CommandlineJava cmd, String argument, File file) { + if (file != null) { + cmd.createArgument().setValue(argument); + cmd.createArgument().setFile(file); + } + } + + /** + * ask if compiler can sort out its own dependencies + * @return true if the compiler wants to do its own + * depends + */ + public boolean implementsOwnDependencyChecking() { + return false; + } + + /** + * get our project + * @return owner project data + */ + public Project getProject() { + return getJspc().getProject(); + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JasperC.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JasperC.java new file mode 100644 index 00000000..f0becacb --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JasperC.java @@ -0,0 +1,182 @@ +/* + * 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.tools.ant.taskdefs.optional.jsp.compilers; + +import java.io.File; + +import org.apache.tools.ant.AntClassLoader; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Java; +import org.apache.tools.ant.taskdefs.optional.jsp.JspC; +import org.apache.tools.ant.taskdefs.optional.jsp.JspMangler; +import org.apache.tools.ant.types.CommandlineJava; +import org.apache.tools.ant.types.Path; + +/** + * The implementation of the jasper compiler. + * This is a cut-and-paste of the original Jspc task. + * + * @since ant1.5 + */ +public class JasperC extends DefaultJspCompilerAdapter { + + // CheckStyle:VisibilityModifier OFF - bc + + /** + * what produces java classes from .jsp files + */ + JspMangler mangler; + + // CheckStyle:VisibilityModifier ON + + /** + * Constructor for JasperC. + * @param mangler a filename converter + */ + public JasperC(JspMangler mangler) { + this.mangler = mangler; + } + + /** + * Our execute method. + * @return true if successful + * @throws BuildException on error + */ + public boolean execute() + throws BuildException { + getJspc().log("Using jasper compiler", Project.MSG_VERBOSE); + CommandlineJava cmd = setupJasperCommand(); + + try { + // Create an instance of the compiler, redirecting output to + // the project log + Java java = new Java(owner); + Path p = getClasspath(); + if (getJspc().getClasspath() != null) { + getProject().log("using user supplied classpath: " + p, + Project.MSG_DEBUG); + } else { + getProject().log("using system classpath: " + p, + Project.MSG_DEBUG); + } + java.setClasspath(p); + java.setDir(getProject().getBaseDir()); + java.setClassname("org.apache.jasper.JspC"); + //this is really irritating; we need a way to set stuff + String []args = cmd.getJavaCommand().getArguments(); + for (int i = 0; i < args.length; i++) { + java.createArg().setValue(args[i]); + } + java.setFailonerror(getJspc().getFailonerror()); + //we are forking here to be sure that if JspC calls + //System.exit() it doesn't halt the build + java.setFork(true); + java.setTaskName("jasperc"); + java.execute(); + return true; + } catch (Exception ex) { + if (ex instanceof BuildException) { + throw (BuildException) ex; + } else { + throw new BuildException("Error running jsp compiler: ", + ex, getJspc().getLocation()); + } + } finally { + getJspc().deleteEmptyJavaFiles(); + } + } + + + + /** + * build up a command line + * @return a command line for jasper + */ + private CommandlineJava setupJasperCommand() { + CommandlineJava cmd = new CommandlineJava(); + JspC jspc = getJspc(); + addArg(cmd, "-d", jspc.getDestdir()); + addArg(cmd, "-p", jspc.getPackage()); + + if (!isTomcat5x()) { + addArg(cmd, "-v" + jspc.getVerbose()); + } else { + getProject().log("this task doesn't support Tomcat 5.x properly, " + + "please use the Tomcat provided jspc task " + + "instead"); + } + + addArg(cmd, "-uriroot", jspc.getUriroot()); + addArg(cmd, "-uribase", jspc.getUribase()); + addArg(cmd, "-ieplugin", jspc.getIeplugin()); + addArg(cmd, "-webinc", jspc.getWebinc()); + addArg(cmd, "-webxml", jspc.getWebxml()); + addArg(cmd, "-die9"); + + if (jspc.isMapped()) { + addArg(cmd, "-mapped"); + } + if (jspc.getWebApp() != null) { + File dir = jspc.getWebApp().getDirectory(); + addArg(cmd, "-webapp", dir); + } + logAndAddFilesToCompile(getJspc(), getJspc().getCompileList(), cmd); + return cmd; + } + + /** + * @return an instance of the mangler this compiler uses + */ + + public JspMangler createMangler() { + return mangler; + } + + /** + * @since Ant 1.6.2 + */ + private Path getClasspath() { + Path p = getJspc().getClasspath(); + if (p == null) { + p = new Path(getProject()); + return p.concatSystemClasspath("only"); + } else { + return p.concatSystemClasspath("ignore"); + } + } + + /** + * @since Ant 1.6.2 + */ + private boolean isTomcat5x() { + AntClassLoader l = null; + try { + l = getProject().createClassLoader(getClasspath()); + l.loadClass("org.apache.jasper.tagplugins.jstl.If"); + return true; + } catch (ClassNotFoundException e) { + return false; + } finally { + if (l != null) { + l.cleanup(); + } + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapter.java new file mode 100644 index 00000000..16b67f9d --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapter.java @@ -0,0 +1,64 @@ +/* + * 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.tools.ant.taskdefs.optional.jsp.compilers; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.optional.jsp.JspC; +import org.apache.tools.ant.taskdefs.optional.jsp.JspMangler; + +/** + * The interface that all jsp compiler adapters must adher to. + * + * <p>A compiler adapter is an adapter that interprets the jspc's + * parameters in preparation to be passed off to the compiler this + * adapter represents. As all the necessary values are stored in the + * Jspc task itself, the only thing all adapters need is the jsp + * task, the execute command and a parameterless constructor (for + * reflection).</p> + * + */ + +public interface JspCompilerAdapter { + + /** + * Sets the compiler attributes, which are stored in the Jspc task. + * @param attributes the jsp compiler attributes + */ + void setJspc(JspC attributes); + + /** + * Executes the task. + * + * @return has the compilation been successful + * @throws BuildException on error + */ + boolean execute() throws BuildException; + + /** + * @return an instance of the mangler this compiler uses + */ + + JspMangler createMangler(); + + /** + * ask if compiler can sort out its own dependencies + * @return true if the compiler wants to do its own + * depends + */ + boolean implementsOwnDependencyChecking(); +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapterFactory.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapterFactory.java new file mode 100644 index 00000000..2876ba0c --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapterFactory.java @@ -0,0 +1,122 @@ +/* + * 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.tools.ant.taskdefs.optional.jsp.compilers; + +import org.apache.tools.ant.AntClassLoader; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.optional.jsp.Jasper41Mangler; +import org.apache.tools.ant.taskdefs.optional.jsp.JspNameMangler; + + +/** + * Creates the necessary compiler adapter, given basic criteria. + * + */ +public final class JspCompilerAdapterFactory { + + /** This is a singleton -- can't create instances!! */ + private JspCompilerAdapterFactory() { + } + + /** + * Based on the parameter passed in, this method creates the necessary + * factory desired. + * + * The current mapping for compiler names are as follows: + * <ul><li>jasper = jasper compiler (the default) + * <li><i>a fully qualified classname</i> = the name of a jsp compiler + * adapter + * </ul> + * + * @param compilerType either the name of the desired compiler, or the + * full classname of the compiler's adapter. + * @param task a task to log through. + * @return the compiler + * @throws BuildException if the compiler type could not be resolved into + * a compiler adapter. + */ + public static JspCompilerAdapter getCompiler(String compilerType, Task task) + throws BuildException { + return getCompiler(compilerType, task, + // Memory-Leak in line below + task.getProject().createClassLoader(null)); + } + + /** + * Based on the parameter passed in, this method creates the necessary + * factory desired. + * + * The current mapping for compiler names are as follows: + * <ul><li>jasper = jasper compiler (the default) + * <li><i>a fully qualified classname</i> = the name of a jsp compiler + * adapter + * </ul> + * + * @param compilerType either the name of the desired compiler, or the + * full classname of the compiler's adapter. + * @param task a task to log through. + * @param loader AntClassLoader with which the compiler should be loaded + * @return the compiler + * @throws BuildException if the compiler type could not be resolved into + * a compiler adapter. + */ + public static JspCompilerAdapter getCompiler(String compilerType, Task task, + AntClassLoader loader) + throws BuildException { + + if (compilerType.equalsIgnoreCase("jasper")) { + //tomcat4.0 gets the old mangler + return new JasperC(new JspNameMangler()); + } + if (compilerType.equalsIgnoreCase("jasper41")) { + //tomcat4.1 gets the new one + return new JasperC(new Jasper41Mangler()); + } + return resolveClassName(compilerType, loader); + } + + /** + * Tries to resolve the given classname into a compiler adapter. + * Throws a fit if it can't. + * + * @param className The fully qualified classname to be created. + * @param classloader Classloader with which to load the class + * @throws BuildException This is the fit that is thrown if className + * isn't an instance of JspCompilerAdapter. + */ + private static JspCompilerAdapter resolveClassName(String className, + AntClassLoader classloader) + throws BuildException { + try { + Class c = classloader.findClass(className); + Object o = c.newInstance(); + return (JspCompilerAdapter) o; + } catch (ClassNotFoundException cnfe) { + throw new BuildException(className + " can\'t be found.", cnfe); + } catch (ClassCastException cce) { + throw new BuildException(className + " isn\'t the classname of " + + "a compiler adapter.", cce); + } catch (Throwable t) { + // for all other possibilities + throw new BuildException(className + " caused an interesting " + + "exception.", t); + } + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/AggregateTransformer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/AggregateTransformer.java new file mode 100644 index 00000000..ec3506d4 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/AggregateTransformer.java @@ -0,0 +1,346 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.Delete; +import org.apache.tools.ant.taskdefs.TempFile; +import org.apache.tools.ant.taskdefs.XSLTProcess; +import org.apache.tools.ant.types.EnumeratedAttribute; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.Resource; +import org.apache.tools.ant.types.resources.FileResource; +import org.apache.tools.ant.types.resources.URLResource; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.JAXPUtils; +import org.w3c.dom.Document; + +/** + * Transform a JUnit xml report. + * The default transformation generates an html report in either framed or non-framed + * style. The non-framed style is convenient to have a concise report via mail, the + * framed report is much more convenient if you want to browse into different + * packages or testcases since it is a Javadoc like report. + * + */ +public class AggregateTransformer { + /** + * name of the frames format. + */ + public static final String FRAMES = "frames"; + + /** + * name of the no frames format. + */ + public static final String NOFRAMES = "noframes"; + + /** + * defines acceptable formats. + */ + public static class Format extends EnumeratedAttribute { + /** + * list authorized values. + * @return authorized values. + */ + public String[] getValues() { + return new String[]{FRAMES, NOFRAMES}; + } + } + + // CheckStyle:VisibilityModifier OFF - bc + /** Task */ + protected Task task; + + /** the xml document to process */ + protected Document document; + + /** the style directory. XSLs should be read from here if necessary */ + protected File styleDir; + + /** the destination directory, this is the root from where html should be generated */ + protected File toDir; + + /** + * The internal XSLT task used to perform the transformation. + * + * @since Ant 1.9.5 + */ + private XSLTProcess xsltTask; + + /** + * Instance of a utility class to use for file operations. + * + * @since Ant 1.7 + */ + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** + * Used to ensure the uniqueness of a property + */ + private static int counter = 0; + + /** the format to use for the report. Must be <tt>FRAMES</tt> or <tt>NOFRAMES</tt> */ + protected String format = FRAMES; + + /** XML Parser factory */ + private static DocumentBuilderFactory privateDBFactory; + + /** XML Parser factory accessible to subclasses */ + protected static DocumentBuilderFactory dbfactory; + + static { + privateDBFactory = DocumentBuilderFactory.newInstance(); + dbfactory = privateDBFactory; + } + // CheckStyle:VisibilityModifier ON + + /** + * constructor creating the transformer from the junitreport task. + * @param task task delegating to this class + */ + public AggregateTransformer(Task task) { + this.task = task; + xsltTask = new XSLTProcess(); + xsltTask.bindToOwner(task); + } + + /** + * Get the Document Builder Factory + * + * @return the DocumentBuilderFactory instance in use + */ + protected static DocumentBuilderFactory getDocumentBuilderFactory() { + return privateDBFactory; + } + + /** + * sets the format. + * @param format Must be <tt>FRAMES</tt> or <tt>NOFRAMES</tt> + */ + public void setFormat(Format format) { + this.format = format.getValue(); + } + + /** + * sets the input document. + * @param doc input dom tree + */ + public void setXmlDocument(Document doc) { + this.document = doc; + } + + /** + * Set the xml file to be processed. This is a helper if you want + * to set the file directly. Much more for testing purposes. + * @param xmlfile xml file to be processed + * @throws BuildException if the document cannot be parsed. + */ + protected void setXmlfile(File xmlfile) throws BuildException { + try { + DocumentBuilder builder = privateDBFactory.newDocumentBuilder(); + InputStream in = new FileInputStream(xmlfile); + try { + Document doc = builder.parse(in); + setXmlDocument(doc); + } finally { + in.close(); + } + } catch (Exception e) { + throw new BuildException("Error while parsing document: " + xmlfile, e); + } + } + + /** + * set the style directory. It is optional and will override the + * default xsl used. + * @param styledir the directory containing the xsl files if the user + * would like to override with its own style. + */ + public void setStyledir(File styledir) { + this.styleDir = styledir; + } + + /** set the destination directory. + * @param todir the destination directory + */ + public void setTodir(File todir) { + this.toDir = todir; + } + + /** set the extension of the output files + * @param ext extension. + */ + public void setExtension(String ext) { + task.log("extension is not used anymore", Project.MSG_WARN); + } + + /** + * Create an instance of an XSL parameter for configuration by Ant. + * + * @return an instance of the Param class to be configured. + * @since Ant 1.7 + */ + public XSLTProcess.Param createParam() { + return xsltTask.createParam(); + } + + /** + * Creates a classpath to be used for the internal XSLT task. + * + * @return the classpath to be configured + * @since Ant 1.9.5 + */ + public Path createClasspath() { + return xsltTask.createClasspath(); + } + + /** + * Creates a factory configuration to be used for the internal XSLT task. + * + * @return the factory description to be configured + * @since Ant 1.9.5 + */ + public XSLTProcess.Factory createFactory() { + return xsltTask.createFactory(); + } + + /** + * transformation + * @throws BuildException exception if something goes wrong with the transformation. + */ + public void transform() throws BuildException { + checkOptions(); + Project project = task.getProject(); + + TempFile tempFileTask = new TempFile(); + tempFileTask.bindToOwner(task); + + xsltTask.setXslResource(getStylesheet()); + + // acrobatic cast. + xsltTask.setIn(((XMLResultAggregator) task).getDestinationFile()); + File outputFile = null; + if (format.equals(FRAMES)) { + String tempFileProperty = getClass().getName() + String.valueOf(counter++); + File tmp = FILE_UTILS.resolveFile(project.getBaseDir(), project + .getProperty("java.io.tmpdir")); + tempFileTask.setDestDir(tmp); + tempFileTask.setProperty(tempFileProperty); + tempFileTask.execute(); + outputFile = new File(project.getProperty(tempFileProperty)); + } else { + outputFile = new File(toDir, "junit-noframes.html"); + } + xsltTask.setOut(outputFile); + XSLTProcess.Param paramx = xsltTask.createParam(); + paramx.setProject(task.getProject()); + paramx.setName("output.dir"); + paramx.setExpression(toDir.getAbsolutePath()); + final long t0 = System.currentTimeMillis(); + try { + xsltTask.execute(); + } catch (Exception e) { + throw new BuildException("Errors while applying transformations: " + e.getMessage(), e); + } + final long dt = System.currentTimeMillis() - t0; + task.log("Transform time: " + dt + "ms"); + if (format.equals(FRAMES)) { + Delete delete = new Delete(); + delete.bindToOwner(task); + delete.setFile(outputFile); + delete.execute(); + } + } + + /** + * access the stylesheet to be used as a resource. + * @return stylesheet as a resource + */ + protected Resource getStylesheet() { + String xslname = "junit-frames.xsl"; + if (NOFRAMES.equals(format)) { + xslname = "junit-noframes.xsl"; + } + if (styleDir == null) { + // If style dir is not specified we have to retrieve + // the stylesheet from the classloader + URL stylesheetURL = getClass().getClassLoader().getResource( + "org/apache/tools/ant/taskdefs/optional/junit/xsl/" + xslname); + return new URLResource(stylesheetURL); + } + // If we are here, then the style dir is here and we + // should read the stylesheet from the filesystem + return new FileResource(new File(styleDir, xslname)); + } + + /** check for invalid options + * @throws BuildException if something goes wrong. + */ + protected void checkOptions() throws BuildException { + // set the destination directory relative from the project if needed. + if (toDir == null) { + toDir = task.getProject().resolveFile("."); + } else if (!toDir.isAbsolute()) { + toDir = task.getProject().resolveFile(toDir.getPath()); + } + } + + /** + * Get the systemid of the appropriate stylesheet based on its + * name and styledir. If no styledir is defined it will load + * it as a java resource in the xsl child package, otherwise it + * will get it from the given directory. + * @return system ID of the stylesheet. + * @throws IOException thrown if the requested stylesheet does + * not exist. + */ + protected String getStylesheetSystemId() throws IOException { + String xslname = "junit-frames.xsl"; + if (NOFRAMES.equals(format)) { + xslname = "junit-noframes.xsl"; + } + if (styleDir == null) { + URL url = getClass().getResource("xsl/" + xslname); + if (url == null) { + throw new FileNotFoundException("Could not find jar resource " + xslname); + } + return url.toExternalForm(); + } + File file = new File(styleDir, xslname); + if (!file.exists()) { + throw new FileNotFoundException("Could not find file '" + file + "'"); + } + return JAXPUtils.getSystemId(file); + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BaseTest.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BaseTest.java new file mode 100644 index 00000000..55e7a5d5 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BaseTest.java @@ -0,0 +1,241 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.io.File; +import java.util.Vector; + +/** + * Baseclass for BatchTest and JUnitTest. + * + */ +public abstract class BaseTest { + // CheckStyle:VisibilityModifier OFF - bc + protected boolean haltOnError = false; + protected boolean haltOnFail = false; + protected boolean filtertrace = true; + protected boolean fork = false; + protected String ifProperty = null; + protected String unlessProperty = null; + protected Vector formatters = new Vector(); + /** destination directory */ + protected File destDir = null; + + protected String failureProperty; + protected String errorProperty; + // CheckStyle:VisibilityModifier ON + + private Object ifCond, unlessCond; + private boolean skipNonTests; + + /** + * Set the filtertrace attribute. + * @param value a <code>boolean</code> value. + */ + public void setFiltertrace(boolean value) { + filtertrace = value; + } + + /** + * Get the filtertrace attribute. + * @return the attribute. + */ + public boolean getFiltertrace() { + return filtertrace; + } + + /** + * Set the fork attribute. + * @param value a <code>boolean</code> value. + */ + public void setFork(boolean value) { + fork = value; + } + + /** + * Get the fork attribute. + * @return the attribute. + */ + public boolean getFork() { + return fork; + } + + /** + * Set the haltonerror attribute. + * @param value a <code>boolean</code> value. + */ + public void setHaltonerror(boolean value) { + haltOnError = value; + } + + /** + * Set the haltonfailure attribute. + * @param value a <code>boolean</code> value. + */ + public void setHaltonfailure(boolean value) { + haltOnFail = value; + } + + /** + * Get the haltonerror attribute. + * @return the attribute. + */ + public boolean getHaltonerror() { + return haltOnError; + } + + /** + * Get the haltonfailure attribute. + * @return the attribute. + */ + public boolean getHaltonfailure() { + return haltOnFail; + } + + /** + * Set the if attribute. + * If this expression evaluates to true or the name of a property + * which is present in project, the test will be run. + * @param ifCondition the expression to evaluate + * @since Ant 1.8.0 + */ + public void setIf(Object ifCondition) { + ifCond = ifCondition; + ifProperty = ifCondition != null ? String.valueOf(ifCondition) : null; + } + + /** + * Set the if attribute. + * If this expression evaluates to true or the name of a property + * which is present in project, the test will be run. + * @param propertyName the expression to evaluate + */ + public void setIf(String propertyName) { + setIf((Object) propertyName); + } + + /** + * The if expression + * @since Ant 1.8.0 + */ + public Object getIfCondition() { + return ifCond; + } + + /** + * Set the unless attribute. If this expression evaluates to + * false or the name of a property which is not present in + * project, the test will be run. + * @param unlessCondition the expression to evaluate + * @since Ant 1.8.0 + */ + public void setUnless(Object unlessCondition) { + unlessCond = unlessCondition; + unlessProperty = unlessCondition != null + ? String.valueOf(unlessCondition) : null; + } + + /** + * Set the unless attribute. If this expression evaluates to + * false or the name of a property which is not present in + * project, the test will be run. + * @param propertyName the expression to evaluate + */ + public void setUnless(String propertyName) { + setUnless((Object) propertyName); + } + + /** + * The unless expression + * @since Ant 1.8.0 + */ + public Object getUnlessCondition() { + return unlessCond; + } + + /** + * Allow a formatter nested element. + * @param elem a formatter nested element. + */ + public void addFormatter(FormatterElement elem) { + formatters.addElement(elem); + } + + /** + * Sets the destination directory. + * @param destDir the destination directory. + */ + public void setTodir(File destDir) { + this.destDir = destDir; + } + + /** + * Get the destination directory. + * @return the destination directory as an absolute path if it exists + * otherwise return <tt>null</tt> + */ + public String getTodir() { + if (destDir != null) { + return destDir.getAbsolutePath(); + } + return null; + } + + /** + * Get the failure property name. + * @return the name of the property to set on failure. + */ + public String getFailureProperty() { + return failureProperty; + } + + /** + * Set the name of the failure property. + * @param failureProperty the name of the property to set if + * the test fails. + */ + public void setFailureProperty(String failureProperty) { + this.failureProperty = failureProperty; + } + + /** + * Get the failure property name. + * @return the name of the property to set on failure. + */ + public String getErrorProperty() { + return errorProperty; + } + + /** + * Set the name of the error property. + * @param errorProperty the name of the property to set if + * the test has an error. + */ + public void setErrorProperty(String errorProperty) { + this.errorProperty = errorProperty; + } + + public void setSkipNonTests(boolean skipNonTests) { + this.skipNonTests = skipNonTests; + } + + public boolean isSkipNonTests() { + return skipNonTests; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java new file mode 100644 index 00000000..f41b96f1 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java @@ -0,0 +1,202 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + + +import java.io.File; +import java.util.Enumeration; +import java.util.Vector; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Resource; +import org.apache.tools.ant.types.ResourceCollection; +import org.apache.tools.ant.types.resources.Resources; + +/** + * <p> Create then run <code>JUnitTest</code>'s based on the list of files + * given by the fileset attribute. + * + * <p> Every <code>.java</code> or <code>.class</code> file in the fileset is + * assumed to be a testcase. + * A <code>JUnitTest</code> is created for each of these named classes with + * basic setup inherited from the parent <code>BatchTest</code>. + * + * @see JUnitTest + */ +public final class BatchTest extends BaseTest { + + /** the reference to the project */ + private Project project; + + /** the list of filesets containing the testcase filename rules */ + private Resources resources = new Resources(); + + /** + * create a new batchtest instance + * @param project the project it depends on. + */ + public BatchTest(Project project) { + this.project = project; + resources.setCache(true); + } + + /** + * Add a new fileset instance to this batchtest. Whatever the fileset is, + * only filename that are <tt>.java</tt> or <tt>.class</tt> will be + * considered as 'candidates'. + * @param fs the new fileset containing the rules to get the testcases. + */ + public void addFileSet(FileSet fs) { + add(fs); + + // this one is here because the changes to support ResourceCollections + // have broken Magic's JUnitTestTask. + // + // The task adds a FileSet to a BatchTest instance using the + // Java API and without telling the FileSet about its project + // instance. The original code would pass in project on the + // call to getDirectoryScanner - which is now hidden deep into + // Resources.iterator() and not reachable. + if (fs.getProject() == null) { + fs.setProject(project); + } + } + + + /** + * Add a new ResourceCollection instance to this + * batchtest. Whatever the collection is, only names that are + * <tt>.java</tt> or <tt>.class</tt> will be considered as + * 'candidates'. + * @param rc the new ResourceCollection containing the rules to + * get the testcases. + * @since Ant 1.7 + */ + public void add(ResourceCollection rc) { + resources.add(rc); + } + + /** + * Return all <tt>JUnitTest</tt> instances obtain by applying the fileset rules. + * @return an enumeration of all elements of this batchtest that are + * a <tt>JUnitTest</tt> instance. + */ + public Enumeration elements() { + JUnitTest[] tests = createAllJUnitTest(); + return Enumerations.fromArray(tests); + } + + /** + * Convenient method to merge the <tt>JUnitTest</tt>s of this batchtest + * to a <tt>Vector</tt>. + * @param v the vector to which should be added all individual tests of this + * batch test. + */ + void addTestsTo(Vector v) { + JUnitTest[] tests = createAllJUnitTest(); + v.ensureCapacity(v.size() + tests.length); + for (int i = 0; i < tests.length; i++) { + v.addElement(tests[i]); + } + } + + /** + * Create all <tt>JUnitTest</tt>s based on the filesets. Each instance + * is configured to match this instance properties. + * @return the array of all <tt>JUnitTest</tt>s that belongs to this batch. + */ + private JUnitTest[] createAllJUnitTest() { + String[] filenames = getFilenames(); + JUnitTest[] tests = new JUnitTest[filenames.length]; + for (int i = 0; i < tests.length; i++) { + String classname = javaToClass(filenames[i]); + tests[i] = createJUnitTest(classname); + } + return tests; + } + + /** + * Iterate over all filesets and return the filename of all files + * that end with <tt>.java</tt> or <tt>.class</tt>. This is to avoid + * wrapping a <tt>JUnitTest</tt> over an xml file for example. A Testcase + * is obviously a java file (compiled or not). + * @return an array of filenames without their extension. As they should + * normally be taken from their root, filenames should match their fully + * qualified class name (If it is not the case it will fail when running the test). + * For the class <tt>org/apache/Whatever.class</tt> it will return <tt>org/apache/Whatever</tt>. + */ + private String[] getFilenames() { + Vector v = new Vector(); + for (Resource r : resources) { + if (r.isExists()) { + String pathname = r.getName(); + if (pathname.endsWith(".java")) { + v.addElement(pathname.substring(0, pathname.length() - ".java".length())); + } else if (pathname.endsWith(".class")) { + v.addElement(pathname.substring(0, pathname.length() - ".class".length())); + } + } + } + + String[] files = new String[v.size()]; + v.copyInto(files); + return files; + } + + /** + * Convenient method to convert a pathname without extension to a + * fully qualified classname. For example <tt>org/apache/Whatever</tt> will + * be converted to <tt>org.apache.Whatever</tt> + * @param filename the filename to "convert" to a classname. + * @return the classname matching the filename. + */ + public static String javaToClass(String filename) { + return filename.replace(File.separatorChar, '.').replace('/', '.') + .replace('\\', '.'); + } + + /** + * Create a <tt>JUnitTest</tt> that has the same property as this + * <tt>BatchTest</tt> instance. + * @param classname the name of the class that should be run as a + * <tt>JUnitTest</tt>. It must be a fully qualified name. + * @return the <tt>JUnitTest</tt> over the given classname. + */ + private JUnitTest createJUnitTest(String classname) { + JUnitTest test = new JUnitTest(); + test.setName(classname); + test.setHaltonerror(this.haltOnError); + test.setHaltonfailure(this.haltOnFail); + test.setFiltertrace(this.filtertrace); + test.setFork(this.fork); + test.setIf(getIfCondition()); + test.setUnless(getUnlessCondition()); + test.setTodir(this.destDir); + test.setFailureProperty(failureProperty); + test.setErrorProperty(errorProperty); + test.setSkipNonTests(isSkipNonTests()); + Enumeration list = this.formatters.elements(); + while (list.hasMoreElements()) { + test.addFormatter((FormatterElement) list.nextElement()); + } + return test; + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BriefJUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BriefJUnitResultFormatter.java new file mode 100644 index 00000000..46d6c616 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BriefJUnitResultFormatter.java @@ -0,0 +1,300 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringWriter; +import java.text.NumberFormat; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.StringUtils; + +/** + * Prints plain text output of the test to a specified Writer. + * Inspired by the PlainJUnitResultFormatter. + * + * @see FormatterElement + * @see PlainJUnitResultFormatter + */ +public class BriefJUnitResultFormatter implements JUnitResultFormatter, IgnoredTestListener { + + private static final double ONE_SECOND = 1000.0; + + /** + * Where to write the log to. + */ + private OutputStream out; + + /** + * Used for writing the results. + */ + private BufferedWriter output; + + /** + * Used as part of formatting the results. + */ + private StringWriter results; + + /** + * Used for writing formatted results to. + */ + private BufferedWriter resultWriter; + + /** + * Formatter for timings. + */ + private NumberFormat numberFormat = NumberFormat.getInstance(); + + /** + * Output suite has written to System.out + */ + private String systemOutput = null; + + /** + * Output suite has written to System.err + */ + private String systemError = null; + + /** + * Constructor for BriefJUnitResultFormatter. + */ + public BriefJUnitResultFormatter() { + results = new StringWriter(); + resultWriter = new BufferedWriter(results); + } + + /** + * Sets the stream the formatter is supposed to write its results to. + * @param out the output stream to write to + */ + public void setOutput(OutputStream out) { + this.out = out; + output = new BufferedWriter(new java.io.OutputStreamWriter(out)); + } + + /** + * @see JUnitResultFormatter#setSystemOutput(String) + */ + /** {@inheritDoc}. */ + public void setSystemOutput(String out) { + systemOutput = out; + } + + /** + * @see JUnitResultFormatter#setSystemError(String) + */ + /** {@inheritDoc}. */ + public void setSystemError(String err) { + systemError = err; + } + + + /** + * The whole testsuite started. + * @param suite the test suite + */ + public void startTestSuite(JUnitTest suite) { + if (output == null) { + return; // Quick return - no output do nothing. + } + StringBuffer sb = new StringBuffer("Testsuite: "); + sb.append(suite.getName()); + sb.append(StringUtils.LINE_SEP); + try { + output.write(sb.toString()); + output.flush(); + } catch (IOException ex) { + throw new BuildException(ex); + } + } + + /** + * The whole testsuite ended. + * @param suite the test suite + */ + public void endTestSuite(JUnitTest suite) { + StringBuffer sb = new StringBuffer("Tests run: "); + sb.append(suite.runCount()); + sb.append(", Failures: "); + sb.append(suite.failureCount()); + sb.append(", Errors: "); + sb.append(suite.errorCount()); + sb.append(", Skipped: "); + sb.append(suite.skipCount()); + sb.append(", Time elapsed: "); + sb.append(numberFormat.format(suite.getRunTime() / ONE_SECOND)); + sb.append(" sec"); + sb.append(StringUtils.LINE_SEP); + sb.append(StringUtils.LINE_SEP); + + // append the err and output streams to the log + if (systemOutput != null && systemOutput.length() > 0) { + sb.append("------------- Standard Output ---------------") + .append(StringUtils.LINE_SEP) + .append(systemOutput) + .append("------------- ---------------- ---------------") + .append(StringUtils.LINE_SEP); + } + + if (systemError != null && systemError.length() > 0) { + sb.append("------------- Standard Error -----------------") + .append(StringUtils.LINE_SEP) + .append(systemError) + .append("------------- ---------------- ---------------") + .append(StringUtils.LINE_SEP); + } + + if (output != null) { + try { + output.write(sb.toString()); + resultWriter.close(); + output.write(results.toString()); + } catch (IOException ex) { + throw new BuildException(ex); + } finally { + try { + output.flush(); + } catch (IOException ex) { + // swallow, there has likely been an exception before this + } + if (out != System.out && out != System.err) { + FileUtils.close(out); + } + } + } + } + + /** + * A test started. + * @param test a test + */ + public void startTest(Test test) { + } + + /** + * A test ended. + * @param test a test + */ + public void endTest(Test test) { + } + + /** + * Interface TestListener for JUnit <= 3.4. + * + * <p>A Test failed. + * @param test a test + * @param t the exception thrown by the test + */ + public void addFailure(Test test, Throwable t) { + formatError("\tFAILED", test, t); + } + + /** + * Interface TestListener for JUnit > 3.4. + * + * <p>A Test failed. + * @param test a test + * @param t the assertion failed by the test + */ + public void addFailure(Test test, AssertionFailedError t) { + addFailure(test, (Throwable) t); + } + + /** + * A test caused an error. + * @param test a test + * @param error the error thrown by the test + */ + public void addError(Test test, Throwable error) { + formatError("\tCaused an ERROR", test, error); + } + + /** + * Format the test for printing.. + * @param test a test + * @return the formatted testname + */ + protected String formatTest(Test test) { + if (test == null) { + return "Null Test: "; + } else { + return "Testcase: " + test.toString() + ":"; + } + } + + /** + * Format an error and print it. + * @param type the type of error + * @param test the test that failed + * @param error the exception that the test threw + */ + protected synchronized void formatError(String type, Test test, + Throwable error) { + if (test != null) { + endTest(test); + } + + try { + resultWriter.write(formatTest(test) + type); + resultWriter.newLine(); + resultWriter.write(String.valueOf(error.getMessage())); + resultWriter.newLine(); + String strace = JUnitTestRunner.getFilteredTrace(error); + resultWriter.write(strace); + resultWriter.newLine(); + resultWriter.newLine(); + } catch (IOException ex) { + throw new BuildException(ex); + } + } + + + public void testIgnored(Test test) { + formatSkip(test, JUnitVersionHelper.getIgnoreMessage(test)); + } + + + public void formatSkip(Test test, String message) { + if (test != null) { + endTest(test); + } + + try { + resultWriter.write(formatTest(test) + "SKIPPED"); + if (message != null) { + resultWriter.write(": "); + resultWriter.write(message); + } + resultWriter.newLine(); + } catch (IOException ex) { + throw new BuildException(ex); + } + + } + + public void testAssumptionFailure(Test test, Throwable cause) { + formatSkip(test, cause.getMessage()); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Constants.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Constants.java new file mode 100644 index 00000000..368c72e2 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Constants.java @@ -0,0 +1,46 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +/** + * Constants, like filenames shared between various classes in this package. + */ +public class Constants { + + private Constants() { + } + + static final String METHOD_NAMES = "methods="; + static final String HALT_ON_ERROR = "haltOnError="; + static final String HALT_ON_FAILURE = "haltOnFailure="; + static final String FILTERTRACE = "filtertrace="; + static final String CRASHFILE = "crashfile="; + static final String BEFORE_FIRST_TEST = "BeforeFirstTest"; + static final String PROPSFILE = "propsfile="; + static final String SHOWOUTPUT = "showoutput="; + static final String OUTPUT_TO_FORMATTERS = "outputtoformatters="; + static final String FORMATTER = "formatter="; + static final String LOGTESTLISTENEREVENTS = "logtestlistenerevents="; + static final String TESTSFILE = "testsfile="; + static final String TERMINATED_SUCCESSFULLY = "terminated successfully"; + static final String LOG_FAILED_TESTS="logfailedtests="; + static final String SKIP_NON_TESTS = "skipNonTests="; + /** @since Ant 1.9.4 */ + static final String THREADID="threadid="; +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/CustomJUnit4TestAdapterCache.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/CustomJUnit4TestAdapterCache.java new file mode 100644 index 00000000..8ad40dd6 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/CustomJUnit4TestAdapterCache.java @@ -0,0 +1,90 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import junit.framework.JUnit4TestAdapter; +import junit.framework.JUnit4TestAdapterCache; +import junit.framework.TestResult; + +import org.junit.runner.Description; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; +import org.junit.runner.notification.RunNotifier; + +/** + * Provides a custom implementation of the notifier for a JUnit4TestAdapter + * so that skipped and ignored tests can be reported to the existing + * <tt>TestListener</tt>s. + * + */ +public class CustomJUnit4TestAdapterCache extends JUnit4TestAdapterCache { + + private static final CustomJUnit4TestAdapterCache INSTANCE = new CustomJUnit4TestAdapterCache(); + + public static CustomJUnit4TestAdapterCache getInstance() { + return INSTANCE; + } + + private CustomJUnit4TestAdapterCache() { + super(); + } + + public RunNotifier getNotifier(final TestResult result, final JUnit4TestAdapter adapter) { + return getNotifier(result); + } + + public RunNotifier getNotifier(final TestResult result) { + + final IgnoredTestResult resultWrapper = (IgnoredTestResult) result; + + RunNotifier notifier = new RunNotifier(); + notifier.addListener(new RunListener() { + @Override + public void testFailure(Failure failure) throws Exception { + result.addError(asTest(failure.getDescription()), failure.getException()); + } + + @Override + public void testFinished(Description description) throws Exception { + result.endTest(asTest(description)); + } + + @Override + public void testStarted(Description description) throws Exception { + result.startTest(asTest(description)); + } + + @Override + public void testIgnored(Description description) throws Exception { + if (resultWrapper != null) { + resultWrapper.testIgnored(asTest(description)); + } + } + + @Override + public void testAssumptionFailure(Failure failure) { + if (resultWrapper != null) { + resultWrapper.testAssumptionFailure(asTest(failure.getDescription()), failure.getException()); + } + } + }); + + return notifier; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/DOMUtil.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/DOMUtil.java new file mode 100644 index 00000000..325f44cf --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/DOMUtil.java @@ -0,0 +1,228 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.util.Vector; + +import org.w3c.dom.Attr; +import org.w3c.dom.CDATASection; +import org.w3c.dom.Comment; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.ProcessingInstruction; +import org.w3c.dom.Text; + +/** + * Some utilities that might be useful when manipulating DOM trees. + * + */ +public final class DOMUtil { + + /** unused constructor */ + private DOMUtil() { + } + + /** + * Filter interface to be applied when iterating over a DOM tree. + * Just think of it like a <tt>FileFilter</tt> clone. + */ + public interface NodeFilter { + /** + * @param node the node to check for acceptance. + * @return <tt>true</tt> if the node is accepted by this filter, + * otherwise <tt>false</tt> + */ + boolean accept(Node node); + } + + /** + * list a set of node that match a specific filter. The list can be made + * recursively or not. + * @param parent the parent node to search from + * @param filter the filter that children should match. + * @param recurse <tt>true</tt> if you want the list to be made recursively + * otherwise <tt>false</tt>. + * @return the node list that matches the filter. + */ + public static NodeList listChildNodes(Node parent, NodeFilter filter, boolean recurse) { + NodeListImpl matches = new NodeListImpl(); + NodeList children = parent.getChildNodes(); + if (children != null) { + final int len = children.getLength(); + for (int i = 0; i < len; i++) { + Node child = children.item(i); + if (filter.accept(child)) { + matches.addElement(child); + } + if (recurse) { + NodeList recmatches = listChildNodes(child, filter, recurse); + final int reclength = recmatches.getLength(); + for (int j = 0; j < reclength; j++) { + matches.addElement(recmatches.item(i)); + } + } + } + } + return matches; + } + + /** custom implementation of a nodelist */ + public static class NodeListImpl extends Vector implements NodeList { + private static final long serialVersionUID = 3175749150080946423L; + + /** + * Get the number of nodes in the list. + * @return the length of the list. + */ + public int getLength() { + return size(); + } + /** + * Get a particular node. + * @param i the index of the node to get. + * @return the node if the index is in bounds, null otherwise. + */ + public Node item(int i) { + try { + return (Node) elementAt(i); + } catch (ArrayIndexOutOfBoundsException e) { + return null; // conforming to NodeList interface + } + } + } + + /** + * return the attribute value of an element. + * @param node the node to get the attribute from. + * @param name the name of the attribute we are looking for the value. + * @return the value of the requested attribute or <tt>null</tt> if the + * attribute was not found or if <tt>node</tt> is not an <tt>Element</tt>. + */ + public static String getNodeAttribute(Node node, String name) { + if (node instanceof Element) { + Element element = (Element) node; + return element.getAttribute(name); + } + return null; + } + + + /** + * Iterate over the children of a given node and return the first node + * that has a specific name. + * @param parent the node to search child from. Can be <tt>null</tt>. + * @param tagname the child name we are looking for. Cannot be <tt>null</tt>. + * @return the first child that matches the given name or <tt>null</tt> if + * the parent is <tt>null</tt> or if a child does not match the + * given name. + */ + public static Element getChildByTagName (Node parent, String tagname) { + if (parent == null) { + return null; + } + NodeList childList = parent.getChildNodes(); + final int len = childList.getLength(); + for (int i = 0; i < len; i++) { + Node child = childList.item(i); + if (child != null && child.getNodeType() == Node.ELEMENT_NODE + && child.getNodeName().equals(tagname)) { + return (Element) child; + } + } + return null; + } + + /** + * Simple tree walker that will clone recursively a node. This is to + * avoid using parser-specific API such as Sun's <tt>changeNodeOwner</tt> + * when we are dealing with DOM L1 implementations since <tt>cloneNode(boolean)</tt> + * will not change the owner document. + * <tt>changeNodeOwner</tt> is much faster and avoid the costly cloning process. + * <tt>importNode</tt> is in the DOM L2 interface. + * @param parent the node parent to which we should do the import to. + * @param child the node to clone recursively. Its clone will be + * appended to <tt>parent</tt>. + * @return the cloned node that is appended to <tt>parent</tt> + */ + public static Node importNode(Node parent, Node child) { + Node copy = null; + final Document doc = parent.getOwnerDocument(); + + switch (child.getNodeType()) { + case Node.CDATA_SECTION_NODE: + copy = doc.createCDATASection(((CDATASection) child).getData()); + break; + case Node.COMMENT_NODE: + copy = doc.createComment(((Comment) child).getData()); + break; + case Node.DOCUMENT_FRAGMENT_NODE: + copy = doc.createDocumentFragment(); + break; + case Node.ELEMENT_NODE: + final Element elem = doc.createElement(((Element) child).getTagName()); + copy = elem; + final NamedNodeMap attributes = child.getAttributes(); + if (attributes != null) { + final int size = attributes.getLength(); + for (int i = 0; i < size; i++) { + final Attr attr = (Attr) attributes.item(i); + elem.setAttribute(attr.getName(), attr.getValue()); + } + } + break; + case Node.ENTITY_REFERENCE_NODE: + copy = doc.createEntityReference(child.getNodeName()); + break; + case Node.PROCESSING_INSTRUCTION_NODE: + final ProcessingInstruction pi = (ProcessingInstruction) child; + copy = doc.createProcessingInstruction(pi.getTarget(), pi.getData()); + break; + case Node.TEXT_NODE: + copy = doc.createTextNode(((Text) child).getData()); + break; + default: + // this should never happen + throw new IllegalStateException("Invalid node type: " + child.getNodeType()); + } + + // okay we have a copy of the child, now the child becomes the parent + // and we are iterating recursively over its children. + try { + final NodeList children = child.getChildNodes(); + if (children != null) { + final int size = children.getLength(); + for (int i = 0; i < size; i++) { + final Node newChild = children.item(i); + if (newChild != null) { + importNode(copy, newChild); + } + } + } + } catch (DOMException ignored) { + // Ignore + } + + // bingo append it. (this should normally not be done here) + parent.appendChild(copy); + return copy; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Enumerations.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Enumerations.java new file mode 100644 index 00000000..327547ef --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Enumerations.java @@ -0,0 +1,177 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.util.Enumeration; +import java.util.NoSuchElementException; + +/** + * A couple of methods related to enumerations that might be useful. + * This class should probably disappear once the required JDK is set to 1.2 + * instead of 1.1. + * + */ +public final class Enumerations { + + private Enumerations() { + } + + /** + * creates an enumeration from an array of objects. + * @param array the array of object to enumerate. + * @return the enumeration over the array of objects. + */ + public static Enumeration fromArray(Object[] array) { + return new ArrayEnumeration(array); + } + + /** + * creates an enumeration from an array of enumeration. The created enumeration + * will sequentially enumerate over all elements of each enumeration and skip + * <tt>null</tt> enumeration elements in the array. + * @param enums the array of enumerations. + * @return the enumeration over the array of enumerations. + */ + public static Enumeration fromCompound(Enumeration[] enums) { + return new CompoundEnumeration(enums); + } + +} + + +/** + * Convenient enumeration over an array of objects. + */ +class ArrayEnumeration implements Enumeration { + + /** object array */ + private Object[] array; + + /** current index */ + private int pos; + + /** + * Initialize a new enumeration that wraps an array. + * @param array the array of object to enumerate. + */ + public ArrayEnumeration(Object[] array) { + this.array = array; + this.pos = 0; + } + /** + * Tests if this enumeration contains more elements. + * + * @return <code>true</code> if and only if this enumeration object + * contains at least one more element to provide; + * <code>false</code> otherwise. + */ + public boolean hasMoreElements() { + return (pos < array.length); + } + + /** + * Returns the next element of this enumeration if this enumeration + * object has at least one more element to provide. + * + * @return the next element of this enumeration. + * @throws NoSuchElementException if no more elements exist. + */ + public Object nextElement() throws NoSuchElementException { + if (hasMoreElements()) { + Object o = array[pos]; + pos++; + return o; + } + throw new NoSuchElementException(); + } +} +/** + * Convenient enumeration over an array of enumeration. For example: + * <pre> + * Enumeration e1 = v1.elements(); + * while (e1.hasMoreElements()) { + * // do something + * } + * Enumeration e2 = v2.elements(); + * while (e2.hasMoreElements()) { + * // do the same thing + * } + * </pre> + * can be written as: + * <pre> + * Enumeration[] enums = { v1.elements(), v2.elements() }; + * Enumeration e = Enumerations.fromCompound(enums); + * while (e.hasMoreElements()) { + * // do something + * } + * </pre> + * Note that the enumeration will skip null elements in the array. The following is + * thus possible: + * <pre> + * Enumeration[] enums = { v1.elements(), null, v2.elements() }; // a null enumeration in the array + * Enumeration e = Enumerations.fromCompound(enums); + * while (e.hasMoreElements()) { + * // do something + * } + * </pre> + */ + class CompoundEnumeration implements Enumeration { + + /** enumeration array */ + private Enumeration[] enumArray; + + /** index in the enums array */ + private int index = 0; + + public CompoundEnumeration(Enumeration[] enumarray) { + this.enumArray = enumarray; + } + + /** + * Tests if this enumeration contains more elements. + * + * @return <code>true</code> if and only if this enumeration object + * contains at least one more element to provide; + * <code>false</code> otherwise. + */ + public boolean hasMoreElements() { + while (index < enumArray.length) { + if (enumArray[index] != null && enumArray[index].hasMoreElements()) { + return true; + } + index++; + } + return false; + } + + /** + * Returns the next element of this enumeration if this enumeration + * object has at least one more element to provide. + * + * @return the next element of this enumeration. + * @throws NoSuchElementException if no more elements exist. + */ + public Object nextElement() throws NoSuchElementException { + if (hasMoreElements()) { + return enumArray[index].nextElement(); + } + throw new NoSuchElementException(); + } +} + + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FailureRecorder.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FailureRecorder.java new file mode 100644 index 00000000..3046b75a --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FailureRecorder.java @@ -0,0 +1,448 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Iterator; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.Vector; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; + +import org.apache.tools.ant.BuildEvent; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.BuildListener; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectComponent; +import org.apache.tools.ant.util.FileUtils; + +/** + * <p>Collects all failing test <i>cases</i> and creates a new JUnit test class containing + * a suite() method which calls these failed tests.</p> + * <p>Having classes <i>A</i> ... <i>D</i> with each several testcases you could earn a new + * test class like + * <pre> + * // generated on: 2007.08.06 09:42:34,555 + * import junit.framework.*; + * public class FailedTests extends TestCase { + * public FailedTests(String testname) { + * super(testname); + * } + * public static Test suite() { + * TestSuite suite = new TestSuite(); + * suite.addTest( new B("test04") ); + * suite.addTest( new org.D("test10") ); + * return suite; + * } + * } + * </pre> + * + * Because each running test case gets its own formatter, we collect + * the failing test cases in a static list. Because we dont have a finalizer + * method in the formatters "lifecycle", we register this formatter as + * BuildListener and generate the new java source on taskFinished event. + * + * @since Ant 1.8.0 + */ +public class FailureRecorder extends ProjectComponent implements JUnitResultFormatter, BuildListener { + + /** + * This is the name of a magic System property ({@value}). The value of this + * <b>System</b> property should point to the location where to store the + * generated class (without suffix). + * Default location and name is defined in DEFAULT_CLASS_LOCATION. + * @see #DEFAULT_CLASS_LOCATION + */ + public static final String MAGIC_PROPERTY_CLASS_LOCATION + = "ant.junit.failureCollector"; + + /** Default location and name for the generated JUnit class file, + * in the temp directory + FailedTests */ + public static final String DEFAULT_CLASS_LOCATION + = System.getProperty("java.io.tmpdir") + "FailedTests"; + + /** Prefix for logging. {@value} */ + private static final String LOG_PREFIX = " [junit]"; + + /** Class names of failed tests without duplicates. */ + private static SortedSet/*<TestInfos>*/ failedTests = new TreeSet(); + + /** A writer for writing the generated source to. */ + private BufferedWriter writer; + + /** + * Location and name of the generated JUnit class. + * Lazy instantiated via getLocationName(). + */ + private static String locationName; + + /** + * Returns the (lazy evaluated) location for the collector class. + * Order for evaluation: System property > Ant property > default value + * @return location for the collector class + * @see #MAGIC_PROPERTY_CLASS_LOCATION + * @see #DEFAULT_CLASS_LOCATION + */ + private String getLocationName() { + if (locationName == null) { + String syspropValue = System.getProperty(MAGIC_PROPERTY_CLASS_LOCATION); + String antpropValue = getProject().getProperty(MAGIC_PROPERTY_CLASS_LOCATION); + + if (syspropValue != null) { + locationName = syspropValue; + verbose("System property '" + MAGIC_PROPERTY_CLASS_LOCATION + "' set, so use " + + "its value '" + syspropValue + "' as location for collector class."); + } else if (antpropValue != null) { + locationName = antpropValue; + verbose("Ant property '" + MAGIC_PROPERTY_CLASS_LOCATION + "' set, so use " + + "its value '" + antpropValue + "' as location for collector class."); + } else { + locationName = DEFAULT_CLASS_LOCATION; + verbose("System property '" + MAGIC_PROPERTY_CLASS_LOCATION + "' not set, so use " + + "value as location for collector class: '" + + DEFAULT_CLASS_LOCATION + "'"); + } + + File locationFile = new File(locationName); + if (!locationFile.isAbsolute()) { + File f = new File(getProject().getBaseDir(), locationName); + locationName = f.getAbsolutePath(); + verbose("Location file is relative (" + locationFile + ")" + + " use absolute path instead (" + locationName + ")"); + } + } + + return locationName; + } + + /** + * This method is called by the Ant runtime by reflection. We use the project reference for + * registration of this class as BuildListener. + * + * @param project + * project reference + */ + public void setProject(Project project) { + // store project reference for logging + super.setProject(project); + // check if already registered + boolean alreadyRegistered = false; + Vector allListeners = project.getBuildListeners(); + final int size = allListeners.size(); + for (int i = 0; i < size; i++) { + Object listener = allListeners.get(i); + if (listener instanceof FailureRecorder) { + alreadyRegistered = true; + break; + } + } + // register if needed + if (!alreadyRegistered) { + verbose("Register FailureRecorder (@" + this.hashCode() + ") as BuildListener"); + project.addBuildListener(this); + } + } + + // ===== JUnitResultFormatter ===== + + /** + * Not used + * {@inheritDoc} + */ + public void endTestSuite(JUnitTest suite) throws BuildException { + } + + /** + * Add the failed test to the list. + * @param test the test that errored. + * @param throwable the reason it errored. + * @see junit.framework.TestListener#addError(junit.framework.Test, java.lang.Throwable) + */ + public void addError(Test test, Throwable throwable) { + failedTests.add(new TestInfos(test)); + } + + // CheckStyle:LineLengthCheck OFF - @see is long + /** + * Add the failed test to the list. + * @param test the test that failed. + * @param error the assertion that failed. + * @see junit.framework.TestListener#addFailure(junit.framework.Test, junit.framework.AssertionFailedError) + */ + // CheckStyle:LineLengthCheck ON + public void addFailure(Test test, AssertionFailedError error) { + failedTests.add(new TestInfos(test)); + } + + /** + * Not used + * {@inheritDoc} + */ + public void setOutput(OutputStream out) { + // unused, close output file so it can be deleted before the VM exits + if (out != System.out) { + FileUtils.close(out); + } + } + + /** + * Not used + * {@inheritDoc} + */ + public void setSystemError(String err) { + } + + /** + * Not used + * {@inheritDoc} + */ + public void setSystemOutput(String out) { + } + + /** + * Not used + * {@inheritDoc} + */ + public void startTestSuite(JUnitTest suite) throws BuildException { + } + + /** + * Not used + * {@inheritDoc} + */ + public void endTest(Test test) { + } + + /** + * Not used + * {@inheritDoc} + */ + public void startTest(Test test) { + } + + // ===== "Templates" for generating the JUnit class ===== + + private void writeJavaClass() { + try { + File sourceFile = new File((getLocationName() + ".java")); + verbose("Write collector class to '" + sourceFile.getAbsolutePath() + "'"); + + if (sourceFile.exists() && !sourceFile.delete()) { + throw new IOException("could not delete " + sourceFile); + } + writer = new BufferedWriter(new FileWriter(sourceFile)); + + createClassHeader(); + createSuiteMethod(); + createClassFooter(); + + } catch (IOException e) { + e.printStackTrace(); + } finally { + FileUtils.close(writer); + } + } + + private void createClassHeader() throws IOException { + String className = getLocationName().replace('\\', '/'); + if (className.indexOf('/') > -1) { + className = className.substring(className.lastIndexOf('/') + 1); + } + SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss,SSS"); + writer.write("// generated on: "); + writer.write(sdf.format(new Date())); + writer.newLine(); + writer.write("import junit.framework.*;"); + writer.newLine(); + writer.write("public class "); + writer.write(className); + // If this class does not extend TC, Ant doesn't run these + writer.write(" extends TestCase {"); + writer.newLine(); + // standard String-constructor + writer.write(" public "); + writer.write(className); + writer.write("(String testname) {"); + writer.newLine(); + writer.write(" super(testname);"); + writer.newLine(); + writer.write(" }"); + writer.newLine(); + } + + private void createSuiteMethod() throws IOException { + writer.write(" public static Test suite() {"); + writer.newLine(); + writer.write(" TestSuite suite = new TestSuite();"); + writer.newLine(); + for (Iterator iter = failedTests.iterator(); iter.hasNext();) { + TestInfos testInfos = (TestInfos) iter.next(); + writer.write(" suite.addTest("); + writer.write(String.valueOf(testInfos)); + writer.write(");"); + writer.newLine(); + } + writer.write(" return suite;"); + writer.newLine(); + writer.write(" }"); + writer.newLine(); + } + + private void createClassFooter() throws IOException { + writer.write("}"); + writer.newLine(); + } + + // ===== Helper classes and methods ===== + + /** + * Logging facade in INFO-mode. + * @param message Log-message + */ + public void log(String message) { + getProject().log(LOG_PREFIX + " " + message, Project.MSG_INFO); + } + + /** + * Logging facade in VERBOSE-mode. + * @param message Log-message + */ + public void verbose(String message) { + getProject().log(LOG_PREFIX + " " + message, Project.MSG_VERBOSE); + } + + /** + * TestInfos holds information about a given test for later use. + */ + public static class TestInfos implements Comparable { + + /** The class name of the test. */ + private final String className; + + /** The method name of the testcase. */ + private final String methodName; + + /** + * This constructor extracts the needed information from the given test. + * @param test Test to analyze + */ + public TestInfos(Test test) { + className = test.getClass().getName(); + String _methodName = test.toString(); + methodName = _methodName.substring(0, _methodName.indexOf('(')); + } + + /** + * This String-Representation can directly be used for instantiation of + * the JUnit testcase. + * @return the string representation. + * @see java.lang.Object#toString() + * @see FailureRecorder#createSuiteMethod() + */ + public String toString() { + return "new " + className + "(\"" + methodName + "\")"; + } + + /** + * The SortedMap needs comparable elements. + * @param other the object to compare to. + * @return the result of the comparison. + * @see java.lang.Comparable#compareTo + * @see SortedSet#comparator() + */ + public int compareTo(Object other) { + if (other instanceof TestInfos) { + TestInfos otherInfos = (TestInfos) other; + return toString().compareTo(otherInfos.toString()); + } else { + return -1; + } + } + public boolean equals(Object obj) { + return obj instanceof TestInfos && toString().equals(obj.toString()); + } + public int hashCode() { + return toString().hashCode(); + } + } + + // ===== BuildListener ===== + + /** + * Not used + * {@inheritDoc} + */ + public void buildFinished(BuildEvent event) { + } + + /** + * Not used + * {@inheritDoc} + */ + public void buildStarted(BuildEvent event) { + } + + /** + * Not used + * {@inheritDoc} + */ + public void messageLogged(BuildEvent event) { + } + + /** + * Not used + * {@inheritDoc} + */ + public void targetFinished(BuildEvent event) { + } + + /** + * Not used + * {@inheritDoc} + */ + public void targetStarted(BuildEvent event) { + } + + /** + * The task outside of this JUnitResultFormatter is the <junit> task. So all tests passed + * and we could create the new java class. + * @param event not used + * @see org.apache.tools.ant.BuildListener#taskFinished(org.apache.tools.ant.BuildEvent) + */ + public void taskFinished(BuildEvent event) { + if (!failedTests.isEmpty()) { + writeJavaClass(); + } + } + + /** + * Not used + * {@inheritDoc} + */ + public void taskStarted(BuildEvent event) { + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FormatterElement.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FormatterElement.java new file mode 100644 index 00000000..f9fbcb0a --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FormatterElement.java @@ -0,0 +1,401 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.PropertyHelper; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.EnumeratedAttribute; +import org.apache.tools.ant.util.KeepAliveOutputStream; + +/** + * <p> A wrapper for the implementations of <code>JUnitResultFormatter</code>. + * In particular, used as a nested <code><formatter></code> element in + * a <code><junit></code> task. + * <p> For example, + * <code><pre> + * <junit printsummary="no" haltonfailure="yes" fork="false"> + * <formatter type="plain" usefile="false" /> + * <test name="org.apache.ecs.InternationalCharTest" /> + * </junit></pre></code> + * adds a <code>plain</code> type implementation + * (<code>PlainJUnitResultFormatter</code>) to display the results of the test. + * + * <p> Either the <code>type</code> or the <code>classname</code> attribute + * must be set. + * + * @see JUnitTask + * @see XMLJUnitResultFormatter + * @see BriefJUnitResultFormatter + * @see PlainJUnitResultFormatter + * @see FailureRecorder + * @see JUnitResultFormatter + */ +public class FormatterElement { + + private String classname; + private String extension; + private OutputStream out = new KeepAliveOutputStream(System.out); + private File outFile; + private boolean useFile = true; + private Object ifCond; + private Object unlessCond; + + /** + * Store the project reference for passing it to nested components. + * @since Ant 1.8 + */ + private Project project; + + /** xml formatter class */ + public static final String XML_FORMATTER_CLASS_NAME = + "org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter"; + /** brief formatter class */ + public static final String BRIEF_FORMATTER_CLASS_NAME = + "org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"; + /** plain formatter class */ + public static final String PLAIN_FORMATTER_CLASS_NAME = + "org.apache.tools.ant.taskdefs.optional.junit.PlainJUnitResultFormatter"; + /** failure recorder class */ + public static final String FAILURE_RECORDER_CLASS_NAME = + "org.apache.tools.ant.taskdefs.optional.junit.FailureRecorder"; + + /** + * <p> Quick way to use a standard formatter. + * + * <p> At the moment, there are three supported standard formatters. + * <ul> + * <li> The <code>xml</code> type uses a <code>XMLJUnitResultFormatter</code>. + * <li> The <code>brief</code> type uses a <code>BriefJUnitResultFormatter</code>. + * <li> The <code>plain</code> type (the default) uses a <code>PlainJUnitResultFormatter</code>. + * <li> The <code>failure</code> type uses a <code>FailureRecorder</code>. + * </ul> + * + * <p> Sets <code>classname</code> attribute - so you can't use that + * attribute if you use this one. + * @param type the enumerated value to use. + */ + public void setType(TypeAttribute type) { + if ("xml".equals(type.getValue())) { + setClassname(XML_FORMATTER_CLASS_NAME); + } else { + if ("brief".equals(type.getValue())) { + setClassname(BRIEF_FORMATTER_CLASS_NAME); + } else { + if ("failure".equals(type.getValue())) { + setClassname(FAILURE_RECORDER_CLASS_NAME); + } else { // must be plain, ensured by TypeAttribute + setClassname(PLAIN_FORMATTER_CLASS_NAME); + } + } + } + } + + /** + * <p> Set name of class to be used as the formatter. + * + * <p> This class must implement <code>JUnitResultFormatter</code> + * @param classname the name of the formatter class. + */ + public void setClassname(String classname) { + this.classname = classname; + if (XML_FORMATTER_CLASS_NAME.equals(classname)) { + setExtension(".xml"); + } else if (PLAIN_FORMATTER_CLASS_NAME.equals(classname)) { + setExtension(".txt"); + } else if (BRIEF_FORMATTER_CLASS_NAME.equals(classname)) { + setExtension(".txt"); + } + } + + /** + * Get name of class to be used as the formatter. + * @return the name of the class. + */ + public String getClassname() { + return classname; + } + + /** + * Set the extension to use for the report file. + * @param ext the extension to use. + */ + public void setExtension(String ext) { + this.extension = ext; + } + + /** + * Get the extension used for the report file. + * @return the extension. + */ + public String getExtension() { + return extension; + } + + /** + * <p> Set the file which the formatte should log to. + * + * <p> Note that logging to file must be enabled . + */ + void setOutfile(File out) { + this.outFile = out; + } + + /** + * <p> Set output stream for formatter to use. + * + * <p> Defaults to standard out. + * @param out the output stream to use. + */ + public void setOutput(OutputStream out) { + if (out == System.out || out == System.err) { + out = new KeepAliveOutputStream(out); + } + this.out = out; + } + + /** + * Set whether the formatter should log to file. + * @param useFile if true use a file, if false send + * to standard out. + */ + public void setUseFile(boolean useFile) { + this.useFile = useFile; + } + + /** + * Get whether the formatter should log to file. + */ + boolean getUseFile() { + return useFile; + } + + /** + * Set whether this formatter should be used. It will be used if + * the expression evaluates to true or the name of a property + * which has been set, otherwise it won't. + * @param ifCond name of property + * @since Ant 1.8.0 + */ + public void setIf(Object ifCond) { + this.ifCond = ifCond; + } + + /** + * Set whether this formatter should be used. It will be used if + * the expression evaluates to true or the name of a property + * which has been set, otherwise it won't. + * @param ifCond name of property + */ + public void setIf(String ifCond) { + setIf((Object) ifCond); + } + + /** + * Set whether this formatter should NOT be used. It will be used + * if the expression evaluates to false or the name of a property + * which has not been set, orthwise it will not be used. + * @param unlessCond name of property + * @since Ant 1.8.0 + */ + public void setUnless(Object unlessCond) { + this.unlessCond = unlessCond; + } + + /** + * Set whether this formatter should NOT be used. It will be used + * if the expression evaluates to false or the name of a property + * which has not been set, orthwise it will not be used. + * @param unlessCond name of property + */ + public void setUnless(String unlessCond) { + setUnless((Object) unlessCond); + } + + /** + * Ensures that the selector passes the conditions placed + * on it with <code>if</code> and <code>unless</code> properties. + * @param t the task the this formatter is used in. + * @return true if the formatter should be used. + */ + public boolean shouldUse(Task t) { + PropertyHelper ph = PropertyHelper.getPropertyHelper(t.getProject()); + return ph.testIfCondition(ifCond) + && ph.testUnlessCondition(unlessCond); + } + + /** + * @since Ant 1.2 + */ + JUnitTaskMirror.JUnitResultFormatterMirror createFormatter() throws BuildException { + return createFormatter(null); + } + + /** + * Store the project reference for passing it to nested components. + * @param project the reference + * @since Ant 1.8 + */ + public void setProject(Project project) { + this.project = project; + } + + + /** + * @since Ant 1.6 + */ + JUnitTaskMirror.JUnitResultFormatterMirror createFormatter(ClassLoader loader) + throws BuildException { + + if (classname == null) { + throw new BuildException("you must specify type or classname"); + } + //although this code appears to duplicate that of ClasspathUtils.newInstance, + //we cannot use that because this formatter may run in a forked process, + //without that class. + Class f = null; + try { + if (loader == null) { + f = Class.forName(classname); + } else { + f = Class.forName(classname, true, loader); + } + } catch (ClassNotFoundException e) { + throw new BuildException( + "Using loader " + loader + " on class " + classname + + ": " + e, e); + } catch (NoClassDefFoundError e) { + throw new BuildException( + "Using loader " + loader + " on class " + classname + + ": " + e, e); + } + + Object o = null; + try { + o = f.newInstance(); + } catch (InstantiationException e) { + throw new BuildException(e); + } catch (IllegalAccessException e) { + throw new BuildException(e); + } + + if (!(o instanceof JUnitTaskMirror.JUnitResultFormatterMirror)) { + throw new BuildException(classname + " is not a JUnitResultFormatter"); + } + JUnitTaskMirror.JUnitResultFormatterMirror r = + (JUnitTaskMirror.JUnitResultFormatterMirror) o; + if (useFile && outFile != null) { + out = new DelayedFileOutputStream(outFile); + } + r.setOutput(out); + + + boolean needToSetProjectReference = true; + try { + Field field = r.getClass().getField("project"); + Object value = field.get(r); + if (value instanceof Project) { + // there is already a project reference so dont overwrite this + needToSetProjectReference = false; + } + } catch (NoSuchFieldException e) { + // no field present, so no previous reference exists + } catch (IllegalAccessException e) { + throw new BuildException(e); + } + + if (needToSetProjectReference) { + Method setter; + try { + setter = r.getClass().getMethod("setProject", new Class[] {Project.class}); + setter.invoke(r, new Object[] {project}); + } catch (NoSuchMethodException e) { + // no setProject to invoke; just ignore + } catch (IllegalAccessException e) { + throw new BuildException(e); + } catch (InvocationTargetException e) { + throw new BuildException(e); + } + } + + return r; + } + + /** + * <p> Enumerated attribute with the values "plain", "xml", "brief" and "failure". + * + * <p> Use to enumerate options for <code>type</code> attribute. + */ + public static class TypeAttribute extends EnumeratedAttribute { + /** {@inheritDoc}. */ + public String[] getValues() { + return new String[] {"plain", "xml", "brief", "failure"}; + } + } + + /** + * A standard FileOutputStream creates a file as soon as it's opened. This + * class delays the creation of the file until the first time a caller attempts + * to write to it so we don't end up with empty files if the listeners don't use + * them. + */ + private static class DelayedFileOutputStream extends OutputStream { + + private BufferedOutputStream outputStream; + private final File file; + + public DelayedFileOutputStream(File file) { + this.file = file; + } + + @Override + public void write(int b) throws IOException { + synchronized (this) { + if (outputStream == null) { + outputStream = new BufferedOutputStream(new FileOutputStream(file)); + } + } + outputStream.write(b); + } + + @Override + public void flush() throws IOException { + if (outputStream != null) { + outputStream.flush(); + } + } + + @Override + public void close() throws IOException { + if (outputStream != null) { + outputStream.close(); + } + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestListener.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestListener.java new file mode 100644 index 00000000..6741912e --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestListener.java @@ -0,0 +1,52 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import junit.framework.Test; +import junit.framework.TestListener; + +/** + * Provides the functionality for TestListeners to be able to be notified of + * the necessary JUnit4 events for test being ignored (@Ignore annotation) + * or skipped (Assume failures). Tests written in JUnit4 will report against + * the methods in this interface alongside the methods in the existing TestListener + */ +public interface IgnoredTestListener extends TestListener { + + /** + * Reports when a test has been marked with the @Ignore annotation. The parameter + * should normally be typed to JUnit's {@link junit.framework.JUnit4TestCaseFacade} + * so implementing classes should be able to get the details of the ignore by casting + * the argument and retrieving the descriptor from the test. + * @param test the details of the test and failure that have triggered this report. + */ + void testIgnored(Test test); + + /** + * Receive a report that a test has failed an assumption. Within JUnit4 + * this is normally treated as a test being skipped, although how any + * listener handles this is up to that specific listener.<br /> + * <b>Note:</b> Tests that throw assumption failures will still report + * the endTest method, which may differ from how the addError and addFailure + * methods work, it's up for any implementing classes to handle this. + * @param test the details of the test and failure that have triggered this report. + * @param exception the AssumptionViolatedException thrown from the current assumption failure. + */ + void testAssumptionFailure(Test test, Throwable exception); +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestResult.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestResult.java new file mode 100644 index 00000000..c3bb18da --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestResult.java @@ -0,0 +1,99 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.Test; +import junit.framework.TestListener; +import junit.framework.TestResult; + +/** + * Records ignored and skipped tests reported as part of the execution of + * JUnit 4 tests. + * + */ +public class IgnoredTestResult extends TestResult { + + + private List<IgnoredTestListener> listeners = new ArrayList<IgnoredTestListener>(); + private List<TestIgnored> ignored = new ArrayList<TestIgnored>(); + private List<TestIgnored> skipped = new ArrayList<TestIgnored>(); + + public IgnoredTestResult() { + super(); + } + + + public synchronized void addListener(TestListener listener) { + if (listener instanceof IgnoredTestListener) { + listeners.add((IgnoredTestListener)listener); + } + super.addListener(listener); + } + + public synchronized void removeListener(TestListener listener) { + if (listener instanceof IgnoredTestListener) { + listeners.remove(listener); + } + super.removeListener(listener); + } + + /** + * Record a test as having been ignored, normally by the @Ignore annotation. + * @param test the test that was ignored. + * @throws Exception is the listener thrown an exception on handling the notification. + */ + public synchronized void testIgnored(Test test) throws Exception { + ignored.add(new TestIgnored(test)); + for (IgnoredTestListener listener : listeners) { + listener.testIgnored(test); + } + } + + /** + * Report how many tests were ignored. + * @return the number of tests reported as ignored during the current execution. + */ + public long ignoredCount() { + return ignored.size(); + } + + /** + * Records a test as having an assumption failure so JUnit will no longer be executing it. + * Under normal circumstances this would be counted as a skipped test. + * @param test the test to record + * @param cause the details of the test and assumption failure. + */ + public void testAssumptionFailure(Test test, Throwable cause) { + skipped.add(new TestIgnored(test)); + for (IgnoredTestListener listener : listeners) { + listener.testAssumptionFailure(test, cause); + } + } + + /** + * Report how many tests has assumption failures. + * @return the number of tests that reported assumption failures during the current execution. + */ + public long skippedCount() { + return skipped.size(); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnit4TestMethodAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnit4TestMethodAdapter.java new file mode 100644 index 00000000..f03a409b --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnit4TestMethodAdapter.java @@ -0,0 +1,193 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.util.Iterator; +import java.util.List; + +import junit.framework.Test; +import junit.framework.TestResult; + +import org.junit.runner.Description; +import org.junit.runner.Request; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.Filter; + +/** + * Adapter between JUnit 3.8.x API and JUnit 4.x API for execution of tests + * and listening of events (test start, test finish, test failure, test skipped). + * The constructor is passed a JUnit 4 test class and a list of name of methods + * in it that should be executed. Method {@link #run run(TestResult)} executes + * the given JUnit-4-style test methods and notifies the given {@code TestResult} + * object using its old (JUnit 3.8.x style) API. + * + * @author Marian Petras + */ +public class JUnit4TestMethodAdapter implements Test { + + private final Class testClass; + private final String[] methodNames; + private final Runner runner; + private final CustomJUnit4TestAdapterCache cache; + + /** + * Creates a new adapter for the given class and a method within the class. + * + * @param testClass test class containing the method to be executed + * @param methodNames names of the test methods that are to be executed + * @exception java.lang.IllegalArgumentException + * if any of the arguments is {@code null} + * or if any of the given method names is {@code null} or empty + */ + public JUnit4TestMethodAdapter(final Class testClass, + final String[] methodNames) { + if (testClass == null) { + throw new IllegalArgumentException("testClass is <null>"); + } + if (methodNames == null) { + throw new IllegalArgumentException("methodNames is <null>"); + } + for (int i = 0; i < methodNames.length; i++) { + if (methodNames[i] == null) { + throw new IllegalArgumentException("method name #" + i + " is <null>"); + } + if (methodNames[i].length() == 0) { + throw new IllegalArgumentException("method name #" + i + " is empty"); + } + } + this.testClass = testClass; + this.methodNames = methodNames.clone(); + this.cache = CustomJUnit4TestAdapterCache.getInstance(); + + // Warning: If 'testClass' is an old-style (pre-JUnit-4) class, + // then all its test methods will be executed by the returned runner! + Request request; + if (methodNames.length == 1) { + request = Request.method(testClass, methodNames[0]); + } else { + request = Request.aClass(testClass).filterWith( + new MultipleMethodsFilter(testClass, methodNames)); + } + runner = request.getRunner(); + } + + public int countTestCases() { + return runner.testCount(); + } + + public Description getDescription() { + return runner.getDescription(); + } + + public List/*<Test>*/ getTests() { + return cache.asTestList(getDescription()); + } + + public Class getTestClass() { + return testClass; + } + + public void run(final TestResult result) { + runner.run(cache.getNotifier(result)); + } + + @Override + public String toString() { + String testClassName = testClass.getName(); + StringBuilder buf = new StringBuilder(testClassName.length() + + 12 * methodNames.length) + .append(':'); + if (methodNames.length != 0) { + buf.append(methodNames[0]); + for (int i = 1; i < methodNames.length; i++) { + buf.append(',') + .append(methodNames[i]); + } + } + return buf.toString(); + } + + private static final class MultipleMethodsFilter extends Filter { + + private final Description methodsListDescription; + private final Class testClass; + private final String[] methodNames; + + private MultipleMethodsFilter(Class testClass, String[] methodNames) { + if (testClass == null) { + throw new IllegalArgumentException("testClass is <null>"); + } + if (methodNames == null) { + throw new IllegalArgumentException("methodNames is <null>"); + } + methodsListDescription = Description.createSuiteDescription(testClass); + for (int i = 0; i < methodNames.length; i++) { + methodsListDescription.addChild( + Description.createTestDescription(testClass, methodNames[i])); + } + this.testClass = testClass; + this.methodNames = methodNames; + } + + @Override + public boolean shouldRun(Description description) { + if (methodNames.length == 0) { + return false; + } + if (description.isTest()) { + Iterator/*<Description>*/ it = methodsListDescription.getChildren().iterator(); + while (it.hasNext()) { + Description methodDescription = (Description) it.next(); + if (methodDescription.equals(description)) { + return true; + } + } + } else { + Iterator/*<Description>*/ it = description.getChildren().iterator(); + while (it.hasNext()) { + Description each = (Description) it.next(); + if (shouldRun(each)) { + return true; + } + } + } + return false; + } + + @Override + public String describe() { + StringBuilder buf = new StringBuilder(40); + if (methodNames.length == 0) { + buf.append("No methods"); + } else { + buf.append(methodNames.length == 1 ? "Method" : "Methods"); + buf.append(' '); + buf.append(methodNames[0]); + for (int i = 1; i < methodNames.length; i++) { + buf.append(',').append(methodNames[i]); + } + } + buf.append('(').append(testClass.getName()).append(')'); + return buf.toString(); + } + + } + + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java new file mode 100644 index 00000000..2119fc94 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java @@ -0,0 +1,65 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.io.OutputStream; + +import junit.framework.TestListener; + +import org.apache.tools.ant.BuildException; + +/** + * This Interface describes classes that format the results of a JUnit + * testrun. + * + */ +public interface JUnitResultFormatter + extends TestListener, JUnitTaskMirror.JUnitResultFormatterMirror { + /** + * The whole testsuite started. + * @param suite the suite. + * @throws BuildException on error. + */ + void startTestSuite(JUnitTest suite) throws BuildException; + + /** + * The whole testsuite ended. + * @param suite the suite. + * @throws BuildException on error. + */ + void endTestSuite(JUnitTest suite) throws BuildException; + + /** + * Sets the stream the formatter is supposed to write its results to. + * @param out the output stream to use. + */ + void setOutput(OutputStream out); + + /** + * This is what the test has written to System.out + * @param out the string to write. + */ + void setSystemOutput(String out); + + /** + * This is what the test has written to System.err + * @param err the string to write. + */ + void setSystemError(String err); +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java new file mode 100644 index 00000000..459bd3d5 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java @@ -0,0 +1,2283 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.lang.reflect.Constructor; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Vector; + +import org.apache.tools.ant.AntClassLoader; +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.ExecuteWatchdog; +import org.apache.tools.ant.taskdefs.LogOutputStream; +import org.apache.tools.ant.taskdefs.PumpStreamHandler; +import org.apache.tools.ant.types.Assertions; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.CommandlineJava; +import org.apache.tools.ant.types.EnumeratedAttribute; +import org.apache.tools.ant.types.Environment; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.Permissions; +import org.apache.tools.ant.types.PropertySet; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.LoaderUtils; +import org.apache.tools.ant.util.SplitClassLoader; + +/** + * Runs JUnit tests. + * + * <p> JUnit is a framework to create unit tests. It has been initially + * created by Erich Gamma and Kent Beck. JUnit can be found at <a + * href="http://www.junit.org">http://www.junit.org</a>. + * + * <p> <code>JUnitTask</code> can run a single specific + * <code>JUnitTest</code> using the <code>test</code> element.</p> + * For example, the following target <code><pre> + * <target name="test-int-chars" depends="jar-test"> + * <echo message="testing international characters"/> + * <junit printsummary="no" haltonfailure="yes" fork="false"> + * <classpath refid="classpath"/> + * <formatter type="plain" usefile="false" /> + * <test name="org.apache.ecs.InternationalCharTest" /> + * </junit> + * </target> + * </pre></code> + * <p>runs a single junit test + * (<code>org.apache.ecs.InternationalCharTest</code>) in the current + * VM using the path with id <code>classpath</code> as classpath and + * presents the results formatted using the standard + * <code>plain</code> formatter on the command line.</p> + * + * <p> This task can also run batches of tests. The + * <code>batchtest</code> element creates a <code>BatchTest</code> + * based on a fileset. This allows, for example, all classes found in + * directory to be run as testcases.</p> + * + * <p>For example,</p><code><pre> + * <target name="run-tests" depends="dump-info,compile-tests" if="junit.present"> + * <junit printsummary="no" haltonfailure="yes" fork="${junit.fork}"> + * <jvmarg value="-classic"/> + * <classpath refid="tests-classpath"/> + * <sysproperty key="build.tests" value="${build.tests}"/> + * <formatter type="brief" usefile="false" /> + * <batchtest> + * <fileset dir="${tests.dir}"> + * <include name="**/*Test*" /> + * </fileset> + * </batchtest> + * </junit> + * </target> + * </pre></code> + * <p>this target finds any classes with a <code>test</code> directory + * anywhere in their path (under the top <code>${tests.dir}</code>, of + * course) and creates <code>JUnitTest</code>'s for each one.</p> + * + * <p> Of course, <code><junit></code> and + * <code><batch></code> elements can be combined for more + * complex tests. For an example, see the ant <code>build.xml</code> + * target <code>run-tests</code> (the second example is an edited + * version).</p> + * + * <p> To spawn a new Java VM to prevent interferences between + * different testcases, you need to enable <code>fork</code>. A + * number of attributes and elements allow you to set up how this JVM + * runs. + * + * + * @since Ant 1.2 + * + * @see JUnitTest + * @see BatchTest + */ +public class JUnitTask extends Task { + + private static final String LINE_SEP + = System.getProperty("line.separator"); + private static final String CLASSPATH = "CLASSPATH"; + private CommandlineJava commandline; + private final Vector<JUnitTest> tests = new Vector<JUnitTest>(); + private final Vector<BatchTest> batchTests = new Vector<BatchTest>(); + private final Vector<FormatterElement> formatters = new Vector<FormatterElement>(); + private File dir = null; + + private Integer timeout = null; + private boolean summary = false; + private boolean reloading = true; + private String summaryValue = ""; + private JUnitTaskMirror.JUnitTestRunnerMirror runner = null; + + private boolean newEnvironment = false; + private final Environment env = new Environment(); + + private boolean includeAntRuntime = true; + private Path antRuntimeClasses = null; + + // Do we send output to System.out/.err in addition to the formatters? + private boolean showOutput = false; + + // Do we send output to the formatters ? + private boolean outputToFormatters = true; + + private boolean logFailedTests = true; + + private File tmpDir; + private AntClassLoader classLoader = null; + private Permissions perm = null; + private ForkMode forkMode = new ForkMode("perTest"); + + private boolean splitJUnit = false; + private boolean enableTestListenerEvents = false; + private JUnitTaskMirror delegate; + private ClassLoader mirrorLoader; + + /** A boolean on whether to get the forked path for ant classes */ + private boolean forkedPathChecked = false; + + /* set when a test fails/errs with haltonfailure/haltonerror and >1 thread to stop other threads */ + private volatile BuildException caughtBuildException = null; + + // Attributes for basetest + private boolean haltOnError = false; + private boolean haltOnFail = false; + private boolean filterTrace = true; + private boolean fork = false; + private int threads = 1; + private String failureProperty; + private String errorProperty; + + private static final int STRING_BUFFER_SIZE = 128; + /** + * @since Ant 1.7 + */ + public static final String TESTLISTENER_PREFIX = + "junit.framework.TestListener: "; + + /** + * Name of magic property that enables test listener events. + */ + public static final String ENABLE_TESTLISTENER_EVENTS = + "ant.junit.enabletestlistenerevents"; + + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** + * If true, force ant to re-classload all classes for each JUnit TestCase + * + * @param value force class reloading for each test case + */ + public void setReloading(final boolean value) { + reloading = value; + } + + /** + * If true, smartly filter the stack frames of + * JUnit errors and failures before reporting them. + * + * <p>This property is applied on all BatchTest (batchtest) and + * JUnitTest (test) however it can possibly be overridden by their + * own properties.</p> + * @param value <tt>false</tt> if it should not filter, otherwise + * <tt>true<tt> + * + * @since Ant 1.5 + */ + public void setFiltertrace(final boolean value) { + this.filterTrace = value; + } + + /** + * If true, stop the build process when there is an error in a test. + * This property is applied on all BatchTest (batchtest) and JUnitTest + * (test) however it can possibly be overridden by their own + * properties. + * @param value <tt>true</tt> if it should halt, otherwise + * <tt>false</tt> + * + * @since Ant 1.2 + */ + public void setHaltonerror(final boolean value) { + this.haltOnError = value; + } + + /** + * Property to set to "true" if there is a error in a test. + * + * <p>This property is applied on all BatchTest (batchtest) and + * JUnitTest (test), however, it can possibly be overridden by + * their own properties.</p> + * @param propertyName the name of the property to set in the + * event of an error. + * + * @since Ant 1.4 + */ + public void setErrorProperty(final String propertyName) { + this.errorProperty = propertyName; + } + + /** + * If true, stop the build process if a test fails + * (errors are considered failures as well). + * This property is applied on all BatchTest (batchtest) and + * JUnitTest (test) however it can possibly be overridden by their + * own properties. + * @param value <tt>true</tt> if it should halt, otherwise + * <tt>false</tt> + * + * @since Ant 1.2 + */ + public void setHaltonfailure(final boolean value) { + this.haltOnFail = value; + } + + /** + * Property to set to "true" if there is a failure in a test. + * + * <p>This property is applied on all BatchTest (batchtest) and + * JUnitTest (test), however, it can possibly be overridden by + * their own properties.</p> + * @param propertyName the name of the property to set in the + * event of an failure. + * + * @since Ant 1.4 + */ + public void setFailureProperty(final String propertyName) { + this.failureProperty = propertyName; + } + + /** + * If true, JVM should be forked for each test. + * + * <p>It avoids interference between testcases and possibly avoids + * hanging the build. this property is applied on all BatchTest + * (batchtest) and JUnitTest (test) however it can possibly be + * overridden by their own properties.</p> + * @param value <tt>true</tt> if a JVM should be forked, otherwise + * <tt>false</tt> + * @see #setTimeout + * + * @since Ant 1.2 + */ + public void setFork(final boolean value) { + this.fork = value; + } + + /** + * Set the behavior when {@link #setFork fork} fork has been enabled. + * + * <p>Possible values are "once", "perTest" and "perBatch". If + * set to "once", only a single Java VM will be forked for all + * tests, with "perTest" (the default) each test will run in a + * fresh Java VM and "perBatch" will run all tests from the same + * <batchtest> in the same Java VM.</p> + * + * <p>This attribute will be ignored if tests run in the same VM + * as Ant.</p> + * + * <p>Only tests with the same configuration of haltonerror, + * haltonfailure, errorproperty, failureproperty and filtertrace + * can share a forked Java VM, so even if you set the value to + * "once", Ant may need to fork multiple VMs.</p> + * @param mode the mode to use. + * @since Ant 1.6.2 + */ + public void setForkMode(final ForkMode mode) { + this.forkMode = mode; + } + + /** + * Set the number of test threads to be used for parallel test + * execution. The default is 1, which is the same behavior as + * before parallel test execution was possible. + * + * <p>This attribute will be ignored if tests run in the same VM + * as Ant.</p> + * + * @since Ant 1.9.4 + */ + public void setThreads(final int threads) { + if (threads >= 0) { + this.threads = threads; + } + } + + /** + * If true, print one-line statistics for each test, or "withOutAndErr" + * to also show standard output and error. + * + * Can take the values on, off, and withOutAndErr. + * @param value <tt>true</tt> to print a summary, + * <tt>withOutAndErr</tt> to include the test's output as + * well, <tt>false</tt> otherwise. + * @see SummaryJUnitResultFormatter + * + * @since Ant 1.2 + */ + public void setPrintsummary(final SummaryAttribute value) { + summaryValue = value.getValue(); + summary = value.asBoolean(); + } + + /** + * Print summary enumeration values. + */ + public static class SummaryAttribute extends EnumeratedAttribute { + /** + * list the possible values + * @return array of allowed values + */ + @Override + public String[] getValues() { + return new String[] {"true", "yes", "false", "no", + "on", "off", "withOutAndErr"}; + } + + /** + * gives the boolean equivalent of the authorized values + * @return boolean equivalent of the value + */ + public boolean asBoolean() { + final String v = getValue(); + return "true".equals(v) + || "on".equals(v) + || "yes".equals(v) + || "withOutAndErr".equals(v); + } + } + + /** + * Set the timeout value (in milliseconds). + * + * <p>If the test is running for more than this value, the test + * will be canceled. (works only when in 'fork' mode).</p> + * @param value the maximum time (in milliseconds) allowed before + * declaring the test as 'timed-out' + * @see #setFork(boolean) + * + * @since Ant 1.2 + */ + public void setTimeout(final Integer value) { + timeout = value; + } + + /** + * Set the maximum memory to be used by all forked JVMs. + * @param max the value as defined by <tt>-mx</tt> or <tt>-Xmx</tt> + * in the java command line options. + * + * @since Ant 1.2 + */ + public void setMaxmemory(final String max) { + getCommandline().setMaxmemory(max); + } + + /** + * The command used to invoke the Java Virtual Machine, + * default is 'java'. The command is resolved by + * java.lang.Runtime.exec(). Ignored if fork is disabled. + * + * @param value the new VM to use instead of <tt>java</tt> + * @see #setFork(boolean) + * + * @since Ant 1.2 + */ + public void setJvm(final String value) { + getCommandline().setVm(value); + } + + /** + * Adds a JVM argument; ignored if not forking. + * + * @return create a new JVM argument so that any argument can be + * passed to the JVM. + * @see #setFork(boolean) + * + * @since Ant 1.2 + */ + public Commandline.Argument createJvmarg() { + return getCommandline().createVmArgument(); + } + + /** + * The directory to invoke the VM in. Ignored if no JVM is forked. + * @param dir the directory to invoke the JVM from. + * @see #setFork(boolean) + * + * @since Ant 1.2 + */ + public void setDir(final File dir) { + this.dir = dir; + } + + /** + * Adds a system property that tests can access. + * This might be useful to transfer Ant properties to the + * testcases when JVM forking is not enabled. + * + * @since Ant 1.3 + * @deprecated since ant 1.6 + * @param sysp environment variable to add + */ + @Deprecated + public void addSysproperty(final Environment.Variable sysp) { + + getCommandline().addSysproperty(sysp); + } + + /** + * Adds a system property that tests can access. + * This might be useful to transfer Ant properties to the + * testcases when JVM forking is not enabled. + * @param sysp new environment variable to add + * @since Ant 1.6 + */ + public void addConfiguredSysproperty(final Environment.Variable sysp) { + // get a build exception if there is a missing key or value + // see bugzilla report 21684 + final String testString = sysp.getContent(); + getProject().log("sysproperty added : " + testString, Project.MSG_DEBUG); + getCommandline().addSysproperty(sysp); + } + + /** + * Adds a set of properties that will be used as system properties + * that tests can access. + * + * This might be useful to transfer Ant properties to the + * testcases when JVM forking is not enabled. + * + * @param sysp set of properties to be added + * @since Ant 1.6 + */ + public void addSyspropertyset(final PropertySet sysp) { + getCommandline().addSyspropertyset(sysp); + } + + /** + * Adds path to classpath used for tests. + * + * @return reference to the classpath in the embedded java command line + * @since Ant 1.2 + */ + public Path createClasspath() { + return getCommandline().createClasspath(getProject()).createPath(); + } + + /** + * Adds a path to the bootclasspath. + * @return reference to the bootclasspath in the embedded java command line + * @since Ant 1.6 + */ + public Path createBootclasspath() { + return getCommandline().createBootclasspath(getProject()).createPath(); + } + + /** + * Adds an environment variable; used when forking. + * + * <p>Will be ignored if we are not forking a new VM.</p> + * @param var environment variable to be added + * @since Ant 1.5 + */ + public void addEnv(final Environment.Variable var) { + env.addVariable(var); + } + + /** + * If true, use a new environment when forked. + * + * <p>Will be ignored if we are not forking a new VM.</p> + * + * @param newenv boolean indicating if setting a new environment is wished + * @since Ant 1.5 + */ + public void setNewenvironment(final boolean newenv) { + newEnvironment = newenv; + } + + /** + * Preset the attributes of the test + * before configuration in the build + * script. + * This allows attributes in the <junit> task + * be be defaults for the tests, but allows + * individual tests to override the defaults. + */ + private void preConfigure(final BaseTest test) { + test.setFiltertrace(filterTrace); + test.setHaltonerror(haltOnError); + if (errorProperty != null) { + test.setErrorProperty(errorProperty); + } + test.setHaltonfailure(haltOnFail); + if (failureProperty != null) { + test.setFailureProperty(failureProperty); + } + test.setFork(fork); + } + + /** + * Add a new single testcase. + * @param test a new single testcase + * @see JUnitTest + * + * @since Ant 1.2 + */ + public void addTest(final JUnitTest test) { + tests.addElement(test); + preConfigure(test); + } + + /** + * Adds a set of tests based on pattern matching. + * + * @return a new instance of a batch test. + * @see BatchTest + * + * @since Ant 1.2 + */ + public BatchTest createBatchTest() { + final BatchTest test = new BatchTest(getProject()); + batchTests.addElement(test); + preConfigure(test); + return test; + } + + /** + * Add a new formatter to all tests of this task. + * + * @param fe formatter element + * @since Ant 1.2 + */ + public void addFormatter(final FormatterElement fe) { + formatters.addElement(fe); + } + + /** + * If true, include ant.jar, optional.jar and junit.jar in the forked VM. + * + * @param b include ant run time yes or no + * @since Ant 1.5 + */ + public void setIncludeantruntime(final boolean b) { + includeAntRuntime = b; + } + + /** + * If true, send any output generated by tests to Ant's logging system + * as well as to the formatters. + * By default only the formatters receive the output. + * + * <p>Output will always be passed to the formatters and not by + * shown by default. This option should for example be set for + * tests that are interactive and prompt the user to do + * something.</p> + * + * @param showOutput if true, send output to Ant's logging system too + * @since Ant 1.5 + */ + public void setShowOutput(final boolean showOutput) { + this.showOutput = showOutput; + } + + /** + * If true, send any output generated by tests to the formatters. + * + * @param outputToFormatters if true, send output to formatters (Default + * is true). + * @since Ant 1.7.0 + */ + public void setOutputToFormatters(final boolean outputToFormatters) { + this.outputToFormatters = outputToFormatters; + } + + /** + * If true, write a single "FAILED" line for failed tests to Ant's + * log system. + * + * @since Ant 1.8.0 + */ + public void setLogFailedTests(final boolean logFailedTests) { + this.logFailedTests = logFailedTests; + } + + /** + * Assertions to enable in this program (if fork=true) + * @since Ant 1.6 + * @param asserts assertion set + */ + public void addAssertions(final Assertions asserts) { + if (getCommandline().getAssertions() != null) { + throw new BuildException("Only one assertion declaration is allowed"); + } + getCommandline().setAssertions(asserts); + } + + /** + * Sets the permissions for the application run inside the same JVM. + * @since Ant 1.6 + * @return . + */ + public Permissions createPermissions() { + if (perm == null) { + perm = new Permissions(); + } + return perm; + } + + /** + * If set, system properties will be copied to the cloned VM - as + * well as the bootclasspath unless you have explicitly specified + * a bootclasspath. + * + * <p>Doesn't have any effect unless fork is true.</p> + * @param cloneVm a <code>boolean</code> value. + * @since Ant 1.7 + */ + public void setCloneVm(final boolean cloneVm) { + getCommandline().setCloneVm(cloneVm); + } + + /** + * Creates a new JUnitRunner and enables fork of a new Java VM. + * + * @throws Exception under ??? circumstances + * @since Ant 1.2 + */ + public JUnitTask() throws Exception { + } + + /** + * Where Ant should place temporary files. + * + * @param tmpDir location where temporary files should go to + * @since Ant 1.6 + */ + public void setTempdir(final File tmpDir) { + if (tmpDir != null) { + if (!tmpDir.exists() || !tmpDir.isDirectory()) { + throw new BuildException(tmpDir.toString() + + " is not a valid temp directory"); + } + } + this.tmpDir = tmpDir; + } + + /** + * Whether test listener events shall be generated. + * + * <p>Defaults to false.</p> + * + * <p>This value will be overridden by the magic property + * ant.junit.enabletestlistenerevents if it has been set.</p> + * + * @since Ant 1.8.2 + */ + public void setEnableTestListenerEvents(final boolean b) { + enableTestListenerEvents = b; + } + + /** + * Whether test listener events shall be generated. + * @since Ant 1.8.2 + */ + public boolean getEnableTestListenerEvents() { + final String e = getProject().getProperty(ENABLE_TESTLISTENER_EVENTS); + if (e != null) { + return Project.toBoolean(e); + } + return enableTestListenerEvents; + } + + /** + * Adds the jars or directories containing Ant, this task and + * JUnit to the classpath - this should make the forked JVM work + * without having to specify them directly. + * + * @since Ant 1.4 + */ + @Override + public void init() { + antRuntimeClasses = new Path(getProject()); + splitJUnit = !addClasspathResource("/junit/framework/TestCase.class"); + addClasspathEntry("/org/apache/tools/ant/launch/AntMain.class"); + addClasspathEntry("/org/apache/tools/ant/Task.class"); + addClasspathEntry("/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.class"); + addClasspathEntry("/org/apache/tools/ant/taskdefs/optional/junit/JUnit4TestMethodAdapter.class"); + } + + private static JUnitTaskMirror createMirror(final JUnitTask task, final ClassLoader loader) { + try { + loader.loadClass("junit.framework.Test"); // sanity check + } catch (final ClassNotFoundException e) { + throw new BuildException( + "The <classpath> for <junit> must include junit.jar " + + "if not in Ant's own classpath", + e, task.getLocation()); + } + try { + final Class c = loader.loadClass(JUnitTaskMirror.class.getName() + "Impl"); + if (c.getClassLoader() != loader) { + throw new BuildException("Overdelegating loader", task.getLocation()); + } + final Constructor cons = c.getConstructor(new Class[] {JUnitTask.class}); + return (JUnitTaskMirror) cons.newInstance(new Object[] {task}); + } catch (final Exception e) { + throw new BuildException(e, task.getLocation()); + } + } + + /** + * Sets up the delegate that will actually run the tests. + * + * <p>Will be invoked implicitly once the delegate is needed.</p> + * + * @since Ant 1.7.1 + */ + protected void setupJUnitDelegate() { + final ClassLoader myLoader = JUnitTask.class.getClassLoader(); + if (splitJUnit) { + final Path path = new Path(getProject()); + path.add(antRuntimeClasses); + final Path extra = getCommandline().getClasspath(); + if (extra != null) { + path.add(extra); + } + mirrorLoader = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + return new SplitClassLoader(myLoader, path, getProject(), + new String[] { + "BriefJUnitResultFormatter", + "JUnit4TestMethodAdapter", + "JUnitResultFormatter", + "JUnitTaskMirrorImpl", + "JUnitTestRunner", + "JUnitVersionHelper", + "OutErrSummaryJUnitResultFormatter", + "PlainJUnitResultFormatter", + "SummaryJUnitResultFormatter", + "TearDownOnVmCrash", + "XMLJUnitResultFormatter", + "IgnoredTestListener", + "IgnoredTestResult", + "CustomJUnit4TestAdapterCache", + "TestListenerWrapper" + }); + } + }); + } else { + mirrorLoader = myLoader; + } + delegate = createMirror(this, mirrorLoader); + } + + /** + * Runs the testcase. + * + * @throws BuildException in case of test failures or errors + * @since Ant 1.2 + */ + @Override + public void execute() throws BuildException { + checkMethodLists(); + + setupJUnitDelegate(); + + final List<List> testLists = new ArrayList<List>(); + /* parallel test execution is only supported for multi-process execution */ + final int threads = ((!fork) || (forkMode.getValue().equals(ForkMode.ONCE)) + ? 1 + : this.threads); + + final boolean forkPerTest = forkMode.getValue().equals(ForkMode.PER_TEST); + if (forkPerTest || forkMode.getValue().equals(ForkMode.ONCE)) { + testLists.addAll(executeOrQueue(getIndividualTests(), + forkPerTest)); + } else { /* forkMode.getValue().equals(ForkMode.PER_BATCH) */ + final int count = batchTests.size(); + for (int i = 0; i < count; i++) { + final BatchTest batchtest = batchTests.elementAt(i); + testLists.addAll(executeOrQueue(batchtest.elements(), false)); + } + testLists.addAll(executeOrQueue(tests.elements(), forkPerTest)); + } + + try { + /* prior to parallel the code in 'oneJunitThread' used to be here. */ + runTestsInThreads(testLists, threads); + } finally { + cleanup(); + } + } + + /* + * When the list of tests is established, an array of threads is created to pick the + * tests off the list one at a time and execute them until the list is empty. Tests are + * not assigned to threads until the thread is available. + * + * This class is the runnable thread subroutine that takes care of passing the shared + * list iterator and the handle back to the main class to the test execution subroutine + * code 'runTestsInThreads'. One object is created for each thread and each one gets + * a unique thread id that can be useful for tracing test starts and stops. + * + * Because the threads are picking tests off the same list, it is the list *iterator* + * that must be shared, not the list itself - and the iterator must have a thread-safe + * ability to pop the list - hence the synchronized 'getNextTest'. + */ + private class JunitTestThread implements Runnable { + + JunitTestThread(final JUnitTask master, final Iterator<List> iterator, final int id) { + this.masterTask = master; + this.iterator = iterator; + this.id = id; + } + + public void run() { + try { + masterTask.oneJunitThread(iterator, id); + } catch (final BuildException b) { + /* saved to rethrow in main thread to be like single-threaded case */ + caughtBuildException = b; + } + } + + private final JUnitTask masterTask; + private final Iterator<List> iterator; + private final int id; + } + + /* + * Because the threads are picking tests off the same list, it is the list *iterator* + * that must be shared, not the list itself - and the iterator must have a thread-safe + * ability to pop the list - hence the synchronized 'getNextTest'. We can't have two + * threads get the same test, or two threads simultaneously pop the list so that a test + * gets skipped! + */ + private List getNextTest(final Iterator<List> iter) { + synchronized(iter) { + if (iter.hasNext()) { + return iter.next(); + } + return null; + } + } + + /* + * This code loops keeps executing the next test or test bunch (depending on fork mode) + * on the list of test cases until none are left. Basically this body of code used to + * be in the execute routine above; now, several copies (one for each test thread) execute + * simultaneously. The while loop was modified to call the new thread-safe atomic list + * popping subroutine and the logging messages were added. + * + * If one thread aborts due to a BuildException (haltOnError, haltOnFailure, or any other + * fatal reason, no new tests/batches will be started but the running threads will be + * permitted to complete. Additional tests may start in already-running batch-test threads. + */ + private void oneJunitThread(final Iterator<List> iter, final int threadId) { + + List l; + log("Starting test thread " + threadId, Project.MSG_VERBOSE); + while ((caughtBuildException == null) && ((l = getNextTest(iter)) != null)) { + log("Running test " + l.get(0).toString() + "(" + l.size() + ") in thread " + threadId, Project.MSG_VERBOSE); + if (l.size() == 1) { + execute((JUnitTest) l.get(0), threadId); + } else { + execute(l, threadId); + } + } + log("Ending test thread " + threadId, Project.MSG_VERBOSE); + } + + + private void runTestsInThreads(final List<List> testList, final int numThreads) { + + Iterator<List> iter = testList.iterator(); + + if (numThreads == 1) { + /* with just one thread just run the test - don't create any threads */ + oneJunitThread(iter, 0); + } else { + final Thread[] threads = new Thread[numThreads]; + int i; + boolean exceptionOccurred; + + /* Need to split apart tests, which are still grouped in batches */ + /* is there a simpler Java mechanism to do this? */ + /* I assume we don't want to do this with "per batch" forking. */ + List<List> newlist = new ArrayList<List>(); + if (forkMode.getValue().equals(ForkMode.PER_TEST)) { + final Iterator<List> i1 = testList.iterator(); + while (i1.hasNext()) { + final List l = i1.next(); + if (l.size() == 1) { + newlist.add(l); + } else { + final Iterator i2 = l.iterator(); + while (i2.hasNext()) { + final List tmpSingleton = new ArrayList(); + tmpSingleton.add(i2.next()); + newlist.add(tmpSingleton); + } + } + } + } else { + newlist = testList; + } + iter = newlist.iterator(); + + /* create 1 thread using the passthrough class, and let each thread start */ + for (i = 0; i < numThreads; i++) { + threads[i] = new Thread(new JunitTestThread(this, iter, i+1)); + threads[i].start(); + } + + /* wait for all of the threads to complete. Not sure if the exception can actually occur in this use case. */ + do { + exceptionOccurred = false; + + try { + for (i = 0; i < numThreads; i++) { + threads[i].join(); + } + } catch (final InterruptedException e) { + exceptionOccurred = true; + } + } while (exceptionOccurred); + + /* an exception occurred in one of the threads - usually a haltOnError/Failure. + throw the exception again so it behaves like the single-thread case */ + if (caughtBuildException != null) { + throw new BuildException(caughtBuildException); + } + + /* all threads are completed - that's all there is to do. */ + /* control will flow back to the test cleanup call and then execute is done. */ + } + } + + /** + * Run the tests. + * @param arg one JUnitTest + * @param thread Identifies which thread is test running in (0 for single-threaded runs) + * @throws BuildException in case of test failures or errors + */ + protected void execute(final JUnitTest arg, final int thread) throws BuildException { + validateTestName(arg.getName()); + + final JUnitTest test = (JUnitTest) arg.clone(); + test.setThread(thread); + + // set the default values if not specified + //@todo should be moved to the test class instead. + if (test.getTodir() == null) { + test.setTodir(getProject().resolveFile(".")); + } + + if (test.getOutfile() == null) { + test.setOutfile("TEST-" + test.getName()); + } + + // execute the test and get the return code + TestResultHolder result = null; + if (!test.getFork()) { + result = executeInVM(test); + } else { + final ExecuteWatchdog watchdog = createWatchdog(); + result = executeAsForked(test, watchdog, null); + // null watchdog means no timeout, you'd better not check with null + } + actOnTestResult(result, test, "Test " + test.getName()); + } + + /** + * Run the tests. + * @param arg one JUnitTest + * @throws BuildException in case of test failures or errors + */ + protected void execute(final JUnitTest arg) throws BuildException { + execute(arg, 0); + } + + /** + * Throws a <code>BuildException</code> if the given test name is invalid. + * Validity is defined as not <code>null</code>, not empty, and not the + * string "null". + * @param testName the test name to be validated + * @throws BuildException if <code>testName</code> is not a valid test name + */ + private void validateTestName(final String testName) throws BuildException { + if (testName == null || testName.length() == 0 + || testName.equals("null")) { + throw new BuildException("test name must be specified"); + } + } + + /** + * Execute a list of tests in a single forked Java VM. + * @param testList the list of tests to execute. + * @param thread Identifies which thread is test running in (0 for single-threaded runs) + * @throws BuildException on error. + */ + protected void execute(final List testList, final int thread) throws BuildException { + JUnitTest test = null; + // Create a temporary file to pass the test cases to run to + // the runner (one test case per line) + final File casesFile = createTempPropertiesFile("junittestcases"); + BufferedWriter writer = null; + try { + writer = new BufferedWriter(new FileWriter(casesFile)); + + log("Creating casesfile '" + casesFile.getAbsolutePath() + + "' with content: ", Project.MSG_VERBOSE); + final PrintStream logWriter = + new PrintStream(new LogOutputStream(this, Project.MSG_VERBOSE)); + + final Iterator iter = testList.iterator(); + while (iter.hasNext()) { + test = (JUnitTest) iter.next(); + test.setThread(thread); + printDual(writer, logWriter, test.getName()); + if (test.getMethods() != null) { + printDual(writer, logWriter, ":" + test.getMethodsString().replace(',', '+')); + } + if (test.getTodir() == null) { + printDual(writer, logWriter, + "," + getProject().resolveFile(".")); + } else { + printDual(writer, logWriter, "," + test.getTodir()); + } + + if (test.getOutfile() == null) { + printlnDual(writer, logWriter, + "," + "TEST-" + test.getName()); + } else { + printlnDual(writer, logWriter, "," + test.getOutfile()); + } + } + writer.flush(); + writer.close(); + writer = null; + + // execute the test and get the return code + final ExecuteWatchdog watchdog = createWatchdog(); + final TestResultHolder result = + executeAsForked(test, watchdog, casesFile); + actOnTestResult(result, test, "Tests"); + } catch (final IOException e) { + log(e.toString(), Project.MSG_ERR); + throw new BuildException(e); + } finally { + FileUtils.close(writer); + + try { + FILE_UTILS.tryHardToDelete(casesFile); + } catch (final Exception e) { + log(e.toString(), Project.MSG_ERR); + } + } + } + + /** + * Execute a list of tests in a single forked Java VM. + * @param testList the list of tests to execute. + * @throws BuildException on error. + */ + protected void execute(final List testList) throws BuildException { + execute(testList, 0); + } + + /** + * Execute a testcase by forking a new JVM. The command will block + * until it finishes. To know if the process was destroyed or not + * or whether the forked Java VM exited abnormally, use the + * attributes of the returned holder object. + * @param test the testcase to execute. + * @param watchdog the watchdog in charge of cancelling the test if it + * exceeds a certain amount of time. Can be <tt>null</tt>, in this case + * the test could probably hang forever. + * @param casesFile list of test cases to execute. Can be <tt>null</tt>, + * in this case only one test is executed. + * @return the test results from the JVM itself. + * @throws BuildException in case of error creating a temporary property file, + * or if the junit process can not be forked + */ + private TestResultHolder executeAsForked(JUnitTest test, + final ExecuteWatchdog watchdog, + final File casesFile) + throws BuildException { + + if (perm != null) { + log("Permissions ignored when running in forked mode!", + Project.MSG_WARN); + } + + CommandlineJava cmd; + try { + cmd = (CommandlineJava) (getCommandline().clone()); + } catch (final CloneNotSupportedException e) { + throw new BuildException("This shouldn't happen", e, getLocation()); + } + if (casesFile == null) { + cmd.createArgument().setValue(test.getName()); + if (test.getMethods() != null) { + cmd.createArgument().setValue(Constants.METHOD_NAMES + test.getMethodsString()); + } + } else { + log("Running multiple tests in the same VM", Project.MSG_VERBOSE); + cmd.createArgument().setValue(Constants.TESTSFILE + casesFile); + } + + cmd.createArgument().setValue(Constants.SKIP_NON_TESTS + String.valueOf(test.isSkipNonTests())); + cmd.createArgument().setValue(Constants.FILTERTRACE + test.getFiltertrace()); + cmd.createArgument().setValue(Constants.HALT_ON_ERROR + test.getHaltonerror()); + cmd.createArgument().setValue(Constants.HALT_ON_FAILURE + + test.getHaltonfailure()); + checkIncludeAntRuntime(cmd); + + checkIncludeSummary(cmd); + + cmd.createArgument().setValue(Constants.SHOWOUTPUT + + String.valueOf(showOutput)); + cmd.createArgument().setValue(Constants.OUTPUT_TO_FORMATTERS + + String.valueOf(outputToFormatters)); + cmd.createArgument().setValue(Constants.LOG_FAILED_TESTS + + String.valueOf(logFailedTests)); + cmd.createArgument().setValue(Constants.THREADID + + String.valueOf(test.getThread())); + + // #31885 + cmd.createArgument().setValue(Constants.LOGTESTLISTENEREVENTS + + String.valueOf(getEnableTestListenerEvents())); + + StringBuffer formatterArg = new StringBuffer(STRING_BUFFER_SIZE); + final FormatterElement[] feArray = mergeFormatters(test); + for (int i = 0; i < feArray.length; i++) { + final FormatterElement fe = feArray[i]; + if (fe.shouldUse(this)) { + formatterArg.append(Constants.FORMATTER); + formatterArg.append(fe.getClassname()); + final File outFile = getOutput(fe, test); + if (outFile != null) { + formatterArg.append(","); + formatterArg.append(outFile); + } + cmd.createArgument().setValue(formatterArg.toString()); + formatterArg = new StringBuffer(); + } + } + + final File vmWatcher = createTempPropertiesFile("junitvmwatcher"); + cmd.createArgument().setValue(Constants.CRASHFILE + + vmWatcher.getAbsolutePath()); + final File propsFile = createTempPropertiesFile("junit"); + cmd.createArgument().setValue(Constants.PROPSFILE + + propsFile.getAbsolutePath()); + final Hashtable p = getProject().getProperties(); + final Properties props = new Properties(); + for (final Enumeration e = p.keys(); e.hasMoreElements();) { + final Object key = e.nextElement(); + props.put(key, p.get(key)); + } + try { + final FileOutputStream outstream = new FileOutputStream(propsFile); + props.store(outstream, "Ant JUnitTask generated properties file"); + outstream.close(); + } catch (final java.io.IOException e) { + FILE_UTILS.tryHardToDelete(propsFile); + throw new BuildException("Error creating temporary properties " + + "file.", e, getLocation()); + } + + final Execute execute = new Execute( + new JUnitLogStreamHandler( + this, + Project.MSG_INFO, + Project.MSG_WARN), + watchdog); + execute.setCommandline(cmd.getCommandline()); + execute.setAntRun(getProject()); + if (dir != null) { + execute.setWorkingDirectory(dir); + } + + final String[] environment = env.getVariables(); + if (environment != null) { + for (int i = 0; i < environment.length; i++) { + log("Setting environment variable: " + environment[i], + Project.MSG_VERBOSE); + } + } + execute.setNewenvironment(newEnvironment); + execute.setEnvironment(environment); + + log(cmd.describeCommand(), Project.MSG_VERBOSE); + + checkForkedPath(cmd); + + final TestResultHolder result = new TestResultHolder(); + try { + result.exitCode = execute.execute(); + } catch (final IOException e) { + throw new BuildException("Process fork failed.", e, getLocation()); + } finally { + String vmCrashString = "unknown"; + BufferedReader br = null; + try { + if (vmWatcher.exists()) { + br = new BufferedReader(new FileReader(vmWatcher)); + vmCrashString = br.readLine(); + } else { + vmCrashString = "Monitor file (" + + vmWatcher.getAbsolutePath() + + ") missing, location not writable," + + " testcase not started or mixing ant versions?"; + } + } catch (final Exception e) { + e.printStackTrace(); + // ignored. + } finally { + FileUtils.close(br); + if (vmWatcher.exists()) { + FILE_UTILS.tryHardToDelete(vmWatcher); + } + } + + final boolean crash = (watchdog != null && watchdog.killedProcess()) + || !Constants.TERMINATED_SUCCESSFULLY.equals(vmCrashString); + + if (casesFile != null && crash) { + test = createDummyTestForBatchTest(test); + } + + if (watchdog != null && watchdog.killedProcess()) { + result.timedOut = true; + logTimeout(feArray, test, vmCrashString); + } else if (crash) { + result.crashed = true; + logVmCrash(feArray, test, vmCrashString); + } + + if (!FILE_UTILS.tryHardToDelete(propsFile)) { + throw new BuildException("Could not delete temporary " + + "properties file '" + + propsFile.getAbsolutePath() + "'."); + } + } + + return result; + } + + /** + * Adding ant runtime. + * @param cmd command to run + */ + private void checkIncludeAntRuntime(final CommandlineJava cmd) { + if (includeAntRuntime) { + final Map/*<String, String>*/ env = Execute.getEnvironmentVariables(); + final String cp = (String) env.get(CLASSPATH); + if (cp != null) { + cmd.createClasspath(getProject()).createPath() + .append(new Path(getProject(), cp)); + } + log("Implicitly adding " + antRuntimeClasses + " to CLASSPATH", + Project.MSG_VERBOSE); + cmd.createClasspath(getProject()).createPath() + .append(antRuntimeClasses); + } + } + + + /** + * check for the parameter being "withoutanderr" in a locale-independent way. + * @param summaryOption the summary option -can be null + * @return true if the run should be withoutput and error + */ + private boolean equalsWithOutAndErr(final String summaryOption) { + return "withoutanderr".equalsIgnoreCase(summaryOption); + } + + private void checkIncludeSummary(final CommandlineJava cmd) { + if (summary) { + String prefix = ""; + if (equalsWithOutAndErr(summaryValue)) { + prefix = "OutErr"; + } + cmd.createArgument() + .setValue(Constants.FORMATTER + + "org.apache.tools.ant.taskdefs.optional.junit." + + prefix + "SummaryJUnitResultFormatter"); + } + } + + /** + * Check the path for multiple different versions of + * ant. + * @param cmd command to execute + */ + private void checkForkedPath(final CommandlineJava cmd) { + if (forkedPathChecked) { + return; + } + forkedPathChecked = true; + if (!cmd.haveClasspath()) { + return; + } + AntClassLoader loader = null; + try { + loader = + AntClassLoader.newAntClassLoader(null, getProject(), + cmd.createClasspath(getProject()), + true); + final String projectResourceName = + LoaderUtils.classNameToResource(Project.class.getName()); + URL previous = null; + try { + for (final Enumeration e = loader.getResources(projectResourceName); + e.hasMoreElements();) { + final URL current = (URL) e.nextElement(); + if (previous != null && !urlEquals(current, previous)) { + log("WARNING: multiple versions of ant detected " + + "in path for junit " + + LINE_SEP + " " + previous + + LINE_SEP + " and " + current, + Project.MSG_WARN); + return; + } + previous = current; + } + } catch (final Exception ex) { + // Ignore exception + } + } finally { + if (loader != null) { + loader.cleanup(); + } + } + } + + /** + * Compares URLs for equality but takes case-sensitivity into + * account when comparing file URLs and ignores the jar specific + * part of the URL if present. + */ + private static boolean urlEquals(final URL u1, final URL u2) { + final String url1 = maybeStripJarAndClass(u1); + final String url2 = maybeStripJarAndClass(u2); + if (url1.startsWith("file:") && url2.startsWith("file:")) { + return new File(FILE_UTILS.fromURI(url1)) + .equals(new File(FILE_UTILS.fromURI(url2))); + } + return url1.equals(url2); + } + + private static String maybeStripJarAndClass(final URL u) { + String s = u.toString(); + if (s.startsWith("jar:")) { + final int pling = s.indexOf('!'); + s = s.substring(4, pling == -1 ? s.length() : pling); + } + return s; + } + + /** + * Create a temporary file to pass the properties to a new process. + * Will auto-delete on (graceful) exit. + * The file will be in the project basedir unless tmpDir declares + * something else. + * @param prefix + * @return created file + */ + private File createTempPropertiesFile(final String prefix) { + final File propsFile = + FILE_UTILS.createTempFile(prefix, ".properties", + tmpDir != null ? tmpDir : getProject().getBaseDir(), true, true); + return propsFile; + } + + + /** + * Pass output sent to System.out to the TestRunner so it can + * collect it for the formatters. + * + * @param output output coming from System.out + * @since Ant 1.5 + */ + @Override + protected void handleOutput(final String output) { + if (output.startsWith(TESTLISTENER_PREFIX)) { + log(output, Project.MSG_VERBOSE); + } else if (runner != null) { + if (outputToFormatters) { + runner.handleOutput(output); + } + if (showOutput) { + super.handleOutput(output); + } + } else { + super.handleOutput(output); + } + } + + /** + * Handle an input request by this task. + * @see Task#handleInput(byte[], int, int) + * This implementation delegates to a runner if it + * present. + * @param buffer the buffer into which data is to be read. + * @param offset the offset into the buffer at which data is stored. + * @param length the amount of data to read. + * + * @return the number of bytes read. + * @exception IOException if the data cannot be read. + * + * @since Ant 1.6 + */ + @Override + protected int handleInput(final byte[] buffer, final int offset, final int length) + throws IOException { + if (runner != null) { + return runner.handleInput(buffer, offset, length); + } else { + return super.handleInput(buffer, offset, length); + } + } + + + /** + * Pass output sent to System.out to the TestRunner so it can + * collect ot for the formatters. + * + * @param output output coming from System.out + * @since Ant 1.5.2 + */ + @Override + protected void handleFlush(final String output) { + if (runner != null) { + runner.handleFlush(output); + if (showOutput) { + super.handleFlush(output); + } + } else { + super.handleFlush(output); + } + } + + /** + * Pass output sent to System.err to the TestRunner so it can + * collect it for the formatters. + * + * @param output output coming from System.err + * @since Ant 1.5 + */ + @Override + public void handleErrorOutput(final String output) { + if (runner != null) { + runner.handleErrorOutput(output); + if (showOutput) { + super.handleErrorOutput(output); + } + } else { + super.handleErrorOutput(output); + } + } + + + /** + * Pass output sent to System.err to the TestRunner so it can + * collect it for the formatters. + * + * @param output coming from System.err + * @since Ant 1.5.2 + */ + @Override + public void handleErrorFlush(final String output) { + if (runner != null) { + runner.handleErrorFlush(output); + if (showOutput) { + super.handleErrorFlush(output); + } + } else { + super.handleErrorFlush(output); + } + } + + // in VM is not very nice since it could probably hang the + // whole build. IMHO this method should be avoided and it would be best + // to remove it in future versions. TBD. (SBa) + + /** + * Execute inside VM. + * @param arg one JUnitTest + * @throws BuildException under unspecified circumstances + * @return the results + */ + private TestResultHolder executeInVM(final JUnitTest arg) throws BuildException { + if (delegate == null) { + setupJUnitDelegate(); + } + + final JUnitTest test = (JUnitTest) arg.clone(); + test.setProperties(getProject().getProperties()); + if (dir != null) { + log("dir attribute ignored if running in the same VM", + Project.MSG_WARN); + } + + if (newEnvironment || null != env.getVariables()) { + log("Changes to environment variables are ignored if running in " + + "the same VM.", Project.MSG_WARN); + } + + if (getCommandline().getBootclasspath() != null) { + log("bootclasspath is ignored if running in the same VM.", + Project.MSG_WARN); + } + + final CommandlineJava.SysProperties sysProperties = + getCommandline().getSystemProperties(); + if (sysProperties != null) { + sysProperties.setSystem(); + } + + try { + log("Using System properties " + System.getProperties(), + Project.MSG_VERBOSE); + if (splitJUnit) { + classLoader = (AntClassLoader) delegate.getClass().getClassLoader(); + } else { + createClassLoader(); + } + if (classLoader != null) { + classLoader.setThreadContextLoader(); + } + runner = delegate.newJUnitTestRunner(test, test.getMethods(), test.getHaltonerror(), + test.getFiltertrace(), + test.getHaltonfailure(), false, + getEnableTestListenerEvents(), + classLoader); + if (summary) { + + final JUnitTaskMirror.SummaryJUnitResultFormatterMirror f = + delegate.newSummaryJUnitResultFormatter(); + f.setWithOutAndErr(equalsWithOutAndErr(summaryValue)); + f.setOutput(getDefaultOutput()); + runner.addFormatter(f); + } + + runner.setPermissions(perm); + + final FormatterElement[] feArray = mergeFormatters(test); + for (int i = 0; i < feArray.length; i++) { + final FormatterElement fe = feArray[i]; + if (fe.shouldUse(this)) { + final File outFile = getOutput(fe, test); + if (outFile != null) { + fe.setOutfile(outFile); + } else { + fe.setOutput(getDefaultOutput()); + } + runner.addFormatter(fe.createFormatter(classLoader)); + } + } + + runner.run(); + final TestResultHolder result = new TestResultHolder(); + result.exitCode = runner.getRetCode(); + return result; + } finally { + if (sysProperties != null) { + sysProperties.restoreSystem(); + } + if (classLoader != null) { + classLoader.resetThreadContextLoader(); + } + } + } + + /** + * @return <tt>null</tt> if there is a timeout value, otherwise the + * watchdog instance. + * + * @throws BuildException under unspecified circumstances + * @since Ant 1.2 + */ + protected ExecuteWatchdog createWatchdog() throws BuildException { + if (timeout == null) { + return null; + } + return new ExecuteWatchdog((long) timeout.intValue()); + } + + /** + * Get the default output for a formatter. + * + * @return default output stream for a formatter + * @since Ant 1.3 + */ + protected OutputStream getDefaultOutput() { + return new LogOutputStream(this, Project.MSG_INFO); + } + + /** + * Merge all individual tests from the batchtest with all individual tests + * and return an enumeration over all <tt>JUnitTest</tt>. + * + * @return enumeration over individual tests + * @since Ant 1.3 + */ + protected Enumeration<JUnitTest> getIndividualTests() { + final int count = batchTests.size(); + final Enumeration[] enums = new Enumeration[ count + 1]; + for (int i = 0; i < count; i++) { + final BatchTest batchtest = batchTests.elementAt(i); + enums[i] = batchtest.elements(); + } + enums[enums.length - 1] = tests.elements(); + return Enumerations.fromCompound(enums); + } + + /** + * Verifies all <code>test</code> elements having the <code>methods</code> + * attribute specified and having the <code>if</code>-condition resolved + * to true, that the value of the <code>methods</code> attribute is valid. + * @exception BuildException if some of the tests matching the described + * conditions has invalid value of the + * <code>methods</code> attribute + * @since 1.8.2 + */ + private void checkMethodLists() throws BuildException { + if (tests.isEmpty()) { + return; + } + + final Enumeration<JUnitTest> testsEnum = tests.elements(); + while (testsEnum.hasMoreElements()) { + final JUnitTest test = testsEnum.nextElement(); + if (test.hasMethodsSpecified() && test.shouldRun(getProject())) { + test.resolveMethods(); + } + } + } + + /** + * return an enumeration listing each test, then each batchtest + * @return enumeration + * @since Ant 1.3 + */ + protected Enumeration<JUnitTest> allTests() { + final Enumeration[] enums = {tests.elements(), batchTests.elements()}; + return Enumerations.fromCompound(enums); + } + + /** + * @param test junit test + * @return array of FormatterElement + * @since Ant 1.3 + */ + private FormatterElement[] mergeFormatters(final JUnitTest test) { + final Vector<FormatterElement> feVector = (Vector<FormatterElement>) formatters.clone(); + test.addFormattersTo(feVector); + final FormatterElement[] feArray = new FormatterElement[feVector.size()]; + feVector.copyInto(feArray); + return feArray; + } + + /** + * If the formatter sends output to a file, return that file. + * null otherwise. + * @param fe formatter element + * @param test one JUnit test + * @return file reference + * @since Ant 1.3 + */ + protected File getOutput(final FormatterElement fe, final JUnitTest test) { + if (fe.getUseFile()) { + String base = test.getOutfile(); + if (base == null) { + base = JUnitTaskMirror.JUnitTestRunnerMirror.IGNORED_FILE_NAME; + } + final String filename = base + fe.getExtension(); + final File destFile = new File(test.getTodir(), filename); + final String absFilename = destFile.getAbsolutePath(); + return getProject().resolveFile(absFilename); + } + return null; + } + + /** + * Search for the given resource and add the directory or archive + * that contains it to the classpath. + * + * <p>Doesn't work for archives in JDK 1.1 as the URL returned by + * getResource doesn't contain the name of the archive.</p> + * + * @param resource resource that one wants to lookup + * @since Ant 1.4 + */ + protected void addClasspathEntry(final String resource) { + addClasspathResource(resource); + } + + /** + * Implementation of addClasspathEntry. + * + * @param resource resource that one wants to lookup + * @return true if something was in fact added + * @since Ant 1.7.1 + */ + private boolean addClasspathResource(String resource) { + /* + * pre Ant 1.6 this method used to call getClass().getResource + * while Ant 1.6 will call ClassLoader.getResource(). + * + * The difference is that Class.getResource expects a leading + * slash for "absolute" resources and will strip it before + * delegating to ClassLoader.getResource - so we now have to + * emulate Class's behavior. + */ + if (resource.startsWith("/")) { + resource = resource.substring(1); + } else { + resource = "org/apache/tools/ant/taskdefs/optional/junit/" + + resource; + } + + final File f = LoaderUtils.getResourceSource(JUnitTask.class.getClassLoader(), + resource); + if (f != null) { + log("Found " + f.getAbsolutePath(), Project.MSG_DEBUG); + antRuntimeClasses.createPath().setLocation(f); + return true; + } else { + log("Couldn\'t find " + resource, Project.MSG_DEBUG); + return false; + } + } + + static final String TIMEOUT_MESSAGE = + "Timeout occurred. Please note the time in the report does" + + " not reflect the time until the timeout."; + + /** + * Take care that some output is produced in report files if the + * watchdog kills the test. + * + * @since Ant 1.5.2 + */ + private void logTimeout(final FormatterElement[] feArray, final JUnitTest test, + final String testCase) { + logVmExit(feArray, test, TIMEOUT_MESSAGE, testCase); + } + + /** + * Take care that some output is produced in report files if the + * forked machine exited before the test suite finished but the + * reason is not a timeout. + * + * @since Ant 1.7 + */ + private void logVmCrash(final FormatterElement[] feArray, final JUnitTest test, final String testCase) { + logVmExit( + feArray, test, + "Forked Java VM exited abnormally. Please note the time in the report" + + " does not reflect the time until the VM exit.", + testCase); + } + + /** + * Take care that some output is produced in report files if the + * forked machine terminated before the test suite finished + * + * @since Ant 1.7 + */ + private void logVmExit(final FormatterElement[] feArray, final JUnitTest test, + final String message, final String testCase) { + if (delegate == null) { + setupJUnitDelegate(); + } + + try { + log("Using System properties " + System.getProperties(), + Project.MSG_VERBOSE); + if (splitJUnit) { + classLoader = (AntClassLoader) delegate.getClass().getClassLoader(); + } else { + createClassLoader(); + } + if (classLoader != null) { + classLoader.setThreadContextLoader(); + } + + test.setCounts(1, 0, 1, 0); + test.setProperties(getProject().getProperties()); + for (int i = 0; i < feArray.length; i++) { + final FormatterElement fe = feArray[i]; + if (fe.shouldUse(this)) { + final JUnitTaskMirror.JUnitResultFormatterMirror formatter = + fe.createFormatter(classLoader); + if (formatter != null) { + OutputStream out = null; + final File outFile = getOutput(fe, test); + if (outFile != null) { + try { + out = new FileOutputStream(outFile); + } catch (final IOException e) { + // ignore + } + } + if (out == null) { + out = getDefaultOutput(); + } + delegate.addVmExit(test, formatter, out, message, + testCase); + } + } + } + if (summary) { + final JUnitTaskMirror.SummaryJUnitResultFormatterMirror f = + delegate.newSummaryJUnitResultFormatter(); + f.setWithOutAndErr(equalsWithOutAndErr(summaryValue)); + delegate.addVmExit(test, f, getDefaultOutput(), message, testCase); + } + } finally { + if (classLoader != null) { + classLoader.resetThreadContextLoader(); + } + } + } + + /** + * Creates and configures an AntClassLoader instance from the + * nested classpath element. + * + * @since Ant 1.6 + */ + private void createClassLoader() { + final Path userClasspath = getCommandline().getClasspath(); + if (userClasspath != null) { + if (reloading || classLoader == null) { + deleteClassLoader(); + final Path classpath = (Path) userClasspath.clone(); + if (includeAntRuntime) { + log("Implicitly adding " + antRuntimeClasses + + " to CLASSPATH", Project.MSG_VERBOSE); + classpath.append(antRuntimeClasses); + } + classLoader = getProject().createClassLoader(classpath); + if (getClass().getClassLoader() != null + && getClass().getClassLoader() != Project.class.getClassLoader()) { + classLoader.setParent(getClass().getClassLoader()); + } + classLoader.setParentFirst(false); + classLoader.addJavaLibraries(); + log("Using CLASSPATH " + classLoader.getClasspath(), + Project.MSG_VERBOSE); + // make sure the test will be accepted as a TestCase + classLoader.addSystemPackageRoot("junit"); + // make sure the test annotation are accepted + classLoader.addSystemPackageRoot("org.junit"); + // will cause trouble in JDK 1.1 if omitted + classLoader.addSystemPackageRoot("org.apache.tools.ant"); + } + } + } + + /** + * Removes resources. + * + * <p>Is invoked in {@link #execute execute}. Subclasses that + * don't invoke execute should invoke this method in a finally + * block.</p> + * + * @since Ant 1.7.1 + */ + protected void cleanup() { + deleteClassLoader(); + delegate = null; + } + + /** + * Removes a classloader if needed. + * @since Ant 1.7 + */ + private void deleteClassLoader() { + if (classLoader != null) { + classLoader.cleanup(); + classLoader = null; + } + if (mirrorLoader instanceof SplitClassLoader) { + ((SplitClassLoader) mirrorLoader).cleanup(); + } + mirrorLoader = null; + } + + /** + * Get the command line used to run the tests. + * @return the command line. + * @since Ant 1.6.2 + */ + protected CommandlineJava getCommandline() { + if (commandline == null) { + commandline = new CommandlineJava(); + commandline.setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner"); + } + return commandline; + } + + /** + * Forked test support + * @since Ant 1.6.2 + */ + private static final class ForkedTestConfiguration { + private final boolean filterTrace; + private final boolean haltOnError; + private final boolean haltOnFailure; + private final String errorProperty; + private final String failureProperty; + + /** + * constructor for forked test configuration + * @param filterTrace + * @param haltOnError + * @param haltOnFailure + * @param errorProperty + * @param failureProperty + */ + ForkedTestConfiguration(final boolean filterTrace, final boolean haltOnError, + final boolean haltOnFailure, final String errorProperty, + final String failureProperty) { + this.filterTrace = filterTrace; + this.haltOnError = haltOnError; + this.haltOnFailure = haltOnFailure; + this.errorProperty = errorProperty; + this.failureProperty = failureProperty; + } + + /** + * configure from a test; sets member variables to attributes of the test + * @param test + */ + ForkedTestConfiguration(final JUnitTest test) { + this(test.getFiltertrace(), + test.getHaltonerror(), + test.getHaltonfailure(), + test.getErrorProperty(), + test.getFailureProperty()); + } + + /** + * equality test checks all the member variables + * @param other + * @return true if everything is equal + */ + @Override + public boolean equals(final Object other) { + if (other == null + || other.getClass() != ForkedTestConfiguration.class) { + return false; + } + final ForkedTestConfiguration o = (ForkedTestConfiguration) other; + return filterTrace == o.filterTrace + && haltOnError == o.haltOnError + && haltOnFailure == o.haltOnFailure + && ((errorProperty == null && o.errorProperty == null) + || + (errorProperty != null + && errorProperty.equals(o.errorProperty))) + && ((failureProperty == null && o.failureProperty == null) + || + (failureProperty != null + && failureProperty.equals(o.failureProperty))); + } + + /** + * hashcode is based only on the boolean members, and returns a value + * in the range 0-7. + * @return hash code value + */ + @Override + public int hashCode() { + // CheckStyle:MagicNumber OFF + return (filterTrace ? 1 : 0) + + (haltOnError ? 2 : 0) + + (haltOnFailure ? 4 : 0); + // CheckStyle:MagicNumber ON + } + } + + /** + * These are the different forking options + * @since 1.6.2 + */ + public static final class ForkMode extends EnumeratedAttribute { + + /** + * fork once only + */ + public static final String ONCE = "once"; + /** + * fork once per test class + */ + public static final String PER_TEST = "perTest"; + /** + * fork once per batch of tests + */ + public static final String PER_BATCH = "perBatch"; + + /** No arg constructor. */ + public ForkMode() { + super(); + } + + /** + * Constructor using a value. + * @param value the value to use - once, perTest or perBatch. + */ + public ForkMode(final String value) { + super(); + setValue(value); + } + + /** {@inheritDoc}. */ + @Override + public String[] getValues() { + return new String[] {ONCE, PER_TEST, PER_BATCH}; + } + } + + /** + * Executes all tests that don't need to be forked (or all tests + * if the runIndividual argument is true. Returns a collection of + * lists of tests that share the same VM configuration and haven't + * been executed yet. + * @param testList the list of tests to be executed or queued. + * @param runIndividual if true execute each test individually. + * @return a list of tasks to be executed. + * @since 1.6.2 + */ + protected Collection<List> executeOrQueue(final Enumeration<JUnitTest> testList, + final boolean runIndividual) { + final Map<ForkedTestConfiguration, List> testConfigurations = new HashMap<ForkedTestConfiguration, List>(); + while (testList.hasMoreElements()) { + final JUnitTest test = testList.nextElement(); + if (test.shouldRun(getProject())) { + /* with multi-threaded runs need to defer execution of even */ + /* individual tests so the threads can pick tests off the queue. */ + if ((runIndividual || !test.getFork()) && (threads == 1)) { + execute(test, 0); + } else { + final ForkedTestConfiguration c = + new ForkedTestConfiguration(test); + List<JUnitTest> l = testConfigurations.get(c); + if (l == null) { + l = new ArrayList<JUnitTest>(); + testConfigurations.put(c, l); + } + l.add(test); + } + } + } + return testConfigurations.values(); + } + + /** + * Logs information about failed tests, potentially stops + * processing (by throwing a BuildException) if a failure/error + * occurred or sets a property. + * @param exitValue the exitValue of the test. + * @param wasKilled if true, the test had been killed. + * @param test the test in question. + * @param name the name of the test. + * @since Ant 1.6.2 + */ + protected void actOnTestResult(final int exitValue, final boolean wasKilled, + final JUnitTest test, final String name) { + final TestResultHolder t = new TestResultHolder(); + t.exitCode = exitValue; + t.timedOut = wasKilled; + actOnTestResult(t, test, name); + } + + /** + * Logs information about failed tests, potentially stops + * processing (by throwing a BuildException) if a failure/error + * occurred or sets a property. + * @param result the result of the test. + * @param test the test in question. + * @param name the name of the test. + * @since Ant 1.7 + */ + protected void actOnTestResult(final TestResultHolder result, final JUnitTest test, + final String name) { + // if there is an error/failure and that it should halt, stop + // everything otherwise just log a statement + final boolean fatal = result.timedOut || result.crashed; + final boolean errorOccurredHere = + result.exitCode == JUnitTaskMirror.JUnitTestRunnerMirror.ERRORS || fatal; + final boolean failureOccurredHere = + result.exitCode != JUnitTaskMirror.JUnitTestRunnerMirror.SUCCESS || fatal; + if (errorOccurredHere || failureOccurredHere) { + if ((errorOccurredHere && test.getHaltonerror()) + || (failureOccurredHere && test.getHaltonfailure())) { + throw new BuildException(name + " failed" + + (result.timedOut ? " (timeout)" : "") + + (result.crashed ? " (crashed)" : ""), getLocation()); + } else { + if (logFailedTests) { + log(name + " FAILED" + + (result.timedOut ? " (timeout)" : "") + + (result.crashed ? " (crashed)" : ""), + Project.MSG_ERR); + } + if (errorOccurredHere && test.getErrorProperty() != null) { + getProject().setNewProperty(test.getErrorProperty(), "true"); + } + if (failureOccurredHere && test.getFailureProperty() != null) { + getProject().setNewProperty(test.getFailureProperty(), "true"); + } + } + } + } + + /** + * A value class that contains the result of a test. + */ + protected static class TestResultHolder { + // CheckStyle:VisibilityModifier OFF - bc + /** the exit code of the test. */ + public int exitCode = JUnitTaskMirror.JUnitTestRunnerMirror.ERRORS; + /** true if the test timed out */ + public boolean timedOut = false; + /** true if the test crashed */ + public boolean crashed = false; + // CheckStyle:VisibilityModifier ON + } + + /** + * A stream handler for handling the junit task. + * @since Ant 1.7 + */ + protected static class JUnitLogOutputStream extends LogOutputStream { + private final Task task; // local copy since LogOutputStream.task is private + + /** + * Constructor. + * @param task the task being logged. + * @param level the log level used to log data written to this stream. + */ + public JUnitLogOutputStream(final Task task, final int level) { + super(task, level); + this.task = task; + } + + /** + * Logs a line. + * If the line starts with junit.framework.TestListener: set the level + * to MSG_VERBOSE. + * @param line the line to log. + * @param level the logging level to use. + */ + @Override + protected void processLine(final String line, final int level) { + if (line.startsWith(TESTLISTENER_PREFIX)) { + task.log(line, Project.MSG_VERBOSE); + } else { + super.processLine(line, level); + } + } + } + + /** + * A log stream handler for junit. + * @since Ant 1.7 + */ + protected static class JUnitLogStreamHandler extends PumpStreamHandler { + /** + * Constructor. + * @param task the task to log. + * @param outlevel the level to use for standard output. + * @param errlevel the level to use for error output. + */ + public JUnitLogStreamHandler(final Task task, final int outlevel, final int errlevel) { + super(new JUnitLogOutputStream(task, outlevel), + new LogOutputStream(task, errlevel)); + } + } + + static final String NAME_OF_DUMMY_TEST = "Batch-With-Multiple-Tests"; + + /** + * Creates a JUnitTest instance that shares all flags with the + * passed in instance but has a more meaningful name. + * + * <p>If a VM running multiple tests crashes, we don't know which + * test failed. Prior to Ant 1.8.0 Ant would log the error with + * the last test of the batch test, which caused some confusion + * since the log might look as if a test had been executed last + * that was never started. With Ant 1.8.0 the test's name will + * indicate that something went wrong with a test inside the batch + * without giving it a real name.</p> + * + * @see "https://issues.apache.org/bugzilla/show_bug.cgi?id=45227" + */ + private static JUnitTest createDummyTestForBatchTest(final JUnitTest test) { + final JUnitTest t = (JUnitTest) test.clone(); + final int index = test.getName().lastIndexOf('.'); + // make sure test looks as if it was in the same "package" as + // the last test of the batch + final String pack = index > 0 ? test.getName().substring(0, index + 1) : ""; + t.setName(pack + NAME_OF_DUMMY_TEST); + return t; + } + + private static void printDual(final BufferedWriter w, final PrintStream s, final String text) + throws IOException { + w.write(String.valueOf(text)); + s.print(text); + } + + private static void printlnDual(final BufferedWriter w, final PrintStream s, final String text) + throws IOException { + w.write(String.valueOf(text)); + w.newLine(); + s.println(text); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirror.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirror.java new file mode 100644 index 00000000..694e1d8c --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirror.java @@ -0,0 +1,190 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.tools.ant.AntClassLoader; +import org.apache.tools.ant.types.Permissions; + +/** + * Handles the portions of {@link JUnitTask} which need to directly access + * actual JUnit classes, so that junit.jar need not be on Ant's startup classpath. + * Neither JUnitTask.java nor JUnitTaskMirror.java nor their transitive static + * deps may import any junit.** classes! + * Specifically, need to not refer to + * - JUnitResultFormatter or its subclasses + * - JUnitVersionHelper + * - JUnitTestRunner + * Cf. JUnitTask.SplitLoader#isSplit(String) + * Public only to permit access from classes in this package; do not use directly. + * + * @since 1.7 + * @see "bug #38799" + */ +public interface JUnitTaskMirror { + + /** + * Add the formatter to be called when the jvm exits before + * the test suite finishes. + * @param test the test. + * @param formatter the formatter to use. + * @param out the output stream to use. + * @param message the message to write out. + * @param testCase the name of the test. + */ + void addVmExit(JUnitTest test, JUnitResultFormatterMirror formatter, + OutputStream out, String message, String testCase); + + /** + * Create a new test runner for a test. + * @param test the test to run. + * @param methods names of the test methods to be run. + * @param haltOnError if true halt the tests if an error occurs. + * @param filterTrace if true filter the stack traces. + * @param haltOnFailure if true halt the test if a failure occurs. + * @param showOutput if true show output. + * @param logTestListenerEvents if true log test listener events. + * @param classLoader the classloader to use to create the runner. + * @return the test runner. + */ + JUnitTestRunnerMirror newJUnitTestRunner(JUnitTest test, String[] methods, boolean haltOnError, + boolean filterTrace, boolean haltOnFailure, boolean showOutput, + boolean logTestListenerEvents, AntClassLoader classLoader); + + /** + * Create a summary result formatter. + * @return the created formatter. + */ + SummaryJUnitResultFormatterMirror newSummaryJUnitResultFormatter(); + + + /** The interface that JUnitResultFormatter extends. */ + public interface JUnitResultFormatterMirror { + /** + * Set the output stream. + * @param outputStream the stream to use. + */ + void setOutput(OutputStream outputStream); + } + + /** The interface that SummaryJUnitResultFormatter extends. */ + public interface SummaryJUnitResultFormatterMirror + extends JUnitResultFormatterMirror { + + /** + * Set where standard out and standard error should be included. + * @param value if true include the outputs in the summary. + */ + void setWithOutAndErr(boolean value); + } + + /** Interface that test runners implement. */ + public interface JUnitTestRunnerMirror { + + /** + * Used in formatter arguments as a placeholder for the basename + * of the output file (which gets replaced by a test specific + * output file name later). + * + * @since Ant 1.6.3 + */ + String IGNORED_FILE_NAME = "IGNORETHIS"; + + /** + * No problems with this test. + */ + int SUCCESS = 0; + + /** + * Some tests failed. + */ + int FAILURES = 1; + + /** + * An error occurred. + */ + int ERRORS = 2; + + /** + * Permissions for the test run. + * @param perm the permissions to use. + */ + void setPermissions(Permissions perm); + + /** Run the test. */ + void run(); + + /** + * Add a formatter to the test. + * @param formatter the formatter to use. + */ + void addFormatter(JUnitResultFormatterMirror formatter); + + /** + * Returns what System.exit() would return in the standalone version. + * + * @return 2 if errors occurred, 1 if tests failed else 0. + */ + int getRetCode(); + + /** + * Handle output sent to System.err. + * + * @param output coming from System.err + */ + void handleErrorFlush(String output); + + /** + * Handle output sent to System.err. + * + * @param output output for System.err + */ + void handleErrorOutput(String output); + + /** + * Handle output sent to System.out. + * + * @param output output for System.out. + */ + void handleOutput(String output); + + /** + * Handle an input request. + * + * @param buffer the buffer into which data is to be read. + * @param offset the offset into the buffer at which data is stored. + * @param length the amount of data to read. + * + * @return the number of bytes read. + * + * @exception IOException if the data cannot be read. + */ + int handleInput(byte[] buffer, int offset, int length) throws IOException; + + /** + * Handle output sent to System.out. + * + * @param output output for System.out. + */ + void handleFlush(String output); + + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirrorImpl.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirrorImpl.java new file mode 100644 index 00000000..c7dae258 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirrorImpl.java @@ -0,0 +1,109 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.io.OutputStream; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; +import junit.framework.TestResult; + +import org.apache.tools.ant.AntClassLoader; + +/** + * Implementation of the part of the junit task which can directly refer to junit.* classes. + * Public only to permit use of reflection; do not use directly. + * @see JUnitTaskMirror + * @see "bug #38799" + * @since 1.7 + */ +public final class JUnitTaskMirrorImpl implements JUnitTaskMirror { + + private final JUnitTask task; + + /** + * Constructor. + * @param task the junittask that uses this mirror. + */ + public JUnitTaskMirrorImpl(JUnitTask task) { + this.task = task; + } + + /** {@inheritDoc}. */ + public void addVmExit(JUnitTest test, JUnitTaskMirror.JUnitResultFormatterMirror aFormatter, + OutputStream out, String message, String testCase) { + JUnitResultFormatter formatter = (JUnitResultFormatter) aFormatter; + formatter.setOutput(out); + formatter.startTestSuite(test); + //the trick to integrating test output to the formatter, is to + //create a special test class that asserts an error + //and tell the formatter that it raised. + TestCase t = new VmExitErrorTest(message, test, testCase); + formatter.startTest(t); + formatter.addError(t, new AssertionFailedError(message)); + formatter.endTestSuite(test); + } + + /** {@inheritDoc}. */ + public JUnitTaskMirror.JUnitTestRunnerMirror newJUnitTestRunner(JUnitTest test, + String[] methods, + boolean haltOnError, boolean filterTrace, boolean haltOnFailure, + boolean showOutput, boolean logTestListenerEvents, AntClassLoader classLoader) { + return new JUnitTestRunner(test, methods, haltOnError, filterTrace, haltOnFailure, + showOutput, logTestListenerEvents, classLoader); + } + + /** {@inheritDoc}. */ + public JUnitTaskMirror.SummaryJUnitResultFormatterMirror newSummaryJUnitResultFormatter() { + return new SummaryJUnitResultFormatter(); + } + + static class VmExitErrorTest extends TestCase { + + private String message; + private JUnitTest test; + private String testCase; + + VmExitErrorTest(String aMessage, JUnitTest anOriginalTest, String aTestCase) { + message = aMessage; + test = anOriginalTest; + testCase = aTestCase; + } + + public int countTestCases() { + return 1; + } + + public void run(TestResult r) { + throw new AssertionFailedError(message); + } + + public String getName() { + return testCase; + } + + String getClassName() { + return test.getName(); + } + + public String toString() { + return test.getName() + ":" + testCase; + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java new file mode 100644 index 00000000..835c013b --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java @@ -0,0 +1,542 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Properties; +import java.util.Vector; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.PropertyHelper; + +/** + * <p> Run a single JUnit test. + * + * <p> The JUnit test is actually run by {@link JUnitTestRunner}. + * So read the doc comments for that class :) + * + * @since Ant 1.2 + * + * @see JUnitTask + * @see JUnitTestRunner + */ +public class JUnitTest extends BaseTest implements Cloneable { + + /** the name of the test case */ + private String name = null; + + /** + * whether the list of test methods has been specified + * @see #setMethods(java.lang.String) + * @see #setMethods(java.lang.String[]) + */ + private boolean methodsSpecified = false; + + /** comma-separated list of names of test methods to execute */ + private String methodsList = null; + + /** the names of test methods to execute */ + private String[] methods = null; + + /** the name of the result file */ + private String outfile = null; + + // @todo this is duplicating TestResult information. Only the time is not + // part of the result. So we'd better derive a new class from TestResult + // and deal with it. (SB) + private long runs, failures, errors; + /** + @since Ant 1.9.0 + */ + private long skips; + + private long runTime; + + private int antThreadID; + + // Snapshot of the system properties + private Properties props = null; + + /** No arg constructor. */ + public JUnitTest() { + } + + /** + * Constructor with name. + * @param name the name of the test. + */ + public JUnitTest(String name) { + this.name = name; + } + + /** + * Constructor with options. + * @param name the name of the test. + * @param haltOnError if true halt the tests if there is an error. + * @param haltOnFailure if true halt the tests if there is a failure. + * @param filtertrace if true filter stack traces. + */ + public JUnitTest(String name, boolean haltOnError, boolean haltOnFailure, + boolean filtertrace) { + this(name, haltOnError, haltOnFailure, filtertrace, null, 0); + } + + /** + * Constructor with options. + * @param name the name of the test. + * @param haltOnError if true halt the tests if there is an error. + * @param haltOnFailure if true halt the tests if there is a failure. + * @param filtertrace if true filter stack traces. + * @param methods if non-null run only these test methods + * @since 1.8.2 + */ + public JUnitTest(String name, boolean haltOnError, boolean haltOnFailure, + boolean filtertrace, String[] methods) { + this(name, haltOnError, haltOnFailure, filtertrace, methods, 0); + } + + /** + * Constructor with options. + * @param name the name of the test. + * @param haltOnError if true halt the tests if there is an error. + * @param haltOnFailure if true halt the tests if there is a failure. + * @param filtertrace if true filter stack traces. + * @param methods if non-null run only these test methods + * @param thread Ant thread ID in which test is currently running + * @since 1.9.4 + */ + public JUnitTest(String name, boolean haltOnError, boolean haltOnFailure, + boolean filtertrace, String[] methods, int thread) { + this.name = name; + this.haltOnError = haltOnError; + this.haltOnFail = haltOnFailure; + this.filtertrace = filtertrace; + this.methodsSpecified = methods != null; + this.methods = methodsSpecified ? (String[]) methods.clone() : null; + this.antThreadID = thread; + } + + /** + * Sets names of individual test methods to be executed. + * @param value comma-separated list of names of individual test methods + * to be executed, + * or <code>null</code> if all test methods should be executed + * @since 1.8.2 + */ + public void setMethods(String value) { + methodsList = value; + methodsSpecified = (value != null); + methods = null; + } + + /** + * Sets names of individual test methods to be executed. + * @param value non-empty array of names of test methods to be executed + * @see #setMethods(String) + * @since 1.8.2 + */ + void setMethods(String[] value) { + methods = value; + methodsSpecified = (value != null); + methodsList = null; + } + + /** + * Set the name of the test class. + * @param value the name to use. + */ + public void setName(String value) { + name = value; + } + + /** + * Set the thread id + * @param thread the Ant id of the thread running this test + * (this is not the system process or thread id) + * (this will be 0 in single-threaded mode). + * @since Ant 1.9.4 + */ + public void setThread(int thread) { + this.antThreadID = thread; + } + + /** + * Set the name of the output file. + * @param value the name of the output file to use. + */ + public void setOutfile(String value) { + outfile = value; + } + + /** + * Informs whether a list of test methods has been specified in this test. + * @return <code>true</code> if test methods to be executed have been + * specified, <code>false</code> otherwise + * @see #setMethods(java.lang.String) + * @see #setMethods(java.lang.String[]) + * @since 1.8.2 + */ + boolean hasMethodsSpecified() { + return methodsSpecified; + } + + /** + * Get names of individual test methods to be executed. + * + * @return array of names of the individual test methods to be executed, + * or <code>null</code> if all test methods in the suite + * defined by the test class will be executed + * @since 1.8.2 + */ + String[] getMethods() { + if (methodsSpecified && (methods == null)) { + resolveMethods(); + } + return methods; + } + + /** + * Gets a comma-separated list of names of methods that are to be executed + * by this test. + * @return the comma-separated list of test method names, or an empty + * string of no method is to be executed, or <code>null</code> + * if no method is specified + * @since 1.8.2 + */ + String getMethodsString() { + if ((methodsList == null) && methodsSpecified) { + if (methods.length == 0) { + methodsList = ""; + } else if (methods.length == 1) { + methodsList = methods[0]; + } else { + StringBuffer buf = new StringBuffer(methods.length * 16); + buf.append(methods[0]); + for (int i = 1; i < methods.length; i++) { + buf.append(',').append(methods[i]); + } + methodsList = buf.toString(); + } + } + return methodsList; + } + + /** + * Computes the value of the {@link #methods} field from the value + * of the {@link #methodsList} field, if it has not been computed yet. + * @exception BuildException if the value of the {@link #methodsList} field + * was invalid + * @since 1.8.2 + */ + void resolveMethods() { + if ((methods == null) && methodsSpecified) { + try { + methods = parseTestMethodNamesList(methodsList); + } catch (IllegalArgumentException ex) { + throw new BuildException( + "Invalid specification of test methods: \"" + + methodsList + + "\"; expected: comma-separated list of valid Java identifiers", + ex); + } + } + } + + /** + * Parses a comma-separated list of method names and check their validity. + * @param methodNames comma-separated list of method names to be parsed + * @return array of individual test method names + * @exception java.lang.IllegalArgumentException + * if the given string is <code>null</code> or if it is not + * a comma-separated list of valid Java identifiers; + * an empty string is acceptable and is handled as an empty + * list + * @since 1.8.2 + */ + public static String[] parseTestMethodNamesList(String methodNames) + throws IllegalArgumentException { + if (methodNames == null) { + throw new IllegalArgumentException("methodNames is <null>"); + } + + methodNames = methodNames.trim(); + + int length = methodNames.length(); + if (length == 0) { + return new String[0]; + } + + /* strip the trailing comma, if any */ + if (methodNames.charAt(length - 1) == ',') { + methodNames = methodNames.substring(0, length - 1).trim(); + length = methodNames.length(); + if (length == 0) { + throw new IllegalArgumentException("Empty method name"); + } + } + + final char[] chars = methodNames.toCharArray(); + /* easy detection of one particular case of illegal string: */ + if (chars[0] == ',') { + throw new IllegalArgumentException("Empty method name"); + } + /* count number of method names: */ + int wordCount = 1; + for (int i = 1; i < chars.length; i++) { + if (chars[i] == ',') { + wordCount++; + } + } + /* prepare the resulting array: */ + String[] result = new String[wordCount]; + /* parse the string: */ + final int stateBeforeWord = 1; + final int stateInsideWord = 2; + final int stateAfterWord = 3; + // + int state = stateBeforeWord; + int wordStartIndex = -1; + int wordIndex = 0; + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + switch (state) { + case stateBeforeWord: + if (c == ',') { + throw new IllegalArgumentException("Empty method name"); + } else if (c == ' ') { + // remain in the same state + } else if (Character.isJavaIdentifierStart(c)) { + wordStartIndex = i; + state = stateInsideWord; + } else { + throw new IllegalArgumentException("Illegal start of method name: " + c); + } + break; + case stateInsideWord: + if (c == ',') { + result[wordIndex++] = methodNames.substring(wordStartIndex, i); + state = stateBeforeWord; + } else if (c == ' ') { + result[wordIndex++] = methodNames.substring(wordStartIndex, i); + state = stateAfterWord; + } else if (Character.isJavaIdentifierPart(c)) { + // remain in the same state + } else { + throw new IllegalArgumentException("Illegal character in method name: " + c); + } + break; + case stateAfterWord: + if (c == ',') { + state = stateBeforeWord; + } else if (c == ' ') { + // remain in the same state + } else { + throw new IllegalArgumentException("Space in method name"); + } + break; + default: + // this should never happen + } + } + switch (state) { + case stateBeforeWord: + case stateAfterWord: + break; + case stateInsideWord: + result[wordIndex++] = methodNames.substring(wordStartIndex, chars.length); + break; + default: + // this should never happen + } + return result; + } + + /** + * Get the name of the test class. + * @return the name of the test. + */ + public String getName() { + return name; + } + + /** + * Get the Ant id of the thread running the test. + * @return the thread id + */ + public int getThread() { + return antThreadID; + } + + /** + * Get the name of the output file + * + * @return the name of the output file. + */ + public String getOutfile() { + return outfile; + } + + /** + * Set the number of runs, failures, errors, and skipped tests. + * @param runs the number of runs. + * @param failures the number of failures. + * @param errors the number of errors. + * Kept for backward compatibility with Ant 1.8.4 + */ + public void setCounts(long runs, long failures, long errors) { + this.runs = runs; + this.failures = failures; + this.errors = errors; + } + /** + * Set the number of runs, failures, errors, and skipped tests. + * @param runs the number of runs. + * @param failures the number of failures. + * @param errors the number of errors. + * @param skips the number of skipped tests. + * @since Ant 1.9.0 + */ + public void setCounts(long runs, long failures, long errors, long skips) { + this.runs = runs; + this.failures = failures; + this.errors = errors; + this.skips = skips; + } + + /** + * Set the runtime. + * @param runTime the time in milliseconds. + */ + public void setRunTime(long runTime) { + this.runTime = runTime; + } + + /** + * Get the number of runs. + * @return the number of runs. + */ + public long runCount() { + return runs; + } + + /** + * Get the number of failures. + * @return the number of failures. + */ + public long failureCount() { + return failures; + } + + /** + * Get the number of errors. + * @return the number of errors. + */ + public long errorCount() { + return errors; + } + + /** + * Get the number of skipped tests. + * @return the number of skipped tests. + */ + public long skipCount() { + return skips; + } + + /** + * Get the run time. + * @return the run time in milliseconds. + */ + public long getRunTime() { + return runTime; + } + + /** + * Get the properties used in the test. + * @return the properties. + */ + public Properties getProperties() { + return props; + } + + /** + * Set the properties to be used in the test. + * @param p the properties. + * This is a copy of the projects ant properties. + */ + public void setProperties(Hashtable p) { + props = new Properties(); + for (Enumeration e = p.keys(); e.hasMoreElements();) { + Object key = e.nextElement(); + props.put(key, p.get(key)); + } + } + + /** + * Check if this test should run based on the if and unless + * attributes. + * @param p the project to use to check if the if and unless + * properties exist in. + * @return true if this test or testsuite should be run. + */ + public boolean shouldRun(Project p) { + PropertyHelper ph = PropertyHelper.getPropertyHelper(p); + return ph.testIfCondition(getIfCondition()) + && ph.testUnlessCondition(getUnlessCondition()); + } + + /** + * Get the formatters set for this test. + * @return the formatters as an array. + */ + public FormatterElement[] getFormatters() { + FormatterElement[] fes = new FormatterElement[formatters.size()]; + formatters.copyInto(fes); + return fes; + } + + /** + * Convenient method to add formatters to a vector + */ + void addFormattersTo(Vector v) { + final int count = formatters.size(); + for (int i = 0; i < count; i++) { + v.addElement(formatters.elementAt(i)); + } + } + + /** + * @since Ant 1.5 + * @return a clone of this test. + */ + @Override + public Object clone() { + try { + JUnitTest t = (JUnitTest) super.clone(); + t.props = props == null ? null : (Properties) props.clone(); + t.formatters = (Vector) formatters.clone(); + return t; + } catch (CloneNotSupportedException e) { + // plain impossible + return this; + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java new file mode 100644 index 00000000..a457375e --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java @@ -0,0 +1,1297 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.StringReader; +import java.io.StringWriter; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.Vector; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestFailure; +import junit.framework.TestListener; +import junit.framework.TestResult; +import junit.framework.TestSuite; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.types.Permissions; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.StringUtils; +import org.apache.tools.ant.util.TeeOutputStream; + +/** + * Simple Testrunner for JUnit that runs all tests of a testsuite. + * + * <p>This TestRunner expects a name of a TestCase class as its + * argument. If this class provides a static suite() method it will be + * called and the resulting Test will be run. So, the signature should be + * <pre><code> + * public static junit.framework.Test suite() + * </code></pre> + * + * <p> If no such method exists, all public methods starting with + * "test" and taking no argument will be run. + * + * <p> Summary output is generated at the end. + * + * @since Ant 1.2 + */ + +public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestRunnerMirror { + + /** + * Holds the registered formatters. + */ + private final Vector<JUnitTaskMirror.JUnitResultFormatterMirror> formatters = new Vector(); + + /** + * Collects TestResults. + */ + private IgnoredTestResult res; + + /** + * Do we filter junit.*.* stack frames out of failure and error exceptions. + */ + private static boolean filtertrace = true; + + /** + * Do we send output to System.out/.err in addition to the formatters? + */ + private boolean showOutput = false; + + private boolean outputToFormatters = true; + + /** + * The permissions set for the test to run. + */ + private Permissions perm = null; + + private static final String JUNIT_4_TEST_ADAPTER + = "junit.framework.JUnit4TestAdapter"; + + private static final String[] DEFAULT_TRACE_FILTERS = new String[] { + "junit.framework.TestCase", + "junit.framework.TestResult", + "junit.framework.TestSuite", + "junit.framework.Assert.", // don't filter AssertionFailure + "junit.swingui.TestRunner", + "junit.awtui.TestRunner", + "junit.textui.TestRunner", + "java.lang.reflect.Method.invoke(", + "sun.reflect.", + "org.apache.tools.ant.", + // JUnit 4 support: + "org.junit.", + "junit.framework.JUnit4TestAdapter", + " more", + }; + + + /** + * Do we stop on errors. + */ + private boolean haltOnError = false; + + /** + * Do we stop on test failures. + */ + private boolean haltOnFailure = false; + + /** + * Returncode + */ + private int retCode = SUCCESS; + + /** + * The TestSuite we are currently running. + */ + private final JUnitTest junitTest; + + /** output written during the test */ + private PrintStream systemError; + + /** Error output during the test */ + private PrintStream systemOut; + + /** is this runner running in forked mode? */ + private boolean forked = false; + + /** Running more than one test suite? */ + private static boolean multipleTests = false; + + /** ClassLoader passed in in non-forked mode. */ + private final ClassLoader loader; + + /** Do we print TestListener events? */ + private boolean logTestListenerEvents = false; + + /** Turned on if we are using JUnit 4 for this test suite. see #38811 */ + private boolean junit4; + + /** + * The file used to indicate that the build crashed. + * File will be empty in case the build did not crash. + */ + private static String crashFile = null; + + /** Names of test methods to execute */ + private String[] methods = null; + + /** + * Constructor for fork=true or when the user hasn't specified a + * classpath. + * @param test the test to run. + * @param haltOnError whether to stop the run if an error is found. + * @param filtertrace whether to filter junit.*.* stack frames out of exceptions + * @param haltOnFailure whether to stop the run if failure is found. + */ + public JUnitTestRunner(final JUnitTest test, final boolean haltOnError, + final boolean filtertrace, final boolean haltOnFailure) { + this(test, haltOnError, filtertrace, haltOnFailure, false); + } + + /** + * Constructor for fork=true or when the user hasn't specified a + * classpath. + * @param test the test to run. + * @param haltOnError whether to stop the run if an error is found. + * @param filtertrace whether to filter junit.*.* stack frames out of exceptions + * @param haltOnFailure whether to stop the run if failure is found. + * @param showOutput whether to send output to System.out/.err as well as formatters. + */ + public JUnitTestRunner(final JUnitTest test, final boolean haltOnError, + final boolean filtertrace, final boolean haltOnFailure, + final boolean showOutput) { + this(test, haltOnError, filtertrace, haltOnFailure, showOutput, false); + } + + /** + * Constructor for fork=true or when the user hasn't specified a + * classpath. + * @param test the test to run. + * @param haltOnError whether to stop the run if an error is found. + * @param filtertrace whether to filter junit.*.* stack frames out of exceptions + * @param haltOnFailure whether to stop the run if failure is found. + * @param showOutput whether to send output to System.out/.err as well as formatters. + * @param logTestListenerEvents whether to print TestListener events. + * @since Ant 1.7 + */ + public JUnitTestRunner(final JUnitTest test, final boolean haltOnError, + final boolean filtertrace, final boolean haltOnFailure, + final boolean showOutput, final boolean logTestListenerEvents) { + this(test, null, haltOnError, filtertrace, haltOnFailure, showOutput, + logTestListenerEvents, null); + } + + /** + * Constructor for fork=true or when the user hasn't specified a + * classpath. + * @param test the test to run. + * @param methods names of methods of the test to be executed. + * @param haltOnError whether to stop the run if an error is found. + * @param filtertrace whether to filter junit.*.* stack frames out of exceptions + * @param haltOnFailure whether to stop the run if failure is found. + * @param showOutput whether to send output to System.out/.err as well as formatters. + * @param logTestListenerEvents whether to print TestListener events. + * @since 1.8.2 + */ + public JUnitTestRunner(final JUnitTest test, final String[] methods, final boolean haltOnError, + final boolean filtertrace, final boolean haltOnFailure, + final boolean showOutput, final boolean logTestListenerEvents) { + this(test, methods, haltOnError, filtertrace, haltOnFailure, showOutput, + logTestListenerEvents, null); + } + + /** + * Constructor to use when the user has specified a classpath. + * @param test the test to run. + * @param haltOnError whether to stop the run if an error is found. + * @param filtertrace whether to filter junit.*.* stack frames out of exceptions + * @param haltOnFailure whether to stop the run if failure is found. + * @param loader the classloader to use running the test. + */ + public JUnitTestRunner(final JUnitTest test, final boolean haltOnError, + final boolean filtertrace, final boolean haltOnFailure, + final ClassLoader loader) { + this(test, haltOnError, filtertrace, haltOnFailure, false, loader); + } + + /** + * Constructor to use when the user has specified a classpath. + * @param test the test to run. + * @param haltOnError whether to stop the run if an error is found. + * @param filtertrace whether to filter junit.*.* stack frames out of exceptions + * @param haltOnFailure whether to stop the run if failure is found. + * @param showOutput whether to send output to System.out/.err as well as formatters. + * @param loader the classloader to use running the test. + */ + public JUnitTestRunner(final JUnitTest test, final boolean haltOnError, + final boolean filtertrace, final boolean haltOnFailure, + final boolean showOutput, final ClassLoader loader) { + this(test, haltOnError, filtertrace, haltOnFailure, showOutput, + false, loader); + } + + /** + * Constructor to use when the user has specified a classpath. + * @param test the test to run. + * @param haltOnError whether to stop the run if an error is found. + * @param filtertrace whether to filter junit.*.* stack frames out of exceptions + * @param haltOnFailure whether to stop the run if failure is found. + * @param showOutput whether to send output to System.out/.err as well as formatters. + * @param logTestListenerEvents whether to print TestListener events. + * @param loader the classloader to use running the test. + * @since Ant 1.7 + */ + public JUnitTestRunner(final JUnitTest test, final boolean haltOnError, + final boolean filtertrace, final boolean haltOnFailure, + final boolean showOutput, final boolean logTestListenerEvents, + final ClassLoader loader) { + this(test, null, haltOnError, filtertrace, haltOnFailure, showOutput, + logTestListenerEvents, loader); + } + + + /** + * Constructor to use when the user has specified a classpath. + * @since 1.8.2 + */ + public JUnitTestRunner(final JUnitTest test, final String[] methods, final boolean haltOnError, + final boolean filtertrace, final boolean haltOnFailure, + final boolean showOutput, final boolean logTestListenerEvents, + final ClassLoader loader) { + super(); + JUnitTestRunner.filtertrace = filtertrace; // TODO clumsy, should use instance field somehow + this.junitTest = test; + this.haltOnError = haltOnError; + this.haltOnFailure = haltOnFailure; + this.showOutput = showOutput; + this.logTestListenerEvents = logTestListenerEvents; + this.methods = methods != null ? (String[]) methods.clone() : null; + this.loader = loader; + } + + private PrintStream savedOut = null; + private PrintStream savedErr = null; + + private PrintStream createEmptyStream() { + return new PrintStream( + new OutputStream() { + @Override + public void write(final int b) { + } + }); + } + + private PrintStream createTeePrint(final PrintStream ps1, final PrintStream ps2) { + return new PrintStream(new TeeOutputStream(ps1, ps2)); + } + + private void setupIOStreams(final ByteArrayOutputStream o, + final ByteArrayOutputStream e) { + systemOut = new PrintStream(o); + systemError = new PrintStream(e); + + if (forked) { + if (!outputToFormatters) { + if (!showOutput) { + savedOut = System.out; + savedErr = System.err; + System.setOut(createEmptyStream()); + System.setErr(createEmptyStream()); + } + } else { + savedOut = System.out; + savedErr = System.err; + if (!showOutput) { + System.setOut(systemOut); + System.setErr(systemError); + } else { + System.setOut(createTeePrint(savedOut, systemOut)); + System.setErr(createTeePrint(savedErr, systemError)); + } + perm = null; + } + } else { + if (perm != null) { + perm.setSecurityManager(); + } + } + } + + /** + * Run the test. + */ + public void run() { + res = new IgnoredTestResult(); + res.addListener(wrapListener(this)); + final int size = formatters.size(); + for (int i = 0; i < size; i++) { + res.addListener(wrapListener((TestListener) formatters.elementAt(i))); + } + + final ByteArrayOutputStream errStrm = new ByteArrayOutputStream(); + final ByteArrayOutputStream outStrm = new ByteArrayOutputStream(); + + setupIOStreams(outStrm, errStrm); + + Test suite = null; + Throwable exception = null; + boolean startTestSuiteSuccess = false; + + try { + + try { + Class testClass = null; + if (loader == null) { + testClass = Class.forName(junitTest.getName()); + } else { + testClass = Class.forName(junitTest.getName(), true, + loader); + } + + final boolean testMethodsSpecified = (methods != null); + + // check for a static suite method first, even when using + // JUnit 4 + Method suiteMethod = null; + if (!testMethodsSpecified) { + try { + // check if there is a suite method + suiteMethod = testClass.getMethod("suite", new Class[0]); + } catch (final NoSuchMethodException e) { + // no appropriate suite method found. We don't report any + // error here since it might be perfectly normal. + } + } + + if (suiteMethod != null) { + // if there is a suite method available, then try + // to extract the suite from it. If there is an error + // here it will be caught below and reported. + suite = (Test) suiteMethod.invoke(null, new Object[0]); + + } else { + Class junit4TestAdapterClass = null; + Class junit4TestAdapterCacheClass = null; + boolean useSingleMethodAdapter = false; + + if (junit.framework.TestCase.class.isAssignableFrom(testClass)) { + // Do not use JUnit 4 API for running JUnit 3.x + // tests - it is not able to run individual test + // methods. + // + // Technical details: + // org.junit.runner.Request.method(Class, String).getRunner() + // would return a runner which always executes all + // test methods. The reason is that the Runner would be + // an instance of class + // org.junit.internal.runners.OldTestClassRunner + // that does not implement interface Filterable - so it + // is unable to filter out test methods not matching + // the requested name. + } else { + // Check for JDK 5 first. Will *not* help on JDK 1.4 + // if only junit-4.0.jar in CP because in that case + // linkage of whole task will already have failed! But + // will help if CP has junit-3.8.2.jar:junit-4.0.jar. + + // In that case first C.fN will fail with CNFE and we + // will avoid UnsupportedClassVersionError. + + try { + Class.forName("java.lang.annotation.Annotation"); + junit4TestAdapterCacheClass = Class.forName("org.apache.tools.ant.taskdefs.optional.junit.CustomJUnit4TestAdapterCache"); + if (loader == null) { + junit4TestAdapterClass = + Class.forName(JUNIT_4_TEST_ADAPTER); + if (testMethodsSpecified) { + /* + * We cannot try to load the JUnit4TestAdapter + * before trying to load JUnit4TestMethodAdapter + * because it might fail with + * NoClassDefFoundException, instead of plain + * ClassNotFoundException. + */ + junit4TestAdapterClass = Class.forName( + "org.apache.tools.ant.taskdefs.optional.junit.JUnit4TestMethodAdapter"); + useSingleMethodAdapter = true; + } + } else { + junit4TestAdapterClass = + Class.forName(JUNIT_4_TEST_ADAPTER, + true, loader); + if (testMethodsSpecified) { + junit4TestAdapterClass = + Class.forName( + "org.apache.tools.ant.taskdefs.optional.junit.JUnit4TestMethodAdapter", + true, loader); + useSingleMethodAdapter = true; + } + } + } catch (final ClassNotFoundException e) { + // OK, fall back to JUnit 3. + } + } + junit4 = junit4TestAdapterClass != null; + + if (junitTest.isSkipNonTests()) { + if (!containsTests(testClass, junit4)) { + return; + } + } + + + if (junit4) { + // Let's use it! + Class[] formalParams; + Object[] actualParams; + if (useSingleMethodAdapter) { + formalParams = new Class[] {Class.class, String[].class}; + actualParams = new Object[] {testClass, methods}; + } else { + formalParams = new Class[] {Class.class, Class.forName("junit.framework.JUnit4TestAdapterCache")}; + actualParams = new Object[] {testClass, junit4TestAdapterCacheClass.getMethod("getInstance").invoke(null)}; + } + suite = + (Test) junit4TestAdapterClass + .getConstructor(formalParams). + newInstance(actualParams); + } else { + // Use JUnit 3. + + // try to extract a test suite automatically this + // will generate warnings if the class is no + // suitable Test + if (!testMethodsSpecified) { + suite = new TestSuite(testClass); + } else if (methods.length == 1) { + suite = TestSuite.createTest(testClass, methods[0]); + } else { + final TestSuite testSuite = new TestSuite(testClass.getName()); + for (int i = 0; i < methods.length; i++) { + testSuite.addTest( + TestSuite.createTest(testClass, methods[i])); + } + suite = testSuite; + } + } + + } + + } catch (final Throwable e) { + retCode = ERRORS; + exception = e; + } + + final long start = System.currentTimeMillis(); + + fireStartTestSuite(); + startTestSuiteSuccess = true; + if (exception != null) { // had an exception constructing suite + final int formatterSize = formatters.size(); + for (int i = 0; i < formatterSize; i++) { + ((TestListener) formatters.elementAt(i)) + .addError(null, exception); + } + junitTest.setCounts(1, 0, 1, 0); + junitTest.setRunTime(0); + } else { + try { + logTestListenerEvent("tests to run: " + suite.countTestCases()); + suite.run(res); + } finally { + if (junit4 || + suite.getClass().getName().equals(JUNIT_4_TEST_ADAPTER)) { + final int[] cnts = findJUnit4FailureErrorCount(res); + junitTest.setCounts(res.runCount() + res.ignoredCount(), cnts[0], cnts[1], res.ignoredCount() + res.skippedCount()); + } else { + junitTest.setCounts(res.runCount() + res.ignoredCount(), res.failureCount(), + res.errorCount(), res.ignoredCount() + res.skippedCount()); + } + junitTest.setRunTime(System.currentTimeMillis() - start); + } + } + } finally { + if (perm != null) { + perm.restoreSecurityManager(); + } + if (savedOut != null) { + System.setOut(savedOut); + } + if (savedErr != null) { + System.setErr(savedErr); + } + + systemError.close(); + systemError = null; + systemOut.close(); + systemOut = null; + if (startTestSuiteSuccess) { + String out, err; + try { + out = new String(outStrm.toByteArray()); + } catch (final OutOfMemoryError ex) { + out = "out of memory on output stream"; + } + try { + err = new String(errStrm.toByteArray()); + } catch (final OutOfMemoryError ex) { + err = "out of memory on error stream"; + } + sendOutAndErr(out, err); + } + } + fireEndTestSuite(); + + // junitTest has the correct counts for JUnit4, while res doesn't + if (retCode != SUCCESS || junitTest.errorCount() != 0) { + retCode = ERRORS; + } else if (junitTest.failureCount() != 0) { + retCode = FAILURES; + } + } + + private static boolean containsTests(final Class<?> testClass, final boolean isJUnit4) { + Class testAnnotation = null; + Class suiteAnnotation = null; + Class runWithAnnotation = null; + + try { + testAnnotation = Class.forName("org.junit.Test"); + } catch (final ClassNotFoundException e) { + if (isJUnit4) { + // odd - we think we're JUnit4 but don't support the test annotation. We therefore can't have any tests! + return false; + } + // else... we're a JUnit3 test and don't need the annotation + } + + try { + suiteAnnotation = Class.forName("org.junit.Suite.SuiteClasses"); + } catch(final ClassNotFoundException ex) { + // ignore - we don't have this annotation so make sure we don't check for it + } + try { + runWithAnnotation = Class.forName("org.junit.runner.RunWith"); + } catch(final ClassNotFoundException ex) { + // also ignore as this annotation doesn't exist so tests can't use it + } + + + if (!isJUnit4 && !TestCase.class.isAssignableFrom(testClass)) { + //a test we think is JUnit3 but does not extend TestCase. Can't really be a test. + return false; + } + + // check if we have any inner classes that contain suitable test methods + for (final Class<?> innerClass : testClass.getDeclaredClasses()) { + if (containsTests(innerClass, isJUnit4) || containsTests(innerClass, !isJUnit4)) { + return true; + } + } + + if (Modifier.isAbstract(testClass.getModifiers()) || Modifier.isInterface(testClass.getModifiers())) { + // can't instantiate class and no inner classes are tests either + return false; + } + + if (isJUnit4) { + if (suiteAnnotation != null && testClass.getAnnotation(suiteAnnotation) != null) { + // class is marked as a suite. Let JUnit try and work its magic on it. + return true; + } + if (runWithAnnotation != null && testClass.getAnnotation(runWithAnnotation) != null) { + /* Class is marked with @RunWith. If this class is badly written (no test methods, multiple + * constructors, private constructor etc) then the class is automatically run and fails in the + * IDEs I've tried... so I'm happy handing the class to JUnit to try and run, and let JUnit + * report a failure if a bad test case is provided. Trying to do anything else is likely to + * result in us filtering out cases that could be valid for future versions of JUnit so would + * just increase future maintenance work. + */ + return true; + } + } + + for (final Method m : testClass.getMethods()) { + if (isJUnit4) { + // check if suspected JUnit4 classes have methods with @Test annotation + if (m.getAnnotation(testAnnotation) != null) { + return true; + } + } else { + // check if JUnit3 class have public or protected no-args methods starting with names starting with test + if (m.getName().startsWith("test") && m.getParameterTypes().length == 0 + && (Modifier.isProtected(m.getModifiers()) || Modifier.isPublic(m.getModifiers()))) { + return true; + } + } + // check if JUnit3 or JUnit4 test have a public or protected, static, + // no-args 'suite' method + if (m.getName().equals("suite") && m.getParameterTypes().length == 0 + && (Modifier.isProtected(m.getModifiers()) || Modifier.isPublic(m.getModifiers())) + && Modifier.isStatic(m.getModifiers())) { + return true; + } + } + + // no test methods found + return false; + } + + /** + * Returns what System.exit() would return in the standalone version. + * + * @return 2 if errors occurred, 1 if tests failed else 0. + */ + public int getRetCode() { + return retCode; + } + + /** + * Interface TestListener. + * + * <p>A new Test is started. + * @param t the test. + */ + public void startTest(final Test t) { + final String testName = JUnitVersionHelper.getTestCaseName(t); + logTestListenerEvent("startTest(" + testName + ")"); + } + + /** + * Interface TestListener. + * + * <p>A Test is finished. + * @param test the test. + */ + public void endTest(final Test test) { + final String testName = JUnitVersionHelper.getTestCaseName(test); + logTestListenerEvent("endTest(" + testName + ")"); + } + + private void logTestListenerEvent(String msg) { + if (logTestListenerEvents) { + final PrintStream out = savedOut != null ? savedOut : System.out; + out.flush(); + if (msg == null) { + msg = "null"; + } + final StringTokenizer msgLines = new StringTokenizer(msg, "\r\n", false); + while (msgLines.hasMoreTokens()) { + out.println(JUnitTask.TESTLISTENER_PREFIX + + msgLines.nextToken()); + } + out.flush(); + } + } + + /** + * Interface TestListener for JUnit <= 3.4. + * + * <p>A Test failed. + * @param test the test. + * @param t the exception thrown by the test. + */ + public void addFailure(final Test test, final Throwable t) { + final String testName = JUnitVersionHelper.getTestCaseName(test); + logTestListenerEvent("addFailure(" + testName + ", " + t.getMessage() + ")"); + if (haltOnFailure) { + res.stop(); + } + } + + /** + * Interface TestListener for JUnit > 3.4. + * + * <p>A Test failed. + * @param test the test. + * @param t the assertion thrown by the test. + */ + public void addFailure(final Test test, final AssertionFailedError t) { + addFailure(test, (Throwable) t); + } + + /** + * Interface TestListener. + * + * <p>An error occurred while running the test. + * @param test the test. + * @param t the error thrown by the test. + */ + public void addError(final Test test, final Throwable t) { + final String testName = JUnitVersionHelper.getTestCaseName(test); + logTestListenerEvent("addError(" + testName + ", " + t.getMessage() + ")"); + if (haltOnError) { + res.stop(); + } + } + + /** + * Permissions for the test run. + * @since Ant 1.6 + * @param permissions the permissions to use. + */ + public void setPermissions(final Permissions permissions) { + perm = permissions; + } + + /** + * Handle a string destined for standard output. + * @param output the string to output + */ + public void handleOutput(final String output) { + if (!logTestListenerEvents && output.startsWith(JUnitTask.TESTLISTENER_PREFIX)) { + // ignore + } else if (systemOut != null) { + systemOut.print(output); + } + } + + /** + * Handle input. + * @param buffer not used. + * @param offset not used. + * @param length not used. + * @return -1 always. + * @throws IOException never. + * @see org.apache.tools.ant.Task#handleInput(byte[], int, int) + * + * @since Ant 1.6 + */ + public int handleInput(final byte[] buffer, final int offset, final int length) + throws IOException { + return -1; + } + + /** {@inheritDoc}. */ + public void handleErrorOutput(final String output) { + if (systemError != null) { + systemError.print(output); + } + } + + /** {@inheritDoc}. */ + public void handleFlush(final String output) { + if (systemOut != null) { + systemOut.print(output); + } + } + + /** {@inheritDoc}. */ + public void handleErrorFlush(final String output) { + if (systemError != null) { + systemError.print(output); + } + } + + private void sendOutAndErr(final String out, final String err) { + final int size = formatters.size(); + for (int i = 0; i < size; i++) { + final JUnitResultFormatter formatter = + ((JUnitResultFormatter) formatters.elementAt(i)); + + formatter.setSystemOutput(out); + formatter.setSystemError(err); + } + } + + private void fireStartTestSuite() { + final int size = formatters.size(); + for (int i = 0; i < size; i++) { + ((JUnitResultFormatter) formatters.elementAt(i)) + .startTestSuite(junitTest); + } + } + + private void fireEndTestSuite() { + final int size = formatters.size(); + for (int i = 0; i < size; i++) { + ((JUnitResultFormatter) formatters.elementAt(i)) + .endTestSuite(junitTest); + } + } + + /** + * Add a formatter. + * @param f the formatter to add. + */ + public void addFormatter(final JUnitResultFormatter f) { + formatters.addElement(f); + } + + /** {@inheritDoc}. */ + public void addFormatter(final JUnitTaskMirror.JUnitResultFormatterMirror f) { + formatters.addElement(f); + } + + /** + * Entry point for standalone (forked) mode. + * + * Parameters: testcaseclassname plus parameters in the format + * key=value, none of which is required. + * + * <table cols="4" border="1"> + * <tr><th>key</th><th>description</th><th>default value</th></tr> + * + * <tr><td>haltOnError</td><td>halt test on + * errors?</td><td>false</td></tr> + * + * <tr><td>haltOnFailure</td><td>halt test on + * failures?</td><td>false</td></tr> + * + * <tr><td>formatter</td><td>A JUnitResultFormatter given as + * classname,filename. If filename is omitted, System.out is + * assumed.</td><td>none</td></tr> + * + * <tr><td>showoutput</td><td>send output to System.err/.out as + * well as to the formatters?</td><td>false</td></tr> + * + * <tr><td>logtestlistenerevents</td><td>log TestListener events to + * System.out.</td><td>false</td></tr> + * + * <tr><td>methods</td><td>Comma-separated list of names of individual + * test methods to execute. + * </td><td>null</td></tr> + * + * </table> + * @param args the command line arguments. + * @throws IOException on error. + */ + public static void main(final String[] args) throws IOException { + String[] methods = null; + boolean haltError = false; + boolean haltFail = false; + boolean stackfilter = true; + final Properties props = new Properties(); + boolean showOut = false; + boolean outputToFormat = true; + boolean logFailedTests = true; + boolean logTestListenerEvents = false; + boolean skipNonTests = false; + int antThreadID = 0; /* Ant id of thread running this unit test, 0 in single-threaded mode */ + + if (args.length == 0) { + System.err.println("required argument TestClassName missing"); + System.exit(ERRORS); + } + + if (args[0].startsWith(Constants.TESTSFILE)) { + multipleTests = true; + args[0] = args[0].substring(Constants.TESTSFILE.length()); + } + + for (int i = 1; i < args.length; i++) { + if (args[i].startsWith(Constants.METHOD_NAMES)) { + try { + final String methodsList = args[i].substring(Constants.METHOD_NAMES.length()); + methods = JUnitTest.parseTestMethodNamesList(methodsList); + } catch (final IllegalArgumentException ex) { + System.err.println("Invalid specification of test method names: " + args[i]); + System.exit(ERRORS); + } + } else if (args[i].startsWith(Constants.HALT_ON_ERROR)) { + haltError = Project.toBoolean(args[i].substring(Constants.HALT_ON_ERROR.length())); + } else if (args[i].startsWith(Constants.HALT_ON_FAILURE)) { + haltFail = Project.toBoolean(args[i].substring(Constants.HALT_ON_FAILURE.length())); + } else if (args[i].startsWith(Constants.FILTERTRACE)) { + stackfilter = Project.toBoolean(args[i].substring(Constants.FILTERTRACE.length())); + } else if (args[i].startsWith(Constants.CRASHFILE)) { + crashFile = args[i].substring(Constants.CRASHFILE.length()); + registerTestCase(Constants.BEFORE_FIRST_TEST); + } else if (args[i].startsWith(Constants.FORMATTER)) { + try { + createAndStoreFormatter(args[i].substring(Constants.FORMATTER.length())); + } catch (final BuildException be) { + System.err.println(be.getMessage()); + System.exit(ERRORS); + } + } else if (args[i].startsWith(Constants.PROPSFILE)) { + final FileInputStream in = new FileInputStream(args[i] + .substring(Constants.PROPSFILE.length())); + props.load(in); + in.close(); + } else if (args[i].startsWith(Constants.SHOWOUTPUT)) { + showOut = Project.toBoolean(args[i].substring(Constants.SHOWOUTPUT.length())); + } else if (args[i].startsWith(Constants.LOGTESTLISTENEREVENTS)) { + logTestListenerEvents = Project.toBoolean( + args[i].substring(Constants.LOGTESTLISTENEREVENTS.length())); + } else if (args[i].startsWith(Constants.OUTPUT_TO_FORMATTERS)) { + outputToFormat = Project.toBoolean( + args[i].substring(Constants.OUTPUT_TO_FORMATTERS.length())); + } else if (args[i].startsWith(Constants.LOG_FAILED_TESTS)) { + logFailedTests = Project.toBoolean( + args[i].substring(Constants.LOG_FAILED_TESTS.length())); + } else if (args[i].startsWith(Constants.SKIP_NON_TESTS)) { + skipNonTests = Project.toBoolean( + args[i].substring(Constants.SKIP_NON_TESTS.length())); + } else if (args[i].startsWith(Constants.THREADID)) { + antThreadID = Integer.parseInt(args[i].substring(Constants.THREADID.length())); + } + } + + // Add/overlay system properties on the properties from the Ant project + final Hashtable p = System.getProperties(); + for (final Enumeration e = p.keys(); e.hasMoreElements();) { + final Object key = e.nextElement(); + props.put(key, p.get(key)); + } + + int returnCode = SUCCESS; + if (multipleTests) { + try { + final java.io.BufferedReader reader = + new java.io.BufferedReader(new java.io.FileReader(args[0])); + String testCaseName; + String[] testMethodNames; + int code = 0; + boolean errorOccurred = false; + boolean failureOccurred = false; + String line = null; + while ((line = reader.readLine()) != null) { + final StringTokenizer st = new StringTokenizer(line, ","); + final String testListSpec = st.nextToken(); + final int colonIndex = testListSpec.indexOf(':'); + if (colonIndex == -1) { + testCaseName = testListSpec; + testMethodNames = null; + } else { + testCaseName = testListSpec.substring(0, colonIndex); + testMethodNames = JUnitTest.parseTestMethodNamesList( + testListSpec + .substring(colonIndex + 1) + .replace('+', ',')); + } + final JUnitTest t = new JUnitTest(testCaseName); + t.setTodir(new File(st.nextToken())); + t.setOutfile(st.nextToken()); + t.setProperties(props); + t.setSkipNonTests(skipNonTests); + t.setThread(antThreadID); + code = launch(t, testMethodNames, haltError, stackfilter, haltFail, + showOut, outputToFormat, + logTestListenerEvents); + errorOccurred = (code == ERRORS); + failureOccurred = (code != SUCCESS); + if (errorOccurred || failureOccurred) { + if ((errorOccurred && haltError) + || (failureOccurred && haltFail)) { + registerNonCrash(); + System.exit(code); + } else { + if (code > returnCode) { + returnCode = code; + } + if (logFailedTests) { + System.out.println("TEST " + t.getName() + + " FAILED"); + } + } + } + } + } catch (final IOException e) { + e.printStackTrace(); + } + } else { + final JUnitTest t = new JUnitTest(args[0]); + t.setThread(antThreadID); + t.setProperties(props); + t.setSkipNonTests(skipNonTests); + returnCode = launch( + t, methods, haltError, stackfilter, haltFail, + showOut, outputToFormat, logTestListenerEvents); + } + + registerNonCrash(); + System.exit(returnCode); + } + + private static Vector fromCmdLine = new Vector(); + + private static void transferFormatters(final JUnitTestRunner runner, + final JUnitTest test) { + runner.addFormatter(new JUnitResultFormatter() { + + public void startTestSuite(final JUnitTest suite) throws BuildException { + } + + public void endTestSuite(final JUnitTest suite) throws BuildException { + } + + public void setOutput(final OutputStream out) { + } + + public void setSystemOutput(final String out) { + } + + public void setSystemError(final String err) { + } + + public void addError(final Test arg0, final Throwable arg1) { + } + + public void addFailure(final Test arg0, final AssertionFailedError arg1) { + } + + public void endTest(final Test arg0) { + } + + public void startTest(final Test arg0) { + registerTestCase(JUnitVersionHelper.getTestCaseName(arg0)); + } + }); + final int size = fromCmdLine.size(); + for (int i = 0; i < size; i++) { + final FormatterElement fe = (FormatterElement) fromCmdLine.elementAt(i); + if (multipleTests && fe.getUseFile()) { + final File destFile = + new File(test.getTodir(), + test.getOutfile() + fe.getExtension()); + fe.setOutfile(destFile); + } + runner.addFormatter((JUnitResultFormatter) fe.createFormatter()); + } + } + + /** + * Line format is: formatter=<classname>(,<pathname>)? + */ + private static void createAndStoreFormatter(final String line) + throws BuildException { + final FormatterElement fe = new FormatterElement(); + final int pos = line.indexOf(','); + if (pos == -1) { + fe.setClassname(line); + fe.setUseFile(false); + } else { + fe.setClassname(line.substring(0, pos)); + fe.setUseFile(true); + if (!multipleTests) { + fe.setOutfile(new File(line.substring(pos + 1))); + } else { + final int fName = line.indexOf(IGNORED_FILE_NAME); + if (fName > -1) { + fe.setExtension(line + .substring(fName + + IGNORED_FILE_NAME.length())); + } + } + } + fromCmdLine.addElement(fe); + } + + /** + * Returns a filtered stack trace. + * This is ripped out of junit.runner.BaseTestRunner. + * @param t the exception to filter. + * @return the filtered stack trace. + */ + public static String getFilteredTrace(final Throwable t) { + final String trace = StringUtils.getStackTrace(t); + return JUnitTestRunner.filterStack(trace); + } + + /** + * Filters stack frames from internal JUnit and Ant classes + * @param stack the stack trace to filter. + * @return the filtered stack. + */ + public static String filterStack(final String stack) { + if (!filtertrace) { + return stack; + } + final StringWriter sw = new StringWriter(); + final BufferedWriter pw = new BufferedWriter(sw); + final StringReader sr = new StringReader(stack); + final BufferedReader br = new BufferedReader(sr); + + String line; + try { + boolean firstLine = true; + while ((line = br.readLine()) != null) { + if (firstLine || !filterLine(line)) { + pw.write(line); + pw.newLine(); + } + firstLine = false; + } + } catch (final Exception e) { + return stack; // return the stack unfiltered + } finally { + FileUtils.close(pw); + } + return sw.toString(); + } + + private static boolean filterLine(final String line) { + for (int i = 0; i < DEFAULT_TRACE_FILTERS.length; i++) { + if (line.indexOf(DEFAULT_TRACE_FILTERS[i]) != -1) { + return true; + } + } + return false; + } + + /** + * @since Ant 1.6.2 + */ + private static int launch(final JUnitTest t, final String[] methods, final boolean haltError, + final boolean stackfilter, final boolean haltFail, + final boolean showOut, final boolean outputToFormat, + final boolean logTestListenerEvents) { + final JUnitTestRunner runner = + new JUnitTestRunner(t, methods, haltError, stackfilter, haltFail, showOut, + logTestListenerEvents, null); + runner.forked = true; + runner.outputToFormatters = outputToFormat; + transferFormatters(runner, t); + + runner.run(); + return runner.getRetCode(); + } + + /** + * @since Ant 1.7 + */ + private static void registerNonCrash() + throws IOException { + if (crashFile != null) { + FileWriter out = null; + try { + out = new FileWriter(crashFile); + out.write(Constants.TERMINATED_SUCCESSFULLY + "\n"); + out.flush(); + } finally { + FileUtils.close(out); + } + } + } + + private static void registerTestCase(final String testCase) { + if (crashFile != null) { + try { + FileWriter out = null; + try { + out = new FileWriter(crashFile); + out.write(testCase + "\n"); + out.flush(); + } finally { + FileUtils.close(out); + } + } catch (final IOException e) { + // ignored. + } + } + } + + /** + * Modifies a TestListener when running JUnit 4: treats AssertionFailedError + * as a failure not an error. + * + * @since Ant 1.7 + */ + private TestListenerWrapper wrapListener(final TestListener testListener) { + return new TestListenerWrapper(testListener) { + @Override + public void addError(final Test test, final Throwable t) { + if (junit4 && t instanceof AssertionFailedError) { + // JUnit 4 does not distinguish between errors and failures + // even in the JUnit 3 adapter. + // So we need to help it a bit to retain compatibility for JUnit 3 tests. + testListener.addFailure(test, (AssertionFailedError) t); + } else if (junit4 && t instanceof AssertionError) { + // Not strictly necessary but probably desirable. + // JUnit 4-specific test GUIs will show just "failures". + // But Ant's output shows "failures" vs. "errors". + // We would prefer to show "failure" for things that logically are. + final String msg = t.getMessage(); + final AssertionFailedError failure = msg != null + ? new AssertionFailedError(msg) : new AssertionFailedError(); + failure.setStackTrace(t.getStackTrace()); + testListener.addFailure(test, failure); + } else { + testListener.addError(test, t); + } + } + @Override + public void addFailure(final Test test, final AssertionFailedError t) { + testListener.addFailure(test, t); + } + public void addFailure(final Test test, final Throwable t) { // pre-3.4 + if (t instanceof AssertionFailedError) { + testListener.addFailure(test, (AssertionFailedError) t); + } else { + testListener.addError(test, t); + } + } + @Override + public void endTest(final Test test) { + testListener.endTest(test); + } + @Override + public void startTest(final Test test) { + testListener.startTest(test); + } + }; + } + + /** + * Use instead of TestResult.get{Failure,Error}Count on JUnit 4, + * since the adapter claims that all failures are errors. + * @since Ant 1.7 + */ + private int[] findJUnit4FailureErrorCount(final TestResult result) { + int failures = 0; + int errors = 0; + Enumeration e = result.failures(); + while (e.hasMoreElements()) { + e.nextElement(); + failures++; + } + e = result.errors(); + while (e.hasMoreElements()) { + final Throwable t = ((TestFailure) e.nextElement()).thrownException(); + if (t instanceof AssertionFailedError + || t instanceof AssertionError) { + failures++; + } else { + errors++; + } + } + return new int[] {failures, errors}; + } + +} // JUnitTestRunner diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java new file mode 100644 index 00000000..9a21caee --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java @@ -0,0 +1,179 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import junit.framework.Test; +import junit.framework.TestCase; + +/** + * Work around for some changes to the public JUnit API between + * different JUnit releases. + * @since Ant 1.7 + */ +// CheckStyle:HideUtilityClassConstructorCheck OFF (bc) +public class JUnitVersionHelper { + + private static Method testCaseName = null; + + /** + * Name of the JUnit4 class we look for. + * {@value} + * @since Ant 1.7.1 + */ + public static final String JUNIT_FRAMEWORK_JUNIT4_TEST_CASE_FACADE + = "junit.framework.JUnit4TestCaseFacade"; + private static final String UNKNOWN_TEST_CASE_NAME = "unknown"; + + static { + try { + testCaseName = TestCase.class.getMethod("getName", new Class[0]); + } catch (NoSuchMethodException e) { + // pre JUnit 3.7 + try { + testCaseName = TestCase.class.getMethod("name", new Class[0]); + } catch (NoSuchMethodException ignored) { + // ignore + } + } + } + + /** + * JUnit 3.7 introduces TestCase.getName() and subsequent versions + * of JUnit remove the old name() method. This method provides + * access to the name of a TestCase via reflection that is + * supposed to work with version before and after JUnit 3.7. + * + * <p>since Ant 1.5.1 this method will invoke "<code>public + * String getName()</code>" on any implementation of Test if + * it exists.</p> + * + * <p>Since Ant 1.7 also checks for JUnit4TestCaseFacade explicitly. + * This is used by junit.framework.JUnit4TestAdapter.</p> + * @param t the test. + * @return the name of the test. + */ + public static String getTestCaseName(Test t) { + if (t == null) { + return UNKNOWN_TEST_CASE_NAME; + } + if (t.getClass().getName().equals(JUNIT_FRAMEWORK_JUNIT4_TEST_CASE_FACADE)) { + // Self-describing as of JUnit 4 (#38811). But trim "(ClassName)". + String name = t.toString(); + if (name.endsWith(")")) { + int paren = name.lastIndexOf('('); + return name.substring(0, paren); + } else { + return name; + } + } + if (t instanceof TestCase && testCaseName != null) { + try { + return (String) testCaseName.invoke(t, new Object[0]); + } catch (Throwable ignored) { + // ignore + } + } else { + try { + Method getNameMethod = null; + try { + getNameMethod = + t.getClass().getMethod("getName", new Class [0]); + } catch (NoSuchMethodException e) { + getNameMethod = t.getClass().getMethod("name", + new Class [0]); + } + if (getNameMethod != null + && getNameMethod.getReturnType() == String.class) { + return (String) getNameMethod.invoke(t, new Object[0]); + } + } catch (Throwable ignored) { + // ignore + } + } + return UNKNOWN_TEST_CASE_NAME; + } + + /** + * Tries to find the name of the class which a test represents + * across JUnit 3 and 4. For JUnit4 it parses the toString() value of the + * test, and extracts it from there. + * @since Ant 1.7.1 (it was private until then) + * @param test test case to look at + * @return the extracted class name. + */ + public static String getTestCaseClassName(Test test) { + String className = test.getClass().getName(); + if (test instanceof JUnitTaskMirrorImpl.VmExitErrorTest) { + className = ((JUnitTaskMirrorImpl.VmExitErrorTest) test).getClassName(); + } else + if (className.equals(JUNIT_FRAMEWORK_JUNIT4_TEST_CASE_FACADE)) { + // JUnit 4 wraps solo tests this way. We can extract + // the original test name with a little hack. + String name = test.toString(); + int paren = name.lastIndexOf('('); + if (paren != -1 && name.endsWith(")")) { + className = name.substring(paren + 1, name.length() - 1); + } + } + return className; + } + + public static String getIgnoreMessage(Test test) { + String message = null; + + try { + Class<?> junit4FacadeClass = Class.forName("junit.framework.JUnit4TestCaseFacade"); + if (test != null && test.getClass().isAssignableFrom(junit4FacadeClass)) { + //try and get the message coded as part of the ignore + /* + * org.junit.runner.Description contains a getAnnotation(Class) method... but this + * wasn't in older versions of JUnit4 so we have to try and do this by reflection + */ + Class<?> testClass = Class.forName(JUnitVersionHelper.getTestCaseClassName(test)); + + Method testMethod = testClass.getMethod(JUnitVersionHelper.getTestCaseName(test)); + Class ignoreAnnotation = Class.forName("org.junit.Ignore"); + Annotation annotation = testMethod.getAnnotation(ignoreAnnotation); + if (annotation != null) { + Method valueMethod = annotation.getClass().getMethod("value"); + String value = (String) valueMethod.invoke(annotation); + if (value != null && value.length() > 0) { + message = value; + } + } + + } + } catch (NoSuchMethodException e) { + // silently ignore - we'll report a skip with no message + } catch (ClassNotFoundException e) { + // silently ignore - we'll report a skip with no message + } catch (InvocationTargetException e) { + // silently ignore - we'll report a skip with no message + } catch (IllegalAccessException e) { + // silently ignore - we'll report a skip with no message + } + return message; + + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/OutErrSummaryJUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/OutErrSummaryJUnitResultFormatter.java new file mode 100644 index 00000000..e0dc16c3 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/OutErrSummaryJUnitResultFormatter.java @@ -0,0 +1,36 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +/** + * Used instead of SummaryJUnitResultFormatter in forked tests if + * withOutAndErr is requested. + */ + +public class OutErrSummaryJUnitResultFormatter + extends SummaryJUnitResultFormatter { + + /** + * Empty + */ + public OutErrSummaryJUnitResultFormatter() { + super(); + setWithOutAndErr(true); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java new file mode 100644 index 00000000..3386ee50 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java @@ -0,0 +1,317 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringWriter; +import java.text.NumberFormat; +import java.util.Hashtable; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.StringUtils; + + +/** + * Prints plain text output of the test to a specified Writer. + * + */ + +public class PlainJUnitResultFormatter implements JUnitResultFormatter, IgnoredTestListener { + + private static final double ONE_SECOND = 1000.0; + + /** + * Formatter for timings. + */ + private NumberFormat nf = NumberFormat.getInstance(); + /** + * Timing helper. + */ + private Hashtable testStarts = new Hashtable(); + /** + * Where to write the log to. + */ + private OutputStream out; + /** + * Helper to store intermediate output. + */ + private StringWriter inner; + /** + * Convenience layer on top of {@link #inner inner}. + */ + private BufferedWriter wri; + /** + * Suppress endTest if testcase failed. + */ + private Hashtable failed = new Hashtable(); + + private String systemOutput = null; + private String systemError = null; + + /** No arg constructor */ + public PlainJUnitResultFormatter() { + inner = new StringWriter(); + wri = new BufferedWriter(inner); + } + + /** {@inheritDoc}. */ + public void setOutput(OutputStream out) { + this.out = out; + } + + /** {@inheritDoc}. */ + public void setSystemOutput(String out) { + systemOutput = out; + } + + /** {@inheritDoc}. */ + public void setSystemError(String err) { + systemError = err; + } + + /** + * The whole testsuite started. + * @param suite the test suite + * @throws BuildException if unable to write the output + */ + public void startTestSuite(JUnitTest suite) throws BuildException { + if (out == null) { + return; // Quick return - no output do nothing. + } + StringBuffer sb = new StringBuffer("Testsuite: "); + sb.append(suite.getName()); + sb.append(StringUtils.LINE_SEP); + try { + out.write(sb.toString().getBytes()); + out.flush(); + } catch (IOException ex) { + throw new BuildException("Unable to write output", ex); + } + } + + /** + * The whole testsuite ended. + * @param suite the test suite + * @throws BuildException if unable to write the output + */ + public void endTestSuite(JUnitTest suite) throws BuildException { + try { + StringBuffer sb = new StringBuffer("Tests run: "); + sb.append(suite.runCount()); + sb.append(", Failures: "); + sb.append(suite.failureCount()); + sb.append(", Errors: "); + sb.append(suite.errorCount()); + sb.append(", Skipped: "); + sb.append(suite.skipCount()); + sb.append(", Time elapsed: "); + sb.append(nf.format(suite.getRunTime() / ONE_SECOND)); + sb.append(" sec"); + sb.append(StringUtils.LINE_SEP); + write(sb.toString()); + + // write the err and output streams to the log + if (systemOutput != null && systemOutput.length() > 0) { + write("------------- Standard Output ---------------"); + write(StringUtils.LINE_SEP); + write(systemOutput); + write("------------- ---------------- ---------------"); + write(StringUtils.LINE_SEP); + } + + if (systemError != null && systemError.length() > 0) { + write("------------- Standard Error -----------------"); + write(StringUtils.LINE_SEP); + write(systemError); + write("------------- ---------------- ---------------"); + write(StringUtils.LINE_SEP); + } + + write(StringUtils.LINE_SEP); + if (out != null) { + try { + wri.flush(); + write(inner.toString()); + } catch (IOException ioex) { + throw new BuildException("Unable to write output", ioex); + } + } + } finally { + if (out != null) { + try { + wri.close(); + } catch (IOException ioex) { + throw new BuildException("Unable to flush output", ioex); + } finally { + if (out != System.out && out != System.err) { + FileUtils.close(out); + } + wri = null; + out = null; + } + } + } + } + + /** + * Interface TestListener. + * + * <p>A new Test is started. + * @param t the test. + */ + public void startTest(Test t) { + testStarts.put(t, new Long(System.currentTimeMillis())); + failed.put(t, Boolean.FALSE); + } + + /** + * Interface TestListener. + * + * <p>A Test is finished. + * @param test the test. + */ + public void endTest(Test test) { + if (Boolean.TRUE.equals(failed.get(test))) { + return; + } + synchronized (wri) { + try { + wri.write("Testcase: " + + JUnitVersionHelper.getTestCaseName(test)); + Long l = (Long) testStarts.get(test); + double seconds = 0; + // can be null if an error occurred in setUp + if (l != null) { + seconds = + (System.currentTimeMillis() - l.longValue()) / ONE_SECOND; + } + + wri.write(" took " + nf.format(seconds) + " sec"); + wri.newLine(); + } catch (IOException ex) { + throw new BuildException(ex); + } + } + } + + /** + * Interface TestListener for JUnit <= 3.4. + * + * <p>A Test failed. + * @param test the test. + * @param t the exception. + */ + public void addFailure(Test test, Throwable t) { + formatError("\tFAILED", test, t); + } + + /** + * Interface TestListener for JUnit > 3.4. + * + * <p>A Test failed. + * @param test the test. + * @param t the assertion that failed. + */ + public void addFailure(Test test, AssertionFailedError t) { + addFailure(test, (Throwable) t); + } + + /** + * Interface TestListener. + * + * <p>An error occurred while running the test. + * @param test the test. + * @param t the exception. + */ + public void addError(Test test, Throwable t) { + formatError("\tCaused an ERROR", test, t); + } + + private void formatError(String type, Test test, Throwable t) { + synchronized (wri) { + if (test != null) { + endTest(test); + failed.put(test, Boolean.TRUE); + } + + try { + wri.write(type); + wri.newLine(); + wri.write(String.valueOf(t.getMessage())); + wri.newLine(); + String strace = JUnitTestRunner.getFilteredTrace(t); + wri.write(strace); + wri.newLine(); + } catch (IOException ex) { + throw new BuildException(ex); + } + } + } + + public void testIgnored(Test test) { + formatSkip(test, JUnitVersionHelper.getIgnoreMessage(test)); + } + + + public void formatSkip(Test test, String message) { + if (test != null) { + endTest(test); + } + + try { + wri.write("\tSKIPPED"); + if (message != null) { + wri.write(": "); + wri.write(message); + } + wri.newLine(); + } catch (IOException ex) { + throw new BuildException(ex); + } + + } + + public void testAssumptionFailure(Test test, Throwable throwable) { + formatSkip(test, throwable.getMessage()); + } + + /** + * Print out some text, and flush the output stream; encoding in the platform + * local default encoding. + * @param text text to write. + * @throws BuildException on IO Problems. + */ + private void write(String text) { + if (out == null) { + return; + } + try { + out.write(text.getBytes()); + out.flush(); + } catch (IOException ex) { + throw new BuildException("Unable to write output " + ex, ex); + } + } +} // PlainJUnitResultFormatter diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java new file mode 100644 index 00000000..0b09fa20 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java @@ -0,0 +1,213 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.io.IOException; +import java.io.OutputStream; +import java.text.NumberFormat; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; + +import org.apache.tools.ant.BuildException; + +/** + * Prints short summary output of the test to Ant's logging system. + * + */ + +public class SummaryJUnitResultFormatter + implements JUnitResultFormatter, JUnitTaskMirror.SummaryJUnitResultFormatterMirror { + + private static final double ONE_SECOND = 1000.0; + + /** + * Formatter for timings. + */ + private NumberFormat nf = NumberFormat.getInstance(); + /** + * OutputStream to write to. + */ + private OutputStream out; + + private boolean withOutAndErr = false; + private String systemOutput = null; + private String systemError = null; + + /** + * Empty + */ + public SummaryJUnitResultFormatter() { + } + + /** + * Insures that a line of log output is written and flushed as a single + * operation, to prevent lines from being spliced into other lines. + * (Hopefully this solves the issue of run on lines - + * [junit] Tests Run: 2 Failures: 2 [junit] Tests run: 5... + * synchronized doesn't seem to be to harsh a penalty since it only + * occurs twice per test - at the beginning and end. Note that message + * construction occurs outside the locked block. + * + * @param b data to be written as an unbroken block + */ + private synchronized void writeOutputLine(byte[] b) { + try { + out.write(b); + out.flush(); + } catch (IOException ioex) { + throw new BuildException("Unable to write summary output", ioex); + } + } + + /** + * The testsuite started. + * @param suite the testsuite. + */ + public void startTestSuite(JUnitTest suite) { + String newLine = System.getProperty("line.separator"); + StringBuffer sb = new StringBuffer("Running "); + int antThreadID = suite.getThread(); + + sb.append(suite.getName()); + /* only write thread id in multi-thread mode so default old way doesn't change output */ + if (antThreadID > 0) { + sb.append(" in thread "); + sb.append(antThreadID); + } + sb.append(newLine); + writeOutputLine(sb.toString().getBytes()); + } + /** + * Empty + * @param t not used. + */ + public void startTest(Test t) { + } + /** + * Empty + * @param test not used. + */ + public void endTest(Test test) { + } + /** + * Empty + * @param test not used. + * @param t not used. + */ + public void addFailure(Test test, Throwable t) { + } + /** + * Interface TestListener for JUnit > 3.4. + * + * <p>A Test failed. + * @param test not used. + * @param t not used. + */ + public void addFailure(Test test, AssertionFailedError t) { + addFailure(test, (Throwable) t); + } + /** + * Empty + * @param test not used. + * @param t not used. + */ + public void addError(Test test, Throwable t) { + } + + /** {@inheritDoc}. */ + public void setOutput(OutputStream out) { + this.out = out; + } + + /** {@inheritDoc}. */ + public void setSystemOutput(String out) { + systemOutput = out; + } + + /** {@inheritDoc}. */ + public void setSystemError(String err) { + systemError = err; + } + + /** + * Should the output to System.out and System.err be written to + * the summary. + * @param value if true write System.out and System.err to the summary. + */ + public void setWithOutAndErr(boolean value) { + withOutAndErr = value; + } + + /** + * The whole testsuite ended. + * @param suite the testsuite. + * @throws BuildException if there is an error. + */ + public void endTestSuite(JUnitTest suite) throws BuildException { + String newLine = System.getProperty("line.separator"); + StringBuffer sb = new StringBuffer("Tests run: "); + sb.append(suite.runCount()); + sb.append(", Failures: "); + sb.append(suite.failureCount()); + sb.append(", Errors: "); + sb.append(suite.errorCount()); + sb.append(", Skipped: "); + sb.append(suite.skipCount()); + sb.append(", Time elapsed: "); + sb.append(nf.format(suite.getRunTime() / ONE_SECOND)); + sb.append(" sec"); + + /* class name needed with multi-threaded execution because + results line may not appear immediately below start line. + only write thread id, class name in multi-thread mode so + the line still looks as much like the old line as possible. */ + if (suite.getThread() > 0) { + sb.append(", Thread: "); + sb.append(suite.getThread()); + sb.append(", Class: "); + sb.append(suite.getName()); + } + sb.append(newLine); + + if (withOutAndErr) { + if (systemOutput != null && systemOutput.length() > 0) { + sb.append("Output:").append(newLine).append(systemOutput) + .append(newLine); + } + + if (systemError != null && systemError.length() > 0) { + sb.append("Error: ").append(newLine).append(systemError) + .append(newLine); + } + } + + try { + writeOutputLine(sb.toString().getBytes()); + } finally { + if (out != System.out && out != System.err) { + try { + out.close(); + } catch (IOException e) { + // ignore + } + } + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TearDownOnVmCrash.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TearDownOnVmCrash.java new file mode 100644 index 00000000..e381a70c --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TearDownOnVmCrash.java @@ -0,0 +1,144 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; + +/** + * Formatter that doesn't create any output but tries to invoke the + * tearDown method on a testcase if that test was forked and caused a + * timeout or VM crash. + * + * <p>This formatter has some limitations, for details see the + * <junit> task's manual.</p> + * + * @since Ant 1.8.0 + */ +public class TearDownOnVmCrash implements JUnitResultFormatter { + + private String suiteName; + + /** + * Records the suite's name to later determine the class to invoke + * tearDown on. + */ + public void startTestSuite(final JUnitTest suite) { + suiteName = suite.getName(); + if (suiteName != null && + suiteName.endsWith(JUnitTask.NAME_OF_DUMMY_TEST)) { + // no way to know which class caused the timeout + suiteName = null; + } + } + + /** + * Only invoke tearDown if the suite is known and not the dummy + * test we get when a Batch fails and the error is an actual + * error generated by Ant. + */ + public void addError(final Test fakeTest, final Throwable t) { + if (suiteName != null + && fakeTest instanceof JUnitTaskMirrorImpl.VmExitErrorTest) { + tearDown(); + } + } + + // no need to implement the rest + public void addFailure(Test test, Throwable t) {} + + public void addFailure(Test test, AssertionFailedError t) {} + + public void startTest(Test test) {} + + public void endTest(Test test) {} + + public void endTestSuite(JUnitTest suite) {} + + public void setOutput(OutputStream out) {} + + public void setSystemOutput(String out) {} + + public void setSystemError(String err) {} + + private void tearDown() { + try { + // first try to load the class and let's hope it is on our + // classpath + Class testClass = null; + if (Thread.currentThread().getContextClassLoader() != null) { + try { + testClass = Thread.currentThread().getContextClassLoader() + .loadClass(suiteName); + } catch (ClassNotFoundException cnfe) { + // ignore + } + } + if (testClass == null && getClass().getClassLoader() != null) { + try { + testClass = + getClass().getClassLoader().loadClass(suiteName); + } catch (ClassNotFoundException cnfe) { + // ignore + } + } + if (testClass == null) { + // fall back to system classloader + testClass = Class.forName(suiteName); + } + + // if the test has a suite method, then we can't know + // which test of the executed suite timed out, ignore it + try { + // check if there is a suite method + testClass.getMethod("suite", new Class[0]); + return; + } catch (NoSuchMethodException e) { + // no suite method + } + + // a loadable class and no suite method + // no reason to check for JUnit 4 since JUnit4TestAdapter + // doesn't have any tearDown method. + + try { + Method td = testClass.getMethod("tearDown", new Class[0]); + if (td.getReturnType() == Void.TYPE) { + td.invoke(testClass.newInstance(), new Object[0]); + } + } catch (NoSuchMethodException nsme) { + // no tearDown, fine + } + + } catch (ClassNotFoundException cnfe) { + // class probably is not in our classpath, there is + // nothing we can do + } catch (InvocationTargetException ite) { + System.err.println("Caught an exception while trying to invoke" + + " tearDown: " + ite.getMessage()); + } catch (Throwable t) { + System.err.println("Caught an exception while trying to invoke" + + " tearDown: " + t.getMessage()); + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestIgnored.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestIgnored.java new file mode 100644 index 00000000..79a88785 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestIgnored.java @@ -0,0 +1,35 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import junit.framework.Test; + +public class TestIgnored { + + private Test test; + + public TestIgnored(Test test) { + this.test = test; + } + + public Test getTest() { + return test; + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestListenerWrapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestListenerWrapper.java new file mode 100644 index 00000000..692e4fc9 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestListenerWrapper.java @@ -0,0 +1,63 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestListener; + + +public class TestListenerWrapper implements TestListener, IgnoredTestListener { + + private TestListener wrapped; + + public TestListenerWrapper(TestListener listener) { + super(); + wrapped = listener; + } + + public void addError(Test test, Throwable throwable) { + wrapped.addError(test, throwable); + } + + public void addFailure(Test test, AssertionFailedError assertionFailedError) { + wrapped.addFailure(test, assertionFailedError); + } + + public void endTest(Test test) { + wrapped.endTest(test); + } + + public void startTest(Test test) { + wrapped.startTest(test); + } + + public void testIgnored(Test test) { + if (wrapped instanceof IgnoredTestListener) { + ((IgnoredTestListener)wrapped).testIgnored(test); + } + } + + public void testAssumptionFailure(Test test, Throwable throwable) { + if (wrapped instanceof IgnoredTestListener) { + ((IgnoredTestListener)wrapped).testAssumptionFailure(test, throwable); + } + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLConstants.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLConstants.java new file mode 100644 index 00000000..03760cc9 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLConstants.java @@ -0,0 +1,141 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +/** + * <p> Interface groups XML constants. + * Interface that groups all constants used throughout the <tt>XML</tt> + * documents that are generated by the <tt>XMLJUnitResultFormatter</tt>. + * <p> + * As of now the DTD is: + * <code><pre> + * <!ELEMENT testsuites (testsuite*)> + * + * <!ELEMENT testsuite (properties, testcase*, + * failure?, error?, + * system-out?, system-err?)> + * <!ATTLIST testsuite name CDATA #REQUIRED> + * <!ATTLIST testsuite tests CDATA #REQUIRED> + * <!ATTLIST testsuite failures CDATA #REQUIRED> + * <!ATTLIST testsuite errors CDATA #REQUIRED> + * <!ATTLIST testsuite time CDATA #REQUIRED> + * <!ATTLIST testsuite package CDATA #IMPLIED> + * <!ATTLIST testsuite id CDATA #IMPLIED> + * + * + * <!ELEMENT properties (property*)> + * + * <!ELEMENT property EMPTY> + * <!ATTLIST property name CDATA #REQUIRED> + * <!ATTLIST property value CDATA #REQUIRED> + * + * <!ELEMENT testcase (failure?, error?)> + * <!ATTLIST testcase name CDATA #REQUIRED> + * <!ATTLIST testcase classname CDATA #IMPLIED> + * <!ATTLIST testcase time CDATA #REQUIRED> + * + * <!ELEMENT failure (#PCDATA)> + * <!ATTLIST failure message CDATA #IMPLIED> + * <!ATTLIST failure type CDATA #REQUIRED> + * + * <!ELEMENT error (#PCDATA)> + * <!ATTLIST error message CDATA #IMPLIED> + * <!ATTLIST error type CDATA #REQUIRED> + * + * <!ELEMENT system-err (#PCDATA)> + * + * <!ELEMENT system-out (#PCDATA)> + * + * </pre></code> + * @see XMLJUnitResultFormatter + * @see XMLResultAggregator + */ +// CheckStyle:InterfaceIsTypeCheck OFF (bc) +public interface XMLConstants { + /** the testsuites element for the aggregate document */ + String TESTSUITES = "testsuites"; + + /** the testsuite element */ + String TESTSUITE = "testsuite"; + + /** the testcase element */ + String TESTCASE = "testcase"; + + /** the error element */ + String ERROR = "error"; + + /** the failure element */ + String FAILURE = "failure"; + + /** the system-err element */ + String SYSTEM_ERR = "system-err"; + + /** the system-out element */ + String SYSTEM_OUT = "system-out"; + + /** package attribute for the aggregate document */ + String ATTR_PACKAGE = "package"; + + /** name attribute for property, testcase and testsuite elements */ + String ATTR_NAME = "name"; + + /** time attribute for testcase and testsuite elements */ + String ATTR_TIME = "time"; + + /** errors attribute for testsuite elements */ + String ATTR_ERRORS = "errors"; + + /** failures attribute for testsuite elements */ + String ATTR_FAILURES = "failures"; + + /** tests attribute for testsuite elements */ + String ATTR_TESTS = "tests"; + + String ATTR_SKIPPED = "skipped"; + + /** type attribute for failure and error elements */ + String ATTR_TYPE = "type"; + + /** message attribute for failure elements */ + String ATTR_MESSAGE = "message"; + + /** the properties element */ + String PROPERTIES = "properties"; + + /** the property element */ + String PROPERTY = "property"; + + /** value attribute for property elements */ + String ATTR_VALUE = "value"; + + /** classname attribute for testcase elements */ + String ATTR_CLASSNAME = "classname"; + + /** id attribute */ + String ATTR_ID = "id"; + + /** + * timestamp of test cases + */ + String TIMESTAMP = "timestamp"; + + /** + * name of host running the tests + */ + String HOSTNAME = "hostname"; +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java new file mode 100644 index 00000000..416c10d4 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java @@ -0,0 +1,366 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Date; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Properties; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.util.DOMElementWriter; +import org.apache.tools.ant.util.DateUtils; +import org.apache.tools.ant.util.FileUtils; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Text; + + +/** + * Prints XML output of the test to a specified Writer. + * + * @see FormatterElement + */ + +public class XMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstants, IgnoredTestListener { + + private static final double ONE_SECOND = 1000.0; + + /** constant for unnnamed testsuites/cases */ + private static final String UNKNOWN = "unknown"; + + private static DocumentBuilder getDocumentBuilder() { + try { + return DocumentBuilderFactory.newInstance().newDocumentBuilder(); + } catch (final Exception exc) { + throw new ExceptionInInitializerError(exc); + } + } + + /** + * The XML document. + */ + private Document doc; + + /** + * The wrapper for the whole testsuite. + */ + private Element rootElement; + + /** + * Element for the current test. + * + * The keying of this map is a bit of a hack: tests are keyed by caseName(className) since + * the Test we get for Test-start isn't the same as the Test we get during test-assumption-fail, + * so we can't easily match Test objects without manually iterating over all keys and checking + * individual fields. + */ + private final Hashtable<String, Element> testElements = new Hashtable<String, Element>(); + + /** + * tests that failed. + */ + private final Hashtable failedTests = new Hashtable(); + + /** + * Tests that were skipped. + */ + private final Hashtable<String, Test> skippedTests = new Hashtable<String, Test>(); + /** + * Tests that were ignored. See the note above about the key being a bit of a hack. + */ + private final Hashtable<String, Test> ignoredTests = new Hashtable<String, Test>(); + /** + * Timing helper. + */ + private final Hashtable<String, Long> testStarts = new Hashtable<String, Long>(); + /** + * Where to write the log to. + */ + private OutputStream out; + + /** No arg constructor. */ + public XMLJUnitResultFormatter() { + } + + /** {@inheritDoc}. */ + public void setOutput(final OutputStream out) { + this.out = out; + } + + /** {@inheritDoc}. */ + public void setSystemOutput(final String out) { + formatOutput(SYSTEM_OUT, out); + } + + /** {@inheritDoc}. */ + public void setSystemError(final String out) { + formatOutput(SYSTEM_ERR, out); + } + + /** + * The whole testsuite started. + * @param suite the testsuite. + */ + public void startTestSuite(final JUnitTest suite) { + doc = getDocumentBuilder().newDocument(); + rootElement = doc.createElement(TESTSUITE); + final String n = suite.getName(); + rootElement.setAttribute(ATTR_NAME, n == null ? UNKNOWN : n); + + //add the timestamp + final String timestamp = DateUtils.format(new Date(), + DateUtils.ISO8601_DATETIME_PATTERN); + rootElement.setAttribute(TIMESTAMP, timestamp); + //and the hostname. + rootElement.setAttribute(HOSTNAME, getHostname()); + + // Output properties + final Element propsElement = doc.createElement(PROPERTIES); + rootElement.appendChild(propsElement); + final Properties props = suite.getProperties(); + if (props != null) { + final Enumeration e = props.propertyNames(); + while (e.hasMoreElements()) { + final String name = (String) e.nextElement(); + final Element propElement = doc.createElement(PROPERTY); + propElement.setAttribute(ATTR_NAME, name); + propElement.setAttribute(ATTR_VALUE, props.getProperty(name)); + propsElement.appendChild(propElement); + } + } + } + + /** + * get the local hostname + * @return the name of the local host, or "localhost" if we cannot work it out + */ + private String getHostname() { + String hostname = "localhost"; + try { + final InetAddress localHost = InetAddress.getLocalHost(); + if (localHost != null) { + hostname = localHost.getHostName(); + } + } catch (final UnknownHostException e) { + // fall back to default 'localhost' + } + return hostname; + } + + /** + * The whole testsuite ended. + * @param suite the testsuite. + * @throws BuildException on error. + */ + public void endTestSuite(final JUnitTest suite) throws BuildException { + rootElement.setAttribute(ATTR_TESTS, "" + suite.runCount()); + rootElement.setAttribute(ATTR_FAILURES, "" + suite.failureCount()); + rootElement.setAttribute(ATTR_ERRORS, "" + suite.errorCount()); + rootElement.setAttribute(ATTR_SKIPPED, "" + suite.skipCount()); + rootElement.setAttribute( + ATTR_TIME, "" + (suite.getRunTime() / ONE_SECOND)); + if (out != null) { + Writer wri = null; + try { + wri = new BufferedWriter(new OutputStreamWriter(out, "UTF8")); + wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); + (new DOMElementWriter()).write(rootElement, wri, 0, " "); + } catch (final IOException exc) { + throw new BuildException("Unable to write log file", exc); + } finally { + if (wri != null) { + try { + wri.flush(); + } catch (final IOException ex) { + // ignore + } + } + if (out != System.out && out != System.err) { + FileUtils.close(wri); + } + } + } + } + + /** + * Interface TestListener. + * + * <p>A new Test is started. + * @param t the test. + */ + public void startTest(final Test t) { + testStarts.put(createDescription(t), System.currentTimeMillis()); + } + + private static String createDescription(final Test test) throws BuildException { + return JUnitVersionHelper.getTestCaseName(test) + "(" + JUnitVersionHelper.getTestCaseClassName(test) + ")"; + } + + /** + * Interface TestListener. + * + * <p>A Test is finished. + * @param test the test. + */ + public void endTest(final Test test) { + final String testDescription = createDescription(test); + + // Fix for bug #5637 - if a junit.extensions.TestSetup is + // used and throws an exception during setUp then startTest + // would never have been called + if (!testStarts.containsKey(testDescription)) { + startTest(test); + } + Element currentTest; + if (!failedTests.containsKey(test) && !skippedTests.containsKey(testDescription) && !ignoredTests.containsKey(testDescription)) { + currentTest = doc.createElement(TESTCASE); + final String n = JUnitVersionHelper.getTestCaseName(test); + currentTest.setAttribute(ATTR_NAME, + n == null ? UNKNOWN : n); + // a TestSuite can contain Tests from multiple classes, + // even tests with the same name - disambiguate them. + currentTest.setAttribute(ATTR_CLASSNAME, + JUnitVersionHelper.getTestCaseClassName(test)); + rootElement.appendChild(currentTest); + testElements.put(createDescription(test), currentTest); + } else { + currentTest = testElements.get(testDescription); + } + + final Long l = testStarts.get(createDescription(test)); + currentTest.setAttribute(ATTR_TIME, + "" + ((System.currentTimeMillis() - l) / ONE_SECOND)); + } + + /** + * Interface TestListener for JUnit <= 3.4. + * + * <p>A Test failed. + * @param test the test. + * @param t the exception. + */ + public void addFailure(final Test test, final Throwable t) { + formatError(FAILURE, test, t); + } + + /** + * Interface TestListener for JUnit > 3.4. + * + * <p>A Test failed. + * @param test the test. + * @param t the assertion. + */ + public void addFailure(final Test test, final AssertionFailedError t) { + addFailure(test, (Throwable) t); + } + + /** + * Interface TestListener. + * + * <p>An error occurred while running the test. + * @param test the test. + * @param t the error. + */ + public void addError(final Test test, final Throwable t) { + formatError(ERROR, test, t); + } + + private void formatError(final String type, final Test test, final Throwable t) { + if (test != null) { + endTest(test); + failedTests.put(test, test); + } + + final Element nested = doc.createElement(type); + Element currentTest; + if (test != null) { + currentTest = testElements.get(createDescription(test)); + } else { + currentTest = rootElement; + } + + currentTest.appendChild(nested); + + final String message = t.getMessage(); + if (message != null && message.length() > 0) { + nested.setAttribute(ATTR_MESSAGE, t.getMessage()); + } + nested.setAttribute(ATTR_TYPE, t.getClass().getName()); + + final String strace = JUnitTestRunner.getFilteredTrace(t); + final Text trace = doc.createTextNode(strace); + nested.appendChild(trace); + } + + private void formatOutput(final String type, final String output) { + final Element nested = doc.createElement(type); + rootElement.appendChild(nested); + nested.appendChild(doc.createCDATASection(output)); + } + + public void testIgnored(final Test test) { + formatSkip(test, JUnitVersionHelper.getIgnoreMessage(test)); + if (test != null) { + ignoredTests.put(createDescription(test), test); + } + } + + + public void formatSkip(final Test test, final String message) { + if (test != null) { + endTest(test); + } + + final Element nested = doc.createElement("skipped"); + + if (message != null) { + nested.setAttribute("message", message); + } + + Element currentTest; + if (test != null) { + currentTest = testElements.get(createDescription(test)); + } else { + currentTest = rootElement; + } + + currentTest.appendChild(nested); + + } + + public void testAssumptionFailure(final Test test, final Throwable failure) { + formatSkip(test, failure.getMessage()); + skippedTests.put(createDescription(test), test); + + } +} // XMLJUnitResultFormatter diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLResultAggregator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLResultAggregator.java new file mode 100644 index 00000000..4f76c968 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLResultAggregator.java @@ -0,0 +1,329 @@ +/* + * 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.tools.ant.taskdefs.optional.junit; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Enumeration; +import java.util.Vector; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.util.DOMElementWriter; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.StringUtils; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + + +/** + * Aggregates all <junit> XML formatter testsuite data under + * a specific directory and transforms the results via XSLT. + * It is not particularly clean but + * should be helpful while I am thinking about another technique. + * + * <p> The main problem is due to the fact that a JVM can be forked for a testcase + * thus making it impossible to aggregate all testcases since the listener is + * (obviously) in the forked JVM. A solution could be to write a + * TestListener that will receive events from the TestRunner via sockets. This + * is IMHO the simplest way to do it to avoid this file hacking thing. + * + * @ant.task name="junitreport" category="testing" + */ +public class XMLResultAggregator extends Task implements XMLConstants { + + // CheckStyle:VisibilityModifier OFF - bc + /** the list of all filesets, that should contains the xml to aggregate */ + protected Vector filesets = new Vector(); + + /** the name of the result file */ + protected String toFile; + + /** the directory to write the file to */ + protected File toDir; + + protected Vector transformers = new Vector(); + + /** The default directory: <tt>.</tt>. It is resolved from the project directory */ + public static final String DEFAULT_DIR = "."; + + /** the default file name: <tt>TESTS-TestSuites.xml</tt> */ + public static final String DEFAULT_FILENAME = "TESTS-TestSuites.xml"; + + /** the current generated id */ + protected int generatedId = 0; + + /** + * text checked for in tests, {@value} + */ + static final String WARNING_IS_POSSIBLY_CORRUPTED + = " is not a valid XML document. It is possibly corrupted."; + /** + * text checked for in tests, {@value} + */ + static final String WARNING_INVALID_ROOT_ELEMENT + = " is not a valid testsuite XML document"; + /** + * text checked for in tests, {@value} + */ + static final String WARNING_EMPTY_FILE + = " is empty.\nThis can be caused by the test JVM exiting unexpectedly"; + // CheckStyle:VisibilityModifier ON + + /** + * Generate a report based on the document created by the merge. + * @return the report + */ + public AggregateTransformer createReport() { + AggregateTransformer transformer = new AggregateTransformer(this); + transformers.addElement(transformer); + return transformer; + } + + /** + * Set the name of the aggregegated results file. It must be relative + * from the <tt>todir</tt> attribute. If not set it will use {@link #DEFAULT_FILENAME} + * @param value the name of the file. + * @see #setTodir(File) + */ + public void setTofile(String value) { + toFile = value; + } + + /** + * Set the destination directory where the results should be written. If not + * set if will use {@link #DEFAULT_DIR}. When given a relative directory + * it will resolve it from the project directory. + * @param value the directory where to write the results, absolute or + * relative. + */ + public void setTodir(File value) { + toDir = value; + } + + /** + * Add a new fileset containing the XML results to aggregate + * @param fs the new fileset of xml results. + */ + public void addFileSet(FileSet fs) { + filesets.addElement(fs); + } + + /** + * Aggregate all testsuites into a single document and write it to the + * specified directory and file. + * @throws BuildException thrown if there is a serious error while writing + * the document. + */ + public void execute() throws BuildException { + Element rootElement = createDocument(); + File destFile = getDestinationFile(); + // write the document + try { + writeDOMTree(rootElement.getOwnerDocument(), destFile); + } catch (IOException e) { + throw new BuildException("Unable to write test aggregate to '" + destFile + "'", e); + } + // apply transformation + Enumeration e = transformers.elements(); + while (e.hasMoreElements()) { + AggregateTransformer transformer = + (AggregateTransformer) e.nextElement(); + transformer.setXmlDocument(rootElement.getOwnerDocument()); + transformer.transform(); + } + } + + /** + * Get the full destination file where to write the result. It is made of + * the <tt>todir</tt> and <tt>tofile</tt> attributes. + * @return the destination file where should be written the result file. + */ + public File getDestinationFile() { + if (toFile == null) { + toFile = DEFAULT_FILENAME; + } + if (toDir == null) { + toDir = getProject().resolveFile(DEFAULT_DIR); + } + return new File(toDir, toFile); + } + + /** + * Get all <code>.xml</code> files in the fileset. + * + * @return all files in the fileset that end with a '.xml'. + */ + protected File[] getFiles() { + Vector v = new Vector(); + final int size = filesets.size(); + for (int i = 0; i < size; i++) { + FileSet fs = (FileSet) filesets.elementAt(i); + DirectoryScanner ds = fs.getDirectoryScanner(getProject()); + ds.scan(); + String[] f = ds.getIncludedFiles(); + for (int j = 0; j < f.length; j++) { + String pathname = f[j]; + if (pathname.endsWith(".xml")) { + File file = new File(ds.getBasedir(), pathname); + file = getProject().resolveFile(file.getPath()); + v.addElement(file); + } + } + } + + File[] files = new File[v.size()]; + v.copyInto(files); + return files; + } + + //----- from now, the methods are all related to DOM tree manipulation + + /** + * Write the DOM tree to a file. + * @param doc the XML document to dump to disk. + * @param file the filename to write the document to. Should obviously be a .xml file. + * @throws IOException thrown if there is an error while writing the content. + */ + protected void writeDOMTree(Document doc, File file) throws IOException { + OutputStream os = new FileOutputStream(file); + try { + PrintWriter wri = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(os), "UTF8")); + wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); + (new DOMElementWriter()).write(doc.getDocumentElement(), wri, 0, " "); + wri.flush(); + // writers do not throw exceptions, so check for them. + if (wri.checkError()) { + throw new IOException("Error while writing DOM content"); + } + } finally { + os.close(); + } + } + + /** + * <p> Create a DOM tree. + * Has 'testsuites' as firstchild and aggregates all + * testsuite results that exists in the base directory. + * @return the root element of DOM tree that aggregates all testsuites. + */ + protected Element createDocument() { + // create the dom tree + DocumentBuilder builder = getDocumentBuilder(); + Document doc = builder.newDocument(); + Element rootElement = doc.createElement(TESTSUITES); + doc.appendChild(rootElement); + + generatedId = 0; + + // get all files and add them to the document + File[] files = getFiles(); + for (int i = 0; i < files.length; i++) { + File file = files[i]; + try { + log("Parsing file: '" + file + "'", Project.MSG_VERBOSE); + if (file.length() > 0) { + Document testsuiteDoc + = builder.parse( + FileUtils.getFileUtils().toURI(files[i].getAbsolutePath())); + Element elem = testsuiteDoc.getDocumentElement(); + // make sure that this is REALLY a testsuite. + if (TESTSUITE.equals(elem.getNodeName())) { + addTestSuite(rootElement, elem); + generatedId++; + } else { + //wrong root element name + // issue a warning. + log("the file " + file + + WARNING_INVALID_ROOT_ELEMENT, + Project.MSG_WARN); + } + } else { + log("the file " + file + + WARNING_EMPTY_FILE, + Project.MSG_WARN); + } + } catch (SAXException e) { + // a testcase might have failed and write a zero-length document, + // It has already failed, but hey.... mm. just put a warning + log("The file " + file + WARNING_IS_POSSIBLY_CORRUPTED, Project.MSG_WARN); + log(StringUtils.getStackTrace(e), Project.MSG_DEBUG); + } catch (IOException e) { + log("Error while accessing file " + file + ": " + + e.getMessage(), Project.MSG_ERR); + log("Error while accessing file " + file + ": " + + e.getMessage(), e, Project.MSG_VERBOSE); + } + } + return rootElement; + } + + /** + * <p> Add a new testsuite node to the document. + * The main difference is that it + * split the previous fully qualified name into a package and a name. + * <p> For example: <tt>org.apache.Whatever</tt> will be split into + * <tt>org.apache</tt> and <tt>Whatever</tt>. + * @param root the root element to which the <tt>testsuite</tt> node should + * be appended. + * @param testsuite the element to append to the given root. It will slightly + * modify the original node to change the name attribute and add + * a package one. + */ + protected void addTestSuite(Element root, Element testsuite) { + String fullclassname = testsuite.getAttribute(ATTR_NAME); + int pos = fullclassname.lastIndexOf('.'); + + // a missing . might imply no package at all. Don't get fooled. + String pkgName = (pos == -1) ? "" : fullclassname.substring(0, pos); + String classname = (pos == -1) ? fullclassname : fullclassname.substring(pos + 1); + Element copy = (Element) DOMUtil.importNode(root, testsuite); + + // modify the name attribute and set the package + copy.setAttribute(ATTR_NAME, classname); + copy.setAttribute(ATTR_PACKAGE, pkgName); + copy.setAttribute(ATTR_ID, Integer.toString(generatedId)); + } + + /** + * Create a new document builder. Will issue an <tt>ExceptionInitializerError</tt> + * if something is going wrong. It is fatal anyway. + * @todo factorize this somewhere else. It is duplicated code. + * @return a new document builder to create a DOM + */ + private static DocumentBuilder getDocumentBuilder() { + try { + return DocumentBuilderFactory.newInstance().newDocumentBuilder(); + } catch (Exception exc) { + throw new ExceptionInInitializerError(exc); + } + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/DefaultNative2Ascii.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/DefaultNative2Ascii.java new file mode 100644 index 00000000..3cd52afe --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/DefaultNative2Ascii.java @@ -0,0 +1,106 @@ +/* + * 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.tools.ant.taskdefs.optional.native2ascii; + +import java.io.File; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.ProjectComponent; +import org.apache.tools.ant.taskdefs.optional.Native2Ascii; +import org.apache.tools.ant.types.Commandline; + +/** + * encapsulates the handling common to diffent Native2Asciiadapter + * implementations. + * + * @since Ant 1.6.3 + */ +public abstract class DefaultNative2Ascii implements Native2AsciiAdapter { + + /** No-arg constructor. */ + public DefaultNative2Ascii() { + } + + /** + * Splits the task into setting up the command line switches + * @param args the native 2 ascii arguments. + * @param srcFile the source file. + * @param destFile the destination file. + * @return run if the conversion was successful. + * @throws BuildException if there is a problem. + * (delegated to {@link #setup setup}), adding the file names + * (delegated to {@link #addFiles addFiles}) and running the tool + * (delegated to {@link #run run}). + */ + public final boolean convert(Native2Ascii args, File srcFile, + File destFile) throws BuildException { + Commandline cmd = new Commandline(); + setup(cmd, args); + addFiles(cmd, args, srcFile, destFile); + return run(cmd, args); + } + + /** + * Sets up the initial command line. + * + * <p>only the -encoding argument and nested arg elements get + * handled here.</p> + * + * @param cmd Command line to add to + * @param args provides the user-setting and access to Ant's + * logging system. + * @throws BuildException if there was a problem. + */ + protected void setup(Commandline cmd, Native2Ascii args) + throws BuildException { + if (args.getEncoding() != null) { + cmd.createArgument().setValue("-encoding"); + cmd.createArgument().setValue(args.getEncoding()); + } + cmd.addArguments(args.getCurrentArgs()); + } + + /** + * Adds source and dest files to the command line. + * + * <p>This implementation adds them without any leading + * qualifiers, source first.</p> + * + * @param cmd Command line to add to + * @param log provides access to Ant's logging system. + * @param src the source file + * @param dest the destination file + * @throws BuildException if there was a problem. + */ + protected void addFiles(Commandline cmd, ProjectComponent log, File src, + File dest) throws BuildException { + cmd.createArgument().setFile(src); + cmd.createArgument().setFile(dest); + } + + /** + * Executes the command. + * + * @param cmd Command line to execute + * @param log provides access to Ant's logging system. + * @return whether execution was successful + * @throws BuildException if there was a problem. + */ + protected abstract boolean run(Commandline cmd, ProjectComponent log) + throws BuildException; +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/KaffeNative2Ascii.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/KaffeNative2Ascii.java new file mode 100644 index 00000000..da4836fd --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/KaffeNative2Ascii.java @@ -0,0 +1,88 @@ +/* + * 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.tools.ant.taskdefs.optional.native2ascii; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.ProjectComponent; +import org.apache.tools.ant.taskdefs.ExecuteJava; +import org.apache.tools.ant.taskdefs.optional.Native2Ascii; +import org.apache.tools.ant.types.Commandline; + +/** + * Adapter to kaffe.tools.native2ascii.Native2Ascii. + * + * @since Ant 1.6.3 + */ +public final class KaffeNative2Ascii extends DefaultNative2Ascii { + + // sorted by newest Kaffe version first + private static final String[] N2A_CLASSNAMES = new String[] { + "gnu.classpath.tools.native2ascii.Native2ASCII", + // pre Kaffe 1.1.5 + "kaffe.tools.native2ascii.Native2Ascii", + }; + + /** + * Identifies this adapter. + */ + public static final String IMPLEMENTATION_NAME = "kaffe"; + + /** {@inheritDoc} */ + protected void setup(Commandline cmd, Native2Ascii args) + throws BuildException { + if (args.getReverse()) { + throw new BuildException("-reverse is not supported by Kaffe"); + } + super.setup(cmd, args); + } + + /** {@inheritDoc} */ + protected boolean run(Commandline cmd, ProjectComponent log) + throws BuildException { + ExecuteJava ej = new ExecuteJava(); + Class c = getN2aClass(); + if (c == null) { + throw new BuildException("Couldn't load Kaffe's Native2Ascii" + + " class"); + } + + cmd.setExecutable(c.getName()); + ej.setJavaCommand(cmd); + ej.execute(log.getProject()); + // otherwise ExecuteJava has thrown an exception + return true; + } + + /** + * tries to load Kaffe Native2Ascii and falls back to the older + * class name if necessary. + * + * @return null if neither class can get loaded. + */ + private static Class getN2aClass() { + for (int i = 0; i < N2A_CLASSNAMES.length; i++) { + try { + return Class.forName(N2A_CLASSNAMES[i]); + } catch (ClassNotFoundException cnfe) { + // Ignore + } + } + return null; + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapter.java new file mode 100644 index 00000000..af414004 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapter.java @@ -0,0 +1,44 @@ +/* + * 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.tools.ant.taskdefs.optional.native2ascii; + +import java.io.File; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.optional.Native2Ascii; + +/** + * Interface for an adapter to a native2ascii implementation. + * + * @since Ant 1.6.3 + */ +// CheckStyle:HideUtilityClassConstructorCheck OFF (bc) +public interface Native2AsciiAdapter { + /** + * Convert the encoding of srcFile writing to destFile. + * + * @param args Task that holds command line arguments and allows + * the implementation to send messages to Ant's logging system + * @param srcFile the source to convert + * @param destFile where to send output to + * @return whether the conversion has been successful. + * @throws BuildException if there was a problem. + */ + boolean convert(Native2Ascii args, File srcFile, File destFile) + throws BuildException; +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapterFactory.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapterFactory.java new file mode 100644 index 00000000..ae419039 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapterFactory.java @@ -0,0 +1,116 @@ +/* + * 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.tools.ant.taskdefs.optional.native2ascii; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.ProjectComponent; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.ClasspathUtils; +import org.apache.tools.ant.util.JavaEnvUtils; + +/** + * Creates the Native2AsciiAdapter based on the user choice and + * potentially the VM vendor. + * + * @since Ant 1.6.3 + */ +// CheckStyle:HideUtilityClassConstructorCheck OFF (bc) +public class Native2AsciiAdapterFactory { + + /** + * Determines the default choice of adapter based on the VM + * vendor. + * + * @return the default choice of adapter based on the VM + * vendor + */ + public static String getDefault() { + if (JavaEnvUtils.isKaffe() || JavaEnvUtils.isClasspathBased()) { + return KaffeNative2Ascii.IMPLEMENTATION_NAME; + } + return SunNative2Ascii.IMPLEMENTATION_NAME; + } + + /** + * Creates the Native2AsciiAdapter based on the user choice and + * potentially the VM vendor. + * + * @param choice the user choice (if any). + * @param log a ProjectComponent instance used to access Ant's + * logging system. + * @return The adapter to use. + * @throws BuildException if there was a problem. + */ + public static Native2AsciiAdapter getAdapter(String choice, + ProjectComponent log) + throws BuildException { + return getAdapter(choice, log, null); + } + + /** + * Creates the Native2AsciiAdapter based on the user choice and + * potentially the VM vendor. + * + * @param choice the user choice (if any). + * @param log a ProjectComponent instance used to access Ant's + * logging system. + * @param classpath the classpath to use when looking up an + * adapter class + * @return The adapter to use. + * @throws BuildException if there was a problem. + * @since Ant 1.8.0 + */ + public static Native2AsciiAdapter getAdapter(String choice, + ProjectComponent log, + Path classpath) + throws BuildException { + if (((JavaEnvUtils.isKaffe() || JavaEnvUtils.isClasspathBased()) && choice == null) + || KaffeNative2Ascii.IMPLEMENTATION_NAME.equals(choice)) { + return new KaffeNative2Ascii(); + } else if (SunNative2Ascii.IMPLEMENTATION_NAME.equals(choice)) { + return new SunNative2Ascii(); + } else if (choice != null) { + return resolveClassName(choice, + // Memory leak in line below + log.getProject() + .createClassLoader(classpath)); + } + + // This default has been good enough until Ant 1.6.3, so stick + // with it + return new SunNative2Ascii(); + } + + /** + * Tries to resolve the given classname into a native2ascii adapter. + * Throws a fit if it can't. + * + * @param className The fully qualified classname to be created. + * @param loader the classloader to use + * @throws BuildException This is the fit that is thrown if className + * isn't an instance of Native2AsciiAdapter. + */ + private static Native2AsciiAdapter resolveClassName(String className, + ClassLoader loader) + throws BuildException { + return (Native2AsciiAdapter) ClasspathUtils.newInstance(className, + loader != null ? loader : + Native2AsciiAdapterFactory.class.getClassLoader(), + Native2AsciiAdapter.class); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/SunNative2Ascii.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/SunNative2Ascii.java new file mode 100644 index 00000000..fac94b18 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/SunNative2Ascii.java @@ -0,0 +1,71 @@ +/* + * 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.tools.ant.taskdefs.optional.native2ascii; + +import java.lang.reflect.Method; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.ProjectComponent; +import org.apache.tools.ant.taskdefs.optional.Native2Ascii; +import org.apache.tools.ant.types.Commandline; + +/** + * Adapter to sun.tools.native2ascii.Main. + * + * @since Ant 1.6.3 + */ +public final class SunNative2Ascii extends DefaultNative2Ascii { + + /** + * Identifies this adapter. + */ + public static final String IMPLEMENTATION_NAME = "sun"; + + /** {@inheritDoc} */ + protected void setup(Commandline cmd, Native2Ascii args) + throws BuildException { + if (args.getReverse()) { + cmd.createArgument().setValue("-reverse"); + } + super.setup(cmd, args); + } + + /** {@inheritDoc} */ + protected boolean run(Commandline cmd, ProjectComponent log) + throws BuildException { + try { + Class n2aMain = Class.forName("sun.tools.native2ascii.Main"); + Class[] param = new Class[] {String[].class}; + Method convert = n2aMain.getMethod("convert", param); + if (convert == null) { + throw new BuildException("Could not find convert() method in " + + "sun.tools.native2ascii.Main"); + } + Object o = n2aMain.newInstance(); + return ((Boolean) convert.invoke(o, + new Object[] {cmd.getArguments()}) + ).booleanValue(); + } catch (BuildException ex) { + //rethrow + throw ex; + } catch (Exception ex) { + //wrap + throw new BuildException("Error starting Sun's native2ascii: ", ex); + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTP.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTP.java new file mode 100644 index 00000000..cef9dda0 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTP.java @@ -0,0 +1,2725 @@ +/* + * 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.tools.ant.taskdefs.optional.net; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.Vector; + +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPClientConfig; +import org.apache.commons.net.ftp.FTPFile; +import org.apache.commons.net.ftp.FTPReply; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.Delete; +import org.apache.tools.ant.types.EnumeratedAttribute; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.selectors.SelectorUtils; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.RetryHandler; +import org.apache.tools.ant.util.Retryable; +import org.apache.tools.ant.util.VectorSet; + +/** + * Basic FTP client. Performs the following actions: + * <ul> + * <li> <strong>send</strong> - send files to a remote server. This is the + * default action.</li> + * <li> <strong>get</strong> - retrieve files from a remote server.</li> + * <li> <strong>del</strong> - delete files from a remote server.</li> + * <li> <strong>list</strong> - create a file listing.</li> + * <li> <strong>chmod</strong> - change unix file permissions.</li> + * <li> <strong>rmdir</strong> - remove directories, if empty, from a + * remote server.</li> + * </ul> + * <strong>Note:</strong> Some FTP servers - notably the Solaris server - seem + * to hold data ports open after a "retr" operation, allowing them to timeout + * instead of shutting them down cleanly. This happens in active or passive + * mode, and the ports will remain open even after ending the FTP session. FTP + * "send" operations seem to close ports immediately. This behavior may cause + * problems on some systems when downloading large sets of files. + * + * @since Ant 1.3 + */ +public class FTP extends Task implements FTPTaskConfig { + protected static final int SEND_FILES = 0; + protected static final int GET_FILES = 1; + protected static final int DEL_FILES = 2; + protected static final int LIST_FILES = 3; + protected static final int MK_DIR = 4; + protected static final int CHMOD = 5; + protected static final int RM_DIR = 6; + protected static final int SITE_CMD = 7; + /** return code of ftp */ + private static final int CODE_521 = 521; + private static final int CODE_550 = 550; + private static final int CODE_553 = 553; + + /** adjust uptodate calculations where server timestamps are HH:mm and client's + * are HH:mm:ss */ + private static final long GRANULARITY_MINUTE = 60000L; + + /** Date formatter used in logging, note not thread safe! */ + private static final SimpleDateFormat TIMESTAMP_LOGGING_SDF = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + /** Default port for FTP */ + public static final int DEFAULT_FTP_PORT = 21; + + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + private String remotedir; + private String server; + private String userid; + private String password; + private String account; + private File listing; + private boolean binary = true; + private boolean passive = false; + private boolean verbose = false; + private boolean newerOnly = false; + private long timeDiffMillis = 0; + private long granularityMillis = 0L; + private boolean timeDiffAuto = false; + private int action = SEND_FILES; + private Vector filesets = new Vector(); + private Set dirCache = new HashSet(); + private int transferred = 0; + private String remoteFileSep = "/"; + private int port = DEFAULT_FTP_PORT; + private boolean skipFailedTransfers = false; + private int skipped = 0; + private boolean ignoreNoncriticalErrors = false; + private boolean preserveLastModified = false; + private String chmod = null; + private String umask = null; + private FTPSystemType systemTypeKey = FTPSystemType.getDefault(); + private String defaultDateFormatConfig = null; + private String recentDateFormatConfig = null; + private LanguageCode serverLanguageCodeConfig = LanguageCode.getDefault(); + private String serverTimeZoneConfig = null; + private String shortMonthNamesConfig = null; + private Granularity timestampGranularity = Granularity.getDefault(); + private boolean isConfigurationSet = false; + private int retriesAllowed = 0; + private String siteCommand = null; + private String initialSiteCommand = null; + private boolean enableRemoteVerification = true; + + protected static final String[] ACTION_STRS = { + "sending", + "getting", + "deleting", + "listing", + "making directory", + "chmod", + "removing", + "site" + }; + + protected static final String[] COMPLETED_ACTION_STRS = { + "sent", + "retrieved", + "deleted", + "listed", + "created directory", + "mode changed", + "removed", + "site command executed" + }; + + protected static final String[] ACTION_TARGET_STRS = { + "files", + "files", + "files", + "files", + "directory", + "files", + "directories", + "site command" + }; + + /** + * internal class providing a File-like interface to some of the information + * available from the FTP server + * + */ + protected static class FTPFileProxy extends File { + + private final FTPFile file; + private final String[] parts; + private final String name; + + /** + * creates a proxy to a FTP file + * @param file + */ + public FTPFileProxy(FTPFile file) { + super(file.getName()); + name = file.getName(); + this.file = file; + parts = FileUtils.getPathStack(name); + } + + /** + * creates a proxy to a FTP directory + * @param completePath the remote directory. + */ + public FTPFileProxy(String completePath) { + super(completePath); + file = null; + name = completePath; + parts = FileUtils.getPathStack(completePath); + } + + + /* (non-Javadoc) + * @see java.io.File#exists() + */ + public boolean exists() { + return true; + } + + + /* (non-Javadoc) + * @see java.io.File#getAbsolutePath() + */ + public String getAbsolutePath() { + return name; + } + + + /* (non-Javadoc) + * @see java.io.File#getName() + */ + public String getName() { + return parts.length > 0 ? parts[parts.length - 1] : name; + } + + + /* (non-Javadoc) + * @see java.io.File#getParent() + */ + public String getParent() { + String result = ""; + for(int i = 0; i < parts.length - 1; i++){ + result += File.separatorChar + parts[i]; + } + return result; + } + + + /* (non-Javadoc) + * @see java.io.File#getPath() + */ + public String getPath() { + return name; + } + + + /** + * FTP files are stored as absolute paths + * @return true + */ + public boolean isAbsolute() { + return true; + } + + + /* (non-Javadoc) + * @see java.io.File#isDirectory() + */ + public boolean isDirectory() { + return file == null; + } + + + /* (non-Javadoc) + * @see java.io.File#isFile() + */ + public boolean isFile() { + return file != null; + } + + + /** + * FTP files cannot be hidden + * + * @return false + */ + public boolean isHidden() { + return false; + } + + + /* (non-Javadoc) + * @see java.io.File#lastModified() + */ + public long lastModified() { + if (file != null) { + return file.getTimestamp().getTimeInMillis(); + } + return 0; + } + + + /* (non-Javadoc) + * @see java.io.File#length() + */ + public long length() { + if (file != null) { + return file.getSize(); + } + return 0; + } + } + + /** + * internal class allowing to read the contents of a remote file system + * using the FTP protocol + * used in particular for ftp get operations + * differences with DirectoryScanner + * "" (the root of the fileset) is never included in the included directories + * followSymlinks defaults to false + */ + protected class FTPDirectoryScanner extends DirectoryScanner { + // CheckStyle:VisibilityModifier OFF - bc + protected FTPClient ftp = null; + // CheckStyle:VisibilityModifier ON + + private String rootPath = null; + + /** + * since ant 1.6 + * this flag should be set to true on UNIX and can save scanning time + */ + private boolean remoteSystemCaseSensitive = false; + private boolean remoteSensitivityChecked = false; + + /** + * constructor + * @param ftp ftpclient object + */ + public FTPDirectoryScanner(FTPClient ftp) { + super(); + this.ftp = ftp; + this.setFollowSymlinks(false); + } + + + /** + * scans the remote directory, + * storing internally the included files, directories, ... + */ + public void scan() { + if (includes == null) { + // No includes supplied, so set it to 'matches all' + includes = new String[1]; + includes[0] = "**"; + } + if (excludes == null) { + excludes = new String[0]; + } + + filesIncluded = new VectorSet(); + filesNotIncluded = new Vector(); + filesExcluded = new VectorSet(); + dirsIncluded = new VectorSet(); + dirsNotIncluded = new Vector(); + dirsExcluded = new VectorSet(); + + try { + String cwd = ftp.printWorkingDirectory(); + // always start from the current ftp working dir + forceRemoteSensitivityCheck(); + + checkIncludePatterns(); + clearCaches(); + ftp.changeWorkingDirectory(cwd); + } catch (IOException e) { + throw new BuildException("Unable to scan FTP server: ", e); + } + } + + + /** + * this routine is actually checking all the include patterns in + * order to avoid scanning everything under base dir + * @since ant1.6 + */ + private void checkIncludePatterns() { + + Hashtable newroots = new Hashtable(); + // put in the newroots vector the include patterns without + // wildcard tokens + for (int icounter = 0; icounter < includes.length; icounter++) { + String newpattern = + SelectorUtils.rtrimWildcardTokens(includes[icounter]); + newroots.put(newpattern, includes[icounter]); + } + if (remotedir == null) { + try { + remotedir = ftp.printWorkingDirectory(); + } catch (IOException e) { + throw new BuildException("could not read current ftp directory", + getLocation()); + } + } + AntFTPFile baseFTPFile = new AntFTPRootFile(ftp, remotedir); + rootPath = baseFTPFile.getAbsolutePath(); + // construct it + if (newroots.containsKey("")) { + // we are going to scan everything anyway + scandir(rootPath, "", true); + } else { + // only scan directories that can include matched files or + // directories + Enumeration enum2 = newroots.keys(); + + while (enum2.hasMoreElements()) { + String currentelement = (String) enum2.nextElement(); + String originalpattern = (String) newroots.get(currentelement); + AntFTPFile myfile = new AntFTPFile(baseFTPFile, currentelement); + boolean isOK = true; + boolean traversesSymlinks = false; + String path = null; + + if (myfile.exists()) { + forceRemoteSensitivityCheck(); + if (remoteSensitivityChecked + && remoteSystemCaseSensitive && isFollowSymlinks()) { + // cool case, + //we do not need to scan all the subdirs in the relative path + path = myfile.getFastRelativePath(); + } else { + // may be on a case insensitive file system. We want + // the results to show what's really on the disk, so + // we need to double check. + try { + path = myfile.getRelativePath(); + traversesSymlinks = myfile.isTraverseSymlinks(); + } catch (IOException be) { + throw new BuildException(be, getLocation()); + } catch (BuildException be) { + isOK = false; + } + } + } else { + isOK = false; + } + if (isOK) { + currentelement = path.replace(remoteFileSep.charAt(0), File.separatorChar); + if (!isFollowSymlinks() + && traversesSymlinks) { + continue; + } + + if (myfile.isDirectory()) { + if (isIncluded(currentelement) + && currentelement.length() > 0) { + accountForIncludedDir(currentelement, myfile, true); + } else { + if (currentelement.length() > 0) { + if (currentelement.charAt(currentelement + .length() - 1) + != File.separatorChar) { + currentelement = + currentelement + File.separatorChar; + } + } + scandir(myfile.getAbsolutePath(), currentelement, true); + } + } else { + if (isCaseSensitive + && originalpattern.equals(currentelement)) { + accountForIncludedFile(currentelement); + } else if (!isCaseSensitive + && originalpattern + .equalsIgnoreCase(currentelement)) { + accountForIncludedFile(currentelement); + } + } + } + } + } + } + /** + * scans a particular directory. populates the scannedDirs cache. + * + * @param dir directory to scan + * @param vpath relative path to the base directory of the remote fileset + * always ended with a File.separator + * @param fast seems to be always true in practice + */ + protected void scandir(String dir, String vpath, boolean fast) { + // avoid double scanning of directories, can only happen in fast mode + if (fast && hasBeenScanned(vpath)) { + return; + } + try { + if (!ftp.changeWorkingDirectory(dir)) { + return; + } + String completePath = null; + if (!vpath.equals("")) { + completePath = rootPath + remoteFileSep + + vpath.replace(File.separatorChar, remoteFileSep.charAt(0)); + } else { + completePath = rootPath; + } + FTPFile[] newfiles = listFiles(completePath, false); + + if (newfiles == null) { + ftp.changeToParentDirectory(); + return; + } + for (int i = 0; i < newfiles.length; i++) { + FTPFile file = newfiles[i]; + if (file != null + && !file.getName().equals(".") + && !file.getName().equals("..")) { + String name = vpath + file.getName(); + scannedDirs.put(name, new FTPFileProxy(file)); + if (isFunctioningAsDirectory(ftp, dir, file)) { + boolean slowScanAllowed = true; + if (!isFollowSymlinks() && file.isSymbolicLink()) { + dirsExcluded.addElement(name); + slowScanAllowed = false; + } else if (isIncluded(name)) { + accountForIncludedDir(name, + new AntFTPFile(ftp, file, completePath) , fast); + } else { + dirsNotIncluded.addElement(name); + if (fast && couldHoldIncluded(name)) { + scandir(file.getName(), + name + File.separator, fast); + } + } + if (!fast && slowScanAllowed) { + scandir(file.getName(), + name + File.separator, fast); + } + } else { + if (!isFollowSymlinks() && file.isSymbolicLink()) { + filesExcluded.addElement(name); + } else if (isFunctioningAsFile(ftp, dir, file)) { + accountForIncludedFile(name); + } + } + } + } + ftp.changeToParentDirectory(); + } catch (IOException e) { + throw new BuildException("Error while communicating with FTP " + + "server: ", e); + } + } + /** + * process included file + * @param name path of the file relative to the directory of the fileset + */ + private void accountForIncludedFile(String name) { + if (!filesIncluded.contains(name) + && !filesExcluded.contains(name)) { + + if (isIncluded(name)) { + if (!isExcluded(name) + && isSelected(name, (File) scannedDirs.get(name))) { + filesIncluded.addElement(name); + } else { + filesExcluded.addElement(name); + } + } else { + filesNotIncluded.addElement(name); + } + } + } + + /** + * + * @param name path of the directory relative to the directory of + * the fileset + * @param file directory as file + * @param fast + */ + private void accountForIncludedDir(String name, AntFTPFile file, boolean fast) { + if (!dirsIncluded.contains(name) + && !dirsExcluded.contains(name)) { + + if (!isExcluded(name)) { + if (fast) { + if (file.isSymbolicLink()) { + try { + file.getClient().changeWorkingDirectory(file.curpwd); + } catch (IOException ioe) { + throw new BuildException("could not change directory to curpwd"); + } + scandir(file.getLink(), + name + File.separator, fast); + } else { + try { + file.getClient().changeWorkingDirectory(file.curpwd); + } catch (IOException ioe) { + throw new BuildException("could not change directory to curpwd"); + } + scandir(file.getName(), + name + File.separator, fast); + } + } + dirsIncluded.addElement(name); + } else { + dirsExcluded.addElement(name); + if (fast && couldHoldIncluded(name)) { + try { + file.getClient().changeWorkingDirectory(file.curpwd); + } catch (IOException ioe) { + throw new BuildException("could not change directory to curpwd"); + } + scandir(file.getName(), + name + File.separator, fast); + } + } + } + } + /** + * temporary table to speed up the various scanning methods below + * + * @since Ant 1.6 + */ + private Map fileListMap = new HashMap(); + /** + * List of all scanned directories. + * + * @since Ant 1.6 + */ + + private Map scannedDirs = new HashMap(); + + /** + * Has the directory with the given path relative to the base + * directory already been scanned? + * + * @since Ant 1.6 + */ + private boolean hasBeenScanned(String vpath) { + return scannedDirs.containsKey(vpath); + } + + /** + * Clear internal caches. + * + * @since Ant 1.6 + */ + private void clearCaches() { + fileListMap.clear(); + scannedDirs.clear(); + } + /** + * list the files present in one directory. + * @param directory full path on the remote side + * @param changedir if true change to directory directory before listing + * @return array of FTPFile + */ + public FTPFile[] listFiles(String directory, boolean changedir) { + //getProject().log("listing files in directory " + directory, Project.MSG_DEBUG); + String currentPath = directory; + if (changedir) { + try { + boolean result = ftp.changeWorkingDirectory(directory); + if (!result) { + return null; + } + currentPath = ftp.printWorkingDirectory(); + } catch (IOException ioe) { + throw new BuildException(ioe, getLocation()); + } + } + if (fileListMap.containsKey(currentPath)) { + getProject().log("filelist map used in listing files", Project.MSG_DEBUG); + return ((FTPFile[]) fileListMap.get(currentPath)); + } + FTPFile[] result = null; + try { + result = ftp.listFiles(); + } catch (IOException ioe) { + throw new BuildException(ioe, getLocation()); + } + fileListMap.put(currentPath, result); + if (!remoteSensitivityChecked) { + checkRemoteSensitivity(result, directory); + } + return result; + } + + private void forceRemoteSensitivityCheck() { + if (!remoteSensitivityChecked) { + try { + checkRemoteSensitivity(ftp.listFiles(), ftp.printWorkingDirectory()); + } catch (IOException ioe) { + throw new BuildException(ioe, getLocation()); + } + } + } + /** + * cd into one directory and + * list the files present in one directory. + * @param directory full path on the remote side + * @return array of FTPFile + */ + public FTPFile[] listFiles(String directory) { + return listFiles(directory, true); + } + private void checkRemoteSensitivity(FTPFile[] array, String directory) { + if (array == null) { + return; + } + boolean candidateFound = false; + String target = null; + for (int icounter = 0; icounter < array.length; icounter++) { + if (array[icounter] != null && array[icounter].isDirectory()) { + if (!array[icounter].getName().equals(".") + && !array[icounter].getName().equals("..")) { + candidateFound = true; + target = fiddleName(array[icounter].getName()); + getProject().log("will try to cd to " + + target + " where a directory called " + array[icounter].getName() + + " exists", Project.MSG_DEBUG); + for (int pcounter = 0; pcounter < array.length; pcounter++) { + if (array[pcounter] != null + && pcounter != icounter + && target.equals(array[pcounter].getName())) { + candidateFound = false; + break; + } + } + if (candidateFound) { + break; + } + } + } + } + if (candidateFound) { + try { + getProject().log("testing case sensitivity, attempting to cd to " + + target, Project.MSG_DEBUG); + remoteSystemCaseSensitive = !ftp.changeWorkingDirectory(target); + } catch (IOException ioe) { + remoteSystemCaseSensitive = true; + } finally { + try { + ftp.changeWorkingDirectory(directory); + } catch (IOException ioe) { + throw new BuildException(ioe, getLocation()); + } + } + getProject().log("remote system is case sensitive : " + remoteSystemCaseSensitive, + Project.MSG_VERBOSE); + remoteSensitivityChecked = true; + } + } + private String fiddleName(String origin) { + StringBuffer result = new StringBuffer(); + for (int icounter = 0; icounter < origin.length(); icounter++) { + if (Character.isLowerCase(origin.charAt(icounter))) { + result.append(Character.toUpperCase(origin.charAt(icounter))); + } else if (Character.isUpperCase(origin.charAt(icounter))) { + result.append(Character.toLowerCase(origin.charAt(icounter))); + } else { + result.append(origin.charAt(icounter)); + } + } + return result.toString(); + } + /** + * an AntFTPFile is a representation of a remote file + * @since Ant 1.6 + */ + protected class AntFTPFile { + /** + * ftp client + */ + private FTPClient client; + /** + * parent directory of the file + */ + private String curpwd; + /** + * the file itself + */ + private FTPFile ftpFile; + /** + * + */ + private AntFTPFile parent = null; + private boolean relativePathCalculated = false; + private boolean traversesSymlinks = false; + private String relativePath = ""; + /** + * constructor + * @param client ftp client variable + * @param ftpFile the file + * @param curpwd absolute remote path where the file is found + */ + public AntFTPFile(FTPClient client, FTPFile ftpFile, String curpwd) { + this.client = client; + this.ftpFile = ftpFile; + this.curpwd = curpwd; + } + /** + * other constructor + * @param parent the parent file + * @param path a relative path to the parent file + */ + public AntFTPFile(AntFTPFile parent, String path) { + this.parent = parent; + this.client = parent.client; + Vector pathElements = SelectorUtils.tokenizePath(path); + try { + boolean result = this.client.changeWorkingDirectory(parent.getAbsolutePath()); + //this should not happen, except if parent has been deleted by another process + if (!result) { + return; + } + this.curpwd = parent.getAbsolutePath(); + } catch (IOException ioe) { + throw new BuildException("could not change working dir to " + + parent.curpwd); + } + final int size = pathElements.size(); + for (int fcount = 0; fcount < size - 1; fcount++) { + String currentPathElement = (String) pathElements.elementAt(fcount); + try { + boolean result = this.client.changeWorkingDirectory(currentPathElement); + if (!result && !isCaseSensitive() + && (remoteSystemCaseSensitive || !remoteSensitivityChecked)) { + currentPathElement = findPathElementCaseUnsensitive(this.curpwd, + currentPathElement); + if (currentPathElement == null) { + return; + } + } else if (!result) { + return; + } + this.curpwd = getCurpwdPlusFileSep() + + currentPathElement; + } catch (IOException ioe) { + throw new BuildException("could not change working dir to " + + (String) pathElements.elementAt(fcount) + + " from " + this.curpwd); + } + + } + String lastpathelement = (String) pathElements.elementAt(size - 1); + FTPFile [] theFiles = listFiles(this.curpwd); + this.ftpFile = getFile(theFiles, lastpathelement); + } + /** + * find a file in a directory in case unsensitive way + * @param parentPath where we are + * @param soughtPathElement what is being sought + * @return the first file found or null if not found + */ + private String findPathElementCaseUnsensitive(String parentPath, + String soughtPathElement) { + // we are already in the right path, so the second parameter + // is false + FTPFile[] theFiles = listFiles(parentPath, false); + if (theFiles == null) { + return null; + } + for (int icounter = 0; icounter < theFiles.length; icounter++) { + if (theFiles[icounter] != null + && theFiles[icounter].getName().equalsIgnoreCase(soughtPathElement)) { + return theFiles[icounter].getName(); + } + } + return null; + } + /** + * find out if the file exists + * @return true if the file exists + */ + public boolean exists() { + return (ftpFile != null); + } + /** + * if the file is a symbolic link, find out to what it is pointing + * @return the target of the symbolic link + */ + public String getLink() { + return ftpFile.getLink(); + } + /** + * get the name of the file + * @return the name of the file + */ + public String getName() { + return ftpFile.getName(); + } + /** + * find out the absolute path of the file + * @return absolute path as string + */ + public String getAbsolutePath() { + return getCurpwdPlusFileSep() + ftpFile.getName(); + } + /** + * find out the relative path assuming that the path used to construct + * this AntFTPFile was spelled properly with regards to case. + * This is OK on a case sensitive system such as UNIX + * @return relative path + */ + public String getFastRelativePath() { + String absPath = getAbsolutePath(); + if (absPath.startsWith(rootPath + remoteFileSep)) { + return absPath.substring(rootPath.length() + remoteFileSep.length()); + } + return null; + } + /** + * find out the relative path to the rootPath of the enclosing scanner. + * this relative path is spelled exactly like on disk, + * for instance if the AntFTPFile has been instantiated as ALPHA, + * but the file is really called alpha, this method will return alpha. + * If a symbolic link is encountered, it is followed, but the name of the link + * rather than the name of the target is returned. + * (ie does not behave like File.getCanonicalPath()) + * @return relative path, separated by remoteFileSep + * @throws IOException if a change directory fails, ... + * @throws BuildException if one of the components of the relative path cannot + * be found. + */ + public String getRelativePath() throws IOException, BuildException { + if (!relativePathCalculated) { + if (parent != null) { + traversesSymlinks = parent.isTraverseSymlinks(); + relativePath = getRelativePath(parent.getAbsolutePath(), + parent.getRelativePath()); + } else { + relativePath = getRelativePath(rootPath, ""); + relativePathCalculated = true; + } + } + return relativePath; + } + /** + * get the relative path of this file + * @param currentPath base path + * @param currentRelativePath relative path of the base path with regards to remote dir + * @return relative path + */ + private String getRelativePath(String currentPath, String currentRelativePath) { + Vector pathElements = SelectorUtils.tokenizePath(getAbsolutePath(), remoteFileSep); + Vector pathElements2 = SelectorUtils.tokenizePath(currentPath, remoteFileSep); + String relPath = currentRelativePath; + final int size = pathElements.size(); + for (int pcount = pathElements2.size(); pcount < size; pcount++) { + String currentElement = (String) pathElements.elementAt(pcount); + FTPFile[] theFiles = listFiles(currentPath); + FTPFile theFile = null; + if (theFiles != null) { + theFile = getFile(theFiles, currentElement); + } + if (!relPath.equals("")) { + relPath = relPath + remoteFileSep; + } + if (theFile == null) { + // hit a hidden file assume not a symlink + relPath = relPath + currentElement; + currentPath = currentPath + remoteFileSep + currentElement; + log("Hidden file " + relPath + + " assumed to not be a symlink.", + Project.MSG_VERBOSE); + } else { + traversesSymlinks = traversesSymlinks || theFile.isSymbolicLink(); + relPath = relPath + theFile.getName(); + currentPath = currentPath + remoteFileSep + theFile.getName(); + } + } + return relPath; + } + /** + * find a file matching a string in an array of FTPFile. + * This method will find "alpha" when requested for "ALPHA" + * if and only if the caseSensitive attribute is set to false. + * When caseSensitive is set to true, only the exact match is returned. + * @param theFiles array of files + * @param lastpathelement the file name being sought + * @return null if the file cannot be found, otherwise return the matching file. + */ + public FTPFile getFile(FTPFile[] theFiles, String lastpathelement) { + if (theFiles == null) { + return null; + } + for (int fcount = 0; fcount < theFiles.length; fcount++) { + if (theFiles[fcount] != null) { + if (theFiles[fcount].getName().equals(lastpathelement)) { + return theFiles[fcount]; + } else if (!isCaseSensitive() + && theFiles[fcount].getName().equalsIgnoreCase( + lastpathelement)) { + return theFiles[fcount]; + } + } + } + return null; + } + /** + * tell if a file is a directory. + * note that it will return false for symbolic links pointing to directories. + * @return <code>true</code> for directories + */ + public boolean isDirectory() { + return ftpFile.isDirectory(); + } + /** + * tell if a file is a symbolic link + * @return <code>true</code> for symbolic links + */ + public boolean isSymbolicLink() { + return ftpFile.isSymbolicLink(); + } + /** + * return the attached FTP client object. + * Warning : this instance is really shared with the enclosing class. + * @return FTP client + */ + protected FTPClient getClient() { + return client; + } + + /** + * sets the current path of an AntFTPFile + * @param curpwd the current path one wants to set + */ + protected void setCurpwd(String curpwd) { + this.curpwd = curpwd; + } + /** + * returns the path of the directory containing the AntFTPFile. + * of the full path of the file itself in case of AntFTPRootFile + * @return parent directory of the AntFTPFile + */ + public String getCurpwd() { + return curpwd; + } + /** + * returns the path of the directory containing the AntFTPFile. + * of the full path of the file itself in case of AntFTPRootFile + * and appends the remote file separator if necessary. + * @return parent directory of the AntFTPFile + * @since Ant 1.8.2 + */ + public String getCurpwdPlusFileSep() { + return curpwd.endsWith(remoteFileSep) ? curpwd + : curpwd + remoteFileSep; + } + /** + * find out if a symbolic link is encountered in the relative path of this file + * from rootPath. + * @return <code>true</code> if a symbolic link is encountered in the relative path. + * @throws IOException if one of the change directory or directory listing operations + * fails + * @throws BuildException if a path component in the relative path cannot be found. + */ + public boolean isTraverseSymlinks() throws IOException, BuildException { + if (!relativePathCalculated) { + // getRelativePath also finds about symlinks + getRelativePath(); + } + return traversesSymlinks; + } + + /** + * Get a string rep of this object. + * @return a string containing the pwd and the file. + */ + public String toString() { + return "AntFtpFile: " + curpwd + "%" + ftpFile; + } + } + /** + * special class to represent the remote directory itself + * @since Ant 1.6 + */ + protected class AntFTPRootFile extends AntFTPFile { + private String remotedir; + /** + * constructor + * @param aclient FTP client + * @param remotedir remote directory + */ + public AntFTPRootFile(FTPClient aclient, String remotedir) { + super(aclient, null, remotedir); + this.remotedir = remotedir; + try { + this.getClient().changeWorkingDirectory(this.remotedir); + this.setCurpwd(this.getClient().printWorkingDirectory()); + } catch (IOException ioe) { + throw new BuildException(ioe, getLocation()); + } + } + /** + * find the absolute path + * @return absolute path + */ + public String getAbsolutePath() { + return this.getCurpwd(); + } + /** + * find out the relative path to root + * @return empty string + * @throws BuildException actually never + * @throws IOException actually never + */ + public String getRelativePath() throws BuildException, IOException { + return ""; + } + } + } + /** + * check FTPFiles to check whether they function as directories too + * the FTPFile API seem to make directory and symbolic links incompatible + * we want to find out if we can cd to a symbolic link + * @param dir the parent directory of the file to test + * @param file the file to test + * @return true if it is possible to cd to this directory + * @since ant 1.6 + */ + private boolean isFunctioningAsDirectory(FTPClient ftp, String dir, FTPFile file) { + boolean result = false; + String currentWorkingDir = null; + if (file.isDirectory()) { + return true; + } else if (file.isFile()) { + return false; + } + try { + currentWorkingDir = ftp.printWorkingDirectory(); + } catch (IOException ioe) { + getProject().log("could not find current working directory " + dir + + " while checking a symlink", + Project.MSG_DEBUG); + } + if (currentWorkingDir != null) { + try { + result = ftp.changeWorkingDirectory(file.getLink()); + } catch (IOException ioe) { + getProject().log("could not cd to " + file.getLink() + " while checking a symlink", + Project.MSG_DEBUG); + } + if (result) { + boolean comeback = false; + try { + comeback = ftp.changeWorkingDirectory(currentWorkingDir); + } catch (IOException ioe) { + getProject().log("could not cd back to " + dir + " while checking a symlink", + Project.MSG_ERR); + } finally { + if (!comeback) { + throw new BuildException("could not cd back to " + dir + + " while checking a symlink"); + } + } + } + } + return result; + } + /** + * check FTPFiles to check whether they function as directories too + * the FTPFile API seem to make directory and symbolic links incompatible + * we want to find out if we can cd to a symbolic link + * @param dir the parent directory of the file to test + * @param file the file to test + * @return true if it is possible to cd to this directory + * @since ant 1.6 + */ + private boolean isFunctioningAsFile(FTPClient ftp, String dir, FTPFile file) { + if (file.isDirectory()) { + return false; + } else if (file.isFile()) { + return true; + } + return !isFunctioningAsDirectory(ftp, dir, file); + } + /** + * Sets the remote directory where files will be placed. This may be a + * relative or absolute path, and must be in the path syntax expected by + * the remote server. No correction of path syntax will be performed. + * + * @param dir the remote directory name. + */ + public void setRemotedir(String dir) { + this.remotedir = dir; + } + + + /** + * Sets the FTP server to send files to. + * + * @param server the remote server name. + */ + public void setServer(String server) { + this.server = server; + } + + + /** + * Sets the FTP port used by the remote server. + * + * @param port the port on which the remote server is listening. + */ + public void setPort(int port) { + this.port = port; + } + + + /** + * Sets the login user id to use on the specified server. + * + * @param userid remote system userid. + */ + public void setUserid(String userid) { + this.userid = userid; + } + + + /** + * Sets the login password for the given user id. + * + * @param password the password on the remote system. + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * Sets the login account to use on the specified server. + * + * @param pAccount the account name on remote system + * @since Ant 1.7 + */ + public void setAccount(String pAccount) { + this.account = pAccount; + } + + + /** + * If true, uses binary mode, otherwise text mode (default is binary). + * + * @param binary if true use binary mode in transfers. + */ + public void setBinary(boolean binary) { + this.binary = binary; + } + + + /** + * Specifies whether to use passive mode. Set to true if you are behind a + * firewall and cannot connect without it. Passive mode is disabled by + * default. + * + * @param passive true is passive mode should be used. + */ + public void setPassive(boolean passive) { + this.passive = passive; + } + + + /** + * Set to true to receive notification about each file as it is + * transferred. + * + * @param verbose true if verbose notifications are required. + */ + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + + /** + * A synonym for <tt>depends</tt>. Set to true to transmit only new + * or changed files. + * + * See the related attributes timediffmillis and timediffauto. + * + * @param newer if true only transfer newer files. + */ + public void setNewer(boolean newer) { + this.newerOnly = newer; + } + + /** + * number of milliseconds to add to the time on the remote machine + * to get the time on the local machine. + * + * use in conjunction with <code>newer</code> + * + * @param timeDiffMillis number of milliseconds + * + * @since ant 1.6 + */ + public void setTimeDiffMillis(long timeDiffMillis) { + this.timeDiffMillis = timeDiffMillis; + } + + /** + * "true" to find out automatically the time difference + * between local and remote machine. + * + * This requires right to create + * and delete a temporary file in the remote directory. + * + * @param timeDiffAuto true = find automatically the time diff + * + * @since ant 1.6 + */ + public void setTimeDiffAuto(boolean timeDiffAuto) { + this.timeDiffAuto = timeDiffAuto; + } + + /** + * Set to true to preserve modification times for "gotten" files. + * + * @param preserveLastModified if true preserver modification times. + */ + public void setPreserveLastModified(boolean preserveLastModified) { + this.preserveLastModified = preserveLastModified; + } + + + /** + * Set to true to transmit only files that are new or changed from their + * remote counterparts. The default is to transmit all files. + * + * @param depends if true only transfer newer files. + */ + public void setDepends(boolean depends) { + this.newerOnly = depends; + } + + + /** + * Sets the remote file separator character. This normally defaults to the + * Unix standard forward slash, but can be manually overridden using this + * call if the remote server requires some other separator. Only the first + * character of the string is used. + * + * @param separator the file separator on the remote system. + */ + public void setSeparator(String separator) { + remoteFileSep = separator; + } + + + /** + * Sets the file permission mode (Unix only) for files sent to the + * server. + * + * @param theMode unix style file mode for the files sent to the remote + * system. + */ + public void setChmod(String theMode) { + this.chmod = theMode; + } + + + /** + * Sets the default mask for file creation on a unix server. + * + * @param theUmask unix style umask for files created on the remote server. + */ + public void setUmask(String theUmask) { + this.umask = theUmask; + } + + + /** + * A set of files to upload or download + * + * @param set the set of files to be added to the list of files to be + * transferred. + */ + public void addFileset(FileSet set) { + filesets.addElement(set); + } + + + /** + * Sets the FTP action to be taken. Currently accepts "put", "get", "del", + * "mkdir", "chmod", "list", and "site". + * + * @deprecated since 1.5.x. + * setAction(String) is deprecated and is replaced with + * setAction(FTP.Action) to make Ant's Introspection mechanism do the + * work and also to encapsulate operations on the type in its own + * class. + * @ant.attribute ignore="true" + * + * @param action the FTP action to be performed. + * + * @throws BuildException if the action is not a valid action. + */ + public void setAction(String action) throws BuildException { + log("DEPRECATED - The setAction(String) method has been deprecated." + + " Use setAction(FTP.Action) instead."); + + Action a = new Action(); + + a.setValue(action); + this.action = a.getAction(); + } + + + /** + * Sets the FTP action to be taken. Currently accepts "put", "get", "del", + * "mkdir", "chmod", "list", and "site". + * + * @param action the FTP action to be performed. + * + * @throws BuildException if the action is not a valid action. + */ + public void setAction(Action action) throws BuildException { + this.action = action.getAction(); + } + + + /** + * The output file for the "list" action. This attribute is ignored for + * any other actions. + * + * @param listing file in which to store the listing. + */ + public void setListing(File listing) { + this.listing = listing; + } + + + /** + * If true, enables unsuccessful file put, delete and get + * operations to be skipped with a warning and the remainder + * of the files still transferred. + * + * @param skipFailedTransfers true if failures in transfers are ignored. + */ + public void setSkipFailedTransfers(boolean skipFailedTransfers) { + this.skipFailedTransfers = skipFailedTransfers; + } + + + /** + * set the flag to skip errors on directory creation. + * (and maybe later other server specific errors) + * + * @param ignoreNoncriticalErrors true if non-critical errors should not + * cause a failure. + */ + public void setIgnoreNoncriticalErrors(boolean ignoreNoncriticalErrors) { + this.ignoreNoncriticalErrors = ignoreNoncriticalErrors; + } + + private void configurationHasBeenSet() { + this.isConfigurationSet = true; + } + + /** + * Sets the systemTypeKey attribute. + * Method for setting <code>FTPClientConfig</code> remote system key. + * + * @param systemKey the key to be set - BUT if blank + * the default value of null (which signifies "autodetect") will be kept. + * @see org.apache.commons.net.ftp.FTPClientConfig + */ + public void setSystemTypeKey(FTPSystemType systemKey) { + if (systemKey != null && !systemKey.getValue().equals("")) { + this.systemTypeKey = systemKey; + configurationHasBeenSet(); + } + } + + /** + * Sets the defaultDateFormatConfig attribute. + * @param defaultDateFormat configuration to be set, unless it is + * null or empty string, in which case ignored. + * @see org.apache.commons.net.ftp.FTPClientConfig + */ + public void setDefaultDateFormatConfig(String defaultDateFormat) { + if (defaultDateFormat != null && !defaultDateFormat.equals("")) { + this.defaultDateFormatConfig = defaultDateFormat; + configurationHasBeenSet(); + } + } + + /** + * Sets the recentDateFormatConfig attribute. + * @param recentDateFormat configuration to be set, unless it is + * null or empty string, in which case ignored. + * @see org.apache.commons.net.ftp.FTPClientConfig + */ + public void setRecentDateFormatConfig(String recentDateFormat) { + if (recentDateFormat != null && !recentDateFormat.equals("")) { + this.recentDateFormatConfig = recentDateFormat; + configurationHasBeenSet(); + } + } + + /** + * Sets the serverLanguageCode attribute. + * @param serverLanguageCode configuration to be set, unless it is + * null or empty string, in which case ignored. + * @see org.apache.commons.net.ftp.FTPClientConfig + */ + public void setServerLanguageCodeConfig(LanguageCode serverLanguageCode) { + if (serverLanguageCode != null && !"".equals(serverLanguageCode.getValue())) { + this.serverLanguageCodeConfig = serverLanguageCode; + configurationHasBeenSet(); + } + } + + /** + * Sets the serverTimeZoneConfig attribute. + * @param serverTimeZoneId configuration to be set, unless it is + * null or empty string, in which case ignored. + * @see org.apache.commons.net.ftp.FTPClientConfig + */ + public void setServerTimeZoneConfig(String serverTimeZoneId) { + if (serverTimeZoneId != null && !serverTimeZoneId.equals("")) { + this.serverTimeZoneConfig = serverTimeZoneId; + configurationHasBeenSet(); + } + } + + /** + * Sets the shortMonthNamesConfig attribute + * + * @param shortMonthNames configuration to be set, unless it is + * null or empty string, in which case ignored. + * @see org.apache.commons.net.ftp.FTPClientConfig + */ + public void setShortMonthNamesConfig(String shortMonthNames) { + if (shortMonthNames != null && !shortMonthNames.equals("")) { + this.shortMonthNamesConfig = shortMonthNames; + configurationHasBeenSet(); + } + } + + + + /** + * Defines how many times to retry executing FTP command before giving up. + * Default is 0 - try once and if failure then give up. + * + * @param retriesAllowed number of retries to allow. -1 means + * keep trying forever. "forever" may also be specified as a + * synonym for -1. + */ + public void setRetriesAllowed(String retriesAllowed) { + if ("FOREVER".equalsIgnoreCase(retriesAllowed)) { + this.retriesAllowed = Retryable.RETRY_FOREVER; + } else { + try { + int retries = Integer.parseInt(retriesAllowed); + if (retries < Retryable.RETRY_FOREVER) { + throw new BuildException( + "Invalid value for retriesAllowed attribute: " + + retriesAllowed); + + } + this.retriesAllowed = retries; + } catch (NumberFormatException px) { + throw new BuildException( + "Invalid value for retriesAllowed attribute: " + + retriesAllowed); + + } + + } + } + /** + * @return Returns the systemTypeKey. + */ + public String getSystemTypeKey() { + return systemTypeKey.getValue(); + } + /** + * @return Returns the defaultDateFormatConfig. + */ + public String getDefaultDateFormatConfig() { + return defaultDateFormatConfig; + } + /** + * @return Returns the recentDateFormatConfig. + */ + public String getRecentDateFormatConfig() { + return recentDateFormatConfig; + } + /** + * @return Returns the serverLanguageCodeConfig. + */ + public String getServerLanguageCodeConfig() { + return serverLanguageCodeConfig.getValue(); + } + /** + * @return Returns the serverTimeZoneConfig. + */ + public String getServerTimeZoneConfig() { + return serverTimeZoneConfig; + } + /** + * @return Returns the shortMonthNamesConfig. + */ + public String getShortMonthNamesConfig() { + return shortMonthNamesConfig; + } + /** + * @return Returns the timestampGranularity. + */ + Granularity getTimestampGranularity() { + return timestampGranularity; + } + /** + * Sets the timestampGranularity attribute + * @param timestampGranularity The timestampGranularity to set. + */ + public void setTimestampGranularity(Granularity timestampGranularity) { + if (null == timestampGranularity || "".equals(timestampGranularity.getValue())) { + return; + } + this.timestampGranularity = timestampGranularity; + } + /** + * Sets the siteCommand attribute. This attribute + * names the command that will be executed if the action + * is "site". + * @param siteCommand The siteCommand to set. + */ + public void setSiteCommand(String siteCommand) { + this.siteCommand = siteCommand; + } + /** + * Sets the initialSiteCommand attribute. This attribute + * names a site command that will be executed immediately + * after connection. + * @param initialCommand The initialSiteCommand to set. + */ + public void setInitialSiteCommand(String initialCommand) { + this.initialSiteCommand = initialCommand; + } + + /** + * Whether to verify that data and control connections are + * connected to the same remote host. + * + * @since Ant 1.8.0 + */ + public void setEnableRemoteVerification(boolean b) { + enableRemoteVerification = b; + } + + /** + * Checks to see that all required parameters are set. + * + * @throws BuildException if the configuration is not valid. + */ + protected void checkAttributes() throws BuildException { + if (server == null) { + throw new BuildException("server attribute must be set!"); + } + if (userid == null) { + throw new BuildException("userid attribute must be set!"); + } + if (password == null) { + throw new BuildException("password attribute must be set!"); + } + + if ((action == LIST_FILES) && (listing == null)) { + throw new BuildException("listing attribute must be set for list " + + "action!"); + } + + if (action == MK_DIR && remotedir == null) { + throw new BuildException("remotedir attribute must be set for " + + "mkdir action!"); + } + + if (action == CHMOD && chmod == null) { + throw new BuildException("chmod attribute must be set for chmod " + + "action!"); + } + if (action == SITE_CMD && siteCommand == null) { + throw new BuildException("sitecommand attribute must be set for site " + + "action!"); + } + + + if (this.isConfigurationSet) { + try { + Class.forName("org.apache.commons.net.ftp.FTPClientConfig"); + } catch (ClassNotFoundException e) { + throw new BuildException( + "commons-net.jar >= 1.4.0 is required for at least one" + + " of the attributes specified."); + } + } + } + + /** + * Executable a retryable object. + * @param h the retry handler. + * @param r the object that should be retried until it succeeds + * or the number of retrys is reached. + * @param descr a description of the command that is being run. + * @throws IOException if there is a problem. + */ + protected void executeRetryable(RetryHandler h, Retryable r, String descr) + throws IOException { + h.execute(r, descr); + } + + + /** + * For each file in the fileset, do the appropriate action: send, get, + * delete, or list. + * + * @param ftp the FTPClient instance used to perform FTP actions + * @param fs the fileset on which the actions are performed. + * + * @return the number of files to be transferred. + * + * @throws IOException if there is a problem reading a file + * @throws BuildException if there is a problem in the configuration. + */ + protected int transferFiles(final FTPClient ftp, FileSet fs) + throws IOException, BuildException { + DirectoryScanner ds; + if (action == SEND_FILES) { + ds = fs.getDirectoryScanner(getProject()); + } else { + ds = new FTPDirectoryScanner(ftp); + fs.setupDirectoryScanner(ds, getProject()); + ds.setFollowSymlinks(fs.isFollowSymlinks()); + ds.scan(); + } + + String[] dsfiles = null; + if (action == RM_DIR) { + dsfiles = ds.getIncludedDirectories(); + } else { + dsfiles = ds.getIncludedFiles(); + } + String dir = null; + + if ((ds.getBasedir() == null) + && ((action == SEND_FILES) || (action == GET_FILES))) { + throw new BuildException("the dir attribute must be set for send " + + "and get actions"); + } else { + if ((action == SEND_FILES) || (action == GET_FILES)) { + dir = ds.getBasedir().getAbsolutePath(); + } + } + + // If we are doing a listing, we need the output stream created now. + BufferedWriter bw = null; + + try { + if (action == LIST_FILES) { + File pd = listing.getParentFile(); + + if (!pd.exists()) { + pd.mkdirs(); + } + bw = new BufferedWriter(new FileWriter(listing)); + } + RetryHandler h = new RetryHandler(this.retriesAllowed, this); + if (action == RM_DIR) { + // to remove directories, start by the end of the list + // the trunk does not let itself be removed before the leaves + for (int i = dsfiles.length - 1; i >= 0; i--) { + final String dsfile = dsfiles[i]; + executeRetryable(h, new Retryable() { + public void execute() throws IOException { + rmDir(ftp, dsfile); + } + }, dsfile); + } + } else { + final BufferedWriter fbw = bw; + final String fdir = dir; + if (this.newerOnly) { + this.granularityMillis = + this.timestampGranularity.getMilliseconds(action); + } + for (int i = 0; i < dsfiles.length; i++) { + final String dsfile = dsfiles[i]; + executeRetryable(h, new Retryable() { + public void execute() throws IOException { + switch (action) { + case SEND_FILES: + sendFile(ftp, fdir, dsfile); + break; + case GET_FILES: + getFile(ftp, fdir, dsfile); + break; + case DEL_FILES: + delFile(ftp, dsfile); + break; + case LIST_FILES: + listFile(ftp, fbw, dsfile); + break; + case CHMOD: + doSiteCommand(ftp, "chmod " + chmod + + " " + resolveFile(dsfile)); + transferred++; + break; + default: + throw new BuildException("unknown ftp action " + action); + } + } + }, dsfile); + } + } + } finally { + FileUtils.close(bw); + } + + return dsfiles.length; + } + + + /** + * Sends all files specified by the configured filesets to the remote + * server. + * + * @param ftp the FTPClient instance used to perform FTP actions + * + * @throws IOException if there is a problem reading a file + * @throws BuildException if there is a problem in the configuration. + */ + protected void transferFiles(FTPClient ftp) + throws IOException, BuildException { + transferred = 0; + skipped = 0; + + if (filesets.size() == 0) { + throw new BuildException("at least one fileset must be specified."); + } else { + // get files from filesets + final int size = filesets.size(); + for (int i = 0; i < size; i++) { + FileSet fs = (FileSet) filesets.elementAt(i); + + if (fs != null) { + transferFiles(ftp, fs); + } + } + } + + log(transferred + " " + ACTION_TARGET_STRS[action] + " " + + COMPLETED_ACTION_STRS[action]); + if (skipped != 0) { + log(skipped + " " + ACTION_TARGET_STRS[action] + + " were not successfully " + COMPLETED_ACTION_STRS[action]); + } + } + + + /** + * Correct a file path to correspond to the remote host requirements. This + * implementation currently assumes that the remote end can handle + * Unix-style paths with forward-slash separators. This can be overridden + * with the <code>separator</code> task parameter. No attempt is made to + * determine what syntax is appropriate for the remote host. + * + * @param file the remote file name to be resolved + * + * @return the filename as it will appear on the server. + */ + protected String resolveFile(String file) { + return file.replace(System.getProperty("file.separator").charAt(0), + remoteFileSep.charAt(0)); + } + + + /** + * Creates all parent directories specified in a complete relative + * pathname. Attempts to create existing directories will not cause + * errors. + * + * @param ftp the FTP client instance to use to execute FTP actions on + * the remote server. + * @param filename the name of the file whose parents should be created. + * @throws IOException under non documented circumstances + * @throws BuildException if it is impossible to cd to a remote directory + * + */ + protected void createParents(FTPClient ftp, String filename) + throws IOException, BuildException { + + File dir = new File(filename); + if (dirCache.contains(dir)) { + return; + } + + Vector parents = new Vector(); + String dirname; + + while ((dirname = dir.getParent()) != null) { + File checkDir = new File(dirname); + if (dirCache.contains(checkDir)) { + break; + } + dir = checkDir; + parents.addElement(dir); + } + + // find first non cached dir + int i = parents.size() - 1; + + if (i >= 0) { + String cwd = ftp.printWorkingDirectory(); + String parent = dir.getParent(); + if (parent != null) { + if (!ftp.changeWorkingDirectory(resolveFile(parent))) { + throw new BuildException("could not change to " + + "directory: " + ftp.getReplyString()); + } + } + + while (i >= 0) { + dir = (File) parents.elementAt(i--); + // check if dir exists by trying to change into it. + if (!ftp.changeWorkingDirectory(dir.getName())) { + // could not change to it - try to create it + log("creating remote directory " + + resolveFile(dir.getPath()), Project.MSG_VERBOSE); + if (!ftp.makeDirectory(dir.getName())) { + handleMkDirFailure(ftp); + } + if (!ftp.changeWorkingDirectory(dir.getName())) { + throw new BuildException("could not change to " + + "directory: " + ftp.getReplyString()); + } + } + dirCache.add(dir); + } + ftp.changeWorkingDirectory(cwd); + } + } + /** + * auto find the time difference between local and remote + * @param ftp handle to ftp client + * @return number of millis to add to remote time to make it comparable to local time + * @since ant 1.6 + */ + private long getTimeDiff(FTPClient ftp) { + long returnValue = 0; + File tempFile = findFileName(ftp); + try { + // create a local temporary file + FILE_UTILS.createNewFile(tempFile); + long localTimeStamp = tempFile.lastModified(); + BufferedInputStream instream = new BufferedInputStream(new FileInputStream(tempFile)); + ftp.storeFile(tempFile.getName(), instream); + instream.close(); + boolean success = FTPReply.isPositiveCompletion(ftp.getReplyCode()); + if (success) { + FTPFile [] ftpFiles = ftp.listFiles(tempFile.getName()); + if (ftpFiles.length == 1) { + long remoteTimeStamp = ftpFiles[0].getTimestamp().getTime().getTime(); + returnValue = localTimeStamp - remoteTimeStamp; + } + ftp.deleteFile(ftpFiles[0].getName()); + } + // delegate the deletion of the local temp file to the delete task + // because of race conditions occurring on Windows + Delete mydelete = new Delete(); + mydelete.bindToOwner(this); + mydelete.setFile(tempFile.getCanonicalFile()); + mydelete.execute(); + } catch (Exception e) { + throw new BuildException(e, getLocation()); + } + return returnValue; + } + /** + * find a suitable name for local and remote temporary file + */ + private File findFileName(FTPClient ftp) { + FTPFile [] theFiles = null; + final int maxIterations = 1000; + for (int counter = 1; counter < maxIterations; counter++) { + File localFile = FILE_UTILS.createTempFile( + "ant" + Integer.toString(counter), ".tmp", + null, false, false); + String fileName = localFile.getName(); + boolean found = false; + try { + if (theFiles == null) { + theFiles = ftp.listFiles(); + } + for (int counter2 = 0; counter2 < theFiles.length; counter2++) { + if (theFiles[counter2] != null + && theFiles[counter2].getName().equals(fileName)) { + found = true; + break; + } + } + } catch (IOException ioe) { + throw new BuildException(ioe, getLocation()); + } + if (!found) { + localFile.deleteOnExit(); + return localFile; + } + } + return null; + } + + /** + * Checks to see if the remote file is current as compared with the local + * file. Returns true if the target file is up to date. + * @param ftp ftpclient + * @param localFile local file + * @param remoteFile remote file + * @return true if the target file is up to date + * @throws IOException in unknown circumstances + * @throws BuildException if the date of the remote files cannot be found and the action is + * GET_FILES + */ + protected boolean isUpToDate(FTPClient ftp, File localFile, + String remoteFile) + throws IOException, BuildException { + log("checking date for " + remoteFile, Project.MSG_VERBOSE); + + FTPFile[] files = ftp.listFiles(remoteFile); + + // For Microsoft's Ftp-Service an Array with length 0 is + // returned if configured to return listings in "MS-DOS"-Format + if (files == null || files.length == 0) { + // If we are sending files, then assume out of date. + // If we are getting files, then throw an error + + if (action == SEND_FILES) { + log("Could not date test remote file: " + remoteFile + + "assuming out of date.", Project.MSG_VERBOSE); + return false; + } else { + throw new BuildException("could not date test remote file: " + + ftp.getReplyString()); + } + } + + long remoteTimestamp = files[0].getTimestamp().getTime().getTime(); + long localTimestamp = localFile.lastModified(); + long adjustedRemoteTimestamp = + remoteTimestamp + this.timeDiffMillis + this.granularityMillis; + + StringBuffer msg; + synchronized(TIMESTAMP_LOGGING_SDF) { + msg = new StringBuffer(" [") + .append(TIMESTAMP_LOGGING_SDF.format(new Date(localTimestamp))) + .append("] local"); + } + log(msg.toString(), Project.MSG_VERBOSE); + + synchronized(TIMESTAMP_LOGGING_SDF) { + msg = new StringBuffer(" [") + .append(TIMESTAMP_LOGGING_SDF.format(new Date(adjustedRemoteTimestamp))) + .append("] remote"); + } + if (remoteTimestamp != adjustedRemoteTimestamp) { + synchronized(TIMESTAMP_LOGGING_SDF) { + msg.append(" - (raw: ") + .append(TIMESTAMP_LOGGING_SDF.format(new Date(remoteTimestamp))) + .append(")"); + } + } + log(msg.toString(), Project.MSG_VERBOSE); + + + + if (this.action == SEND_FILES) { + return adjustedRemoteTimestamp >= localTimestamp; + } else { + return localTimestamp >= adjustedRemoteTimestamp; + } + } + + + /** + * Sends a site command to the ftp server + * @param ftp ftp client + * @param theCMD command to execute + * @throws IOException in unknown circumstances + * @throws BuildException in unknown circumstances + */ + protected void doSiteCommand(FTPClient ftp, String theCMD) + throws IOException, BuildException { + boolean rc; + String[] myReply = null; + + log("Doing Site Command: " + theCMD, Project.MSG_VERBOSE); + + rc = ftp.sendSiteCommand(theCMD); + + if (!rc) { + log("Failed to issue Site Command: " + theCMD, Project.MSG_WARN); + } else { + + myReply = ftp.getReplyStrings(); + + for (int x = 0; x < myReply.length; x++) { + if (myReply[x] != null && myReply[x].indexOf("200") == -1) { + log(myReply[x], Project.MSG_WARN); + } + } + } + } + + + /** + * Sends a single file to the remote host. <code>filename</code> may + * contain a relative path specification. When this is the case, <code>sendFile</code> + * will attempt to create any necessary parent directories before sending + * the file. The file will then be sent using the entire relative path + * spec - no attempt is made to change directories. It is anticipated that + * this may eventually cause problems with some FTP servers, but it + * simplifies the coding. + * @param ftp ftp client + * @param dir base directory of the file to be sent (local) + * @param filename relative path of the file to be send + * locally relative to dir + * remotely relative to the remotedir attribute + * @throws IOException in unknown circumstances + * @throws BuildException in unknown circumstances + */ + protected void sendFile(FTPClient ftp, String dir, String filename) + throws IOException, BuildException { + InputStream instream = null; + + try { + // TODO - why not simply new File(dir, filename)? + File file = getProject().resolveFile(new File(dir, filename).getPath()); + + if (newerOnly && isUpToDate(ftp, file, resolveFile(filename))) { + return; + } + + if (verbose) { + log("transferring " + file.getAbsolutePath()); + } + + instream = new BufferedInputStream(new FileInputStream(file)); + + createParents(ftp, filename); + + ftp.storeFile(resolveFile(filename), instream); + + boolean success = FTPReply.isPositiveCompletion(ftp.getReplyCode()); + + if (!success) { + String s = "could not put file: " + ftp.getReplyString(); + + if (skipFailedTransfers) { + log(s, Project.MSG_WARN); + skipped++; + } else { + throw new BuildException(s); + } + + } else { + // see if we should issue a chmod command + if (chmod != null) { + doSiteCommand(ftp, "chmod " + chmod + " " + resolveFile(filename)); + } + log("File " + file.getAbsolutePath() + " copied to " + server, + Project.MSG_VERBOSE); + transferred++; + } + } finally { + FileUtils.close(instream); + } + } + + + /** + * Delete a file from the remote host. + * @param ftp ftp client + * @param filename file to delete + * @throws IOException in unknown circumstances + * @throws BuildException if skipFailedTransfers is set to false + * and the deletion could not be done + */ + protected void delFile(FTPClient ftp, String filename) + throws IOException, BuildException { + if (verbose) { + log("deleting " + filename); + } + + if (!ftp.deleteFile(resolveFile(filename))) { + String s = "could not delete file: " + ftp.getReplyString(); + + if (skipFailedTransfers) { + log(s, Project.MSG_WARN); + skipped++; + } else { + throw new BuildException(s); + } + } else { + log("File " + filename + " deleted from " + server, + Project.MSG_VERBOSE); + transferred++; + } + } + + /** + * Delete a directory, if empty, from the remote host. + * @param ftp ftp client + * @param dirname directory to delete + * @throws IOException in unknown circumstances + * @throws BuildException if skipFailedTransfers is set to false + * and the deletion could not be done + */ + protected void rmDir(FTPClient ftp, String dirname) + throws IOException, BuildException { + if (verbose) { + log("removing " + dirname); + } + + if (!ftp.removeDirectory(resolveFile(dirname))) { + String s = "could not remove directory: " + ftp.getReplyString(); + + if (skipFailedTransfers) { + log(s, Project.MSG_WARN); + skipped++; + } else { + throw new BuildException(s); + } + } else { + log("Directory " + dirname + " removed from " + server, + Project.MSG_VERBOSE); + transferred++; + } + } + + + /** + * Retrieve a single file from the remote host. <code>filename</code> may + * contain a relative path specification. <p> + * + * The file will then be retrieved using the entire relative path spec - + * no attempt is made to change directories. It is anticipated that this + * may eventually cause problems with some FTP servers, but it simplifies + * the coding.</p> + * @param ftp the ftp client + * @param dir local base directory to which the file should go back + * @param filename relative path of the file based upon the ftp remote directory + * and/or the local base directory (dir) + * @throws IOException in unknown circumstances + * @throws BuildException if skipFailedTransfers is false + * and the file cannot be retrieved. + */ + protected void getFile(FTPClient ftp, String dir, String filename) + throws IOException, BuildException { + OutputStream outstream = null; + try { + File file = getProject().resolveFile(new File(dir, filename).getPath()); + + if (newerOnly && isUpToDate(ftp, file, resolveFile(filename))) { + return; + } + + if (verbose) { + log("transferring " + filename + " to " + + file.getAbsolutePath()); + } + + File pdir = file.getParentFile(); + + if (!pdir.exists()) { + pdir.mkdirs(); + } + outstream = new BufferedOutputStream(new FileOutputStream(file)); + ftp.retrieveFile(resolveFile(filename), outstream); + + if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { + String s = "could not get file: " + ftp.getReplyString(); + + if (skipFailedTransfers) { + log(s, Project.MSG_WARN); + skipped++; + } else { + throw new BuildException(s); + } + + } else { + log("File " + file.getAbsolutePath() + " copied from " + + server, Project.MSG_VERBOSE); + transferred++; + if (preserveLastModified) { + outstream.close(); + outstream = null; + FTPFile[] remote = ftp.listFiles(resolveFile(filename)); + if (remote.length > 0) { + FILE_UTILS.setFileLastModified(file, + remote[0].getTimestamp() + .getTime().getTime()); + } + } + } + } finally { + FileUtils.close(outstream); + } + } + + + /** + * List information about a single file from the remote host. <code>filename</code> + * may contain a relative path specification. <p> + * + * The file listing will then be retrieved using the entire relative path + * spec - no attempt is made to change directories. It is anticipated that + * this may eventually cause problems with some FTP servers, but it + * simplifies the coding.</p> + * @param ftp ftp client + * @param bw buffered writer + * @param filename the directory one wants to list + * @throws IOException in unknown circumstances + * @throws BuildException in unknown circumstances + */ + protected void listFile(FTPClient ftp, BufferedWriter bw, String filename) + throws IOException, BuildException { + if (verbose) { + log("listing " + filename); + } + FTPFile[] ftpfiles = ftp.listFiles(resolveFile(filename)); + + if (ftpfiles != null && ftpfiles.length > 0) { + bw.write(ftpfiles[0].toString()); + bw.newLine(); + transferred++; + } + } + + + /** + * Create the specified directory on the remote host. + * + * @param ftp The FTP client connection + * @param dir The directory to create (format must be correct for host + * type) + * @throws IOException in unknown circumstances + * @throws BuildException if ignoreNoncriticalErrors has not been set to true + * and a directory could not be created, for instance because it was + * already existing. Precisely, the codes 521, 550 and 553 will trigger + * a BuildException + */ + protected void makeRemoteDir(FTPClient ftp, String dir) + throws IOException, BuildException { + String workingDirectory = ftp.printWorkingDirectory(); + if (verbose) { + if (dir.startsWith("/") || workingDirectory == null) { + log("Creating directory: " + dir + " in /"); + } else { + log("Creating directory: " + dir + " in " + workingDirectory); + } + } + if (dir.startsWith("/")) { + ftp.changeWorkingDirectory("/"); + } + String subdir = ""; + StringTokenizer st = new StringTokenizer(dir, "/"); + while (st.hasMoreTokens()) { + subdir = st.nextToken(); + log("Checking " + subdir, Project.MSG_DEBUG); + if (!ftp.changeWorkingDirectory(subdir)) { + if (!ftp.makeDirectory(subdir)) { + // codes 521, 550 and 553 can be produced by FTP Servers + // to indicate that an attempt to create a directory has + // failed because the directory already exists. + int rc = ftp.getReplyCode(); + if (!(ignoreNoncriticalErrors + && (rc == CODE_550 || rc == CODE_553 + || rc == CODE_521))) { + throw new BuildException("could not create directory: " + + ftp.getReplyString()); + } + if (verbose) { + log("Directory already exists"); + } + } else { + if (verbose) { + log("Directory created OK"); + } + ftp.changeWorkingDirectory(subdir); + } + } + } + if (workingDirectory != null) { + ftp.changeWorkingDirectory(workingDirectory); + } + } + + /** + * look at the response for a failed mkdir action, decide whether + * it matters or not. If it does, we throw an exception + * @param ftp current ftp connection + * @throws BuildException if this is an error to signal + */ + private void handleMkDirFailure(FTPClient ftp) + throws BuildException { + int rc = ftp.getReplyCode(); + if (!(ignoreNoncriticalErrors + && (rc == CODE_550 || rc == CODE_553 || rc == CODE_521))) { + throw new BuildException("could not create directory: " + + ftp.getReplyString()); + } + } + + /** + * Runs the task. + * + * @throws BuildException if the task fails or is not configured + * correctly. + */ + public void execute() throws BuildException { + checkAttributes(); + + FTPClient ftp = null; + + try { + log("Opening FTP connection to " + server, Project.MSG_VERBOSE); + + ftp = new FTPClient(); + if (this.isConfigurationSet) { + ftp = FTPConfigurator.configure(ftp, this); + } + + ftp.setRemoteVerificationEnabled(enableRemoteVerification); + ftp.connect(server, port); + if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { + throw new BuildException("FTP connection failed: " + + ftp.getReplyString()); + } + + log("connected", Project.MSG_VERBOSE); + log("logging in to FTP server", Project.MSG_VERBOSE); + + if ((this.account != null && !ftp.login(userid, password, account)) + || (this.account == null && !ftp.login(userid, password))) { + throw new BuildException("Could not login to FTP server"); + } + + log("login succeeded", Project.MSG_VERBOSE); + + if (binary) { + ftp.setFileType(org.apache.commons.net.ftp.FTP.BINARY_FILE_TYPE); + if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { + throw new BuildException("could not set transfer type: " + + ftp.getReplyString()); + } + } else { + ftp.setFileType(org.apache.commons.net.ftp.FTP.ASCII_FILE_TYPE); + if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { + throw new BuildException("could not set transfer type: " + + ftp.getReplyString()); + } + } + + if (passive) { + log("entering passive mode", Project.MSG_VERBOSE); + ftp.enterLocalPassiveMode(); + if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { + throw new BuildException("could not enter into passive " + + "mode: " + ftp.getReplyString()); + } + } + + // If an initial command was configured then send it. + // Some FTP servers offer different modes of operation, + // E.G. switching between a UNIX file system mode and + // a legacy file system. + if (this.initialSiteCommand != null) { + RetryHandler h = new RetryHandler(this.retriesAllowed, this); + final FTPClient lftp = ftp; + executeRetryable(h, new Retryable() { + public void execute() throws IOException { + doSiteCommand(lftp, FTP.this.initialSiteCommand); + } + }, "initial site command: " + this.initialSiteCommand); + } + + + // For a unix ftp server you can set the default mask for all files + // created. + + if (umask != null) { + RetryHandler h = new RetryHandler(this.retriesAllowed, this); + final FTPClient lftp = ftp; + executeRetryable(h, new Retryable() { + public void execute() throws IOException { + doSiteCommand(lftp, "umask " + umask); + } + }, "umask " + umask); + } + + // If the action is MK_DIR, then the specified remote + // directory is the directory to create. + + if (action == MK_DIR) { + RetryHandler h = new RetryHandler(this.retriesAllowed, this); + final FTPClient lftp = ftp; + executeRetryable(h, new Retryable() { + public void execute() throws IOException { + makeRemoteDir(lftp, remotedir); + } + }, remotedir); + } else if (action == SITE_CMD) { + RetryHandler h = new RetryHandler(this.retriesAllowed, this); + final FTPClient lftp = ftp; + executeRetryable(h, new Retryable() { + public void execute() throws IOException { + doSiteCommand(lftp, FTP.this.siteCommand); + } + }, "Site Command: " + this.siteCommand); + } else { + if (remotedir != null) { + log("changing the remote directory to " + remotedir, + Project.MSG_VERBOSE); + ftp.changeWorkingDirectory(remotedir); + if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { + throw new BuildException("could not change remote " + + "directory: " + ftp.getReplyString()); + } + } + if (newerOnly && timeDiffAuto) { + // in this case we want to find how much time span there is between local + // and remote + timeDiffMillis = getTimeDiff(ftp); + } + log(ACTION_STRS[action] + " " + ACTION_TARGET_STRS[action]); + transferFiles(ftp); + } + + } catch (IOException ex) { + throw new BuildException("error during FTP transfer: " + ex, ex); + } finally { + if (ftp != null && ftp.isConnected()) { + try { + log("disconnecting", Project.MSG_VERBOSE); + ftp.logout(); + ftp.disconnect(); + } catch (IOException ex) { + // ignore it + } + } + } + } + + + /** + * an action to perform, one of + * "send", "put", "recv", "get", "del", "delete", "list", "mkdir", "chmod", + * "rmdir" + */ + public static class Action extends EnumeratedAttribute { + + private static final String[] VALID_ACTIONS = { + "send", "put", "recv", "get", "del", "delete", "list", "mkdir", + "chmod", "rmdir", "site" + }; + + + /** + * Get the valid values + * + * @return an array of the valid FTP actions. + */ + public String[] getValues() { + return VALID_ACTIONS; + } + + + /** + * Get the symbolic equivalent of the action value. + * + * @return the SYMBOL representing the given action. + */ + public int getAction() { + String actionL = getValue().toLowerCase(Locale.ENGLISH); + if (actionL.equals("send") || actionL.equals("put")) { + return SEND_FILES; + } else if (actionL.equals("recv") || actionL.equals("get")) { + return GET_FILES; + } else if (actionL.equals("del") || actionL.equals("delete")) { + return DEL_FILES; + } else if (actionL.equals("list")) { + return LIST_FILES; + } else if (actionL.equals("chmod")) { + return CHMOD; + } else if (actionL.equals("mkdir")) { + return MK_DIR; + } else if (actionL.equals("rmdir")) { + return RM_DIR; + } else if (actionL.equals("site")) { + return SITE_CMD; + } + return SEND_FILES; + } + } + /** + * represents one of the valid timestamp adjustment values + * recognized by the <code>timestampGranularity</code> attribute.<p> + + * A timestamp adjustment may be used in file transfers for checking + * uptodateness. MINUTE means to add one minute to the server + * timestamp. This is done because FTP servers typically list + * timestamps HH:mm and client FileSystems typically use HH:mm:ss. + * + * The default is to use MINUTE for PUT actions and NONE for GET + * actions, since GETs have the <code>preserveLastModified</code> + * option, which takes care of the problem in most use cases where + * this level of granularity is an issue. + * + */ + public static class Granularity extends EnumeratedAttribute { + + private static final String[] VALID_GRANULARITIES = { + "", "MINUTE", "NONE" + }; + + /** + * Get the valid values. + * @return the list of valid Granularity values + */ + public String[] getValues() { + return VALID_GRANULARITIES; + } + /** + * returns the number of milliseconds associated with + * the attribute, which can vary in some cases depending + * on the value of the action parameter. + * @param action SEND_FILES or GET_FILES + * @return the number of milliseconds associated with + * the attribute, in the context of the supplied action + */ + public long getMilliseconds(int action) { + String granularityU = getValue().toUpperCase(Locale.ENGLISH); + if ("".equals(granularityU)) { + if (action == SEND_FILES) { + return GRANULARITY_MINUTE; + } + } else if ("MINUTE".equals(granularityU)) { + return GRANULARITY_MINUTE; + } + return 0L; + } + static final Granularity getDefault() { + Granularity g = new Granularity(); + g.setValue(""); + return g; + } + + } + /** + * one of the valid system type keys recognized by the systemTypeKey + * attribute. + */ + public static class FTPSystemType extends EnumeratedAttribute { + + private static final String[] VALID_SYSTEM_TYPES = { + "", "UNIX", "VMS", "WINDOWS", "OS/2", "OS/400", + "MVS" + }; + + + /** + * Get the valid values. + * @return the list of valid system types. + */ + public String[] getValues() { + return VALID_SYSTEM_TYPES; + } + + static final FTPSystemType getDefault() { + FTPSystemType ftpst = new FTPSystemType(); + ftpst.setValue(""); + return ftpst; + } + } + /** + * Enumerated class for languages. + */ + public static class LanguageCode extends EnumeratedAttribute { + + + private static final String[] VALID_LANGUAGE_CODES = + getValidLanguageCodes(); + + private static String[] getValidLanguageCodes() { + Collection c = FTPClientConfig.getSupportedLanguageCodes(); + String[] ret = new String[c.size() + 1]; + int i = 0; + ret[i++] = ""; + for (Iterator it = c.iterator(); it.hasNext(); i++) { + ret[i] = (String) it.next(); + } + return ret; + } + + + /** + * Return the value values. + * @return the list of valid language types. + */ + public String[] getValues() { + return VALID_LANGUAGE_CODES; + } + + static final LanguageCode getDefault() { + LanguageCode lc = new LanguageCode(); + lc.setValue(""); + return lc; + } + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPConfigurator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPConfigurator.java new file mode 100644 index 00000000..0604dac9 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPConfigurator.java @@ -0,0 +1,99 @@ +/* + * 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.tools.ant.taskdefs.optional.net; +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPClientConfig; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; + +/** + * The sole purpose of this class is (note that it is package-private + * is to serve as a separate, static compilation unit for importing + * FTPClientConfig, to enable users who wish to use the FTP task + * without using its new features to avoid the need to + * upgrade to jakarta-commons-net 1.4.0, where FTPClientConfig was + * introduced. + */ +// CheckStyle:HideUtilityClassConstructorCheck OFF (bc) +class FTPConfigurator { + /** + * configures the supplied FTPClient with the various + * attributes set in the supplied FTP task. + * @param client the FTPClient to be configured + * @param task the FTP task whose attributes are used to + * configure the client + * @return the client as configured. + */ + static FTPClient configure(FTPClient client, FTPTaskConfig task) { + task.log("custom configuration", Project.MSG_VERBOSE); + FTPClientConfig config; + String systemTypeKey = task.getSystemTypeKey(); + if (systemTypeKey != null && !"".equals(systemTypeKey)) { + config = new FTPClientConfig(systemTypeKey); + task.log("custom config: system key = " + + systemTypeKey, Project.MSG_VERBOSE); + } else { + config = new FTPClientConfig(); + task.log("custom config: system key = default (UNIX)", + Project.MSG_VERBOSE); + } + + String defaultDateFormatConfig = task.getDefaultDateFormatConfig(); + if (defaultDateFormatConfig != null) { + config.setDefaultDateFormatStr(defaultDateFormatConfig); + task.log("custom config: default date format = " + + defaultDateFormatConfig, Project.MSG_VERBOSE); + } + + String recentDateFormatConfig = task.getRecentDateFormatConfig(); + if (recentDateFormatConfig != null) { + config.setRecentDateFormatStr(recentDateFormatConfig); + task.log("custom config: recent date format = " + + recentDateFormatConfig, Project.MSG_VERBOSE); + } + + String serverLanguageCodeConfig = task.getServerLanguageCodeConfig(); + if (serverLanguageCodeConfig != null) { + if (!"".equals(serverLanguageCodeConfig) + && !FTPClientConfig.getSupportedLanguageCodes() + .contains(serverLanguageCodeConfig)) { + throw new BuildException("unsupported language code" + + serverLanguageCodeConfig); + } + config.setServerLanguageCode(serverLanguageCodeConfig); + task.log("custom config: server language code = " + + serverLanguageCodeConfig, Project.MSG_VERBOSE); + } + + String serverTimeZoneConfig = task.getServerTimeZoneConfig(); + if (serverTimeZoneConfig != null) { + config.setServerTimeZoneId(serverTimeZoneConfig); + task.log("custom config: server time zone ID = " + + serverTimeZoneConfig, Project.MSG_VERBOSE); + } + + String shortMonthNamesConfig = task.getShortMonthNamesConfig(); + if (shortMonthNamesConfig != null) { + config.setShortMonthNames(shortMonthNamesConfig); + task.log("custom config: short month names = " + + shortMonthNamesConfig, Project.MSG_VERBOSE); + } + client.configure(config); + return client; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTask.java new file mode 100644 index 00000000..79780c78 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTask.java @@ -0,0 +1,965 @@ +/* + * 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.tools.ant.taskdefs.optional.net; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.util.Locale; +import java.util.Vector; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.EnumeratedAttribute; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.LoaderUtils; +import org.apache.tools.ant.util.Retryable; +import org.apache.tools.ant.util.SplitClassLoader; + +/** + * Basic FTP client. Performs the following actions: + * <ul> + * <li> <strong>send</strong> - send files to a remote server. This is the + * default action.</li> + * <li> <strong>get</strong> - retrieve files from a remote server.</li> + * <li> <strong>del</strong> - delete files from a remote server.</li> + * <li> <strong>list</strong> - create a file listing.</li> + * <li> <strong>chmod</strong> - change unix file permissions.</li> + * <li> <strong>rmdir</strong> - remove directories, if empty, from a + * remote server.</li> + * </ul> + * <strong>Note:</strong> Some FTP servers - notably the Solaris server - seem + * to hold data ports open after a "retr" operation, allowing them to timeout + * instead of shutting them down cleanly. This happens in active or passive + * mode, and the ports will remain open even after ending the FTP session. FTP + * "send" operations seem to close ports immediately. This behavior may cause + * problems on some systems when downloading large sets of files. + * + * @since Ant 1.3 + */ +public class FTPTask extends Task implements FTPTaskConfig { + public static final int SEND_FILES = 0; + public static final int GET_FILES = 1; + public static final int DEL_FILES = 2; + public static final int LIST_FILES = 3; + public static final int MK_DIR = 4; + public static final int CHMOD = 5; + public static final int RM_DIR = 6; + public static final int SITE_CMD = 7; + + /** adjust uptodate calculations where server timestamps are HH:mm and client's + * are HH:mm:ss */ + private static final long GRANULARITY_MINUTE = 60000L; + + /** Default port for FTP */ + public static final int DEFAULT_FTP_PORT = 21; + + private String remotedir; + private String server; + private String userid; + private String password; + private String account; + private File listing; + private boolean binary = true; + private boolean passive = false; + private boolean verbose = false; + private boolean newerOnly = false; + private long timeDiffMillis = 0; + private long granularityMillis = 0L; + private boolean timeDiffAuto = false; + private int action = SEND_FILES; + private Vector filesets = new Vector(); + private String remoteFileSep = "/"; + private int port = DEFAULT_FTP_PORT; + private boolean skipFailedTransfers = false; + private boolean ignoreNoncriticalErrors = false; + private boolean preserveLastModified = false; + private String chmod = null; + private String umask = null; + private FTPSystemType systemTypeKey = FTPSystemType.getDefault(); + private String defaultDateFormatConfig = null; + private String recentDateFormatConfig = null; + private String serverLanguageCodeConfig = null; + private String serverTimeZoneConfig = null; + private String shortMonthNamesConfig = null; + private Granularity timestampGranularity = Granularity.getDefault(); + private boolean isConfigurationSet = false; + private int retriesAllowed = 0; + private String siteCommand = null; + private String initialSiteCommand = null; + private boolean enableRemoteVerification = true; + + private Path classpath; + private ClassLoader mirrorLoader; + private FTPTaskMirror delegate = null; + + public static final String[] ACTION_STRS = { + "sending", + "getting", + "deleting", + "listing", + "making directory", + "chmod", + "removing", + "site" + }; + + public static final String[] COMPLETED_ACTION_STRS = { + "sent", + "retrieved", + "deleted", + "listed", + "created directory", + "mode changed", + "removed", + "site command executed" + }; + + public static final String[] ACTION_TARGET_STRS = { + "files", + "files", + "files", + "files", + "directory", + "files", + "directories", + "site command" + }; + + /** + * Sets the remote directory where files will be placed. This may be a + * relative or absolute path, and must be in the path syntax expected by + * the remote server. No correction of path syntax will be performed. + * + * @param dir the remote directory name. + */ + public void setRemotedir(String dir) { + this.remotedir = dir; + } + + public String getRemotedir() { + return remotedir; + } + + /** + * Sets the FTP server to send files to. + * + * @param server the remote server name. + */ + public void setServer(String server) { + this.server = server; + } + + public String getServer() { + return server; + } + + /** + * Sets the FTP port used by the remote server. + * + * @param port the port on which the remote server is listening. + */ + public void setPort(int port) { + this.port = port; + } + + public int getPort() { + return port; + } + + /** + * Sets the login user id to use on the specified server. + * + * @param userid remote system userid. + */ + public void setUserid(String userid) { + this.userid = userid; + } + + public String getUserid() { + return userid; + } + + /** + * Sets the login password for the given user id. + * + * @param password the password on the remote system. + */ + public void setPassword(String password) { + this.password = password; + } + + public String getPassword() { + return password; + } + + /** + * Sets the login account to use on the specified server. + * + * @param pAccount the account name on remote system + * @since Ant 1.7 + */ + public void setAccount(String pAccount) { + this.account = pAccount; + } + + public String getAccount() { + return account; + } + + /** + * If true, uses binary mode, otherwise text mode (default is binary). + * + * @param binary if true use binary mode in transfers. + */ + public void setBinary(boolean binary) { + this.binary = binary; + } + + public boolean isBinary() { + return binary; + } + + /** + * Specifies whether to use passive mode. Set to true if you are behind a + * firewall and cannot connect without it. Passive mode is disabled by + * default. + * + * @param passive true is passive mode should be used. + */ + public void setPassive(boolean passive) { + this.passive = passive; + } + + public boolean isPassive() { + return passive; + } + + /** + * Set to true to receive notification about each file as it is + * transferred. + * + * @param verbose true if verbose notifications are required. + */ + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + public boolean isVerbose() { + return verbose; + } + + /** + * A synonym for <tt>depends</tt>. Set to true to transmit only new + * or changed files. + * + * See the related attributes timediffmillis and timediffauto. + * + * @param newer if true only transfer newer files. + */ + public void setNewer(boolean newer) { + this.newerOnly = newer; + } + + public boolean isNewer() { + return newerOnly; + } + + /** + * number of milliseconds to add to the time on the remote machine + * to get the time on the local machine. + * + * use in conjunction with <code>newer</code> + * + * @param timeDiffMillis number of milliseconds + * + * @since ant 1.6 + */ + public void setTimeDiffMillis(long timeDiffMillis) { + this.timeDiffMillis = timeDiffMillis; + } + + public long getTimeDiffMillis() { + return timeDiffMillis; + } + + /** + * "true" to find out automatically the time difference + * between local and remote machine. + * + * This requires right to create + * and delete a temporary file in the remote directory. + * + * @param timeDiffAuto true = find automatically the time diff + * + * @since ant 1.6 + */ + public void setTimeDiffAuto(boolean timeDiffAuto) { + this.timeDiffAuto = timeDiffAuto; + } + + public boolean isTimeDiffAuto() { + return timeDiffAuto; + } + + /** + * Set to true to preserve modification times for "gotten" files. + * + * @param preserveLastModified if true preserver modification times. + */ + public void setPreserveLastModified(boolean preserveLastModified) { + this.preserveLastModified = preserveLastModified; + } + + public boolean isPreserveLastModified() { + return preserveLastModified; + } + + /** + * Set to true to transmit only files that are new or changed from their + * remote counterparts. The default is to transmit all files. + * + * @param depends if true only transfer newer files. + */ + public void setDepends(boolean depends) { + this.newerOnly = depends; + } + + + /** + * Sets the remote file separator character. This normally defaults to the + * Unix standard forward slash, but can be manually overridden using this + * call if the remote server requires some other separator. Only the first + * character of the string is used. + * + * @param separator the file separator on the remote system. + */ + public void setSeparator(String separator) { + remoteFileSep = separator; + } + + + public String getSeparator() { + return remoteFileSep; + } + + /** + * Sets the file permission mode (Unix only) for files sent to the + * server. + * + * @param theMode unix style file mode for the files sent to the remote + * system. + */ + public void setChmod(String theMode) { + this.chmod = theMode; + } + + public String getChmod() { + return chmod; + } + + /** + * Sets the default mask for file creation on a unix server. + * + * @param theUmask unix style umask for files created on the remote server. + */ + public void setUmask(String theUmask) { + this.umask = theUmask; + } + + public String getUmask() { + return umask; + } + + /** + * A set of files to upload or download + * + * @param set the set of files to be added to the list of files to be + * transferred. + */ + public void addFileset(FileSet set) { + filesets.addElement(set); + } + + public Vector getFilesets() { + return filesets; + } + + /** + * Sets the FTP action to be taken. Currently accepts "put", "get", "del", + * "mkdir", "chmod", "list", and "site". + * + * @deprecated since 1.5.x. + * setAction(String) is deprecated and is replaced with + * setAction(FTP.Action) to make Ant's Introspection mechanism do the + * work and also to encapsulate operations on the type in its own + * class. + * @ant.attribute ignore="true" + * + * @param action the FTP action to be performed. + * + * @throws BuildException if the action is not a valid action. + */ + public void setAction(String action) throws BuildException { + log("DEPRECATED - The setAction(String) method has been deprecated." + + " Use setAction(FTP.Action) instead."); + + Action a = new Action(); + + a.setValue(action); + this.action = a.getAction(); + } + + + /** + * Sets the FTP action to be taken. Currently accepts "put", "get", "del", + * "mkdir", "chmod", "list", and "site". + * + * @param action the FTP action to be performed. + * + * @throws BuildException if the action is not a valid action. + */ + public void setAction(Action action) throws BuildException { + this.action = action.getAction(); + } + + public int getAction() { + return this.action; + } + + /** + * The output file for the "list" action. This attribute is ignored for + * any other actions. + * + * @param listing file in which to store the listing. + */ + public void setListing(File listing) { + this.listing = listing; + } + + public File getListing() { + return listing; + } + + /** + * If true, enables unsuccessful file put, delete and get + * operations to be skipped with a warning and the remainder + * of the files still transferred. + * + * @param skipFailedTransfers true if failures in transfers are ignored. + */ + public void setSkipFailedTransfers(boolean skipFailedTransfers) { + this.skipFailedTransfers = skipFailedTransfers; + } + + public boolean isSkipFailedTransfers() { + return skipFailedTransfers; + } + + /** + * set the flag to skip errors on directory creation. + * (and maybe later other server specific errors) + * + * @param ignoreNoncriticalErrors true if non-critical errors should not + * cause a failure. + */ + public void setIgnoreNoncriticalErrors(boolean ignoreNoncriticalErrors) { + this.ignoreNoncriticalErrors = ignoreNoncriticalErrors; + } + + public boolean isIgnoreNoncriticalErrors() { + return ignoreNoncriticalErrors; + } + + private void configurationHasBeenSet() { + this.isConfigurationSet = true; + } + + public boolean isConfigurationSet() { + return this.isConfigurationSet; + } + + /** + * Sets the systemTypeKey attribute. + * Method for setting <code>FTPClientConfig</code> remote system key. + * + * @param systemKey the key to be set - BUT if blank + * the default value of null (which signifies "autodetect") will be kept. + * @see org.apache.commons.net.ftp.FTPClientConfig + */ + public void setSystemTypeKey(FTPSystemType systemKey) { + if (systemKey != null && !systemKey.getValue().equals("")) { + this.systemTypeKey = systemKey; + configurationHasBeenSet(); + } + } + + /** + * Sets the defaultDateFormatConfig attribute. + * @param defaultDateFormat configuration to be set, unless it is + * null or empty string, in which case ignored. + * @see org.apache.commons.net.ftp.FTPClientConfig + */ + public void setDefaultDateFormatConfig(String defaultDateFormat) { + if (defaultDateFormat != null && !defaultDateFormat.equals("")) { + this.defaultDateFormatConfig = defaultDateFormat; + configurationHasBeenSet(); + } + } + + /** + * Sets the recentDateFormatConfig attribute. + * @param recentDateFormat configuration to be set, unless it is + * null or empty string, in which case ignored. + * @see org.apache.commons.net.ftp.FTPClientConfig + */ + public void setRecentDateFormatConfig(String recentDateFormat) { + if (recentDateFormat != null && !recentDateFormat.equals("")) { + this.recentDateFormatConfig = recentDateFormat; + configurationHasBeenSet(); + } + } + + /** + * Sets the serverLanguageCode attribute. + * @param serverLanguageCode configuration to be set, unless it is + * null or empty string, in which case ignored. + * @see org.apache.commons.net.ftp.FTPClientConfig + */ + public void setServerLanguageCodeConfig(String serverLanguageCode) { + if (serverLanguageCode != null && !"".equals(serverLanguageCode)) { + this.serverLanguageCodeConfig = serverLanguageCode; + configurationHasBeenSet(); + } + } + + /** + * Sets the serverTimeZoneConfig attribute. + * @param serverTimeZoneId configuration to be set, unless it is + * null or empty string, in which case ignored. + * @see org.apache.commons.net.ftp.FTPClientConfig + */ + public void setServerTimeZoneConfig(String serverTimeZoneId) { + if (serverTimeZoneId != null && !serverTimeZoneId.equals("")) { + this.serverTimeZoneConfig = serverTimeZoneId; + configurationHasBeenSet(); + } + } + + /** + * Sets the shortMonthNamesConfig attribute + * + * @param shortMonthNames configuration to be set, unless it is + * null or empty string, in which case ignored. + * @see org.apache.commons.net.ftp.FTPClientConfig + */ + public void setShortMonthNamesConfig(String shortMonthNames) { + if (shortMonthNames != null && !shortMonthNames.equals("")) { + this.shortMonthNamesConfig = shortMonthNames; + configurationHasBeenSet(); + } + } + + + + /** + * Defines how many times to retry executing FTP command before giving up. + * Default is 0 - try once and if failure then give up. + * + * @param retriesAllowed number of retries to allow. -1 means + * keep trying forever. "forever" may also be specified as a + * synonym for -1. + */ + public void setRetriesAllowed(String retriesAllowed) { + if ("FOREVER".equalsIgnoreCase(retriesAllowed)) { + this.retriesAllowed = Retryable.RETRY_FOREVER; + } else { + try { + int retries = Integer.parseInt(retriesAllowed); + if (retries < Retryable.RETRY_FOREVER) { + throw new BuildException( + "Invalid value for retriesAllowed attribute: " + + retriesAllowed); + + } + this.retriesAllowed = retries; + } catch (NumberFormatException px) { + throw new BuildException( + "Invalid value for retriesAllowed attribute: " + + retriesAllowed); + + } + + } + } + + public int getRetriesAllowed() { + return retriesAllowed; + } + + /** + * @return Returns the systemTypeKey. + */ + public String getSystemTypeKey() { + return systemTypeKey.getValue(); + } + /** + * @return Returns the defaultDateFormatConfig. + */ + public String getDefaultDateFormatConfig() { + return defaultDateFormatConfig; + } + /** + * @return Returns the recentDateFormatConfig. + */ + public String getRecentDateFormatConfig() { + return recentDateFormatConfig; + } + /** + * @return Returns the serverLanguageCodeConfig. + */ + public String getServerLanguageCodeConfig() { + return serverLanguageCodeConfig; + } + /** + * @return Returns the serverTimeZoneConfig. + */ + public String getServerTimeZoneConfig() { + return serverTimeZoneConfig; + } + /** + * @return Returns the shortMonthNamesConfig. + */ + public String getShortMonthNamesConfig() { + return shortMonthNamesConfig; + } + /** + * @return Returns the timestampGranularity. + */ + public Granularity getTimestampGranularity() { + return timestampGranularity; + } + /** + * Sets the timestampGranularity attribute + * @param timestampGranularity The timestampGranularity to set. + */ + public void setTimestampGranularity(Granularity timestampGranularity) { + if (null == timestampGranularity || "".equals(timestampGranularity.getValue())) { + return; + } + this.timestampGranularity = timestampGranularity; + } + /** + * Sets the siteCommand attribute. This attribute + * names the command that will be executed if the action + * is "site". + * @param siteCommand The siteCommand to set. + */ + public void setSiteCommand(String siteCommand) { + this.siteCommand = siteCommand; + } + + public String getSiteCommand() { + return siteCommand; + } + + /** + * Sets the initialSiteCommand attribute. This attribute + * names a site command that will be executed immediately + * after connection. + * @param initialCommand The initialSiteCommand to set. + */ + public void setInitialSiteCommand(String initialCommand) { + this.initialSiteCommand = initialCommand; + } + + public String getInitialSiteCommand() { + return initialSiteCommand; + } + + public long getGranularityMillis() { + return this.granularityMillis; + } + + public void setGranularityMillis(long granularity) { + this.granularityMillis = granularity; + } + + /** + * Whether to verify that data and control connections are + * connected to the same remote host. + * + * @since Ant 1.8.0 + */ + public void setEnableRemoteVerification(boolean b) { + enableRemoteVerification = b; + } + + public boolean getEnableRemoteVerification() { + return enableRemoteVerification; + } + + /** + * Checks to see that all required parameters are set. + * + * @throws BuildException if the configuration is not valid. + */ + protected void checkAttributes() throws BuildException { + if (server == null) { + throw new BuildException("server attribute must be set!"); + } + if (userid == null) { + throw new BuildException("userid attribute must be set!"); + } + if (password == null) { + throw new BuildException("password attribute must be set!"); + } + + if ((action == LIST_FILES) && (listing == null)) { + throw new BuildException("listing attribute must be set for list " + + "action!"); + } + + if (action == MK_DIR && remotedir == null) { + throw new BuildException("remotedir attribute must be set for " + + "mkdir action!"); + } + + if (action == CHMOD && chmod == null) { + throw new BuildException("chmod attribute must be set for chmod " + + "action!"); + } + if (action == SITE_CMD && siteCommand == null) { + throw new BuildException("sitecommand attribute must be set for site " + + "action!"); + } + + + if (this.isConfigurationSet) { + try { + Class.forName("org.apache.commons.net.ftp.FTPClientConfig"); + } catch (ClassNotFoundException e) { + throw new BuildException( + "commons-net.jar >= 1.4.0 is required for at least one" + + " of the attributes specified."); + } + } + } + + /** + * Runs the task. + * + * @throws BuildException if the task fails or is not configured + * correctly. + */ + public void execute() throws BuildException { + checkAttributes(); + try { + setupFTPDelegate(); + delegate.doFTP(); + } finally { + if (mirrorLoader instanceof SplitClassLoader) { + ((SplitClassLoader) mirrorLoader).cleanup(); + } + mirrorLoader = null; + } + } + + public Path createClasspath() { + if (classpath == null) { + classpath = new Path(getProject()); + } + return classpath; + } + + protected void setupFTPDelegate() { + ClassLoader myLoader = FTPTask.class.getClassLoader(); + if (mustSplit()) { + mirrorLoader = + new SplitClassLoader(myLoader, classpath, getProject(), + new String[] { + "FTPTaskMirrorImpl", + "FTPConfigurator" + }); + } else { + mirrorLoader = myLoader; + } + delegate = createMirror(this, mirrorLoader); + } + + private static boolean mustSplit() { + return LoaderUtils.getResourceSource(FTPTask.class.getClassLoader(), + "/org/apache/commons/net/" + + "ftp/FTP.class") + == null; + } + + private static FTPTaskMirror createMirror(FTPTask task, + ClassLoader loader) { + try { + loader.loadClass("org.apache.commons.net.ftp.FTP"); // sanity check + } catch (ClassNotFoundException e) { + throw new BuildException("The <classpath> for <ftp> must include" + + " commons-net.jar if not in Ant's own " + + " classpath", e, task.getLocation()); + } + try { + Class c = loader.loadClass(FTPTaskMirror.class.getName() + "Impl"); + if (c.getClassLoader() != loader) { + throw new BuildException("Overdelegating loader", + task.getLocation()); + } + Constructor cons = c.getConstructor(new Class[] {FTPTask.class}); + return (FTPTaskMirror) cons.newInstance(new Object[] {task}); + } catch (Exception e) { + throw new BuildException(e, task.getLocation()); + } + } + + /** + * an action to perform, one of + * "send", "put", "recv", "get", "del", "delete", "list", "mkdir", "chmod", + * "rmdir" + */ + public static class Action extends EnumeratedAttribute { + + private static final String[] VALID_ACTIONS = { + "send", "put", "recv", "get", "del", "delete", "list", "mkdir", + "chmod", "rmdir", "site" + }; + + + /** + * Get the valid values + * + * @return an array of the valid FTP actions. + */ + public String[] getValues() { + return VALID_ACTIONS; + } + + + /** + * Get the symbolic equivalent of the action value. + * + * @return the SYMBOL representing the given action. + */ + public int getAction() { + String actionL = getValue().toLowerCase(Locale.ENGLISH); + if (actionL.equals("send") || actionL.equals("put")) { + return SEND_FILES; + } else if (actionL.equals("recv") || actionL.equals("get")) { + return GET_FILES; + } else if (actionL.equals("del") || actionL.equals("delete")) { + return DEL_FILES; + } else if (actionL.equals("list")) { + return LIST_FILES; + } else if (actionL.equals("chmod")) { + return CHMOD; + } else if (actionL.equals("mkdir")) { + return MK_DIR; + } else if (actionL.equals("rmdir")) { + return RM_DIR; + } else if (actionL.equals("site")) { + return SITE_CMD; + } + return SEND_FILES; + } + } + /** + * represents one of the valid timestamp adjustment values + * recognized by the <code>timestampGranularity</code> attribute.<p> + + * A timestamp adjustment may be used in file transfers for checking + * uptodateness. MINUTE means to add one minute to the server + * timestamp. This is done because FTP servers typically list + * timestamps HH:mm and client FileSystems typically use HH:mm:ss. + * + * The default is to use MINUTE for PUT actions and NONE for GET + * actions, since GETs have the <code>preserveLastModified</code> + * option, which takes care of the problem in most use cases where + * this level of granularity is an issue. + * + */ + public static class Granularity extends EnumeratedAttribute { + + private static final String[] VALID_GRANULARITIES = { + "", "MINUTE", "NONE" + }; + + /** + * Get the valid values. + * @return the list of valid Granularity values + */ + public String[] getValues() { + return VALID_GRANULARITIES; + } + /** + * returns the number of milliseconds associated with + * the attribute, which can vary in some cases depending + * on the value of the action parameter. + * @param action SEND_FILES or GET_FILES + * @return the number of milliseconds associated with + * the attribute, in the context of the supplied action + */ + public long getMilliseconds(int action) { + String granularityU = getValue().toUpperCase(Locale.ENGLISH); + if ("".equals(granularityU)) { + if (action == SEND_FILES) { + return GRANULARITY_MINUTE; + } + } else if ("MINUTE".equals(granularityU)) { + return GRANULARITY_MINUTE; + } + return 0L; + } + static final Granularity getDefault() { + Granularity g = new Granularity(); + g.setValue(""); + return g; + } + + } + /** + * one of the valid system type keys recognized by the systemTypeKey + * attribute. + */ + public static class FTPSystemType extends EnumeratedAttribute { + + private static final String[] VALID_SYSTEM_TYPES = { + "", "UNIX", "VMS", "WINDOWS", "OS/2", "OS/400", + "MVS" + }; + + + /** + * Get the valid values. + * @return the list of valid system types. + */ + public String[] getValues() { + return VALID_SYSTEM_TYPES; + } + + static final FTPSystemType getDefault() { + FTPSystemType ftpst = new FTPSystemType(); + ftpst.setValue(""); + return ftpst; + } + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskConfig.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskConfig.java new file mode 100644 index 00000000..006df61f --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskConfig.java @@ -0,0 +1,28 @@ +/* + * 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.tools.ant.taskdefs.optional.net; + +interface FTPTaskConfig { + void log(String msg, int level); + String getSystemTypeKey(); + String getDefaultDateFormatConfig(); + String getRecentDateFormatConfig(); + String getServerLanguageCodeConfig(); + String getServerTimeZoneConfig(); + String getShortMonthNamesConfig(); +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskMirror.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskMirror.java new file mode 100644 index 00000000..5d09e6f6 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskMirror.java @@ -0,0 +1,24 @@ +/* + * 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.tools.ant.taskdefs.optional.net; + +import org.apache.tools.ant.BuildException; + +public interface FTPTaskMirror { + void doFTP() throws BuildException; +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskMirrorImpl.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskMirrorImpl.java new file mode 100644 index 00000000..a4f24130 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskMirrorImpl.java @@ -0,0 +1,1951 @@ +/* + * 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.tools.ant.taskdefs.optional.net; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.Vector; + +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPFile; +import org.apache.commons.net.ftp.FTPReply; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Delete; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.selectors.SelectorUtils; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.RetryHandler; +import org.apache.tools.ant.util.Retryable; +import org.apache.tools.ant.util.VectorSet; + +public class FTPTaskMirrorImpl implements FTPTaskMirror { + + /** return code of ftp */ + private static final int CODE_521 = 521; + private static final int CODE_550 = 550; + private static final int CODE_553 = 553; + + /** Date formatter used in logging, note not thread safe! */ + private static final SimpleDateFormat TIMESTAMP_LOGGING_SDF = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + private final FTPTask task; + private Set dirCache = new HashSet(); + private int transferred = 0; + private int skipped = 0; + + /** + * Constructor. + * @param task the FTPTask that uses this mirror. + */ + public FTPTaskMirrorImpl(FTPTask task) { + this.task = task; + } + + /** + * internal class providing a File-like interface to some of the information + * available from the FTP server + * + */ + protected static class FTPFileProxy extends File { + + private final FTPFile file; + private final String[] parts; + private final String name; + + /** + * creates a proxy to a FTP file + * @param file + */ + public FTPFileProxy(FTPFile file) { + super(file.getName()); + name = file.getName(); + this.file = file; + parts = FileUtils.getPathStack(name); + } + + /** + * creates a proxy to a FTP directory + * @param completePath the remote directory. + */ + public FTPFileProxy(String completePath) { + super(completePath); + file = null; + name = completePath; + parts = FileUtils.getPathStack(completePath); + } + + + /* (non-Javadoc) + * @see java.io.File#exists() + */ + public boolean exists() { + return true; + } + + + /* (non-Javadoc) + * @see java.io.File#getAbsolutePath() + */ + public String getAbsolutePath() { + return name; + } + + + /* (non-Javadoc) + * @see java.io.File#getName() + */ + public String getName() { + return parts.length > 0 ? parts[parts.length - 1] : name; + } + + + /* (non-Javadoc) + * @see java.io.File#getParent() + */ + public String getParent() { + String result = ""; + for(int i = 0; i < parts.length - 1; i++){ + result += File.separatorChar + parts[i]; + } + return result; + } + + + /* (non-Javadoc) + * @see java.io.File#getPath() + */ + public String getPath() { + return name; + } + + + /** + * FTP files are stored as absolute paths + * @return true + */ + public boolean isAbsolute() { + return true; + } + + + /* (non-Javadoc) + * @see java.io.File#isDirectory() + */ + public boolean isDirectory() { + return file == null; + } + + + /* (non-Javadoc) + * @see java.io.File#isFile() + */ + public boolean isFile() { + return file != null; + } + + + /** + * FTP files cannot be hidden + * + * @return false + */ + public boolean isHidden() { + return false; + } + + + /* (non-Javadoc) + * @see java.io.File#lastModified() + */ + public long lastModified() { + if (file != null) { + return file.getTimestamp().getTimeInMillis(); + } + return 0; + } + + + /* (non-Javadoc) + * @see java.io.File#length() + */ + public long length() { + if (file != null) { + return file.getSize(); + } + return 0; + } + } + + /** + * internal class allowing to read the contents of a remote file system + * using the FTP protocol + * used in particular for ftp get operations + * differences with DirectoryScanner + * "" (the root of the fileset) is never included in the included directories + * followSymlinks defaults to false + */ + protected class FTPDirectoryScanner extends DirectoryScanner { + // CheckStyle:VisibilityModifier OFF - bc + protected FTPClient ftp = null; + // CheckStyle:VisibilityModifier ON + + private String rootPath = null; + + /** + * since ant 1.6 + * this flag should be set to true on UNIX and can save scanning time + */ + private boolean remoteSystemCaseSensitive = false; + private boolean remoteSensitivityChecked = false; + + /** + * constructor + * @param ftp ftpclient object + */ + public FTPDirectoryScanner(FTPClient ftp) { + super(); + this.ftp = ftp; + this.setFollowSymlinks(false); + } + + + /** + * scans the remote directory, + * storing internally the included files, directories, ... + */ + public void scan() { + if (includes == null) { + // No includes supplied, so set it to 'matches all' + includes = new String[1]; + includes[0] = "**"; + } + if (excludes == null) { + excludes = new String[0]; + } + + filesIncluded = new VectorSet(); + filesNotIncluded = new Vector(); + filesExcluded = new VectorSet(); + dirsIncluded = new VectorSet(); + dirsNotIncluded = new Vector(); + dirsExcluded = new VectorSet(); + + try { + String cwd = ftp.printWorkingDirectory(); + // always start from the current ftp working dir + forceRemoteSensitivityCheck(); + + checkIncludePatterns(); + clearCaches(); + ftp.changeWorkingDirectory(cwd); + } catch (IOException e) { + throw new BuildException("Unable to scan FTP server: ", e); + } + } + + + /** + * this routine is actually checking all the include patterns in + * order to avoid scanning everything under base dir + * @since ant1.6 + */ + private void checkIncludePatterns() { + + Hashtable newroots = new Hashtable(); + // put in the newroots vector the include patterns without + // wildcard tokens + for (int icounter = 0; icounter < includes.length; icounter++) { + String newpattern = + SelectorUtils.rtrimWildcardTokens(includes[icounter]); + newroots.put(newpattern, includes[icounter]); + } + if (task.getRemotedir() == null) { + try { + task.setRemotedir(ftp.printWorkingDirectory()); + } catch (IOException e) { + throw new BuildException("could not read current ftp directory", + task.getLocation()); + } + } + AntFTPFile baseFTPFile = new AntFTPRootFile(ftp, task.getRemotedir()); + rootPath = baseFTPFile.getAbsolutePath(); + // construct it + if (newroots.containsKey("")) { + // we are going to scan everything anyway + scandir(rootPath, "", true); + } else { + // only scan directories that can include matched files or + // directories + Enumeration enum2 = newroots.keys(); + + while (enum2.hasMoreElements()) { + String currentelement = (String) enum2.nextElement(); + String originalpattern = (String) newroots.get(currentelement); + AntFTPFile myfile = new AntFTPFile(baseFTPFile, currentelement); + boolean isOK = true; + boolean traversesSymlinks = false; + String path = null; + + if (myfile.exists()) { + forceRemoteSensitivityCheck(); + if (remoteSensitivityChecked + && remoteSystemCaseSensitive && isFollowSymlinks()) { + // cool case, + //we do not need to scan all the subdirs in the relative path + path = myfile.getFastRelativePath(); + } else { + // may be on a case insensitive file system. We want + // the results to show what's really on the disk, so + // we need to double check. + try { + path = myfile.getRelativePath(); + traversesSymlinks = myfile.isTraverseSymlinks(); + } catch (IOException be) { + throw new BuildException(be, task.getLocation()); + } catch (BuildException be) { + isOK = false; + } + } + } else { + isOK = false; + } + if (isOK) { + currentelement = path.replace(task.getSeparator().charAt(0), File.separatorChar); + if (!isFollowSymlinks() + && traversesSymlinks) { + continue; + } + + if (myfile.isDirectory()) { + if (isIncluded(currentelement) + && currentelement.length() > 0) { + accountForIncludedDir(currentelement, myfile, true); + } else { + if (currentelement.length() > 0) { + if (currentelement.charAt(currentelement + .length() - 1) + != File.separatorChar) { + currentelement = + currentelement + File.separatorChar; + } + } + scandir(myfile.getAbsolutePath(), currentelement, true); + } + } else { + if (isCaseSensitive + && originalpattern.equals(currentelement)) { + accountForIncludedFile(currentelement); + } else if (!isCaseSensitive + && originalpattern + .equalsIgnoreCase(currentelement)) { + accountForIncludedFile(currentelement); + } + } + } + } + } + } + /** + * scans a particular directory. populates the scannedDirs cache. + * + * @param dir directory to scan + * @param vpath relative path to the base directory of the remote fileset + * always ended with a File.separator + * @param fast seems to be always true in practice + */ + protected void scandir(String dir, String vpath, boolean fast) { + // avoid double scanning of directories, can only happen in fast mode + if (fast && hasBeenScanned(vpath)) { + return; + } + try { + if (!ftp.changeWorkingDirectory(dir)) { + return; + } + String completePath = null; + if (!vpath.equals("")) { + completePath = rootPath + task.getSeparator() + + vpath.replace(File.separatorChar, task.getSeparator().charAt(0)); + } else { + completePath = rootPath; + } + FTPFile[] newfiles = listFiles(completePath, false); + + if (newfiles == null) { + ftp.changeToParentDirectory(); + return; + } + for (int i = 0; i < newfiles.length; i++) { + FTPFile file = newfiles[i]; + if (file != null + && !file.getName().equals(".") + && !file.getName().equals("..")) { + String name = vpath + file.getName(); + scannedDirs.put(name, new FTPFileProxy(file)); + if (isFunctioningAsDirectory(ftp, dir, file)) { + boolean slowScanAllowed = true; + if (!isFollowSymlinks() && file.isSymbolicLink()) { + dirsExcluded.addElement(name); + slowScanAllowed = false; + } else if (isIncluded(name)) { + accountForIncludedDir(name, + new AntFTPFile(ftp, file, completePath) , fast); + } else { + dirsNotIncluded.addElement(name); + if (fast && couldHoldIncluded(name)) { + scandir(file.getName(), + name + File.separator, fast); + } + } + if (!fast && slowScanAllowed) { + scandir(file.getName(), + name + File.separator, fast); + } + } else { + if (!isFollowSymlinks() && file.isSymbolicLink()) { + filesExcluded.addElement(name); + } else if (isFunctioningAsFile(ftp, dir, file)) { + accountForIncludedFile(name); + } + } + } + } + ftp.changeToParentDirectory(); + } catch (IOException e) { + throw new BuildException("Error while communicating with FTP " + + "server: ", e); + } + } + /** + * process included file + * @param name path of the file relative to the directory of the fileset + */ + private void accountForIncludedFile(String name) { + if (!filesIncluded.contains(name) + && !filesExcluded.contains(name)) { + + if (isIncluded(name)) { + if (!isExcluded(name) + && isSelected(name, (File) scannedDirs.get(name))) { + filesIncluded.addElement(name); + } else { + filesExcluded.addElement(name); + } + } else { + filesNotIncluded.addElement(name); + } + } + } + + /** + * + * @param name path of the directory relative to the directory of + * the fileset + * @param file directory as file + * @param fast + */ + private void accountForIncludedDir(String name, AntFTPFile file, boolean fast) { + if (!dirsIncluded.contains(name) + && !dirsExcluded.contains(name)) { + + if (!isExcluded(name)) { + if (fast) { + if (file.isSymbolicLink()) { + try { + file.getClient().changeWorkingDirectory(file.curpwd); + } catch (IOException ioe) { + throw new BuildException("could not change directory to curpwd"); + } + scandir(file.getLink(), + name + File.separator, fast); + } else { + try { + file.getClient().changeWorkingDirectory(file.curpwd); + } catch (IOException ioe) { + throw new BuildException("could not change directory to curpwd"); + } + scandir(file.getName(), + name + File.separator, fast); + } + } + dirsIncluded.addElement(name); + } else { + dirsExcluded.addElement(name); + if (fast && couldHoldIncluded(name)) { + try { + file.getClient().changeWorkingDirectory(file.curpwd); + } catch (IOException ioe) { + throw new BuildException("could not change directory to curpwd"); + } + scandir(file.getName(), + name + File.separator, fast); + } + } + } + } + /** + * temporary table to speed up the various scanning methods below + * + * @since Ant 1.6 + */ + private Map fileListMap = new HashMap(); + /** + * List of all scanned directories. + * + * @since Ant 1.6 + */ + + private Map scannedDirs = new HashMap(); + + /** + * Has the directory with the given path relative to the base + * directory already been scanned? + * + * @since Ant 1.6 + */ + private boolean hasBeenScanned(String vpath) { + return scannedDirs.containsKey(vpath); + } + + /** + * Clear internal caches. + * + * @since Ant 1.6 + */ + private void clearCaches() { + fileListMap.clear(); + scannedDirs.clear(); + } + /** + * list the files present in one directory. + * @param directory full path on the remote side + * @param changedir if true change to directory directory before listing + * @return array of FTPFile + */ + public FTPFile[] listFiles(String directory, boolean changedir) { + //task.log("listing files in directory " + directory, Project.MSG_DEBUG); + String currentPath = directory; + if (changedir) { + try { + boolean result = ftp.changeWorkingDirectory(directory); + if (!result) { + return null; + } + currentPath = ftp.printWorkingDirectory(); + } catch (IOException ioe) { + throw new BuildException(ioe, task.getLocation()); + } + } + if (fileListMap.containsKey(currentPath)) { + task.log("filelist map used in listing files", Project.MSG_DEBUG); + return ((FTPFile[]) fileListMap.get(currentPath)); + } + FTPFile[] result = null; + try { + result = ftp.listFiles(); + } catch (IOException ioe) { + throw new BuildException(ioe, task.getLocation()); + } + fileListMap.put(currentPath, result); + if (!remoteSensitivityChecked) { + checkRemoteSensitivity(result, directory); + } + return result; + } + + private void forceRemoteSensitivityCheck() { + if (!remoteSensitivityChecked) { + try { + checkRemoteSensitivity(ftp.listFiles(), ftp.printWorkingDirectory()); + } catch (IOException ioe) { + throw new BuildException(ioe, task.getLocation()); + } + } + } + /** + * cd into one directory and + * list the files present in one directory. + * @param directory full path on the remote side + * @return array of FTPFile + */ + public FTPFile[] listFiles(String directory) { + return listFiles(directory, true); + } + private void checkRemoteSensitivity(FTPFile[] array, String directory) { + if (array == null) { + return; + } + boolean candidateFound = false; + String target = null; + for (int icounter = 0; icounter < array.length; icounter++) { + if (array[icounter] != null && array[icounter].isDirectory()) { + if (!array[icounter].getName().equals(".") + && !array[icounter].getName().equals("..")) { + candidateFound = true; + target = fiddleName(array[icounter].getName()); + task.log("will try to cd to " + + target + " where a directory called " + array[icounter].getName() + + " exists", Project.MSG_DEBUG); + for (int pcounter = 0; pcounter < array.length; pcounter++) { + if (array[pcounter] != null + && pcounter != icounter + && target.equals(array[pcounter].getName())) { + candidateFound = false; + break; + } + } + if (candidateFound) { + break; + } + } + } + } + if (candidateFound) { + try { + task.log("testing case sensitivity, attempting to cd to " + + target, Project.MSG_DEBUG); + remoteSystemCaseSensitive = !ftp.changeWorkingDirectory(target); + } catch (IOException ioe) { + remoteSystemCaseSensitive = true; + } finally { + try { + ftp.changeWorkingDirectory(directory); + } catch (IOException ioe) { + throw new BuildException(ioe, task.getLocation()); + } + } + task.log("remote system is case sensitive : " + + remoteSystemCaseSensitive, + Project.MSG_VERBOSE); + remoteSensitivityChecked = true; + } + } + private String fiddleName(String origin) { + StringBuffer result = new StringBuffer(); + for (int icounter = 0; icounter < origin.length(); icounter++) { + if (Character.isLowerCase(origin.charAt(icounter))) { + result.append(Character.toUpperCase(origin.charAt(icounter))); + } else if (Character.isUpperCase(origin.charAt(icounter))) { + result.append(Character.toLowerCase(origin.charAt(icounter))); + } else { + result.append(origin.charAt(icounter)); + } + } + return result.toString(); + } + /** + * an AntFTPFile is a representation of a remote file + * @since Ant 1.6 + */ + protected class AntFTPFile { + /** + * ftp client + */ + private FTPClient client; + /** + * parent directory of the file + */ + private String curpwd; + /** + * the file itself + */ + private FTPFile ftpFile; + /** + * + */ + private AntFTPFile parent = null; + private boolean relativePathCalculated = false; + private boolean traversesSymlinks = false; + private String relativePath = ""; + /** + * constructor + * @param client ftp client variable + * @param ftpFile the file + * @param curpwd absolute remote path where the file is found + */ + public AntFTPFile(FTPClient client, FTPFile ftpFile, String curpwd) { + this.client = client; + this.ftpFile = ftpFile; + this.curpwd = curpwd; + } + /** + * other constructor + * @param parent the parent file + * @param path a relative path to the parent file + */ + public AntFTPFile(AntFTPFile parent, String path) { + this.parent = parent; + this.client = parent.client; + Vector pathElements = SelectorUtils.tokenizePath(path); + try { + boolean result = this.client.changeWorkingDirectory(parent.getAbsolutePath()); + //this should not happen, except if parent has been deleted by another process + if (!result) { + return; + } + this.curpwd = parent.getAbsolutePath(); + } catch (IOException ioe) { + throw new BuildException("could not change working dir to " + + parent.curpwd); + } + final int size = pathElements.size(); + for (int fcount = 0; fcount < size - 1; fcount++) { + String currentPathElement = (String) pathElements.elementAt(fcount); + try { + boolean result = this.client.changeWorkingDirectory(currentPathElement); + if (!result && !isCaseSensitive() + && (remoteSystemCaseSensitive || !remoteSensitivityChecked)) { + currentPathElement = findPathElementCaseUnsensitive(this.curpwd, + currentPathElement); + if (currentPathElement == null) { + return; + } + } else if (!result) { + return; + } + this.curpwd = getCurpwdPlusFileSep() + + currentPathElement; + } catch (IOException ioe) { + throw new BuildException("could not change working dir to " + + (String) pathElements.elementAt(fcount) + + " from " + this.curpwd); + } + + } + String lastpathelement = (String) pathElements.elementAt(size - 1); + FTPFile [] theFiles = listFiles(this.curpwd); + this.ftpFile = getFile(theFiles, lastpathelement); + } + /** + * find a file in a directory in case unsensitive way + * @param parentPath where we are + * @param soughtPathElement what is being sought + * @return the first file found or null if not found + */ + private String findPathElementCaseUnsensitive(String parentPath, + String soughtPathElement) { + // we are already in the right path, so the second parameter + // is false + FTPFile[] theFiles = listFiles(parentPath, false); + if (theFiles == null) { + return null; + } + for (int icounter = 0; icounter < theFiles.length; icounter++) { + if (theFiles[icounter] != null + && theFiles[icounter].getName().equalsIgnoreCase(soughtPathElement)) { + return theFiles[icounter].getName(); + } + } + return null; + } + /** + * find out if the file exists + * @return true if the file exists + */ + public boolean exists() { + return (ftpFile != null); + } + /** + * if the file is a symbolic link, find out to what it is pointing + * @return the target of the symbolic link + */ + public String getLink() { + return ftpFile.getLink(); + } + /** + * get the name of the file + * @return the name of the file + */ + public String getName() { + return ftpFile.getName(); + } + /** + * find out the absolute path of the file + * @return absolute path as string + */ + public String getAbsolutePath() { + return getCurpwdPlusFileSep() + ftpFile.getName(); + } + /** + * find out the relative path assuming that the path used to construct + * this AntFTPFile was spelled properly with regards to case. + * This is OK on a case sensitive system such as UNIX + * @return relative path + */ + public String getFastRelativePath() { + String absPath = getAbsolutePath(); + if (absPath.startsWith(rootPath + task.getSeparator())) { + return absPath.substring(rootPath.length() + + task.getSeparator().length()); + } + return null; + } + /** + * find out the relative path to the rootPath of the enclosing scanner. + * this relative path is spelled exactly like on disk, + * for instance if the AntFTPFile has been instantiated as ALPHA, + * but the file is really called alpha, this method will return alpha. + * If a symbolic link is encountered, it is followed, but the name of the link + * rather than the name of the target is returned. + * (ie does not behave like File.getCanonicalPath()) + * @return relative path, separated by remoteFileSep + * @throws IOException if a change directory fails, ... + * @throws BuildException if one of the components of the relative path cannot + * be found. + */ + public String getRelativePath() throws IOException, BuildException { + if (!relativePathCalculated) { + if (parent != null) { + traversesSymlinks = parent.isTraverseSymlinks(); + relativePath = getRelativePath(parent.getAbsolutePath(), + parent.getRelativePath()); + } else { + relativePath = getRelativePath(rootPath, ""); + relativePathCalculated = true; + } + } + return relativePath; + } + /** + * get the relative path of this file + * @param currentPath base path + * @param currentRelativePath relative path of the base path with regards to remote dir + * @return relative path + */ + private String getRelativePath(String currentPath, String currentRelativePath) { + Vector pathElements = SelectorUtils.tokenizePath(getAbsolutePath(), task.getSeparator()); + Vector pathElements2 = SelectorUtils.tokenizePath(currentPath, + task.getSeparator()); + String relPath = currentRelativePath; + final int size = pathElements.size(); + for (int pcount = pathElements2.size(); pcount < size; pcount++) { + String currentElement = (String) pathElements.elementAt(pcount); + FTPFile[] theFiles = listFiles(currentPath); + FTPFile theFile = null; + if (theFiles != null) { + theFile = getFile(theFiles, currentElement); + } + if (!relPath.equals("")) { + relPath = relPath + task.getSeparator(); + } + if (theFile == null) { + // hit a hidden file assume not a symlink + relPath = relPath + currentElement; + currentPath = currentPath + task.getSeparator() + + currentElement; + task.log("Hidden file " + relPath + + " assumed to not be a symlink.", + Project.MSG_VERBOSE); + } else { + traversesSymlinks = traversesSymlinks || theFile.isSymbolicLink(); + relPath = relPath + theFile.getName(); + currentPath = currentPath + task.getSeparator() + + theFile.getName(); + } + } + return relPath; + } + /** + * find a file matching a string in an array of FTPFile. + * This method will find "alpha" when requested for "ALPHA" + * if and only if the caseSensitive attribute is set to false. + * When caseSensitive is set to true, only the exact match is returned. + * @param theFiles array of files + * @param lastpathelement the file name being sought + * @return null if the file cannot be found, otherwise return the matching file. + */ + public FTPFile getFile(FTPFile[] theFiles, String lastpathelement) { + if (theFiles == null) { + return null; + } + for (int fcount = 0; fcount < theFiles.length; fcount++) { + if (theFiles[fcount] != null) { + if (theFiles[fcount].getName().equals(lastpathelement)) { + return theFiles[fcount]; + } else if (!isCaseSensitive() + && theFiles[fcount].getName().equalsIgnoreCase( + lastpathelement)) { + return theFiles[fcount]; + } + } + } + return null; + } + /** + * tell if a file is a directory. + * note that it will return false for symbolic links pointing to directories. + * @return <code>true</code> for directories + */ + public boolean isDirectory() { + return ftpFile.isDirectory(); + } + /** + * tell if a file is a symbolic link + * @return <code>true</code> for symbolic links + */ + public boolean isSymbolicLink() { + return ftpFile.isSymbolicLink(); + } + /** + * return the attached FTP client object. + * Warning : this instance is really shared with the enclosing class. + * @return FTP client + */ + protected FTPClient getClient() { + return client; + } + + /** + * sets the current path of an AntFTPFile + * @param curpwd the current path one wants to set + */ + protected void setCurpwd(String curpwd) { + this.curpwd = curpwd; + } + /** + * returns the path of the directory containing the AntFTPFile. + * of the full path of the file itself in case of AntFTPRootFile + * @return parent directory of the AntFTPFile + */ + public String getCurpwd() { + return curpwd; + } + /** + * returns the path of the directory containing the AntFTPFile. + * of the full path of the file itself in case of AntFTPRootFile + * and appends the remote file separator if necessary. + * @return parent directory of the AntFTPFile + * @since Ant 1.8.2 + */ + public String getCurpwdPlusFileSep() { + String sep = task.getSeparator(); + return curpwd.endsWith(sep) ? curpwd : curpwd + sep; + } + /** + * find out if a symbolic link is encountered in the relative path of this file + * from rootPath. + * @return <code>true</code> if a symbolic link is encountered in the relative path. + * @throws IOException if one of the change directory or directory listing operations + * fails + * @throws BuildException if a path component in the relative path cannot be found. + */ + public boolean isTraverseSymlinks() throws IOException, BuildException { + if (!relativePathCalculated) { + // getRelativePath also finds about symlinks + getRelativePath(); + } + return traversesSymlinks; + } + + /** + * Get a string rep of this object. + * @return a string containing the pwd and the file. + */ + public String toString() { + return "AntFtpFile: " + curpwd + "%" + ftpFile; + } + } + /** + * special class to represent the remote directory itself + * @since Ant 1.6 + */ + protected class AntFTPRootFile extends AntFTPFile { + private String remotedir; + /** + * constructor + * @param aclient FTP client + * @param remotedir remote directory + */ + public AntFTPRootFile(FTPClient aclient, String remotedir) { + super(aclient, null, remotedir); + this.remotedir = remotedir; + try { + this.getClient().changeWorkingDirectory(this.remotedir); + this.setCurpwd(this.getClient().printWorkingDirectory()); + } catch (IOException ioe) { + throw new BuildException(ioe, task.getLocation()); + } + } + /** + * find the absolute path + * @return absolute path + */ + public String getAbsolutePath() { + return this.getCurpwd(); + } + /** + * find out the relative path to root + * @return empty string + * @throws BuildException actually never + * @throws IOException actually never + */ + public String getRelativePath() throws BuildException, IOException { + return ""; + } + } + } + /** + * check FTPFiles to check whether they function as directories too + * the FTPFile API seem to make directory and symbolic links incompatible + * we want to find out if we can cd to a symbolic link + * @param dir the parent directory of the file to test + * @param file the file to test + * @return true if it is possible to cd to this directory + * @since ant 1.6 + */ + private boolean isFunctioningAsDirectory(FTPClient ftp, String dir, FTPFile file) { + boolean result = false; + String currentWorkingDir = null; + if (file.isDirectory()) { + return true; + } else if (file.isFile()) { + return false; + } + try { + currentWorkingDir = ftp.printWorkingDirectory(); + } catch (IOException ioe) { + task.log("could not find current working directory " + dir + + " while checking a symlink", Project.MSG_DEBUG); + } + if (currentWorkingDir != null) { + try { + result = ftp.changeWorkingDirectory(file.getLink()); + } catch (IOException ioe) { + task.log("could not cd to " + file.getLink() + + " while checking a symlink", + Project.MSG_DEBUG); + } + if (result) { + boolean comeback = false; + try { + comeback = ftp.changeWorkingDirectory(currentWorkingDir); + } catch (IOException ioe) { + task.log("could not cd back to " + dir + " while checking a symlink", + Project.MSG_ERR); + } finally { + if (!comeback) { + throw new BuildException("could not cd back to " + dir + + " while checking a symlink"); + } + } + } + } + return result; + } + /** + * check FTPFiles to check whether they function as directories too + * the FTPFile API seem to make directory and symbolic links incompatible + * we want to find out if we can cd to a symbolic link + * @param dir the parent directory of the file to test + * @param file the file to test + * @return true if it is possible to cd to this directory + * @since ant 1.6 + */ + private boolean isFunctioningAsFile(FTPClient ftp, String dir, FTPFile file) { + if (file.isDirectory()) { + return false; + } else if (file.isFile()) { + return true; + } + return !isFunctioningAsDirectory(ftp, dir, file); + } + + /** + * Executable a retryable object. + * @param h the retry handler. + * @param r the object that should be retried until it succeeds + * or the number of retrys is reached. + * @param descr a description of the command that is being run. + * @throws IOException if there is a problem. + */ + protected void executeRetryable(RetryHandler h, Retryable r, String descr) + throws IOException { + h.execute(r, descr); + } + + + /** + * For each file in the fileset, do the appropriate action: send, get, + * delete, or list. + * + * @param ftp the FTPClient instance used to perform FTP actions + * @param fs the fileset on which the actions are performed. + * + * @return the number of files to be transferred. + * + * @throws IOException if there is a problem reading a file + * @throws BuildException if there is a problem in the configuration. + */ + protected int transferFiles(final FTPClient ftp, FileSet fs) + throws IOException, BuildException { + DirectoryScanner ds; + if (task.getAction() == FTPTask.SEND_FILES) { + ds = fs.getDirectoryScanner(task.getProject()); + } else { + ds = new FTPDirectoryScanner(ftp); + fs.setupDirectoryScanner(ds, task.getProject()); + ds.setFollowSymlinks(fs.isFollowSymlinks()); + ds.scan(); + } + + String[] dsfiles = null; + if (task.getAction() == FTPTask.RM_DIR) { + dsfiles = ds.getIncludedDirectories(); + } else { + dsfiles = ds.getIncludedFiles(); + } + String dir = null; + + if ((ds.getBasedir() == null) + && ((task.getAction() == FTPTask.SEND_FILES) || (task.getAction() == FTPTask.GET_FILES))) { + throw new BuildException("the dir attribute must be set for send " + + "and get actions"); + } else { + if ((task.getAction() == FTPTask.SEND_FILES) || (task.getAction() == FTPTask.GET_FILES)) { + dir = ds.getBasedir().getAbsolutePath(); + } + } + + // If we are doing a listing, we need the output stream created now. + BufferedWriter bw = null; + + try { + if (task.getAction() == FTPTask.LIST_FILES) { + File pd = task.getListing().getParentFile(); + + if (!pd.exists()) { + pd.mkdirs(); + } + bw = new BufferedWriter(new FileWriter(task.getListing())); + } + RetryHandler h = new RetryHandler(task.getRetriesAllowed(), task); + if (task.getAction() == FTPTask.RM_DIR) { + // to remove directories, start by the end of the list + // the trunk does not let itself be removed before the leaves + for (int i = dsfiles.length - 1; i >= 0; i--) { + final String dsfile = dsfiles[i]; + executeRetryable(h, new Retryable() { + public void execute() throws IOException { + rmDir(ftp, dsfile); + } + }, dsfile); + } + } else { + final BufferedWriter fbw = bw; + final String fdir = dir; + if (task.isNewer()) { + task.setGranularityMillis(task.getTimestampGranularity() + .getMilliseconds(task.getAction())); + } + for (int i = 0; i < dsfiles.length; i++) { + final String dsfile = dsfiles[i]; + executeRetryable(h, new Retryable() { + public void execute() throws IOException { + switch (task.getAction()) { + case FTPTask.SEND_FILES: + sendFile(ftp, fdir, dsfile); + break; + case FTPTask.GET_FILES: + getFile(ftp, fdir, dsfile); + break; + case FTPTask.DEL_FILES: + delFile(ftp, dsfile); + break; + case FTPTask.LIST_FILES: + listFile(ftp, fbw, dsfile); + break; + case FTPTask.CHMOD: + doSiteCommand(ftp, "chmod " + task.getChmod() + " " + + resolveFile(dsfile)); + transferred++; + break; + default: + throw new BuildException("unknown ftp action " + + task.getAction()); + } + } + }, dsfile); + } + } + } finally { + if (bw != null) { + bw.close(); + } + } + + return dsfiles.length; + } + + + /** + * Sends all files specified by the configured filesets to the remote + * server. + * + * @param ftp the FTPClient instance used to perform FTP actions + * + * @throws IOException if there is a problem reading a file + * @throws BuildException if there is a problem in the configuration. + */ + protected void transferFiles(FTPClient ftp) + throws IOException, BuildException { + transferred = 0; + skipped = 0; + + if (task.getFilesets().size() == 0) { + throw new BuildException("at least one fileset must be specified."); + } else { + // get files from filesets + final int size = task.getFilesets().size(); + for (int i = 0; i < size; i++) { + FileSet fs = (FileSet) task.getFilesets().elementAt(i); + + if (fs != null) { + transferFiles(ftp, fs); + } + } + } + + task.log(transferred + " " + FTPTask.ACTION_TARGET_STRS[task.getAction()] + " " + + FTPTask.COMPLETED_ACTION_STRS[task.getAction()]); + if (skipped != 0) { + task.log(skipped + " " + FTPTask.ACTION_TARGET_STRS[task.getAction()] + + " were not successfully " + FTPTask.COMPLETED_ACTION_STRS[task.getAction()]); + } + } + + + /** + * Correct a file path to correspond to the remote host requirements. This + * implementation currently assumes that the remote end can handle + * Unix-style paths with forward-slash separators. This can be overridden + * with the <code>separator</code> task parameter. No attempt is made to + * determine what syntax is appropriate for the remote host. + * + * @param file the remote file name to be resolved + * + * @return the filename as it will appear on the server. + */ + protected String resolveFile(String file) { + return file.replace(System.getProperty("file.separator").charAt(0), + task.getSeparator().charAt(0)); + } + + + /** + * Creates all parent directories specified in a complete relative + * pathname. Attempts to create existing directories will not cause + * errors. + * + * @param ftp the FTP client instance to use to execute FTP actions on + * the remote server. + * @param filename the name of the file whose parents should be created. + * @throws IOException under non documented circumstances + * @throws BuildException if it is impossible to cd to a remote directory + * + */ + protected void createParents(FTPClient ftp, String filename) + throws IOException, BuildException { + + File dir = new File(filename); + if (dirCache.contains(dir)) { + return; + } + + Vector parents = new Vector(); + String dirname; + + while ((dirname = dir.getParent()) != null) { + File checkDir = new File(dirname); + if (dirCache.contains(checkDir)) { + break; + } + dir = checkDir; + parents.addElement(dir); + } + + // find first non cached dir + int i = parents.size() - 1; + + if (i >= 0) { + String cwd = ftp.printWorkingDirectory(); + String parent = dir.getParent(); + if (parent != null) { + if (!ftp.changeWorkingDirectory(resolveFile(parent))) { + throw new BuildException("could not change to " + + "directory: " + ftp.getReplyString()); + } + } + + while (i >= 0) { + dir = (File) parents.elementAt(i--); + // check if dir exists by trying to change into it. + if (!ftp.changeWorkingDirectory(dir.getName())) { + // could not change to it - try to create it + task.log("creating remote directory " + + resolveFile(dir.getPath()), Project.MSG_VERBOSE); + if (!ftp.makeDirectory(dir.getName())) { + handleMkDirFailure(ftp); + } + if (!ftp.changeWorkingDirectory(dir.getName())) { + throw new BuildException("could not change to " + + "directory: " + ftp.getReplyString()); + } + } + dirCache.add(dir); + } + ftp.changeWorkingDirectory(cwd); + } + } + /** + * auto find the time difference between local and remote + * @param ftp handle to ftp client + * @return number of millis to add to remote time to make it comparable to local time + * @since ant 1.6 + */ + private long getTimeDiff(FTPClient ftp) { + long returnValue = 0; + File tempFile = findFileName(ftp); + try { + // create a local temporary file + FILE_UTILS.createNewFile(tempFile); + long localTimeStamp = tempFile.lastModified(); + BufferedInputStream instream = new BufferedInputStream(new FileInputStream(tempFile)); + ftp.storeFile(tempFile.getName(), instream); + instream.close(); + boolean success = FTPReply.isPositiveCompletion(ftp.getReplyCode()); + if (success) { + FTPFile [] ftpFiles = ftp.listFiles(tempFile.getName()); + if (ftpFiles.length == 1) { + long remoteTimeStamp = ftpFiles[0].getTimestamp().getTime().getTime(); + returnValue = localTimeStamp - remoteTimeStamp; + } + ftp.deleteFile(ftpFiles[0].getName()); + } + // delegate the deletion of the local temp file to the delete task + // because of race conditions occurring on Windows + Delete mydelete = new Delete(); + mydelete.bindToOwner(task); + mydelete.setFile(tempFile.getCanonicalFile()); + mydelete.execute(); + } catch (Exception e) { + throw new BuildException(e, task.getLocation()); + } + return returnValue; + } + /** + * find a suitable name for local and remote temporary file + */ + private File findFileName(FTPClient ftp) { + FTPFile [] theFiles = null; + final int maxIterations = 1000; + for (int counter = 1; counter < maxIterations; counter++) { + File localFile = FILE_UTILS.createTempFile( + "ant" + Integer.toString(counter), ".tmp", + null, false, false); + String fileName = localFile.getName(); + boolean found = false; + try { + if (theFiles == null) { + theFiles = ftp.listFiles(); + } + for (int counter2 = 0; counter2 < theFiles.length; counter2++) { + if (theFiles[counter2] != null + && theFiles[counter2].getName().equals(fileName)) { + found = true; + break; + } + } + } catch (IOException ioe) { + throw new BuildException(ioe, task.getLocation()); + } + if (!found) { + localFile.deleteOnExit(); + return localFile; + } + } + return null; + } + + /** + * Checks to see if the remote file is current as compared with the local + * file. Returns true if the target file is up to date. + * @param ftp ftpclient + * @param localFile local file + * @param remoteFile remote file + * @return true if the target file is up to date + * @throws IOException in unknown circumstances + * @throws BuildException if the date of the remote files cannot be found and the action is + * GET_FILES + */ + protected boolean isUpToDate(FTPClient ftp, File localFile, + String remoteFile) + throws IOException, BuildException { + task.log("checking date for " + remoteFile, Project.MSG_VERBOSE); + + FTPFile[] files = ftp.listFiles(remoteFile); + + // For Microsoft's Ftp-Service an Array with length 0 is + // returned if configured to return listings in "MS-DOS"-Format + if (files == null || files.length == 0) { + // If we are sending files, then assume out of date. + // If we are getting files, then throw an error + + if (task.getAction() == FTPTask.SEND_FILES) { + task.log("Could not date test remote file: " + remoteFile + + "assuming out of date.", Project.MSG_VERBOSE); + return false; + } else { + throw new BuildException("could not date test remote file: " + + ftp.getReplyString()); + } + } + + long remoteTimestamp = files[0].getTimestamp().getTime().getTime(); + long localTimestamp = localFile.lastModified(); + long adjustedRemoteTimestamp = remoteTimestamp + task.getTimeDiffMillis() + + task.getGranularityMillis(); + + StringBuffer msg; + synchronized(TIMESTAMP_LOGGING_SDF) { + msg = new StringBuffer(" [") + .append(TIMESTAMP_LOGGING_SDF.format(new Date(localTimestamp))) + .append("] local"); + } + task.log(msg.toString(), Project.MSG_VERBOSE); + + synchronized(TIMESTAMP_LOGGING_SDF) { + msg = new StringBuffer(" [") + .append(TIMESTAMP_LOGGING_SDF.format(new Date(adjustedRemoteTimestamp))) + .append("] remote"); + } + if (remoteTimestamp != adjustedRemoteTimestamp) { + synchronized(TIMESTAMP_LOGGING_SDF) { + msg.append(" - (raw: ") + .append(TIMESTAMP_LOGGING_SDF.format(new Date(remoteTimestamp))) + .append(")"); + } + } + task.log(msg.toString(), Project.MSG_VERBOSE); + + if (task.getAction() == FTPTask.SEND_FILES) { + return adjustedRemoteTimestamp >= localTimestamp; + } else { + return localTimestamp >= adjustedRemoteTimestamp; + } + } + + + /** + * Sends a site command to the ftp server + * @param ftp ftp client + * @param theCMD command to execute + * @throws IOException in unknown circumstances + * @throws BuildException in unknown circumstances + */ + protected void doSiteCommand(FTPClient ftp, String theCMD) + throws IOException, BuildException { + boolean rc; + String[] myReply = null; + + task.log("Doing Site Command: " + theCMD, Project.MSG_VERBOSE); + + rc = ftp.sendSiteCommand(theCMD); + + if (!rc) { + task.log("Failed to issue Site Command: " + theCMD, Project.MSG_WARN); + } else { + + myReply = ftp.getReplyStrings(); + + for (int x = 0; x < myReply.length; x++) { + if (myReply[x].indexOf("200") == -1) { + task.log(myReply[x], Project.MSG_WARN); + } + } + } + } + + + /** + * Sends a single file to the remote host. <code>filename</code> may + * contain a relative path specification. When this is the case, <code>sendFile</code> + * will attempt to create any necessary parent directories before sending + * the file. The file will then be sent using the entire relative path + * spec - no attempt is made to change directories. It is anticipated that + * this may eventually cause problems with some FTP servers, but it + * simplifies the coding. + * @param ftp ftp client + * @param dir base directory of the file to be sent (local) + * @param filename relative path of the file to be send + * locally relative to dir + * remotely relative to the remotedir attribute + * @throws IOException in unknown circumstances + * @throws BuildException in unknown circumstances + */ + protected void sendFile(FTPClient ftp, String dir, String filename) + throws IOException, BuildException { + InputStream instream = null; + + try { + // TODO - why not simply new File(dir, filename)? + File file = task.getProject().resolveFile(new File(dir, filename).getPath()); + + if (task.isNewer() && isUpToDate(ftp, file, resolveFile(filename))) { + return; + } + + if (task.isVerbose()) { + task.log("transferring " + file.getAbsolutePath()); + } + + instream = new BufferedInputStream(new FileInputStream(file)); + + createParents(ftp, filename); + + ftp.storeFile(resolveFile(filename), instream); + + boolean success = FTPReply.isPositiveCompletion(ftp.getReplyCode()); + + if (!success) { + String s = "could not put file: " + ftp.getReplyString(); + + if (task.isSkipFailedTransfers()) { + task.log(s, Project.MSG_WARN); + skipped++; + } else { + throw new BuildException(s); + } + + } else { + // see if we should issue a chmod command + if (task.getChmod() != null) { + doSiteCommand(ftp, "chmod " + task.getChmod() + " " + + resolveFile(filename)); + } + task.log("File " + file.getAbsolutePath() + " copied to " + task.getServer(), + Project.MSG_VERBOSE); + transferred++; + } + } finally { + if (instream != null) { + try { + instream.close(); + } catch (IOException ex) { + // ignore it + } + } + } + } + + + /** + * Delete a file from the remote host. + * @param ftp ftp client + * @param filename file to delete + * @throws IOException in unknown circumstances + * @throws BuildException if skipFailedTransfers is set to false + * and the deletion could not be done + */ + protected void delFile(FTPClient ftp, String filename) + throws IOException, BuildException { + if (task.isVerbose()) { + task.log("deleting " + filename); + } + + if (!ftp.deleteFile(resolveFile(filename))) { + String s = "could not delete file: " + ftp.getReplyString(); + + if (task.isSkipFailedTransfers()) { + task.log(s, Project.MSG_WARN); + skipped++; + } else { + throw new BuildException(s); + } + } else { + task.log("File " + filename + " deleted from " + task.getServer(), + Project.MSG_VERBOSE); + transferred++; + } + } + + /** + * Delete a directory, if empty, from the remote host. + * @param ftp ftp client + * @param dirname directory to delete + * @throws IOException in unknown circumstances + * @throws BuildException if skipFailedTransfers is set to false + * and the deletion could not be done + */ + protected void rmDir(FTPClient ftp, String dirname) + throws IOException, BuildException { + if (task.isVerbose()) { + task.log("removing " + dirname); + } + + if (!ftp.removeDirectory(resolveFile(dirname))) { + String s = "could not remove directory: " + ftp.getReplyString(); + + if (task.isSkipFailedTransfers()) { + task.log(s, Project.MSG_WARN); + skipped++; + } else { + throw new BuildException(s); + } + } else { + task.log("Directory " + dirname + " removed from " + task.getServer(), + Project.MSG_VERBOSE); + transferred++; + } + } + + + /** + * Retrieve a single file from the remote host. <code>filename</code> may + * contain a relative path specification. <p> + * + * The file will then be retreived using the entire relative path spec - + * no attempt is made to change directories. It is anticipated that this + * may eventually cause problems with some FTP servers, but it simplifies + * the coding.</p> + * @param ftp the ftp client + * @param dir local base directory to which the file should go back + * @param filename relative path of the file based upon the ftp remote directory + * and/or the local base directory (dir) + * @throws IOException in unknown circumstances + * @throws BuildException if skipFailedTransfers is false + * and the file cannot be retrieved. + */ + protected void getFile(FTPClient ftp, String dir, String filename) + throws IOException, BuildException { + OutputStream outstream = null; + try { + File file = task.getProject().resolveFile(new File(dir, filename).getPath()); + + if (task.isNewer() && isUpToDate(ftp, file, resolveFile(filename))) { + return; + } + + if (task.isVerbose()) { + task.log("transferring " + filename + " to " + + file.getAbsolutePath()); + } + + File pdir = file.getParentFile(); + + if (!pdir.exists()) { + pdir.mkdirs(); + } + outstream = new BufferedOutputStream(new FileOutputStream(file)); + ftp.retrieveFile(resolveFile(filename), outstream); + + if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { + String s = "could not get file: " + ftp.getReplyString(); + + if (task.isSkipFailedTransfers()) { + task.log(s, Project.MSG_WARN); + skipped++; + } else { + throw new BuildException(s); + } + + } else { + task.log( + "File " + file.getAbsolutePath() + " copied from " + + task.getServer(), Project.MSG_VERBOSE); + transferred++; + if (task.isPreserveLastModified()) { + outstream.close(); + outstream = null; + FTPFile[] remote = ftp.listFiles(resolveFile(filename)); + if (remote.length > 0) { + FILE_UTILS.setFileLastModified(file, + remote[0].getTimestamp() + .getTime().getTime()); + } + } + } + } finally { + if (outstream != null) { + try { + outstream.close(); + } catch (IOException ex) { + // ignore it + } + } + } + } + + + /** + * List information about a single file from the remote host. <code>filename</code> + * may contain a relative path specification. <p> + * + * The file listing will then be retrieved using the entire relative path + * spec - no attempt is made to change directories. It is anticipated that + * this may eventually cause problems with some FTP servers, but it + * simplifies the coding.</p> + * @param ftp ftp client + * @param bw buffered writer + * @param filename the directory one wants to list + * @throws IOException in unknown circumstances + * @throws BuildException in unknown circumstances + */ + protected void listFile(FTPClient ftp, BufferedWriter bw, String filename) + throws IOException, BuildException { + if (task.isVerbose()) { + task.log("listing " + filename); + } + FTPFile[] ftpfiles = ftp.listFiles(resolveFile(filename)); + + if (ftpfiles != null && ftpfiles.length > 0) { + bw.write(ftpfiles[0].toString()); + bw.newLine(); + transferred++; + } + } + + + /** + * Create the specified directory on the remote host. + * + * @param ftp The FTP client connection + * @param dir The directory to create (format must be correct for host + * type) + * @throws IOException in unknown circumstances + * @throws BuildException if ignoreNoncriticalErrors has not been set to true + * and a directory could not be created, for instance because it was + * already existing. Precisely, the codes 521, 550 and 553 will trigger + * a BuildException + */ + protected void makeRemoteDir(FTPClient ftp, String dir) + throws IOException, BuildException { + String workingDirectory = ftp.printWorkingDirectory(); + if (task.isVerbose()) { + if (dir.startsWith("/") || workingDirectory == null) { + task.log("Creating directory: " + dir + " in /"); + } else { + task.log("Creating directory: " + dir + " in " + workingDirectory); + } + } + if (dir.startsWith("/")) { + ftp.changeWorkingDirectory("/"); + } + String subdir = ""; + StringTokenizer st = new StringTokenizer(dir, "/"); + while (st.hasMoreTokens()) { + subdir = st.nextToken(); + task.log("Checking " + subdir, Project.MSG_DEBUG); + if (!ftp.changeWorkingDirectory(subdir)) { + if (!ftp.makeDirectory(subdir)) { + // codes 521, 550 and 553 can be produced by FTP Servers + // to indicate that an attempt to create a directory has + // failed because the directory already exists. + int rc = ftp.getReplyCode(); + if (!(task.isIgnoreNoncriticalErrors() && (rc == CODE_550 + || rc == CODE_553 + || rc == CODE_521))) { + throw new BuildException("could not create directory: " + + ftp.getReplyString()); + } + if (task.isVerbose()) { + task.log("Directory already exists"); + } + } else { + if (task.isVerbose()) { + task.log("Directory created OK"); + } + ftp.changeWorkingDirectory(subdir); + } + } + } + if (workingDirectory != null) { + ftp.changeWorkingDirectory(workingDirectory); + } + } + + /** + * look at the response for a failed mkdir action, decide whether + * it matters or not. If it does, we throw an exception + * @param ftp current ftp connection + * @throws BuildException if this is an error to signal + */ + private void handleMkDirFailure(FTPClient ftp) + throws BuildException { + int rc = ftp.getReplyCode(); + if (!(task.isIgnoreNoncriticalErrors() && (rc == CODE_550 + || rc == CODE_553 + || rc == CODE_521))) { + throw new BuildException("could not create directory: " + + ftp.getReplyString()); + } + } + + public void doFTP() throws BuildException { + FTPClient ftp = null; + + try { + task.log("Opening FTP connection to " + task.getServer(), Project.MSG_VERBOSE); + + ftp = new FTPClient(); + if (task.isConfigurationSet()) { + ftp = FTPConfigurator.configure(ftp, task); + } + + ftp.setRemoteVerificationEnabled(task.getEnableRemoteVerification()); + ftp.connect(task.getServer(), task.getPort()); + if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { + throw new BuildException("FTP connection failed: " + + ftp.getReplyString()); + } + + task.log("connected", Project.MSG_VERBOSE); + task.log("logging in to FTP server", Project.MSG_VERBOSE); + + if ((task.getAccount() != null && !ftp.login(task.getUserid(), task.getPassword(), task.getAccount())) + || (task.getAccount() == null && !ftp.login(task.getUserid(), task.getPassword()))) { + throw new BuildException("Could not login to FTP server"); + } + + task.log("login succeeded", Project.MSG_VERBOSE); + + if (task.isBinary()) { + ftp.setFileType(org.apache.commons.net.ftp.FTP.BINARY_FILE_TYPE); + if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { + throw new BuildException("could not set transfer type: " + + ftp.getReplyString()); + } + } else { + ftp.setFileType(org.apache.commons.net.ftp.FTP.ASCII_FILE_TYPE); + if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { + throw new BuildException("could not set transfer type: " + + ftp.getReplyString()); + } + } + + if (task.isPassive()) { + task.log("entering passive mode", Project.MSG_VERBOSE); + ftp.enterLocalPassiveMode(); + if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { + throw new BuildException("could not enter into passive " + + "mode: " + ftp.getReplyString()); + } + } + + // If an initial command was configured then send it. + // Some FTP servers offer different modes of operation, + // E.G. switching between a UNIX file system mode and + // a legacy file system. + if (task.getInitialSiteCommand() != null) { + RetryHandler h = new RetryHandler(task.getRetriesAllowed(), task); + final FTPClient lftp = ftp; + executeRetryable(h, new Retryable() { + public void execute() throws IOException { + doSiteCommand(lftp, task.getInitialSiteCommand()); + } + }, "initial site command: " + task.getInitialSiteCommand()); + } + + + // For a unix ftp server you can set the default mask for all files + // created. + + if (task.getUmask() != null) { + RetryHandler h = new RetryHandler(task.getRetriesAllowed(), task); + final FTPClient lftp = ftp; + executeRetryable(h, new Retryable() { + public void execute() throws IOException { + doSiteCommand(lftp, "umask " + task.getUmask()); + } + }, "umask " + task.getUmask()); + } + + // If the action is MK_DIR, then the specified remote + // directory is the directory to create. + + if (task.getAction() == FTPTask.MK_DIR) { + RetryHandler h = new RetryHandler(task.getRetriesAllowed(), task); + final FTPClient lftp = ftp; + executeRetryable(h, new Retryable() { + public void execute() throws IOException { + makeRemoteDir(lftp, task.getRemotedir()); + } + }, task.getRemotedir()); + } else if (task.getAction() == FTPTask.SITE_CMD) { + RetryHandler h = new RetryHandler(task.getRetriesAllowed(), task); + final FTPClient lftp = ftp; + executeRetryable(h, new Retryable() { + public void execute() throws IOException { + doSiteCommand(lftp, task.getSiteCommand()); + } + }, "Site Command: " + task.getSiteCommand()); + } else { + if (task.getRemotedir() != null) { + task.log("changing the remote directory", Project.MSG_VERBOSE); + ftp.changeWorkingDirectory(task.getRemotedir()); + if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) { + throw new BuildException("could not change remote " + + "directory: " + ftp.getReplyString()); + } + } + if (task.isNewer() && task.isTimeDiffAuto()) { + // in this case we want to find how much time span there is between local + // and remote + task.setTimeDiffMillis(getTimeDiff(ftp)); + } + task.log(FTPTask.ACTION_STRS[task.getAction()] + " " + FTPTask.ACTION_TARGET_STRS[task.getAction()]); + transferFiles(ftp); + } + + } catch (IOException ex) { + throw new BuildException("error during FTP transfer: " + ex, ex); + } finally { + if (ftp != null && ftp.isConnected()) { + try { + task.log("disconnecting", Project.MSG_VERBOSE); + ftp.logout(); + ftp.disconnect(); + } catch (IOException ex) { + // ignore it + } + } + } + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/MimeMail.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/MimeMail.java new file mode 100644 index 00000000..fca4215e --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/MimeMail.java @@ -0,0 +1,43 @@ +/* + * 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.tools.ant.taskdefs.optional.net; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.email.EmailTask; + +/** + * A task to send SMTP email; Use <tt>mail</tt> instead + * + * @deprecated since 1.6.x. + * Use {@link EmailTask} instead. + * + * @since Ant1.4 + */ +public class MimeMail extends EmailTask { + /** + * Executes this build task. + * + * @exception BuildException On error. + */ + public void execute() + throws BuildException { + log("DEPRECATED - The " + getTaskName() + " task is deprecated. " + + "Use the mail task instead."); + super.execute(); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/RExecTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/RExecTask.java new file mode 100644 index 00000000..01cb4ba9 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/RExecTask.java @@ -0,0 +1,479 @@ +/* + * 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.tools.ant.taskdefs.optional.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Calendar; +import java.util.Enumeration; +import java.util.Vector; + +import org.apache.commons.net.bsd.RExecClient; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; + +/** + * Automates the rexec protocol. + * + * @since Ant 1.6 + */ + +public class RExecTask extends Task { + + private static final int PAUSE_TIME = 250; + + /** + * The userid to login with, if automated login is used + */ + private String userid = null; + + /** + * The password to login with, if automated login is used + */ + private String password = null; + + /** + * The command to execute + */ + private String command = null; + + /** + * The server to connect to. + */ + private String server = null; + + /** + * The tcp port to connect to. + */ + private int port = RExecClient.DEFAULT_PORT; + + /** + * The list of read/write commands for this session + */ + private Vector rexecTasks = new Vector(); + + /** + * If true, adds a CR to beginning of login script + */ + private boolean addCarriageReturn = false; + + /** + * Default time allowed for waiting for a valid response + * for all child reads. A value of 0 means no limit. + */ + private Integer defaultTimeout = null; + + /** + * This class is the parent of the Read and Write tasks. + * It handles the common attributes for both. + */ + public class RExecSubTask { + // CheckStyle:VisibilityModifier OFF - bc + protected String taskString = ""; + // CheckStyle:VisibilityModifier ON + + /** + * Execute the subtask. + * @param rexec the client + * @throws BuildException always as it is not allowed to instantiate this object + */ + public void execute(AntRExecClient rexec) + throws BuildException { + throw new BuildException("Shouldn't be able instantiate a SubTask directly"); + } + + /** + * the message as nested text + * @param s the nested text + */ + public void addText(String s) { + setString(getProject().replaceProperties(s)); + } + + /** + * the message as an attribute + * @param s a <code>String</code> value + */ + public void setString(String s) { + taskString += s; + } + } + + /** + * Sends text to the connected server + */ + public class RExecWrite extends RExecSubTask { + private boolean echoString = true; + /** + * Execute the write exec task. + * @param rexec the task to use + * @throws BuildException on error + */ + public void execute(AntRExecClient rexec) + throws BuildException { + rexec.sendString(taskString, echoString); + } + + /** + * Whether or not the message should be echoed to the log. + * Defaults to <code>true</code>. + * @param b a <code>boolean</code> value + */ + public void setEcho(boolean b) { + echoString = b; + } + } + + /** + * Reads the output from the connected server + * until the required string is found or we time out. + */ + public class RExecRead extends RExecSubTask { + private Integer timeout = null; + /** + * Execute the read exec task. + * @param rexec the task to use + * @throws BuildException on error + */ + public void execute(AntRExecClient rexec) + throws BuildException { + rexec.waitForString(taskString, timeout); + } + /** + * a timeout value that overrides any task wide timeout. + * @param i an <code>Integer</code> value + */ + public void setTimeout(Integer i) { + this.timeout = i; + } + + /** + * Sets the default timeout if none has been set already + * @param defaultTimeout an <code>Integer</code> value + * @ant.attribute ignore="true" + */ + public void setDefaultTimeout(Integer defaultTimeout) { + if (timeout == null) { + timeout = defaultTimeout; + } + } + } + + /** + * This class handles the abstraction of the rexec protocol. + * Currently it is a wrapper around <a + * href="http://jakarta.apache.org/commons/net/index.html">Jakarta + * Commons Net</a>. + */ + public class AntRExecClient extends RExecClient { + /** + * Read from the rexec session until the string we are + * waiting for is found + * @param s The string to wait on + */ + public void waitForString(String s) { + waitForString(s, null); + } + + /** + * Read from the rexec session until the string we are + * waiting for is found or the timeout has been reached + * @param s The string to wait on + * @param timeout The maximum number of seconds to wait + */ + public void waitForString(String s, Integer timeout) { + InputStream is = this.getInputStream(); + try { + StringBuffer sb = new StringBuffer(); + int windowStart = -s.length(); + if (timeout == null || timeout.intValue() == 0) { + while (windowStart < 0 + || !sb.substring(windowStart).equals(s)) { + sb.append((char) is.read()); + windowStart++; + } + } else { + Calendar endTime = Calendar.getInstance(); + endTime.add(Calendar.SECOND, timeout.intValue()); + while (windowStart < 0 + || !sb.substring(windowStart).equals(s)) { + while (Calendar.getInstance().before(endTime) + && is.available() == 0) { + Thread.sleep(PAUSE_TIME); + } + if (is.available() == 0) { + throw new BuildException( + "Response timed-out waiting for \"" + s + '\"', + getLocation()); + } + sb.append((char) is.read()); + windowStart++; + } + } + log(sb.toString(), Project.MSG_INFO); + } catch (BuildException be) { + throw be; + } catch (Exception e) { + throw new BuildException(e, getLocation()); + } + } + + /** + * Write this string to the rexec session. + * @param s the string to write + * @param echoString if true log the string sent + */ + public void sendString(String s, boolean echoString) { + OutputStream os = this.getOutputStream(); + try { + os.write((s + "\n").getBytes()); + if (echoString) { + log(s, Project.MSG_INFO); + } + os.flush(); + } catch (Exception e) { + throw new BuildException(e, getLocation()); + } + } + /** + * Read from the rexec session until the EOF is found or + * the timeout has been reached + * @param timeout The maximum number of seconds to wait + */ + public void waitForEOF(Integer timeout) { + InputStream is = this.getInputStream(); + try { + StringBuffer sb = new StringBuffer(); + if (timeout == null || timeout.intValue() == 0) { + int read; + while ((read = is.read()) != -1) { + char c = (char) read; + sb.append(c); + if (c == '\n') { + log(sb.toString(), Project.MSG_INFO); + sb.delete(0, sb.length()); + } + } + } else { + Calendar endTime = Calendar.getInstance(); + endTime.add(Calendar.SECOND, timeout.intValue()); + int read = 0; + while (read != -1) { + while (Calendar.getInstance().before(endTime) && is.available() == 0) { + Thread.sleep(PAUSE_TIME); + } + if (is.available() == 0) { + log(sb.toString(), Project.MSG_INFO); + throw new BuildException( + "Response timed-out waiting for EOF", + getLocation()); + } + read = is.read(); + if (read != -1) { + char c = (char) read; + sb.append(c); + if (c == '\n') { + log(sb.toString(), Project.MSG_INFO); + sb.delete(0, sb.length()); + } + } + } + } + if (sb.length() > 0) { + log(sb.toString(), Project.MSG_INFO); + } + } catch (BuildException be) { + throw be; + } catch (Exception e) { + throw new BuildException(e, getLocation()); + } + } + + } + /** + * A string to wait for from the server. + * A subTask <read> tag was found. Create the object, + * Save it in our list, and return it. + * @return a read sub task + */ + + public RExecSubTask createRead() { + RExecSubTask task = (RExecSubTask) new RExecRead(); + rexecTasks.addElement(task); + return task; + } + /** + * Add text to send to the server + * A subTask <write> tag was found. Create the object, + * Save it in our list, and return it. + * @return a write sub task + */ + public RExecSubTask createWrite() { + RExecSubTask task = (RExecSubTask) new RExecWrite(); + rexecTasks.addElement(task); + return task; + } + /** + * Verify that all parameters are included. + * Connect and possibly login. + * Iterate through the list of Reads and writes. + * @throws BuildException on error + */ + public void execute() throws BuildException { + /** A server name is required to continue */ + if (server == null) { + throw new BuildException("No Server Specified"); + } + /** A userid and password must appear together + * if they appear. They are not required. + */ + if (userid == null && password != null) { + throw new BuildException("No Userid Specified"); + } + if (password == null && userid != null) { + throw new BuildException("No Password Specified"); + } + + /** Create the telnet client object */ + AntRExecClient rexec = null; + try { + rexec = new AntRExecClient(); + try { + rexec.connect(server, port); + } catch (IOException e) { + throw new BuildException("Can't connect to " + server); + } + if (userid != null && password != null && command != null + && rexecTasks.size() == 0) { + // simple one-shot execution + rexec.rexec(userid, password, command); + } else { + // need nested read/write elements + handleMultipleTasks(rexec); + } + + /** Keep reading input stream until end of it or time-out */ + rexec.waitForEOF(defaultTimeout); + } catch (IOException e) { + throw new BuildException("Error r-executing command", e); + } finally { + if (rexec != null && rexec.isConnected()) { + try { + rexec.disconnect(); + } catch (IOException e) { + throw new BuildException("Error disconnecting from " + + server); + } + } + } + } + /** + * Process a 'typical' login. If it differs, use the read + * and write tasks explicitly + */ + private void login(AntRExecClient rexec) { + if (addCarriageReturn) { + rexec.sendString("\n", true); + } + rexec.waitForString("ogin:"); + rexec.sendString(userid, true); + rexec.waitForString("assword:"); + rexec.sendString(password, false); + } + /** + * Set the the command to execute on the server; + * @param c a <code>String</code> value + */ + public void setCommand(String c) { + this.command = c; + } + + /** + * send a carriage return after connecting; optional, defaults to false. + * @param b a <code>boolean</code> value + */ + public void setInitialCR(boolean b) { + this.addCarriageReturn = b; + } + /** + * Set the the login password to use + * required if <tt>userid</tt> is set. + * @param p a <code>String</code> value + */ + public void setPassword(String p) { + this.password = p; + } + + /** + * Set the tcp port to connect to; default is 23. + * @param p an <code>int</code> value + */ + public void setPort(int p) { + this.port = p; + } + + /** + * Set the hostname or address of the remote server. + * @param m a <code>String</code> value + */ + public void setServer(String m) { + this.server = m; + } + + /** + * set a default timeout in seconds to wait for a response, + * zero means forever (the default) + * @param i an <code>Integer</code> value + */ + public void setTimeout(Integer i) { + this.defaultTimeout = i; + } + /** + * Set the the login id to use on the server; + * required if <tt>password</tt> is set. + * @param u a <code>String</code> value + */ + public void setUserid(String u) { + this.userid = u; + } + + /** + * Deals with multiple read/write calls. + * + * @since Ant 1.6.3 + */ + private void handleMultipleTasks(AntRExecClient rexec) { + + /** Login if userid and password were specified */ + if (userid != null && password != null) { + login(rexec); + } + /** Process each sub command */ + Enumeration tasksToRun = rexecTasks.elements(); + while (tasksToRun != null && tasksToRun.hasMoreElements()) { + RExecSubTask task = (RExecSubTask) tasksToRun.nextElement(); + if (task instanceof RExecRead && defaultTimeout != null) { + ((RExecRead) task).setDefaultTimeout(defaultTimeout); + } + task.execute(rexec); + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/SetProxy.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/SetProxy.java new file mode 100644 index 00000000..1e1f6591 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/SetProxy.java @@ -0,0 +1,283 @@ +/* + * 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.tools.ant.taskdefs.optional.net; + +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.util.Properties; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.util.ProxySetup; + +/** + * Sets Java's web proxy properties, so that tasks and code run in + * the same JVM can have through-the-firewall access to remote web sites, + * and remote ftp sites. + * You can nominate an http and ftp proxy, or a socks server, reset the server + * settings, or do nothing at all. + * <p> + * Examples + * <pre><setproxy/></pre> + * do nothing + * <pre><setproxy proxyhost="firewall"/></pre> + * set the proxy to firewall:80 + * <pre><setproxy proxyhost="firewall" proxyport="81"/></pre> + * set the proxy to firewall:81 + * <pre><setproxy proxyhost=""/></pre> + * stop using the http proxy; don't change the socks settings + * <pre><setproxy socksproxyhost="socksy"/></pre> + * use socks via socksy:1080 + * <pre><setproxy socksproxyhost=""/></pre> + * stop using the socks server. + * <p> + * You can set a username and password for http with the <tt>proxyHost</tt> + * and <tt>proxyPassword</tt> attributes. These can also be + * used against SOCKS5 servers. + * </p> + * @see <a href="http://java.sun.com/j2se/1.5.0/docs/guide/net/properties.html"> + * java 1.5 network property list</a> + *@since Ant 1.5 + * @ant.task category="network" + */ +public class SetProxy extends Task { + private static final int HTTP_PORT = 80; + private static final int SOCKS_PORT = 1080; + // CheckStyle:VisibilityModifier OFF - bc + /** + * proxy details + */ + protected String proxyHost = null; + + /** + * name of proxy port + */ + protected int proxyPort = HTTP_PORT; + + // CheckStyle:VisibilityModifier ON + + /** + * socks host. + */ + private String socksProxyHost = null; + + /** + * Socks proxy port. Default is 1080. + */ + private int socksProxyPort = SOCKS_PORT; + + + /** + * list of non proxy hosts + */ + private String nonProxyHosts = null; + + /** + * user for http only + */ + private String proxyUser = null; + + /** + * password for http only + */ + private String proxyPassword = null; + + /** + * the HTTP/ftp proxy host. Set this to "" for the http proxy + * option to be disabled + * + * @param hostname the new proxy hostname + */ + public void setProxyHost(String hostname) { + proxyHost = hostname; + } + + + /** + * the HTTP/ftp proxy port number; default is 80 + * + * @param port port number of the proxy + */ + public void setProxyPort(int port) { + proxyPort = port; + } + + /** + * The name of a Socks server. Set to "" to turn socks + * proxying off. + * + * @param host The new SocksProxyHost value + */ + public void setSocksProxyHost(String host) { + this.socksProxyHost = host; + } + + + /** + * Set the ProxyPort for socks connections. The default value is 1080 + * + * @param port The new SocksProxyPort value + */ + public void setSocksProxyPort(int port) { + this.socksProxyPort = port; + } + + + /** + * A list of hosts to bypass the proxy on. These should be separated + * with the vertical bar character '|'. Only in Java 1.4 does ftp use + * this list. + * e.g. fozbot.corp.sun.com|*.eng.sun.com + * @param nonProxyHosts lists of hosts to talk direct to + */ + public void setNonProxyHosts(String nonProxyHosts) { + this.nonProxyHosts = nonProxyHosts; + } + + /** + * set the proxy user. Probably requires a password to accompany this + * setting. Default="" + * @param proxyUser username + * @since Ant1.6 + */ + public void setProxyUser(String proxyUser) { + this.proxyUser = proxyUser; + } + + /** + * Set the password for the proxy. Used only if the proxyUser is set. + * @param proxyPassword password to go with the username + * @since Ant1.6 + */ + public void setProxyPassword(String proxyPassword) { + this.proxyPassword = proxyPassword; + } + + /** + * if the proxy port and host settings are not null, then the settings + * get applied these settings last beyond the life of the object and + * apply to all network connections + * Relevant docs: buglist #4183340 + */ + + public void applyWebProxySettings() { + boolean settingsChanged = false; + boolean enablingProxy = false; + Properties sysprops = System.getProperties(); + if (proxyHost != null) { + settingsChanged = true; + if (proxyHost.length() != 0) { + traceSettingInfo(); + enablingProxy = true; + sysprops.put(ProxySetup.HTTP_PROXY_HOST, proxyHost); + String portString = Integer.toString(proxyPort); + sysprops.put(ProxySetup.HTTP_PROXY_PORT, portString); + sysprops.put(ProxySetup.HTTPS_PROXY_HOST, proxyHost); + sysprops.put(ProxySetup.HTTPS_PROXY_PORT, portString); + sysprops.put(ProxySetup.FTP_PROXY_HOST, proxyHost); + sysprops.put(ProxySetup.FTP_PROXY_PORT, portString); + if (nonProxyHosts != null) { + sysprops.put(ProxySetup.HTTP_NON_PROXY_HOSTS, nonProxyHosts); + sysprops.put(ProxySetup.HTTPS_NON_PROXY_HOSTS, nonProxyHosts); + sysprops.put(ProxySetup.FTP_NON_PROXY_HOSTS, nonProxyHosts); + } + if (proxyUser != null) { + sysprops.put(ProxySetup.HTTP_PROXY_USERNAME, proxyUser); + sysprops.put(ProxySetup.HTTP_PROXY_PASSWORD, proxyPassword); + } + } else { + log("resetting http proxy", Project.MSG_VERBOSE); + sysprops.remove(ProxySetup.HTTP_PROXY_HOST); + sysprops.remove(ProxySetup.HTTP_PROXY_PORT); + sysprops.remove(ProxySetup.HTTP_PROXY_USERNAME); + sysprops.remove(ProxySetup.HTTP_PROXY_PASSWORD); + sysprops.remove(ProxySetup.HTTPS_PROXY_HOST); + sysprops.remove(ProxySetup.HTTPS_PROXY_PORT); + sysprops.remove(ProxySetup.FTP_PROXY_HOST); + sysprops.remove(ProxySetup.FTP_PROXY_PORT); + } + } + + //socks + if (socksProxyHost != null) { + settingsChanged = true; + if (socksProxyHost.length() != 0) { + enablingProxy = true; + sysprops.put(ProxySetup.SOCKS_PROXY_HOST, socksProxyHost); + sysprops.put(ProxySetup.SOCKS_PROXY_PORT, Integer.toString(socksProxyPort)); + if (proxyUser != null) { + //this may be a java1.4 thingy only + sysprops.put(ProxySetup.SOCKS_PROXY_USERNAME, proxyUser); + sysprops.put(ProxySetup.SOCKS_PROXY_PASSWORD, proxyPassword); + } + + } else { + log("resetting socks proxy", Project.MSG_VERBOSE); + sysprops.remove(ProxySetup.SOCKS_PROXY_HOST); + sysprops.remove(ProxySetup.SOCKS_PROXY_PORT); + sysprops.remove(ProxySetup.SOCKS_PROXY_USERNAME); + sysprops.remove(ProxySetup.SOCKS_PROXY_PASSWORD); + } + } + + if (proxyUser != null) { + if (enablingProxy) { + Authenticator.setDefault(new ProxyAuth(proxyUser, + proxyPassword)); + } else if (settingsChanged) { + Authenticator.setDefault(new ProxyAuth("", "")); + } + } + } + + /** + * list out what is going on + */ + private void traceSettingInfo() { + log("Setting proxy to " + + (proxyHost != null ? proxyHost : "''") + + ":" + proxyPort, + Project.MSG_VERBOSE); + } + + /** + * Does the work. + * + * @exception BuildException thrown in unrecoverable error. + */ + public void execute() throws BuildException { + applyWebProxySettings(); + } + + /** + * @since 1.6.3 + */ + private static final class ProxyAuth extends Authenticator { + private PasswordAuthentication auth; + + private ProxyAuth(String user, String pass) { + auth = new PasswordAuthentication(user, pass.toCharArray()); + } + + protected PasswordAuthentication getPasswordAuthentication() { + return auth; + } + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/TelnetTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/TelnetTask.java new file mode 100644 index 00000000..82625fa7 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/TelnetTask.java @@ -0,0 +1,397 @@ +/* + * 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.tools.ant.taskdefs.optional.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Calendar; +import java.util.Enumeration; +import java.util.Vector; + +import org.apache.commons.net.telnet.TelnetClient; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; + +/** + * Automates the telnet protocol. + * + */ + +public class TelnetTask extends Task { + private static final int WAIT_INTERVAL = 250; + private static final int TELNET_PORT = 23; + + /** + * The userid to login with, if automated login is used + */ + private String userid = null; + + /** + * The password to login with, if automated login is used + */ + private String password = null; + + /** + * The server to connect to. + */ + private String server = null; + + /** + * The tcp port to connect to. + */ + private int port = TELNET_PORT; + + /** + * The list of read/write commands for this session + */ + private Vector telnetTasks = new Vector(); + + /** + * If true, adds a CR to beginning of login script + */ + private boolean addCarriageReturn = false; + + /** + * Default time allowed for waiting for a valid response + * for all child reads. A value of 0 means no limit. + */ + private Integer defaultTimeout = null; + + /** + * Verify that all parameters are included. + * Connect and possibly login + * Iterate through the list of Reads and writes + * @throws BuildException on error + */ + public void execute() throws BuildException { + /** A server name is required to continue */ + if (server == null) { + throw new BuildException("No Server Specified"); + } + /** A userid and password must appear together + * if they appear. They are not required. + */ + if (userid == null && password != null) { + throw new BuildException("No Userid Specified"); + } + if (password == null && userid != null) { + throw new BuildException("No Password Specified"); + } + + /** Create the telnet client object */ + AntTelnetClient telnet = null; + try { + telnet = new AntTelnetClient(); + try { + telnet.connect(server, port); + } catch (IOException e) { + throw new BuildException("Can't connect to " + server); + } + /** Login if userid and password were specified */ + if (userid != null && password != null) { + login(telnet); + } + /** Process each sub command */ + Enumeration tasksToRun = telnetTasks.elements(); + while (tasksToRun != null && tasksToRun.hasMoreElements()) { + TelnetSubTask task = (TelnetSubTask) tasksToRun.nextElement(); + if (task instanceof TelnetRead && defaultTimeout != null) { + ((TelnetRead) task).setDefaultTimeout(defaultTimeout); + } + task.execute(telnet); + } + } finally { + if (telnet != null && telnet.isConnected()) { + try { + telnet.disconnect(); + } catch (IOException e) { + throw new BuildException("Error disconnecting from " + + server); + } + } + } + } + + /** + * Process a 'typical' login. If it differs, use the read + * and write tasks explicitly + */ + private void login(AntTelnetClient telnet) { + if (addCarriageReturn) { + telnet.sendString("\n", true); + } + telnet.waitForString("ogin:"); + telnet.sendString(userid, true); + telnet.waitForString("assword:"); + telnet.sendString(password, false); + } + + /** + * Set the the login id to use on the server; + * required if <tt>password</tt> is set. + * @param u a <code>String</code> value + */ + public void setUserid(String u) { + this.userid = u; + } + + /** + * Set the the login password to use + * required if <tt>userid</tt> is set. + * @param p a <code>String</code> value + */ + public void setPassword(String p) { + this.password = p; + } + + /** + * Set the hostname or address of the remote server. + * @param m a <code>String</code> value + */ + public void setServer(String m) { + this.server = m; + } + + /** + * Set the tcp port to connect to; default is 23. + * @param p an <code>int</code> value + */ + public void setPort(int p) { + this.port = p; + } + + /** + * send a carriage return after connecting; optional, defaults to false. + * @param b a <code>boolean</code> value + */ + public void setInitialCR(boolean b) { + this.addCarriageReturn = b; + } + + /** + * set a default timeout in seconds to wait for a response, + * zero means forever (the default) + * @param i an <code>Integer</code> value + */ + public void setTimeout(Integer i) { + this.defaultTimeout = i; + } + + /** + * A string to wait for from the server. + * A subTask <read> tag was found. Create the object, + * Save it in our list, and return it. + * @return a read telnet sub task + */ + + public TelnetSubTask createRead() { + TelnetSubTask task = (TelnetSubTask) new TelnetRead(); + telnetTasks.addElement(task); + return task; + } + + /** + * Add text to send to the server + * A subTask <write> tag was found. Create the object, + * Save it in our list, and return it. + * @return a write telnet sub task + */ + public TelnetSubTask createWrite() { + TelnetSubTask task = (TelnetSubTask) new TelnetWrite(); + telnetTasks.addElement(task); + return task; + } + + /** + * This class is the parent of the Read and Write tasks. + * It handles the common attributes for both. + */ + public class TelnetSubTask { + // CheckStyle:VisibilityModifier OFF - bc + protected String taskString = ""; + // CheckStyle:VisibilityModifier ON + /** + * Execute the subtask. + * @param telnet the client + * @throws BuildException always as it is not allowed to instantiate this object + */ + public void execute(AntTelnetClient telnet) + throws BuildException { + throw new BuildException("Shouldn't be able instantiate a SubTask directly"); + } + + /** + * the message as nested text + * @param s the nested text + */ + public void addText(String s) { + setString(getProject().replaceProperties(s)); + } + + /** + * the message as an attribute + * @param s a <code>String</code> value + */ + public void setString(String s) { + taskString += s; + } + } + + /** + * Sends text to the connected server + */ + public class TelnetWrite extends TelnetSubTask { + private boolean echoString = true; + /** + * Execute the write task. + * @param telnet the task to use + * @throws BuildException on error + */ + public void execute(AntTelnetClient telnet) + throws BuildException { + telnet.sendString(taskString, echoString); + } + + /** + * Whether or not the message should be echoed to the log. + * Defaults to <code>true</code>. + * @param b a <code>boolean</code> value + */ + public void setEcho(boolean b) { + echoString = b; + } + } + + /** + * Reads the output from the connected server + * until the required string is found or we time out. + */ + public class TelnetRead extends TelnetSubTask { + private Integer timeout = null; + /** + * Execute the read task. + * @param telnet the task to use + * @throws BuildException on error + */ + public void execute(AntTelnetClient telnet) + throws BuildException { + telnet.waitForString(taskString, timeout); + } + /** + * a timeout value that overrides any task wide timeout. + * @param i an <code>Integer</code> value + */ + public void setTimeout(Integer i) { + this.timeout = i; + } + + /** + * Sets the default timeout if none has been set already + * @param defaultTimeout an <code>Integer</code> value + * @ant.attribute ignore="true" + */ + public void setDefaultTimeout(Integer defaultTimeout) { + if (timeout == null) { + timeout = defaultTimeout; + } + } + } + + /** + * This class handles the abstraction of the telnet protocol. + * Currently it is a wrapper around <a + * href="http://jakarta.apache.org/commons/net/index.html">Jakarta + * Commons Net</a>. + */ + public class AntTelnetClient extends TelnetClient { + /** + * Read from the telnet session until the string we are + * waiting for is found + * @param s The string to wait on + */ + public void waitForString(String s) { + waitForString(s, null); + } + + /** + * Read from the telnet session until the string we are + * waiting for is found or the timeout has been reached + * @param s The string to wait on + * @param timeout The maximum number of seconds to wait + */ + public void waitForString(String s, Integer timeout) { + InputStream is = this.getInputStream(); + try { + StringBuffer sb = new StringBuffer(); + int windowStart = -s.length(); + if (timeout == null || timeout.intValue() == 0) { + while (windowStart < 0 + || !sb.substring(windowStart).equals(s)) { + sb.append((char) is.read()); + windowStart++; + } + } else { + Calendar endTime = Calendar.getInstance(); + endTime.add(Calendar.SECOND, timeout.intValue()); + while (windowStart < 0 + || !sb.substring(windowStart).equals(s)) { + while (Calendar.getInstance().before(endTime) + && is.available() == 0) { + Thread.sleep(WAIT_INTERVAL); + } + if (is.available() == 0) { + log("Read before running into timeout: " + + sb.toString(), Project.MSG_DEBUG); + throw new BuildException( + "Response timed-out waiting for \"" + s + '\"', + getLocation()); + } + sb.append((char) is.read()); + windowStart++; + } + } + log(sb.toString(), Project.MSG_INFO); + } catch (BuildException be) { + throw be; + } catch (Exception e) { + throw new BuildException(e, getLocation()); + } + } + + /** + * Write this string to the telnet session. + * @param s the string to write + * @param echoString if true log the string sent + */ + public void sendString(String s, boolean echoString) { + OutputStream os = this.getOutputStream(); + try { + os.write((s + "\n").getBytes()); + if (echoString) { + log(s, Project.MSG_INFO); + } + os.flush(); + } catch (Exception e) { + throw new BuildException(e, getLocation()); + } + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/Pvcs.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/Pvcs.java new file mode 100644 index 00000000..dd6016a8 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/Pvcs.java @@ -0,0 +1,675 @@ +/* + * 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.tools.ant.taskdefs.optional.pvcs; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.text.MessageFormat; +import java.text.ParseException; +import java.util.Enumeration; +import java.util.Random; +import java.util.Vector; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +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.LogStreamHandler; +import org.apache.tools.ant.taskdefs.PumpStreamHandler; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.util.FileUtils; + +/** + * + * Extracts the latest edition of the source code from a PVCS repository. + * PVCS is a version control system + * developed by <a href="http://www.merant.com/products/pvcs">Merant</a>. + * <br> + * Before using this tag, the user running ant must have access to the commands + * of PVCS (get and pcli) and must have access to the repository. Note that the way to specify + * the repository is platform dependent so use property to specify location of repository. + * <br> + * This version has been tested against PVCS version 6.5 and 6.6 under Windows and Solaris. + + * + * <b>19-04-2001</b> <p>The task now has a more robust + * parser. It allows for platform independent file paths + * and supports file names with <i>()</i>. Thanks to Erik Husby for + * bringing the bug to my attention. + * + * <b>27-04-2001</b> <p>UNC paths are now handled properly. + * Fix provided by Don Jeffery. He also added an <i>UpdateOnly</i> flag + * that, when true, conditions the PVCS get using the -U option to only + * update those files that have a modification time (in PVCS) that is newer + * than the existing workfile. + * + * <b>25-10-2002</b> <p>Added a revision attribute that currently is a + * synonym for label, but in a future release the behavior of the label + * attribute will change to use the -v option of GET. See bug #13847 for + * discussion. + * + */ +public class Pvcs extends org.apache.tools.ant.Task { + // CheckStyle - magic numbers + // checking for "X:\ 0=dquote,1=letter,2=:,3=\ + private static final int POS_1 = 1; + private static final int POS_2 = 2; + private static final int POS_3 = 3; + + private String pvcsbin; + private String repository; + private String pvcsProject; + private Vector pvcsProjects; + private String workspace; + private String force; + private String promotiongroup; + private String label; + private String revision; + private boolean ignorerc; + private boolean updateOnly; + private String filenameFormat; + private String lineStart; + private String userId; + private String config; + /** + * Constant for the thing to execute + */ + private static final String PCLI_EXE = "pcli"; + + /* + * Constant for the PCLI listversionedfiles recursive i a format "get" understands + */ + // private static final String PCLI_LVF_ARGS = "lvf -z -aw"; + + /** + * Constant for the thing to execute + */ + private static final String GET_EXE = "get"; + + + /** + * Run the command. + * @param cmd the command line to use. + * @param out the output stream handler to use. + * @return the exit code of the command. + */ + protected int runCmd(Commandline cmd, ExecuteStreamHandler out) { + try { + Project aProj = getProject(); + Execute exe = new Execute(out); + exe.setAntRun(aProj); + exe.setWorkingDirectory(aProj.getBaseDir()); + exe.setCommandline(cmd.getCommandline()); + return exe.execute(); + } catch (java.io.IOException e) { + String msg = "Failed executing: " + cmd.toString() + + ". Exception: " + e.getMessage(); + throw new BuildException(msg, getLocation()); + } + } + + private String getExecutable(String exe) { + StringBuffer correctedExe = new StringBuffer(); + if (getPvcsbin() != null) { + if (pvcsbin.endsWith(File.separator)) { + correctedExe.append(pvcsbin); + } else { + correctedExe.append(pvcsbin).append(File.separator); + } + } + return correctedExe.append(exe).toString(); + } + + /** + * @exception org.apache.tools.ant.BuildException Something is stopping the build... + */ + public void execute() throws org.apache.tools.ant.BuildException { + int result = 0; + + if (repository == null || repository.trim().equals("")) { + throw new BuildException("Required argument repository not specified"); + } + + // Check workspace exists + // Launch PCLI listversionedfiles -z -aw + // Capture output + // build the command line from what we got the format is + Commandline commandLine = new Commandline(); + commandLine.setExecutable(getExecutable(PCLI_EXE)); + + commandLine.createArgument().setValue("lvf"); + commandLine.createArgument().setValue("-z"); + commandLine.createArgument().setValue("-aw"); + if (getWorkspace() != null) { + commandLine.createArgument().setValue("-sp" + getWorkspace()); + } + commandLine.createArgument().setValue("-pr" + getRepository()); + + String uid = getUserId(); + + if (uid != null) { + commandLine.createArgument().setValue("-id" + uid); + } + + // default pvcs project is "/" + if (getPvcsproject() == null && getPvcsprojects().isEmpty()) { + pvcsProject = "/"; + } + + if (getPvcsproject() != null) { + commandLine.createArgument().setValue(getPvcsproject()); + } + if (!getPvcsprojects().isEmpty()) { + Enumeration e = getPvcsprojects().elements(); + while (e.hasMoreElements()) { + String projectName = ((PvcsProject) e.nextElement()).getName(); + if (projectName == null || (projectName.trim()).equals("")) { + throw new BuildException("name is a required attribute " + + "of pvcsproject"); + } + commandLine.createArgument().setValue(projectName); + } + } + + File tmp = null; + File tmp2 = null; + try { + Random rand = new Random(System.currentTimeMillis()); + tmp = new File("pvcs_ant_" + rand.nextLong() + ".log"); + FileOutputStream fos = new FileOutputStream(tmp); + tmp2 = new File("pvcs_ant_" + rand.nextLong() + ".log"); + log(commandLine.describeCommand(), Project.MSG_VERBOSE); + try { + result = runCmd(commandLine, + new PumpStreamHandler(fos, + new LogOutputStream(this, + Project.MSG_WARN))); + } finally { + FileUtils.close(fos); + } + + if (Execute.isFailure(result) && !ignorerc) { + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + + if (!tmp.exists()) { + throw new BuildException("Communication between ant and pvcs " + + "failed. No output generated from executing PVCS " + + "commandline interface \"pcli\" and \"get\""); + } + + // Create folders in workspace + log("Creating folders", Project.MSG_INFO); + createFolders(tmp); + + // Massage PCLI lvf output transforming '\' to '/' so get command works appropriately + massagePCLI(tmp, tmp2); + + // Launch get on output captured from PCLI lvf + commandLine.clearArgs(); + commandLine.setExecutable(getExecutable(GET_EXE)); + + if (getConfig() != null && getConfig().length() > 0) { + commandLine.createArgument().setValue("-c" + getConfig()); + } + + if (getForce() != null && getForce().equals("yes")) { + commandLine.createArgument().setValue("-Y"); + } else { + commandLine.createArgument().setValue("-N"); + } + + if (getPromotiongroup() != null) { + commandLine.createArgument().setValue("-G" + + getPromotiongroup()); + } else { + if (getLabel() != null) { + commandLine.createArgument().setValue("-v" + getLabel()); + } else { + if (getRevision() != null) { + commandLine.createArgument().setValue("-r" + + getRevision()); + } + } + } + + if (updateOnly) { + commandLine.createArgument().setValue("-U"); + } + + commandLine.createArgument().setValue("@" + tmp2.getAbsolutePath()); + log("Getting files", Project.MSG_INFO); + log("Executing " + commandLine.toString(), Project.MSG_VERBOSE); + result = runCmd(commandLine, + new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_WARN)); + if (result != 0 && !ignorerc) { + String msg = "Failed executing: " + commandLine.toString() + + ". Return code was " + result; + throw new BuildException(msg, getLocation()); + } + + } catch (FileNotFoundException e) { + String msg = "Failed executing: " + commandLine.toString() + + ". Exception: " + e.getMessage(); + throw new BuildException(msg, getLocation()); + } catch (IOException e) { + String msg = "Failed executing: " + commandLine.toString() + + ". Exception: " + e.getMessage(); + throw new BuildException(msg, getLocation()); + } catch (ParseException e) { + String msg = "Failed executing: " + commandLine.toString() + + ". Exception: " + e.getMessage(); + throw new BuildException(msg, getLocation()); + } finally { + if (tmp != null) { + tmp.delete(); + } + if (tmp2 != null) { + tmp2.delete(); + } + } + } + + /** + * Parses the file and creates the folders specified in the output section + */ + private void createFolders(File file) throws IOException, ParseException { + BufferedReader in = null; + try { + in = new BufferedReader(new FileReader(file)); + MessageFormat mf = new MessageFormat(getFilenameFormat()); + String line = in.readLine(); + while (line != null) { + log("Considering \"" + line + "\"", Project.MSG_VERBOSE); + if (line.startsWith("\"\\") // Checking for "\ + || line.startsWith("\"/") // or "/ + // or "X:\... + || (line.length() > POS_3 && line.startsWith("\"") + && Character.isLetter(line.charAt(POS_1)) + && String.valueOf(line.charAt(POS_2)).equals(":") + && String.valueOf(line.charAt(POS_3)).equals("\\"))) { + Object[] objs = mf.parse(line); + String f = (String) objs[1]; + // Extract the name of the directory from the filename + int index = f.lastIndexOf(File.separator); + if (index > -1) { + File dir = new File(f.substring(0, index)); + if (!dir.exists()) { + log("Creating " + dir.getAbsolutePath(), + Project.MSG_VERBOSE); + if (dir.mkdirs() || dir.isDirectory()) { + log("Created " + dir.getAbsolutePath(), + Project.MSG_INFO); + } else { + log("Failed to create " + + dir.getAbsolutePath(), + Project.MSG_INFO); + } + } else { + log(dir.getAbsolutePath() + " exists. Skipping", + Project.MSG_VERBOSE); + } + } else { + log("File separator problem with " + line, + Project.MSG_WARN); + } + } else { + log("Skipped \"" + line + "\"", Project.MSG_VERBOSE); + } + line = in.readLine(); + } + } finally { + FileUtils.close(in); + } + } + + + /** + * Simple hack to handle the PVCS command-line tools botch when + * handling UNC notation. + * @throws IOException if there is an error. + */ + private void massagePCLI(File in, File out) + throws IOException { + BufferedReader inReader = null; + BufferedWriter outWriter = null; + try { + inReader = new BufferedReader(new FileReader(in)); + outWriter = new BufferedWriter(new FileWriter(out)); + String s = null; + while ((s = inReader.readLine()) != null) { + String sNormal = s.replace('\\', '/'); + outWriter.write(sNormal); + outWriter.newLine(); + } + } finally { + FileUtils.close(inReader); + FileUtils.close(outWriter); + } + } + + /** + * Get network name of the PVCS repository + * @return String + */ + public String getRepository() { + return repository; + } + + /** + * The filenameFormat attribute defines a MessageFormat string used + * to parse the output of the pcli command. It defaults to + * <code>{0}-arc({1})</code>. Repositories where the archive + * extension is not -arc should set this. + * @return the filename format attribute. + */ + public String getFilenameFormat() { + return filenameFormat; + } + + /** + * The format of the folder names; optional. + * This must be in a format suitable for + * <code>java.text.MessageFormat</code>. + * Index 1 of the format will be used as the file name. + * Defaults to <code>{0}-arc({1})</code> + * @param f the format to use. + */ + public void setFilenameFormat(String f) { + filenameFormat = f; + } + + /** + + * The lineStart attribute is used to parse the output of the pcli + * command. It defaults to <code>"P:</code>. The parser already + * knows about / and \\, this property is useful in cases where the + * repository is accessed on a Windows platform via a drive letter + * mapping. + * @return the lineStart attribute. + */ + public String getLineStart() { + return lineStart; + } + + /** + * What a valid return value from PVCS looks like + * when it describes a file. Defaults to <code>"P:</code>. + * If you are not using an UNC name for your repository and the + * drive letter <code>P</code> is incorrect for your setup, you may + * need to change this value, UNC names will always be + * accepted. + * @param l the value to use. + */ + public void setLineStart(String l) { + lineStart = l; + } + + /** + * The network name of the PVCS repository; required. + * @param repo String + */ + public void setRepository(String repo) { + repository = repo; + } + + /** + * Get name of the project in the PVCS repository + * @return String + */ + public String getPvcsproject() { + return pvcsProject; + } + + /** + * The project within the PVCS repository to extract files from; + * optional, default "/" + * @param prj String + */ + public void setPvcsproject(String prj) { + pvcsProject = prj; + } + + /** + * Get name of the project in the PVCS repository + * @return Vector + */ + public Vector getPvcsprojects() { + return pvcsProjects; + } + + /** + * Get name of the workspace to store the retrieved files + * @return String + */ + public String getWorkspace() { + return workspace; + } + + /** + * Workspace to use; optional. + * By specifying a workspace, the files are extracted to that location. + * A PVCS workspace is a name for a location of the workfiles and + * isn't as such the location itself. + * You define the location for a workspace using the PVCS GUI clients. + * If this isn't specified the default workspace for the current user is used. + * @param ws String + */ + public void setWorkspace(String ws) { + workspace = ws; + } + + /** + * Get name of the PVCS bin directory + * @return String + */ + public String getPvcsbin() { + return pvcsbin; + } + + /** + * Specifies the location of the PVCS bin directory; optional if on the PATH. + * On some systems the PVCS executables <i>pcli</i> + * and <i>get</i> are not found in the PATH. In such cases this attribute + * should be set to the bin directory of the PVCS installation containing + * the executables mentioned before. If this attribute isn't specified the + * tag expects the executables to be found using the PATH environment variable. + * @param bin PVCS bin directory + * @todo use a File setter and resolve paths. + */ + public void setPvcsbin(String bin) { + pvcsbin = bin; + } + + /** + * Get value of force + * @return String + */ + public String getForce() { + return force; + } + + /** + * Specifies the value of the force argument; optional. + * If set to <i>yes</i> all files that exists and are + * writable are overwritten. Default <i>no</i> causes the files + * that are writable to be ignored. This stops the PVCS command + * <i>get</i> to stop asking questions! + * @todo make a boolean setter + * @param f String (yes/no) + */ + public void setForce(String f) { + if (f != null && f.equalsIgnoreCase("yes")) { + force = "yes"; + } else { + force = "no"; + } + } + + /** + * Get value of promotiongroup + * @return String + */ + public String getPromotiongroup() { + return promotiongroup; + } + + /** + * Specifies the name of the promotiongroup argument + * @param w String + */ + public void setPromotiongroup(String w) { + promotiongroup = w; + } + + /** + * Get value of label + * @return String + */ + public String getLabel() { + return label; + } + + /** + * Only files marked with this label are extracted; optional. + * @param l String + */ + public void setLabel(String l) { + label = l; + } + + /** + * Get value of revision + * @return String + */ + public String getRevision() { + return revision; + } + + /** + * Only files with this revision are extract; optional. + * @param r String + */ + public void setRevision(String r) { + revision = r; + } + + /** + * Get value of ignorereturncode + * @return String + */ + public boolean getIgnoreReturnCode() { + return ignorerc; + } + + /** + * If set to true the return value from executing the pvcs + * commands are ignored; optional, default false. + * @param b a <code>boolean</code> value. + */ + public void setIgnoreReturnCode(boolean b) { + ignorerc = b; + } + + /** + * Specify a project within the PVCS repository to extract files from. + * @param p the pvcs project to use. + */ + public void addPvcsproject(PvcsProject p) { + pvcsProjects.addElement(p); + } + + /** + * get the updateOnly attribute. + * @return the updateOnly attribute. + */ + public boolean getUpdateOnly() { + return updateOnly; + } + + /** + * If set to <i>true</i> files are fetched only if + * newer than existing local files; optional, default false. + * @param l a <code>boolean</code> value. + */ + public void setUpdateOnly(boolean l) { + updateOnly = l; + } + + /** + * returns the path of the configuration file to be used + * @return the path of the config file + */ + public String getConfig() { + return config; + } + + /** + * Sets a configuration file other than the default to be used. + * These files have a .cfg extension and are often found in archive or pvcsprop folders. + * @param f config file - can be given absolute or relative to ant basedir + */ + public void setConfig(File f) { + config = f.toString(); + } + + + /** + * Get the userid. + * @return the userid. + */ + public String getUserId() { + return userId; + } + + /** + * User ID + * @param u the value to use. + */ + public void setUserId(String u) { + userId = u; + } + + /** + * Creates a Pvcs object + */ + public Pvcs() { + super(); + pvcsProject = null; + pvcsProjects = new Vector(); + workspace = null; + repository = null; + pvcsbin = null; + force = null; + promotiongroup = null; + label = null; + ignorerc = false; + updateOnly = false; + lineStart = "\"P:"; + filenameFormat = "{0}-arc({1})"; + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/PvcsProject.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/PvcsProject.java new file mode 100644 index 00000000..a8c6a2a1 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/PvcsProject.java @@ -0,0 +1,49 @@ +/* + * 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.tools.ant.taskdefs.optional.pvcs; + + + +/** + * represents a project within the PVCS repository to extract files from. + */ +public class PvcsProject { + private String name; + + /** no arg constructor */ + public PvcsProject() { + super(); + } + + /** + * Set the name of the project + * @param name the value to use. + */ + public void setName(String name) { + PvcsProject.this.name = name; + } + + /** + * Get the name of the project + * @return the name of the project. + */ + public String getName() { + return name; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDef.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDef.java new file mode 100644 index 00000000..ac9eb88e --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDef.java @@ -0,0 +1,397 @@ +/* + * 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.tools.ant.taskdefs.optional.script; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.apache.tools.ant.AntTypeDefinition; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.ComponentHelper; +import org.apache.tools.ant.MagicNames; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectHelper; +import org.apache.tools.ant.taskdefs.DefBase; +import org.apache.tools.ant.types.ResourceCollection; +import org.apache.tools.ant.util.ClasspathUtils; +import org.apache.tools.ant.util.ScriptRunnerBase; +import org.apache.tools.ant.util.ScriptRunnerHelper; + +/** + * Define a task using a script + * + * @since Ant 1.6 + */ +public class ScriptDef extends DefBase { + /** + * script runner helper + */ + private ScriptRunnerHelper helper = new ScriptRunnerHelper(); + + /** the name by which this script will be activated */ + private String name; + + /** Attributes definitions of this script */ + private List attributes = new ArrayList(); + + /** Nested Element definitions of this script */ + private List nestedElements = new ArrayList(); + + /** The attribute names as a set */ + private Set attributeSet; + + /** The nested element definitions indexed by their names */ + private Map nestedElementMap; + + /** + * Set the project. + * @param project the project that this def belows to. + */ + public void setProject(Project project) { + super.setProject(project); + helper.setProjectComponent(this); + helper.setSetBeans(false); + } + + /** + * set the name under which this script will be activated in a build + * file + * + * @param name the name of the script + */ + public void setName(String name) { + this.name = name; + } + + /** + * Indicates whether the task supports a given attribute name + * + * @param attributeName the name of the attribute. + * + * @return true if the attribute is supported by the script. + */ + public boolean isAttributeSupported(String attributeName) { + return attributeSet.contains(attributeName); + } + + /** + * Class representing an attribute definition + */ + public static class Attribute { + /** The attribute name */ + private String name; + + /** + * Set the attribute name + * + * @param name the attribute name + */ + public void setName(String name) { + this.name = name.toLowerCase(Locale.ENGLISH); + } + } + + /** + * Add an attribute definition to this script. + * + * @param attribute the attribute definition. + */ + public void addAttribute(Attribute attribute) { + attributes.add(attribute); + } + + /** + * Class to represent a nested element definition + */ + public static class NestedElement { + /** The name of the nested element */ + private String name; + + /** The Ant type to which this nested element corresponds. */ + private String type; + + /** The class to be created for this nested element */ + private String className; + + /** + * set the tag name for this nested element + * + * @param name the name of this nested element + */ + public void setName(String name) { + this.name = name.toLowerCase(Locale.ENGLISH); + } + + /** + * Set the type of this element. This is the name of an + * Ant task or type which is to be used when this element is to be + * created. This is an alternative to specifying the class name directly + * + * @param type the name of an Ant type, or task, to use for this nested + * element. + */ + public void setType(String type) { + this.type = type; + } + + /** + * Set the classname of the class to be used for the nested element. + * This specifies the class directly and is an alternative to specifying + * the Ant type name. + * + * @param className the name of the class to use for this nested + * element. + */ + public void setClassName(String className) { + this.className = className; + } + } + + /** + * Add a nested element definition. + * + * @param nestedElement the nested element definition. + */ + public void addElement(NestedElement nestedElement) { + nestedElements.add(nestedElement); + } + + /** + * Define the script. + */ + public void execute() { + if (name == null) { + throw new BuildException("scriptdef requires a name attribute to " + + "name the script"); + } + + if (helper.getLanguage() == null) { + throw new BuildException("<scriptdef> requires a language attribute " + + "to specify the script language"); + } + + // Check if need to set the loader + if (getAntlibClassLoader() != null || hasCpDelegate()) { + helper.setClassLoader(createLoader()); + } + + attributeSet = new HashSet(); + for (Iterator i = attributes.iterator(); i.hasNext();) { + Attribute attribute = (Attribute) i.next(); + if (attribute.name == null) { + throw new BuildException("scriptdef <attribute> elements " + + "must specify an attribute name"); + } + + if (attributeSet.contains(attribute.name)) { + throw new BuildException("scriptdef <" + name + "> declares " + + "the " + attribute.name + " attribute more than once"); + } + attributeSet.add(attribute.name); + } + + nestedElementMap = new HashMap(); + for (Iterator i = nestedElements.iterator(); i.hasNext();) { + NestedElement nestedElement = (NestedElement) i.next(); + if (nestedElement.name == null) { + throw new BuildException("scriptdef <element> elements " + + "must specify an element name"); + } + if (nestedElementMap.containsKey(nestedElement.name)) { + throw new BuildException("scriptdef <" + name + "> declares " + + "the " + nestedElement.name + " nested element more " + + "than once"); + } + + if (nestedElement.className == null + && nestedElement.type == null) { + throw new BuildException("scriptdef <element> elements " + + "must specify either a classname or type attribute"); + } + if (nestedElement.className != null + && nestedElement.type != null) { + throw new BuildException("scriptdef <element> elements " + + "must specify only one of the classname and type " + + "attributes"); + } + + + nestedElementMap.put(nestedElement.name, nestedElement); + } + + // find the script repository - it is stored in the project + Map scriptRepository = lookupScriptRepository(); + name = ProjectHelper.genComponentName(getURI(), name); + scriptRepository.put(name, this); + AntTypeDefinition def = new AntTypeDefinition(); + def.setName(name); + def.setClass(ScriptDefBase.class); + ComponentHelper.getComponentHelper( + getProject()).addDataTypeDefinition(def); + } + + /** + * Find or create the script repository - it is stored in the project. + * This method is synchronized on the project under {@link MagicNames#SCRIPT_REPOSITORY} + * @return the current script repository registered as a reference. + */ + private Map lookupScriptRepository() { + Map scriptRepository = null; + Project p = getProject(); + synchronized (p) { + scriptRepository = + (Map) p.getReference(MagicNames.SCRIPT_REPOSITORY); + if (scriptRepository == null) { + scriptRepository = new HashMap(); + p.addReference(MagicNames.SCRIPT_REPOSITORY, + scriptRepository); + } + } + return scriptRepository; + } + + /** + * Create a nested element to be configured. + * + * @param elementName the name of the nested element. + * @return object representing the element name. + */ + public Object createNestedElement(String elementName) { + NestedElement definition + = (NestedElement) nestedElementMap.get(elementName); + if (definition == null) { + throw new BuildException("<" + name + "> does not support " + + "the <" + elementName + "> nested element"); + } + + Object instance = null; + String classname = definition.className; + if (classname == null) { + instance = getProject().createTask(definition.type); + if (instance == null) { + instance = getProject().createDataType(definition.type); + } + } else { + /* + // try the context classloader + ClassLoader loader + = Thread.currentThread().getContextClassLoader(); + */ + ClassLoader loader = createLoader(); + + try { + instance = ClasspathUtils.newInstance(classname, loader); + } catch (BuildException e) { + instance = ClasspathUtils.newInstance(classname, ScriptDef.class.getClassLoader()); + } + + getProject().setProjectReference(instance); + } + + if (instance == null) { + throw new BuildException("<" + name + "> is unable to create " + + "the <" + elementName + "> nested element"); + } + return instance; + } + + /** + * Execute the script. + * + * @param attributes collection of attributes + * @param elements a list of nested element values. + * @deprecated since 1.7. + * Use executeScript(attribute, elements, instance) instead. + */ + public void executeScript(Map attributes, Map elements) { + executeScript(attributes, elements, null); + } + + /** + * Execute the script. + * This is called by the script instance to execute the script for this + * definition. + * + * @param attributes collection of attributes + * @param elements a list of nested element values. + * @param instance the script instance; can be null + */ + public void executeScript(Map attributes, Map elements, ScriptDefBase instance) { + ScriptRunnerBase runner = helper.getScriptRunner(); + runner.addBean("attributes", attributes); + runner.addBean("elements", elements); + runner.addBean("project", getProject()); + if (instance != null) { + runner.addBean("self", instance); + } + runner.executeScript("scriptdef_" + name); + } + + /** + * Defines the manager. + * + * @param manager the scripting manager. + */ + public void setManager(String manager) { + helper.setManager(manager); + } + + /** + * Defines the language (required). + * + * @param language the scripting language name for the script. + */ + public void setLanguage(String language) { + helper.setLanguage(language); + } + + /** + * Load the script from an external file ; optional. + * + * @param file the file containing the script source. + */ + public void setSrc(File file) { + helper.setSrc(file); + } + + /** + * Set the script text. + * + * @param text a component of the script text to be added. + */ + public void addText(String text) { + helper.addText(text); + } + + /** + * Add any source resource. + * @since Ant1.7.1 + * @param resource source of script + */ + public void add(ResourceCollection resource) { + helper.add(resource); + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDefBase.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDefBase.java new file mode 100644 index 00000000..95d15aa6 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDefBase.java @@ -0,0 +1,132 @@ +/* + * 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.tools.ant.taskdefs.optional.script; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DynamicConfigurator; +import org.apache.tools.ant.MagicNames; +import org.apache.tools.ant.Task; + +/** + * The script execution class. This class finds the defining script task + * and passes control to that task's executeScript method. + * + * @since Ant 1.6 + */ +public class ScriptDefBase extends Task implements DynamicConfigurator { + + /** Nested elements */ + private Map nestedElementMap = new HashMap(); + + /** Attributes */ + private Map attributes = new HashMap(); + + private String text; + + /** + * Locate the script defining task and execute the script by passing + * control to it + */ + public void execute() { + getScript().executeScript(attributes, nestedElementMap, this); + } + + private ScriptDef getScript() { + String name = getTaskType(); + Map scriptRepository + = (Map) getProject().getReference(MagicNames.SCRIPT_REPOSITORY); + if (scriptRepository == null) { + throw new BuildException("Script repository not found for " + name); + } + + ScriptDef definition = (ScriptDef) scriptRepository.get(getTaskType()); + if (definition == null) { + throw new BuildException("Script definition not found for " + name); + } + return definition; + } + + /** + * Create a nested element + * + * @param name the nested element name + * @return the element to be configured + */ + public Object createDynamicElement(String name) { + List nestedElementList = (List) nestedElementMap.get(name); + if (nestedElementList == null) { + nestedElementList = new ArrayList(); + nestedElementMap.put(name, nestedElementList); + } + Object element = getScript().createNestedElement(name); + nestedElementList.add(element); + return element; + } + + /** + * Set a task attribute + * + * @param name the attribute name. + * @param value the attribute's string value + */ + public void setDynamicAttribute(String name, String value) { + ScriptDef definition = getScript(); + if (!definition.isAttributeSupported(name)) { + throw new BuildException("<" + getTaskType() + + "> does not support the \"" + name + "\" attribute"); + } + + attributes.put(name, value); + } + + /** + * Set the script text. + * + * @param text a component of the script text to be added. + * @since ant1.7 + */ + public void addText(String text) { + this.text = getProject().replaceProperties(text); + } + + /** + * get the text of this element; may be null + * @return text or null for no nested text + * @since ant1.7 + */ + public String getText() { + return text; + } + + /** + * Utility method for nested scripts; throws a BuildException + * with the given message. + * @param message text to pass to the BuildException + * @throws BuildException always. + * @since ant1.7 + */ + public void fail(String message) { + throw new BuildException(message); + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOS.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOS.java new file mode 100644 index 00000000..03031d5e --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOS.java @@ -0,0 +1,479 @@ +/* + * 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.tools.ant.taskdefs.optional.sos; + +import java.io.File; + +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.LogStreamHandler; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.FileUtils; + +/** + * A base class for creating tasks for executing commands on SourceOffSite. + * + * These tasks were inspired by the VSS tasks. + * + */ + +public abstract class SOS extends Task implements SOSCmd { + + private static final int ERROR_EXIT_STATUS = 255; + + private String sosCmdDir = null; + private String sosUsername = null; + private String sosPassword = ""; + private String projectPath = null; + private String vssServerPath = null; + private String sosServerPath = null; + private String sosHome = null; + private String localPath = null; + private String version = null; + private String label = null; + private String comment = null; + private String filename = null; + + private boolean noCompress = false; + private boolean noCache = false; + private boolean recursive = false; + private boolean verbose = false; + + // CheckStyle:VisibilityModifier OFF - bc + /** Commandline to be executed. */ + protected Commandline commandLine; + // CheckStyle:VisibilityModifier ON + + /** + * Flag to disable the cache when set. + * Required if SOSHOME is set as an environment variable. + * Defaults to false. + * + * @param nocache True to disable caching. + */ + public final void setNoCache(boolean nocache) { + noCache = nocache; + } + + /** + * Flag to disable compression when set. Defaults to false. + * + * @param nocompress True to disable compression. + */ + public final void setNoCompress(boolean nocompress) { + noCompress = nocompress; + } + + /** + * The directory where soscmd(.exe) is located. + * soscmd must be on the path if omitted. + * + * @param dir The new sosCmd value. + */ + public final void setSosCmd(String dir) { + sosCmdDir = FileUtils.translatePath(dir); + } + + /** + * The SourceSafe username. + * + * @param username The new username value. + * + * @ant.attribute group="required" + */ + public final void setUsername(String username) { + sosUsername = username; + } + + /** + * The SourceSafe password. + * + * @param password The new password value. + */ + public final void setPassword(String password) { + sosPassword = password; + } + + /** + * The SourceSafe project path. + * + * @param projectpath The new projectpath value. + * + * @ant.attribute group="required" + */ + public final void setProjectPath(String projectpath) { + if (projectpath.startsWith(SOSCmd.PROJECT_PREFIX)) { + projectPath = projectpath; + } else { + projectPath = SOSCmd.PROJECT_PREFIX + projectpath; + } + } + + /** + * The path to the location of the ss.ini file. + * + * @param vssServerPath The new vssServerPath value. + * + * @ant.attribute group="required" + */ + public final void setVssServerPath(String vssServerPath) { + this.vssServerPath = vssServerPath; + } + + /** + * Path to the SourceOffSite home directory. + * + * @param sosHome The new sosHome value. + */ + public final void setSosHome(String sosHome) { + this.sosHome = sosHome; + } + + /** + * The address and port of SourceOffSite Server, + * for example 192.168.0.1:8888. + * + * @param sosServerPath The new sosServerPath value. + * + * @ant.attribute group="required" + */ + public final void setSosServerPath(String sosServerPath) { + this.sosServerPath = sosServerPath; + } + + /** + * Override the working directory and get to the specified path. + * + * @param path The new localPath value. + */ + public final void setLocalPath(Path path) { + localPath = path.toString(); + } + + /** + * Enable verbose output. Defaults to false. + * + * @param verbose True for verbose output. + */ + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + // Special setters for the sub-classes + + /** + * Set the file name. + * @param file the filename to use. + */ + protected void setInternalFilename(String file) { + filename = file; + } + + /** + * Set the recursive flag. + * @param recurse if true use the recursive flag on the command line. + */ + protected void setInternalRecursive(boolean recurse) { + recursive = recurse; + } + + /** + * Set the comment text. + * @param text the comment text to use. + */ + protected void setInternalComment(String text) { + comment = text; + } + + /** + * Set the label. + * @param text the label to use. + */ + protected void setInternalLabel(String text) { + label = text; + } + + /** + * Set the version. + * @param text the version to use. + */ + protected void setInternalVersion(String text) { + version = text; + } + + /** + * Get the executable to run. Add the path if it was specified in the build file + * + * @return the executable to run. + */ + protected String getSosCommand() { + if (sosCmdDir == null) { + return COMMAND_SOS_EXE; + } else { + return sosCmdDir + File.separator + COMMAND_SOS_EXE; + } + } + + /** + * Get the comment + * @return if it was set, null if not. + */ + protected String getComment() { + return comment; + } + + /** + * Get the version + * @return if it was set, null if not. + */ + protected String getVersion() { + return version; + } + + /** + * Get the label + * @return if it was set, null if not. + */ + protected String getLabel() { + return label; + } + + /** + * Get the username + * @return if it was set, null if not. + */ + protected String getUsername() { + return sosUsername; + } + + /** + * Get the password + * @return empty string if it wasn't set. + */ + protected String getPassword() { + return sosPassword; + } + + /** + * Get the project path + * @return if it was set, null if not. + */ + protected String getProjectPath() { + return projectPath; + } + + /** + * Get the VSS server path + * @return if it was set, null if not. + */ + protected String getVssServerPath() { + return vssServerPath; + } + + /** + * Get the SOS home directory. + * @return if it was set, null if not. + */ + protected String getSosHome() { + return sosHome; + } + + /** + * Get the SOS serve path. + * @return if it was set, null if not. + */ + protected String getSosServerPath() { + return sosServerPath; + } + + /** + * Get the filename to be acted upon. + * @return if it was set, null if not. + */ + protected String getFilename() { + return filename; + } + + /** + * Get the NoCompress flag. + * + * @return the 'nocompress' Flag if the attribute was 'true', + * otherwise an empty string. + */ + protected String getNoCompress() { + return noCompress ? FLAG_NO_COMPRESSION : ""; + } + + /** + * Get the NoCache flag. + * + * @return the 'nocache' Flag if the attribute was 'true', otherwise an empty string. + */ + protected String getNoCache() { + return noCache ? FLAG_NO_CACHE : ""; + } + + /** + * Get the 'verbose' Flag. + * + * @return the 'verbose' Flag if the attribute was 'true', otherwise an empty string. + */ + protected String getVerbose() { + return verbose ? FLAG_VERBOSE : ""; + } + + /** + * Get the 'recursive' Flag. + * + * @return the 'recursive' Flag if the attribute was 'true', otherwise an empty string. + */ + protected String getRecursive() { + return recursive ? FLAG_RECURSION : ""; + } + + /** + * Builds and returns the working directory. + * <p> + * The localpath is created if it didn't exist. + * + * @return the absolute path of the working directory. + */ + protected String getLocalPath() { + if (localPath == null) { + return getProject().getBaseDir().getAbsolutePath(); + } else { + // make sure localDir exists, create it if it doesn't + File dir = getProject().resolveFile(localPath); + if (!dir.exists()) { + boolean done = dir.mkdirs() || dir.isDirectory(); + if (!done) { + String msg = "Directory " + localPath + " creation was not " + + "successful for an unknown reason"; + throw new BuildException(msg, getLocation()); + } + getProject().log("Created dir: " + dir.getAbsolutePath()); + } + return dir.getAbsolutePath(); + } + } + + /** + * Subclasses implement the logic required to construct the command line. + * + * @return The command line to execute. + */ + abstract Commandline buildCmdLine(); + + + /** + * Execute the created command line. + * + * @throws BuildException on error. + */ + public void execute() + throws BuildException { + int result = 0; + buildCmdLine(); + result = run(commandLine); + if (result == ERROR_EXIT_STATUS) { // This is the exit status + String msg = "Failed executing: " + commandLine.toString(); + throw new BuildException(msg, getLocation()); + } + } + + /** + * Execute the created command line. + * + * @param cmd The command line to run. + * @return int the exit code. + * @throws BuildException + */ + protected int run(Commandline cmd) { + try { + Execute exe = new Execute(new LogStreamHandler(this, + Project.MSG_INFO, + Project.MSG_WARN)); + + exe.setAntRun(getProject()); + exe.setWorkingDirectory(getProject().getBaseDir()); + exe.setCommandline(cmd.getCommandline()); + exe.setVMLauncher(false); // Use the OS VM launcher so we get environment variables + return exe.execute(); + } catch (java.io.IOException e) { + throw new BuildException(e, getLocation()); + } + } + + /** Sets the executable and add the required attributes to the command line. */ + protected void getRequiredAttributes() { + // Get the path to the soscmd(.exe) + commandLine.setExecutable(getSosCommand()); + // SOS server address is required + if (getSosServerPath() == null) { + throw new BuildException("sosserverpath attribute must be set!", getLocation()); + } + commandLine.createArgument().setValue(FLAG_SOS_SERVER); + commandLine.createArgument().setValue(getSosServerPath()); + // Login info is required + if (getUsername() == null) { + throw new BuildException("username attribute must be set!", getLocation()); + } + commandLine.createArgument().setValue(FLAG_USERNAME); + commandLine.createArgument().setValue(getUsername()); + // The SOS class knows that the SOS server needs the password flag, + // even if there is no password ,so we send a " " + commandLine.createArgument().setValue(FLAG_PASSWORD); + commandLine.createArgument().setValue(getPassword()); + // VSS Info is required + if (getVssServerPath() == null) { + throw new BuildException("vssserverpath attribute must be set!", getLocation()); + } + commandLine.createArgument().setValue(FLAG_VSS_SERVER); + commandLine.createArgument().setValue(getVssServerPath()); + // VSS project is required + if (getProjectPath() == null) { + throw new BuildException("projectpath attribute must be set!", getLocation()); + } + commandLine.createArgument().setValue(FLAG_PROJECT); + commandLine.createArgument().setValue(getProjectPath()); + } + + /** Adds the optional attributes to the command line. */ + protected void getOptionalAttributes() { + // -verbose + commandLine.createArgument().setValue(getVerbose()); + // Disable Compression + commandLine.createArgument().setValue(getNoCompress()); + // Path to the SourceOffSite home directory /home/user/.sos + if (getSosHome() == null) { + // If -soshome was not specified then we can look for nocache + commandLine.createArgument().setValue(getNoCache()); + } else { + commandLine.createArgument().setValue(FLAG_SOS_HOME); + commandLine.createArgument().setValue(getSosHome()); + } + //If a working directory was specified then add it to the command line + if (getLocalPath() != null) { + commandLine.createArgument().setValue(FLAG_WORKING_DIR); + commandLine.createArgument().setValue(getLocalPath()); + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckin.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckin.java new file mode 100644 index 00000000..9095f075 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckin.java @@ -0,0 +1,101 @@ +/* + * 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.tools.ant.taskdefs.optional.sos; + +import org.apache.tools.ant.types.Commandline; + +/** + * Commits and unlocks files in Visual SourceSafe via a SourceOffSite server. + * + * @ant.task name="soscheckin" category="scm" + */ +public class SOSCheckin extends SOS { + + /** + * The filename to act upon. + * If no file is specified then the task + * acts upon the project. + * + * @param filename The new file value + */ + public final void setFile(String filename) { + super.setInternalFilename(filename); + } + + /** + * Flag to recursively apply the action. Defaults to false. + * + * @param recursive True for recursive operation. + */ + public void setRecursive(boolean recursive) { + super.setInternalRecursive(recursive); + } + + /** + * The comment to apply to all files being labelled. + * + * @param comment The new comment value + */ + public void setComment(String comment) { + super.setInternalComment(comment); + } + + /** + * Build the command line. <p> + * + * CheckInFile required parameters: -server -name -password -database -project + * -file<br> + * CheckInFile optional parameters: -workdir -log -verbose -nocache -nocompression + * -soshome<br> + * CheckInProject required parameters: -server -name -password -database + * -project<br> + * CheckInProject optional parameters: workdir -recursive -log -verbose + * -nocache -nocompression -soshome<br> + * + * @return Commandline the generated command to be executed + */ + protected Commandline buildCmdLine() { + commandLine = new Commandline(); + + // If we find a "file" attribute then act on a file otherwise act on a project + if (getFilename() != null) { + // add -command CheckInFile to the commandline + commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND); + commandLine.createArgument().setValue(SOSCmd.COMMAND_CHECKIN_FILE); + // add -file xxxxx to the commandline + commandLine.createArgument().setValue(SOSCmd.FLAG_FILE); + commandLine.createArgument().setValue(getFilename()); + } else { + // add -command CheckInProject to the commandline + commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND); + commandLine.createArgument().setValue(SOSCmd.COMMAND_CHECKIN_PROJECT); + // look for a recursive option + commandLine.createArgument().setValue(getRecursive()); + } + + getRequiredAttributes(); + getOptionalAttributes(); + + // Look for a comment + if (getComment() != null) { + commandLine.createArgument().setValue(SOSCmd.FLAG_COMMENT); + commandLine.createArgument().setValue(getComment()); + } + return commandLine; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckout.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckout.java new file mode 100644 index 00000000..fab6fb9f --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckout.java @@ -0,0 +1,85 @@ +/* + * 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.tools.ant.taskdefs.optional.sos; + +import org.apache.tools.ant.types.Commandline; + +/** + * Retrieves and locks files in Visual SourceSafe via a SourceOffSite server. + * + * @ant.task name="soscheckout" category="scm" + */ +public class SOSCheckout extends SOS { + + /** + * The filename to act upon. + * If no file is specified then the task + * acts upon the project. + * + * @param filename The new file value + */ + public final void setFile(String filename) { + super.setInternalFilename(filename); + } + + /** + * Flag to recursively apply the action. Defaults to false. + * + * @param recursive True for recursive operation. + */ + public void setRecursive(boolean recursive) { + super.setInternalRecursive(recursive); + } + + /** + * Build the command line <br> + * + * CheckOutFile required parameters: -server -name -password -database -project -file<br> + * CheckOutFile optional parameters: -workdir -verbose -nocache -nocompression -soshome<br> + * + * CheckOutProject required parameters: -server -name -password -database -project<br> + * CheckOutProject optional parameters:-workdir -recursive -verbose -nocache + * -nocompression -soshome<br> + * + * @return Commandline the generated command to be executed + */ + protected Commandline buildCmdLine() { + commandLine = new Commandline(); + + // If we find a "file" attribute then act on a file otherwise act on a project + if (getFilename() != null) { + // add -command CheckOutFile to the commandline + commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND); + commandLine.createArgument().setValue(SOSCmd.COMMAND_CHECKOUT_FILE); + // add -file xxxxx to the commandline + commandLine.createArgument().setValue(SOSCmd.FLAG_FILE); + commandLine.createArgument().setValue(getFilename()); + } else { + // add -command CheckOutProject to the commandline + commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND); + commandLine.createArgument().setValue(SOSCmd.COMMAND_CHECKOUT_PROJECT); + // look for a recursive option + commandLine.createArgument().setValue(getRecursive()); + } + + getRequiredAttributes(); + getOptionalAttributes(); + + return commandLine; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCmd.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCmd.java new file mode 100644 index 00000000..3543c414 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCmd.java @@ -0,0 +1,81 @@ +/* + * 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.tools.ant.taskdefs.optional.sos; + +/** + * Interface to hold constants used by the SOS tasks + * + */ +// CheckStyle:InterfaceIsTypeCheck OFF (bc) +public interface SOSCmd { + // soscmd Command options + /** The sos executable */ + String COMMAND_SOS_EXE = "soscmd"; + /** The get file command */ + String COMMAND_GET_FILE = "GetFile"; + /** The get project command */ + String COMMAND_GET_PROJECT = "GetProject"; + /** The checkout file command */ + String COMMAND_CHECKOUT_FILE = "CheckOutFile"; + /** The checkout project command */ + String COMMAND_CHECKOUT_PROJECT = "CheckOutProject"; + /** The checkin file command */ + String COMMAND_CHECKIN_FILE = "CheckInFile"; + /** The checkin project command */ + String COMMAND_CHECKIN_PROJECT = "CheckInProject"; + /** The get history command */ + String COMMAND_HISTORY = "GetFileHistory"; + /** The add label command */ + String COMMAND_LABEL = "AddLabel"; + /** The project prefix */ + String PROJECT_PREFIX = "$"; + + // soscmd Option flags + /** The command option */ + String FLAG_COMMAND = "-command"; + /** The database (vss server) option */ + String FLAG_VSS_SERVER = "-database"; + /** The username option */ + String FLAG_USERNAME = "-name"; + /** The password option */ + String FLAG_PASSWORD = "-password"; + /** The log option */ + String FLAG_COMMENT = "-log"; + /** The workdir option */ + String FLAG_WORKING_DIR = "-workdir"; + /** The recursive option */ + String FLAG_RECURSION = "-recursive"; + /** The revision option */ + String FLAG_VERSION = "-revision"; + /** The label option */ + String FLAG_LABEL = "-label"; + /** The no compression option */ + String FLAG_NO_COMPRESSION = "-nocompress"; + /** The no cache option */ + String FLAG_NO_CACHE = "-nocache"; + /** The server option */ + String FLAG_SOS_SERVER = "-server"; + /** The sos home option */ + String FLAG_SOS_HOME = "-soshome"; + /** The project option */ + String FLAG_PROJECT = "-project"; + /** The file option */ + String FLAG_FILE = "-file"; + /** The verbose option */ + String FLAG_VERBOSE = "-verbose"; +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSGet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSGet.java new file mode 100644 index 00000000..cdbf92bc --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSGet.java @@ -0,0 +1,116 @@ +/* + * 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.tools.ant.taskdefs.optional.sos; + +import org.apache.tools.ant.types.Commandline; + +/** + * Retrieves a read-only copy of the specified project or file + * from Visual SourceSafe via a SourceOffSite server. + * + * @ant.task name="sosget" category="scm" + */ +public class SOSGet extends SOS { + + /** + * The Filename to act upon. + * If no file is specified then the tasks + * act upon the project. + * + * @param filename The new file value + */ + public final void setFile(String filename) { + super.setInternalFilename(filename); + } + + /** + * Flag to recursively apply the action. Defaults to false + * + * @param recursive True for recursive operation. + */ + public void setRecursive(boolean recursive) { + super.setInternalRecursive(recursive); + } + + /** + * Set the version number to get - + * only works with SOSGet on a file. + * + * @param version The new version value + */ + public void setVersion(String version) { + super.setInternalVersion(version); + } + + /** + * The labeled version to operate on in SourceSafe. + * + * @param label The new label value + */ + public void setLabel(String label) { + super.setInternalLabel(label); + } + + /** + * Build the command line <br> + * + * GetFile required parameters: -server -name -password -database -project -file<br> + * GetFile optional parameters: -workdir -revision -verbose -nocache -nocompression -soshome<br> + * + * GetProject required parameters: -server -name -password -database -project<br> + * GetProject optional parameters: -label -workdir -recursive -verbose -nocache + * -nocompression -soshome<br> + * + * @return Commandline the generated command to be executed + */ + protected Commandline buildCmdLine() { + commandLine = new Commandline(); + + // If we find a "file" attribute then act on a file otherwise act on a project + if (getFilename() != null) { + // add -command GetFile to the commandline + commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND); + commandLine.createArgument().setValue(SOSCmd.COMMAND_GET_FILE); + // add -file xxxxx to the commandline + commandLine.createArgument().setValue(SOSCmd.FLAG_FILE); + commandLine.createArgument().setValue(getFilename()); + // look for a version attribute + if (getVersion() != null) { + //add -revision xxxxx to the commandline + commandLine.createArgument().setValue(SOSCmd.FLAG_VERSION); + commandLine.createArgument().setValue(getVersion()); + } + } else { + // add -command GetProject to the commandline + commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND); + commandLine.createArgument().setValue(SOSCmd.COMMAND_GET_PROJECT); + // look for a recursive option + commandLine.createArgument().setValue(getRecursive()); + // look for a label option + if (getLabel() != null) { + commandLine.createArgument().setValue(SOSCmd.FLAG_LABEL); + commandLine.createArgument().setValue(getLabel()); + } + } + + getRequiredAttributes(); + getOptionalAttributes(); + + return commandLine; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSLabel.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSLabel.java new file mode 100644 index 00000000..dd6b13a2 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSLabel.java @@ -0,0 +1,91 @@ +/* + * 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.tools.ant.taskdefs.optional.sos; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.types.Commandline; + +/** + * Labels Visual SourceSafe files via a SourceOffSite server. + * + * @ant.task name="soslabel" category="scm" + */ +public class SOSLabel extends SOS { + + /** + * The version number to label. + * + * @param version The new version value + */ + public void setVersion(String version) { + super.setInternalVersion(version); + } + + /** + * The label to apply the the files in SourceSafe. + * + * @param label The new label value + * + * @ant.attribute group="required" + */ + public void setLabel(String label) { + super.setInternalLabel(label); + } + + /** + * The comment to apply to all files being labelled. + * + * @param comment The new comment value + */ + public void setComment(String comment) { + super.setInternalComment(comment); + } + + /** + * Build the command line <br> + * AddLabel required parameters: -server -name -password -database -project -label<br> + * AddLabel optional parameters: -verbose -comment<br> + * + * @return Commandline the generated command to be executed + */ + protected Commandline buildCmdLine() { + commandLine = new Commandline(); + + // add -command AddLabel to the commandline + commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND); + commandLine.createArgument().setValue(SOSCmd.COMMAND_LABEL); + + getRequiredAttributes(); + + // a label is required + if (getLabel() == null) { + throw new BuildException("label attribute must be set!", getLocation()); + } + commandLine.createArgument().setValue(SOSCmd.FLAG_LABEL); + commandLine.createArgument().setValue(getLabel()); + + // -verbose + commandLine.createArgument().setValue(getVerbose()); + // Look for a comment + if (getComment() != null) { + commandLine.createArgument().setValue(SOSCmd.FLAG_COMMENT); + commandLine.createArgument().setValue(getComment()); + } + return commandLine; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/package.html b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/package.html new file mode 100644 index 00000000..a0538c21 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/package.html @@ -0,0 +1,29 @@ +<html> +<!-- + 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. +--> +<body> + <p> + Ant tasks for working with a SourceOffSite source control system. + </p> + <p> + The <SOSGet> Retreives file(s) from a SOS database<br> + The <SOSCheckin> Commits and unlocks file(s) in a SOS database<br> + The <SOSCheckout> Retreives and lock file(s) in a SOS database<br> + The <SOSLabel> Label a SOS database<br> + </p> +</body> +</html> diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sound/AntSoundPlayer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sound/AntSoundPlayer.java new file mode 100644 index 00000000..7988bc60 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sound/AntSoundPlayer.java @@ -0,0 +1,251 @@ +/* + * 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.tools.ant.taskdefs.optional.sound; + +// ant includes +import java.io.File; +import java.io.IOException; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Clip; +import javax.sound.sampled.DataLine; +import javax.sound.sampled.Line; +import javax.sound.sampled.LineEvent; +import javax.sound.sampled.LineListener; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.UnsupportedAudioFileException; + +import org.apache.tools.ant.BuildEvent; +import org.apache.tools.ant.BuildListener; +import org.apache.tools.ant.Project; + + + +/** + * This class is designed to be used by any AntTask that requires audio output. + * + * It implements the BuildListener interface to listen for BuildEvents + * and could be easily extended to provide audio output upon any + * specific build events occurring. + * + * I have only tested this with .WAV and .AIFF sound file formats. Both seem to work fine. + * + */ + +public class AntSoundPlayer implements LineListener, BuildListener { + + private File fileSuccess = null; + private int loopsSuccess = 0; + private Long durationSuccess = null; + + private File fileFail = null; + private int loopsFail = 0; + private Long durationFail = null; + + /** Constructor for AntSoundPlayer. */ + public AntSoundPlayer() { + } + + /** + * @param file the location of the audio file to be played when the + * build is successful + * @param loops the number of times the file should be played when + * the build is successful + * @param duration the number of milliseconds the file should be + * played when the build is successful + */ + public void addBuildSuccessfulSound(File file, int loops, Long duration) { + this.fileSuccess = file; + this.loopsSuccess = loops; + this.durationSuccess = duration; + } + + + /** + * @param fileFail the location of the audio file to be played + * when the build fails + * @param loopsFail the number of times the file should be played + * when the build is fails + * @param durationFail the number of milliseconds the file should be + * played when the build fails + */ + public void addBuildFailedSound(File fileFail, int loopsFail, Long durationFail) { + this.fileFail = fileFail; + this.loopsFail = loopsFail; + this.durationFail = durationFail; + } + + /** + * Plays the file for duration milliseconds or loops. + */ + private void play(Project project, File file, int loops, Long duration) { + + Clip audioClip = null; + + AudioInputStream audioInputStream = null; + + + try { + audioInputStream = AudioSystem.getAudioInputStream(file); + } catch (UnsupportedAudioFileException uafe) { + project.log("Audio format is not yet supported: " + + uafe.getMessage()); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + + if (audioInputStream != null) { + AudioFormat format = audioInputStream.getFormat(); + DataLine.Info info = new DataLine.Info(Clip.class, format, + AudioSystem.NOT_SPECIFIED); + try { + audioClip = (Clip) AudioSystem.getLine(info); + audioClip.addLineListener(this); + audioClip.open(audioInputStream); + } catch (LineUnavailableException e) { + project.log("The sound device is currently unavailable"); + return; + } catch (IOException e) { + e.printStackTrace(); + } + + if (duration != null) { + playClip(audioClip, duration.longValue()); + } else { + playClip(audioClip, loops); + } + audioClip.drain(); + audioClip.close(); + } else { + project.log("Can't get data from file " + file.getName()); + } + } + + private void playClip(Clip clip, int loops) { + + clip.loop(loops); + do { + try { + long timeLeft = + (clip.getMicrosecondLength() - clip.getMicrosecondPosition()) + / 1000; + if (timeLeft > 0) { + Thread.sleep(timeLeft); + } + } catch (InterruptedException e) { + break; + } + } while (clip.isRunning()); + + if (clip.isRunning()) { + clip.stop(); + } + } + + private void playClip(Clip clip, long duration) { + clip.loop(Clip.LOOP_CONTINUOUSLY); + try { + Thread.sleep(duration); + } catch (InterruptedException e) { + // Ignore Exception + } + clip.stop(); + } + + /** + * This is implemented to listen for any line events and closes the + * clip if required. + * @param event the line event to follow + */ + public void update(LineEvent event) { + if (event.getType().equals(LineEvent.Type.STOP)) { + Line line = event.getLine(); + line.close(); + } + } + + + /** + * Fired before any targets are started. + * @param event ignored + */ + public void buildStarted(BuildEvent event) { + } + + /** + * Fired after the last target has finished. This event + * will still be thrown if an error occurred during the build. + * @param event the build finished event. + * @see BuildEvent#getException() + */ + public void buildFinished(BuildEvent event) { + if (event.getException() == null && fileSuccess != null) { + // build successful! + play(event.getProject(), fileSuccess, loopsSuccess, durationSuccess); + } else if (event.getException() != null && fileFail != null) { + play(event.getProject(), fileFail, loopsFail, durationFail); + } + } + + /** + * Fired when a target is started. + * @param event ignored. + * @see BuildEvent#getTarget() + */ + public void targetStarted(BuildEvent event) { + } + + /** + * Fired when a target has finished. This event will + * still be thrown if an error occurred during the build. + * @param event ignored. + * @see BuildEvent#getException() + */ + public void targetFinished(BuildEvent event) { + } + + /** + * Fired when a task is started. + * @param event ignored. + * @see BuildEvent#getTask() + */ + public void taskStarted(BuildEvent event) { + } + + /** + * Fired when a task has finished. This event will still + * be throw if an error occurred during the build. + * @param event ignored. + * @see BuildEvent#getException() + */ + public void taskFinished(BuildEvent event) { + } + + /** + * Fired whenever a message is logged. + * @param event the build event + * @see BuildEvent#getMessage() + * @see BuildEvent#getPriority() + */ + public void messageLogged(BuildEvent event) { + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sound/SoundTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sound/SoundTask.java new file mode 100644 index 00000000..dfb6e69a --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sound/SoundTask.java @@ -0,0 +1,193 @@ +/* + * 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.tools.ant.taskdefs.optional.sound; + +import java.io.File; +import java.util.Random; +import java.util.Vector; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; + +/** + * Plays a sound file at the end of the build, according to whether the build failed or succeeded. + * + * There are three attributes to be set: + * + * <code>source</code>: the location of the audio file to be played + * <code>duration</code>: play the sound file continuously until "duration" milliseconds has expired + * <code>loops</code>: the number of times the sound file should be played until stopped + * + * I have only tested this with .WAV and .AIFF sound file formats. Both seem + * to work fine. + * + * plans for the future: + * - use the midi api to define sounds (or drum beat etc) in xml and have + * Ant play them back + * + */ + +public class SoundTask extends Task { + + private BuildAlert success = null; + private BuildAlert fail = null; + + /** + * add a sound when the build succeeds + * @return a BuildAlert to be configured + */ + public BuildAlert createSuccess() { + success = new BuildAlert(); + return success; + } + + /** + * add a sound when the build fails + * @return a BuildAlert to be configured + */ + public BuildAlert createFail() { + fail = new BuildAlert(); + return fail; + } + + /** Constructor for SoundTask. */ + public SoundTask() { + } + + /** + * Initialize the task. + */ + public void init() { + } + + /** + * Execute the task. + */ + public void execute() { + + AntSoundPlayer soundPlayer = new AntSoundPlayer(); + + if (success == null) { + log("No nested success element found.", Project.MSG_WARN); + } else { + soundPlayer.addBuildSuccessfulSound(success.getSource(), + success.getLoops(), success.getDuration()); + } + + if (fail == null) { + log("No nested failure element found.", Project.MSG_WARN); + } else { + soundPlayer.addBuildFailedSound(fail.getSource(), + fail.getLoops(), fail.getDuration()); + } + + getProject().addBuildListener(soundPlayer); + + } + + /** + * A class to be extended by any BuildAlert's that require the output + * of sound. + */ + public class BuildAlert { + private File source = null; + private int loops = 0; + private Long duration = null; + + /** + * Sets the duration in milliseconds the file should be played; optional. + * @param duration the duration in milliseconds + */ + public void setDuration(Long duration) { + this.duration = duration; + } + + /** + * Sets the location of the file to get the audio; required. + * + * @param source the name of a sound-file directory or of the audio file + */ + public void setSource(File source) { + this.source = source; + } + + /** + * Sets the number of times the source file should be played; optional. + * + * @param loops the number of loops to play the source file + */ + public void setLoops(int loops) { + this.loops = loops; + } + + /** + * Gets the location of the file to get the audio. + * @return the file location + */ + public File getSource() { + File nofile = null; + // Check if source is a directory + if (source.exists()) { + if (source.isDirectory()) { + // get the list of files in the dir + String[] entries = source.list(); + Vector files = new Vector(); + for (int i = 0; i < entries.length; i++) { + File f = new File(source, entries[i]); + if (f.isFile()) { + files.addElement(f); + } + } + if (files.size() < 1) { + throw new BuildException("No files found in directory " + source); + } + int numfiles = files.size(); + // get a random number between 0 and the number of files + Random rn = new Random(); + int x = rn.nextInt(numfiles); + // set the source to the file at that location + this.source = (File) files.elementAt(x); + } + } else { + log(source + ": invalid path.", Project.MSG_WARN); + this.source = nofile; + } + return this.source; + } + + /** + * Sets the number of times the source file should be played. + * + * @return the number of loops to play the source file + */ + public int getLoops() { + return this.loops; + } + + /** + * Gets the duration in milliseconds the file should be played. + * @return the duration in milliseconds + */ + public Long getDuration() { + return this.duration; + } + } +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashScreen.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashScreen.java new file mode 100644 index 00000000..5de84cca --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashScreen.java @@ -0,0 +1,181 @@ +/* + * 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.tools.ant.taskdefs.optional.splash; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JWindow; + +import org.apache.tools.ant.BuildEvent; +import org.apache.tools.ant.BuildListener; + +class SplashScreen extends JWindow implements ActionListener, BuildListener { + private static final int FONT_SIZE = 12; + private JLabel text; + private JProgressBar pb; + private int total; + private static final int MIN = 0; + private static final int MAX = 200; + private Pattern progressRegExpPattern; + + public SplashScreen(String msg) { + this(msg, null, null); + } + + public SplashScreen(ImageIcon img) { + this(img, null, null); + } + + public SplashScreen(String msg, String progressRegExp, String displayText) { + init(null, progressRegExp, displayText); + setText(msg); + } + + public SplashScreen(ImageIcon img, String progressRegExp, + String displayText) { + init(img, progressRegExp, displayText); + } + + protected void init(ImageIcon img) { + init(img, null, null); + } + + protected void init(ImageIcon img, String progressRegExp, + String displayText) { + if (progressRegExp != null) { + progressRegExpPattern = Pattern.compile(progressRegExp); + } + + JPanel pan = (JPanel) getContentPane(); + JLabel piccy; + if (img == null) { + piccy = new JLabel(); + } else { + piccy = new JLabel(img); + } + + piccy.setBorder(BorderFactory.createLineBorder(Color.black, 1)); + if (displayText == null) { + displayText = "Building...."; + } + text = new JLabel(displayText, JLabel.CENTER); + text.setFont(new Font("Sans-Serif", Font.BOLD, FONT_SIZE)); + text.setBorder(BorderFactory.createEtchedBorder()); + + pb = new JProgressBar(MIN, MAX); + pb.setBorder(BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED)); + JPanel pan2 = new JPanel(); + pan2.setLayout(new BorderLayout()); + + pan2.add(text, BorderLayout.NORTH); + pan2.add(pb, BorderLayout.SOUTH); + + pan.setLayout(new BorderLayout()); + pan.add(piccy, BorderLayout.CENTER); + pan.add(pan2, BorderLayout.SOUTH); + + pan.setBorder(BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED)); + + pack(); + + Dimension size = getSize(); + Dimension scr = Toolkit.getDefaultToolkit().getScreenSize(); + int x = (scr.width - size.width) / 2; + int y = (scr.height - size.height) / 2; + setBounds(x, y, size.width, size.height); + } + + public void setText(String txt) { + text.setText(txt); + } + + public void actionPerformed(ActionEvent a) { + if (!hasProgressPattern()) { + if (total < MAX) { + total++; + } else { + total = MIN; + } + pb.setValue(total); + } + } + + public void buildStarted(BuildEvent event) { + actionPerformed(null); + } + + public void buildFinished(BuildEvent event) { + pb.setValue(MAX); + setVisible(false); + dispose(); + } + public void targetStarted(BuildEvent event) { + actionPerformed(null); + } + + public void targetFinished(BuildEvent event) { + actionPerformed(null); + } + + public void taskStarted(BuildEvent event) { + actionPerformed(null); + } + + public void taskFinished(BuildEvent event) { + actionPerformed(null); + } + + public void messageLogged(BuildEvent event) { + actionPerformed(null); + if (hasProgressPattern()) { + String message = event.getMessage(); + Matcher matcher = progressRegExpPattern.matcher(message); + if (matcher != null && matcher.matches()) { + String gr = matcher.group(1); + try { + int i = Math.min(new Integer(gr).intValue() * 2, MAX); + pb.setValue(i); + } catch (NumberFormatException e) { + //TODO: how to reach logger?!? + //log("Number parsing error in progressRegExp", Project.MSG_VERBOSE); + + } + } + } + } + + protected boolean hasProgressPattern() { + return progressRegExpPattern != null; + } + +} + diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashTask.java new file mode 100644 index 00000000..3a73bdf7 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashTask.java @@ -0,0 +1,297 @@ +/* + * 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.tools.ant.taskdefs.optional.splash; + +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; + +import javax.swing.ImageIcon; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.optional.net.SetProxy; +import org.apache.tools.ant.util.Base64Converter; + +/** + * Creates a splash screen. The splash screen is displayed + * for the duration of the build and includes a handy progress bar as + * well. Use in conjunction with the sound task to provide interest + * whilst waiting for your builds to complete... + * @since Ant1.5 + */ +public class SplashTask extends Task { + private static final int DEFAULT_SHOW_DURATION = 5000; + + private String imgurl = null; + private String proxy = null; + private String user = null; + private String password = null; + private String port = "80"; + private int showDuration = DEFAULT_SHOW_DURATION; + private boolean useProxy = false; + private String progressRegExp = null; + private String displayText = null; + + private static SplashScreen splash = null; + + /** + * A URL pointing to an image to display; optional, default antlogo.gif + * from the classpath. + * @param imgurl the url string pointing to the image + */ + public void setImageURL(String imgurl) { + this.imgurl = imgurl; + } + + /** + * flag to enable proxy settings; optional, deprecated : consider + * using <setproxy> instead + * @param useProxy if ture, enable proxy settings + * @deprecated since 1.5.x. + * Use org.apache.tools.ant.taskdefs.optional.net.SetProxy + */ + @Deprecated + public void setUseproxy(boolean useProxy) { + this.useProxy = useProxy; + } + + /** + * name of proxy; optional. + * @param proxy the name of the proxy host + * @deprecated since 1.5.x. + * Use org.apache.tools.ant.taskdefs.optional.net.SetProxy + */ + @Deprecated + public void setProxy(String proxy) { + this.proxy = proxy; + } + + /** + * Proxy port; optional, default 80. + * @param port the proxy port + * @deprecated since 1.5.x. + * Use org.apache.tools.ant.taskdefs.optional.net.SetProxy + */ + @Deprecated + public void setPort(String port) { + this.port = port; + } + + /** + * Proxy user; optional, default =none. + * @param user the proxy user + * @deprecated since 1.5.x. + * Use org.apache.tools.ant.taskdefs.optional.net.SetProxy + */ + @Deprecated + public void setUser(String user) { + this.user = user; + } + + /** + * Proxy password; required if <tt>user</tt> is set. + * @param password the proxy password + * @deprecated since 1.5.x. + * Use org.apache.tools.ant.taskdefs.optional.net.SetProxy + */ + @Deprecated + public void setPassword(String password) { + this.password = password; + } + + /** + * how long to show the splash screen in milliseconds, + * optional; default 5000 ms. + * @param duration the splash duration in milliseconds + */ + public void setShowduration(int duration) { + this.showDuration = duration; + } + + + /** + * Progress regular expression which is used to parse the output + * and dig out current progress optional; if not provided, + * progress is increased every action and log output line + * @param progressRegExp Progress regular expression, exactly one + * group pattern must exists, and it represents the progress + * number (0-100) (i.e "Progress: (.*)%") + * @since Ant 1.8.0 + */ + public void setProgressRegExp(String progressRegExp) { + this.progressRegExp = progressRegExp; + } + + /** + * Sets the display text presented in the splash window. + * optional; defaults to "Building ..." + * @param displayText the display text presented the splash window + * @since Ant 1.8.0 + */ + public void setDisplayText(String displayText) { + this.displayText = displayText; + } + + /** + * Execute the task. + * @throws BuildException on error + */ + @Override + public void execute() throws BuildException { + if (splash != null) { + splash.setVisible(false); + getProject().removeBuildListener(splash); + splash.dispose(); + splash = null; + } + + log("Creating new SplashScreen", Project.MSG_VERBOSE); + InputStream in = null; + + if (imgurl != null) { + try { + URLConnection conn = null; + + SetProxy sp = new SetProxy(); + sp.setProxyHost(proxy); + if (port != null) { + sp.setProxyPort(Integer.parseInt(port)); + } + sp.setProxyUser(user); + sp.setProxyPassword(password); + sp.applyWebProxySettings(); + + if (useProxy && (proxy != null && proxy.length() > 0) + && (port != null && port.length() > 0)) { + + log("Using proxied Connection", Project.MSG_DEBUG); + System.getProperties().put("http.proxySet", "true"); + + URL url = new URL(imgurl); + + conn = url.openConnection(); + if (user != null && user.length() > 0) { + // converted from sun internal classes to + // new Base64Converter + // utility class extracted from Get task + String encodedcreds = + new Base64Converter().encode(user + ":" + password); + conn.setRequestProperty("Proxy-Authorization", + encodedcreds); + } + + } else { + System.getProperties().put("http.proxySet", "false"); + log("Using Direction HTTP Connection", Project.MSG_DEBUG); + URL url = new URL(imgurl); + conn = url.openConnection(); + } + conn.setDoInput(true); + conn.setDoOutput(false); + + in = conn.getInputStream(); + + // Catch everything - some of the above return nulls, + // throw exceptions or generally misbehave + // in the event of a problem etc + + } catch (Throwable ioe) { + log("Unable to download image, trying default Ant Logo", + Project.MSG_DEBUG); + log("(Exception was \"" + ioe.getMessage() + "\"", + Project.MSG_DEBUG); + } + } + + if (in == null) { + ClassLoader cl = SplashTask.class.getClassLoader(); + if (cl != null) { + in = cl.getResourceAsStream("images/ant_logo_large.gif"); + } else { + in = ClassLoader + .getSystemResourceAsStream("images/ant_logo_large.gif"); + } + } + + boolean success = false; + if (in != null) { + DataInputStream din = new DataInputStream(in); + try { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + int data; + while ((data = din.read()) != -1) { + bout.write((byte) data); + } + + log("Got ByteArray, creating splash", Project.MSG_DEBUG); + + try { + ImageIcon img = new ImageIcon(bout.toByteArray()); + splash = new SplashScreen(img, progressRegExp, displayText); + success = true; + } catch (Throwable e) { + logHeadless(e); + } + } catch (Exception e) { + throw new BuildException(e); + } finally { + try { + din.close(); + } catch (IOException ioe) { + // swallow if there was an error before so that + // original error will be passed up + if (success) { + throw new BuildException(ioe); + } + } + } + } else { + try { + splash = new SplashScreen("Image Unavailable.", progressRegExp, + displayText); + success = true; + } catch (Throwable e) { + logHeadless(e); + } + } + + if (success) { + splash.setVisible(true); + splash.toFront(); + getProject().addBuildListener(splash); + try { + Thread.sleep(showDuration); + } catch (InterruptedException e) { + // Ignore Exception + } + } + } + + private void logHeadless(Throwable e) { + log("failed to display SplashScreen, caught " + + e.getClass().getName() + " with message: " + e.getMessage(), + Project.MSG_WARN); + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/AbstractSshMessage.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/AbstractSshMessage.java new file mode 100644 index 00000000..9365e8cd --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/AbstractSshMessage.java @@ -0,0 +1,272 @@ +/* + * 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.tools.ant.taskdefs.optional.ssh; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.NumberFormat; + +import org.apache.tools.ant.BuildException; + +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.SftpProgressMonitor; + +/** + * Abstract class for ssh upload and download + */ +public abstract class AbstractSshMessage { + private static final double ONE_SECOND = 1000.0; + + private final Session session; + private final boolean verbose; + private LogListener listener = new LogListener() { + public void log(final String message) { + // do nothing; + } + }; + + /** + * Constructor for AbstractSshMessage + * @param session the ssh session to use + */ + public AbstractSshMessage(final Session session) { + this(false, session); + } + + /** + * Constructor for AbstractSshMessage + * @param verbose if true do verbose logging + * @param session the ssh session to use + * @since Ant 1.6.2 + */ + public AbstractSshMessage(final boolean verbose, final Session session) { + this.verbose = verbose; + this.session = session; + } + + /** + * Open an ssh channel. + * @param command the command to use + * @return the channel + * @throws JSchException on error + */ + protected Channel openExecChannel(final String command) throws JSchException { + final ChannelExec channel = (ChannelExec) session.openChannel("exec"); + channel.setCommand(command); + + return channel; + } + + /** + * Open an ssh sftp channel. + * @return the channel + * @throws JSchException on error + */ + protected ChannelSftp openSftpChannel() throws JSchException { + final ChannelSftp channel = (ChannelSftp) session.openChannel("sftp"); + + return channel; + } + + /** + * Send an ack. + * @param out the output stream to use + * @throws IOException on error + */ + protected void sendAck(final OutputStream out) throws IOException { + final byte[] buf = new byte[1]; + buf[0] = 0; + out.write(buf); + out.flush(); + } + + /** + * Reads the response, throws a BuildException if the response + * indicates an error. + * @param in the input stream to use + * @throws IOException on I/O error + * @throws BuildException on other errors + */ + protected void waitForAck(final InputStream in) + throws IOException, BuildException { + final int b = in.read(); + + // b may be 0 for success, + // 1 for error, + // 2 for fatal error, + + if (b == -1) { + // didn't receive any response + throw new BuildException("No response from server"); + } else if (b != 0) { + final StringBuffer sb = new StringBuffer(); + + int c = in.read(); + while (c > 0 && c != '\n') { + sb.append((char) c); + c = in.read(); + } + + if (b == 1) { + throw new BuildException("server indicated an error: " + + sb.toString()); + } else if (b == 2) { + throw new BuildException("server indicated a fatal error: " + + sb.toString()); + } else { + throw new BuildException("unknown response, code " + b + + " message: " + sb.toString()); + } + } + } + + /** + * Carry out the transfer. + * @throws IOException on I/O errors + * @throws JSchException on ssh errors + */ + public abstract void execute() throws IOException, JSchException; + + /** + * Set a log listener. + * @param aListener the log listener + */ + public void setLogListener(final LogListener aListener) { + listener = aListener; + } + + /** + * Log a message to the log listener. + * @param message the message to log + */ + protected void log(final String message) { + listener.log(message); + } + + /** + * Log transfer stats to the log listener. + * @param timeStarted the time started + * @param timeEnded the finishing time + * @param totalLength the total length + */ + protected void logStats(final long timeStarted, + final long timeEnded, + final long totalLength) { + final double duration = (timeEnded - timeStarted) / ONE_SECOND; + final NumberFormat format = NumberFormat.getNumberInstance(); + format.setMaximumFractionDigits(2); + format.setMinimumFractionDigits(1); + listener.log("File transfer time: " + format.format(duration) + + " Average Rate: " + format.format(totalLength / duration) + + " B/s"); + } + + /** + * Is the verbose attribute set. + * @return true if the verbose attribute is set + * @since Ant 1.6.2 + */ + protected final boolean getVerbose() { + return verbose; + } + + /** + * Track progress every 10% if 100kb < filesize < 1mb. For larger + * files track progress for every percent transmitted. + * @param filesize the size of the file been transmitted + * @param totalLength the total transmission size + * @param percentTransmitted the current percent transmitted + * @return the percent that the file is of the total + */ + protected final int trackProgress(final long filesize, final long totalLength, + final int percentTransmitted) { + + // CheckStyle:MagicNumber OFF + final int percent = (int) Math.round(Math.floor((totalLength + / (double) filesize) * 100)); + + if (percent > percentTransmitted) { + if (filesize < 1048576) { + if (percent % 10 == 0) { + if (percent == 100) { + System.out.println(" 100%"); + } else { + System.out.print("*"); + } + } + } else { + if (percent == 50) { + System.out.println(" 50%"); + } else if (percent == 100) { + System.out.println(" 100%"); + } else { + System.out.print("."); + } + } + } + // CheckStyle:MagicNumber ON + + return percent; + } + + private ProgressMonitor monitor = null; + + /** + * Get the progress monitor. + * @return the progress monitor. + */ + protected SftpProgressMonitor getProgressMonitor() { + if (monitor == null) { + monitor = new ProgressMonitor(); + } + return monitor; + } + + private class ProgressMonitor implements SftpProgressMonitor { + private long initFileSize = 0; + private long totalLength = 0; + private int percentTransmitted = 0; + + public void init(final int op, final String src, final String dest, final long max) { + initFileSize = max; + totalLength = 0; + percentTransmitted = 0; + } + + public boolean count(final long len) { + totalLength += len; + percentTransmitted = trackProgress(initFileSize, + totalLength, + percentTransmitted); + return true; + } + + public void end() { + } + + public long getTotalLength() { + return totalLength; + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Directory.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Directory.java new file mode 100644 index 00000000..b5088a7d --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Directory.java @@ -0,0 +1,196 @@ +/* + * 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.tools.ant.taskdefs.optional.ssh; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.StringTokenizer; + +/** + * A helper object for Scp representing a directory in a file system. + */ +public class Directory { + + private File directory; + private Set childDirectories; + private ArrayList files; + private Directory parent; + + /** + * Constructor for a Directory. + * @param directory a directory. + */ + public Directory(File directory) { + this(directory, null); + } + + /** + * Constructor for a Directory. + * @param directory a directory + * @param parent a parent Directory + */ + public Directory(File directory , Directory parent) { + this.parent = parent; + this.childDirectories = new LinkedHashSet(); + this.files = new ArrayList(); + this.directory = directory; + } + + /** + * Add a directory to the child directories. + * @param directory a Directory + */ + public void addDirectory(Directory directory) { + if (!childDirectories.contains(directory)) { + childDirectories.add(directory); + } + } + + /** + * Add a file to the list of files. + * @param file a file to add + */ + public void addFile(File file) { + files.add(file); + } + + /** + * Get an iterator over the child Directories. + * @return an iterator + */ + public Iterator directoryIterator() { + return childDirectories.iterator(); + } + + /** + * Get an iterator over the files. + * @return an iterator + */ + public Iterator filesIterator() { + return files.iterator(); + } + + /** + * Get the parent Directory. + * @return the parent Directory. + */ + public Directory getParent() { + return parent; + } + + /** + * Is this a root Directory? + * @return true if there is no parent Directory + */ + public boolean isRoot() { + return parent == null; + } + + /** + * Get the directory file. + * @return the directory file + */ + public File getDirectory() { + return directory; + } + + /** + * Get a child directory of this directory. + * @param dir the directory to look for + * @return the child directory, or null if not found + */ + public Directory getChild(File dir) { + for (Iterator i = childDirectories.iterator(); i.hasNext();) { + Directory current = (Directory) i.next(); + if (current.getDirectory().equals(dir)) { + return current; + } + } + return null; + } + + /** + * The equality method. + * This checks if the directory field is the same. + * @param obj the object to compare to + * @return true if this object has an equal directory field as the other object + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (!(obj instanceof Directory)) { + return false; + } + + Directory d = (Directory) obj; + + return this.directory.equals(d.directory); + } + + /** + * The hashcode method. + * @return the hash code of the directory field + */ + @Override + public int hashCode() { + return directory.hashCode(); + } + + /** + * Get the path components of this directory. + * @return the path components as an array of strings. + */ + public String[] getPath() { + return getPath(directory.getAbsolutePath()); + } + + /** + * Convert a file path to an array of path components. + * This uses File.separator to split the file path string. + * @param thePath the file path string to convert + * @return an array of path components + */ + public static String[] getPath(String thePath) { + StringTokenizer tokenizer = new StringTokenizer(thePath, + File.separator); + String[] path = new String[ tokenizer.countTokens() ]; + + int i = 0; + while (tokenizer.hasMoreTokens()) { + path[i] = tokenizer.nextToken(); + i++; + } + + return path; + } + + /** + * Get the number of files in the files attribute. + * @return the number of files + */ + public int fileSize() { + return files.size(); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/LogListener.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/LogListener.java new file mode 100644 index 00000000..41209ceb --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/LogListener.java @@ -0,0 +1,30 @@ +/* + * 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.tools.ant.taskdefs.optional.ssh; + +/** + * Interface for ssh log listeners to implement. + */ +public interface LogListener { + /** + * Method for the loglistener to implement to receive log messages. + * @param message the message to log + */ + void log(String message); +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHBase.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHBase.java new file mode 100644 index 00000000..68419a85 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHBase.java @@ -0,0 +1,236 @@ +/* + * 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.tools.ant.taskdefs.optional.ssh; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; + +/** + * Base class for Ant tasks using jsch. + * + * @since Ant 1.6 + */ +public abstract class SSHBase extends Task implements LogListener { + + /** Default listen port for SSH daemon */ + private static final int SSH_PORT = 22; + + private String host; + private String knownHosts; + private int port = SSH_PORT; + private boolean failOnError = true; + private boolean verbose; + private final SSHUserInfo userInfo; + + /** + * Constructor for SSHBase. + */ + public SSHBase() { + super(); + userInfo = new SSHUserInfo(); + } + + /** + * Remote host, either DNS name or IP. + * + * @param host The new host value + */ + public void setHost(final String host) { + this.host = host; + } + + /** + * Get the host. + * @return the host + */ + public String getHost() { + return host; + } + + /** + * Set the failonerror flag. + * Default is true + * @param failure if true throw a build exception when a failure occuries, + * otherwise just log the failure and continue + */ + public void setFailonerror(final boolean failure) { + failOnError = failure; + } + + /** + * Get the failonerror flag. + * @return the failonerror flag + */ + public boolean getFailonerror() { + return failOnError; + } + + /** + * Set the verbose flag. + * @param verbose if true output more verbose logging + * @since Ant 1.6.2 + */ + public void setVerbose(final boolean verbose) { + this.verbose = verbose; + } + + /** + * Get the verbose flag. + * @return the verbose flag + * @since Ant 1.6.2 + */ + public boolean getVerbose() { + return verbose; + } + + /** + * Username known to remote host. + * + * @param username The new username value + */ + public void setUsername(final String username) { + userInfo.setName(username); + } + + + /** + * Sets the password for the user. + * + * @param password The new password value + */ + public void setPassword(final String password) { + userInfo.setPassword(password); + } + + /** + * Sets the keyfile for the user. + * + * @param keyfile The new keyfile value + */ + public void setKeyfile(final String keyfile) { + userInfo.setKeyfile(keyfile); + } + + /** + * Sets the passphrase for the users key. + * + * @param passphrase The new passphrase value + */ + public void setPassphrase(final String passphrase) { + userInfo.setPassphrase(passphrase); + } + + /** + * Sets the path to the file that has the identities of + * all known hosts. This is used by SSH protocol to validate + * the identity of the host. The default is + * <i>${user.home}/.ssh/known_hosts</i>. + * + * @param knownHosts a path to the known hosts file. + */ + public void setKnownhosts(final String knownHosts) { + this.knownHosts = knownHosts; + } + + /** + * Setting this to true trusts hosts whose identity is unknown. + * + * @param yesOrNo if true trust the identity of unknown hosts. + */ + public void setTrust(final boolean yesOrNo) { + userInfo.setTrust(yesOrNo); + } + + /** + * Changes the port used to connect to the remote host. + * + * @param port port number of remote host. + */ + public void setPort(final int port) { + this.port = port; + } + + /** + * Get the port attribute. + * @return the port + */ + public int getPort() { + return port; + } + + /** + * Initialize the task. + * This initializizs the known hosts and sets the default port. + * @throws BuildException on error + */ + public void init() throws BuildException { + super.init(); + this.knownHosts = System.getProperty("user.home") + "/.ssh/known_hosts"; + this.port = SSH_PORT; + } + + /** + * Open an ssh session. + * @return the opened session + * @throws JSchException on error + */ + protected Session openSession() throws JSchException { + final JSch jsch = new JSch(); + final SSHBase base = this; + if (verbose) { + JSch.setLogger(new com.jcraft.jsch.Logger(){ + public boolean isEnabled(final int level){ + return true; + } + public void log(final int level, final String message){ + base.log(message, Project.MSG_INFO); + } + }); + } + if (null != userInfo.getKeyfile()) { + jsch.addIdentity(userInfo.getKeyfile()); + } + + if (!userInfo.getTrust() && knownHosts != null) { + log("Using known hosts: " + knownHosts, Project.MSG_DEBUG); + jsch.setKnownHosts(knownHosts); + } + + final Session session = jsch.getSession(userInfo.getName(), host, port); + session.setConfig("PreferredAuthentications", + "publickey,keyboard-interactive,password"); + session.setUserInfo(userInfo); + log("Connecting to " + host + ":" + port); + session.connect(); + return session; + } + + /** + * Get the user information. + * @return the user information + */ + protected SSHUserInfo getUserInfo() { + return userInfo; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java new file mode 100644 index 00000000..a04dfefa --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java @@ -0,0 +1,519 @@ +/* + * 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.tools.ant.taskdefs.optional.ssh; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.StringReader; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.types.Resource; +import org.apache.tools.ant.types.resources.FileResource; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.KeepAliveInputStream; +import org.apache.tools.ant.util.KeepAliveOutputStream; +import org.apache.tools.ant.util.TeeOutputStream; + +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; + +/** + * Executes a command on a remote machine via ssh. + * @since Ant 1.6 (created February 2, 2003) + */ +public class SSHExec extends SSHBase { + + private static final int BUFFER_SIZE = 8192; + private static final int RETRY_INTERVAL = 500; + + /** the command to execute via ssh */ + private String command = null; + + /** units are milliseconds, default is 0=infinite */ + private long maxwait = 0; + + /** for waiting for the command to finish */ + private Thread thread = null; + + private String outputProperty = null; // like <exec> + private String errorProperty = null; + private String resultProperty = null; + private File outputFile = null; // like <exec> + private File errorFile = null; + private String inputProperty = null; + private String inputString = null; // like <exec> + private File inputFile = null; // like <exec> + private boolean append = false; // like <exec> + private boolean appenderr = false; + private boolean usePty = false; + private boolean useSystemIn = false; + + private Resource commandResource = null; + + private static final String TIMEOUT_MESSAGE = + "Timeout period exceeded, connection dropped."; + + /** + * To suppress writing logs to System.out + */ + private boolean suppressSystemOut = false; + + /** + * To suppress writing logs to System.err + */ + private boolean suppressSystemErr = false; + + /** + * Constructor for SSHExecTask. + */ + public SSHExec() { + super(); + } + + /** + * Sets the command to execute on the remote host. + * + * @param command The new command value + */ + public void setCommand(final String command) { + this.command = command; + } + + /** + * Sets a commandResource from a file + * @param f the value to use. + * @since Ant 1.7.1 + */ + public void setCommandResource(final String f) { + this.commandResource = new FileResource(new File(f)); + } + + /** + * The connection can be dropped after a specified number of + * milliseconds. This is sometimes useful when a connection may be + * flaky. Default is 0, which means "wait forever". + * + * @param timeout The new timeout value in seconds + */ + public void setTimeout(final long timeout) { + maxwait = timeout; + } + + /** + * If used, stores the output of the command to the given file. + * + * @param output The file to write to. + */ + public void setOutput(final File output) { + outputFile = output; + } + + /** + * If used, stores the erroutput of the command to the given file. + * + * @param output The file to write to. + * @since Apache Ant 1.9.4 + */ + public void setErrorOutput(final File output) { + errorFile = output; + } + + /** + * If used, the content of the file is piped to the remote command + * + * @param input The file which provides the input data for the remote command + * + * @since Ant 1.8.0 + */ + public void setInput(final File input) { + inputFile = input; + } + + /** + * If used, the content of the property is piped to the remote command + * + * @param inputProperty The property which contains the input data + * for the remote command. + * + * @since Ant 1.8.0 + */ + public void setInputProperty(final String inputProperty) { + this.inputProperty = inputProperty; + } + + /** + * If used, the string is piped to the remote command. + * + * @param inputString the input data for the remote command. + * + * @since Ant 1.8.3 + */ + public void setInputString(final String inputString) { + this.inputString = inputString; + } + + /** + * Determines if the output is appended to the file given in + * <code>setOutput</code>. Default is false, that is, overwrite + * the file. + * + * @param append True to append to an existing file, false to overwrite. + */ + public void setAppend(final boolean append) { + this.append = append; + } + + /** + * Determines if the output is appended to the file given in + * <code>setErrorOutput</code>. Default is false, that is, overwrite + * the file. + * + * @param appenderr True to append to an existing file, false to overwrite. + * @since Apache Ant 1.9.4 + */ + public void setErrAppend(final boolean appenderr) { + this.appenderr = appenderr; + } + + /** + * If set, the output of the command will be stored in the given property. + * + * @param property The name of the property in which the command output + * will be stored. + */ + public void setOutputproperty(final String property) { + outputProperty = property; + } + + /** + * If set, the erroroutput of the command will be stored in the given property. + * + * @param property The name of the property in which the command erroroutput + * will be stored. + * @since Apache Ant 1.9.4 + */ + public void setErrorproperty (final String property) { + errorProperty = property; + } + + /** + * If set, the exitcode of the command will be stored in the given property. + * + * @param property The name of the property in which the exitcode + * will be stored. + * @since Apache Ant 1.9.4 + */ + public void setResultproperty(final String property) { + resultProperty = property; + } + + /** + * Whether a pseudo-tty should be allocated. + * @since Apache Ant 1.8.3 + */ + public void setUsePty(final boolean b) { + usePty = b; + } + + /** + * If set, input will be taken from System.in + * + * @param useSystemIn True to use System.in as InputStream, false otherwise + * @since Apache Ant 1.9.4 + */ + public void setUseSystemIn(final boolean useSystemIn) { + this.useSystemIn = useSystemIn; + } + + /** + * If suppressSystemOut is <code>true</code>, output will not be sent to System.out<br/> + * If suppressSystemOut is <code>false</code>, normal behavior + * @since Ant 1.9.0 + */ + public void setSuppressSystemOut(final boolean suppressSystemOut) { + this.suppressSystemOut = suppressSystemOut; + } + + /** + * If suppressSystemErr is <code>true</code>, output will not be sent to System.err<br/> + * If suppressSystemErr is <code>false</code>, normal behavior + * @since Ant 1.9.4 + */ + public void setSuppressSystemErr(final boolean suppressSystemErr) { + this.suppressSystemErr = suppressSystemErr; + } + + /** + * Execute the command on the remote host. + * + * @exception BuildException Most likely a network error or bad parameter. + */ + @Override + public void execute() throws BuildException { + + if (getHost() == null) { + throw new BuildException("Host is required."); + } + if (getUserInfo().getName() == null) { + throw new BuildException("Username is required."); + } + if (getUserInfo().getKeyfile() == null + && getUserInfo().getPassword() == null) { + throw new BuildException("Password or Keyfile is required."); + } + if (command == null && commandResource == null) { + throw new BuildException("Command or commandResource is required."); + } + + final int numberOfInputs = (inputFile != null ? 1 : 0) + + (inputProperty != null ? 1 : 0) + + (inputString != null ? 1 : 0); + if (numberOfInputs > 1) { + throw new BuildException("You can't specify more than one of" + + " inputFile, inputProperty and" + + " inputString."); + } + if (inputFile != null && !inputFile.exists()) { + throw new BuildException("The input file " + + inputFile.getAbsolutePath() + + " does not exist."); + } + + Session session = null; + final StringBuffer output = new StringBuffer(); + try { + session = openSession(); + /* called once */ + if (command != null) { + log("cmd : " + command, Project.MSG_INFO); + executeCommand(session, command, output); + } else { // read command resource and execute for each command + try { + final BufferedReader br = new BufferedReader( + new InputStreamReader(commandResource.getInputStream())); + String cmd; + while ((cmd = br.readLine()) != null) { + log("cmd : " + cmd, Project.MSG_INFO); + output.append(cmd).append(" : "); + executeCommand(session, cmd, output); + output.append("\n"); + } + FileUtils.close(br); + } catch (final IOException e) { + if (getFailonerror()) { + throw new BuildException(e); + } else { + log("Caught exception: " + e.getMessage(), + Project.MSG_ERR); + } + } + } + } catch (final JSchException e) { + if (getFailonerror()) { + throw new BuildException(e); + } else { + log("Caught exception: " + e.getMessage(), Project.MSG_ERR); + } + } finally { + if (outputProperty != null) { + getProject().setNewProperty(outputProperty, output.toString()); + } + if (session != null && session.isConnected()) { + session.disconnect(); + } + } + } + + private void executeCommand(final Session session, final String cmd, final StringBuffer sb) + throws BuildException { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final ByteArrayOutputStream errout = new ByteArrayOutputStream(); + final OutputStream teeErr = suppressSystemErr ? errout : new TeeOutputStream(errout, KeepAliveOutputStream.wrapSystemErr()); + final OutputStream tee = suppressSystemOut ? out : new TeeOutputStream(out, KeepAliveOutputStream.wrapSystemOut()); + + InputStream istream = null; + if (inputFile != null) { + try { + istream = new FileInputStream(inputFile); + } catch (final IOException e) { + // because we checked the existence before, this one + // shouldn't happen What if the file exists, but there + // are no read permissions? + log("Failed to read " + inputFile + " because of: " + + e.getMessage(), Project.MSG_WARN); + } + } + if (inputProperty != null) { + final String inputData = getProject().getProperty(inputProperty); + if (inputData != null) { + istream = new ByteArrayInputStream(inputData.getBytes()); + } + } + if (inputString != null) { + istream = new ByteArrayInputStream(inputString.getBytes()); + } + + if (useSystemIn) { + istream = KeepAliveInputStream.wrapSystemIn(); + } + + try { + final ChannelExec channel; + session.setTimeout((int) maxwait); + /* execute the command */ + channel = (ChannelExec) session.openChannel("exec"); + channel.setCommand(cmd); + channel.setOutputStream(tee); + channel.setExtOutputStream(tee); + channel.setErrStream(teeErr); + if (istream != null) { + channel.setInputStream(istream); + } + channel.setPty(usePty); + channel.connect(); + // wait for it to finish + thread = + new Thread() { + @Override + public void run() { + while (!channel.isClosed()) { + if (thread == null) { + return; + } + try { + sleep(RETRY_INTERVAL); + } catch (final Exception e) { + // ignored + } + } + } + }; + + thread.start(); + thread.join(maxwait); + + if (thread.isAlive()) { + // ran out of time + thread = null; + if (getFailonerror()) { + throw new BuildException(TIMEOUT_MESSAGE); + } else { + log(TIMEOUT_MESSAGE, Project.MSG_ERR); + } + } else { + // stdout to outputFile + if (outputFile != null) { + writeToFile(out.toString(), append, outputFile); + } + // set errorProperty + if (errorProperty != null) { + getProject().setNewProperty(errorProperty, errout.toString()); + } + // stderr to errorFile + if (errorFile != null) { + writeToFile(errout.toString(), appenderr, errorFile); + } + // this is the wrong test if the remote OS is OpenVMS, + // but there doesn't seem to be a way to detect it. + final int ec = channel.getExitStatus(); + // set resultproperty + if (resultProperty != null) { + getProject().setNewProperty(resultProperty, Integer.toString(ec)); + } + if (ec != 0) { + final String msg = "Remote command failed with exit status " + ec; + if (getFailonerror()) { + throw new BuildException(msg); + } else { + log(msg, Project.MSG_ERR); + } + } + } + } catch (final BuildException e) { + throw e; + } catch (final JSchException e) { + if (e.getMessage().indexOf("session is down") >= 0) { + if (getFailonerror()) { + throw new BuildException(TIMEOUT_MESSAGE, e); + } else { + log(TIMEOUT_MESSAGE, Project.MSG_ERR); + } + } else { + if (getFailonerror()) { + throw new BuildException(e); + } else { + log("Caught exception: " + e.getMessage(), + Project.MSG_ERR); + } + } + } catch (final Exception e) { + if (getFailonerror()) { + throw new BuildException(e); + } else { + log("Caught exception: " + e.getMessage(), Project.MSG_ERR); + } + } finally { + sb.append(out.toString()); + FileUtils.close(istream); + } + } + + /** + * Writes a string to a file. If destination file exists, it may be + * overwritten depending on the "append" value. + * + * @param from string to write + * @param to file to write to + * @param append if true, append to existing file, else overwrite + * @exception Exception most likely an IOException + */ + private void writeToFile(final String from, final boolean append, final File to) + throws IOException { + FileWriter out = null; + try { + out = new FileWriter(to.getAbsolutePath(), append); + final StringReader in = new StringReader(from); + final char[] buffer = new char[BUFFER_SIZE]; + int bytesRead; + while (true) { + bytesRead = in.read(buffer); + if (bytesRead == -1) { + break; + } + out.write(buffer, 0, bytesRead); + } + out.flush(); + } finally { + if (out != null) { + out.close(); + } + } + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHSession.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHSession.java new file mode 100644 index 00000000..e9f26757 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHSession.java @@ -0,0 +1,333 @@ +/* + * 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.tools.ant.taskdefs.optional.ssh; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +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.TaskContainer; + +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; + + +/** + * Establishes an ssh session with a remote machine, optionally + * establishing port forwarding, then executes any nested task(s) + * before closing the session. + * @since Ant 1.8.0 + */ +public class SSHSession extends SSHBase { + + /** units are milliseconds, default is 0=infinite */ + private long maxwait = 0; + + private final Vector localTunnels = new Vector(); + private final Set localPortsUsed = new TreeSet(); + private final Vector remoteTunnels = new Vector(); + private final Set remotePortsUsed = new TreeSet(); + private NestedSequential nestedSequential = null; + + private static final String TIMEOUT_MESSAGE = + "Timeout period exceeded, connection dropped."; + + + /** Optional Vector holding the nested tasks */ + private final Vector nestedTasks = new Vector(); + + /** + * Add a nested task to Sequential. + * <p> + * @param nestedTask Nested task to execute Sequential + * <p> + */ + public void addTask(final Task nestedTask) { + nestedTasks.addElement(nestedTask); + } + + /** + * The connection can be dropped after a specified number of + * milliseconds. This is sometimes useful when a connection may be + * flaky. Default is 0, which means "wait forever". + * + * @param timeout The new timeout value in seconds + */ + public void setTimeout(final long timeout) { + maxwait = timeout; + } + + /** + * Changes the comma-delimited list of local tunnels to establish + * on the connection. + * + * @param tunnels a comma-delimited list of lport:rhost:rport + * tunnel specifications + */ + public void setLocaltunnels(final String tunnels) { + final String[] specs = tunnels.split(", "); + for (int i = 0; i < specs.length; i++) { + if (specs[i].length() > 0) { + final String[] spec = specs[i].split(":", 3); + final int lport = Integer.parseInt(spec[0]); + final String rhost = spec[1]; + final int rport = Integer.parseInt(spec[2]); + final LocalTunnel tunnel = createLocalTunnel(); + tunnel.setLPort(lport); + tunnel.setRHost(rhost); + tunnel.setRPort(rport); + } + } + } + + /** + * Changes the comma-delimited list of remote tunnels to establish + * on the connection. + * + * @param tunnels a comma-delimited list of rport:lhost:lport + * tunnel specifications + */ + public void setRemotetunnels(final String tunnels) { + final String[] specs = tunnels.split(", "); + for (int i = 0; i < specs.length; i++) { + if (specs[i].length() > 0) { + final String[] spec = specs[i].split(":", 3); + final int rport = Integer.parseInt(spec[0]); + final String lhost = spec[1]; + final int lport = Integer.parseInt(spec[2]); + final RemoteTunnel tunnel = createRemoteTunnel(); + tunnel.setRPort(rport); + tunnel.setLHost(lhost); + tunnel.setLPort(lport); + } + } + } + + + /** + * Establish the ssh session and execute all nestedTasks + * + * @exception BuildException if one of the nested tasks fails, or + * network error or bad parameter. + */ + @Override + public void execute() throws BuildException { + if (getHost() == null) { + throw new BuildException("Host is required."); + } + if (getUserInfo().getName() == null) { + throw new BuildException("Username is required."); + } + if (getUserInfo().getKeyfile() == null + && getUserInfo().getPassword() == null) { + throw new BuildException("Password or Keyfile is required."); + } + if (nestedSequential == null) { + throw new BuildException("Missing sequential element."); + } + + + Session session = null; + try { + // establish the session + session = openSession(); + session.setTimeout((int) maxwait); + + for (final Iterator i = localTunnels.iterator(); i.hasNext();) { + final LocalTunnel tunnel = (LocalTunnel) i.next(); + session.setPortForwardingL(tunnel.getLPort(), + tunnel.getRHost(), + tunnel.getRPort()); + } + + for (final Iterator i = remoteTunnels.iterator(); i.hasNext();) { + final RemoteTunnel tunnel = (RemoteTunnel) i.next(); + session.setPortForwardingR(tunnel.getRPort(), + tunnel.getLHost(), + tunnel.getLPort()); + } + + for (final Iterator i = nestedSequential.getNested().iterator(); + i.hasNext();) { + final Task nestedTask = (Task) i.next(); + nestedTask.perform(); + } + // completed successfully + + } catch (final JSchException e) { + if (e.getMessage().indexOf("session is down") >= 0) { + if (getFailonerror()) { + throw new BuildException(TIMEOUT_MESSAGE, e); + } else { + log(TIMEOUT_MESSAGE, Project.MSG_ERR); + } + } else { + if (getFailonerror()) { + throw new BuildException(e); + } else { + log("Caught exception: " + e.getMessage(), + Project.MSG_ERR); + } + } + } catch (final BuildException e) { + // avoid wrapping it into yet another BuildException further down + throw e; + } catch (final Exception e) { + if (getFailonerror()) { + throw new BuildException(e); + } else { + log("Caught exception: " + e.getMessage(), Project.MSG_ERR); + } + } finally { + if (session != null && session.isConnected()) { + session.disconnect(); + } + } + } + + public LocalTunnel createLocalTunnel() { + final LocalTunnel tunnel = new LocalTunnel(); + localTunnels.add(tunnel); + return tunnel; + } + + public RemoteTunnel createRemoteTunnel() { + final RemoteTunnel tunnel = new RemoteTunnel(); + remoteTunnels.add(tunnel); + return tunnel; + } + + public class LocalTunnel { + public LocalTunnel() {} + + int lport = 0; + String rhost = null; + int rport = 0; + public void setLPort(final int lport) { + final Integer portKey = new Integer(lport); + if (localPortsUsed.contains(portKey)) { + throw new BuildException("Multiple local tunnels defined to" + + " use same local port " + lport); + } + localPortsUsed.add(portKey); + this.lport = lport; + } + public void setRHost(final String rhost) { this.rhost = rhost; } + public void setRPort(final int rport) { this.rport = rport; } + public int getLPort() { + if (lport == 0) { + throw new BuildException("lport is required for LocalTunnel."); + } + return lport; + } + public String getRHost() { + if (rhost == null) { + throw new BuildException("rhost is required for LocalTunnel."); + } + return rhost; + } + public int getRPort() { + if (rport == 0) { + throw new BuildException("rport is required for LocalTunnel."); + } + return rport; + } + } + + public class RemoteTunnel { + public RemoteTunnel() {} + + int lport = 0; + String lhost = null; + int rport = 0; + public void setLPort(final int lport) { this.lport = lport; } + public void setLHost(final String lhost) { this.lhost = lhost; } + public void setRPort(final int rport) { + final Integer portKey = new Integer(rport); + if (remotePortsUsed.contains(portKey)) { + throw new BuildException("Multiple remote tunnels defined to" + + " use same remote port " + rport); + } + remotePortsUsed.add(portKey); + this.rport = rport; + } + public int getLPort() { + if (lport == 0) { + throw new BuildException("lport is required for RemoteTunnel."); + } + return lport; + } + public String getLHost() { + if (lhost == null) { + throw new BuildException("lhost is required for RemoteTunnel."); + } + return lhost; + } + public int getRPort() { + if (rport == 0) { + throw new BuildException("rport is required for RemoteTunnel."); + } + return rport; + } + } + + /** + * This is the sequential nested element of the macrodef. + * + * @return a sequential element to be configured. + */ + public NestedSequential createSequential() { + if (this.nestedSequential != null) { + throw new BuildException("Only one sequential allowed"); + } + this.nestedSequential = new NestedSequential(); + return this.nestedSequential; + } + + /** + * The class corresponding to the sequential nested element. + * This is a simple task container. + */ + public static class NestedSequential implements TaskContainer { + private final List<Task> nested = new ArrayList<Task>(); + + /** + * Add a task or type to the container. + * + * @param task an unknown element. + */ + public void addTask(final Task task) { + nested.add(task); + } + + /** + * @return the list of unknown elements + */ + public List<Task> getNested() { + return nested; + } + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHUserInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHUserInfo.java new file mode 100644 index 00000000..54e70293 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHUserInfo.java @@ -0,0 +1,217 @@ +/* + * 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.tools.ant.taskdefs.optional.ssh; + +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; + + +/** + * Class containing information on an SSH user. + */ +public class SSHUserInfo implements UserInfo, UIKeyboardInteractive { + + private String name; + private String password = null; + private String keyfile; + private String passphrase = null; + private boolean trustAllCertificates; + + /** Constructor for SSHUserInfo. */ + public SSHUserInfo() { + super(); + this.trustAllCertificates = false; + } + + /** + * Constructor for SSHUserInfo. + * @param password the user's password + * @param trustAllCertificates if true trust hosts whose identity is unknown + */ + public SSHUserInfo(String password, boolean trustAllCertificates) { + super(); + this.password = password; + this.trustAllCertificates = trustAllCertificates; + } + + /** + * Gets the user name. + * @return the user name + */ + public String getName() { + return name; + } + + /** + * Gets the pass phrase of the user. + * @param message a message + * @return the passphrase + */ + public String getPassphrase(String message) { + return passphrase; + } + + /** + * Gets the user's password. + * @return the user's password + */ + public String getPassword() { + return password; + } + + /** + * Prompts a string. + * @param str the string + * @return whether the string was prompted + */ + public boolean prompt(String str) { + return false; + } + + /** + * Indicates whether a retry was done. + * @return whether a retry was done + */ + public boolean retry() { + return false; + } + + /** + * Sets the name. + * @param name The name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * Sets the passphrase. + * @param passphrase The passphrase to set + */ + public void setPassphrase(String passphrase) { + this.passphrase = passphrase; + } + + /** + * Sets the password. + * @param password The password to set + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * Sets the trust. + * @param trust whether to trust or not. + */ + public void setTrust(boolean trust) { + this.trustAllCertificates = trust; + } + + /** + * @return whether to trust or not. + */ + public boolean getTrust() { + return this.trustAllCertificates; + } + + /** + * Returns the passphrase. + * @return String + */ + public String getPassphrase() { + return passphrase; + } + + /** + * Returns the keyfile. + * @return String + */ + public String getKeyfile() { + return keyfile; + } + + /** + * Sets the keyfile. + * @param keyfile The keyfile to set + */ + public void setKeyfile(String keyfile) { + this.keyfile = keyfile; + } + + /** + * Implement the UserInfo interface. + * @param message ignored + * @return true always + */ + public boolean promptPassphrase(String message) { + return true; + } + + /** + * Implement the UserInfo interface. + * @param passwordPrompt ignored + * @return true the first time this is called, false otherwise + */ + public boolean promptPassword(String passwordPrompt) { + return true; + } + + /** + * Implement the UserInfo interface. + * @param message ignored + * @return the value of trustAllCertificates + */ + public boolean promptYesNo(String message) { + return trustAllCertificates; + } + +//why do we do nothing? + /** + * Implement the UserInfo interface (noop). + * @param message ignored + */ + public void showMessage(String message) { + //log(message, Project.MSG_DEBUG); + } + + /** + * Implementation of UIKeyboardInteractive#promptKeyboardInteractive. + * @param destination not used. + * @param name not used. + * @param instruction not used. + * @param prompt the method checks if this is one in length. + * @param echo the method checks if the first element is false. + * @return the password in an size one array if there is a password + * and if the prompt and echo checks pass. + */ + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo) { + if (prompt.length != 1 || echo[0] || this.password == null) { + return null; + } + String[] response = new String[1]; + response[0] = this.password; + return response; + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java new file mode 100644 index 00000000..46e2ac64 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java @@ -0,0 +1,486 @@ +/* + * 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.tools.ant.taskdefs.optional.ssh; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.types.FileSet; + +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; + +/** + * Ant task for sending files to remote machine over ssh/scp. + * + * @since Ant 1.6 + */ +public class Scp extends SSHBase { + + private static final String[] FROM_ATTRS = { + "file", "localfile", "remotefile" }; + + private static final String[] TO_ATTRS = { + "todir", "localtodir", "remotetodir", "localtofile", "remotetofile" }; + + private String fromUri; + private String toUri; + private boolean preserveLastModified = false; + private List fileSets = null; + private boolean isFromRemote, isToRemote; + private boolean isSftp = false; + private Integer fileMode, dirMode; + + /** + * Sets the file to be transferred. This can either be a remote + * file or a local file. Remote files take the form:<br> + * <i>user:password@host:/directory/path/file.example</i><br> + * Files to transfer can also include a wildcard to include all + * files in a remote directory. For example:<br> + * <i>user:password@host:/directory/path/*</i><br> + * @param aFromUri a string representing the file to transfer. + */ + public void setFile(final String aFromUri) { + setFromUri(aFromUri); + this.isFromRemote = isRemoteUri(this.fromUri); + } + + /** + * Sets the location where files will be transferred to. + * This can either be a remote directory or a local directory. + * Remote directories take the form of:<br> + * <i>user:password@host:/directory/path/</i><br> + * This parameter is required. + + * @param aToUri a string representing the target of the copy. + */ + public void setTodir(final String aToUri) { + setToUri(aToUri); + this.isToRemote = isRemoteUri(this.toUri); + } + + /** + * Similar to {@link #setFile setFile} but explicitly states that + * the file is a local file. This is the only way to specify a + * local file with a @ character. + * @param aFromUri a string representing the source of the copy. + * @since Ant 1.6.2 + */ + public void setLocalFile(final String aFromUri) { + setFromUri(aFromUri); + this.isFromRemote = false; + } + + /** + * Similar to {@link #setFile setFile} but explicitly states that + * the file is a remote file. + * @param aFromUri a string representing the source of the copy. + * @since Ant 1.6.2 + */ + public void setRemoteFile(final String aFromUri) { + validateRemoteUri("remoteFile", aFromUri); + setFromUri(aFromUri); + this.isFromRemote = true; + } + + /** + * Similar to {@link #setTodir setTodir} but explicitly states + * that the directory is a local. This is the only way to specify + * a local directory with a @ character. + * @param aToUri a string representing the target of the copy. + * @since Ant 1.6.2 + */ + public void setLocalTodir(final String aToUri) { + setToUri(aToUri); + this.isToRemote = false; + } + + /** + * Sets flag to determine if file timestamp from + * remote system is to be preserved during copy. + * @since Ant 1.8.0 + */ + public void setPreservelastmodified(final boolean yesOrNo) { + this.preserveLastModified = yesOrNo; + } + + /** + * Similar to {@link #setTodir setTodir} but explicitly states + * that the directory is a remote. + * @param aToUri a string representing the target of the copy. + * @since Ant 1.6.2 + */ + public void setRemoteTodir(final String aToUri) { + validateRemoteUri("remoteToDir", aToUri); + setToUri(aToUri); + this.isToRemote = true; + } + + private static void validateRemoteUri(final String type, final String aToUri) { + if (!isRemoteUri(aToUri)) { + throw new BuildException(type + " '" + aToUri + "' is invalid. " + + "The 'remoteToDir' attribute must " + + "have syntax like the " + + "following: user:password@host:/path" + + " - the :password part is optional"); + } + } + + /** + * Changes the file name to the given name while receiving it, + * only useful if receiving a single file. + * @param aToUri a string representing the target of the copy. + * @since Ant 1.6.2 + */ + public void setLocalTofile(final String aToUri) { + setToUri(aToUri); + this.isToRemote = false; + } + + /** + * Changes the file name to the given name while sending it, + * only useful if sending a single file. + * @param aToUri a string representing the target of the copy. + * @since Ant 1.6.2 + */ + public void setRemoteTofile(final String aToUri) { + validateRemoteUri("remoteToFile", aToUri); + setToUri(aToUri); + this.isToRemote = true; + } + + /** + * Setting this to true to use sftp protocol. + * + * @param yesOrNo if true sftp protocol will be used. + */ + public void setSftp(final boolean yesOrNo) { + isSftp = yesOrNo; + } + + /** + * Set the file mode, defaults to "644". + * @since Ant 1.9.5 + */ + public void setFileMode(String fileMode) { + this.fileMode = Integer.parseInt(fileMode, 8); + } + + /** + * Set the dir mode, defaults to "755". + * @since Ant 1.9.5 + */ + public void setDirMode(String dirMode) { + this.dirMode = Integer.parseInt(dirMode, 8); + } + + /** + * Adds a FileSet transfer to remote host. NOTE: Either + * addFileSet() or setFile() are required. But, not both. + * + * @param set FileSet to send to remote host. + */ + public void addFileset(final FileSet set) { + if (fileSets == null) { + fileSets = new LinkedList(); + } + fileSets.add(set); + } + + /** + * Initialize this task. + * @throws BuildException on error + */ + @Override + public void init() throws BuildException { + super.init(); + this.toUri = null; + this.fromUri = null; + this.fileSets = null; + } + + /** + * Execute this task. + * @throws BuildException on error + */ + @Override + public void execute() throws BuildException { + if (toUri == null) { + throw exactlyOne(TO_ATTRS); + } + if (fromUri == null && fileSets == null) { + throw exactlyOne(FROM_ATTRS, "one or more nested filesets"); + } + try { + if (isFromRemote && !isToRemote) { + download(fromUri, toUri); + } else if (!isFromRemote && isToRemote) { + if (fileSets != null) { + upload(fileSets, toUri); + } else { + upload(fromUri, toUri); + } + } else if (isFromRemote && isToRemote) { + throw new BuildException( + "Copying from a remote server to a remote server is not supported."); + } else { + throw new BuildException("'todir' and 'file' attributes " + + "must have syntax like the following: " + + "user:password@host:/path"); + } + } catch (final Exception e) { + if (getFailonerror()) { + if(e instanceof BuildException) { + final BuildException be = (BuildException) e; + if(be.getLocation() == null) { + be.setLocation(getLocation()); + } + throw be; + } else { + throw new BuildException(e); + } + } else { + log("Caught exception: " + e.getMessage(), Project.MSG_ERR); + } + } + } + + private void download(final String fromSshUri, final String toPath) + throws JSchException, IOException { + final String file = parseUri(fromSshUri); + + Session session = null; + try { + session = openSession(); + ScpFromMessage message = null; + if (!isSftp) { + message = + new ScpFromMessage(getVerbose(), session, file, + getProject().resolveFile(toPath), + fromSshUri.endsWith("*"), + preserveLastModified); + } else { + message = + new ScpFromMessageBySftp(getVerbose(), session, file, + getProject().resolveFile(toPath), + fromSshUri.endsWith("*"), + preserveLastModified); + } + log("Receiving file: " + file); + message.setLogListener(this); + message.execute(); + } finally { + if (session != null) { + session.disconnect(); + } + } + } + + private void upload(final List fileSet, final String toSshUri) + throws IOException, JSchException { + final String file = parseUri(toSshUri); + + Session session = null; + try { + final List list = new ArrayList(fileSet.size()); + for (final Iterator i = fileSet.iterator(); i.hasNext();) { + final FileSet set = (FileSet) i.next(); + final Directory d = createDirectory(set); + if (d != null) { + list.add(d); + } + } + if (!list.isEmpty()) { + session = openSession(); + ScpToMessage message = null; + if (!isSftp) { + message = new ScpToMessage(getVerbose(), session, + list, file); + } else { + message = new ScpToMessageBySftp(getVerbose(), session, + list, file); + } + message.setLogListener(this); + if (fileMode != null) { + message.setFileMode(fileMode.intValue()); + } + if (dirMode != null) { + message.setDirMode(dirMode.intValue()); + } + message.execute(); + } + } finally { + if (session != null) { + session.disconnect(); + } + } + } + + private void upload(final String fromPath, final String toSshUri) + throws IOException, JSchException { + final String file = parseUri(toSshUri); + + Session session = null; + try { + session = openSession(); + ScpToMessage message = null; + if (!isSftp) { + message = + new ScpToMessage(getVerbose(), session, + getProject().resolveFile(fromPath), file); + } else { + message = + new ScpToMessageBySftp(getVerbose(), session, + getProject().resolveFile(fromPath), + file); + } + message.setLogListener(this); + if (fileMode != null) { + message.setFileMode(fileMode.intValue()); + } + if (dirMode != null) { + message.setDirMode(dirMode.intValue()); + } + message.execute(); + } finally { + if (session != null) { + session.disconnect(); + } + } + } + + private String parseUri(final String uri) { + + int indexOfAt = uri.indexOf('@'); + final int indexOfColon = uri.indexOf(':'); + + if (indexOfColon > -1 && indexOfColon < indexOfAt) { + // user:password@host:/path notation + // everything upto the last @ before the last : is considered + // password. (so if the path contains an @ and a : it will not work) + int indexOfCurrentAt = indexOfAt; + final int indexOfLastColon = uri.lastIndexOf(':'); + while (indexOfCurrentAt > -1 && indexOfCurrentAt < indexOfLastColon) + { + indexOfAt = indexOfCurrentAt; + indexOfCurrentAt = uri.indexOf('@', indexOfCurrentAt + 1); + } + setUsername(uri.substring(0, indexOfColon)); + setPassword(uri.substring(indexOfColon + 1, indexOfAt)); + } else if (indexOfAt > -1) { + // no password, will require keyfile + setUsername(uri.substring(0, indexOfAt)); + } else { + throw new BuildException("no username was given. Can't authenticate."); + } + + if (getUserInfo().getPassword() == null + && getUserInfo().getKeyfile() == null) { + throw new BuildException("neither password nor keyfile for user " + + getUserInfo().getName() + " has been " + + "given. Can't authenticate."); + } + + final int indexOfPath = uri.indexOf(':', indexOfAt + 1); + if (indexOfPath == -1) { + throw new BuildException("no remote path in " + uri); + } + + setHost(uri.substring(indexOfAt + 1, indexOfPath)); + String remotePath = uri.substring(indexOfPath + 1); + if (remotePath.equals("")) { + remotePath = "."; + } + return remotePath; + } + + private static boolean isRemoteUri(final String uri) { + boolean isRemote = true; + final int indexOfAt = uri.indexOf('@'); + if (indexOfAt < 0) { + isRemote = false; + } + return isRemote; + } + + private Directory createDirectory(final FileSet set) { + final DirectoryScanner scanner = set.getDirectoryScanner(getProject()); + Directory root = new Directory(scanner.getBasedir()); + final String[] files = scanner.getIncludedFiles(); + if (files.length != 0) { + for (int j = 0; j < files.length; j++) { + final String[] path = Directory.getPath(files[j]); + Directory current = root; + File currentParent = scanner.getBasedir(); + for (int i = 0; i < path.length; i++) { + final File file = new File(currentParent, path[i]); + if (file.isDirectory()) { + current.addDirectory(new Directory(file)); + current = current.getChild(file); + currentParent = current.getDirectory(); + } else if (file.isFile()) { + current.addFile(file); + } + } + } + } else { + // skip + root = null; + } + return root; + } + + private void setFromUri(final String fromUri) { + if (this.fromUri != null) { + throw exactlyOne(FROM_ATTRS); + } + this.fromUri = fromUri; + } + + private void setToUri(final String toUri) { + if (this.toUri != null) { + throw exactlyOne(TO_ATTRS); + } + this.toUri = toUri; + } + + private BuildException exactlyOne(final String[] attrs) { + return exactlyOne(attrs, null); + } + + private BuildException exactlyOne(final String[] attrs, final String alt) { + final StringBuffer buf = new StringBuffer("Exactly one of ").append( + '[').append(attrs[0]); + for (int i = 1; i < attrs.length; i++) { + buf.append('|').append(attrs[i]); + } + buf.append(']'); + if (alt != null) { + buf.append(" or ").append(alt); + } + return new BuildException(buf.append(" is required.").toString()); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java new file mode 100644 index 00000000..b6d47379 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java @@ -0,0 +1,311 @@ +/* + * 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.tools.ant.taskdefs.optional.ssh; + +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.tools.ant.util.FileUtils; + +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.SftpATTRS; +import com.jcraft.jsch.SftpException; + +/** + * A helper object representing an scp download. + */ +public class ScpFromMessage extends AbstractSshMessage { + + private static final int HUNDRED_KILOBYTES = 102400; + private static final byte LINE_FEED = 0x0a; + private static final int BUFFER_SIZE = 100*1024; + + private String remoteFile; + private File localFile; + private boolean isRecursive = false; + private boolean preserveLastModified = false; + + /** + * Constructor for ScpFromMessage + * @param session the ssh session to use + */ + public ScpFromMessage(final Session session) { + super(session); + } + + /** + * Constructor for ScpFromMessage + * @param verbose if true do verbose logging + * @param session the ssh session to use + * @since Ant 1.7 + */ + public ScpFromMessage(final boolean verbose, final Session session) { + super(verbose, session); + } + + /** + * Constructor for ScpFromMessage. + * @param verbose if true log extra information + * @param session the Scp session to use + * @param aRemoteFile the remote file name + * @param aLocalFile the local file + * @param recursive if true use recursion (-r option to scp) + * @since Ant 1.6.2 + */ + public ScpFromMessage(final boolean verbose, + final Session session, + final String aRemoteFile, + final File aLocalFile, + final boolean recursive) { + this(false, session, aRemoteFile, aLocalFile, recursive, false); + } + + /** + * Constructor for ScpFromMessage. + * @param session the Scp session to use + * @param aRemoteFile the remote file name + * @param aLocalFile the local file + * @param recursive if true use recursion (-r option to scp) + */ + public ScpFromMessage(final Session session, + final String aRemoteFile, + final File aLocalFile, + final boolean recursive) { + this(false, session, aRemoteFile, aLocalFile, recursive); + } + + /** + * Constructor for ScpFromMessage. + * @param verbose if true log extra information + * @param session the Scp session to use + * @param aRemoteFile the remote file name + * @param aLocalFile the local file + * @param recursive if true use recursion (-r option to scp) + * @param preserveLastModified whether to preserve file + * modification times + * @since Ant 1.8.0 + */ + public ScpFromMessage(final boolean verbose, + final Session session, + final String aRemoteFile, + final File aLocalFile, + final boolean recursive, + final boolean preserveLastModified) { + super(verbose, session); + this.remoteFile = aRemoteFile; + this.localFile = aLocalFile; + this.isRecursive = recursive; + this.preserveLastModified = preserveLastModified; + } + + /** + * Carry out the transfer. + * @throws IOException on i/o errors + * @throws JSchException on errors detected by scp + */ + public void execute() throws IOException, JSchException { + String command = "scp -f "; + if (isRecursive) { + command += "-r "; + } + command += remoteFile; + final Channel channel = openExecChannel(command); + try { + // get I/O streams for remote scp + final OutputStream out = channel.getOutputStream(); + final InputStream in = channel.getInputStream(); + + channel.connect(); + + sendAck(out); + startRemoteCpProtocol(in, out, localFile); + } finally { + if (channel != null) { + channel.disconnect(); + } + } + log("done\n"); + } + + protected boolean getPreserveLastModified() { + return preserveLastModified; + } + + private void startRemoteCpProtocol(final InputStream in, + final OutputStream out, + final File localFile) + throws IOException, JSchException { + File startFile = localFile; + while (true) { + // C0644 filesize filename - header for a regular file + // T time 0 time 0\n - present if perserve time. + // D directory - this is the header for a directory. + final ByteArrayOutputStream stream = new ByteArrayOutputStream(); + while (true) { + final int read = in.read(); + if (read < 0) { + return; + } + if ((byte) read == LINE_FEED) { + break; + } + stream.write(read); + } + final String serverResponse = stream.toString("UTF-8"); + if (serverResponse.charAt(0) == 'C') { + parseAndFetchFile(serverResponse, startFile, out, in); + } else if (serverResponse.charAt(0) == 'D') { + startFile = parseAndCreateDirectory(serverResponse, + startFile); + sendAck(out); + } else if (serverResponse.charAt(0) == 'E') { + startFile = startFile.getParentFile(); + sendAck(out); + } else if (serverResponse.charAt(0) == '\01' + || serverResponse.charAt(0) == '\02') { + // this indicates an error. + throw new IOException(serverResponse.substring(1)); + } + } + } + + private File parseAndCreateDirectory(final String serverResponse, + final File localFile) { + int start = serverResponse.indexOf(" "); + // appears that the next token is not used and it's zero. + start = serverResponse.indexOf(" ", start + 1); + final String directoryName = serverResponse.substring(start + 1); + if (localFile.isDirectory()) { + final File dir = new File(localFile, directoryName); + dir.mkdir(); + log("Creating: " + dir); + return dir; + } + return null; + } + + private void parseAndFetchFile(final String serverResponse, + final File localFile, + final OutputStream out, + final InputStream in) + throws IOException, JSchException { + int start = 0; + int end = serverResponse.indexOf(" ", start + 1); + start = end + 1; + end = serverResponse.indexOf(" ", start + 1); + final long filesize = Long.parseLong(serverResponse.substring(start, end)); + final String filename = serverResponse.substring(end + 1); + log("Receiving: " + filename + " : " + filesize); + final File transferFile = (localFile.isDirectory()) + ? new File(localFile, filename) + : localFile; + fetchFile(transferFile, filesize, out, in); + waitForAck(in); + sendAck(out); + } + + private void fetchFile(final File localFile, + long filesize, + final OutputStream out, + final InputStream in) + throws IOException, JSchException { + final byte[] buf = new byte[BUFFER_SIZE]; + sendAck(out); + + // read a content of lfile + final FileOutputStream fos = new FileOutputStream(localFile); + int length; + long totalLength = 0; + final long startTime = System.currentTimeMillis(); + + // only track progress for files larger than 100kb in verbose mode + final boolean trackProgress = getVerbose() && filesize > HUNDRED_KILOBYTES; + // since filesize keeps on decreasing we have to store the + // initial filesize + final long initFilesize = filesize; + int percentTransmitted = 0; + + try { + while (true) { + length = in.read(buf, 0, + (BUFFER_SIZE < filesize) ? BUFFER_SIZE + : (int) filesize); + if (length < 0) { + throw new EOFException("Unexpected end of stream."); + } + fos.write(buf, 0, length); + filesize -= length; + totalLength += length; + if (filesize == 0) { + break; + } + + if (trackProgress) { + percentTransmitted = trackProgress(initFilesize, + totalLength, + percentTransmitted); + } + } + } finally { + final long endTime = System.currentTimeMillis(); + logStats(startTime, endTime, totalLength); + fos.flush(); + fos.close(); + } + + if (getPreserveLastModified()) { + setLastModified(localFile); + } + } + + private void setLastModified(final File localFile) throws JSchException { + SftpATTRS fileAttributes = null; + final ChannelSftp channel = openSftpChannel(); + channel.connect(); + try { + fileAttributes = channel.lstat(remoteDir(remoteFile) + + localFile.getName()); + } catch (final SftpException e) { + throw new JSchException("failed to stat remote file", e); + } + FileUtils.getFileUtils().setFileLastModified(localFile, + ((long) fileAttributes + .getMTime()) + * 1000); + } + + /** + * returns the directory part of the remote file, if any. + */ + private static String remoteDir(final String remoteFile) { + int index = remoteFile.lastIndexOf("/"); + if (index < 0) { + index = remoteFile.lastIndexOf("\\"); + } + return index > -1 ? remoteFile.substring(0, index + 1) : ""; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessageBySftp.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessageBySftp.java new file mode 100644 index 00000000..04f72d23 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessageBySftp.java @@ -0,0 +1,205 @@ +/* + * 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.tools.ant.taskdefs.optional.ssh; + +import java.io.File; +import java.io.IOException; + +import org.apache.tools.ant.util.FileUtils; + +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.SftpATTRS; +import com.jcraft.jsch.SftpException; +import com.jcraft.jsch.SftpProgressMonitor; + +/** + * A helper object representing an scp download. + */ +public class ScpFromMessageBySftp extends ScpFromMessage { + + private static final int HUNDRED_KILOBYTES = 102400; + + private String remoteFile; + private final File localFile; + private boolean isRecursive = false; + private boolean verbose = false; + + /** + * Constructor for ScpFromMessageBySftp. + * @param verbose if true log extra information + * @param session the Scp session to use + * @param aRemoteFile the remote file name + * @param aLocalFile the local file + * @param recursive if true use recursion + * @since Ant 1.7 + */ + public ScpFromMessageBySftp(final boolean verbose, + final Session session, + final String aRemoteFile, + final File aLocalFile, + final boolean recursive) { + this(verbose, session, aRemoteFile, aLocalFile, recursive, false); + } + + /** + * Constructor for ScpFromMessageBySftp. + * @param session the Scp session to use + * @param aRemoteFile the remote file name + * @param aLocalFile the local file + * @param recursive if true use recursion + */ + public ScpFromMessageBySftp(final Session session, + final String aRemoteFile, + final File aLocalFile, + final boolean recursive) { + this(false, session, aRemoteFile, aLocalFile, recursive); + } + + /** + * Constructor for ScpFromMessageBySftp. + * @param verbose if true log extra information + * @param session the Scp session to use + * @param aRemoteFile the remote file name + * @param aLocalFile the local file + * @param recursive if true use recursion + * @param preserveLastModified whether to preserve file + * modification times + * @since Ant 1.8.0 + */ + public ScpFromMessageBySftp(final boolean verbose, + final Session session, + final String aRemoteFile, + final File aLocalFile, + final boolean recursive, + final boolean preserveLastModified) { + super(verbose, session, aRemoteFile, aLocalFile, recursive, + preserveLastModified); + this.verbose = verbose; + this.remoteFile = aRemoteFile; + this.localFile = aLocalFile; + this.isRecursive = recursive; + } + + /** + * Carry out the transfer. + * @throws IOException on i/o errors + * @throws JSchException on errors detected by scp + */ + public void execute() throws IOException, JSchException { + final ChannelSftp channel = openSftpChannel(); + try { + channel.connect(); + try { + final SftpATTRS attrs = channel.stat(remoteFile); + if (attrs.isDir() && !remoteFile.endsWith("/")) { + remoteFile = remoteFile + "/"; + } + } catch (final SftpException ee) { + // Ignored + } + getDir(channel, remoteFile, localFile); + } catch (final SftpException e) { + final JSchException schException = new JSchException("Could not get '"+ remoteFile + +"' to '"+localFile+"' - " + +e.toString()); + schException.initCause(e); + throw schException; + } finally { + if (channel != null) { + channel.disconnect(); + } + } + log("done\n"); + } + + private void getDir(final ChannelSftp channel, + final String remoteFile, + final File localFile) throws IOException, SftpException { + String pwd = remoteFile; + if (remoteFile.lastIndexOf('/') != -1) { + if (remoteFile.length() > 1) { + pwd = remoteFile.substring(0, remoteFile.lastIndexOf('/')); + } + } + channel.cd(pwd); + if (!localFile.exists()) { + localFile.mkdirs(); + } + final java.util.Vector files = channel.ls(remoteFile); + final int size = files.size(); + for (int i = 0; i < size; i++) { + final ChannelSftp.LsEntry le = (ChannelSftp.LsEntry) files.elementAt(i); + final String name = le.getFilename(); + if (le.getAttrs().isDir()) { + if (name.equals(".") || name.equals("..")) { + continue; + } + getDir(channel, + channel.pwd() + "/" + name + "/", + new File(localFile, le.getFilename())); + } else { + getFile(channel, le, localFile); + } + } + channel.cd(".."); + } + + private void getFile(final ChannelSftp channel, + final ChannelSftp.LsEntry le, + File localFile) throws IOException, SftpException { + final String remoteFile = le.getFilename(); + if (!localFile.exists()) { + final String path = localFile.getAbsolutePath(); + final int i = path.lastIndexOf(File.pathSeparator); + if (i != -1) { + if (path.length() > File.pathSeparator.length()) { + new File(path.substring(0, i)).mkdirs(); + } + } + } + + if (localFile.isDirectory()) { + localFile = new File(localFile, remoteFile); + } + + final long startTime = System.currentTimeMillis(); + final long totalLength = le.getAttrs().getSize(); + + SftpProgressMonitor monitor = null; + final boolean trackProgress = getVerbose() && totalLength > HUNDRED_KILOBYTES; + if (trackProgress) { + monitor = getProgressMonitor(); + } + try { + log("Receiving: " + remoteFile + " : " + le.getAttrs().getSize()); + channel.get(remoteFile, localFile.getAbsolutePath(), monitor); + } finally { + final long endTime = System.currentTimeMillis(); + logStats(startTime, endTime, (int) totalLength); + } + if (getPreserveLastModified()) { + FileUtils.getFileUtils().setFileLastModified(localFile, + ((long) le.getAttrs() + .getMTime()) + * 1000); + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessage.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessage.java new file mode 100644 index 00000000..5ba6b559 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessage.java @@ -0,0 +1,331 @@ +/* + * 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.tools.ant.taskdefs.optional.ssh; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Iterator; +import java.util.List; + +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; + +/** + * Utility class to carry out an upload scp transfer. + */ +public class ScpToMessage extends AbstractSshMessage { + + private static final int HUNDRED_KILOBYTES = 102400; + private static final int BUFFER_SIZE = 100*1024; + private static final int DEFAULT_DIR_MODE = 0755; + private static final int DEFAULT_FILE_MODE = 0644; + + private File localFile; + private String remotePath; + private List directoryList; + private Integer fileMode, dirMode; + + /** + * Constructor for ScpToMessage + * @param session the ssh session to use + */ + public ScpToMessage(final Session session) { + super(session); + } + + /** + * Constructor for ScpToMessage + * @param verbose if true do verbose logging + * @param session the ssh session to use + * @since Ant 1.7 + */ + public ScpToMessage(final boolean verbose, final Session session) { + super(verbose, session); + } + + /** + * Constructor for a local file to remote. + * @param verbose if true do verbose logging + * @param session the scp session to use + * @param aLocalFile the local file + * @param aRemotePath the remote path + * @since Ant 1.6.2 + */ + public ScpToMessage(final boolean verbose, + final Session session, + final File aLocalFile, + final String aRemotePath) { + this(verbose, session, aRemotePath); + + this.localFile = aLocalFile; + } + + /** + * Constructor for a local directories to remote. + * @param verbose if true do verbose logging + * @param session the scp session to use + * @param aDirectoryList a list of directories + * @param aRemotePath the remote path + * @since Ant 1.6.2 + */ + public ScpToMessage(final boolean verbose, + final Session session, + final List aDirectoryList, + final String aRemotePath) { + this(verbose, session, aRemotePath); + + this.directoryList = aDirectoryList; + } + + /** + * Constructor for ScpToMessage. + * @param verbose if true do verbose logging + * @param session the scp session to use + * @param aRemotePath the remote path + * @since Ant 1.6.2 + */ + private ScpToMessage(final boolean verbose, + final Session session, + final String aRemotePath) { + super(verbose, session); + this.remotePath = aRemotePath; + } + + /** + * Constructor for ScpToMessage. + * @param session the scp session to use + * @param aLocalFile the local file + * @param aRemotePath the remote path + */ + public ScpToMessage(final Session session, + final File aLocalFile, + final String aRemotePath) { + this(false, session, aLocalFile, aRemotePath); + } + + /** + * Constructor for ScpToMessage. + * @param session the scp session to use + * @param aDirectoryList a list of directories + * @param aRemotePath the remote path + */ + public ScpToMessage(final Session session, + final List aDirectoryList, + final String aRemotePath) { + this(false, session, aDirectoryList, aRemotePath); + } + + /** + * Carry out the transfer. + * @throws IOException on i/o errors + * @throws JSchException on errors detected by scp + */ + @Override + public void execute() throws IOException, JSchException { + if (directoryList != null) { + doMultipleTransfer(); + } + if (localFile != null) { + doSingleTransfer(); + } + log("done.\n"); + } + + private void doSingleTransfer() throws IOException, JSchException { + final String cmd = "scp -t " + remotePath; + final Channel channel = openExecChannel(cmd); + try { + + final OutputStream out = channel.getOutputStream(); + final InputStream in = channel.getInputStream(); + + channel.connect(); + + waitForAck(in); + sendFileToRemote(localFile, in, out); + } finally { + if (channel != null) { + channel.disconnect(); + } + } + } + + private void doMultipleTransfer() throws IOException, JSchException { + final Channel channel = openExecChannel("scp -r -d -t " + remotePath); + try { + final OutputStream out = channel.getOutputStream(); + final InputStream in = channel.getInputStream(); + + channel.connect(); + + waitForAck(in); + for (final Iterator i = directoryList.iterator(); i.hasNext();) { + final Directory current = (Directory) i.next(); + sendDirectory(current, in, out); + } + } finally { + if (channel != null) { + channel.disconnect(); + } + } + } + + private void sendDirectory(final Directory current, + final InputStream in, + final OutputStream out) throws IOException { + for (final Iterator fileIt = current.filesIterator(); fileIt.hasNext();) { + sendFileToRemote((File) fileIt.next(), in, out); + } + for (final Iterator dirIt = current.directoryIterator(); dirIt.hasNext();) { + final Directory dir = (Directory) dirIt.next(); + sendDirectoryToRemote(dir, in, out); + } + } + + private void sendDirectoryToRemote(final Directory directory, + final InputStream in, + final OutputStream out) throws IOException { + String command = "D0"; + command += Integer.toOctalString(getDirMode()); + command += " 0 "; + command += directory.getDirectory().getName(); + command += "\n"; + + out.write(command.getBytes()); + out.flush(); + + waitForAck(in); + sendDirectory(directory, in, out); + out.write("E\n".getBytes()); + out.flush(); + waitForAck(in); + } + + private void sendFileToRemote(final File localFile, + final InputStream in, + final OutputStream out) throws IOException { + // send "C0644 filesize filename", where filename should not include '/' + final long filesize = localFile.length(); + String command = "C0"; + command += Integer.toOctalString(getFileMode()); + command += " " + filesize + " "; + command += localFile.getName(); + command += "\n"; + + out.write(command.getBytes()); + out.flush(); + + waitForAck(in); + + // send a content of lfile + final FileInputStream fis = new FileInputStream(localFile); + final byte[] buf = new byte[BUFFER_SIZE]; + final long startTime = System.currentTimeMillis(); + long totalLength = 0; + + // only track progress for files larger than 100kb in verbose mode + final boolean trackProgress = getVerbose() && filesize > HUNDRED_KILOBYTES; + // since filesize keeps on decreasing we have to store the + // initial filesize + final long initFilesize = filesize; + int percentTransmitted = 0; + + try { + if (this.getVerbose()) { + log("Sending: " + localFile.getName() + " : " + localFile.length()); + } + while (true) { + final int len = fis.read(buf, 0, buf.length); + if (len <= 0) { + break; + } + out.write(buf, 0, len); + totalLength += len; + + if (trackProgress) { + percentTransmitted = trackProgress(initFilesize, + totalLength, + percentTransmitted); + } + } + out.flush(); + sendAck(out); + waitForAck(in); + } finally { + if (this.getVerbose()) { + final long endTime = System.currentTimeMillis(); + logStats(startTime, endTime, totalLength); + } + fis.close(); + } + } + + /** + * Get the local file + * @return the local file + */ + public File getLocalFile() { + return localFile; + } + + /** + * Get the remote path + * @return the remote path + */ + public String getRemotePath() { + return remotePath; + } + + /** + * Set the file mode, defaults to 0644. + * @since Ant 1.9.5 + */ + public void setFileMode(int fileMode) { + this.fileMode = fileMode; + } + + /** + * Get the file mode. + * @since Ant 1.9.5 + */ + public int getFileMode() { + return fileMode != null ? fileMode.intValue() : DEFAULT_FILE_MODE; + } + + /** + * Set the dir mode, defaults to 0755. + * @since Ant 1.9.5 + */ + public void setDirMode(int dirMode) { + this.dirMode = dirMode; + } + + /** + * Get the dir mode. + * @since Ant 1.9.5 + */ + public int getDirMode() { + return dirMode != null ? dirMode.intValue() : DEFAULT_DIR_MODE; + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessageBySftp.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessageBySftp.java new file mode 100644 index 00000000..2b32907d --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessageBySftp.java @@ -0,0 +1,277 @@ +/* + * 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.tools.ant.taskdefs.optional.ssh; + +import java.io.File; +import java.io.IOException; +import java.util.Iterator; +import java.util.List; + +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.SftpException; +import com.jcraft.jsch.SftpProgressMonitor; + +/** + * Utility class to carry out an upload by sftp. + */ +public class ScpToMessageBySftp extends ScpToMessage/*AbstractSshMessage*/ { + + private static final int HUNDRED_KILOBYTES = 102400; + + private File localFile; + private final String remotePath; + private List directoryList; + + /** + * Constructor for a local file to remote. + * @param verbose if true do verbose logging + * @param session the scp session to use + * @param aLocalFile the local file + * @param aRemotePath the remote path + * @since Ant 1.7 + */ + public ScpToMessageBySftp(final boolean verbose, + final Session session, + final File aLocalFile, + final String aRemotePath) { + this(verbose, session, aRemotePath); + + this.localFile = aLocalFile; + } + + /** + * Constructor for a local directories to remote. + * @param verbose if true do verbose logging + * @param session the scp session to use + * @param aDirectoryList a list of directories + * @param aRemotePath the remote path + * @since Ant 1.7 + */ + public ScpToMessageBySftp(final boolean verbose, + final Session session, + final List aDirectoryList, + final String aRemotePath) { + this(verbose, session, aRemotePath); + + this.directoryList = aDirectoryList; + } + + /** + * Constructor for ScpToMessage. + * @param verbose if true do verbose logging + * @param session the scp session to use + * @param aRemotePath the remote path + * @since Ant 1.6.2 + */ + private ScpToMessageBySftp(final boolean verbose, + final Session session, + final String aRemotePath) { + super(verbose, session); + this.remotePath = aRemotePath; + } + + /** + * Constructor for ScpToMessage. + * @param session the scp session to use + * @param aLocalFile the local file + * @param aRemotePath the remote path + */ + public ScpToMessageBySftp(final Session session, + final File aLocalFile, + final String aRemotePath) { + this(false, session, aLocalFile, aRemotePath); + } + + /** + * Constructor for ScpToMessage. + * @param session the scp session to use + * @param aDirectoryList a list of directories + * @param aRemotePath the remote path + */ + public ScpToMessageBySftp(final Session session, + final List aDirectoryList, + final String aRemotePath) { + this(false, session, aDirectoryList, aRemotePath); + } + + /** + * Carry out the transfer. + * @throws IOException on i/o errors + * @throws JSchException on errors detected by scp + */ + @Override + public void execute() throws IOException, JSchException { + if (directoryList != null) { + doMultipleTransfer(); + } + if (localFile != null) { + doSingleTransfer(); + } + log("done.\n"); + } + + private void doSingleTransfer() throws IOException, JSchException { + final ChannelSftp channel = openSftpChannel(); + try { + channel.connect(); + try { + sendFileToRemote(channel, localFile, remotePath); + } catch (final SftpException e) { + final JSchException schException = new JSchException("Could not send '" + localFile + + "' to '" + remotePath + "' - " + + e.toString()); + schException.initCause(e); + throw schException; + } + } finally { + if (channel != null) { + channel.disconnect(); + } + } + } + + private void doMultipleTransfer() throws IOException, JSchException { + final ChannelSftp channel = openSftpChannel(); + try { + channel.connect(); + + try { + try { + channel.stat(remotePath); + } catch (final SftpException e) { + if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) { + // dir does not exist. + channel.mkdir(remotePath); + channel.chmod(getDirMode(), remotePath); + } else { + throw new JSchException("failed to access remote dir '" + + remotePath + "'", e); + } + } + channel.cd(remotePath); + } catch (final SftpException e) { + throw new JSchException("Could not CD to '" + remotePath + + "' - " + e.toString(), e); + } + Directory current = null; + try { + for (final Iterator i = directoryList.iterator(); i.hasNext();) { + current = (Directory) i.next(); + if (getVerbose()) { + log("Sending directory " + current); + } + sendDirectory(channel, current); + } + } catch (final SftpException e) { + String msg = "Error sending directory"; + if (current != null && current.getDirectory() != null) { + msg += " '" + current.getDirectory().getName() + "'"; + } + throw new JSchException(msg, e); + } + } finally { + if (channel != null) { + channel.disconnect(); + } + } + } + + private void sendDirectory(final ChannelSftp channel, + final Directory current) + throws IOException, SftpException { + for (final Iterator fileIt = current.filesIterator(); fileIt.hasNext();) { + sendFileToRemote(channel, (File) fileIt.next(), null); + } + for (final Iterator dirIt = current.directoryIterator(); dirIt.hasNext();) { + final Directory dir = (Directory) dirIt.next(); + sendDirectoryToRemote(channel, dir); + } + } + + private void sendDirectoryToRemote(final ChannelSftp channel, + final Directory directory) + throws IOException, SftpException { + final String dir = directory.getDirectory().getName(); + try { + channel.stat(dir); + } catch (final SftpException e) { + // dir does not exist. + if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) { + channel.mkdir(dir); + channel.chmod(getDirMode(), dir); + } + } + channel.cd(dir); + sendDirectory(channel, directory); + channel.cd(".."); + } + + private void sendFileToRemote(final ChannelSftp channel, + final File localFile, + String remotePath) + throws IOException, SftpException { + final long filesize = localFile.length(); + + if (remotePath == null) { + remotePath = localFile.getName(); + } + + final long startTime = System.currentTimeMillis(); + final long totalLength = filesize; + + // only track progress for files larger than 100kb in verbose mode + final boolean trackProgress = getVerbose() && filesize > HUNDRED_KILOBYTES; + + SftpProgressMonitor monitor = null; + if (trackProgress) { + monitor = getProgressMonitor(); + } + + try { + if (this.getVerbose()) { + log("Sending: " + localFile.getName() + " : " + filesize); + } + channel.put(localFile.getAbsolutePath(), remotePath, monitor); + channel.chmod(getFileMode(), remotePath); + } finally { + if (this.getVerbose()) { + final long endTime = System.currentTimeMillis(); + logStats(startTime, endTime, (int) totalLength); + } + } + } + + /** + * Get the local file. + * @return the local file. + */ + public File getLocalFile() { + return localFile; + } + + /** + * Get the remote path. + * @return the remote path. + */ + public String getRemotePath() { + return remotePath; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/BlockFor.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/BlockFor.java new file mode 100644 index 00000000..4b2978ab --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/BlockFor.java @@ -0,0 +1,71 @@ +/* + * 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.tools.ant.taskdefs.optional.testing; + +import org.apache.tools.ant.taskdefs.WaitFor; + +/** + * @since Ant 1.8 + */ + +public class BlockFor extends WaitFor { + + /** + * Text to include in a message + */ + private String text; + + + /** + * Constructor that takes the name of the task in the task name. + * + */ + public BlockFor() { + super("blockfor"); + text = getTaskName() + " timed out"; + } + + /** + * Constructor that takes the name of the task in the task name. + * + * @param taskName the name of the task. + */ + public BlockFor(String taskName) { + super(taskName); + } + + /** + * If the wait fails, a BuildException is thrown. All the superclasses actions are called first. + * @throws BuildTimeoutException on timeout, using the text in {@link #text} + * + */ + protected void processTimeout() throws BuildTimeoutException { + super.processTimeout(); + throw new BuildTimeoutException(text, getLocation()); + } + + /** + * Set the error text; all properties are expanded in the message. + * + * @param message the text to use in a failure message + */ + public void addText(String message) { + text = getProject().replaceProperties(message); + } + +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/BuildTimeoutException.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/BuildTimeoutException.java new file mode 100644 index 00000000..143d08c9 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/BuildTimeoutException.java @@ -0,0 +1,114 @@ +/* + * 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.tools.ant.taskdefs.optional.testing; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Location; + +/** + * + * This exception is used to indicate timeouts. + * @since Ant1.8 + * + */ + +public class BuildTimeoutException extends BuildException { + + private static final long serialVersionUID = -8057644603246297562L; + + /** + * Constructs a build exception with no descriptive information. + */ + public BuildTimeoutException() { + } + + /** + * Constructs an exception with the given descriptive message. + * + * @param message A description of or information about the exception. + * Should not be <code>null</code>. + */ + public BuildTimeoutException(String message) { + super(message); + } + + /** + * Constructs an exception with the given message and exception as + * a root cause. + * + * @param message A description of or information about the exception. + * Should not be <code>null</code> unless a cause is specified. + * @param cause The exception that might have caused this one. + * May be <code>null</code>. + */ + public BuildTimeoutException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs an exception with the given message and exception as + * a root cause and a location in a file. + * + * @param msg A description of or information about the exception. + * Should not be <code>null</code> unless a cause is specified. + * @param cause The exception that might have caused this one. + * May be <code>null</code>. + * @param location The location in the project file where the error + * occurred. Must not be <code>null</code>. + */ + public BuildTimeoutException(String msg, Throwable cause, Location location) { + super(msg, cause, location); + } + + /** + * Constructs an exception with the given exception as a root cause. + * + * @param cause The exception that might have caused this one. + * Should not be <code>null</code>. + */ + public BuildTimeoutException(Throwable cause) { + super(cause); + } + + /** + * Constructs an exception with the given descriptive message and a + * location in a file. + * + * @param message A description of or information about the exception. + * Should not be <code>null</code>. + * @param location The location in the project file where the error + * occurred. Must not be <code>null</code>. + */ + public BuildTimeoutException(String message, Location location) { + super(message, location); + } + + /** + * Constructs an exception with the given exception as + * a root cause and a location in a file. + * + * @param cause The exception that might have caused this one. + * Should not be <code>null</code>. + * @param location The location in the project file where the error + * occurred. Must not be <code>null</code>. + */ + public BuildTimeoutException(Throwable cause, Location location) { + super(cause, location); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/Funtest.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/Funtest.java new file mode 100644 index 00000000..2eb357a3 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/Funtest.java @@ -0,0 +1,577 @@ +/* + * 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.tools.ant.taskdefs.optional.testing; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.TaskAdapter; +import org.apache.tools.ant.taskdefs.Parallel; +import org.apache.tools.ant.taskdefs.Sequential; +import org.apache.tools.ant.taskdefs.WaitFor; +import org.apache.tools.ant.taskdefs.condition.Condition; +import org.apache.tools.ant.taskdefs.condition.ConditionBase; +import org.apache.tools.ant.util.WorkerAnt; + +/** + * Task to provide functional testing under Ant, with a fairly complex workflow of: + * + * <ul> + * <li>Conditional execution</li> + * <li>Application to start</li> + * <li>A probe to "waitfor" before running tests</li> + * <li>A tests sequence</li> + * <li>A reporting sequence that runs after the tests have finished</li> + * <li>A "teardown" clause that runs after the rest.</li> + * <li>Automated termination of the program it executes, if a timeout is not met</li> + * <li>Checking of a failure property and automatic raising of a fault + * (with the text in failureText) + * if test shutdown and reporting succeeded</li> + * </ul> + * + * The task is designed to be framework neutral; it will work with JUnit, + * TestNG and other test frameworks That can be + * executed from Ant. It bears a resemblance to the FunctionalTest task from + * SmartFrog, as the attribute names were + * chosen to make migration easier. However, this task benefits from the + * ability to tweak Ant's internals, and so + * simplify the workflow, and from the experience of using the SmartFrog task. + * No code has been shared. + * + * @since Ant 1.8 + */ + +public class Funtest extends Task { + + /** + * A condition that must be true before the tests are run. This makes it + * easier to define complex tests that only + * run if certain conditions are met, such as OS or network state. + */ + + private NestedCondition condition; + + + /** + * Used internally to set the workflow up + */ + private Parallel timedTests; + + /** + * Setup runs if the condition is met. Once setup is complete, teardown + * will be run when the task finishes + */ + private Sequential setup; + + /** + * The application to run + */ + private Sequential application; + + /** + * A block that halts the tests until met. + */ + private BlockFor block; + + /** + * Tests to run + */ + private Sequential tests; + + /** + * Reporting only runs if the tests were executed. If the block stopped + * them, reporting is skipped. + */ + private Sequential reporting; + + /** + * Any teardown operations. + */ + private Sequential teardown; + + /** + * time for the tests to time out + */ + private long timeout; + + private long timeoutUnitMultiplier = WaitFor.ONE_MILLISECOND; + + /** + * time for the execution to time out. + */ + private long shutdownTime = 10 * WaitFor.ONE_SECOND; + + private long shutdownUnitMultiplier = WaitFor.ONE_MILLISECOND; + + /** + * Name of a property to look for + */ + private String failureProperty; + + /** + * Message to send when tests failed + */ + private String failureMessage = "Tests failed"; + + /** + * Flag to set to true if you don't care about any shutdown errors. + * <p/> + * In that situation, errors raised during teardown are logged but not + * turned into BuildFault events. Similar to catching and ignoring + * <code>finally {}</code> clauses in Java/ + */ + private boolean failOnTeardownErrors = true; + + + /** + * What was thrown in the test run (including reporting) + */ + private BuildException testException; + /** + * What got thrown during teardown + */ + private BuildException teardownException; + + /** + * Did the application throw an exception + */ + private BuildException applicationException; + + /** + * Did the task throw an exception + */ + private BuildException taskException; + + /** {@value} */ + public static final String WARN_OVERRIDING = "Overriding previous definition of "; + /** {@value} */ + public static final String APPLICATION_FORCIBLY_SHUT_DOWN = "Application forcibly shut down"; + /** {@value} */ + public static final String SHUTDOWN_INTERRUPTED = "Shutdown interrupted"; + /** {@value} */ + public static final String SKIPPING_TESTS + = "Condition failed -skipping tests"; + /** Application exception : {@value} */ + public static final String APPLICATION_EXCEPTION = "Application Exception"; + /** Teardown exception : {@value} */ + public static final String TEARDOWN_EXCEPTION = "Teardown Exception"; + + /** + * Log if the definition is overriding something + * + * @param name what is being defined + * @param definition what should be null if you don't want a warning + */ + private void logOverride(String name, Object definition) { + if (definition != null) { + log(WARN_OVERRIDING + '<' + name + '>', Project.MSG_INFO); + } + } + + /** + * Add a condition element. + * @return <code>ConditionBase</code>. + * @since Ant 1.6.2 + */ + public ConditionBase createCondition() { + logOverride("condition", condition); + condition = new NestedCondition(); + return condition; + } + + /** + * Add an application. + * @param sequence the application to add. + */ + public void addApplication(Sequential sequence) { + logOverride("application", application); + application = sequence; + } + + /** + * Add a setup sequence. + * @param sequence the setup sequence to add. + */ + public void addSetup(Sequential sequence) { + logOverride("setup", setup); + setup = sequence; + } + + /** + * Add a block. + * @param sequence the block for to add. + */ + public void addBlock(BlockFor sequence) { + logOverride("block", block); + block = sequence; + } + + /** + * add tests. + * @param sequence a sequence to add. + */ + public void addTests(Sequential sequence) { + logOverride("tests", tests); + tests = sequence; + } + + /** + * set reporting sequence of tasks. + * @param sequence a reporting sequence to use. + */ + public void addReporting(Sequential sequence) { + logOverride("reporting", reporting); + reporting = sequence; + } + + /** + * set teardown sequence of tasks. + * @param sequence a teardown sequence to use. + */ + public void addTeardown(Sequential sequence) { + logOverride("teardown", teardown); + teardown = sequence; + } + + /** + * Set the failOnTeardownErrors attribute. + * @param failOnTeardownErrors the value to use. + */ + public void setFailOnTeardownErrors(boolean failOnTeardownErrors) { + this.failOnTeardownErrors = failOnTeardownErrors; + } + + /** + * Set the failureMessage attribute. + * @param failureMessage the value to use. + */ + public void setFailureMessage(String failureMessage) { + this.failureMessage = failureMessage; + } + + /** + * Set the failureProperty attribute. + * @param failureProperty the value to use. + */ + public void setFailureProperty(String failureProperty) { + this.failureProperty = failureProperty; + } + + /** + * Set the shutdownTime attribute. + * @param shutdownTime the value to use. + */ + public void setShutdownTime(long shutdownTime) { + this.shutdownTime = shutdownTime; + } + + /** + * Set the timeout attribute. + * @param timeout the value to use. + */ + public void setTimeout(long timeout) { + this.timeout = timeout; + } + + /** + * Set the timeoutunit attribute. + * @param unit the value to use. + */ + public void setTimeoutUnit(WaitFor.Unit unit) { + timeoutUnitMultiplier = unit.getMultiplier(); + } + + /** + * Set the shutdownunit attribute. + * @param unit the value to use. + */ + public void setShutdownUnit(WaitFor.Unit unit) { + shutdownUnitMultiplier = unit.getMultiplier(); + } + + + /** + * Get the application exception. + * @return the application exception. + */ + public BuildException getApplicationException() { + return applicationException; + } + + /** + * Get the teardown exception. + * @return the teardown exception. + */ + public BuildException getTeardownException() { + return teardownException; + } + + /** + * Get the test exception. + * @return the test exception. + */ + public BuildException getTestException() { + return testException; + } + + /** + * Get the task exception. + * @return the task exception. + */ + public BuildException getTaskException() { + return taskException; + } + + /** + * Bind and initialise a task + * @param task task to bind + */ + private void bind(Task task) { + task.bindToOwner(this); + task.init(); + } + + /** + * Create a newly bound parallel instance + * @param parallelTimeout timeout + * @return a bound and initialised parallel instance. + */ + private Parallel newParallel(long parallelTimeout) { + Parallel par = new Parallel(); + bind(par); + par.setFailOnAny(true); + par.setTimeout(parallelTimeout); + return par; + } + + /** + * Create a newly bound parallel instance with one child + * @param parallelTimeout timeout + * @param child task + * @return a bound and initialised parallel instance. + */ + private Parallel newParallel(long parallelTimeout, Task child) { + Parallel par = newParallel(parallelTimeout); + par.addTask(child); + return par; + } + + /** + * Add any task validation needed to ensure internal code quality + * @param task task + * @param role role of the task + */ + private void validateTask(Task task, String role) { + if (task!=null && task.getProject() == null) { + throw new BuildException(role + " task is not bound to the project" + task); + } + } + + /** + * Run the functional test sequence. + * <p> + * This is a fairly complex workflow -what is going on is that we try to clean up + * no matter how the run ended, and to retain the innermost exception that got thrown + * during cleanup. That is, if teardown fails after the tests themselves failed, it is the + * test failing that is more important. + * @throws BuildException if something was caught during the run or teardown. + */ + public void execute() throws BuildException { + + //validation + validateTask(setup, "setup"); + validateTask(application, "application"); + validateTask(tests, "tests"); + validateTask(reporting, "reporting"); + validateTask(teardown, "teardown"); + + //check the condition + //and bail out if it is defined but not true + if (condition != null && !condition.eval()) { + //we are skipping the test + log(SKIPPING_TESTS); + return; + } + + long timeoutMillis = timeout * timeoutUnitMultiplier; + + //set up the application to run in a separate thread + Parallel applicationRun = newParallel(timeoutMillis); + //with a worker which we can use to manage it + WorkerAnt worker = new WorkerAnt(applicationRun, null); + if (application != null) { + applicationRun.addTask(application); + } + + //The test run consists of the block followed by the tests. + long testRunTimeout = 0; + Sequential testRun = new Sequential(); + bind(testRun); + if (block != null) { + //waitfor is not a task, it needs to be adapted + TaskAdapter ta = new TaskAdapter(block); + ta.bindToOwner(this); + validateTask(ta, "block"); + testRun.addTask(ta); + //add the block time to the total test run timeout + testRunTimeout = block.calculateMaxWaitMillis(); + } + + //add the tests and more delay + if (tests != null) { + testRun.addTask(tests); + testRunTimeout += timeoutMillis; + } + //add the reporting and more delay + if (reporting != null) { + testRun.addTask(reporting); + testRunTimeout += timeoutMillis; + } + + //wrap this in a parallel purely to set up timeouts for the + //test run + timedTests = newParallel(testRunTimeout, testRun); + + try { + //run any setup task + if (setup != null) { + Parallel setupRun = newParallel(timeoutMillis, setup); + setupRun.execute(); + } + //start the worker thread and leave it running + worker.start(); + //start the probe+test sequence + timedTests.execute(); + } catch (BuildException e) { + //Record the exception and continue + testException = e; + } finally { + //teardown always runs; its faults are filed away + if (teardown != null) { + try { + Parallel teardownRun = newParallel(timeoutMillis, teardown); + teardownRun.execute(); + } catch (BuildException e) { + teardownException = e; + } + } + } + + //we get here whether or not the tests/teardown have thrown a BuildException. + //do a forced shutdown of the running application, before processing the faults + + try { + //wait for the worker to have finished + long shutdownTimeMillis = shutdownTime * shutdownUnitMultiplier; + worker.waitUntilFinished(shutdownTimeMillis); + if (worker.isAlive()) { + //then, if it is still running, interrupt it a second time. + log(APPLICATION_FORCIBLY_SHUT_DOWN, Project.MSG_WARN); + worker.interrupt(); + worker.waitUntilFinished(shutdownTimeMillis); + } + } catch (InterruptedException e) { + //success, something interrupted the shutdown. There may be a leaked + //worker; + log(SHUTDOWN_INTERRUPTED, e, Project.MSG_VERBOSE); + } + applicationException = worker.getBuildException(); + + //Now faults are analysed + + processExceptions(); + } + + /** + * Now faults are analysed. + * <p> The priority is + * <ol> + * <li>testexceptions, except those indicating a build timeout when the application itself + failed.<br> + (because often it is the application fault that is more interesting than the probe + failure, which is usually triggered by the application not starting + </li><li> + Application exceptions (above test timeout exceptions) + </li><li> + Teardown exceptions -except when they are being ignored + </li><li> + Test failures as indicated by the failure property + </li></ol> + + */ + protected void processExceptions() { + taskException = testException; + + //look for an application fault + if (applicationException != null) { + if (taskException == null || taskException instanceof BuildTimeoutException) { + taskException = applicationException; + } else { + ignoringThrowable(APPLICATION_EXCEPTION, applicationException); + } + } + + //now look for teardown faults, which may be ignored + if (teardownException != null) { + if (taskException == null && failOnTeardownErrors) { + taskException = teardownException; + } else { + //don't let the cleanup exception get in the way of any other failure + ignoringThrowable(TEARDOWN_EXCEPTION, teardownException); + } + } + + //now, analyse the tests + if (failureProperty != null + && getProject().getProperty(failureProperty) != null) { + //we've failed + log(failureMessage); + if (taskException == null) { + taskException = new BuildException(failureMessage); + } + } + + //at this point taskException is null or not. + //if not, throw the exception + if (taskException != null) { + throw taskException; + } + } + + /** + * log that we are ignoring something rather than rethrowing it. + * @param type name of exception + * @param thrown what was thrown + */ + protected void ignoringThrowable(String type, Throwable thrown) { + log(type + ": " + thrown.toString(), + thrown, + Project.MSG_WARN); + } + + private static class NestedCondition extends ConditionBase implements Condition { + public boolean eval() { + if (countConditions() != 1) { + throw new BuildException( + "A single nested condition is required."); + } + return ((Condition) (getConditions().nextElement())).eval(); + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/AbstractAccessTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/AbstractAccessTask.java new file mode 100644 index 00000000..d3385e6c --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/AbstractAccessTask.java @@ -0,0 +1,110 @@ +/* + * 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. + * + */ + +/* + * Since the initial version of this file was deveolped on the clock on + * an NSF grant I should say the following boilerplate: + * + * This material is based upon work supported by the National Science + * Foundaton under Grant No. EIA-0196404. Any opinions, findings, and + * conclusions or recommendations expressed in this material are those + * of the author and do not necessarily reflect the views of the + * National Science Foundation. + */ + +package org.apache.tools.ant.taskdefs.optional.unix; + +import java.io.File; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.condition.Os; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.FileSet; + +/** + * @since Ant 1.6 + * + * @ant.task category="filesystem" + */ + +public abstract class AbstractAccessTask + extends org.apache.tools.ant.taskdefs.ExecuteOn { + + /** + * Chmod task for setting file and directory permissions. + */ + public AbstractAccessTask() { + super.setParallel(true); + super.setSkipEmptyFilesets(true); + } + + /** + * Set the file which should have its access attributes modified. + * @param src the file to modify + */ + public void setFile(File src) { + FileSet fs = new FileSet(); + fs.setFile(src); + addFileset(fs); + } + + /** + * Prevent the user from specifying a different command. + * + * @ant.attribute ignore="true" + * @param cmdl A user supplied command line that we won't accept. + */ + public void setCommand(Commandline cmdl) { + throw new BuildException(getTaskType() + + " doesn\'t support the command attribute", + getLocation()); + } + + /** + * Prevent the skipping of empty filesets + * + * @ant.attribute ignore="true" + * @param skip A user supplied boolean we won't accept. + */ + public void setSkipEmptyFilesets(boolean skip) { + throw new BuildException(getTaskType() + " doesn\'t support the " + + "skipemptyfileset attribute", + getLocation()); + } + + /** + * Prevent the use of the addsourcefile attribute. + * + * @ant.attribute ignore="true" + * @param b A user supplied boolean we won't accept. + */ + public void setAddsourcefile(boolean b) { + throw new BuildException(getTaskType() + + " doesn\'t support the addsourcefile attribute", getLocation()); + } + + /** + * Automatically approve Unix OS's. + * @return true if a valid OS, for unix this is always true, otherwise + * use the superclasses' test (user set). + */ + protected boolean isValidOs() { + return getOs() == null && getOsFamily() == null + ? Os.isFamily(Os.FAMILY_UNIX) : super.isValidOs(); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chgrp.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chgrp.java new file mode 100644 index 00000000..1279a2c8 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chgrp.java @@ -0,0 +1,84 @@ +/* + * 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. + * + */ + +/* + * Since the initial version of this file was deveolped on the clock on + * an NSF grant I should say the following boilerplate: + * + * This material is based upon work supported by the National Science + * Foundaton under Grant No. EIA-0196404. Any opinions, findings, and + * conclusions or recommendations expressed in this material are those + * of the author and do not necessarily reflect the views of the + * National Science Foundation. + */ + +package org.apache.tools.ant.taskdefs.optional.unix; + +import org.apache.tools.ant.BuildException; + +/** + * Chgrp equivalent for unix-like environments. + * + * @since Ant 1.6 + * + * @ant.task category="filesystem" + */ +public class Chgrp extends AbstractAccessTask { + + private boolean haveGroup = false; + + /** + * Chgrp task for setting unix group of a file. + */ + public Chgrp() { + super.setExecutable("chgrp"); + } + + /** + * Set the group attribute. + * + * @param group The new group for the file(s) or directory(ies) + */ + public void setGroup(String group) { + createArg().setValue(group); + haveGroup = true; + } + + /** + * Ensure that all the required arguments and other conditions have + * been set. + */ + protected void checkConfiguration() { + if (!haveGroup) { + throw new BuildException("Required attribute group not set in " + + "chgrp", getLocation()); + } + super.checkConfiguration(); + } + + /** + * We don't want to expose the executable attribute, so override it. + * + * @param e User supplied executable that we won't accept. + */ + public void setExecutable(String e) { + throw new BuildException(getTaskType() + + " doesn\'t support the executable" + + " attribute", getLocation()); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chown.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chown.java new file mode 100644 index 00000000..53f72536 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chown.java @@ -0,0 +1,84 @@ +/* + * 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. + * + */ + +/* + * Since the initial version of this file was deveolped on the clock on + * an NSF grant I should say the following boilerplate: + * + * This material is based upon work supported by the National Science + * Foundaton under Grant No. EIA-0196404. Any opinions, findings, and + * conclusions or recommendations expressed in this material are those + * of the author and do not necessarily reflect the views of the + * National Science Foundation. + */ + +package org.apache.tools.ant.taskdefs.optional.unix; + +import org.apache.tools.ant.BuildException; + +/** + * Chown equivalent for unix-like environments. + * + * @since Ant 1.6 + * + * @ant.task category="filesystem" + */ +public class Chown extends AbstractAccessTask { + + private boolean haveOwner = false; + + /** + * Chown task for setting file and directory permissions. + */ + public Chown() { + super.setExecutable("chown"); + } + + /** + * Set the owner attribute. + * + * @param owner The new owner for the file(s) or directory(ies) + */ + public void setOwner(String owner) { + createArg().setValue(owner); + haveOwner = true; + } + + /** + * Ensure that all the required arguments and other conditions have + * been set. + */ + protected void checkConfiguration() { + if (!haveOwner) { + throw new BuildException("Required attribute owner not set in" + + " chown", getLocation()); + } + super.checkConfiguration(); + } + + /** + * We don't want to expose the executable attribute, so override it. + * + * @param e User supplied executable that we won't accept. + */ + public void setExecutable(String e) { + throw new BuildException(getTaskType() + + " doesn\'t support the executable" + + " attribute", getLocation()); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Symlink.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Symlink.java new file mode 100644 index 00000000..ad9aee8b --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Symlink.java @@ -0,0 +1,600 @@ +/* + * 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. + * + */ + +/* + * Since the initial version of this file was developed on the clock on + * an NSF grant I should say the following boilerplate: + * + * This material is based upon work supported by the National Science + * Foundaton under Grant No. EIA-0196404. Any opinions, findings, and + * conclusions or recommendations expressed in this material are those + * of the author and do not necessarily reflect the views of the + * National Science Foundation. + */ + +package org.apache.tools.ant.taskdefs.optional.unix; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Properties; +import java.util.Vector; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.dispatch.DispatchTask; +import org.apache.tools.ant.dispatch.DispatchUtils; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.taskdefs.LogOutputStream; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.SymbolicLinkUtils; + +/** + * Creates, Deletes, Records and Restores Symlinks. + * + * <p> This task performs several related operations. In the most trivial + * and default usage, it creates a link specified in the link attribute to + * a resource specified in the resource attribute. The second usage of this + * task is to traverse a directory structure specified by a fileset, + * and write a properties file in each included directory describing the + * links found in that directory. The third usage is to traverse a + * directory structure specified by a fileset, looking for properties files + * (also specified as included in the fileset) and recreate the links + * that have been previously recorded for each directory. Finally, it can be + * used to remove a symlink without deleting the associated resource. + * + * <p> Usage examples: + * + * <p> Make a link named "foo" to a resource named + * "bar.foo" in subdir: + * <pre> + * <symlink link="${dir.top}/foo" resource="${dir.top}/subdir/bar.foo"/> + * </pre> + * + * <p> Record all links in subdir and its descendants in files named + * "dir.links": + * <pre> + * <symlink action="record" linkfilename="dir.links"> + * <fileset dir="${dir.top}" includes="subdir/**" /> + * </symlink> + * </pre> + * + * <p> Recreate the links recorded in the previous example: + * <pre> + * <symlink action="recreate"> + * <fileset dir="${dir.top}" includes="subdir/**/dir.links" /> + * </symlink> + * </pre> + * + * <p> Delete a link named "foo" to a resource named + * "bar.foo" in subdir: + * <pre> + * <symlink action="delete" link="${dir.top}/foo"/> + * </pre> + * + * <p><strong>LIMITATIONS:</strong> Because Java has no direct support for + * handling symlinks this task divines them by comparing canonical and + * absolute paths. On non-unix systems this may cause false positives. + * Furthermore, any operating system on which the command + * <code>ln -s link resource</code> is not a valid command on the command line + * will not be able to use action="delete", action="single" + * or action="recreate", but action="record" should still + * work. Finally, the lack of support for symlinks in Java means that all links + * are recorded as links to the <strong>canonical</strong> resource name. + * Therefore the link: <code>link --> subdir/dir/../foo.bar</code> will be + * recorded as <code>link=subdir/foo.bar</code> and restored as + * <code>link --> subdir/foo.bar</code>. + * + */ +public class Symlink extends DispatchTask { + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + private static final SymbolicLinkUtils SYMLINK_UTILS = + SymbolicLinkUtils.getSymbolicLinkUtils(); + + private String resource; + private String link; + private Vector fileSets = new Vector(); + private String linkFileName; + private boolean overwrite; + private boolean failonerror; + private boolean executing = false; + + /** + * Initialize the task. + * @throws BuildException on error. + */ + @Override + public void init() throws BuildException { + super.init(); + setDefaults(); + } + + /** + * The standard method for executing any task. + * @throws BuildException on error. + */ + @Override + public synchronized void execute() throws BuildException { + if (executing) { + throw new BuildException( + "Infinite recursion detected in Symlink.execute()"); + } + try { + executing = true; + DispatchUtils.execute(this); + } finally { + executing = false; + } + } + + /** + * Create a symlink. + * @throws BuildException on error. + * @since Ant 1.7 + */ + public void single() throws BuildException { + try { + if (resource == null) { + handleError("Must define the resource to symlink to!"); + return; + } + if (link == null) { + handleError("Must define the link name for symlink!"); + return; + } + doLink(resource, link); + } finally { + setDefaults(); + } + } + + /** + * Delete a symlink. + * @throws BuildException on error. + * @since Ant 1.7 + */ + public void delete() throws BuildException { + try { + if (link == null) { + handleError("Must define the link name for symlink!"); + return; + } + log("Removing symlink: " + link); + SYMLINK_UTILS.deleteSymbolicLink(FILE_UTILS + .resolveFile(new File("."), link), + this); + } catch (FileNotFoundException fnfe) { + handleError(fnfe.toString()); + } catch (IOException ioe) { + handleError(ioe.toString()); + } finally { + setDefaults(); + } + } + + /** + * Restore symlinks. + * @throws BuildException on error. + * @since Ant 1.7 + */ + public void recreate() throws BuildException { + try { + if (fileSets.isEmpty()) { + handleError("File set identifying link file(s) " + + "required for action recreate"); + return; + } + Properties links = loadLinks(fileSets); + + for (Iterator kitr = links.keySet().iterator(); kitr.hasNext();) { + String lnk = (String) kitr.next(); + String res = links.getProperty(lnk); + // handle the case where lnk points to a directory (bug 25181) + try { + File test = new File(lnk); + if (!SYMLINK_UTILS.isSymbolicLink(lnk)) { + doLink(res, lnk); + } else if (!test.getCanonicalPath().equals( + new File(res).getCanonicalPath())) { + SYMLINK_UTILS.deleteSymbolicLink(test, this); + doLink(res, lnk); + } // else lnk exists, do nothing + } catch (IOException ioe) { + handleError("IO exception while creating link"); + } + } + } finally { + setDefaults(); + } + } + + /** + * Record symlinks. + * @throws BuildException on error. + * @since Ant 1.7 + */ + public void record() throws BuildException { + try { + if (fileSets.isEmpty()) { + handleError("Fileset identifying links to record required"); + return; + } + if (linkFileName == null) { + handleError("Name of file to record links in required"); + return; + } + // create a hashtable to group them by parent directory: + Hashtable byDir = new Hashtable(); + + // get an Iterator of file objects representing links (canonical): + for (Iterator litr = findLinks(fileSets).iterator(); + litr.hasNext();) { + File thisLink = (File) litr.next(); + File parent = thisLink.getParentFile(); + Vector v = (Vector) byDir.get(parent); + if (v == null) { + v = new Vector(); + byDir.put(parent, v); + } + v.addElement(thisLink); + } + // write a Properties file in each directory: + for (Iterator dirs = byDir.keySet().iterator(); dirs.hasNext();) { + File dir = (File) dirs.next(); + Vector linksInDir = (Vector) byDir.get(dir); + Properties linksToStore = new Properties(); + + // fill up a Properties object with link and resource names: + for (Iterator dlnk = linksInDir.iterator(); dlnk.hasNext();) { + File lnk = (File) dlnk.next(); + try { + linksToStore.put(lnk.getName(), lnk.getCanonicalPath()); + } catch (IOException ioe) { + handleError("Couldn't get canonical name of parent link"); + } + } + writePropertyFile(linksToStore, dir); + } + } finally { + setDefaults(); + } + } + + /** + * Return all variables to their default state for the next invocation. + * @since Ant 1.7 + */ + private void setDefaults() { + resource = null; + link = null; + linkFileName = null; + failonerror = true; // default behavior is to fail on an error + overwrite = false; // default behavior is to not overwrite + setAction("single"); // default behavior is make a single link + fileSets.clear(); + } + + /** + * Set overwrite mode. If set to false (default) + * the task will not overwrite existing links, and may stop the build + * if a link already exists depending on the setting of failonerror. + * + * @param owrite If true overwrite existing links. + */ + public void setOverwrite(boolean owrite) { + this.overwrite = owrite; + } + + /** + * Set failonerror mode. If set to true (default) the entire build fails + * upon error; otherwise the error is logged and the build will continue. + * + * @param foe If true throw BuildException on error, else log it. + */ + public void setFailOnError(boolean foe) { + this.failonerror = foe; + } + + /** + * Set the action to be performed. May be "single", + * "delete", "recreate" or "record". + * + * @param action The action to perform. + */ + @Override + public void setAction(String action) { + super.setAction(action); + } + + /** + * Set the name of the link. Used when action = "single". + * + * @param lnk The name for the link. + */ + public void setLink(String lnk) { + this.link = lnk; + } + + /** + * Set the name of the resource to which a link should be created. + * Used when action = "single". + * + * @param src The resource to be linked. + */ + public void setResource(String src) { + this.resource = src; + } + + /** + * Set the name of the file to which links will be written. + * Used when action = "record". + * + * @param lf The name of the file to write links to. + */ + public void setLinkfilename(String lf) { + this.linkFileName = lf; + } + + /** + * Add a fileset to this task. + * + * @param set The fileset to add. + */ + public void addFileset(FileSet set) { + fileSets.addElement(set); + } + + /** + * Delete a symlink (without deleting the associated resource). + * + * <p>This is a convenience method that simply invokes + * <code>deleteSymlink(java.io.File)</code>. + * + * @param path A string containing the path of the symlink to delete. + * + * @throws FileNotFoundException When the path results in a + * <code>File</code> that doesn't exist. + * @throws IOException If calls to <code>File.rename</code> + * or <code>File.delete</code> fail. + * @deprecated use + * org.apache.tools.ant.util.SymbolicLinkUtils#deleteSymbolicLink + * instead + */ + @Deprecated + public static void deleteSymlink(String path) + throws IOException, FileNotFoundException { + SYMLINK_UTILS.deleteSymbolicLink(new File(path), null); + } + + /** + * Delete a symlink (without deleting the associated resource). + * + * <p>This is a utility method that removes a unix symlink without removing + * the resource that the symlink points to. If it is accidentally invoked + * on a real file, the real file will not be harmed.</p> + * + * <p>This method works by + * getting the canonical path of the link, using the canonical path to + * rename the resource (breaking the link) and then deleting the link. + * The resource is then returned to its original name inside a finally + * block to ensure that the resource is unharmed even in the event of + * an exception.</p> + * + * <p>Since Ant 1.8.0 this method will try to delete the File object if + * it reports it wouldn't exist (as symlinks pointing nowhere usually do). + * Prior version would throw a FileNotFoundException in that case.</p> + * + * @param linkfil A <code>File</code> object of the symlink to delete. + * + * @throws IOException If calls to <code>File.rename</code>, + * <code>File.delete</code> or + * <code>File.getCanonicalPath</code> + * fail. + * @deprecated use + * org.apache.tools.ant.util.SymbolicLinkUtils#deleteSymbolicLink + * instead + */ + @Deprecated + public static void deleteSymlink(File linkfil) + throws IOException { + SYMLINK_UTILS.deleteSymbolicLink(linkfil, null); + } + + /** + * Write a properties file. This method uses <code>Properties.store</code> + * and thus may throw exceptions that occur while writing the file. + * + * @param properties The properties object to be written. + * @param dir The directory for which we are writing the links. + * @throws BuildException if the property file could not be written + */ + private void writePropertyFile(Properties properties, File dir) + throws BuildException { + BufferedOutputStream bos = null; + try { + bos = new BufferedOutputStream( + new FileOutputStream(new File(dir, linkFileName))); + properties.store(bos, "Symlinks from " + dir); + } catch (IOException ioe) { + throw new BuildException(ioe, getLocation()); + } finally { + FileUtils.close(bos); + } + } + + /** + * Handle errors based on the setting of failonerror. + * + * @param msg The message to log, or include in the + * <code>BuildException</code>. + * @throws BuildException with the message if failonerror=true + */ + private void handleError(String msg) { + if (failonerror) { + throw new BuildException(msg); + } + log(msg); + } + + /** + * Conduct the actual construction of a link. + * + * <p> The link is constructed by calling <code>Execute.runCommand</code>. + * + * @param res The path of the resource we are linking to. + * @param lnk The name of the link we wish to make. + * @throws BuildException when things go wrong + */ + private void doLink(String res, String lnk) throws BuildException { + File linkfil = new File(lnk); + String options = "-s"; + if (overwrite) { + options += "f"; + if (linkfil.exists()) { + try { + SYMLINK_UTILS.deleteSymbolicLink(linkfil, this); + } catch (FileNotFoundException fnfe) { + log("Symlink disappeared before it was deleted: " + lnk); + } catch (IOException ioe) { + log("Unable to overwrite preexisting link or file: " + lnk, + ioe, Project.MSG_INFO); + } + } + } + String[] cmd = new String[] {"ln", options, res, lnk}; + try { + Execute.runCommand(this, cmd); + } catch (BuildException failedToExecute) { + if (failonerror) { + throw failedToExecute; + } else { + //log at the info level, and keep going. + log(failedToExecute.getMessage(), failedToExecute, Project.MSG_INFO); + } + } + } + + /** + * Find all the links in all supplied filesets. + * + * <p> This method is invoked when the action attribute is + * "record". This means that filesets are interpreted + * as the directories in which links may be found. + * + * @param v The filesets specified by the user. + * @return A HashSet of <code>File</code> objects containing the + * links (with canonical parent directories). + */ + private HashSet findLinks(Vector v) { + HashSet result = new HashSet(); + final int size = v.size(); + for (int i = 0; i < size; i++) { + FileSet fs = (FileSet) v.get(i); + DirectoryScanner ds = fs.getDirectoryScanner(getProject()); + String[][] fnd = new String[][] + {ds.getIncludedFiles(), ds.getIncludedDirectories()}; + File dir = fs.getDir(getProject()); + for (int j = 0; j < fnd.length; j++) { + for (int k = 0; k < fnd[j].length; k++) { + try { + File f = new File(dir, fnd[j][k]); + File pf = f.getParentFile(); + String name = f.getName(); + if (SYMLINK_UTILS.isSymbolicLink(pf, name)) { + result.add(new File(pf.getCanonicalFile(), name)); + } + } catch (IOException e) { + handleError("IOException: " + fnd[j][k] + " omitted"); + } + } + } + } + return result; + } + + /** + * Load links from properties files included in one or more FileSets. + * + * <p> This method is only invoked when the action attribute is set to + * "recreate". The filesets passed in are assumed to specify the + * names of the property files with the link information and the + * subdirectories in which to look for them. + * + * @param v The <code>FileSet</code>s for this task. + * @return The links to be made. + */ + private Properties loadLinks(Vector v) { + Properties finalList = new Properties(); + // loop through the supplied file sets: + final int size = v.size(); + for (int i = 0; i < size; i++) { + FileSet fs = (FileSet) v.elementAt(i); + DirectoryScanner ds = new DirectoryScanner(); + fs.setupDirectoryScanner(ds, getProject()); + ds.setFollowSymlinks(false); + ds.scan(); + String[] incs = ds.getIncludedFiles(); + File dir = fs.getDir(getProject()); + + // load included files as properties files: + for (int j = 0; j < incs.length; j++) { + File inc = new File(dir, incs[j]); + File pf = inc.getParentFile(); + Properties lnks = new Properties(); + InputStream is = null; + try { + is = new BufferedInputStream(new FileInputStream(inc)); + lnks.load(is); + pf = pf.getCanonicalFile(); + } catch (FileNotFoundException fnfe) { + handleError("Unable to find " + incs[j] + "; skipping it."); + continue; + } catch (IOException ioe) { + handleError("Unable to open " + incs[j] + + " or its parent dir; skipping it."); + continue; + } finally { + FileUtils.close(is); + } + lnks.list(new PrintStream( + new LogOutputStream(this, Project.MSG_INFO))); + // Write the contents to our master list of links + // This method assumes that all links are defined in + // terms of absolute paths, or paths relative to the + // working directory: + for (Iterator kitr = lnks.keySet().iterator(); kitr.hasNext();) { + String key = (String) kitr.next(); + finalList.put(new File(pf, key).getAbsolutePath(), + lnks.getProperty(key)); + } + } + } + return finalList; + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSS.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSS.java new file mode 100644 index 00000000..f514fc62 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSS.java @@ -0,0 +1,784 @@ +/* + * 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.tools.ant.taskdefs.optional.vss; + +import java.io.File; +import java.io.IOException; +import java.text.DateFormat; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +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.LogStreamHandler; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.EnumeratedAttribute; +import org.apache.tools.ant.util.FileUtils; + +/** + * A base class for creating tasks for executing commands on Visual SourceSafe. + * <p> + * The class extends the 'exec' task as it operates by executing the ss.exe program + * supplied with SourceSafe. By default the task expects ss.exe to be in the path, + * you can override this be specifying the ssdir attribute. + * </p> + * <p> + * This class provides set and get methods for 'login' and 'vsspath' attributes. It + * also contains constants for the flags that can be passed to SS. + * </p> + * + */ +public abstract class MSVSS extends Task implements MSVSSConstants { + + private String ssDir = null; + private String vssLogin = null; + private String vssPath = null; + private String serverPath = null; + + /** Version */ + private String version = null; + /** Date */ + private String date = null; + /** Label */ + private String label = null; + /** Auto response */ + private String autoResponse = null; + /** Local path */ + private String localPath = null; + /** Comment */ + private String comment = null; + /** From label */ + private String fromLabel = null; + /** To label */ + private String toLabel = null; + /** Output file name */ + private String outputFileName = null; + /** User */ + private String user = null; + /** From date */ + private String fromDate = null; + /** To date */ + private String toDate = null; + /** History style */ + private String style = null; + /** Quiet defaults to false */ + private boolean quiet = false; + /** Recursive defaults to false */ + private boolean recursive = false; + /** Writable defaults to false */ + private boolean writable = false; + /** Fail on error defaults to true */ + private boolean failOnError = true; + /** Get local copy for checkout defaults to true */ + private boolean getLocalCopy = true; + /** Number of days offset for History */ + private int numDays = Integer.MIN_VALUE; + /** Date format for History */ + private DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT); + /** Timestamp for retreived files */ + private CurrentModUpdated timestamp = null; + /** Behaviour for writable files */ + private WritableFiles writableFiles = null; + + /** + * Each sub-class must implemnt this method and return the constructed + * command line to be executed. It is up to the sub-task to determine the + * required attrubutes and their order. + * @return The Constructed command line. + */ + abstract Commandline buildCmdLine(); + + /** + * Directory where <code>ss.exe</code> resides. + * By default the task expects it to be in the PATH. + * @param dir The directory containing ss.exe. + */ + public final void setSsdir(String dir) { + this.ssDir = FileUtils.translatePath(dir); + } + + /** + * Login to use when accessing VSS, formatted as "username,password". + * <p> + * You can omit the password if your database is not password protected. + * If you have a password and omit it, Ant will hang. + * @param vssLogin The login string to use. + */ + public final void setLogin(final String vssLogin) { + this.vssLogin = vssLogin; + } + + /** + * SourceSafe path which specifies the project/file(s) you wish to perform + * the action on. + * <p> + * A prefix of 'vss://' will be removed if specified. + * @param vssPath The VSS project path. + * @ant.attribute group="required" + */ + public final void setVsspath(final String vssPath) { + String projectPath; + // CheckStyle:MagicNumber OFF + if (vssPath.startsWith("vss://")) { //$NON-NLS-1$ + projectPath = vssPath.substring(5); + } else { + projectPath = vssPath; + } + // CheckStyle:MagicNumber ON + + if (projectPath.startsWith(PROJECT_PREFIX)) { + this.vssPath = projectPath; + } else { + this.vssPath = PROJECT_PREFIX + projectPath; + } + } + + /** + * Directory where <code>srssafe.ini</code> resides. + * @param serverPath The path to the VSS server. + */ + public final void setServerpath(final String serverPath) { + this.serverPath = serverPath; + } + + /** + * Indicates if the build should fail if the Sourcesafe command does. Defaults to true. + * @param failOnError True if task should fail on any error. + */ + public final void setFailOnError(final boolean failOnError) { + this.failOnError = failOnError; + } + + /** + * Executes the task. <br> + * Builds a command line to execute ss.exe and then calls Exec's run method + * to execute the command line. + * @throws BuildException if the command cannot execute. + */ + public void execute() throws BuildException { + int result = 0; + Commandline commandLine = buildCmdLine(); + result = run(commandLine); + if (Execute.isFailure(result) && getFailOnError()) { + String msg = "Failed executing: " + formatCommandLine(commandLine) + + " With a return code of " + result; + throw new BuildException(msg, getLocation()); + } + } + + // Special setters for the sub-classes + + /** + * Set the internal comment attribute. + * @param comment the value to use. + */ + protected void setInternalComment(final String comment) { + this.comment = comment; + } + + /** + * Set the auto response attribute. + * @param autoResponse the value to use. + */ + protected void setInternalAutoResponse(final String autoResponse) { + this.autoResponse = autoResponse; + } + + /** + * Set the date attribute. + * @param date the value to use. + */ + protected void setInternalDate(final String date) { + this.date = date; + } + + /** + * Set the date format attribute. + * @param dateFormat the value to use. + */ + protected void setInternalDateFormat(final DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + /** + * Set the failOnError attribute. + * @param failOnError the value to use. + */ + protected void setInternalFailOnError(final boolean failOnError) { + this.failOnError = failOnError; + } + + /** + * Set the from date attribute. + * @param fromDate the value to use. + */ + protected void setInternalFromDate(final String fromDate) { + this.fromDate = fromDate; + } + + /** + * Set the from label attribute. + * @param fromLabel the value to use. + */ + protected void setInternalFromLabel(final String fromLabel) { + this.fromLabel = fromLabel; + } + + /** + * Set the label attribute. + * @param label the value to use. + */ + protected void setInternalLabel(final String label) { + this.label = label; + } + + /** + * Set the local path comment attribute. + * @param localPath the value to use. + */ + protected void setInternalLocalPath(final String localPath) { + this.localPath = localPath; + } + + /** + * Set the num days attribute. + * @param numDays the value to use. + */ + protected void setInternalNumDays(final int numDays) { + this.numDays = numDays; + } + + /** + * Set the outputFileName comment attribute. + * @param outputFileName the value to use. + */ + protected void setInternalOutputFilename(final String outputFileName) { + this.outputFileName = outputFileName; + } + + /** + * Set the quiet attribute. + * @param quiet the value to use. + */ + protected void setInternalQuiet(final boolean quiet) { + this.quiet = quiet; + } + + /** + * Set the recursive attribute. + * @param recursive the value to use. + */ + protected void setInternalRecursive(final boolean recursive) { + this.recursive = recursive; + } + + /** + * Set the style attribute. + * @param style the value to use. + */ + protected void setInternalStyle(final String style) { + this.style = style; + } + + /** + * Set the to date attribute. + * @param toDate the value to use. + */ + protected void setInternalToDate(final String toDate) { + this.toDate = toDate; + } + + /** + * Set the to label attribute. + * @param toLabel the value to use. + */ + protected void setInternalToLabel(final String toLabel) { + this.toLabel = toLabel; + } + + /** + * Set the user attribute. + * @param user the value to use. + */ + protected void setInternalUser(final String user) { + this.user = user; + } + + /** + * Set the version attribute. + * @param version the value to use. + */ + protected void setInternalVersion(final String version) { + this.version = version; + } + + /** + * Set the writable attribute. + * @param writable the value to use. + */ + protected void setInternalWritable(final boolean writable) { + this.writable = writable; + } + + /** + * Set the timestamp attribute. + * @param timestamp the value to use. + */ + protected void setInternalFileTimeStamp(final CurrentModUpdated timestamp) { + this.timestamp = timestamp; + } + + /** + * Set the writableFiles attribute. + * @param writableFiles the value to use. + */ + protected void setInternalWritableFiles(final WritableFiles writableFiles) { + this.writableFiles = writableFiles; + } + + /** + * Set the getLocalCopy attribute. + * @param getLocalCopy the value to use. + */ + protected void setInternalGetLocalCopy(final boolean getLocalCopy) { + this.getLocalCopy = getLocalCopy; + } + + /** + * Gets the sscommand string. "ss" or "c:\path\to\ss" + * @return The path to ss.exe or just ss if sscommand is not set. + */ + protected String getSSCommand() { + if (ssDir == null) { + return SS_EXE; + } + return ssDir.endsWith(File.separator) ? ssDir + SS_EXE : ssDir + + File.separator + SS_EXE; + } + + /** + * Gets the vssserverpath string. + * @return null if vssserverpath is not set. + */ + protected String getVsspath() { + return vssPath; + } + + /** + * Gets the quiet string. -O- + * @return An empty string if quiet is not set or is false. + */ + protected String getQuiet() { + return quiet ? FLAG_QUIET : ""; + } + + /** + * Gets the recursive string. "-R" + * @return An empty string if recursive is not set or is false. + */ + protected String getRecursive() { + return recursive ? FLAG_RECURSION : ""; + } + + /** + * Gets the writable string. "-W" + * @return An empty string if writable is not set or is false. + */ + protected String getWritable() { + return writable ? FLAG_WRITABLE : ""; + } + + /** + * Gets the label string. "-Lbuild1" + * Max label length is 32 chars + * @return An empty string if label is not set. + */ + protected String getLabel() { + String shortLabel = ""; + if (label != null && label.length() > 0) { + shortLabel = FLAG_LABEL + getShortLabel(); + } + return shortLabel; + } + /** + * Return at most the 30 first chars of the label, + * logging a warning message about the truncation + * @return at most the 30 first chars of the label + */ + private String getShortLabel() { + String shortLabel; + // CheckStyle:MagicNumber OFF + if (label != null && label.length() > 31) { + shortLabel = this.label.substring(0, 30); + log("Label is longer than 31 characters, truncated to: " + shortLabel, + Project.MSG_WARN); + } else { + shortLabel = label; + } + // CheckStyle:MagicNumber ON + return shortLabel; + } + /** + * Gets the style string. "-Lbuild1" + * @return An empty string if label is not set. + */ + protected String getStyle() { + return style != null ? style : ""; + } + + /** + * Gets the version string. Returns the first specified of version "-V1.0", + * date "-Vd01.01.01", label "-Vlbuild1". + * @return An empty string if a version, date and label are not set. + */ + protected String getVersionDateLabel() { + String versionDateLabel = ""; + if (version != null) { + versionDateLabel = FLAG_VERSION + version; + } else if (date != null) { + versionDateLabel = FLAG_VERSION_DATE + date; + } else { + // Use getShortLabel() so labels longer then 30 char are truncated + // and the user is warned + String shortLabel = getShortLabel(); + if (shortLabel != null && !shortLabel.equals("")) { + versionDateLabel = FLAG_VERSION_LABEL + shortLabel; + } + } + return versionDateLabel; + } + + /** + * Gets the version string. + * @return An empty string if a version is not set. + */ + protected String getVersion() { + return version != null ? FLAG_VERSION + version : ""; + } + + /** + * Gets the localpath string. "-GLc:\source" <p> + * The localpath is created if it didn't exist. + * @return An empty string if localpath is not set. + */ + protected String getLocalpath() { + String lclPath = ""; //set to empty str if no local path return + if (localPath != null) { + //make sure m_LocalDir exists, create it if it doesn't + File dir = getProject().resolveFile(localPath); + if (!dir.exists()) { + boolean done = dir.mkdirs(); + if (!done) { + String msg = "Directory " + localPath + " creation was not " + + "successful for an unknown reason"; + throw new BuildException(msg, getLocation()); + } + getProject().log("Created dir: " + dir.getAbsolutePath()); + } + lclPath = FLAG_OVERRIDE_WORKING_DIR + localPath; + } + return lclPath; + } + + /** + * Gets the comment string. "-Ccomment text" + * @return A comment of "-" if comment is not set. + */ + protected String getComment() { + return comment != null ? FLAG_COMMENT + comment : FLAG_COMMENT + "-"; + } + + /** + * Gets the auto response string. This can be Y "-I-Y" or N "-I-N". + * @return The default value "-I-" if autoresponse is not set. + */ + protected String getAutoresponse() { + if (autoResponse == null) { + return FLAG_AUTORESPONSE_DEF; + } + if (autoResponse.equalsIgnoreCase("Y")) { + return FLAG_AUTORESPONSE_YES; + } else if (autoResponse.equalsIgnoreCase("N")) { + return FLAG_AUTORESPONSE_NO; + } else { + return FLAG_AUTORESPONSE_DEF; + } + } + + /** + * Gets the login string. This can be user and password, "-Yuser,password" + * or just user "-Yuser". + * @return An empty string if login is not set. + */ + protected String getLogin() { + return vssLogin != null ? FLAG_LOGIN + vssLogin : ""; + } + + /** + * Gets the output file string. "-Ooutput.file" + * @return An empty string if user is not set. + */ + protected String getOutput() { + return outputFileName != null ? FLAG_OUTPUT + outputFileName : ""; + } + + /** + * Gets the user string. "-Uusername" + * @return An empty string if user is not set. + */ + protected String getUser() { + return user != null ? FLAG_USER + user : ""; + } + + /** + * Gets the version string. This can be to-from "-VLbuild2~Lbuild1", from + * "~Lbuild1" or to "-VLbuild2". + * @return An empty string if neither tolabel or fromlabel are set. + */ + protected String getVersionLabel() { + if (fromLabel == null && toLabel == null) { + return ""; + } + // CheckStyle:MagicNumber OFF + if (fromLabel != null && toLabel != null) { + if (fromLabel.length() > 31) { + fromLabel = fromLabel.substring(0, 30); + log("FromLabel is longer than 31 characters, truncated to: " + + fromLabel, Project.MSG_WARN); + } + if (toLabel.length() > 31) { + toLabel = toLabel.substring(0, 30); + log("ToLabel is longer than 31 characters, truncated to: " + + toLabel, Project.MSG_WARN); + } + return FLAG_VERSION_LABEL + toLabel + VALUE_FROMLABEL + fromLabel; + } else if (fromLabel != null) { + if (fromLabel.length() > 31) { + fromLabel = fromLabel.substring(0, 30); + log("FromLabel is longer than 31 characters, truncated to: " + + fromLabel, Project.MSG_WARN); + } + return FLAG_VERSION + VALUE_FROMLABEL + fromLabel; + } else { + if (toLabel.length() > 31) { + toLabel = toLabel.substring(0, 30); + log("ToLabel is longer than 31 characters, truncated to: " + + toLabel, Project.MSG_WARN); + } + return FLAG_VERSION_LABEL + toLabel; + } + // CheckStyle:MagicNumber ON + } + + /** + * Gets the Version date string. + * @return An empty string if neither Todate or from date are set. + * @throws BuildException if there is an error. + */ + protected String getVersionDate() throws BuildException { + if (fromDate == null && toDate == null + && numDays == Integer.MIN_VALUE) { + return ""; + } + if (fromDate != null && toDate != null) { + return FLAG_VERSION_DATE + toDate + VALUE_FROMDATE + fromDate; + } else if (toDate != null && numDays != Integer.MIN_VALUE) { + try { + return FLAG_VERSION_DATE + toDate + VALUE_FROMDATE + + calcDate(toDate, numDays); + } catch (ParseException ex) { + String msg = "Error parsing date: " + toDate; + throw new BuildException(msg, getLocation()); + } + } else if (fromDate != null && numDays != Integer.MIN_VALUE) { + try { + return FLAG_VERSION_DATE + calcDate(fromDate, numDays) + + VALUE_FROMDATE + fromDate; + } catch (ParseException ex) { + String msg = "Error parsing date: " + fromDate; + throw new BuildException(msg, getLocation()); + } + } else { + return fromDate != null ? FLAG_VERSION + VALUE_FROMDATE + + fromDate : FLAG_VERSION_DATE + toDate; + } + } + + /** + * Builds and returns the -G- flag if required. + * @return An empty string if get local copy is true. + */ + protected String getGetLocalCopy() { + return (!getLocalCopy) ? FLAG_NO_GET : ""; + } + + /** + * Gets the value of the fail on error flag. + * @return True if the FailOnError flag has been set or if 'writablefiles=skip'. + */ + private boolean getFailOnError() { + return getWritableFiles().equals(WRITABLE_SKIP) ? false : failOnError; + } + + + /** + * Gets the value set for the FileTimeStamp. + * if it equals "current" then we return -GTC + * if it equals "modified" then we return -GTM + * if it equals "updated" then we return -GTU + * otherwise we return -GTC + * + * @return The default file time flag, if not set. + */ + public String getFileTimeStamp() { + if (timestamp == null) { + return ""; + } else if (timestamp.getValue().equals(TIME_MODIFIED)) { + return FLAG_FILETIME_MODIFIED; + } else if (timestamp.getValue().equals(TIME_UPDATED)) { + return FLAG_FILETIME_UPDATED; + } else { + return FLAG_FILETIME_DEF; + } + } + + + /** + * Gets the value to determine the behaviour when encountering writable files. + * @return An empty String, if not set. + */ + public String getWritableFiles() { + if (writableFiles == null) { + return ""; + } else if (writableFiles.getValue().equals(WRITABLE_REPLACE)) { + return FLAG_REPLACE_WRITABLE; + } else if (writableFiles.getValue().equals(WRITABLE_SKIP)) { + // ss.exe exits with '100', when files have been skipped + // so we have to ignore the failure + failOnError = false; + return FLAG_SKIP_WRITABLE; + } else { + return ""; + } + } + + /** + * Sets up the required environment and executes the command line. + * + * @param cmd The command line to execute. + * @return The return code from the exec'd process. + */ + private int run(Commandline cmd) { + try { + Execute exe = new Execute(new LogStreamHandler(this, + Project.MSG_INFO, + Project.MSG_WARN)); + + // If location of ss.ini is specified we need to set the + // environment-variable SSDIR to this value + if (serverPath != null) { + String[] env = exe.getEnvironment(); + if (env == null) { + env = new String[0]; + } + String[] newEnv = new String[env.length + 1]; + System.arraycopy(env, 0, newEnv, 0, env.length); + newEnv[env.length] = "SSDIR=" + serverPath; + + exe.setEnvironment(newEnv); + } + + exe.setAntRun(getProject()); + exe.setWorkingDirectory(getProject().getBaseDir()); + exe.setCommandline(cmd.getCommandline()); + // Use the OS launcher so we get environment variables + exe.setVMLauncher(false); + return exe.execute(); + } catch (IOException e) { + throw new BuildException(e, getLocation()); + } + } + + /** + * Calculates the start date for version comparison. + * <p> + * Calculates the date numDay days earlier than startdate. + * @param startDate The start date. + * @param daysToAdd The number of days to add. + * @return The calculated date. + * @throws ParseException + */ + private String calcDate(String startDate, int daysToAdd) throws ParseException { + Calendar calendar = new GregorianCalendar(); + Date currentDate = dateFormat.parse(startDate); + calendar.setTime(currentDate); + calendar.add(Calendar.DATE, daysToAdd); + return dateFormat.format(calendar.getTime()); + } + + /** + * Changes the password to '***' so it isn't displayed on screen if the build fails + * + * @param cmd The command line to clean + * @return The command line as a string with out the password + */ + private String formatCommandLine(Commandline cmd) { + StringBuffer sBuff = new StringBuffer(cmd.toString()); + int indexUser = sBuff.substring(0).indexOf(FLAG_LOGIN); + if (indexUser > 0) { + int indexPass = sBuff.substring(0).indexOf(",", indexUser); + int indexAfterPass = sBuff.substring(0).indexOf(" ", indexPass); + + for (int i = indexPass + 1; i < indexAfterPass; i++) { + sBuff.setCharAt(i, '*'); + } + } + return sBuff.toString(); + } + + /** + * Extension of EnumeratedAttribute to hold the values for file time stamp. + */ + public static class CurrentModUpdated extends EnumeratedAttribute { + /** + * Gets the list of allowable values. + * @return The values. + */ + public String[] getValues() { + return new String[] {TIME_CURRENT, TIME_MODIFIED, TIME_UPDATED}; + } + } + + /** + * Extension of EnumeratedAttribute to hold the values for writable filess. + */ + public static class WritableFiles extends EnumeratedAttribute { + /** + * Gets the list of allowable values. + * @return The values. + */ + public String[] getValues() { + return new String[] {WRITABLE_REPLACE, WRITABLE_SKIP, WRITABLE_FAIL}; + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSADD.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSADD.java new file mode 100644 index 00000000..0241ee3c --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSADD.java @@ -0,0 +1,122 @@ +/* + * 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.tools.ant.taskdefs.optional.vss; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.Path; + +/** + * Performs Add commands to Microsoft Visual SourceSafe. + * + * @ant.task name="vssadd" category="scm" + */ +public class MSVSSADD extends MSVSS { + + private String localPath = null; + + /** + * Builds a command line to execute ss. + * @return The constructed commandline. + */ + protected Commandline buildCmdLine() { + Commandline commandLine = new Commandline(); + + // first off, make sure that we've got a command and a localPath ... + if (getLocalpath() == null) { + String msg = "localPath attribute must be set!"; + throw new BuildException(msg, getLocation()); + } + + // build the command line from what we got the format is + // ss Add VSS items [-B] [-C] [-D-] [-H] [-I-] [-K] [-N] [-O] [-R] [-W] [-Y] [-?] + // as specified in the SS.EXE help + commandLine.setExecutable(getSSCommand()); + commandLine.createArgument().setValue(COMMAND_ADD); + + // VSS items + commandLine.createArgument().setValue(getLocalpath()); + // -I- or -I-Y or -I-N + commandLine.createArgument().setValue(getAutoresponse()); + // -R + commandLine.createArgument().setValue(getRecursive()); + // -W + commandLine.createArgument().setValue(getWritable()); + // -Y + commandLine.createArgument().setValue(getLogin()); + // -C + commandLine.createArgument().setValue(getComment()); + + return commandLine; + } + + /** + * Returns the local path without the flag.; required + * @todo See why this returns the local path without the flag. + * @return The local path value. + */ + protected String getLocalpath() { + return localPath; + } + + /** + * Add files recursively. Defaults to false. + * + * @param recursive The boolean value for recursive. + */ + public void setRecursive(boolean recursive) { + super.setInternalRecursive(recursive); + } + + /** + * Unset the READ-ONLY flag on local copies of files added to VSS. Defaults to false. + * + * @param writable The boolean value for writable. + */ + public final void setWritable(boolean writable) { + super.setInternalWritable(writable); + } + + /** + * Autoresponce behaviour. Valid options are Y and N. + * + * @param response The auto response value. + */ + public void setAutoresponse(String response) { + super.setInternalAutoResponse(response); + } + + /** + * Comment to apply to files added to SourceSafe. + * + * @param comment The comment to apply in SourceSafe + */ + public void setComment(String comment) { + super.setInternalComment(comment); + } + + /** + * Override the project working directory. + * + * @param localPath The path on disk. + */ + public void setLocalpath(Path localPath) { + this.localPath = localPath.toString(); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKIN.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKIN.java new file mode 100644 index 00000000..f9521aa0 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKIN.java @@ -0,0 +1,114 @@ +/* + * 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.tools.ant.taskdefs.optional.vss; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.Path; + +/** + * Performs CheckIn commands to Microsoft Visual SourceSafe. + * + * @ant.task name="vsscheckin" category="scm" + */ +public class MSVSSCHECKIN extends MSVSS { + + /** + * Builds a command line to execute ss. + * @return The constructed commandline. + */ + protected Commandline buildCmdLine() { + Commandline commandLine = new Commandline(); + + // first off, make sure that we've got a command and a vssdir ... + if (getVsspath() == null) { + String msg = "vsspath attribute must be set!"; + throw new BuildException(msg, getLocation()); + } + + // build the command line from what we got the format is + // ss Checkin VSS items [-H] [-C] [-I-] [-N] [-O] [-R] [-W] [-Y] [-?] + // as specified in the SS.EXE help + commandLine.setExecutable(getSSCommand()); + commandLine.createArgument().setValue(COMMAND_CHECKIN); + + // VSS items + commandLine.createArgument().setValue(getVsspath()); + // -GL + commandLine.createArgument().setValue(getLocalpath()); + // -I- or -I-Y or -I-N + commandLine.createArgument().setValue(getAutoresponse()); + // -R + commandLine.createArgument().setValue(getRecursive()); + // -W + commandLine.createArgument().setValue(getWritable()); + // -Y + commandLine.createArgument().setValue(getLogin()); + // -C + commandLine.createArgument().setValue(getComment()); + + return commandLine; + } + + /** + * Override the project working directory. + * + * @param localPath The path on disk. + */ + public void setLocalpath(Path localPath) { + super.setInternalLocalPath(localPath.toString()); + } + + /** + * Check-in files recursively. Defaults to false. + * + * @param recursive The boolean value for recursive. + */ + public void setRecursive(boolean recursive) { + super.setInternalRecursive(recursive); + } + + /** + * Unset the READ-ONLY flag on local copies of files checked-in to VSS. + * Defaults to false. + * + * @param writable The boolean value for writable. + */ + public final void setWritable(boolean writable) { + super.setInternalWritable(writable); + } + + /** + * Autoresponce behaviour. Valid options are Y and N. + * + * @param response The auto response value. + */ + public void setAutoresponse(String response) { + super.setInternalAutoResponse(response); + } + + /** + * Comment to apply to files checked-in to SourceSafe. + * + * @param comment The comment to apply in SourceSafe + */ + public void setComment(String comment) { + super.setInternalComment(comment); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKOUT.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKOUT.java new file mode 100644 index 00000000..edbcde96 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKOUT.java @@ -0,0 +1,165 @@ +/* + * 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.tools.ant.taskdefs.optional.vss; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.Path; + +/** + * Performs CheckOut commands to Microsoft Visual SourceSafe. + * + * @ant.task name="vsscheckout" category="scm" + * @ant.attribute.group name="vdl" description="Only one of version, date or label" + */ +public class MSVSSCHECKOUT extends MSVSS { + + /** + * Builds a command line to execute ss. + * @return The constructed commandline. + */ + protected Commandline buildCmdLine() { + Commandline commandLine = new Commandline(); + + // first off, make sure that we've got a command and a vssdir ... + if (getVsspath() == null) { + String msg = "vsspath attribute must be set!"; + throw new BuildException(msg, getLocation()); + } + + // build the command line from what we got the format is + // ss Checkout VSS items [-G] [-C] [-H] [-I-] [-N] [-O] [-R] [-V] [-Y] [-?] + // as specified in the SS.EXE help + commandLine.setExecutable(getSSCommand()); + commandLine.createArgument().setValue(COMMAND_CHECKOUT); + + // VSS items + commandLine.createArgument().setValue(getVsspath()); + // -GL + commandLine.createArgument().setValue(getLocalpath()); + // -I- or -I-Y or -I-N + commandLine.createArgument().setValue(getAutoresponse()); + // -R + commandLine.createArgument().setValue(getRecursive()); + // -V + commandLine.createArgument().setValue(getVersionDateLabel()); + // -Y + commandLine.createArgument().setValue(getLogin()); + // -G + commandLine.createArgument().setValue(getFileTimeStamp()); + // -GWS or -GWR + commandLine.createArgument().setValue(getWritableFiles()); + // -G- + commandLine.createArgument().setValue(getGetLocalCopy()); + + return commandLine; + } + + /** + * Override the project working directory. + * + * @param localPath The path on disk. + */ + public void setLocalpath(Path localPath) { + super.setInternalLocalPath(localPath.toString()); + } + + /** + * Check-out files recursively. Defaults to false. + * + * @param recursive The boolean value for recursive. + */ + public void setRecursive(boolean recursive) { + super.setInternalRecursive(recursive); + } + + /** + * Version to check-out. + * + * @param version The version to check-out. + * + * @ant.attribute group="vdl" + */ + public void setVersion(String version) { + super.setInternalVersion(version); + } + + /** + * Date to check-out. + * + * @param date The date to check-out. + * + * @ant.attribute group="vdl" + */ + public void setDate(String date) { + super.setInternalDate(date); + } + + /** + * Label to check-out. + * + * @param label The label to check-out. + * + * @ant.attribute group="vdl" + */ + public void setLabel(String label) { + super.setInternalLabel(label); + } + + /** + * Autoresponce behaviour. Valid options are Y and N. + * + * @param response The auto response value. + */ + public void setAutoresponse(String response) { + super.setInternalAutoResponse(response); + } + + /** + * Date and time stamp given to the local copy. Defaults to <code>current</code>. + * + * @param timestamp The file time stamping behaviour. + */ + public void setFileTimeStamp(CurrentModUpdated timestamp) { + super.setInternalFileTimeStamp(timestamp); + } + + /** + * Action taken when local files are writable. Defaults to <code>fail</code>. + * <p> + * Due to ss.exe returning with an exit code of '100' for both errors and when + * a file has been skipped, <code>failonerror</code> is set to false when using + * the <code>skip</code> option. + * </p> + * + * @param files The writable files behaviour + */ + public void setWritableFiles(WritableFiles files) { + super.setInternalWritableFiles(files); + } + + /** + * Retrieve a local copy during a checkout. Defaults to true. + * + * @param get The get local copy behaviour + */ + public void setGetLocalCopy(boolean get) { + super.setInternalGetLocalCopy(get); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCP.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCP.java new file mode 100644 index 00000000..ae2fcb7c --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCP.java @@ -0,0 +1,69 @@ +/* + * 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.tools.ant.taskdefs.optional.vss; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.types.Commandline; + +/** + * Performs CP (Change Project) commands to Microsoft Visual SourceSafe. + * <p>This task is typically used before a VssAdd in order to set the target project</p> + * + * @ant.task name="vsscp" category="scm" + */ +public class MSVSSCP extends MSVSS { + + /** + * Builds a command line to execute ss. + * @return The constructed commandline. + */ + protected Commandline buildCmdLine() { + Commandline commandLine = new Commandline(); + + // first off, make sure that we've got a command and a vssdir ... + if (getVsspath() == null) { + String msg = "vsspath attribute must be set!"; + throw new BuildException(msg, getLocation()); + } + + // build the command line from what we got the format is + // ss CP VSS items [-H] [-I-] [-Y] [-?] + // as specified in the SS.EXE help + commandLine.setExecutable(getSSCommand()); + commandLine.createArgument().setValue(COMMAND_CP); + + // VSS items + commandLine.createArgument().setValue(getVsspath()); + // -I- or -I-Y or -I-N + commandLine.createArgument().setValue(getAutoresponse()); + // -Y + commandLine.createArgument().setValue(getLogin()); + + return commandLine; + } + + /** + * Autoresponce behaviour. Valid options are Y and N. + * + * @param response The auto response value. + */ + public void setAutoresponse(String response) { + super.setInternalAutoResponse(response); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCREATE.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCREATE.java new file mode 100644 index 00000000..4f298c03 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCREATE.java @@ -0,0 +1,91 @@ +/* + * 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.tools.ant.taskdefs.optional.vss; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.types.Commandline; + +/** + * Creates a new project in Microsoft Visual SourceSafe. + * + * @ant.task name="vsscreate" category="scm" + */ +public class MSVSSCREATE extends MSVSS { + + /** + * Builds a command line to execute ss. + * @return The constructed commandline. + */ + Commandline buildCmdLine() { + Commandline commandLine = new Commandline(); + + // first off, make sure that we've got a command and a vssdir... + if (getVsspath() == null) { + String msg = "vsspath attribute must be set!"; + throw new BuildException(msg, getLocation()); + } + + // build the command line from what we got + // the format is: + // ss Create VSS items [-C] [-H] [-I-] [-N] [-O] [-S] [-Y] [-?] + // as specified in the SS.EXE help + commandLine.setExecutable(getSSCommand()); + commandLine.createArgument().setValue(COMMAND_CREATE); + + // VSS items + commandLine.createArgument().setValue(getVsspath()); + // -C + commandLine.createArgument().setValue(getComment()); + // -I- or -I-Y or -I-N + commandLine.createArgument().setValue(getAutoresponse()); + // -O- + commandLine.createArgument().setValue(getQuiet()); + // -Y + commandLine.createArgument().setValue(getLogin()); + + return commandLine; + } + + /** + * Comment to apply to the project created in SourceSafe. + * + * @param comment The comment to apply in SourceSafe + */ + public void setComment(String comment) { + super.setInternalComment(comment); + } + + /** + * Enable quiet mode. Defaults to false. + * + * @param quiet The boolean value for quiet. + */ + public final void setQuiet (boolean quiet) { + super.setInternalQuiet(quiet); + } + + /** + * Autoresponce behaviour. Valid options are Y and N. + * + * @param response The auto response value. + */ + public void setAutoresponse(String response) { + super.setInternalAutoResponse(response); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSConstants.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSConstants.java new file mode 100644 index 00000000..9e5ec606 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSConstants.java @@ -0,0 +1,127 @@ +/* + * 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.tools.ant.taskdefs.optional.vss; + +/** + * Holds all the constants for the VSS tasks. + * + */ +// CheckStyle:InterfaceIsType OFF (bc) +public interface MSVSSConstants { + /** Constant for the thing to execute */ + String SS_EXE = "ss"; + /** Dollar Sigh to prefix the project path */ + String PROJECT_PREFIX = "$"; + + /** The 'CP' command */ + String COMMAND_CP = "CP"; + /** The 'Add' command */ + String COMMAND_ADD = "Add"; + /** The 'Get' command */ + String COMMAND_GET = "Get"; + /** The 'Checkout' command */ + String COMMAND_CHECKOUT = "Checkout"; + /** The 'Checkin' command */ + String COMMAND_CHECKIN = "Checkin"; + /** The 'Label' command */ + String COMMAND_LABEL = "Label"; + /** The 'History' command */ + String COMMAND_HISTORY = "History"; + /** The 'Create' command */ + String COMMAND_CREATE = "Create"; + + /** The brief style flag */ + String STYLE_BRIEF = "brief"; + /** The codediff style flag */ + String STYLE_CODEDIFF = "codediff"; + /** The nofile style flag */ + String STYLE_NOFILE = "nofile"; + /** The default style flag */ + String STYLE_DEFAULT = "default"; + + /** The text for current (default) timestamp */ + String TIME_CURRENT = "current"; + /** The text for modified timestamp */ + String TIME_MODIFIED = "modified"; + /** The text for updated timestamp */ + String TIME_UPDATED = "updated"; + + /** The text for replacing writable files */ + String WRITABLE_REPLACE = "replace"; + /** The text for skipping writable files */ + String WRITABLE_SKIP = "skip"; + /** The text for failing on writable files */ + String WRITABLE_FAIL = "fail"; + + /** -Y flag */ + String FLAG_LOGIN = "-Y"; + /** -GL flag */ + String FLAG_OVERRIDE_WORKING_DIR = "-GL"; + /** -I- flag */ + String FLAG_AUTORESPONSE_DEF = "-I-"; + /** -I-Y flag */ + String FLAG_AUTORESPONSE_YES = "-I-Y"; + /** -I-N flag */ + String FLAG_AUTORESPONSE_NO = "-I-N"; + /** -R flag */ + String FLAG_RECURSION = "-R"; + /** -V flag */ + String FLAG_VERSION = "-V"; + /** -Vd flag */ + String FLAG_VERSION_DATE = "-Vd"; + /** -VL flag */ + String FLAG_VERSION_LABEL = "-VL"; + /** -W flag */ + String FLAG_WRITABLE = "-W"; + /** -N flag */ + String VALUE_NO = "-N"; + /** -Y flag */ + String VALUE_YES = "-Y"; + /** -O- flag */ + String FLAG_QUIET = "-O-"; + /** -C flag */ + String FLAG_COMMENT = "-C"; + /** -L flag */ + String FLAG_LABEL = "-L"; + /** ~d flag */ + String VALUE_FROMDATE = "~d"; + /** ~L flag */ + String VALUE_FROMLABEL = "~L"; + /** -O flag */ + String FLAG_OUTPUT = "-O"; + /** -U flag */ + String FLAG_USER = "-U"; + /** -F- flag */ + String FLAG_NO_FILE = "-F-"; + /** -B flag */ + String FLAG_BRIEF = "-B"; + /** -D flag */ + String FLAG_CODEDIFF = "-D"; + /** -GTC flag */ + String FLAG_FILETIME_DEF = "-GTC"; + /** -GTM flag */ + String FLAG_FILETIME_MODIFIED = "-GTM"; + /** -GTU flag */ + String FLAG_FILETIME_UPDATED = "-GTU"; + /** -GWR flag */ + String FLAG_REPLACE_WRITABLE = "-GWR"; + /** -GWS flag */ + String FLAG_SKIP_WRITABLE = "-GWS"; + /** -G- flag */ + String FLAG_NO_GET = "-G-"; +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSGET.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSGET.java new file mode 100644 index 00000000..fd5ed096 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSGET.java @@ -0,0 +1,172 @@ +/* + * 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.tools.ant.taskdefs.optional.vss; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.Path; + +/** + * Perform Get commands from Microsoft Visual SourceSafe. + * + * @ant.task name="vssget" category="scm" + * @ant.attribute.group name="vdl" description="Only one of version, date or label" + */ +public class MSVSSGET extends MSVSS { + + /** + * Builds a command line to execute ss. + * @return The constructed commandline. + */ + Commandline buildCmdLine() { + Commandline commandLine = new Commandline(); + + // build the command line from what we got the format is + // ss Get VSS items [-G] [-H] [-I-] [-N] [-O] [-R] [-V] [-W] [-Y] [-?] + // as specified in the SS.EXE help + commandLine.setExecutable(getSSCommand()); + commandLine.createArgument().setValue(COMMAND_GET); + + if (getVsspath() == null) { + throw new BuildException("vsspath attribute must be set!", getLocation()); + } + commandLine.createArgument().setValue(getVsspath()); + + // -GL + commandLine.createArgument().setValue(getLocalpath()); + // -I- or -I-Y or -I-N + commandLine.createArgument().setValue(getAutoresponse()); + // -O- + commandLine.createArgument().setValue(getQuiet()); + // -R + commandLine.createArgument().setValue(getRecursive()); + // -V + commandLine.createArgument().setValue(getVersionDateLabel()); + // -W + commandLine.createArgument().setValue(getWritable()); + // -Y + commandLine.createArgument().setValue(getLogin()); + // -G + commandLine.createArgument().setValue(getFileTimeStamp()); + // -GWS or -GWR + commandLine.createArgument().setValue(getWritableFiles()); + + return commandLine; + } + + /** + * Override the project working directory. + * + * @param localPath The path on disk. + */ + public void setLocalpath(Path localPath) { + super.setInternalLocalPath(localPath.toString()); + } + + /** + * Get files recursively. Defaults to false. + * + * @param recursive The boolean value for recursive. + */ + public final void setRecursive(boolean recursive) { + super.setInternalRecursive(recursive); + } + + /** + * Enable quiet mode. Defaults to false. + * + * @param quiet The boolean value for quiet. + */ + public final void setQuiet (boolean quiet) { + super.setInternalQuiet(quiet); + } + + /** + * Unset the READ-ONLY flag on files retrieved from VSS. Defaults to false. + * + * @param writable The boolean value for writable. + */ + public final void setWritable(boolean writable) { + super.setInternalWritable(writable); + } + + /** + * Version to get. + * + * @param version The version to get. + * + * @ant.attribute group="vdl" + */ + public void setVersion(String version) { + super.setInternalVersion(version); + } + + /** + * Date to get. + * + * @param date The date to get. + * + * @ant.attribute group="vdl" + */ + public void setDate(String date) { + super.setInternalDate(date); + } + + /** + * Label to get. + * + * @param label The label to get. + * + * @ant.attribute group="vdl" + */ + public void setLabel(String label) { + super.setInternalLabel(label); + } + + /** + * Autoresponce behaviour. Valid options are Y and N. + * + * @param response The auto response value. + */ + public void setAutoresponse(String response) { + super.setInternalAutoResponse(response); + } + + /** + * Date and time stamp given to the local copy. Defaults to <code>current</code>. + * + * @param timestamp The file time stamping behaviour. + */ + public void setFileTimeStamp(CurrentModUpdated timestamp) { + super.setInternalFileTimeStamp(timestamp); + } + + /** + * Action taken when local files are writable. Defaults to <code>fail</code>. + * <p> + * Due to ss.exe returning with an exit code of '100' for both errors and when + * a file has been skipped, <code>failonerror</code> is set to false when using + * the <code>skip</code> option. + * + * @param files The action to take. + */ + public void setWritableFiles(WritableFiles files) { + super.setInternalWritableFiles(files); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSHISTORY.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSHISTORY.java new file mode 100644 index 00000000..05ec91c2 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSHISTORY.java @@ -0,0 +1,199 @@ +/* + * 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.tools.ant.taskdefs.optional.vss; + +import java.io.File; +import java.text.SimpleDateFormat; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.EnumeratedAttribute; + +/** + * Performs History commands to Microsoft Visual SourceSafe. + * + * @ant.task name="vsshistory" category="scm" + */ +public class MSVSSHISTORY extends MSVSS { + + /** + * Builds a command line to execute ss. + * @return The constructed commandline. + */ + Commandline buildCmdLine() { + Commandline commandLine = new Commandline(); + + // first off, make sure that we've got a command and a vssdir and a label ... + if (getVsspath() == null) { + String msg = "vsspath attribute must be set!"; + throw new BuildException(msg, getLocation()); + } + + // build the command line from what we got the format is + // ss History elements [-H] [-L] [-N] [-O] [-V] [-Y] [-#] [-?] + // as specified in the SS.EXE help + commandLine.setExecutable(getSSCommand()); + commandLine.createArgument().setValue(COMMAND_HISTORY); + + // VSS items + commandLine.createArgument().setValue(getVsspath()); + // -I- + commandLine.createArgument().setValue(FLAG_AUTORESPONSE_DEF); // ignore all errors + // -Vd + commandLine.createArgument().setValue(getVersionDate()); + // -VL + commandLine.createArgument().setValue(getVersionLabel()); + // -R + commandLine.createArgument().setValue(getRecursive()); + // -B / -D / -F- + commandLine.createArgument().setValue(getStyle()); + // -Y + commandLine.createArgument().setValue(getLogin()); + // -O + commandLine.createArgument().setValue(getOutput()); + + return commandLine; + } + + /** + * Retrieve history recursively. Defaults to false. + * + * @param recursive The boolean value for recursive. + */ + public void setRecursive(boolean recursive) { + super.setInternalRecursive(recursive); + } + + /** + * Name of the user whose change history is generated. + * + * @param user The username. + */ + public void setUser(String user) { + super.setInternalUser(user); + } + + /** + * Date representing the 'start' of the range. + * + * @param fromDate The start date. + */ + public void setFromDate(String fromDate) { + super.setInternalFromDate(fromDate); + } + + /** + * Date representing the 'end' of the range. + * + * @param toDate The end date. + */ + public void setToDate(String toDate) { + super.setInternalToDate(toDate); + } + + /** + * Label representing the 'start' of the range. + * + * @param fromLabel The start label. + */ + public void setFromLabel(String fromLabel) { + super.setInternalFromLabel(fromLabel); + } + + /** + * Label representing the 'end' of the range. + * + * @param toLabel The end label. + */ + public void setToLabel(String toLabel) { + super.setInternalToLabel(toLabel); + } + + /** + * Number of days for comparison. + * Defaults to 2 days. + * + * @param numd The number of days. + */ + public void setNumdays(int numd) { + super.setInternalNumDays(numd); + } + + /** + * Output file name for the history. + * + * @param outfile The output file name. + */ + public void setOutput(File outfile) { + if (outfile != null) { + super.setInternalOutputFilename(outfile.getAbsolutePath()); + } + } + + /** + * Format of dates in <code>fromDate</code and <code>toDate</code>. + * Used when calculating dates with the numdays attribute. + * This string uses the formatting rules of <code>SimpleDateFormat</code>. + * Defaults to <code>DateFormat.SHORT</code>. + * + * @param dateFormat The date format. + */ + public void setDateFormat(String dateFormat) { + super.setInternalDateFormat(new SimpleDateFormat(dateFormat)); + } + + /** + * Output style. Valid options are: + * <ul> + * <li>brief: -B Display a brief history. + * <li>codediff: -D Display line-by-line file changes. + * <li>nofile: -F- Do not display individual file updates in the project history. + * <li>default: No option specified. Display in Source Safe's default format. + * </ul> + * + * @param attr The history style: + */ + public void setStyle(BriefCodediffNofile attr) { + String option = attr.getValue(); + if (option.equals(STYLE_BRIEF)) { + super.setInternalStyle(FLAG_BRIEF); + } else if (option.equals(STYLE_CODEDIFF)) { + super.setInternalStyle(FLAG_CODEDIFF); + } else if (option.equals(STYLE_DEFAULT)) { + super.setInternalStyle(""); + } else if (option.equals(STYLE_NOFILE)) { + super.setInternalStyle(FLAG_NO_FILE); + } else { + throw new BuildException("Style " + attr + " unknown.", getLocation()); + } + } + + /** + * Extension of EnumeratedAttribute to hold the values for style. + */ + public static class BriefCodediffNofile extends EnumeratedAttribute { + /** + * Gets the list of allowable values. + * @return The values. + */ + public String[] getValues() { + return new String[] {STYLE_BRIEF, STYLE_CODEDIFF, STYLE_NOFILE, STYLE_DEFAULT}; + } + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSLABEL.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSLABEL.java new file mode 100644 index 00000000..4ba9d7b3 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSLABEL.java @@ -0,0 +1,108 @@ +/* + * 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.tools.ant.taskdefs.optional.vss; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.types.Commandline; + +/** + * Performs Label commands to Microsoft Visual SourceSafe. + * + * @ant.task name="vsslabel" category="scm" + */ +public class MSVSSLABEL extends MSVSS { + + /** + * Builds a command line to execute ss. + * @return The constructed commandline. + */ + Commandline buildCmdLine() { + Commandline commandLine = new Commandline(); + + // first off, make sure that we've got a command and a vssdir and a label ... + if (getVsspath() == null) { + throw new BuildException("vsspath attribute must be set!", getLocation()); + } + + String label = getLabel(); + if (label.equals("")) { + String msg = "label attribute must be set!"; + throw new BuildException(msg, getLocation()); + } + + // build the command line from what we got the format is + // ss Label VSS items [-C] [-H] [-I-] [-Llabel] [-N] [-O] [-V] [-Y] [-?] + // as specified in the SS.EXE help + commandLine.setExecutable(getSSCommand()); + commandLine.createArgument().setValue(COMMAND_LABEL); + + // VSS items + commandLine.createArgument().setValue(getVsspath()); + // -C + commandLine.createArgument().setValue(getComment()); + // -I- or -I-Y or -I-N + commandLine.createArgument().setValue(getAutoresponse()); + // -L Specify the new label on the command line (instead of being prompted) + commandLine.createArgument().setValue(label); + // -V Label an existing file or project version + commandLine.createArgument().setValue(getVersion()); + // -Y + commandLine.createArgument().setValue(getLogin()); + + return commandLine; + } + + /** + * Label to apply in SourceSafe. + * + * @param label The label to apply. + * + * @ant.attribute group="required" + */ + public void setLabel(String label) { + super.setInternalLabel(label); + } + + /** + * Version to label. + * + * @param version The version to label. + */ + public void setVersion(String version) { + super.setInternalVersion(version); + } + + /** + * Comment to apply to files labeled in SourceSafe. + * + * @param comment The comment to apply in SourceSafe + */ + public void setComment(String comment) { + super.setInternalComment(comment); + } + + /** + * Autoresponce behaviour. Valid options are Y and N. + * + * @param response The auto response value. + */ + public void setAutoresponse(String response) { + super.setInternalAutoResponse(response); + } +} diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/windows/Attrib.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/windows/Attrib.java new file mode 100644 index 00000000..e0291914 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/windows/Attrib.java @@ -0,0 +1,196 @@ +/* + * 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.tools.ant.taskdefs.optional.windows; + +import java.io.File; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.ExecuteOn; +import org.apache.tools.ant.taskdefs.condition.Os; +import org.apache.tools.ant.types.FileSet; + +/** + * Attrib equivalent for Win32 environments. + * Note: Attrib parameters /S and /D are not handled. + * + * @since Ant 1.6 + */ +public class Attrib extends ExecuteOn { + + private static final String ATTR_READONLY = "R"; + private static final String ATTR_ARCHIVE = "A"; + private static final String ATTR_SYSTEM = "S"; + private static final String ATTR_HIDDEN = "H"; + private static final String SET = "+"; + private static final String UNSET = "-"; + + private boolean haveAttr = false; + + /** Constructor for Attrib. */ + public Attrib() { + super.setExecutable("attrib"); + super.setParallel(false); + } + + /** + * A file to be attribed. + * @param src a file + */ + public void setFile(File src) { + FileSet fs = new FileSet(); + fs.setFile(src); + addFileset(fs); + } + + /** + * Set the ReadOnly file attribute. + * @param value a <code>boolean</code> value + */ + public void setReadonly(boolean value) { + addArg(value, ATTR_READONLY); + } + + /** + * Set the Archive file attribute. + * @param value a <code>boolean</code> value + */ + public void setArchive(boolean value) { + addArg(value, ATTR_ARCHIVE); + } + + /** + * Set the System file attribute. + * @param value a <code>boolean</code> value + */ + public void setSystem(boolean value) { + addArg(value, ATTR_SYSTEM); + } + + /** + * Set the Hidden file attribute. + * @param value a <code>boolean</code> value + */ + public void setHidden(boolean value) { + addArg(value, ATTR_HIDDEN); + } + + /** + * Check the attributes. + */ + protected void checkConfiguration() { + if (!haveAttr()) { + throw new BuildException("Missing attribute parameter", + getLocation()); + } + super.checkConfiguration(); + } + + /** + * Set the executable. + * This is not allowed, and it always throws a BuildException. + * @param e ignored + * @ant.attribute ignore="true" + */ + public void setExecutable(String e) { + throw new BuildException(getTaskType() + + " doesn\'t support the executable attribute", getLocation()); + } + + /** + * Set the executable. + * This is not allowed, and it always throws a BuildException. + * @param e ignored + * @ant.attribute ignore="true" + */ + public void setCommand(String e) { + throw new BuildException(getTaskType() + + " doesn\'t support the command attribute", getLocation()); + } + + /** + * Add source file. + * This is not allowed, and it always throws a BuildException. + * @param b ignored + * @ant.attribute ignore="true" + */ + public void setAddsourcefile(boolean b) { + throw new BuildException(getTaskType() + + " doesn\'t support the addsourcefile attribute", getLocation()); + } + + /** + * Set skip empty file sets. + * This is not allowed, and it always throws a BuildException. + * @param skip ignored + * @ant.attribute ignore="true" + */ + public void setSkipEmptyFilesets(boolean skip) { + throw new BuildException(getTaskType() + " doesn\'t support the " + + "skipemptyfileset attribute", + getLocation()); + } + + /** + * Set parallel. + * This is not allowed, and it always throws a BuildException. + * @param parallel ignored + * @ant.attribute ignore="true" + */ + public void setParallel(boolean parallel) { + throw new BuildException(getTaskType() + + " doesn\'t support the parallel attribute", + getLocation()); + } + + /** + * Set max parallel. + * This is not allowed, and it always throws a BuildException. + * @param max ignored + * @ant.attribute ignore="true" + */ + public void setMaxParallel(int max) { + throw new BuildException(getTaskType() + + " doesn\'t support the maxparallel attribute", + getLocation()); + } + + /** + * Check if the os is valid. + * Default is to allow windows + * @return true if the os is valid. + */ + protected boolean isValidOs() { + return getOs() == null && getOsFamily() == null ? + Os.isFamily(Os.FAMILY_WINDOWS) : super.isValidOs(); + } + + private static String getSignString(boolean attr) { + return (attr ? SET : UNSET); + } + + private void addArg(boolean sign, String attribute) { + createArg().setValue(getSignString(sign) + attribute); + haveAttr = true; + } + + private boolean haveAttr() { + return haveAttr; + } + +} |