Open Source Repository

Home /hibernate/hibernate-3.2.4.ga | Repository Home


org/hibernate/tool/instrument/BasicInstrumentationTask.java
package org.hibernate.tool.instrument;

import org.apache.tools.ant.Task;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.types.FileSet;
import org.hibernate.bytecode.util.ClassDescriptor;
import org.hibernate.bytecode.util.ByteCodeHelper;
import org.hibernate.bytecode.util.FieldFilter;
import org.hibernate.bytecode.ClassTransformer;

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import java.util.HashSet;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.CRC32;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.DataInputStream;
import java.io.ByteArrayInputStream;

/**
 * Super class for all Hibernate instrumentation tasks.  Provides the basic
 * templating of how instrumentation should occur.
 *
 @author Steve Ebersole
 */
public abstract class BasicInstrumentationTask extends Task {

  private static final int ZIP_MAGIC = 0x504B0304;
  private static final int CLASS_MAGIC = 0xCAFEBABE;

  protected final Logger logger = new Logger();
  private List filesets = new ArrayList();
  private Set classNames = new HashSet();
  private boolean extended;
  private boolean verbose;

  public void addFileset(FileSet set) {
    this.filesets.addset );
  }

  protected final Iterator filesets() {
    return filesets.iterator();
  }

  public boolean isExtended() {
    return extended;
  }

  public void setExtended(boolean extended) {
    this.extended = extended;
  }

  public boolean isVerbose() {
    return verbose;
  }

  public void setVerbose(boolean verbose) {
    this.verbose = verbose;
  }

  public void execute() throws BuildException {
    if isExtended() ) {
      collectClassNames();
    }
    logger.info"starting instrumentation" );
    Project project = getProject();
    Iterator filesets = filesets();
    while filesets.hasNext() ) {
      FileSet fs = FileSet filesets.next();
      DirectoryScanner ds = fs.getDirectoryScannerproject );
      String[] includedFiles = ds.getIncludedFiles();
      File d = fs.getDirproject );
      for int i = 0; i < includedFiles.length; ++i ) {
        File file = new Filed, includedFiles[i] );
        try {
          processFilefile );
        }
        catch Exception e ) {
          throw new BuildException);
        }
      }
    }
  }

  private void collectClassNames() {
    logger.info"collecting class names for extended instrumentation determination" );
    Project project = getProject();
    Iterator filesets = filesets();
    while filesets.hasNext() ) {
      FileSet fs = FileSet filesets.next();
      DirectoryScanner ds = fs.getDirectoryScannerproject );
      String[] includedFiles = ds.getIncludedFiles();
      File d = fs.getDirproject );
      for int i = 0; i < includedFiles.length; ++i ) {
        File file = new Filed, includedFiles[i] );
        try {
          collectClassNamesfile );
        }
        catch Exception e ) {
          throw new BuildException);
        }
      }
    }
    logger.infoclassNames.size() " class(es) being checked" );
  }

  private void collectClassNames(File filethrows Exception {
      if isClassFilefile ) ) {
      byte[] bytes = ByteCodeHelper.readByteCodefile );
      ClassDescriptor descriptor = getClassDescriptorbytes );
        classNames.adddescriptor.getName() );
      }
      else if isJarFilefile ) ) {
        ZipEntryHandler collector = new ZipEntryHandler() {
          public void handleEntry(ZipEntry entry, byte[] byteCodethrows Exception {
          if !entry.isDirectory() ) {
            // see if the entry represents a class file
            DataInputStream din = new DataInputStreamnew ByteArrayInputStreambyteCode ) );
            if din.readInt() == CLASS_MAGIC ) {
                    classNames.addgetClassDescriptorbyteCode ).getName() );
            }
          }
          }
        };
        ZipFileProcessor processor = new ZipFileProcessorcollector );
        processor.processfile );
      }
  }

  protected void processFile(File filethrows Exception {
      if isClassFilefile ) ) {
          processClassFile(file);
      }
      else if isJarFilefile ) ) {
          processJarFile(file);
      }
      else {
        logger.verbose"ignoring " + file.toURL() );

      }
  }

  protected final boolean isClassFile(File filethrows IOException {
        return checkMagicfile, CLASS_MAGIC );
    }

    protected final boolean isJarFile(File filethrows IOException {
        return checkMagic(file, ZIP_MAGIC);
    }

  protected final boolean checkMagic(File file, long magicthrows IOException {
        DataInputStream in = new DataInputStreamnew FileInputStreamfile ) );
        try {
            int m = in.readInt();
            return magic == m;
        }
        finally {
            in.close();
        }
    }

  protected void processClassFile(File filethrows Exception {
    logger.verbose"Starting class file : " + file.toURL() );
    byte[] bytes = ByteCodeHelper.readByteCodefile );
    ClassDescriptor descriptor = getClassDescriptorbytes );
    ClassTransformer transformer = getClassTransformerdescriptor );
    if transformer == null ) {
      logger.verbose"skipping file : " + file.toURL() );
      return;
    }

    logger.info"processing class [" + descriptor.getName() "]; file = " + file.toURL() );
    byte[] transformedBytes = transformer.transform(
        getClass().getClassLoader(),
        descriptor.getName(),
        null,
        null,
        descriptor.getBytes()
    );

    OutputStream out = new FileOutputStreamfile );
    try {
      out.writetransformedBytes );
      out.flush();
    }
    finally {
      try {
        out.close();
      }
      catch IOException ignore) {
        // intentionally empty
      }
    }
  }

  protected void processJarFile(final File filethrows Exception {
    logger.verbose"starting jar file : " + file.toURL() );

        File tempFile = File.createTempFile(
            file.getName(),
            null,
            new Filefile.getAbsoluteFile().getParent() )
        );

        try {
      FileOutputStream fout = new FileOutputStreamtempFile, false );
      try {
        final ZipOutputStream out = new ZipOutputStreamfout );
        ZipEntryHandler transformer = new ZipEntryHandler() {
          public void handleEntry(ZipEntry entry, byte[] byteCodethrows Exception {
                logger.verbose"starting entry : " + entry.toString() );
                if !entry.isDirectory() ) {
                  // see if the entry represents a class file
                  DataInputStream din = new DataInputStreamnew ByteArrayInputStreambyteCode ) );
                  if din.readInt() == CLASS_MAGIC ) {
                    ClassDescriptor descriptor = getClassDescriptorbyteCode );
                    ClassTransformer transformer = getClassTransformerdescriptor );
                    if transformer == null ) {
                      logger.verbose"skipping entry : " + entry.toString() );
                    }
                    else {
                      logger.info"processing class [" + descriptor.getName() "]; entry = " + file.toURL() );
                      byteCode = transformer.transform(
                          getClass().getClassLoader(),
                          descriptor.getName(),
                          null,
                          null,
                          descriptor.getBytes()
                      );
                    }
                  }
                  else {
                    logger.verbose"ignoring zip entry : " + entry.toString() );
                  }
                }

                ZipEntry outEntry = new ZipEntryentry.getName() );
                outEntry.setMethodentry.getMethod() );
                outEntry.setCommententry.getComment() );
                outEntry.setSizebyteCode.length );

                if outEntry.getMethod() == ZipEntry.STORED ){
                  CRC32 crc = new CRC32();
                  crc.updatebyteCode );
                  outEntry.setCrccrc.getValue() );
                  outEntry.setCompressedSizebyteCode.length );
                }
                out.putNextEntryoutEntry );
                out.writebyteCode );
                out.closeEntry();
          }
        };
        ZipFileProcessor processor = new ZipFileProcessortransformer );
        processor.processfile );
        out.close();
      }
      finally{
        fout.close();
      }

            if file.delete() ) {
              File newFile = new FiletempFile.getAbsolutePath() );
                if!newFile.renameTofile ) ) {
                  throw new IOException"can not rename " + tempFile + " to " + file );
                }
            }
            else {
              throw new IOException("can not delete " + file);
            }
        }
        finally {
          tempFile.delete();
        }
  }

  protected boolean isBeingIntrumented(String className) {
    logger.verbose"checking to see if class [" + className + "] is set to be instrumented" );
    return classNames.containsclassName );
  }

  protected abstract ClassDescriptor getClassDescriptor(byte[] byecodethrows Exception;

  protected abstract ClassTransformer getClassTransformer(ClassDescriptor descriptor);

  protected class CustomFieldFilter implements FieldFilter {
    private final ClassDescriptor descriptor;

    public CustomFieldFilter(ClassDescriptor descriptor) {
      this.descriptor = descriptor;
    }

    public boolean shouldInstrumentField(String className, String fieldName) {
      if descriptor.getName().equalsclassName ) ) {
        logger.verbose"accepting transformation of field [" + className + "." + fieldName + "]" );
        return true;
      }
      else {
        logger.verbose"not accepting transformation of field [" + className + "." + fieldName + "]" );
        return false;
      }
    }

    public boolean shouldTransformFieldAccess(
        String transformingClassName,
        String fieldOwnerClassName,
        String fieldName) {
      if descriptor.getName().equalsfieldOwnerClassName ) ) {
        logger.verbose"accepting transformation of field access [" + fieldOwnerClassName + "." + fieldName + "]" );
        return true;
      }
      else if isExtended() && isBeingIntrumentedfieldOwnerClassName ) ) {
        logger.verbose"accepting extended transformation of field access [" + fieldOwnerClassName + "." + fieldName + "]" );
        return true;
      }
      else {
        logger.verbose"not accepting transformation of field access [" + fieldOwnerClassName + "." + fieldName + "]" );
        return false;
      }
    }
  }

  protected class Logger {
    public void verbose(String message) {
      if verbose ) {
        System.out.printlnmessage );
      }
      else {
        logmessage, Project.MSG_VERBOSE );
      }
    }

    public void debug(String message) {
      logmessage, Project.MSG_DEBUG );
    }

    public void info(String message) {
      logmessage, Project.MSG_INFO );
    }

    public void warn(String message) {
      logmessage, Project.MSG_WARN );
    }
  }


  private static interface ZipEntryHandler {
    public void handleEntry(ZipEntry entry, byte[] byteCodethrows Exception;
  }

  private static class ZipFileProcessor {
    private final ZipEntryHandler entryHandler;

    public ZipFileProcessor(ZipEntryHandler entryHandler) {
      this.entryHandler = entryHandler;
    }

    public void process(File filethrows Exception {
      ZipInputStream zip = new ZipInputStreamnew FileInputStreamfile ) );

      try {
        ZipEntry entry;
        while ( (entry = zip.getNextEntry()) != null ) {
          byte bytes[] = ByteCodeHelper.readByteCodezip );
          entryHandler.handleEntryentry, bytes );
          zip.closeEntry();
        }
            }
            finally {
              zip.close();
            }
    }
  }
}