[jboss-svn-commits] JBL Code SVN: r6524 - in labs/jbossrules/trunk/drools-core/src: main/java/org/drools/base test/java/org/drools test/java/org/drools/base
jboss-svn-commits at lists.jboss.org
jboss-svn-commits at lists.jboss.org
Mon Oct 2 12:39:15 EDT 2006
Author: tirelli
Date: 2006-10-02 12:39:02 -0400 (Mon, 02 Oct 2006)
New Revision: 6524
Added:
labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ShadowProxy.java
labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ShadowProxyFactory.java
labs/jbossrules/trunk/drools-core/src/test/java/org/drools/base/ShadowProxyFactoryTest.java
Modified:
labs/jbossrules/trunk/drools-core/src/test/java/org/drools/Cheese.java
Log:
JBRULES-44:
* Added ShadowProxy interface
* Added ShadowProxy factory
* Added unit test
Added: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ShadowProxy.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ShadowProxy.java 2006-10-02 16:26:11 UTC (rev 6523)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ShadowProxy.java 2006-10-02 16:39:02 UTC (rev 6524)
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2005 JBoss Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.drools.base;
+
+/**
+ * A markup interface for shadow fact proxies
+ */
+public interface ShadowProxy {
+
+ public void resetProxy();
+
+}
Property changes on: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ShadowProxy.java
___________________________________________________________________
Name: svn:executable
+ *
Name: svn:keywords
+ id author date revision
Name: svn:eol-style
+ native
Added: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ShadowProxyFactory.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ShadowProxyFactory.java 2006-10-02 16:26:11 UTC (rev 6523)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ShadowProxyFactory.java 2006-10-02 16:39:02 UTC (rev 6524)
@@ -0,0 +1,404 @@
+/*
+ * Copyright 2005 JBoss Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.drools.base;
+
+import java.lang.reflect.Method;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.drools.RuntimeDroolsException;
+import org.drools.asm.ClassWriter;
+import org.drools.asm.FieldVisitor;
+import org.drools.asm.Label;
+import org.drools.asm.MethodVisitor;
+import org.drools.asm.Opcodes;
+import org.drools.asm.Type;
+import org.drools.util.asm.ClassFieldInspector;
+
+/**
+ * A factory for ShadowProxy classes
+ */
+public class ShadowProxyFactory {
+ private static final String BASE_INTERFACE = Type.getInternalName( ShadowProxy.class );
+
+ private static final String FIELD_NAME_PREFIX = "__";
+
+ private static final String FIELD_SET_FLAG = "IsSet";
+
+ private static final String DELEGATE_FIELD_NAME = "__delegate";
+
+ public static Class getProxy(final Class clazz) {
+ try {
+ final ClassFieldInspector inspector = new ClassFieldInspector( clazz );
+ final String className = Type.getInternalName( clazz ) + "ShadowProxy";
+ // generating byte array to create target class
+ final byte[] bytes = dump( clazz,
+ inspector,
+ className,
+ clazz.isInterface() );
+ // use bytes to get a class
+ final ByteArrayClassLoader classLoader = new ByteArrayClassLoader( Thread.currentThread().getContextClassLoader() );
+ final Class newClass = classLoader.defineClass( className.replace( '/',
+ '.' ),
+ bytes );
+ return newClass;
+ } catch ( final Exception e ) {
+ throw new RuntimeDroolsException( e );
+ }
+ }
+
+ protected static byte[] dump(final Class clazz,
+ final ClassFieldInspector inspector,
+ final String className,
+ final boolean isInterface) throws Exception {
+
+ final ClassWriter cw = new ClassWriter( true );
+
+ buildClassHeader( clazz,
+ className,
+ cw );
+
+ buildField( DELEGATE_FIELD_NAME,
+ Type.getDescriptor( clazz ),
+ cw );
+ Map fieldNames = inspector.getFieldNames();
+ Map fieldTypes = inspector.getFieldTypes();
+ Map fieldGetters = inspector.getGetterMethods();
+ fieldNames.remove( "class" );
+ fieldTypes.remove( "class" );
+ fieldGetters.remove( "class" );
+ for ( Iterator it = fieldNames.keySet().iterator(); it.hasNext(); ) {
+ String fieldName = (String) it.next();
+ buildField( FIELD_NAME_PREFIX + fieldName,
+ Type.getDescriptor( (Class) fieldTypes.get( fieldName ) ),
+ cw );
+ buildField( FIELD_NAME_PREFIX + fieldName + FIELD_SET_FLAG,
+ Type.BOOLEAN_TYPE.getDescriptor(),
+ cw );
+ buildGetMethod( FIELD_NAME_PREFIX + fieldName,
+ (Class) fieldTypes.get( fieldName ),
+ FIELD_NAME_PREFIX + fieldName + FIELD_SET_FLAG,
+ (Method) fieldGetters.get( fieldName ),
+ className,
+ clazz,
+ cw );
+ }
+
+ buildConstructor( clazz,
+ className,
+ cw );
+
+ buildResetProxyMethod( fieldTypes,
+ className,
+ cw );
+
+ return cw.toByteArray();
+ }
+
+ /**
+ * Builds the shadow proxy class header
+ *
+ * @param clazz The class to build shadow proxy for
+ * @param className The shadow proxy class name
+ * @param cw
+ */
+ protected static void buildClassHeader(final Class clazz,
+ final String className,
+ final ClassWriter cw) {
+ cw.visit( Opcodes.V1_2,
+ Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER,
+ className,
+ null,
+ Type.getInternalName( clazz ),
+ new String[]{BASE_INTERFACE} );
+
+ cw.visitSource( null,
+ null );
+ }
+
+ /**
+ * Creates the field defined by the given FieldDefinition
+ *
+ * @param cw
+ * @param fieldDef
+ */
+ protected static void buildField(String name,
+ String type,
+ ClassWriter cw) {
+ FieldVisitor fv;
+ fv = cw.visitField( Opcodes.ACC_PRIVATE,
+ name,
+ type,
+ null,
+ null );
+ fv.visitEnd();
+ }
+
+ /**
+ * Creates a constructor for the shadow proxy receiving
+ * the actual delegate class as parameter
+ *
+ * @param originalClassName
+ * @param className
+ * @param cw
+ */
+ private static void buildConstructor(final Class clazz,
+ final String className,
+ final ClassWriter cw) {
+ MethodVisitor mv;
+ {
+ mv = cw.visitMethod( Opcodes.ACC_PUBLIC,
+ "<init>",
+ Type.getMethodDescriptor( Type.VOID_TYPE,
+ new Type[]{Type.getType( clazz )} ),
+ null,
+ null );
+ mv.visitCode();
+
+ // super();
+ Label l0 = new Label();
+ mv.visitLabel( l0 );
+ mv.visitLineNumber( 41,
+ l0 );
+ mv.visitVarInsn( Opcodes.ALOAD,
+ 0 );
+ mv.visitMethodInsn( Opcodes.INVOKESPECIAL,
+ Type.getInternalName( clazz ),
+ "<init>",
+ Type.getMethodDescriptor( Type.VOID_TYPE,
+ new Type[]{} ) );
+ // this.delegate = delegate
+ Label l1 = new Label();
+ mv.visitLabel( l1 );
+ mv.visitLineNumber( 42,
+ l1 );
+ mv.visitVarInsn( Opcodes.ALOAD,
+ 0 );
+ mv.visitVarInsn( Opcodes.ALOAD,
+ 1 );
+ mv.visitFieldInsn( Opcodes.PUTFIELD,
+ className,
+ DELEGATE_FIELD_NAME,
+ Type.getDescriptor( clazz ) );
+
+ // return
+ Label l2 = new Label();
+ mv.visitLabel( l2 );
+ mv.visitLineNumber( 43,
+ l2 );
+ mv.visitInsn( Opcodes.RETURN );
+
+ Label l3 = new Label();
+ mv.visitLabel( l3 );
+ mv.visitLocalVariable( "this",
+ "L" + className + ";",
+ null,
+ l0,
+ l3,
+ 0 );
+ mv.visitLocalVariable( DELEGATE_FIELD_NAME,
+ Type.getDescriptor( clazz ),
+ null,
+ l0,
+ l3,
+ 1 );
+ mv.visitMaxs( 0,
+ 0 );
+ mv.visitEnd();
+ }
+ }
+
+ /**
+ * Creates the proxy reader method for the given method
+ *
+ * @param fieldName
+ * @param fieldFlag
+ * @param method
+ * @param cw
+ */
+ protected static void buildGetMethod(String fieldName,
+ Class fieldType,
+ String fieldFlag,
+ Method method,
+ String className,
+ Class clazz,
+ ClassWriter cw) {
+ // method signature
+ Class[] exceptionTypes = method.getExceptionTypes();
+ String[] exceptions = new String[exceptionTypes.length];
+ for ( int i = 0; i < exceptions.length; i++ ) {
+ exceptions[i] = Type.getInternalName( exceptionTypes[i] );
+ }
+ MethodVisitor mv = cw.visitMethod( Opcodes.ACC_PUBLIC,
+ method.getName(),
+ Type.getMethodDescriptor( method ),
+ null,
+ exceptions );
+ mv.visitCode();
+
+ // if ( ! _fieldIsSet ) {
+ Label l0 = new Label();
+ mv.visitLabel( l0 );
+ mv.visitVarInsn( Opcodes.ALOAD,
+ 0 );
+ mv.visitFieldInsn( Opcodes.GETFIELD,
+ className,
+ fieldFlag,
+ Type.BOOLEAN_TYPE.getDescriptor() );
+ Label l1 = new Label();
+ mv.visitJumpInsn( Opcodes.IFNE,
+ l1 );
+
+ // __field = this.delegate.method();
+ Label l2 = new Label();
+ mv.visitLabel( l2 );
+ mv.visitVarInsn( Opcodes.ALOAD,
+ 0 );
+ mv.visitVarInsn( Opcodes.ALOAD,
+ 0 );
+ mv.visitFieldInsn( Opcodes.GETFIELD,
+ className,
+ DELEGATE_FIELD_NAME,
+ Type.getDescriptor( clazz ) );
+ mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
+ Type.getInternalName( clazz ),
+ method.getName(),
+ Type.getMethodDescriptor( method ) );
+ mv.visitFieldInsn( Opcodes.PUTFIELD,
+ className,
+ fieldName,
+ Type.getDescriptor( fieldType ) );
+
+ // __fieldIsSet = true;
+ Label l3 = new Label();
+ mv.visitLabel( l3 );
+ mv.visitVarInsn( Opcodes.ALOAD,
+ 0 );
+ mv.visitInsn( Opcodes.ICONST_1 );
+ mv.visitFieldInsn( Opcodes.PUTFIELD,
+ className,
+ fieldFlag,
+ Type.BOOLEAN_TYPE.getDescriptor() );
+
+ // }
+ // return __field;
+ mv.visitLabel( l1 );
+ mv.visitVarInsn( Opcodes.ALOAD,
+ 0 );
+ mv.visitFieldInsn( Opcodes.GETFIELD,
+ className,
+ fieldName,
+ Type.getDescriptor( fieldType ) );
+ mv.visitInsn( Type.getType( fieldType ).getOpcode( Opcodes.IRETURN ) );
+
+ // local variables table
+ Label l4 = new Label();
+ mv.visitLabel( l4 );
+ mv.visitLocalVariable( "this",
+ "L" + className + ";",
+ null,
+ l0,
+ l4,
+ 0 );
+ mv.visitMaxs( 0,
+ 0 );
+ mv.visitEnd();
+ }
+
+ protected static void buildResetProxyMethod(Map fieldTypes,
+ String className,
+ ClassWriter cw) {
+ MethodVisitor mv = cw.visitMethod( Opcodes.ACC_PUBLIC,
+ "resetProxy",
+ Type.getMethodDescriptor( Type.VOID_TYPE,
+ new Type[] { } ),
+ null,
+ null );
+ mv.visitCode();
+ Label l0 = new Label();
+ mv.visitLabel( l0 );
+ for ( Iterator it = fieldTypes.entrySet().iterator(); it.hasNext(); ) {
+ Map.Entry entry = (Map.Entry) it.next();
+ String fieldName = FIELD_NAME_PREFIX + entry.getKey();
+ String fieldFlag = fieldName + FIELD_SET_FLAG;
+ Class fieldType = (Class) entry.getValue();
+ Label l1 = new Label();
+ mv.visitLabel( l1 );
+ mv.visitVarInsn( Opcodes.ALOAD,
+ 0 );
+ if( fieldType.isPrimitive() ) {
+ if ( fieldType.equals( Long.TYPE ) ) {
+ mv.visitInsn( Opcodes.LCONST_0 );
+ } else if ( fieldType.equals( Double.TYPE ) ) {
+ mv.visitInsn( Opcodes.DCONST_0 );
+ } else if ( fieldType.equals( Float.TYPE ) ) {
+ mv.visitInsn( Opcodes.FCONST_0 );
+ } else {
+ mv.visitInsn( Opcodes.ICONST_0 );
+ }
+ } else {
+ mv.visitInsn( Opcodes.ACONST_NULL );
+ }
+ mv.visitFieldInsn( Opcodes.PUTFIELD,
+ className,
+ fieldName,
+ Type.getDescriptor( fieldType ) );
+ Label l2 = new Label();
+ mv.visitLabel( l2 );
+ mv.visitVarInsn( Opcodes.ALOAD,
+ 0 );
+ mv.visitInsn( Opcodes.ICONST_0 );
+ mv.visitFieldInsn( Opcodes.PUTFIELD,
+ className,
+ fieldFlag,
+ Type.BOOLEAN_TYPE.getDescriptor() );
+ }
+ Label l4 = new Label();
+ mv.visitLabel( l4 );
+ mv.visitInsn( Opcodes.RETURN );
+ Label l5 = new Label();
+ mv.visitLabel( l5 );
+ mv.visitLocalVariable( "this",
+ "L" + className + ";",
+ null,
+ l0,
+ l5,
+ 0 );
+ mv.visitMaxs( 0,
+ 0 );
+ mv.visitEnd();
+ }
+
+ /**
+ * Simple classloader
+ * @author Michael Neale
+ */
+ static class ByteArrayClassLoader extends ClassLoader {
+ public ByteArrayClassLoader(final ClassLoader parent) {
+ super( parent );
+ }
+
+ public Class defineClass(final String name,
+ final byte[] bytes) {
+ return defineClass( name,
+ bytes,
+ 0,
+ bytes.length );
+ }
+ }
+
+}
Property changes on: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ShadowProxyFactory.java
___________________________________________________________________
Name: svn:executable
+ *
Name: svn:keywords
+ id author date revision
Name: svn:eol-style
+ native
Modified: labs/jbossrules/trunk/drools-core/src/test/java/org/drools/Cheese.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/test/java/org/drools/Cheese.java 2006-10-02 16:26:11 UTC (rev 6523)
+++ labs/jbossrules/trunk/drools-core/src/test/java/org/drools/Cheese.java 2006-10-02 16:39:02 UTC (rev 6524)
@@ -24,6 +24,9 @@
private String type;
private int price;
+
+ public Cheese() {
+ }
public Cheese(final String type,
final int price) {
Added: labs/jbossrules/trunk/drools-core/src/test/java/org/drools/base/ShadowProxyFactoryTest.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/test/java/org/drools/base/ShadowProxyFactoryTest.java 2006-10-02 16:26:11 UTC (rev 6523)
+++ labs/jbossrules/trunk/drools-core/src/test/java/org/drools/base/ShadowProxyFactoryTest.java 2006-10-02 16:39:02 UTC (rev 6524)
@@ -0,0 +1,62 @@
+package org.drools.base;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.drools.Cheese;
+
+public class ShadowProxyFactoryTest extends TestCase {
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testGetProxy() {
+ try {
+ // creating original object
+ String originalType = "stilton";
+ int originalPrice = 15;
+ Cheese cheese = new Cheese(originalType, originalPrice);
+
+ // creating proxy
+ Class proxy = ShadowProxyFactory.getProxy( Cheese.class );
+ Cheese cheeseProxy = (Cheese) proxy.getConstructor( new Class[] { Cheese.class } ).newInstance( new Object[] { cheese } );
+
+ // proxy is proxying the values
+ Assert.assertEquals( originalType, cheeseProxy.getType() );
+ Assert.assertEquals( originalPrice, cheeseProxy.getPrice() );
+
+ // changing original values
+ String actualType = "rotten stilton";
+ int actualPrice = 1;
+ cheese.setType( actualType );
+ cheese.setPrice( actualPrice );
+
+ // proxy does not see changes
+ Assert.assertEquals( actualType, cheese.getType() );
+ Assert.assertFalse( actualType.equals( cheeseProxy.getType() ) );
+ Assert.assertEquals( originalType, cheeseProxy.getType() );
+ Assert.assertEquals( actualPrice, cheese.getPrice() );
+ Assert.assertFalse( actualPrice == cheeseProxy.getPrice() );
+ Assert.assertEquals( originalPrice, cheeseProxy.getPrice() );
+
+ // reseting proxy
+ ((ShadowProxy) cheeseProxy).resetProxy();
+
+ // now proxy see changes
+ Assert.assertEquals( actualType, cheese.getType() );
+ Assert.assertEquals( actualType, cheeseProxy.getType() );
+ Assert.assertFalse( originalType.equals( cheeseProxy.getType() ) );
+ Assert.assertEquals( actualPrice, cheese.getPrice() );
+ Assert.assertEquals( actualPrice, cheeseProxy.getPrice() );
+ Assert.assertFalse( originalPrice == cheeseProxy.getPrice() );
+
+ } catch ( Exception e ) {
+ fail("Error: "+e.getMessage());
+ }
+ }
+}
Property changes on: labs/jbossrules/trunk/drools-core/src/test/java/org/drools/base/ShadowProxyFactoryTest.java
___________________________________________________________________
Name: svn:executable
+ *
Name: svn:keywords
+ id author date revision
Name: svn:eol-style
+ native
More information about the jboss-svn-commits
mailing list