/*
* $Id: ArraysUtil.java 1011 2008-06-16 17:57:36Z amandel $
*
* Copyright 2006, The jCoderZ.org Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
* * Neither the name of the jCoderZ.org Project nor the names of
* its contributors may be used to endorse or promote products
* derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jcoderz.commons.tracing;
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 java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
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.jcoderz.commons.tracing.TracingInjector.Matcher;
import org.jcoderz.commons.tracing.TracingInjector.MethodMatcher;
import org.jcoderz.commons.util.IoUtil;
/**
* Ant task for TracingInjection.
*
* <p>This ant task takes a fileset <code>fileset</code> as input and
* one directory <code>destDir</code> as output. It copies ALL files
* from the input to the output, if the do not exist at the destination
* OR if the file last modified date of the source is newer than the
* target. Needed directories are created as used.</p>
*
* <p>As a 'side effect' all class files (*.class) that match
* one of the patterns given in the <code>patternFile</code> get
* JDK14 tracing logging (entering/exiting/throwing) injected in
* all methods that match the given pattern.</p>
*
* <p>If you set <code>java5</code> to <code>true</code> the
* generated code uses the much more efficient factory methods
* to box native types that had been introduced with java5
* (<code>new Integer(int i)</code> vs.
* <code>Integer.valueOf(int i)</code>). Nevertheless this only affects
* the code that is guarded with an <code>isLoggable(..)</code> so there no
* runtime penalty in the generated code if logging is disabled.</p>
*
* <p>The <code>patternFile</code> is read line by line and each line
* is expected to be either empty, start with a '#' or '//' or hold a
* valid aspectJ like pattern as defined in the
* AspectPattern according to <a href="http://aspectwerkz.codehaus.org/definition_issues.html">
* aspectwerkz</a>. Call the target with '-v' to see
* the regular expression that is generated out of the input pattern.</p>
*
* <p>The task supports a <code>verbose</code> attribute. If set to true
* a detailed log will be generated about what is going on. This is done
* by increasing the JDK logging loglevel - be aware of possible
* side effects.</p>
*
* The following code fragment defines the
* <code>tracing-injector</code> ant task.<pre>
* <taskdef name="tracing-injector"
* classname="org.jcoderz.commons.tracing.TracingInjectorTask"
* classpath="lib/tracing-injector-0.1.jar"/>
* </pre>
*
* A valid usage sample is:<pre>
* <tracing-injector destDir="build/classes-log"
* patternFile="config/tracing"
* java5="false" verbose="false">
* <fileset dir="build/classes"/>
* </tracing-injector>
* </pre>
*
* TODO: Support different Tracers to be set.
*
* @author Andreas Mandel
*/
public class TracingInjectorTask extends Task
{
// * <pre>
// * TODO: Refine Documentation
// * TODO: Implement validate method
// * TODO: Add dependency checker (AsM lib)
// * TODO: If required allow JAR as input and/or output
// * </pre>
// *
private final List mSrcFiles = new ArrayList();
private String mDestDirName;
private File mDestDir;
private final List mSourceFiles = new LinkedList();
private File mPatternFile;
private Matcher mMatcher;
private boolean mJava5 = false;
private boolean mPai = false;
private boolean mVerbose = false;
/**
*
* @param fileset files to be collected.
*/
public void addFileset(FileSet fileset)
{
mSrcFiles.add(fileset);
}
public void setDestDir(String destDir)
{
mDestDirName = destDir;
}
public void setPatternFile(File patternFile)
{
mPatternFile = patternFile;
}
public void setJava5(boolean java5)
{
mJava5 = java5;
}
public void setPai(boolean pai)
{
mPai = pai;
}
public void setVerbose(boolean verbose)
{
mVerbose = verbose;
}
/**
*
*/
public void execute()
{
validate();
// BUILD MATCHER Object...
try
{
mMatcher = new MethodMatcher(mPatternFile);
// IF loglevel??
log("Read matcher from " + mPatternFile, Project.MSG_VERBOSE);
log(String.valueOf(mMatcher), Project.MSG_VERBOSE);
}
catch (IOException ex)
{
throw new BuildException("Could not build matcher from file.", ex);
}
for (final Iterator itFSets = mSrcFiles.iterator(); itFSets.hasNext();)
{
final FileSet fs = (FileSet) itFSets.next();
final DirectoryScanner ds = fs.getDirectoryScanner(getProject());
final File base = ds.getBasedir();
final String[] includedFiles = ds.getIncludedFiles();
for (int i = 0; i < includedFiles.length; i++)
{
mSourceFiles.add(new SourceFile(
new File(base, includedFiles[i]), includedFiles[i]));
}
}
try
{
inject();
}
catch (IOException ex)
{
throw new BuildException(ex);
}
}
/**
*
* @throws IOException
*/
public void inject()
throws IOException
{
while (!mSourceFiles.isEmpty())
{
final SourceFile mySource = (SourceFile) mSourceFiles.remove(0);
final File targetFile = getTargetForSource(mySource);
if (!targetFile.exists()
|| mySource.getAbsoluteFile().lastModified()
> targetFile.lastModified())
{
ensurePath(targetFile);
if (mySource.getRelativeFileName().endsWith(".class"))
{
try
{
log("About to work on " + mySource.getAbsoluteFile()
+ ".", Project.MSG_VERBOSE);
TracingInjector.inject(mySource.getAbsoluteFile(), targetFile,
mMatcher, mJava5, mPai);
}
catch (RuntimeException ex)
{
log("Failed with " + mySource.mAbsoluteFile + " got "
+ ex, Project.MSG_DEBUG);
if (ex.getMessage() != null
&& ex.getMessage().startsWith("JSR/RET are not supported "))
{
IoUtil.copy(mySource.getAbsoluteFile(), targetFile);
}
else
{
// THIS IS ANT 1.7.0 :-(
log("About to only copy " + mySource.getAbsoluteFile()
+ " cause " + ex + ".", ex, Project.MSG_ERR);
IoUtil.copy(mySource.getAbsoluteFile(), targetFile);
}
}
}
else
{
log("About to copy " + mySource.getAbsoluteFile()
+ ".", Project.MSG_VERBOSE);
IoUtil.copy(mySource.getAbsoluteFile(), targetFile);
}
}
}
}
/**
*
* @param file
* @throws IOException
*/
private void ensurePath(File file)
throws IOException
{
if (!file.exists())
{
if (!file.getParentFile().exists())
{
if (!file.getParentFile().mkdirs())
{
throw new IOException("Could not create dir for target files.");
}
}
}
}
/**
*
* @param mySource
* @return
*/
private File getTargetForSource(SourceFile mySource)
{
return new File(mDestDir, mySource.getRelativeFileName());
}
/**
*
*/
private void validate()
{
mDestDir = new File(mDestDirName);
if (mVerbose)
{
Logger.getLogger(
TracingInjectorTask.class.getPackage().getName()).setLevel(Level.FINEST);
final Handler[] handlers = Logger.getLogger("").getHandlers();
final int amountOfHandlers = handlers.length;
for (int i = 0; i < amountOfHandlers; i++)
{
try
{
handlers[i].setLevel(Level.FINEST);
}
catch (Exception ex)
{
//ignore
}
}
}
}
/**
*
* @author Andreas Mandel
*/
private static class SourceFile
{
private final File mAbsoluteFile;
private final String mRelativeFileName;
/**
*
* @param absoluteFile
* @param relativeFileName
*/
public SourceFile(File absoluteFile, String relativeFileName)
{
mAbsoluteFile = absoluteFile;
mRelativeFileName = relativeFileName;
}
public File getAbsoluteFile()
{
return mAbsoluteFile;
}
public String getRelativeFileName()
{
return mRelativeFileName;
}
}
}