All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.tool.instrument.BasicInstrumentationTask Maven / Gradle / Ivy

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.add( set );
	}

	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.getDirectoryScanner( project );
			String[] includedFiles = ds.getIncludedFiles();
			File d = fs.getDir( project );
			for ( int i = 0; i < includedFiles.length; ++i ) {
				File file = new File( d, includedFiles[i] );
				try {
					processFile( file );
				}
				catch ( Exception e ) {
					throw new BuildException( e );
				}
			}
		}
	}

	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.getDirectoryScanner( project );
			String[] includedFiles = ds.getIncludedFiles();
			File d = fs.getDir( project );
			for ( int i = 0; i < includedFiles.length; ++i ) {
				File file = new File( d, includedFiles[i] );
				try {
					collectClassNames( file );
				}
				catch ( Exception e ) {
					throw new BuildException( e );
				}
			}
		}
		logger.info( classNames.size() + " class(es) being checked" );
	}

	private void collectClassNames(File file) throws Exception {
	    if ( isClassFile( file ) ) {
			byte[] bytes = ByteCodeHelper.readByteCode( file );
			ClassDescriptor descriptor = getClassDescriptor( bytes );
		    classNames.add( descriptor.getName() );
	    }
	    else if ( isJarFile( file ) ) {
		    ZipEntryHandler collector = new ZipEntryHandler() {
			    public void handleEntry(ZipEntry entry, byte[] byteCode) throws Exception {
					if ( !entry.isDirectory() ) {
						// see if the entry represents a class file
						DataInputStream din = new DataInputStream( new ByteArrayInputStream( byteCode ) );
						if ( din.readInt() == CLASS_MAGIC ) {
				            classNames.add( getClassDescriptor( byteCode ).getName() );
						}
					}
			    }
		    };
		    ZipFileProcessor processor = new ZipFileProcessor( collector );
		    processor.process( file );
	    }
	}

	protected void processFile(File file) throws Exception {
	    if ( isClassFile( file ) ) {
	        processClassFile(file);
	    }
	    else if ( isJarFile( file ) ) {
	        processJarFile(file);
	    }
	    else {
		    logger.verbose( "ignoring " + file.toURL() );

	    }
	}

	protected final boolean isClassFile(File file) throws IOException {
        return checkMagic( file, CLASS_MAGIC );
    }

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

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

	protected void processClassFile(File file) throws Exception {
		logger.verbose( "Starting class file : " + file.toURL() );
		byte[] bytes = ByteCodeHelper.readByteCode( file );
		ClassDescriptor descriptor = getClassDescriptor( bytes );
		ClassTransformer transformer = getClassTransformer( descriptor );
		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 FileOutputStream( file );
		try {
			out.write( transformedBytes );
			out.flush();
		}
		finally {
			try {
				out.close();
			}
			catch ( IOException ignore) {
				// intentionally empty
			}
		}
	}

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

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

        try {
			FileOutputStream fout = new FileOutputStream( tempFile, false );
			try {
				final ZipOutputStream out = new ZipOutputStream( fout );
				ZipEntryHandler transformer = new ZipEntryHandler() {
					public void handleEntry(ZipEntry entry, byte[] byteCode) throws Exception {
								logger.verbose( "starting entry : " + entry.toString() );
								if ( !entry.isDirectory() ) {
									// see if the entry represents a class file
									DataInputStream din = new DataInputStream( new ByteArrayInputStream( byteCode ) );
									if ( din.readInt() == CLASS_MAGIC ) {
										ClassDescriptor descriptor = getClassDescriptor( byteCode );
										ClassTransformer transformer = getClassTransformer( descriptor );
										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 ZipEntry( entry.getName() );
								outEntry.setMethod( entry.getMethod() );
								outEntry.setComment( entry.getComment() );
								outEntry.setSize( byteCode.length );

								if ( outEntry.getMethod() == ZipEntry.STORED ){
									CRC32 crc = new CRC32();
									crc.update( byteCode );
									outEntry.setCrc( crc.getValue() );
									outEntry.setCompressedSize( byteCode.length );
								}
								out.putNextEntry( outEntry );
								out.write( byteCode );
								out.closeEntry();
					}
				};
				ZipFileProcessor processor = new ZipFileProcessor( transformer );
				processor.process( file );
				out.close();
			}
			finally{
				fout.close();
			}

            if ( file.delete() ) {
	            File newFile = new File( tempFile.getAbsolutePath() );
                if( !newFile.renameTo( file ) ) {
	                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.contains( className );
	}

	protected abstract ClassDescriptor getClassDescriptor(byte[] byecode) throws 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().equals( className ) ) {
				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().equals( fieldOwnerClassName ) ) {
				logger.verbose( "accepting transformation of field access [" + fieldOwnerClassName + "." + fieldName + "]" );
				return true;
			}
			else if ( isExtended() && isBeingIntrumented( fieldOwnerClassName ) ) {
				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.println( message );
			}
			else {
				log( message, Project.MSG_VERBOSE );
			}
		}

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

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

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


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

	private static class ZipFileProcessor {
		private final ZipEntryHandler entryHandler;

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

		public void process(File file) throws Exception {
			ZipInputStream zip = new ZipInputStream( new FileInputStream( file ) );

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




© 2015 - 2025 Weber Informatics LLC | Privacy Policy