/* * $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.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import org.jcoderz.commons.util.FileUtils; import org.jcoderz.commons.util.IoUtil; import org.jcoderz.commons.util.StringUtil; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Type; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; /** * * TODO: Create unit tests! * TODO: Might add 'this' to <init> exiting logger * TODO: Store {@link System#currentTimeMillis()} / nanos * to compute time! * TODO: Create a method compressing the localVariableTable * + create localVariableTable if none is given! (seen with * aspectj generated classes) * TODO: Instead of boolean indicating if loggable store * reference to logger or null in method variable * * @author mandelan */ public class TracingInjector { static final Type THROWABLE_TYPE = Type.getType(Throwable.class); static final String PREFIX = "LGI$"; static final String LOCAL_PREFIX = "lgi$"; static final String STATIC_LOGGER_FIELD_NAME = PREFIX + "LGR"; static final String IS_LOGGABLE_FIELD_NAME = LOCAL_PREFIX + "isLoggable"; static final String RESULT_VAR_FIELD_NAME = LOCAL_PREFIX + "result"; private static final String CLASSNAME = TracingInjector.class.getName(); private static final Logger logger = Logger.getLogger(CLASSNAME); /** * * @author Andreas Mandel */ public interface Matcher { /** * * @param cn * @return */ public boolean matches (ClassNode cn); /** * * @param mn * @return */ public boolean matches (MethodNode mn); /** * * @param cn * @param mn * @return */ public boolean matches (ClassNode cn, MethodNode mn); } /** * * @author Andreas Mandel */ public static class MethodMatcher implements Matcher { public final static String COMMENT = "#"; public final static String COMMENT2 = "//"; final List mPatterString = new ArrayList(); final List mPatterns = new ArrayList(); /** * Method matcher that is configured by the given File. * * @param inFile * @throws IOException */ public MethodMatcher (File inFile) throws IOException { LineNumberReader reader = null; final FileReader fr = new FileReader(inFile); try { reader = new LineNumberReader(fr); String line = reader.readLine(); while (line != null) { if (!StringUtil.isEmptyOrNull(line) && !line.startsWith(COMMENT) && !line.startsWith(COMMENT2)) { mPatterString.add(line.trim()); final AspectPattern aspectPattern = new AspectPattern(line.trim()); mPatterns.add(aspectPattern); } line = reader.readLine(); } } finally { IoUtil.close(reader); IoUtil.close(fr); } } /** {@inheritDoc} */ public String toString () { return mPatterns.toString(); } /** * @param cn * @return */ public boolean matches(ClassNode cn) { final Iterator i = mPatterns.iterator(); boolean matched = false; while (i.hasNext() && !matched) { final AspectPattern pattern = (AspectPattern) i.next(); matched |= pattern.matchClass(cn.name); // TODO: Check inheritance!!! // return matcher!!! } return matched; } /** * @param mn * @return */ public boolean matches(MethodNode mn) { throw new UnsupportedOperationException("TODO"); } /** * @param cn * @param mn * @return */ public boolean matches(ClassNode cn, MethodNode mn) { final Iterator i = mPatterns.iterator(); boolean matched = false; while (i.hasNext() && !matched) { final AspectPattern pattern = (AspectPattern) i.next(); if (pattern.matchClass(cn.name)) // TODO: Check inheritance!!! { matched |= pattern.matchAccess(mn.access) && pattern.matchMethod(mn.desc); } } return matched; } } /** * * @param inFile * @param outFile * @param matcher * @param java5 * @param pai * @throws IOException */ public static void inject (File inFile, File outFile, Matcher matcher, boolean java5, boolean pai) throws IOException { final ClassNode cn = new ClassNode(); final FileInputStream is = new FileInputStream(inFile); try { final ClassReader cr = new ClassReader(is); cr.accept(cn, 0); } finally { IoUtil.close(is); } if (matcher.matches(cn)) { if (logger.isLoggable(Level.FINE)) { logger.fine("Will inject tracing into: " + cn.name); } new ClassTracingInjector(cn, java5, pai).inject(matcher); final FileOutputStream os = new FileOutputStream(outFile); try { final ClassWriter cw = new ClassWriter( /* ClassWriter.COMPUTE_FRAMES +*/ ClassWriter.COMPUTE_MAXS); cn.accept(cw); os.write(cw.toByteArray()); } finally { IoUtil.close(os); } } else { if (logger.isLoggable(Level.FINE)) { logger.fine("Direct copy for: " + cn.name + " (no match)"); } FileUtils.copy(inFile, outFile); } } }