You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polygene.apache.org by ni...@apache.org on 2016/12/17 10:27:48 UTC
[12/81] [abbrv] [partial] zest-java git commit: ZEST-195 ;
Replace all "zest" with "polygene"
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/FragmentClassLoader.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/FragmentClassLoader.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/FragmentClassLoader.java
new file mode 100644
index 0000000..774bea7
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/FragmentClassLoader.java
@@ -0,0 +1,847 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.polygene.runtime.composite;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.polygene.api.entity.Lifecycle;
+import org.apache.polygene.api.mixin.Initializable;
+import org.apache.polygene.api.util.Classes;
+import org.apache.polygene.api.util.Methods;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import static org.apache.polygene.api.util.Classes.interfacesOf;
+import static org.objectweb.asm.Opcodes.AASTORE;
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ACC_STATIC;
+import static org.objectweb.asm.Opcodes.ACC_SUPER;
+import static org.objectweb.asm.Opcodes.ACONST_NULL;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.ANEWARRAY;
+import static org.objectweb.asm.Opcodes.ARETURN;
+import static org.objectweb.asm.Opcodes.ASTORE;
+import static org.objectweb.asm.Opcodes.ATHROW;
+import static org.objectweb.asm.Opcodes.BIPUSH;
+import static org.objectweb.asm.Opcodes.CHECKCAST;
+import static org.objectweb.asm.Opcodes.DLOAD;
+import static org.objectweb.asm.Opcodes.DRETURN;
+import static org.objectweb.asm.Opcodes.DUP;
+import static org.objectweb.asm.Opcodes.FLOAD;
+import static org.objectweb.asm.Opcodes.FRETURN;
+import static org.objectweb.asm.Opcodes.GETFIELD;
+import static org.objectweb.asm.Opcodes.GETSTATIC;
+import static org.objectweb.asm.Opcodes.GOTO;
+import static org.objectweb.asm.Opcodes.ICONST_0;
+import static org.objectweb.asm.Opcodes.ICONST_1;
+import static org.objectweb.asm.Opcodes.ICONST_2;
+import static org.objectweb.asm.Opcodes.ICONST_3;
+import static org.objectweb.asm.Opcodes.ICONST_4;
+import static org.objectweb.asm.Opcodes.ICONST_5;
+import static org.objectweb.asm.Opcodes.ILOAD;
+import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.INVOKESTATIC;
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+import static org.objectweb.asm.Opcodes.IRETURN;
+import static org.objectweb.asm.Opcodes.LLOAD;
+import static org.objectweb.asm.Opcodes.LRETURN;
+import static org.objectweb.asm.Opcodes.POP;
+import static org.objectweb.asm.Opcodes.PUTSTATIC;
+import static org.objectweb.asm.Opcodes.RETURN;
+import static org.objectweb.asm.Type.getInternalName;
+
+/**
+ * Generate subclasses of mixins/modifiers that implement all interfaces not in the class itself
+ * and which delegates those calls to a given composite invoker.
+ */
+@SuppressWarnings( "raw" )
+public class FragmentClassLoader
+ extends ClassLoader
+{
+ private static final int JDK_VERSION;
+ public static final String GENERATED_POSTFIX = "_Stub";
+
+ static
+ {
+ String jdkString = System.getProperty( "java.specification.version" );
+ switch( jdkString )
+ {
+ case "1.8":
+ JDK_VERSION = Opcodes.V1_8;
+ break;
+ case "1.7":
+ default:
+ JDK_VERSION = Opcodes.V1_7;
+ break;
+ }
+ }
+
+ public FragmentClassLoader( ClassLoader parent )
+ {
+ super( parent );
+ }
+
+ @Override
+ protected Class findClass( String name )
+ throws ClassNotFoundException
+ {
+ if( name.endsWith( GENERATED_POSTFIX ) )
+ {
+ Class baseClass;
+ String baseName = name.substring( 0, name.length() - 5 );
+ try
+ {
+ baseClass = loadClass( baseName );
+ }
+ catch( ClassNotFoundException e )
+ {
+ // Try replacing the last _ with $
+ while( true )
+ {
+ int idx = baseName.lastIndexOf( "_" );
+ if( idx != -1 )
+ {
+ baseName = baseName.substring( 0, idx ) + "$" + baseName.substring( idx + 1 );
+ try
+ {
+ baseClass = loadClass( baseName );
+ break;
+ }
+ catch( ClassNotFoundException e1 )
+ {
+ // Try again
+ }
+ }
+ else
+ {
+ throw e;
+ }
+ }
+ }
+ // To Allow JDK classes to be composed.
+ if( name.startsWith( "java." ) )
+ {
+ name = "polygene." + name;
+ }
+
+ byte[] b = generateClass( name, baseClass );
+ return defineClass( name, b, 0, b.length, baseClass.getProtectionDomain() );
+ }
+
+ // Try the classloader of this classloader -> get classes in Polygene such as CompositeInvoker
+ return getClass().getClassLoader().loadClass( name );
+ }
+
+ public byte[] generateClass( String name, Class baseClass )
+ throws ClassNotFoundException
+ {
+ String classSlash = name.replace( '.', '/' );
+ String baseClassSlash = getInternalName( baseClass );
+
+ ClassWriter cw = new ClassWriter( ClassWriter.COMPUTE_MAXS );
+
+ // Class definition start
+ cw.visit( JDK_VERSION, ACC_PUBLIC + ACC_SUPER, classSlash, null, baseClassSlash, null );
+
+ // Composite reference
+ {
+ cw.visitField( ACC_PUBLIC, "_instance", "Lorg/apache/polygene/api/composite/CompositeInvoker;", null, null )
+ .visitEnd();
+ }
+
+ // Static Method references
+ boolean hasProxyMethods = false;
+ {
+ int idx = 1;
+ for( Method method : baseClass.getMethods() )
+ {
+ if( isOverridden( method, baseClass ) )
+ {
+ cw.visitField( ACC_PRIVATE + ACC_STATIC, "m" + idx++, "Ljava/lang/reflect/Method;", null, null )
+ .visitEnd();
+ hasProxyMethods = true;
+ }
+ }
+ }
+
+ // Constructors
+ for( Constructor constructor : baseClass.getDeclaredConstructors() )
+ {
+ if( Modifier.isPublic( constructor.getModifiers() ) || Modifier.isProtected( constructor.getModifiers() ) )
+ {
+ String desc = org.objectweb.asm.commons.Method.getMethod( constructor ).getDescriptor();
+ MethodVisitor mv = cw.visitMethod( ACC_PUBLIC, "<init>", desc, null, null );
+ mv.visitCode();
+ mv.visitVarInsn( ALOAD, 0 );
+
+ int idx = 1;
+ for( Class aClass : constructor.getParameterTypes() )
+ {
+ final int opcode;
+ if( aClass.equals( Integer.TYPE ) )
+ {
+ opcode = ILOAD;
+ }
+ else if( aClass.equals( Long.TYPE ) )
+ {
+ opcode = LLOAD;
+ }
+ else if( aClass.equals( Float.TYPE ) )
+ {
+ opcode = FLOAD;
+ }
+ else if( aClass.equals( Double.TYPE ) )
+ {
+ opcode = DLOAD;
+ }
+ else
+ {
+ opcode = ALOAD;
+ }
+ mv.visitVarInsn( opcode, idx++ );
+ }
+
+ mv.visitMethodInsn( INVOKESPECIAL, baseClassSlash, "<init>", desc, false );
+ mv.visitInsn( RETURN );
+ mv.visitMaxs( idx, idx );
+ mv.visitEnd();
+ }
+ }
+
+ // Overloaded and unimplemented methods
+ if( hasProxyMethods )
+ {
+ Method[] methods = baseClass.getMethods();
+ int idx = 0;
+ List<Label> exceptionLabels = new ArrayList<>();
+ for( Method method : methods )
+ {
+ if( isOverridden( method, baseClass ) )
+ {
+ idx++;
+ String methodName = method.getName();
+ String desc = org.objectweb.asm.commons.Method.getMethod( method ).getDescriptor();
+
+ String[] exceptions = null;
+ {
+ MethodVisitor mv = cw.visitMethod( ACC_PUBLIC, methodName, desc, null, null );
+ if( isInternalPolygeneMethod( method, baseClass ) )
+ {
+ // generate a NoOp method...
+ mv.visitInsn( RETURN );
+ }
+ else
+ {
+ Label endLabel = null; // Use this if return type is void
+ if( method.getExceptionTypes().length > 0 )
+ {
+ exceptions = new String[ method.getExceptionTypes().length ];
+ for( int i = 0; i < method.getExceptionTypes().length; i++ )
+ {
+ Class<?> aClass = method.getExceptionTypes()[ i ];
+ exceptions[ i ] = getInternalName( aClass );
+ }
+ }
+ mv.visitCode();
+ Label l0 = new Label();
+ Label l1 = new Label();
+
+ exceptionLabels.clear();
+ for( Class<?> declaredException : method.getExceptionTypes() )
+ {
+ Label ld = new Label();
+ mv.visitTryCatchBlock( l0, l1, ld, getInternalName( declaredException ) );
+ exceptionLabels.add( ld ); // Reuse this further down for the catch
+ }
+
+ Label lruntime = new Label();
+ mv.visitTryCatchBlock( l0, l1, lruntime, "java/lang/RuntimeException" );
+ Label lerror = new Label();
+ mv.visitTryCatchBlock( l0, l1, lerror, "java/lang/Throwable" );
+
+ mv.visitLabel( l0 );
+ mv.visitVarInsn( ALOAD, 0 );
+ mv.visitFieldInsn( GETFIELD, classSlash, "_instance",
+ "Lorg/apache/polygene/api/composite/CompositeInvoker;" );
+ mv.visitFieldInsn( GETSTATIC, classSlash, "m" + idx, "Ljava/lang/reflect/Method;" );
+
+ int paramCount = method.getParameterTypes().length;
+ int stackIdx = 0;
+ if( paramCount == 0 )
+ {
+ // Send in null as parameter
+ mv.visitInsn( ACONST_NULL );
+ }
+ else
+ {
+ insn( mv, paramCount );
+ mv.visitTypeInsn( ANEWARRAY, "java/lang/Object" );
+ int pidx = 0;
+ for( Class<?> aClass : method.getParameterTypes() )
+ {
+ mv.visitInsn( DUP );
+ insn( mv, pidx++ );
+ stackIdx = wrapParameter( mv, aClass, stackIdx + 1 );
+ mv.visitInsn( AASTORE );
+ }
+ }
+
+ // Call method
+ mv.visitMethodInsn( INVOKEINTERFACE, "org/apache/zest/api/composite/CompositeInvoker",
+ "invokeComposite",
+ "(Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", true );
+
+ // Return value
+ if( !method.getReturnType().equals( Void.TYPE ) )
+ {
+ unwrapResult( mv, method.getReturnType(), l1 );
+ }
+ else
+ {
+ mv.visitInsn( POP );
+ mv.visitLabel( l1 );
+ endLabel = new Label();
+ mv.visitJumpInsn( GOTO, endLabel );
+ }
+
+ // Increase stack to beyond method args
+ stackIdx++;
+
+ // Declared exceptions
+ int exceptionIdx = 0;
+ for( Class<?> aClass : method.getExceptionTypes() )
+ {
+ mv.visitLabel( exceptionLabels.get( exceptionIdx++ ) );
+ mv.visitFrame( Opcodes.F_SAME1, 0, null, 1, new Object[]{ getInternalName( aClass ) } );
+ mv.visitVarInsn( ASTORE, stackIdx );
+ mv.visitVarInsn( ALOAD, stackIdx );
+ mv.visitInsn( ATHROW );
+ }
+
+ // RuntimeException and Error catch-all
+ mv.visitLabel( lruntime );
+ mv.visitFrame( Opcodes.F_SAME1, 0, null, 1, new Object[]{ "java/lang/RuntimeException" } );
+ mv.visitVarInsn( ASTORE, stackIdx );
+ mv.visitVarInsn( ALOAD, stackIdx );
+ mv.visitInsn( ATHROW );
+
+ mv.visitLabel( lerror );
+ mv.visitFrame( Opcodes.F_SAME1, 0, null, 1, new Object[]{ "java/lang/Throwable" } );
+ mv.visitVarInsn( ASTORE, stackIdx );
+ mv.visitVarInsn( ALOAD, stackIdx );
+ mv.visitTypeInsn( CHECKCAST, "java/lang/Error" );
+ mv.visitInsn( ATHROW );
+
+ // Return type = void
+ if( endLabel != null )
+ {
+ mv.visitLabel( endLabel );
+ mv.visitFrame( Opcodes.F_SAME, 0, null, 0, null );
+ mv.visitInsn( RETURN );
+ }
+
+ mv.visitMaxs( 0, 0 );
+ mv.visitEnd();
+ }
+ }
+
+ if( !Modifier.isAbstract( method.getModifiers() ) )
+ {
+ // Add method with _ as prefix
+ MethodVisitor mv = cw.visitMethod( ACC_PUBLIC, "_" + method.getName(), desc, null, exceptions );
+ mv.visitCode();
+ mv.visitVarInsn( ALOAD, 0 );
+
+ // Parameters
+ int stackIdx = 1;
+ for( Class<?> aClass : method.getParameterTypes() )
+ {
+ stackIdx = loadParameter( mv, aClass, stackIdx ) + 1;
+ }
+
+ // Call method
+ mv.visitMethodInsn( INVOKESPECIAL, baseClassSlash, method.getName(), desc, false );
+
+ // Return value
+ if( !method.getReturnType().equals( Void.TYPE ) )
+ {
+ returnResult( mv, method.getReturnType() );
+ }
+ else
+ {
+ mv.visitInsn( RETURN );
+ }
+
+ mv.visitMaxs( 1, 1 );
+ mv.visitEnd();
+ }
+ }
+ }
+
+ // Class initializer
+ {
+ MethodVisitor mv = cw.visitMethod( ACC_STATIC, "<clinit>", "()V", null, null );
+ mv.visitCode();
+ Label l0 = new Label();
+ Label l1 = new Label();
+ Label l2 = new Label();
+ mv.visitTryCatchBlock( l0, l1, l2, "java/lang/NoSuchMethodException" );
+ mv.visitLabel( l0 );
+
+ // Lookup methods and store in static variables
+ int midx = 0;
+ for( Method method : methods )
+ {
+ if( isOverridden( method, baseClass ) )
+ {
+ method.setAccessible( true );
+ Class methodClass;
+ if( Modifier.isAbstract( method.getModifiers() ) )
+ {
+ methodClass = method.getDeclaringClass();
+ }
+ else
+ {
+ try
+ {
+ methodClass = getInterfaceMethodDeclaration( method,
+ baseClass ); // Overridden method lookup
+ }
+ catch( NoSuchMethodException e )
+ {
+ throw new ClassNotFoundException( name, e );
+ }
+ }
+
+ midx++;
+
+ mv.visitLdcInsn( Type.getType( methodClass ) );
+ mv.visitLdcInsn( method.getName() );
+ insn( mv, method.getParameterTypes().length );
+ mv.visitTypeInsn( ANEWARRAY, "java/lang/Class" );
+
+ int pidx = 0;
+ for( Class<?> aClass : method.getParameterTypes() )
+ {
+ mv.visitInsn( DUP );
+ insn( mv, pidx++ );
+ type( mv, aClass );
+ mv.visitInsn( AASTORE );
+ }
+
+ mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Class", "getMethod",
+ "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false );
+ mv.visitFieldInsn( PUTSTATIC, classSlash, "m" + midx, "Ljava/lang/reflect/Method;" );
+ }
+ }
+
+ mv.visitLabel( l1 );
+ Label l3 = new Label();
+ mv.visitJumpInsn( GOTO, l3 );
+ mv.visitLabel( l2 );
+ mv.visitFrame( Opcodes.F_SAME1, 0, null, 1, new Object[]{ "java/lang/NoSuchMethodException" } );
+ mv.visitVarInsn( ASTORE, 0 );
+ mv.visitVarInsn( ALOAD, 0 );
+ mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/NoSuchMethodException", "printStackTrace", "()V", false );
+ mv.visitLabel( l3 );
+ mv.visitFrame( Opcodes.F_SAME, 0, null, 0, null );
+ mv.visitInsn( RETURN );
+ mv.visitMaxs( 6, 1 );
+ mv.visitEnd();
+ }
+ }
+ cw.visitEnd();
+ return cw.toByteArray();
+ }
+
+ private static boolean isOverridden( Method method, Class baseClass )
+ {
+ if( Modifier.isAbstract( method.getModifiers() ) )
+ {
+ return true; // Implement all abstract methods
+ }
+
+ if( Modifier.isFinal( method.getModifiers() ) )
+ {
+ return false; // Cannot override final methods
+ }
+
+ if( isInterfaceMethod( method, baseClass ) )
+ {
+ // if() used for clarity.
+ //noinspection RedundantIfStatement
+ if( isInternalPolygeneMethod( method, baseClass ) )
+ {
+ return false; // Skip methods in Polygene-internal interfaces
+ }
+ else
+ {
+ return true;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ private static boolean isInternalPolygeneMethod( Method method, Class baseClass )
+ {
+ return isDeclaredIn( method, Initializable.class, baseClass )
+ || isDeclaredIn( method, Lifecycle.class, baseClass );
+ }
+
+ private static boolean isDeclaredIn( Method method, Class<?> clazz, Class<?> baseClass )
+ {
+ return clazz.isAssignableFrom( baseClass ) && checkForMethod( method, clazz );
+ }
+
+ private static Class getInterfaceMethodDeclaration( Method method, Class clazz )
+ throws NoSuchMethodException
+ {
+ return interfacesOf( clazz )
+ .map( Classes.RAW_CLASS )
+ .filter( intface -> checkForMethod( method, intface ) )
+ .findFirst()
+ .orElseThrow( () -> new NoSuchMethodException( method.getName() ) );
+ }
+
+ private static boolean isInterfaceMethod( Method method, Class<?> baseClass )
+ {
+ return interfacesOf( baseClass )
+ .map( Classes.RAW_CLASS )
+ .filter( Methods.HAS_METHODS )
+ .anyMatch( intface -> checkForMethod( method, intface ));
+ }
+
+ private static boolean checkForMethod( Method method, Class<?> intface )
+ {
+ try
+ {
+ Method m = intface.getMethod( method.getName(), method.getParameterTypes() );
+ m.setAccessible( true );
+ return true;
+ }
+ catch( NoSuchMethodException e )
+ {
+ return false;
+ }
+ }
+
+ private static void type( MethodVisitor mv, Class<?> aClass )
+ {
+ if( aClass.equals( Integer.TYPE ) )
+ {
+ mv.visitFieldInsn( GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;" );
+ }
+ else if( aClass.equals( Long.TYPE ) )
+ {
+ mv.visitFieldInsn( GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;" );
+ }
+ else if( aClass.equals( Short.TYPE ) )
+ {
+ mv.visitFieldInsn( GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;" );
+ }
+ else if( aClass.equals( Byte.TYPE ) )
+ {
+ mv.visitFieldInsn( GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;" );
+ }
+ else if( aClass.equals( Double.TYPE ) )
+ {
+ mv.visitFieldInsn( GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;" );
+ }
+ else if( aClass.equals( Float.TYPE ) )
+ {
+ mv.visitFieldInsn( GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;" );
+ }
+ else if( aClass.equals( Boolean.TYPE ) )
+ {
+ mv.visitFieldInsn( GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;" );
+ }
+ else if( aClass.equals( Character.TYPE ) )
+ {
+ mv.visitFieldInsn( GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;" );
+ }
+ else
+ {
+ mv.visitLdcInsn( Type.getType( aClass ) );
+ }
+ }
+
+ private static int wrapParameter( MethodVisitor mv, Class<?> aClass, int idx )
+ {
+ if( aClass.equals( Integer.TYPE ) )
+ {
+ mv.visitVarInsn( ILOAD, idx );
+ mv.visitMethodInsn( INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false );
+ }
+ else if( aClass.equals( Long.TYPE ) )
+ {
+ mv.visitVarInsn( LLOAD, idx );
+ mv.visitMethodInsn( INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false );
+ idx++; // Extra jump
+ }
+ else if( aClass.equals( Short.TYPE ) )
+ {
+ mv.visitVarInsn( ILOAD, idx );
+ mv.visitMethodInsn( INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false );
+ }
+ else if( aClass.equals( Byte.TYPE ) )
+ {
+ mv.visitVarInsn( ILOAD, idx );
+ mv.visitMethodInsn( INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false );
+ }
+ else if( aClass.equals( Double.TYPE ) )
+ {
+ mv.visitVarInsn( DLOAD, idx );
+ idx++; // Extra jump
+ mv.visitMethodInsn( INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false );
+ }
+ else if( aClass.equals( Float.TYPE ) )
+ {
+ mv.visitVarInsn( FLOAD, idx );
+ mv.visitMethodInsn( INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false );
+ }
+ else if( aClass.equals( Boolean.TYPE ) )
+ {
+ mv.visitVarInsn( ILOAD, idx );
+ mv.visitMethodInsn( INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false );
+ }
+ else if( aClass.equals( Character.TYPE ) )
+ {
+ mv.visitVarInsn( ILOAD, idx );
+ mv.visitMethodInsn( INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false );
+ }
+ else
+ {
+ mv.visitVarInsn( ALOAD, idx );
+ }
+
+ return idx;
+ }
+
+ private static void unwrapResult( MethodVisitor mv, Class<?> aClass, Label label )
+ {
+ if( aClass.equals( Integer.TYPE ) )
+ {
+ mv.visitTypeInsn( CHECKCAST, "java/lang/Integer" );
+ mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false );
+ mv.visitLabel( label );
+ mv.visitInsn( IRETURN );
+ }
+ else if( aClass.equals( Long.TYPE ) )
+ {
+ mv.visitTypeInsn( CHECKCAST, "java/lang/Long" );
+ mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false );
+ mv.visitLabel( label );
+ mv.visitInsn( LRETURN );
+ }
+ else if( aClass.equals( Short.TYPE ) )
+ {
+ mv.visitTypeInsn( CHECKCAST, "java/lang/Short" );
+ mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false );
+ mv.visitLabel( label );
+ mv.visitInsn( IRETURN );
+ }
+ else if( aClass.equals( Byte.TYPE ) )
+ {
+ mv.visitTypeInsn( CHECKCAST, "java/lang/Byte" );
+ mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false );
+ mv.visitLabel( label );
+ mv.visitInsn( IRETURN );
+ }
+ else if( aClass.equals( Double.TYPE ) )
+ {
+ mv.visitTypeInsn( CHECKCAST, "java/lang/Double" );
+ mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false );
+ mv.visitLabel( label );
+ mv.visitInsn( DRETURN );
+ }
+ else if( aClass.equals( Float.TYPE ) )
+ {
+ mv.visitTypeInsn( CHECKCAST, "java/lang/Float" );
+ mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false );
+ mv.visitLabel( label );
+ mv.visitInsn( FRETURN );
+ }
+ else if( aClass.equals( Boolean.TYPE ) )
+ {
+ mv.visitTypeInsn( CHECKCAST, "java/lang/Boolean" );
+ mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false );
+ mv.visitLabel( label );
+ mv.visitInsn( IRETURN );
+ }
+ else if( aClass.equals( Character.TYPE ) )
+ {
+ mv.visitTypeInsn( CHECKCAST, "java/lang/Character" );
+ mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false );
+ mv.visitLabel( label );
+ mv.visitInsn( IRETURN );
+ }
+ else
+ {
+ mv.visitTypeInsn( CHECKCAST, getInternalName( aClass ) );
+ mv.visitLabel( label );
+ mv.visitInsn( ARETURN );
+ }
+ }
+
+ private static int loadParameter( MethodVisitor mv, Class<?> aClass, int idx )
+ {
+ if( aClass.equals( Integer.TYPE ) )
+ {
+ mv.visitVarInsn( ILOAD, idx );
+ }
+ else if( aClass.equals( Long.TYPE ) )
+ {
+ mv.visitVarInsn( LLOAD, idx );
+ idx++; // Extra jump
+ }
+ else if( aClass.equals( Short.TYPE ) )
+ {
+ mv.visitVarInsn( ILOAD, idx );
+ }
+ else if( aClass.equals( Byte.TYPE ) )
+ {
+ mv.visitVarInsn( ILOAD, idx );
+ }
+ else if( aClass.equals( Double.TYPE ) )
+ {
+ mv.visitVarInsn( DLOAD, idx );
+ idx++; // Extra jump
+ }
+ else if( aClass.equals( Float.TYPE ) )
+ {
+ mv.visitVarInsn( FLOAD, idx );
+ }
+ else if( aClass.equals( Boolean.TYPE ) )
+ {
+ mv.visitVarInsn( ILOAD, idx );
+ }
+ else if( aClass.equals( Character.TYPE ) )
+ {
+ mv.visitVarInsn( ILOAD, idx );
+ }
+ else
+ {
+ mv.visitVarInsn( ALOAD, idx );
+ }
+
+ return idx;
+ }
+
+ private static void returnResult( MethodVisitor mv, Class<?> aClass )
+ {
+ if( aClass.equals( Integer.TYPE ) )
+ {
+ mv.visitInsn( IRETURN );
+ }
+ else if( aClass.equals( Long.TYPE ) )
+ {
+ mv.visitInsn( LRETURN );
+ }
+ else if( aClass.equals( Short.TYPE ) )
+ {
+ mv.visitInsn( IRETURN );
+ }
+ else if( aClass.equals( Byte.TYPE ) )
+ {
+ mv.visitInsn( IRETURN );
+ }
+ else if( aClass.equals( Double.TYPE ) )
+ {
+ mv.visitInsn( DRETURN );
+ }
+ else if( aClass.equals( Float.TYPE ) )
+ {
+ mv.visitInsn( FRETURN );
+ }
+ else if( aClass.equals( Boolean.TYPE ) )
+ {
+ mv.visitInsn( IRETURN );
+ }
+ else if( aClass.equals( Character.TYPE ) )
+ {
+ mv.visitInsn( IRETURN );
+ }
+ else
+ {
+ mv.visitTypeInsn( CHECKCAST, getInternalName( aClass ) );
+ mv.visitInsn( ARETURN );
+ }
+ }
+
+ private static void insn( MethodVisitor mv, int length )
+ {
+ switch( length )
+ {
+ case 0:
+ mv.visitInsn( ICONST_0 );
+ return;
+ case 1:
+ mv.visitInsn( ICONST_1 );
+ return;
+ case 2:
+ mv.visitInsn( ICONST_2 );
+ return;
+ case 3:
+ mv.visitInsn( ICONST_3 );
+ return;
+ case 4:
+ mv.visitInsn( ICONST_4 );
+ return;
+ case 5:
+ mv.visitInsn( ICONST_5 );
+ return;
+ default:
+ mv.visitIntInsn( BIPUSH, length );
+ }
+ }
+
+ public static boolean isGenerated( Class clazz )
+ {
+ return clazz.getName().endsWith( GENERATED_POSTFIX );
+ }
+
+ public static boolean isGenerated( Object object )
+ {
+ return object.getClass().getName().endsWith( GENERATED_POSTFIX );
+ }
+
+ public Class loadFragmentClass( Class fragmentClass )
+ throws ClassNotFoundException
+ {
+ return loadClass( fragmentClass.getName().replace( '$', '_' ) + GENERATED_POSTFIX );
+ }
+
+ public static Class getSourceClass( Class fragmentClass )
+ {
+ return fragmentClass.getName().endsWith( GENERATED_POSTFIX ) ? fragmentClass.getSuperclass() : fragmentClass;
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/FragmentInvocationHandler.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/FragmentInvocationHandler.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/FragmentInvocationHandler.java
new file mode 100644
index 0000000..f0dfca6
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/FragmentInvocationHandler.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.polygene.runtime.composite;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+/**
+ * JAVADOC
+ */
+abstract class FragmentInvocationHandler
+ implements InvocationHandler
+{
+ private static final String COMPACT_TRACE = "polygene.compacttrace";
+
+ private static final CompactLevel compactLevel;
+
+ static
+ {
+ compactLevel = CompactLevel.valueOf( System.getProperty( COMPACT_TRACE, "proxy" ) );
+ }
+
+ protected Object fragment;
+ protected Method method;
+
+ void setFragment( Object fragment )
+ {
+ this.fragment = fragment;
+ }
+
+ public void setMethod( Method method )
+ {
+ this.method = method;
+ }
+
+ protected Throwable cleanStackTrace( Throwable throwable, Object proxy, Method method )
+ {
+ if( compactLevel == CompactLevel.off )
+ {
+ return throwable;
+ }
+
+ StackTraceElement[] trace = throwable.getStackTrace();
+
+ // Check if exception originated within Polygene or JDK - if so then skip compaction
+ if( trace.length == 0 || !isApplicationClass( trace[ 0 ].getClassName() ) )
+ {
+ return throwable;
+ }
+
+ int count = 0;
+ for( int i = 0; i < trace.length; i++ )
+ {
+ StackTraceElement stackTraceElement = trace[ i ];
+ if( !isApplicationClass( stackTraceElement.getClassName() ) )
+ {
+ // TODO: Should find stack entry outside Runtime, and compact beyond that
+ trace[ i ] = null;
+ count++;
+ }
+ else
+ {
+ boolean classOrigin = stackTraceElement.getClassName().equals( proxy.getClass().getSimpleName() );
+ boolean methodOrigin = stackTraceElement.getMethodName().equals( method.getName() );
+ if( classOrigin && methodOrigin && compactLevel == CompactLevel.proxy )
+ {
+ // Stop removing if the originating method call has been located in the stack.
+ // For 'semi' and 'extensive' compaction, we don't and do the entire stack instead.
+ trace[ i ] = new StackTraceElement( proxy.getClass()
+ .getInterfaces()[ 0 ].getName(), method.getName(), null, -1 );
+ break; // Stop compacting this trace
+ }
+ }
+ }
+
+ // Create new trace array
+ int idx = 0;
+ StackTraceElement[] newTrace = new StackTraceElement[ trace.length - count ];
+ for( StackTraceElement stackTraceElement : trace )
+ {
+ if( stackTraceElement != null )
+ {
+ newTrace[ idx++ ] = stackTraceElement;
+ }
+ }
+ throwable.setStackTrace( newTrace );
+
+ Throwable nested = throwable.getCause();
+ if( nested != null )
+ {
+ //noinspection ThrowableResultOfMethodCallIgnored
+ cleanStackTrace( nested, proxy, method );
+ }
+ for( Throwable suppressed : throwable.getSuppressed() )
+ {
+ //noinspection ThrowableResultOfMethodCallIgnored
+ cleanStackTrace( suppressed, proxy, method );
+ }
+ return throwable;
+ }
+
+ private boolean isApplicationClass( String className )
+ {
+ if( compactLevel == CompactLevel.semi )
+ {
+ return !isJdkInternals( className );
+ }
+ return !( className.endsWith( FragmentClassLoader.GENERATED_POSTFIX ) ||
+ className.startsWith( "org.apache.polygene.runtime" ) ||
+ isJdkInternals( className ) );
+ }
+
+ private boolean isJdkInternals( String className )
+ {
+ return className.startsWith( "java.lang.reflect" )
+ || className.startsWith( "jdk.internal.reflect" )
+ || className.startsWith( "com.sun.proxy" )
+ || className.startsWith( "sun.reflect" );
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/FunctionStateResolver.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/FunctionStateResolver.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/FunctionStateResolver.java
new file mode 100644
index 0000000..1357568
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/FunctionStateResolver.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.polygene.runtime.composite;
+
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import org.apache.polygene.api.association.AssociationDescriptor;
+import org.apache.polygene.api.entity.EntityReference;
+import org.apache.polygene.api.property.PropertyDescriptor;
+import org.apache.polygene.runtime.entity.EntityModel;
+import org.apache.polygene.spi.entity.EntityState;
+import org.apache.polygene.spi.entity.ManyAssociationState;
+import org.apache.polygene.spi.entity.NamedAssociationState;
+
+/**
+ * Function based StateResolver.
+ */
+public class FunctionStateResolver
+ implements StateResolver
+{
+ final Function<PropertyDescriptor, Object> propertyFunction;
+ final Function<AssociationDescriptor, EntityReference> associationFunction;
+ final Function<AssociationDescriptor, Stream<EntityReference>> manyAssociationFunction;
+ final Function<AssociationDescriptor, Stream<Map.Entry<String, EntityReference>>> namedAssociationFunction;
+
+ public FunctionStateResolver( Function<PropertyDescriptor, Object> propertyFunction,
+ Function<AssociationDescriptor, EntityReference> associationFunction,
+ Function<AssociationDescriptor, Stream<EntityReference>> manyAssociationFunction,
+ Function<AssociationDescriptor, Stream<Map.Entry<String, EntityReference>>> namedAssociationFunction )
+ {
+ this.propertyFunction = propertyFunction;
+ this.associationFunction = associationFunction;
+ this.manyAssociationFunction = manyAssociationFunction;
+ this.namedAssociationFunction = namedAssociationFunction;
+ }
+
+ @Override
+ public Object getPropertyState( PropertyDescriptor propertyDescriptor )
+ {
+ return propertyFunction.apply( propertyDescriptor );
+ }
+
+ @Override
+ public EntityReference getAssociationState( AssociationDescriptor associationDescriptor )
+ {
+ return associationFunction.apply( associationDescriptor );
+ }
+
+ @Override
+ public Stream<EntityReference> getManyAssociationState( AssociationDescriptor associationDescriptor )
+ {
+ return manyAssociationFunction.apply( associationDescriptor );
+ }
+
+ @Override
+ public Stream<Map.Entry<String, EntityReference>> getNamedAssociationState(
+ AssociationDescriptor associationDescriptor )
+ {
+ return namedAssociationFunction.apply( associationDescriptor );
+ }
+
+ public void populateState( EntityModel model, EntityState state )
+ {
+ model.state().properties().forEach(
+ propDesc ->
+ {
+ Object value = getPropertyState( propDesc );
+ state.setPropertyValue( propDesc.qualifiedName(), value );
+ } );
+ model.state().associations().forEach(
+ assDesc ->
+ {
+ EntityReference ref = getAssociationState( assDesc );
+ state.setAssociationValue( assDesc.qualifiedName(), ref );
+ } );
+ model.state().manyAssociations().forEach(
+ manyAssDesc ->
+ {
+ ManyAssociationState associationState = state.manyAssociationValueOf( manyAssDesc.qualifiedName() );
+ // First clear existing ones
+ associationState.forEach( associationState::remove );
+ // then add the new ones.
+ getManyAssociationState( manyAssDesc )
+ .forEach( ref -> associationState.add( 0, ref ) );
+ } );
+ model.state().namedAssociations().forEach(
+ namedAssDesc ->
+ {
+ NamedAssociationState associationState = state.namedAssociationValueOf( namedAssDesc.qualifiedName() );
+ // First clear existing ones
+ associationState.forEach( associationState::remove );
+ // then add the new ones.
+ getNamedAssociationState( namedAssDesc )
+ .forEach( entry -> associationState.put( entry.getKey(), entry.getValue() ) );
+ } );
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/GenericFragmentInvocationHandler.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/GenericFragmentInvocationHandler.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/GenericFragmentInvocationHandler.java
new file mode 100644
index 0000000..44259e8
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/GenericFragmentInvocationHandler.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.polygene.runtime.composite;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * JAVADOC
+ */
+public final class GenericFragmentInvocationHandler
+ extends FragmentInvocationHandler
+{
+ // InvocationHandler implementation ------------------------------
+
+ @Override
+ public Object invoke( Object proxy, Method method, Object[] args )
+ throws Throwable
+ {
+ try
+ {
+ return ( (InvocationHandler) fragment ).invoke( proxy, method, args );
+ }
+ catch( InvocationTargetException throwable )
+ {
+ throw cleanStackTrace( throwable.getTargetException(), proxy, method );
+ }
+ catch( Throwable throwable )
+ {
+ throw cleanStackTrace( throwable, proxy, method );
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/Genericpredicate.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/Genericpredicate.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/Genericpredicate.java
new file mode 100644
index 0000000..e8c5cea
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/Genericpredicate.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.polygene.runtime.composite;
+
+import java.lang.reflect.InvocationHandler;
+import java.util.function.Predicate;
+
+/**
+ * Specification that checks whether a given class implements InvocationHandler or not.
+ */
+public class Genericpredicate
+ implements Predicate<Class<?>>
+{
+ public static final Genericpredicate INSTANCE = new Genericpredicate();
+
+ @Override
+ public boolean test( Class<?> item )
+ {
+ return InvocationHandler.class.isAssignableFrom( item );
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/InstancePool.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/InstancePool.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/InstancePool.java
new file mode 100644
index 0000000..0eec577
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/InstancePool.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.polygene.runtime.composite;
+
+/**
+ * JAVADOC
+ */
+public interface InstancePool<T>
+{
+ public T obtainInstance();
+
+ public void releaseInstance( T instance );
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/MixinModel.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/MixinModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/MixinModel.java
new file mode 100644
index 0000000..37722e6
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/MixinModel.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.polygene.runtime.composite;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.polygene.api.common.ConstructionException;
+import org.apache.polygene.api.composite.CompositeInstance;
+import org.apache.polygene.api.injection.scope.This;
+import org.apache.polygene.api.mixin.Initializable;
+import org.apache.polygene.api.mixin.InitializationException;
+import org.apache.polygene.api.mixin.MixinDescriptor;
+import org.apache.polygene.api.property.StateHolder;
+import org.apache.polygene.api.util.HierarchicalVisitor;
+import org.apache.polygene.api.util.VisitableHierarchy;
+import org.apache.polygene.runtime.injection.Dependencies;
+import org.apache.polygene.runtime.injection.DependencyModel;
+import org.apache.polygene.runtime.injection.InjectedFieldsModel;
+import org.apache.polygene.runtime.injection.InjectedMethodsModel;
+import org.apache.polygene.runtime.injection.InjectionContext;
+
+/**
+ * JAVADOC
+ */
+public final class MixinModel
+ implements MixinDescriptor, VisitableHierarchy<Object, Object>, Dependencies
+{
+ private final Class<?> mixinClass;
+ private final Class<?> instantiationClass;
+ private final ConstructorsModel constructorsModel;
+ private final InjectedFieldsModel injectedFieldsModel;
+ private final InjectedMethodsModel injectedMethodsModel;
+ private final List<Class<?>> thisMixinTypes;
+
+ public MixinModel( Class<?> declaredMixinClass, Class<?> instantiationClass )
+ {
+ injectedFieldsModel = new InjectedFieldsModel( declaredMixinClass );
+ injectedMethodsModel = new InjectedMethodsModel( declaredMixinClass );
+
+ this.mixinClass = declaredMixinClass;
+ this.instantiationClass = instantiationClass;
+ constructorsModel = new ConstructorsModel( instantiationClass );
+
+ thisMixinTypes = buildThisMixinTypes();
+ }
+
+ @Override
+ public Class<?> mixinClass()
+ {
+ return mixinClass;
+ }
+
+ public Class<?> instantiationClass()
+ {
+ return instantiationClass;
+ }
+
+ public boolean isGeneric()
+ {
+ return InvocationHandler.class.isAssignableFrom( mixinClass );
+ }
+
+ public Stream<DependencyModel> dependencies()
+ {
+ Stream<? extends Dependencies> models = Stream.of( constructorsModel, injectedFieldsModel, injectedMethodsModel );
+ return models.flatMap( Dependencies::dependencies );
+ }
+
+ @Override
+ public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> visitor )
+ throws ThrowableType
+ {
+ if( visitor.visitEnter( this ) )
+ {
+ if( constructorsModel.accept( visitor ) )
+ {
+ if( injectedFieldsModel.accept( visitor ) )
+ {
+ injectedMethodsModel.accept( visitor );
+ }
+ }
+ }
+ return visitor.visitLeave( this );
+ }
+
+ // Context
+ public Object newInstance( CompositeInstance compositeInstance, StateHolder state, UsesInstance uses )
+ {
+ InjectionContext injectionContext = new InjectionContext( compositeInstance, uses, state );
+ return newInstance( injectionContext );
+ }
+
+ public Object newInstance( InjectionContext injectionContext )
+ {
+ Object mixin;
+ CompositeInstance compositeInstance = injectionContext.compositeInstance();
+
+ mixin = constructorsModel.newInstance( injectionContext );
+
+ if( FragmentClassLoader.isGenerated( instantiationClass ) )
+ {
+ try
+ {
+ instantiationClass.getDeclaredField( "_instance" ).set( mixin,
+ injectionContext.compositeInstance() );
+ }
+ catch( IllegalAccessException | NoSuchFieldException e )
+ {
+ e.printStackTrace();
+ }
+ }
+
+ injectedFieldsModel.inject( injectionContext, mixin );
+ injectedMethodsModel.inject( injectionContext, mixin );
+ if( mixin instanceof Initializable )
+ {
+ try
+ {
+ ( (Initializable) mixin ).initialize();
+ }
+ catch( Exception e )
+ {
+ List<Class<?>> compositeType = compositeInstance.types().collect( Collectors.toList() );
+ String message = "Unable to initialize " + mixinClass + " in composite " + compositeType;
+ throw new ConstructionException( new InitializationException( message, e ) );
+ }
+ }
+ return mixin;
+ }
+
+ public Iterable<Class<?>> thisMixinTypes()
+ {
+ return thisMixinTypes;
+ }
+
+ private List<Class<?>> buildThisMixinTypes()
+ {
+ return dependencies()
+ .filter( new DependencyModel.ScopeSpecification( This.class ) )
+ .distinct()
+ .map( DependencyModel::rawInjectionType )
+ .collect( Collectors.toList() );
+ }
+
+ protected FragmentInvocationHandler newInvocationHandler( Method method )
+ {
+ if( InvocationHandler.class.isAssignableFrom( mixinClass )
+ && !method.getDeclaringClass().isAssignableFrom( mixinClass ) )
+ {
+ return new GenericFragmentInvocationHandler();
+ }
+ else
+ {
+ return new TypedModifierInvocationHandler();
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return mixinClass.getName();
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/MixinsInstance.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/MixinsInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/MixinsInstance.java
new file mode 100644
index 0000000..b26f548
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/MixinsInstance.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.polygene.runtime.composite;
+
+import java.lang.reflect.Method;
+
+/**
+ * JAVADOC
+ */
+public interface MixinsInstance
+{
+ Object invoke( Object composite, Object[] params, CompositeMethodInstance methodInstance )
+ throws Throwable;
+
+ Object invokeObject( Object proxy, Object[] args, Method method )
+ throws Throwable;
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/MixinsModel.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/MixinsModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/MixinsModel.java
new file mode 100644
index 0000000..2f6e333
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/MixinsModel.java
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.polygene.runtime.composite;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Stream;
+import org.apache.polygene.api.util.Classes;
+import org.apache.polygene.api.util.HierarchicalVisitor;
+import org.apache.polygene.api.util.HierarchicalVisitorAdapter;
+import org.apache.polygene.api.util.VisitableHierarchy;
+import org.apache.polygene.bootstrap.BindingException;
+import org.apache.polygene.runtime.injection.Dependencies;
+import org.apache.polygene.runtime.injection.DependencyModel;
+import org.apache.polygene.runtime.injection.InjectedFieldModel;
+import org.apache.polygene.runtime.model.Binder;
+import org.apache.polygene.runtime.model.Resolution;
+
+import static org.apache.polygene.api.util.Classes.interfacesOf;
+
+/**
+ * Base implementation of model for mixins. This records the mapping between methods in the Composite
+ * and mixin implementations.
+ */
+public class MixinsModel
+ implements Binder, VisitableHierarchy<Object, Object>, Dependencies
+{
+ protected final Map<Method, MixinModel> methodImplementation = new HashMap<Method, MixinModel>();
+ protected final Map<Method, Integer> methodIndex = new HashMap<Method, Integer>();
+ protected List<MixinModel> mixinModels = new ArrayList<MixinModel>();
+
+ private final Map<Class, Integer> mixinIndex = new HashMap<Class, Integer>();
+ private final Set<Class<?>> mixinTypes = new LinkedHashSet<Class<?>>();
+
+ public Stream<Class<?>> mixinTypes()
+ {
+ return mixinTypes.stream();
+ }
+
+ public <T> boolean isImplemented( Class<T> mixinType )
+ {
+ return mixinTypes.contains( mixinType );
+ }
+
+ public List<MixinModel> mixinModels()
+ {
+ return mixinModels;
+ }
+
+ public MixinModel mixinFor( Method method )
+ {
+ return methodImplementation.get( method );
+ }
+
+ public MixinModel getMixinModel( Class mixinClass )
+ {
+ for( MixinModel mixinModel : mixinModels )
+ {
+ if( mixinModel.mixinClass().equals( mixinClass ) )
+ {
+ return mixinModel;
+ }
+ }
+ return null;
+ }
+
+ public void addMixinType( Class mixinType )
+ {
+ Stream<? extends Type> stream = interfacesOf( mixinType );
+ Stream<Class<?>> rawClass = stream.map( Classes.RAW_CLASS );
+ rawClass.forEach( mixinTypes::add );
+ }
+
+ public void addMixinModel( MixinModel mixinModel )
+ {
+ mixinModels.add( mixinModel );
+ }
+
+ public void addMethodMixin( Method method, MixinModel mixinModel )
+ {
+ methodImplementation.put( method, mixinModel );
+ }
+
+ @Override
+ public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> visitor )
+ throws ThrowableType
+ {
+ if( visitor.visitEnter( this ) )
+ {
+ for( MixinModel mixinModel : mixinModels )
+ {
+ mixinModel.accept( visitor );
+ }
+ }
+ return visitor.visitLeave( this );
+ }
+
+ // Binding
+ @Override
+ public void bind( final Resolution resolution )
+ throws BindingException
+ {
+ // Order mixins based on @This usages
+ UsageGraph<MixinModel> deps = new UsageGraph<MixinModel>( mixinModels, new Uses(), true );
+ mixinModels = deps.resolveOrder();
+
+ // Populate mappings
+ for( int i = 0; i < mixinModels.size(); i++ )
+ {
+ MixinModel mixinModel = mixinModels.get( i );
+ mixinIndex.put( mixinModel.mixinClass(), i );
+ }
+
+ for( Map.Entry<Method, MixinModel> methodClassEntry : methodImplementation.entrySet() )
+ {
+ methodIndex.put( methodClassEntry.getKey(), mixinIndex.get( methodClassEntry.getValue().mixinClass() ) );
+ }
+
+ for( MixinModel mixinModel : mixinModels )
+ {
+ mixinModel.accept( new HierarchicalVisitorAdapter<Object, Object, BindingException>()
+ {
+ @Override
+ public boolean visitEnter( Object visited )
+ throws BindingException
+ {
+ if( visited instanceof InjectedFieldModel )
+ {
+ InjectedFieldModel fieldModel = (InjectedFieldModel) visited;
+ fieldModel.bind( resolution.forField( fieldModel.field() ) );
+ return false;
+ }
+ else if( visited instanceof Binder )
+ {
+ Binder constructorsModel = (Binder) visited;
+ constructorsModel.bind( resolution );
+
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean visit( Object visited )
+ throws BindingException
+ {
+ if( visited instanceof Binder )
+ {
+ ( (Binder) visited ).bind( resolution );
+ }
+ return true;
+ }
+ } );
+ }
+ }
+
+ // Context
+
+ public Object[] newMixinHolder()
+ {
+ return new Object[ mixinIndex.size() ];
+ }
+
+ public FragmentInvocationHandler newInvocationHandler( final Method method )
+ {
+ return mixinFor( method ).newInvocationHandler( method );
+ }
+
+ public Stream<DependencyModel> dependencies()
+ {
+ return mixinModels.stream().flatMap( Dependencies::dependencies );
+ }
+
+ public Stream<Method> invocationsFor( final Class<?> mixinClass )
+ {
+ return methodImplementation.entrySet()
+ .stream().filter( entry -> entry.getValue().mixinClass().equals( mixinClass ) )
+ .map( Map.Entry::getKey );
+ }
+
+ private class Uses
+ implements UsageGraph.Use<MixinModel>
+ {
+ @Override
+ public Collection<MixinModel> uses( MixinModel source )
+ {
+ // System.out.println("BEGIN> MixinsModel.Uses.uses( "+source+" )");
+ Iterable<Class<?>> thisMixinTypes = source.thisMixinTypes();
+ List<MixinModel> usedMixinClasses = new ArrayList<MixinModel>();
+ // System.out.println("\tSource Mixin Types and Methods: ");
+ for( Class thisMixinType : thisMixinTypes )
+ {
+ // System.out.println("\t\t"+thisMixinType);
+ for( Method method : thisMixinType.getMethods() )
+ {
+ // System.out.println("\t\t\t"+method);
+ if( !Modifier.isStatic( method.getModifiers() ) )
+ {
+ MixinModel used = methodImplementation.get( method );
+ usedMixinClasses.add( used );
+ }
+ }
+ }
+ // System.out.println( "END> MixinsModel.Uses.uses( " + source + " )" );
+ return usedMixinClasses;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ProxyGenerator.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ProxyGenerator.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ProxyGenerator.java
new file mode 100644
index 0000000..df7a02a
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ProxyGenerator.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.polygene.runtime.composite;
+
+import java.lang.reflect.Proxy;
+
+/**
+ * generates proxyclasses
+ */
+public class ProxyGenerator {
+ public static Class<?> createProxyClass(ClassLoader mainTypeClassLoader, Class<?>[] interfaces) {
+ ClassLoader effectiveClassLoader = Thread.currentThread().getContextClassLoader();
+ if (effectiveClassLoader == null) {
+ effectiveClassLoader = mainTypeClassLoader;
+ }
+ return Proxy.getProxyClass(effectiveClassLoader, interfaces);
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ProxyReferenceInvocationHandler.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ProxyReferenceInvocationHandler.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ProxyReferenceInvocationHandler.java
new file mode 100644
index 0000000..5d125a2
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ProxyReferenceInvocationHandler.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.polygene.runtime.composite;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.UndeclaredThrowableException;
+import org.apache.polygene.api.composite.CompositeInvoker;
+
+public final class ProxyReferenceInvocationHandler
+ implements InvocationHandler, CompositeInvoker
+{
+ private Object proxy;
+
+ public Object proxy()
+ {
+ return proxy;
+ }
+
+ public void setProxy( Object proxy )
+ {
+ this.proxy = proxy;
+ }
+
+ public void clearProxy()
+ {
+ proxy = null;
+ }
+
+ @Override
+ public Object invokeComposite( Method method, Object[] args )
+ throws Throwable
+ {
+ try
+ {
+ InvocationHandler invocationHandler = Proxy.getInvocationHandler( this.proxy );
+ return invocationHandler.invoke( this.proxy, method, args );
+ }
+ catch( InvocationTargetException e )
+ {
+ throw e.getTargetException();
+ }
+ catch( UndeclaredThrowableException e )
+ {
+ throw e.getUndeclaredThrowable();
+ }
+ }
+
+ @Override
+ public Object invoke( Object proxy, Method method, Object[] args )
+ throws Throwable
+ {
+ try
+ {
+ InvocationHandler invocationHandler = Proxy.getInvocationHandler( this.proxy );
+ return invocationHandler.invoke( this.proxy, method, args );
+ }
+ catch( InvocationTargetException e )
+ {
+ throw e.getTargetException();
+ }
+ catch( UndeclaredThrowableException e )
+ {
+ throw e.getUndeclaredThrowable();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SideEffectInvocationHandlerResult.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SideEffectInvocationHandlerResult.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SideEffectInvocationHandlerResult.java
new file mode 100644
index 0000000..44c10d5
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SideEffectInvocationHandlerResult.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.polygene.runtime.composite;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+/**
+ * JAVADOC
+ */
+public final class SideEffectInvocationHandlerResult
+ implements InvocationHandler
+{
+ private Object result;
+ private Throwable throwable;
+
+ public SideEffectInvocationHandlerResult()
+ {
+ }
+
+ public void setResult( Object result, Throwable throwable )
+ {
+ this.result = result;
+ this.throwable = throwable;
+ }
+
+ // InvocationHandler implementation ------------------------------
+
+ @Override
+ public Object invoke( Object proxy, Method method, Object[] args )
+ throws Throwable
+ {
+ if( throwable != null )
+ {
+ throw throwable;
+ }
+ else
+ {
+ return result;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SideEffectModel.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SideEffectModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SideEffectModel.java
new file mode 100644
index 0000000..0b19e80
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SideEffectModel.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.polygene.runtime.composite;
+
+import org.apache.polygene.api.sideeffect.SideEffectDescriptor;
+
+/**
+ * JAVADOC
+ */
+public final class SideEffectModel
+ extends AbstractModifierModel
+ implements SideEffectDescriptor
+{
+ public SideEffectModel( Class<?> sideEffectClass, Class<?> instantiationClass )
+ {
+ super( sideEffectClass, instantiationClass );
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SideEffectsInstance.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SideEffectsInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SideEffectsInstance.java
new file mode 100644
index 0000000..011cd9c
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SideEffectsInstance.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.polygene.runtime.composite;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * JAVADOC
+ */
+public final class SideEffectsInstance
+ implements InvocationHandler
+{
+ private final List<InvocationHandler> sideEffects;
+ private final SideEffectInvocationHandlerResult resultInvocationHandler;
+ private final ProxyReferenceInvocationHandler proxyHandler;
+ private InvocationHandler invoker;
+
+ public SideEffectsInstance( List<InvocationHandler> sideEffects,
+ SideEffectInvocationHandlerResult resultInvocationHandler,
+ ProxyReferenceInvocationHandler proxyHandler,
+ InvocationHandler invoker
+ )
+ {
+ this.sideEffects = sideEffects;
+ this.resultInvocationHandler = resultInvocationHandler;
+ this.proxyHandler = proxyHandler;
+ this.invoker = invoker;
+ }
+
+ @Override
+ public Object invoke( Object proxy, Method method, Object[] args )
+ throws Throwable
+ {
+ try
+ {
+ Object result = invoker.invoke( proxy, method, args );
+ invokeSideEffects( proxy, method, args, result, null );
+ return result;
+ }
+ catch( Throwable throwable )
+ {
+ invokeSideEffects( proxy, method, args, null, throwable );
+ throw throwable;
+ }
+ }
+
+ private void invokeSideEffects( Object proxy,
+ Method method,
+ Object[] params,
+ Object result,
+ Throwable originalThrowable
+ )
+ throws Throwable
+ {
+ proxyHandler.setProxy( proxy );
+ resultInvocationHandler.setResult( result, originalThrowable );
+
+ try
+ {
+ for( InvocationHandler sideEffect : sideEffects )
+ {
+ try
+ {
+ sideEffect.invoke( proxy, method, params );
+ }
+ catch( Throwable throwable )
+ {
+ if( throwable != originalThrowable )
+ {
+ throwable.printStackTrace();
+ }
+ }
+ }
+ }
+ finally
+ {
+ proxyHandler.clearProxy();
+ resultInvocationHandler.setResult( null, null );
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SideEffectsModel.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SideEffectsModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SideEffectsModel.java
new file mode 100644
index 0000000..73c910c
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SideEffectsModel.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.polygene.runtime.composite;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Stream;
+import org.apache.polygene.api.sideeffect.SideEffectsDescriptor;
+import org.apache.polygene.api.structure.ModuleDescriptor;
+import org.apache.polygene.api.util.HierarchicalVisitor;
+import org.apache.polygene.api.util.VisitableHierarchy;
+import org.apache.polygene.runtime.injection.Dependencies;
+import org.apache.polygene.runtime.injection.DependencyModel;
+
+/**
+ * JAVADOC
+ */
+public final class SideEffectsModel
+ implements SideEffectsDescriptor, Dependencies, VisitableHierarchy<Object, Object>
+{
+ public static final SideEffectsModel EMPTY_SIDEEFFECTS = new SideEffectsModel( Collections.<SideEffectModel>emptyList() );
+
+ private List<SideEffectModel> sideEffectModels = null;
+
+ public SideEffectsModel( List<SideEffectModel> sideEffectModels )
+ {
+ this.sideEffectModels = sideEffectModels;
+ }
+
+ @Override
+ public Stream<DependencyModel> dependencies()
+ {
+ return sideEffectModels.stream().flatMap( Dependencies::dependencies );
+ }
+
+ // Context
+ public SideEffectsInstance newInstance( Method method, ModuleDescriptor module, InvocationHandler invoker )
+ {
+ ProxyReferenceInvocationHandler proxyHandler = new ProxyReferenceInvocationHandler();
+ SideEffectInvocationHandlerResult result = new SideEffectInvocationHandlerResult();
+ List<InvocationHandler> sideEffects = new ArrayList<InvocationHandler>( sideEffectModels.size() );
+ for( SideEffectModel sideEffectModel : sideEffectModels )
+ {
+ InvocationHandler sideEffect = sideEffectModel.newInstance( module, result, proxyHandler, method );
+ sideEffects.add( sideEffect );
+ }
+ return new SideEffectsInstance( sideEffects, result, proxyHandler, invoker );
+ }
+
+ @Override
+ public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> modelVisitor )
+ throws ThrowableType
+ {
+ if( modelVisitor.visitEnter( this ) )
+ {
+ for( SideEffectModel sideEffectModel : sideEffectModels )
+ {
+ if( !sideEffectModel.accept( modelVisitor ) )
+ {
+ break;
+ }
+ }
+ }
+ return modelVisitor.visitLeave( this );
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/StateModel.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/StateModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/StateModel.java
new file mode 100644
index 0000000..9995d1b
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/StateModel.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.polygene.runtime.composite;
+
+import java.lang.reflect.AccessibleObject;
+import java.util.stream.Stream;
+import org.apache.polygene.api.common.QualifiedName;
+import org.apache.polygene.api.composite.StateDescriptor;
+import org.apache.polygene.api.util.HierarchicalVisitor;
+import org.apache.polygene.api.util.VisitableHierarchy;
+import org.apache.polygene.runtime.property.PropertiesModel;
+import org.apache.polygene.runtime.property.PropertyModel;
+
+/**
+ * Base model for Composite state
+ */
+public class StateModel
+ implements StateDescriptor, VisitableHierarchy<Object, Object>
+{
+ protected final PropertiesModel propertiesModel;
+
+ public StateModel( PropertiesModel propertiesModel )
+ {
+ this.propertiesModel = propertiesModel;
+ }
+
+ public PropertyModel propertyModelFor( AccessibleObject accessor )
+ {
+ return propertiesModel.getProperty( accessor );
+ }
+
+ @Override
+ public PropertyModel findPropertyModelByName( String name )
+ throws IllegalArgumentException
+ {
+ return propertiesModel.getPropertyByName( name );
+ }
+
+ @Override
+ public PropertyModel findPropertyModelByQualifiedName( QualifiedName name )
+ throws IllegalArgumentException
+ {
+ return propertiesModel.getPropertyByQualifiedName( name );
+ }
+
+ @Override
+ public Stream<PropertyModel> properties()
+ {
+ return propertiesModel.properties();
+ }
+
+ @Override
+ public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> visitor )
+ throws ThrowableType
+ {
+ if( visitor.visitEnter( this ) )
+ {
+ ( (VisitableHierarchy<Object, Object>) propertiesModel ).accept( visitor );
+ }
+ return visitor.visitLeave( this );
+ }
+}