/* * 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; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import org.apache.tools.ant.BuildEvent; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.SubBuildListener; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.EnumeratedAttribute; import org.apache.tools.ant.types.LogLevel; /** * Adds a listener to the current build process that records the * output to a file. *
Several recorders can exist at the same time. Each recorder is * associated with a file. The filename is used as a unique identifier for * the recorders. The first call to the recorder task with an unused filename * will create a recorder (using the parameters provided) and add it to the * listeners of the build. All subsequent calls to the recorder task using * this filename will modify that recorders state (recording or not) or other * properties (like logging level).
*Some technical issues: the file's print stream is flushed for "finished" * events (buildFinished, targetFinished and taskFinished), and is closed on * a buildFinished event.
* @see RecorderEntry * @version 0.5 * @since Ant 1.4 * @ant.task name="record" category="utility" */ public class Recorder extends Task implements SubBuildListener { ////////////////////////////////////////////////////////////////////// // ATTRIBUTES /** The name of the file to record to. */ private String filename = null; /** * Whether or not to append. Need Boolean to record an unset state (null). */ private Boolean append = null; /** * Whether to start or stop recording. Need Boolean to record an unset * state (null). */ private Boolean start = null; /** The level to log at. A level of -1 means not initialized yet. */ private int loglevel = -1; /** Strip task banners if true. */ private boolean emacsMode = false; /** The list of recorder entries. */ private static Hashtable recorderEntries = new Hashtable(); ////////////////////////////////////////////////////////////////////// // CONSTRUCTORS / INITIALIZERS /** * Overridden so we can add the task as build listener. * * @since Ant 1.7 */ public void init() { getProject().addBuildListener(this); } ////////////////////////////////////////////////////////////////////// // ACCESSOR METHODS /** * Sets the name of the file to log to, and the name of the recorder * entry. * * @param fname File name of logfile. */ public void setName(String fname) { filename = fname; } /** * Sets the action for the associated recorder entry. * * @param action The action for the entry to take: start or stop. */ public void setAction(ActionChoices action) { if (action.getValue().equalsIgnoreCase("start")) { start = Boolean.TRUE; } else { start = Boolean.FALSE; } } /** * Whether or not the logger should append to a previous file. * @param append if true, append to a previous file. */ public void setAppend(boolean append) { this.append = (append ? Boolean.TRUE : Boolean.FALSE); } /** * Set emacs mode. * @param emacsMode if true use emacs mode */ public void setEmacsMode(boolean emacsMode) { this.emacsMode = emacsMode; } /** * Sets the level to which this recorder entry should log to. * @param level the level to set. * @see VerbosityLevelChoices */ public void setLoglevel(VerbosityLevelChoices level) { loglevel = level.getLevel(); } ////////////////////////////////////////////////////////////////////// // CORE / MAIN BODY /** * The main execution. * @throws BuildException on error */ public void execute() throws BuildException { if (filename == null) { throw new BuildException("No filename specified"); } getProject().log("setting a recorder for name " + filename, Project.MSG_DEBUG); // get the recorder entry RecorderEntry recorder = getRecorder(filename, getProject()); // set the values on the recorder recorder.setMessageOutputLevel(loglevel); recorder.setEmacsMode(emacsMode); if (start != null) { if (start.booleanValue()) { recorder.reopenFile(); recorder.setRecordState(start); } else { recorder.setRecordState(start); recorder.closeFile(); } } } ////////////////////////////////////////////////////////////////////// // INNER CLASSES /** * A list of possible values for thesetAction()
method.
* Possible values include: start and stop.
*/
public static class ActionChoices extends EnumeratedAttribute {
private static final String[] VALUES = {"start", "stop"};
/**
* @see EnumeratedAttribute#getValues()
*/
/** {@inheritDoc}. */
public String[] getValues() {
return VALUES;
}
}
/**
* A list of possible values for the setLoglevel()
method.
* Possible values include: error, warn, info, verbose, debug.
*/
public static class VerbosityLevelChoices extends LogLevel {
}
/**
* Gets the recorder that's associated with the passed in name. If the
* recorder doesn't exist, then a new one is created.
* @param name the name of the recorder
* @param proj the current project
* @return a recorder
* @throws BuildException on error
*/
protected RecorderEntry getRecorder(String name, Project proj)
throws BuildException {
Object o = recorderEntries.get(name);
RecorderEntry entry;
if (o == null) {
// create a recorder entry
entry = new RecorderEntry(name);
if (append == null) {
entry.openFile(false);
} else {
entry.openFile(append.booleanValue());
}
entry.setProject(proj);
recorderEntries.put(name, entry);
} else {
entry = (RecorderEntry) o;
}
return entry;
}
/**
* Empty implementation required by SubBuildListener interface.
* @param event ignored.
* @since Ant 1.7
*/
public void buildStarted(BuildEvent event) {
}
/**
* Empty implementation required by SubBuildListener interface.
* @param event ignored.
* @since Ant 1.7
*/
public void subBuildStarted(BuildEvent event) {
}
/**
* Empty implementation required by SubBuildListener interface.
* @param event ignored.
* @since Ant 1.7
*/
public void targetStarted(BuildEvent event) {
}
/**
* Empty implementation required by SubBuildListener interface.
* @param event ignored.
* @since Ant 1.7
*/
public void targetFinished(BuildEvent event) {
}
/**
* Empty implementation required by SubBuildListener interface.
* @param event ignored.
* @since Ant 1.7
*/
public void taskStarted(BuildEvent event) {
}
/**
* Empty implementation required by SubBuildListener interface.
* @param event ignored.
* @since Ant 1.7
*/
public void taskFinished(BuildEvent event) {
}
/**
* Empty implementation required by SubBuildListener interface.
* @param event ignored.
* @since Ant 1.7
*/
public void messageLogged(BuildEvent event) {
}
/**
* Cleans recorder registry.
* @param event ignored.
* @since Ant 1.7
*/
public void buildFinished(BuildEvent event) {
cleanup();
}
/**
* Cleans recorder registry, if this is the subbuild the task has
* been created in.
* @param event ignored.
* @since Ant 1.7
*/
public void subBuildFinished(BuildEvent event) {
if (event.getProject() == getProject()) {
cleanup();
}
}
/**
* cleans recorder registry and removes itself from BuildListener list.
*
* @since Ant 1.7
*/
private void cleanup() {
Hashtable entries = (Hashtable) recorderEntries.clone();
Iterator itEntries = entries.entrySet().iterator();
while (itEntries.hasNext()) {
Map.Entry entry = (Map.Entry) itEntries.next();
RecorderEntry re = (RecorderEntry) entry.getValue();
if (re.getProject() == getProject()) {
recorderEntries.remove(entry.getKey());
}
}
getProject().removeBuildListener(this);
}
}