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 ); + } +}
