001: //$Id: InstrumentTask.java 10209 2006-08-03 20:35:26Z steve.ebersole@jboss.com $
002: package org.hibernate.tool.instrument.cglib;
003:
004: import org.hibernate.bytecode.util.BasicClassFilter;
005: import org.hibernate.bytecode.util.ClassDescriptor;
006: import org.hibernate.bytecode.cglib.BytecodeProviderImpl;
007: import org.hibernate.bytecode.ClassTransformer;
008: import org.hibernate.tool.instrument.BasicInstrumentationTask;
009: import org.objectweb.asm.ClassReader;
010:
011: import java.io.ByteArrayInputStream;
012:
013: import net.sf.cglib.core.ClassNameReader;
014: import net.sf.cglib.transform.impl.InterceptFieldEnabled;
015:
016: /**
017: * An Ant task for instrumenting persistent classes in order to enable
018: * field-level interception using CGLIB.
019: * <p/>
020: * In order to use this task, typically you would define a a taskdef
021: * similiar to:<pre>
022: * <taskdef name="instrument" classname="org.hibernate.tool.instrument.cglib.InstrumentTask">
023: * <classpath refid="lib.class.path"/>
024: * </taskdef>
025: * </pre>
026: * where <tt>lib.class.path</tt> is an ANT path reference containing all the
027: * required Hibernate and CGLIB libraries.
028: * <p/>
029: * And then use it like:<pre>
030: * <instrument verbose="true">
031: * <fileset dir="${testclasses.dir}/org/hibernate/test">
032: * <include name="yadda/yadda/**"/>
033: * ...
034: * </fileset>
035: * </instrument>
036: * </pre>
037: * where the nested ANT fileset includes the class you would like to have
038: * instrumented.
039: * <p/>
040: * Optionally you can chose to enable "Extended Instrumentation" if desired
041: * by specifying the extended attriubute on the task:<pre>
042: * <instrument verbose="true" extended="true">
043: * ...
044: * </instrument>
045: * </pre>
046: * See the Hibernate manual regarding this option.
047: *
048: * @author Gavin King
049: * @author Steve Ebersole
050: */
051: public class InstrumentTask extends BasicInstrumentationTask {
052:
053: private static final BasicClassFilter CLASS_FILTER = new BasicClassFilter();
054:
055: private final BytecodeProviderImpl provider = new BytecodeProviderImpl();
056:
057: protected ClassDescriptor getClassDescriptor(byte[] byecode)
058: throws Exception {
059: return new CustomClassDescriptor(byecode);
060: }
061:
062: protected ClassTransformer getClassTransformer(
063: ClassDescriptor descriptor) {
064: if (descriptor.isInstrumented()) {
065: logger.verbose("class [" + descriptor.getName()
066: + "] already instrumented");
067: return null;
068: } else {
069: return provider.getTransformer(CLASS_FILTER,
070: new CustomFieldFilter(descriptor));
071: }
072: }
073:
074: private static class CustomClassDescriptor implements
075: ClassDescriptor {
076: private final byte[] bytecode;
077: private final String name;
078: private final boolean isInstrumented;
079:
080: public CustomClassDescriptor(byte[] bytecode) throws Exception {
081: this .bytecode = bytecode;
082: ClassReader reader = new ClassReader(
083: new ByteArrayInputStream(bytecode));
084: String[] names = ClassNameReader.getClassInfo(reader);
085: this .name = names[0];
086: boolean instrumented = false;
087: for (int i = 1; i < names.length; i++) {
088: if (InterceptFieldEnabled.class.getName().equals(
089: names[i])) {
090: instrumented = true;
091: break;
092: }
093: }
094: this .isInstrumented = instrumented;
095: }
096:
097: public String getName() {
098: return name;
099: }
100:
101: public boolean isInstrumented() {
102: return isInstrumented;
103: }
104:
105: public byte[] getBytes() {
106: return bytecode;
107: }
108: }
109:
110: }
|