aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/XmlLogger.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/XmlLogger.java')
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/XmlLogger.java474
1 files changed, 474 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/XmlLogger.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/XmlLogger.java
new file mode 100644
index 00000000..a67a260e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/XmlLogger.java
@@ -0,0 +1,474 @@
+/*
+ * 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;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.Writer;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Stack;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+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.w3c.dom.Node;
+import org.w3c.dom.Text;
+
+/**
+ * Generates a file in the current directory with
+ * an XML description of what happened during a build.
+ * The default filename is "log.xml", but this can be overridden
+ * with the property <code>XmlLogger.file</code>.
+ *
+ * This implementation assumes in its sanity checking that only one
+ * thread runs a particular target/task at a time. This is enforced
+ * by the way that parallel builds and antcalls are done - and
+ * indeed all but the simplest of tasks could run into problems
+ * if executed in parallel.
+ *
+ * @see Project#addBuildListener(BuildListener)
+ */
+public class XmlLogger implements BuildLogger {
+
+ private int msgOutputLevel = Project.MSG_DEBUG;
+ private PrintStream outStream;
+
+ /** DocumentBuilder to use when creating the document to start with. */
+ private static DocumentBuilder builder = getDocumentBuilder();
+
+ /**
+ * Returns a default DocumentBuilder instance or throws an
+ * ExceptionInInitializerError if it can't be created.
+ *
+ * @return a default DocumentBuilder instance.
+ */
+ private static DocumentBuilder getDocumentBuilder() {
+ try {
+ return DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ } catch (Exception exc) {
+ throw new ExceptionInInitializerError(exc);
+ }
+ }
+
+ /** XML element name for a build. */
+ private static final String BUILD_TAG = "build";
+
+ /** XML element name for a target. */
+ private static final String TARGET_TAG = "target";
+
+ /** XML element name for a task. */
+ private static final String TASK_TAG = "task";
+
+ /** XML element name for a message. */
+ private static final String MESSAGE_TAG = "message";
+
+ /** XML attribute name for a name. */
+ private static final String NAME_ATTR = "name";
+
+ /** XML attribute name for a time. */
+ private static final String TIME_ATTR = "time";
+
+ /** XML attribute name for a message priority. */
+ private static final String PRIORITY_ATTR = "priority";
+
+ /** XML attribute name for a file location. */
+ private static final String LOCATION_ATTR = "location";
+
+ /** XML attribute name for an error description. */
+ private static final String ERROR_ATTR = "error";
+
+ /** XML element name for a stack trace. */
+ private static final String STACKTRACE_TAG = "stacktrace";
+
+ /** The complete log document for this build. */
+ private Document doc = builder.newDocument();
+
+ /** Mapping for when tasks started (Task to TimedElement). */
+ private Hashtable<Task, TimedElement> tasks = new Hashtable<Task, TimedElement>();
+
+ /** Mapping for when targets started (Target to TimedElement). */
+ private Hashtable<Target, TimedElement> targets = new Hashtable<Target, XmlLogger.TimedElement>();
+
+ /**
+ * Mapping of threads to stacks of elements
+ * (Thread to Stack of TimedElement).
+ */
+ private Hashtable<Thread, Stack<TimedElement>> threadStacks = new Hashtable<Thread, Stack<TimedElement>>();
+
+ /**
+ * When the build started.
+ */
+ private TimedElement buildElement = null;
+
+ /** Utility class representing the time an element started. */
+ private static class TimedElement {
+ /**
+ * Start time in milliseconds
+ * (as returned by <code>System.currentTimeMillis()</code>).
+ */
+ private long startTime;
+ /** Element created at the start time. */
+ private Element element;
+ public String toString() {
+ return element.getTagName() + ":" + element.getAttribute("name");
+ }
+ }
+
+ /**
+ * Constructs a new BuildListener that logs build events to an XML file.
+ */
+ public XmlLogger() {
+ }
+
+ /**
+ * Fired when the build starts, this builds the top-level element for the
+ * document and remembers the time of the start of the build.
+ *
+ * @param event Ignored.
+ */
+ public void buildStarted(BuildEvent event) {
+ buildElement = new TimedElement();
+ buildElement.startTime = System.currentTimeMillis();
+ buildElement.element = doc.createElement(BUILD_TAG);
+ }
+
+ /**
+ * Fired when the build finishes, this adds the time taken and any
+ * error stacktrace to the build element and writes the document to disk.
+ *
+ * @param event An event with any relevant extra information.
+ * Will not be <code>null</code>.
+ */
+ public void buildFinished(BuildEvent event) {
+ long totalTime = System.currentTimeMillis() - buildElement.startTime;
+ buildElement.element.setAttribute(TIME_ATTR, DefaultLogger.formatTime(totalTime));
+
+ if (event.getException() != null) {
+ buildElement.element.setAttribute(ERROR_ATTR, event.getException().toString());
+ // print the stacktrace in the build file it is always useful...
+ // better have too much info than not enough.
+ Throwable t = event.getException();
+ Text errText = doc.createCDATASection(StringUtils.getStackTrace(t));
+ Element stacktrace = doc.createElement(STACKTRACE_TAG);
+ stacktrace.appendChild(errText);
+ synchronizedAppend(buildElement.element, stacktrace);
+ }
+ String outFilename = getProperty(event, "XmlLogger.file", "log.xml");
+ String xslUri = getProperty(event, "ant.XmlLogger.stylesheet.uri", "log.xsl");
+ Writer out = null;
+ try {
+ // specify output in UTF8 otherwise accented characters will blow
+ // up everything
+ OutputStream stream = outStream;
+ if (stream == null) {
+ stream = new FileOutputStream(outFilename);
+ }
+ out = new OutputStreamWriter(stream, "UTF8");
+ out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ if (xslUri.length() > 0) {
+ out.write("<?xml-stylesheet type=\"text/xsl\" href=\"" + xslUri + "\"?>\n\n");
+ }
+ new DOMElementWriter().write(buildElement.element, out, 0, "\t");
+ out.flush();
+ } catch (IOException exc) {
+ throw new BuildException("Unable to write log file", exc);
+ } finally {
+ FileUtils.close(out);
+ }
+ buildElement = null;
+ }
+
+ private String getProperty(BuildEvent event, String propertyName, String defaultValue) {
+ String rv = defaultValue;
+ if (event != null && event.getProject() != null && event.getProject().getProperty(propertyName) != null) {
+ rv = event.getProject().getProperty(propertyName);
+ }
+ return rv;
+ }
+
+ /**
+ * Returns the stack of timed elements for the current thread.
+ * @return the stack of timed elements for the current thread
+ */
+ private Stack<TimedElement> getStack() {
+ Stack<TimedElement> threadStack = threadStacks.get(Thread.currentThread());
+ if (threadStack == null) {
+ threadStack = new Stack<TimedElement>();
+ threadStacks.put(Thread.currentThread(), threadStack);
+ }
+ /* For debugging purposes uncomment:
+ org.w3c.dom.Comment s = doc.createComment("stack=" + threadStack);
+ buildElement.element.appendChild(s);
+ */
+ return threadStack;
+ }
+
+ /**
+ * Fired when a target starts building, this pushes a timed element
+ * for the target onto the stack of elements for the current thread,
+ * remembering the current time and the name of the target.
+ *
+ * @param event An event with any relevant extra information.
+ * Will not be <code>null</code>.
+ */
+ public void targetStarted(BuildEvent event) {
+ Target target = event.getTarget();
+ TimedElement targetElement = new TimedElement();
+ targetElement.startTime = System.currentTimeMillis();
+ targetElement.element = doc.createElement(TARGET_TAG);
+ targetElement.element.setAttribute(NAME_ATTR, target.getName());
+ targets.put(target, targetElement);
+ getStack().push(targetElement);
+ }
+
+ /**
+ * Fired when a target finishes building, this adds the time taken
+ * and any error stacktrace to the appropriate target element in the log.
+ *
+ * @param event An event with any relevant extra information.
+ * Will not be <code>null</code>.
+ */
+ public void targetFinished(BuildEvent event) {
+ Target target = event.getTarget();
+ TimedElement targetElement = targets.get(target);
+ if (targetElement != null) {
+ long totalTime = System.currentTimeMillis() - targetElement.startTime;
+ targetElement.element.setAttribute(TIME_ATTR, DefaultLogger.formatTime(totalTime));
+
+ TimedElement parentElement = null;
+ Stack<TimedElement> threadStack = getStack();
+ if (!threadStack.empty()) {
+ TimedElement poppedStack = threadStack.pop();
+ if (poppedStack != targetElement) {
+ throw new RuntimeException("Mismatch - popped element = " + poppedStack
+ + " finished target element = " + targetElement);
+ }
+ if (!threadStack.empty()) {
+ parentElement = threadStack.peek();
+ }
+ }
+ if (parentElement == null) {
+ synchronizedAppend(buildElement.element, targetElement.element);
+ } else {
+ synchronizedAppend(parentElement.element,
+ targetElement.element);
+ }
+ }
+ targets.remove(target);
+ }
+
+ /**
+ * Fired when a task starts building, this pushes a timed element
+ * for the task onto the stack of elements for the current thread,
+ * remembering the current time and the name of the task.
+ *
+ * @param event An event with any relevant extra information.
+ * Will not be <code>null</code>.
+ */
+ public void taskStarted(BuildEvent event) {
+ TimedElement taskElement = new TimedElement();
+ taskElement.startTime = System.currentTimeMillis();
+ taskElement.element = doc.createElement(TASK_TAG);
+
+ Task task = event.getTask();
+ String name = event.getTask().getTaskName();
+ if (name == null) {
+ name = "";
+ }
+ taskElement.element.setAttribute(NAME_ATTR, name);
+ taskElement.element.setAttribute(LOCATION_ATTR, event.getTask().getLocation().toString());
+ tasks.put(task, taskElement);
+ getStack().push(taskElement);
+ }
+
+ /**
+ * Fired when a task finishes building, this adds the time taken
+ * and any error stacktrace to the appropriate task element in the log.
+ *
+ * @param event An event with any relevant extra information.
+ * Will not be <code>null</code>.
+ */
+ public void taskFinished(BuildEvent event) {
+ Task task = event.getTask();
+ TimedElement taskElement = tasks.get(task);
+ if (taskElement == null) {
+ throw new RuntimeException("Unknown task " + task + " not in " + tasks);
+ }
+ long totalTime = System.currentTimeMillis() - taskElement.startTime;
+ taskElement.element.setAttribute(TIME_ATTR, DefaultLogger.formatTime(totalTime));
+ Target target = task.getOwningTarget();
+ TimedElement targetElement = null;
+ if (target != null) {
+ targetElement = targets.get(target);
+ }
+ if (targetElement == null) {
+ synchronizedAppend(buildElement.element, taskElement.element);
+ } else {
+ synchronizedAppend(targetElement.element, taskElement.element);
+ }
+ Stack<TimedElement> threadStack = getStack();
+ if (!threadStack.empty()) {
+ TimedElement poppedStack = threadStack.pop();
+ if (poppedStack != taskElement) {
+ throw new RuntimeException("Mismatch - popped element = " + poppedStack
+ + " finished task element = " + taskElement);
+ }
+ }
+ tasks.remove(task);
+ }
+
+ /**
+ * Get the TimedElement associated with a task.
+ *
+ * Where the task is not found directly, search for unknown elements which
+ * may be hiding the real task
+ */
+ private TimedElement getTaskElement(Task task) {
+ TimedElement element = tasks.get(task);
+ if (element != null) {
+ return element;
+ }
+ for (Enumeration<Task> e = tasks.keys(); e.hasMoreElements();) {
+ Task key = e.nextElement();
+ if (key instanceof UnknownElement) {
+ if (((UnknownElement) key).getTask() == task) {
+ return tasks.get(key);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Fired when a message is logged, this adds a message element to the
+ * most appropriate parent element (task, target or build) and records
+ * the priority and text of the message.
+ *
+ * @param event An event with any relevant extra information.
+ * Will not be <code>null</code>.
+ */
+ public void messageLogged(BuildEvent event) {
+ int priority = event.getPriority();
+ if (priority > msgOutputLevel) {
+ return;
+ }
+ Element messageElement = doc.createElement(MESSAGE_TAG);
+
+ String name = "debug";
+ switch (priority) {
+ case Project.MSG_ERR:
+ name = "error";
+ break;
+ case Project.MSG_WARN:
+ name = "warn";
+ break;
+ case Project.MSG_INFO:
+ name = "info";
+ break;
+ default:
+ name = "debug";
+ break;
+ }
+ messageElement.setAttribute(PRIORITY_ATTR, name);
+
+ Throwable ex = event.getException();
+ if (Project.MSG_DEBUG <= msgOutputLevel && ex != null) {
+ Text errText = doc.createCDATASection(StringUtils.getStackTrace(ex));
+ Element stacktrace = doc.createElement(STACKTRACE_TAG);
+ stacktrace.appendChild(errText);
+ synchronizedAppend(buildElement.element, stacktrace);
+ }
+ Text messageText = doc.createCDATASection(event.getMessage());
+ messageElement.appendChild(messageText);
+
+ TimedElement parentElement = null;
+
+ Task task = event.getTask();
+
+ Target target = event.getTarget();
+ if (task != null) {
+ parentElement = getTaskElement(task);
+ }
+ if (parentElement == null && target != null) {
+ parentElement = targets.get(target);
+ }
+ if (parentElement != null) {
+ synchronizedAppend(parentElement.element, messageElement);
+ } else {
+ synchronizedAppend(buildElement.element, messageElement);
+ }
+ }
+
+ // -------------------------------------------------- BuildLogger interface
+
+ /**
+ * Set the logging level when using this as a Logger
+ *
+ * @param level the logging level -
+ * see {@link org.apache.tools.ant.Project#MSG_ERR Project}
+ * class for level definitions
+ */
+ public void setMessageOutputLevel(int level) {
+ msgOutputLevel = level;
+ }
+
+ /**
+ * Set the output stream to which logging output is sent when operating
+ * as a logger.
+ *
+ * @param output the output PrintStream.
+ */
+ public void setOutputPrintStream(PrintStream output) {
+ this.outStream = new PrintStream(output, true);
+ }
+
+ /**
+ * Ignore emacs mode, as it has no meaning in XML format
+ *
+ * @param emacsMode true if logger should produce emacs compatible
+ * output
+ */
+ public void setEmacsMode(boolean emacsMode) {
+ }
+
+ /**
+ * Ignore error print stream. All output will be written to
+ * either the XML log file or the PrintStream provided to
+ * setOutputPrintStream
+ *
+ * @param err the stream we are going to ignore.
+ */
+ public void setErrorPrintStream(PrintStream err) {
+ }
+
+ private void synchronizedAppend(Node parent, Node child) {
+ synchronized(parent) {
+ parent.appendChild(child);
+ }
+ }
+
+}