Author: steve.ebersole(a)jboss.com
Date: 2009-03-03 21:20:50 -0500 (Tue, 03 Mar 2009)
New Revision: 16068
Added:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/AbstractInstrumenter.java
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/CGLIBInstrumenter.java
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/ExecutionException.java
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/Instrumenter.java
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/JavassistInstrumenter.java
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/Logger.java
core/branches/Branch_3_3/hibernate-maven-plugin/
core/branches/Branch_3_3/hibernate-maven-plugin/pom.xml
core/branches/Branch_3_3/hibernate-maven-plugin/src/
core/branches/Branch_3_3/hibernate-maven-plugin/src/main/
core/branches/Branch_3_3/hibernate-maven-plugin/src/main/java/
core/branches/Branch_3_3/hibernate-maven-plugin/src/main/java/org/
core/branches/Branch_3_3/hibernate-maven-plugin/src/main/java/org/hibernate/
core/branches/Branch_3_3/hibernate-maven-plugin/src/main/java/org/hibernate/maven/
core/branches/Branch_3_3/hibernate-maven-plugin/src/main/java/org/hibernate/maven/InstrumentationMojo.java
Modified:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/tool/instrument/BasicInstrumentationTask.java
core/branches/Branch_3_3/core/src/main/java/org/hibernate/tool/instrument/cglib/InstrumentTask.java
core/branches/Branch_3_3/core/src/main/java/org/hibernate/tool/instrument/javassist/InstrumentTask.java
core/branches/Branch_3_3/pom.xml
Log:
HHH-3279 : maven plugin (initial work)
Added:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/AbstractInstrumenter.java
===================================================================
---
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/AbstractInstrumenter.java
(rev 0)
+++
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/AbstractInstrumenter.java 2009-03-04
02:20:50 UTC (rev 16068)
@@ -0,0 +1,443 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.bytecode.buildtime;
+
+import java.util.Iterator;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+import java.util.zip.CRC32;
+import java.io.File;
+import java.io.DataInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.io.OutputStream;
+import java.io.FileOutputStream;
+
+import org.hibernate.bytecode.util.ByteCodeHelper;
+import org.hibernate.bytecode.util.ClassDescriptor;
+import org.hibernate.bytecode.util.FieldFilter;
+import org.hibernate.bytecode.ClassTransformer;
+
+/**
+ * Provides the basic templating of how instrumentation should occur.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractInstrumenter implements Instrumenter {
+ private static final int ZIP_MAGIC = 0x504B0304;
+ private static final int CLASS_MAGIC = 0xCAFEBABE;
+
+ protected final Logger logger;
+ protected final Options options;
+
+ /**
+ * Creates the basic instrumentation strategy.
+ *
+ * @param logger The bridge to the environment's logging system.
+ * @param options User-supplied options.
+ */
+ public AbstractInstrumenter(Logger logger, Options options) {
+ this.logger = logger;
+ this.options = options;
+ }
+
+ /**
+ * Given the bytecode of a java class, retrieve the descriptor for that class.
+ *
+ * @param byecode The class bytecode.
+ *
+ * @return The class's descriptor
+ *
+ * @throws Exception Indicates problems access the bytecode.
+ */
+ protected abstract ClassDescriptor getClassDescriptor(byte[] byecode) throws Exception;
+
+ /**
+ * Create class transformer for the class.
+ *
+ * @param descriptor The descriptor of the class to be instrumented.
+ * @param classNames The names of all classes to be instrumented; the
"pipeline" if you will.
+ *
+ * @return The transformer for the given class; may return null to indicate that
transformation should
+ * be skipped (ala already instrumented).
+ */
+ protected abstract ClassTransformer getClassTransformer(ClassDescriptor descriptor, Set
classNames);
+
+ /**
+ * The main instrumentation entry point. Given a set of files, perform instrumentation
on each discovered class
+ * file.
+ *
+ * @param files The files.
+ */
+ public void execute(Set files) {
+ Set classNames = new HashSet();
+
+ if ( options.performExtendedInstrumentation() ) {
+ logger.debug( "collecting class names for extended instrumentation
determination" );
+ try {
+ Iterator itr = files.iterator();
+ while ( itr.hasNext() ) {
+ final File file = ( File ) itr.next();
+ collectClassNames( file, classNames );
+ }
+ }
+ catch ( ExecutionException ee ) {
+ throw ee;
+ }
+ catch ( Exception e ) {
+ throw new ExecutionException( e );
+ }
+ }
+
+ logger.info( "starting instrumentation" );
+ try {
+ Iterator itr = files.iterator();
+ while ( itr.hasNext() ) {
+ final File file = ( File ) itr.next();
+ processFile( file, classNames );
+ }
+ }
+ catch ( ExecutionException ee ) {
+ throw ee;
+ }
+ catch ( Exception e ) {
+ throw new ExecutionException( e );
+ }
+ }
+
+ /**
+ * Extract the names of classes from file, addding them to the classNames collection.
+ * <p/>
+ * IMPL NOTE : file here may be either a class file or a jar. If a jar, all entries in
the jar file are
+ * processed.
+ *
+ * @param file The file from which to extract class metadata (descriptor).
+ * @param classNames The collected class name collection.
+ *
+ * @throws Exception indicates problems accessing the file or its contents.
+ */
+ private void collectClassNames(File file, final Set classNames) 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 );
+ }
+ }
+
+ /**
+ * Does this file represent a compiled class?
+ *
+ * @param file The file to check.
+ *
+ * @return True if the file is a class; false otherwise.
+ *
+ * @throws IOException Indicates problem access the file.
+ */
+ protected final boolean isClassFile(File file) throws IOException {
+ return checkMagic( file, CLASS_MAGIC );
+ }
+
+ /**
+ * Does this file represent a zip file of some format?
+ *
+ * @param file The file to check.
+ *
+ * @return True if the file is n archive; false otherwise.
+ *
+ * @throws IOException Indicates problem access the file.
+ */
+ 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();
+ }
+ }
+
+ /**
+ * Actually process the file by applying instrumentation transformations to any classes
it contains.
+ * <p/>
+ * Again, just like with {@link #collectClassNames} this method can handle both class
and archive files.
+ *
+ * @param file The file to process.
+ * @param classNames The 'pipeline' of classes to be processed. Only actually
populated when the user
+ * specifies to perform {@link Options#performExtendedInstrumentation() extended}
instrumentation.
+ *
+ * @throws Exception Indicates an issue either access files or applying the
transformations.
+ */
+ protected void processFile(File file, Set classNames) throws Exception {
+ if ( isClassFile( file ) ) {
+ logger.debug( "processing class file : " + file.getAbsolutePath() );
+ processClassFile( file, classNames );
+ }
+ else if ( isJarFile( file ) ) {
+ logger.debug( "processing jar file : " + file.getAbsolutePath() );
+ processJarFile( file, classNames );
+ }
+ else {
+ logger.debug( "ignoring file : " + file.getAbsolutePath() );
+ }
+ }
+
+ /**
+ * Process a class file. Delegated to from {@link #processFile} in the case of a class
file.
+ *
+ * @param file The class file to process.
+ * @param classNames The 'pipeline' of classes to be processed. Only actually
populated when the user
+ * specifies to perform {@link Options#performExtendedInstrumentation() extended}
instrumentation.
+ *
+ * @throws Exception Indicates an issue either access files or applying the
transformations.
+ */
+ protected void processClassFile(File file, Set classNames) throws Exception {
+ byte[] bytes = ByteCodeHelper.readByteCode( file );
+ ClassDescriptor descriptor = getClassDescriptor( bytes );
+ ClassTransformer transformer = getClassTransformer( descriptor, classNames );
+ if ( transformer == null ) {
+ logger.debug( "no trasformer for class file : " + file.getAbsolutePath() );
+ return;
+ }
+
+ logger.info( "processing class : " + descriptor.getName() + "; file =
" + file.getAbsolutePath() );
+ 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
+ }
+ }
+ }
+
+ /**
+ * Process an archive file. Delegated to from {@link #processFile} in the case of an
archive file.
+ *
+ * @param file The archive file to process.
+ * @param classNames The 'pipeline' of classes to be processed. Only actually
populated when the user
+ * specifies to perform {@link Options#performExtendedInstrumentation() extended}
instrumentation.
+ *
+ * @throws Exception Indicates an issue either access files or applying the
transformations.
+ */
+ protected void processJarFile(final File file, final Set classNames) throws Exception {
+ 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.debug( "starting zip 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, classNames );
+ if ( transformer == null ) {
+ logger.debug( "no transformer for zip entry : " + entry.toString()
);
+ }
+ else {
+ logger.info( "processing class : " + descriptor.getName() + ";
entry = " + file.getAbsolutePath() );
+ byteCode = transformer.transform(
+ getClass().getClassLoader(),
+ descriptor.getName(),
+ null,
+ null,
+ descriptor.getBytes()
+ );
+ }
+ }
+ else {
+ logger.debug( "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 {
+ if ( ! tempFile.delete() ) {
+ logger.info( "Unable to cleanup temporary jar file : " +
tempFile.getAbsolutePath() );
+ }
+ }
+ }
+
+ /**
+ * Allows control over what exacctly to transform.
+ */
+ protected class CustomFieldFilter implements FieldFilter {
+ private final ClassDescriptor descriptor;
+ private final Set classNames;
+
+ public CustomFieldFilter(ClassDescriptor descriptor, Set classNames) {
+ this.descriptor = descriptor;
+ this.classNames = classNames;
+ }
+
+ public boolean shouldInstrumentField(String className, String fieldName) {
+ if ( descriptor.getName().equals( className ) ) {
+ logger.trace( "accepting transformation of field [" + className +
"." + fieldName + "]" );
+ return true;
+ }
+ else {
+ logger.trace( "rejecting transformation of field [" + className +
"." + fieldName + "]" );
+ return false;
+ }
+ }
+
+ public boolean shouldTransformFieldAccess(
+ String transformingClassName,
+ String fieldOwnerClassName,
+ String fieldName) {
+ if ( descriptor.getName().equals( fieldOwnerClassName ) ) {
+ logger.trace( "accepting transformation of field access [" +
fieldOwnerClassName + "." + fieldName + "]" );
+ return true;
+ }
+ else if ( options.performExtendedInstrumentation() && classNames.contains(
fieldOwnerClassName ) ) {
+ logger.trace( "accepting extended transformation of field access [" +
fieldOwnerClassName + "." + fieldName + "]" );
+ return true;
+ }
+ else {
+ logger.trace( "rejecting transformation of field access [" +
fieldOwnerClassName + "." + fieldName + "]; caller = " +
transformingClassName );
+ return false;
+ }
+ }
+ }
+
+ /**
+ * General strategy contract for handling entries in an archive file.
+ */
+ private static interface ZipEntryHandler {
+ /**
+ * Apply strategy to the given archive entry.
+ *
+ * @param entry The archive file entry.
+ * @param byteCode
+ *
+ * @throws Exception
+ */
+ public void handleEntry(ZipEntry entry, byte[] byteCode) throws Exception;
+ }
+
+ /**
+ * Applies {@link ZipEntryHandler} strategies to the entries of an archive file.
+ */
+ 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();
+ }
+ }
+ }
+}
Added:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/CGLIBInstrumenter.java
===================================================================
---
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/CGLIBInstrumenter.java
(rev 0)
+++
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/CGLIBInstrumenter.java 2009-03-04
02:20:50 UTC (rev 16068)
@@ -0,0 +1,100 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.bytecode.buildtime;
+
+import java.util.Set;
+import java.io.ByteArrayInputStream;
+
+import org.hibernate.bytecode.util.ClassDescriptor;
+import org.hibernate.bytecode.util.BasicClassFilter;
+import org.hibernate.bytecode.ClassTransformer;
+import org.hibernate.bytecode.cglib.BytecodeProviderImpl;
+import org.hibernate.repackage.cglib.asm.ClassReader;
+import org.hibernate.repackage.cglib.core.ClassNameReader;
+import org.hibernate.repackage.cglib.transform.impl.InterceptFieldEnabled;
+
+/**
+ * Strategy for performing build-time instrumentation of persistent classes in order to
enable
+ * field-level interception using CGLIB.
+ *
+ * @author Steve Ebersole
+ * @author Gavin King
+ */
+public class CGLIBInstrumenter extends AbstractInstrumenter {
+ private static final BasicClassFilter CLASS_FILTER = new BasicClassFilter();
+
+ private final BytecodeProviderImpl provider = new BytecodeProviderImpl();
+
+ public CGLIBInstrumenter(Logger logger, Options options) {
+ super( logger, options );
+ }
+
+ protected ClassDescriptor getClassDescriptor(byte[] byecode) throws Exception {
+ return new CustomClassDescriptor( byecode );
+ }
+
+ protected ClassTransformer getClassTransformer(ClassDescriptor descriptor, Set
classNames) {
+ if ( descriptor.isInstrumented() ) {
+ logger.debug( "class [" + descriptor.getName() + "] already
instrumented" );
+ return null;
+ }
+ else {
+ return provider.getTransformer( CLASS_FILTER, new CustomFieldFilter( descriptor,
classNames ) );
+ }
+ }
+
+ private static class CustomClassDescriptor implements ClassDescriptor {
+ private final byte[] bytecode;
+ private final String name;
+ private final boolean isInstrumented;
+
+ public CustomClassDescriptor(byte[] bytecode) throws Exception {
+ this.bytecode = bytecode;
+ ClassReader reader = new ClassReader( new ByteArrayInputStream( bytecode ) );
+ String[] names = ClassNameReader.getClassInfo( reader );
+ this.name = names[0];
+ boolean instrumented = false;
+ for ( int i = 1; i < names.length; i++ ) {
+ if ( InterceptFieldEnabled.class.getName().equals( names[i] ) ) {
+ instrumented = true;
+ break;
+ }
+ }
+ this.isInstrumented = instrumented;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isInstrumented() {
+ return isInstrumented;
+ }
+
+ public byte[] getBytes() {
+ return bytecode;
+ }
+ }
+
+}
Added:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/ExecutionException.java
===================================================================
---
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/ExecutionException.java
(rev 0)
+++
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/ExecutionException.java 2009-03-04
02:20:50 UTC (rev 16068)
@@ -0,0 +1,43 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.bytecode.buildtime;
+
+/**
+ * Indicates problem performing the instrumentation execution.
+ *
+ * @author Steve Ebersole
+ */
+public class ExecutionException extends RuntimeException {
+ public ExecutionException(String message) {
+ super( message );
+ }
+
+ public ExecutionException(Throwable cause) {
+ super( cause );
+ }
+
+ public ExecutionException(String message, Throwable cause) {
+ super( message, cause );
+ }
+}
Added:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/Instrumenter.java
===================================================================
---
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/Instrumenter.java
(rev 0)
+++
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/Instrumenter.java 2009-03-04
02:20:50 UTC (rev 16068)
@@ -0,0 +1,39 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.bytecode.buildtime;
+
+import java.util.Set;
+
+/**
+ * TODO : javadoc
+ *
+ * @author Steve Ebersole
+ */
+public interface Instrumenter {
+ public void execute(Set files);
+
+ public static interface Options {
+ public boolean performExtendedInstrumentation();
+ }
+}
Added:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/JavassistInstrumenter.java
===================================================================
---
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/JavassistInstrumenter.java
(rev 0)
+++
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/JavassistInstrumenter.java 2009-03-04
02:20:50 UTC (rev 16068)
@@ -0,0 +1,98 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.bytecode.buildtime;
+
+import java.util.Set;
+import java.io.IOException;
+import java.io.DataInputStream;
+import java.io.ByteArrayInputStream;
+
+import javassist.bytecode.ClassFile;
+
+import org.hibernate.bytecode.util.ClassDescriptor;
+import org.hibernate.bytecode.util.BasicClassFilter;
+import org.hibernate.bytecode.ClassTransformer;
+import org.hibernate.bytecode.javassist.BytecodeProviderImpl;
+import org.hibernate.bytecode.javassist.FieldHandled;
+
+/**
+ * Strategy for performing build-time instrumentation of persistent classes in order to
enable
+ * field-level interception using Javassist.
+ *
+ * @author Steve Ebersole
+ * @author Muga Nishizawa
+ */
+public class JavassistInstrumenter extends AbstractInstrumenter {
+
+ private static final BasicClassFilter CLASS_FILTER = new BasicClassFilter();
+
+ private final BytecodeProviderImpl provider = new BytecodeProviderImpl();
+
+ public JavassistInstrumenter(Logger logger, Options options) {
+ super( logger, options );
+ }
+
+ protected ClassDescriptor getClassDescriptor(byte[] bytecode) throws IOException {
+ return new CustomClassDescriptor( bytecode );
+ }
+
+ protected ClassTransformer getClassTransformer(ClassDescriptor descriptor, Set
classNames) {
+ if ( descriptor.isInstrumented() ) {
+ logger.debug( "class [" + descriptor.getName() + "] already
instrumented" );
+ return null;
+ }
+ else {
+ return provider.getTransformer( CLASS_FILTER, new CustomFieldFilter( descriptor,
classNames ) );
+ }
+ }
+
+ private static class CustomClassDescriptor implements ClassDescriptor {
+ private final byte[] bytes;
+ private final ClassFile classFile;
+
+ public CustomClassDescriptor(byte[] bytes) throws IOException {
+ this.bytes = bytes;
+ this.classFile = new ClassFile( new DataInputStream( new ByteArrayInputStream( bytes )
) );
+ }
+
+ public String getName() {
+ return classFile.getName();
+ }
+
+ public boolean isInstrumented() {
+ String[] intfs = classFile.getInterfaces();
+ for ( int i = 0; i < intfs.length; i++ ) {
+ if ( FieldHandled.class.getName().equals( intfs[i] ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public byte[] getBytes() {
+ return bytes;
+ }
+ }
+
+}
Added:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/Logger.java
===================================================================
---
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/Logger.java
(rev 0)
+++
core/branches/Branch_3_3/core/src/main/java/org/hibernate/bytecode/buildtime/Logger.java 2009-03-04
02:20:50 UTC (rev 16068)
@@ -0,0 +1,42 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.bytecode.buildtime;
+
+/**
+ * Provides an abstraction for how instrumentation does logging because it is usually run
in environments (Ant/Maven)
+ * with their own logging infrastructure. This abstraction allows proper bridging.
+ *
+ * @author Steve Ebersole
+ */
+public interface Logger {
+ public void trace(String message);
+
+ public void debug(String message);
+
+ public void info(String message);
+
+ public void warn(String message);
+
+ public void error(String message);
+}
Modified:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/tool/instrument/BasicInstrumentationTask.java
===================================================================
---
core/branches/Branch_3_3/core/src/main/java/org/hibernate/tool/instrument/BasicInstrumentationTask.java 2009-03-04
02:19:29 UTC (rev 16067)
+++
core/branches/Branch_3_3/core/src/main/java/org/hibernate/tool/instrument/BasicInstrumentationTask.java 2009-03-04
02:20:50 UTC (rev 16068)
@@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
- *
*/
package org.hibernate.tool.instrument;
@@ -29,43 +28,31 @@
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 org.hibernate.bytecode.buildtime.Instrumenter;
+import org.hibernate.bytecode.buildtime.Logger;
+
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.
+ * Super class for all Hibernate instrumentation tasks. Provides the basic templating of
how instrumentation
+ * should occur; subclasses simply plug in to that process appropriately for the given
bytecode provider.
*
* @author Steve Ebersole
*/
-public abstract class BasicInstrumentationTask extends Task {
+public abstract class BasicInstrumentationTask extends Task implements
Instrumenter.Options {
- private static final int ZIP_MAGIC = 0x504B0304;
- private static final int CLASS_MAGIC = 0xCAFEBABE;
+ private final LoggerBridge logger = new LoggerBridge();
- protected final Logger logger = new Logger();
private List filesets = new ArrayList();
- private Set classNames = new HashSet();
private boolean extended;
+
+ // deprecated option...
private boolean verbose;
public void addFileset(FileSet set) {
@@ -92,32 +79,24 @@
this.verbose = verbose;
}
+ public final boolean performExtendedInstrumentation() {
+ return isExtended();
+ }
+
+ protected abstract Instrumenter buildInstrumenter(Logger logger, Instrumenter.Options
options);
+
public void execute() throws BuildException {
- if ( isExtended() ) {
- collectClassNames();
+ try {
+ buildInstrumenter( logger, this )
+ .execute( collectSpecifiedFiles() );
}
- 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 );
- }
- }
+ catch ( Throwable t ) {
+ throw new BuildException( t );
}
}
- private void collectClassNames() {
- logger.info( "collecting class names for extended instrumentation
determination" );
+ private Set collectSpecifiedFiles() {
+ HashSet files = new HashSet();
Project project = getProject();
Iterator filesets = filesets();
while ( filesets.hasNext() ) {
@@ -126,239 +105,14 @@
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 );
- }
+ files.add( new File( d, includedFiles[i] ) );
}
}
- logger.info( classNames.size() + " class(es) being checked" );
+ return files;
}
- 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 {
- logger.verbose( "processing file : " + file.toURL() );
- 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( "rejecting 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( "rejecting transformation of field access [" +
fieldOwnerClassName + "." + fieldName + "]; caller = " +
transformingClassName );
- return false;
- }
- }
- }
-
- protected class Logger {
- public void verbose(String message) {
- if ( verbose ) {
- System.out.println( message );
- }
+ protected class LoggerBridge implements Logger {
+ public void trace(String message) {
log( message, Project.MSG_VERBOSE );
}
@@ -373,34 +127,10 @@
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 error(String message) {
+ log( message, Project.MSG_ERR );
}
-
- 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();
- }
- }
}
+
}
Modified:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/tool/instrument/cglib/InstrumentTask.java
===================================================================
---
core/branches/Branch_3_3/core/src/main/java/org/hibernate/tool/instrument/cglib/InstrumentTask.java 2009-03-04
02:19:29 UTC (rev 16067)
+++
core/branches/Branch_3_3/core/src/main/java/org/hibernate/tool/instrument/cglib/InstrumentTask.java 2009-03-04
02:20:50 UTC (rev 16068)
@@ -24,18 +24,11 @@
*/
package org.hibernate.tool.instrument.cglib;
-import org.hibernate.bytecode.util.BasicClassFilter;
-import org.hibernate.bytecode.util.ClassDescriptor;
-import org.hibernate.bytecode.cglib.BytecodeProviderImpl;
-import org.hibernate.bytecode.ClassTransformer;
+import org.hibernate.bytecode.buildtime.CGLIBInstrumenter;
+import org.hibernate.bytecode.buildtime.Instrumenter;
+import org.hibernate.bytecode.buildtime.Logger;
import org.hibernate.tool.instrument.BasicInstrumentationTask;
-import org.hibernate.repackage.cglib.asm.ClassReader;
-import java.io.ByteArrayInputStream;
-
-import org.hibernate.repackage.cglib.core.ClassNameReader;
-import org.hibernate.repackage.cglib.transform.impl.InterceptFieldEnabled;
-
/**
* An Ant task for instrumenting persistent classes in order to enable
* field-level interception using CGLIB.
@@ -50,7 +43,7 @@
* required Hibernate and CGLIB libraries.
* <p/>
* And then use it like:<pre>
- * <instrument verbose="true">
+ * <instrument>
* <fileset dir="${testclasses.dir}/org/hibernate/test">
* <include name="yadda/yadda/**"/>
* ...
@@ -62,7 +55,7 @@
* <p/>
* Optionally you can chose to enable "Extended Instrumentation" if desired
* by specifying the extended attriubute on the task:<pre>
- * <instrument verbose="true" extended="true">
+ * <instrument extended="true">
* ...
* </instrument>
* </pre>
@@ -72,58 +65,7 @@
* @author Steve Ebersole
*/
public class InstrumentTask extends BasicInstrumentationTask {
-
- private static final BasicClassFilter CLASS_FILTER = new BasicClassFilter();
-
- private final BytecodeProviderImpl provider = new BytecodeProviderImpl();
-
-
- protected ClassDescriptor getClassDescriptor(byte[] byecode) throws Exception {
- return new CustomClassDescriptor( byecode );
+ protected Instrumenter buildInstrumenter(Logger logger, Instrumenter.Options options) {
+ return new CGLIBInstrumenter( logger, options );
}
-
- protected ClassTransformer getClassTransformer(ClassDescriptor descriptor) {
- if ( descriptor.isInstrumented() ) {
- logger.verbose( "class [" + descriptor.getName() + "] already
instrumented" );
- return null;
- }
- else {
- return provider.getTransformer( CLASS_FILTER, new CustomFieldFilter( descriptor ) );
- }
- }
-
- private static class CustomClassDescriptor implements ClassDescriptor {
- private final byte[] bytecode;
- private final String name;
- private final boolean isInstrumented;
-
- public CustomClassDescriptor(byte[] bytecode) throws Exception {
- this.bytecode = bytecode;
- ClassReader reader = new ClassReader( new ByteArrayInputStream( bytecode ) );
- String[] names = ClassNameReader.getClassInfo( reader );
- this.name = names[0];
- boolean instrumented = false;
- for ( int i = 1; i < names.length; i++ ) {
- if ( InterceptFieldEnabled.class.getName().equals( names[i] ) ) {
- instrumented = true;
- break;
- }
- }
- this.isInstrumented = instrumented;
- }
-
- public String getName() {
- return name;
- }
-
- public boolean isInstrumented() {
- return isInstrumented;
- }
-
- public byte[] getBytes() {
- return bytecode;
- }
- }
-
-
}
Modified:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/tool/instrument/javassist/InstrumentTask.java
===================================================================
---
core/branches/Branch_3_3/core/src/main/java/org/hibernate/tool/instrument/javassist/InstrumentTask.java 2009-03-04
02:19:29 UTC (rev 16067)
+++
core/branches/Branch_3_3/core/src/main/java/org/hibernate/tool/instrument/javassist/InstrumentTask.java 2009-03-04
02:20:50 UTC (rev 16068)
@@ -24,18 +24,10 @@
*/
package org.hibernate.tool.instrument.javassist;
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.ByteArrayInputStream;
-
-import javassist.bytecode.ClassFile;
-
+import org.hibernate.bytecode.buildtime.Instrumenter;
+import org.hibernate.bytecode.buildtime.JavassistInstrumenter;
+import org.hibernate.bytecode.buildtime.Logger;
import org.hibernate.tool.instrument.BasicInstrumentationTask;
-import org.hibernate.bytecode.util.ClassDescriptor;
-import org.hibernate.bytecode.util.BasicClassFilter;
-import org.hibernate.bytecode.ClassTransformer;
-import org.hibernate.bytecode.javassist.BytecodeProviderImpl;
-import org.hibernate.bytecode.javassist.FieldHandled;
/**
* An Ant task for instrumenting persistent classes in order to enable
@@ -73,51 +65,7 @@
* @author Steve Ebersole
*/
public class InstrumentTask extends BasicInstrumentationTask {
-
- private static final BasicClassFilter CLASS_FILTER = new BasicClassFilter();
-
- private final BytecodeProviderImpl provider = new BytecodeProviderImpl();
-
- protected ClassDescriptor getClassDescriptor(byte[] bytecode) throws IOException {
- return new CustomClassDescriptor( bytecode );
+ protected Instrumenter buildInstrumenter(Logger logger, Instrumenter.Options options) {
+ return new JavassistInstrumenter( logger, options );
}
-
- protected ClassTransformer getClassTransformer(ClassDescriptor descriptor) {
- if ( descriptor.isInstrumented() ) {
- logger.verbose( "class [" + descriptor.getName() + "] already
instrumented" );
- return null;
- }
- else {
- return provider.getTransformer( CLASS_FILTER, new CustomFieldFilter( descriptor ) );
- }
- }
-
- private static class CustomClassDescriptor implements ClassDescriptor {
- private final byte[] bytes;
- private final ClassFile classFile;
-
- public CustomClassDescriptor(byte[] bytes) throws IOException {
- this.bytes = bytes;
- this.classFile = new ClassFile( new DataInputStream( new ByteArrayInputStream( bytes )
) );
- }
-
- public String getName() {
- return classFile.getName();
- }
-
- public boolean isInstrumented() {
- String[] intfs = classFile.getInterfaces();
- for ( int i = 0; i < intfs.length; i++ ) {
- if ( FieldHandled.class.getName().equals( intfs[i] ) ) {
- return true;
- }
- }
- return false;
- }
-
- public byte[] getBytes() {
- return bytes;
- }
- }
-
}
Property changes on: core/branches/Branch_3_3/hibernate-maven-plugin
___________________________________________________________________
Name: svn:ignore
+ target
local
*.ipr
*.iws
*.iml
.classpath
.project
.nbattrs
*.log
*.properties
.clover
Added: core/branches/Branch_3_3/hibernate-maven-plugin/pom.xml
===================================================================
--- core/branches/Branch_3_3/hibernate-maven-plugin/pom.xml (rev
0)
+++ core/branches/Branch_3_3/hibernate-maven-plugin/pom.xml 2009-03-04 02:20:50 UTC (rev
16068)
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Hibernate, Relational Persistence for Idiomatic Java
+ ~
+ ~ Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
+ ~ indicated by the @author tags or express copyright attribution
+ ~ statements applied by the authors. All third-party contributions are
+ ~ distributed under license by Red Hat Middleware LLC.
+ ~
+ ~ This copyrighted material is made available to anyone wishing to use, modify,
+ ~ copy, or redistribute it subject to the terms and conditions of the GNU
+ ~ Lesser General Public License, as published by the Free Software Foundation.
+ ~
+ ~ This program is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ ~ for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+ -->
+<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>hibernate-parent</artifactId>
+ <groupId>org.hibernate</groupId>
+ <version>3.3.2-SNAPSHOT</version>
+ <relativePath>../parent/pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-maven-plugin</artifactId>
+ <packaging>mojo</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>${groupId}</groupId>
+ <artifactId>hibernate-core</artifactId>
+ <version>${version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-plugin-api</artifactId>
+ <version>2.0.9</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-project</artifactId>
+ <version>2.0.9</version>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
Added:
core/branches/Branch_3_3/hibernate-maven-plugin/src/main/java/org/hibernate/maven/InstrumentationMojo.java
===================================================================
---
core/branches/Branch_3_3/hibernate-maven-plugin/src/main/java/org/hibernate/maven/InstrumentationMojo.java
(rev 0)
+++
core/branches/Branch_3_3/hibernate-maven-plugin/src/main/java/org/hibernate/maven/InstrumentationMojo.java 2009-03-04
02:20:50 UTC (rev 16068)
@@ -0,0 +1,175 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.maven;
+
+import java.util.Iterator;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Arrays;
+import java.io.File;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.artifact.Artifact;
+import org.codehaus.plexus.util.DirectoryScanner;
+
+import org.hibernate.bytecode.buildtime.Instrumenter;
+import org.hibernate.bytecode.buildtime.Logger;
+import org.hibernate.bytecode.buildtime.JavassistInstrumenter;
+import org.hibernate.bytecode.buildtime.CGLIBInstrumenter;
+
+/**
+ * @goal instrument
+ * @phase process-classes
+ * @requiresDependencyResolution
+ *
+ * @author Steve Ebersole
+ */
+public class InstrumentationMojo extends AbstractMojo implements Instrumenter.Options {
+ /**
+ * INTERNAL : The Maven Project to which we are attached
+ *
+ * @parameter expression="${project}"
+ * @required
+ */
+ private MavenProject project;
+
+ /**
+ * Specifies the directory containing the classes to be instrumented. By default we use
the
+ * project's output directory, which in turn defaults to
<samp>${basedir}/target/classes</samp>.
+ *
+ * @parameter expression="${project.build.outputDirectory}"
+ * @required
+ */
+ private File instrumentationDirectory;
+
+ /**
+ * @parameter
+ */
+ private boolean extended;
+
+ /**
+ * @parameter
+ */
+ private String provider;
+
+ public boolean performExtendedInstrumentation() {
+ return extended;
+ }
+
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ // first, lets determine whether to apply cglib or javassist based instrumentation...
+ if ( provider == null ) {
+ provider = determineProvider();
+ if ( provider == null ) {
+ throw new MojoExecutionException( "Unable to determine provider to use" );
+ }
+ }
+
+ Instrumenter instrumenter = resolveInstrumenter( provider, new LoggingBridge() );
+ try {
+ instrumenter.execute( collectFilesToProcess() );
+ }
+ catch ( Throwable t ) {
+ throw new MojoExecutionException( "Error executing instrumentation", t );
+ }
+ }
+
+ private Set collectFilesToProcess() {
+ DirectoryScanner scanner = new DirectoryScanner();
+ scanner.setBasedir( instrumentationDirectory );
+ scanner.setIncludes( new String[] { "**/*.class" } );
+ scanner.addDefaultExcludes();
+ scanner.scan();
+ String[] includedFiles = scanner.getIncludedFiles();
+ HashSet fileSet = new HashSet( includedFiles.length + (int)(.75*includedFiles.length) +
1 );
+ fileSet.addAll( Arrays.asList( includedFiles ) );
+ return fileSet;
+ }
+
+ private Instrumenter resolveInstrumenter(String provider, Logger logger) throws
MojoExecutionException {
+ if ( "javassist".equals( provider ) ) {
+ return new JavassistInstrumenter( logger, this );
+ }
+ else if ( "cglib".equals( provider ) ) {
+ return new CGLIBInstrumenter( logger, this );
+ }
+ else {
+ throw new MojoExecutionException( "Unable to resolve provider [" + provider
+ "] to appropriate instrumenter" );
+ }
+ }
+
+ /**
+ * Determine the provider to use. Called in the cases where the user did not explicitly
specify; so we look
+ * through the dependencies for the project and decide which provider should be
applied.
+ * <p/>
+ * NOTE: this impl prefers javassist.
+ *
+ * @return The provider determined from project's dependencies.
+ */
+ private String determineProvider() {
+ if ( project.getCompileArtifacts() != null ) {
+ boolean foundCglib = false;
+ Iterator itr = project.getCompileArtifacts().iterator();
+ while ( itr.hasNext() ) {
+ final Artifact artifact = ( Artifact ) itr.next();
+ if ( "javassist".equals( artifact.getGroupId() ) &&
"javassist".equals( artifact.getArtifactId() ) ) {
+ return "javassist";
+ }
+ else if ( "org.hibernate".equals( artifact.getGroupId() )
+ && "hibernate-cglib-repack".equals( artifact.getArtifactId() ) )
{
+ foundCglib = true;
+ }
+ }
+ if ( foundCglib ) {
+ return "cglib";
+ }
+ }
+ return null;
+ }
+
+ private class LoggingBridge implements Logger {
+ public void trace(String message) {
+ getLog().debug( message );
+ }
+
+ public void debug(String message) {
+ getLog().debug( message );
+ }
+
+ public void info(String message) {
+ getLog().info( message );
+ }
+
+ public void warn(String message) {
+ getLog().warn( message );
+ }
+
+ public void error(String message) {
+ getLog().error( message );
+ }
+ }
+}
Modified: core/branches/Branch_3_3/pom.xml
===================================================================
--- core/branches/Branch_3_3/pom.xml 2009-03-04 02:19:29 UTC (rev 16067)
+++ core/branches/Branch_3_3/pom.xml 2009-03-04 02:20:50 UTC (rev 16068)
@@ -57,7 +57,8 @@
<module>testing</module>
<module>testsuite</module>
<module>tutorials</module>
-<!--
+ <module>hibernate-maven-plugin</module>
+ <!--
Need to scope bytecode providers first...
<module>bytecode-cglib</module>
<module>bytecode-javassist</module>