http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/AbstractModifierModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/AbstractModifierModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/AbstractModifierModel.java new file mode 100644 index 0000000..4a00359 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/AbstractModifierModel.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.Array; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.stream.Stream; +import org.apache.polygene.api.common.ConstructionException; +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; +import org.apache.polygene.runtime.injection.InjectedFieldsModel; +import org.apache.polygene.runtime.injection.InjectedMethodsModel; +import org.apache.polygene.runtime.injection.InjectionContext; + +import static org.apache.polygene.api.util.Classes.RAW_CLASS; +import static org.apache.polygene.api.util.Classes.interfacesOf; + +/** + * JAVADOC + */ +public abstract class AbstractModifierModel + implements Dependencies, VisitableHierarchy<Object, Object> +{ + private final Class<?> modifierClass; + + private final ConstructorsModel constructorsModel; + private final InjectedFieldsModel injectedFieldsModel; + private final InjectedMethodsModel injectedMethodsModel; + + private final Class<Class<?>>[] nextInterfaces; + + @SuppressWarnings( "unchecked" ) + public AbstractModifierModel( Class<?> declaredModifierClass, Class<?> instantiationClass ) + { + this.modifierClass = instantiationClass; + constructorsModel = new ConstructorsModel( modifierClass ); + injectedFieldsModel = new InjectedFieldsModel( declaredModifierClass ); + injectedMethodsModel = new InjectedMethodsModel( declaredModifierClass ); + Class<Class<?>> componentType = (Class<Class<?>>) Class.class.cast( Class.class ); + nextInterfaces = interfacesOf( declaredModifierClass ) + .map( RAW_CLASS ) + .distinct() + .toArray( size -> (Class<Class<?>>[]) Array.newInstance( componentType, size ) ); + } + + public Class<?> modifierClass() + { + return modifierClass; + } + + @Override + @SuppressWarnings( "unchecked" ) + public Stream<DependencyModel> dependencies() + { + Stream<? extends Dependencies> models = Stream.of( this.constructorsModel, injectedFieldsModel, injectedMethodsModel ); + return models.flatMap( Dependencies::dependencies ); + } + + public boolean isGeneric() + { + return InvocationHandler.class.isAssignableFrom( modifierClass ); + } + + @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 InvocationHandler newInstance( ModuleDescriptor module, + InvocationHandler next, + ProxyReferenceInvocationHandler proxyHandler, + Method method + ) + { + InjectionContext injectionContext = new InjectionContext( module, wrapNext( next ), proxyHandler ); + + Object modifier = constructorsModel.newInstance( injectionContext ); + + try + { + if( FragmentClassLoader.isGenerated( modifier ) ) + { + modifier.getClass().getField( "_instance" ).set( modifier, proxyHandler ); + } + } + catch( IllegalAccessException | NoSuchFieldException e ) + { + e.printStackTrace(); + } + + injectedFieldsModel.inject( injectionContext, modifier ); + injectedMethodsModel.inject( injectionContext, modifier ); + + if( isGeneric() ) + { + return (InvocationHandler) modifier; + } + else + { + try + { + Method invocationMethod = modifierClass.getMethod( "_" + method.getName(), method.getParameterTypes() ); + TypedModifierInvocationHandler handler = new TypedModifierInvocationHandler(); + handler.setFragment( modifier ); + handler.setMethod( invocationMethod ); + return handler; + } + catch( NoSuchMethodException e ) + { + throw new ConstructionException( "Could not find modifier method", e ); + } + } + } + + private Object wrapNext( InvocationHandler next ) + { + if( isGeneric() ) + { + return next; + } + else + { + return Proxy.newProxyInstance( modifierClass.getClassLoader(), nextInterfaces, next ); + } + } + + @Override + public boolean equals( Object o ) + { + if( this == o ) + { + return true; + } + if( o == null || getClass() != o.getClass() ) + { + return false; + } + AbstractModifierModel that = (AbstractModifierModel) o; + return modifierClass.equals( that.modifierClass ); + } + + @Override + public int hashCode() + { + return modifierClass.hashCode(); + } +} \ 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/AtomicInstancePool.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/AtomicInstancePool.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/AtomicInstancePool.java new file mode 100644 index 0000000..28ee8b2 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/AtomicInstancePool.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.util.concurrent.atomic.AtomicReference; + +/** + * Method instance pool that keeps a linked list. Uses atomic reference + * to ensure that instances are acquired and returned in a thread-safe + * manner. + */ +public final class AtomicInstancePool + implements InstancePool<CompositeMethodInstance> +{ + private final AtomicReference<CompositeMethodInstance> first = new AtomicReference<CompositeMethodInstance>(); + + @Override + public CompositeMethodInstance obtainInstance() + { + CompositeMethodInstance firstInstance; + do + { + firstInstance = first.get(); + } + while( firstInstance != null && !first.compareAndSet( firstInstance, firstInstance.getNext() ) ); + + return firstInstance; + } + + @Override + public void releaseInstance( CompositeMethodInstance compositeMethodInstance ) + { + CompositeMethodInstance firstInstance; + do + { + firstInstance = first.get(); + compositeMethodInstance.setNext( firstInstance ); + } + while( !first.compareAndSet( firstInstance, compositeMethodInstance ) ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompactLevel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompactLevel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompactLevel.java new file mode 100644 index 0000000..5c44e98 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompactLevel.java @@ -0,0 +1,41 @@ +/* + * 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; + +/** + * Compaction Level of the StackTrace clenaup operation. + * + * <pre> + * <b>off</b> = Do not modify the stack trace. + * <b>proxy</b> = Remove all Polygene internal classes and all JDK internal classes from + * the originating method call. + * <b>semi</b> = Remove all JDK internal classes on the entire stack. + * <b>extensive</b> = Remove all Polygene internal and JDK internal classes from the entire stack. + * </pre> + * + * <p> + * The Compaction is set through the System Property "<code><b>polygene.compacttrace</b></code>" to + * any of the above values. + * </p> + */ +enum CompactLevel +{ + off, proxy, semi, extensive +} \ 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/CompositeConstraintModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeConstraintModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeConstraintModel.java new file mode 100644 index 0000000..5345594 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeConstraintModel.java @@ -0,0 +1,74 @@ +/* + * 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.annotation.Annotation; +import org.apache.polygene.api.common.ConstructionException; +import org.apache.polygene.api.constraint.Constraint; + +/** + * JAVADOC + */ +public final class CompositeConstraintModel + extends AbstractConstraintModel +{ + private final ValueConstraintsModel constraintsModel; + + public CompositeConstraintModel( Annotation annotation, ValueConstraintsModel constraintsModel ) + { + super( annotation ); + this.constraintsModel = constraintsModel; + } + + @Override + @SuppressWarnings( {"raw", "unchecked"} ) + public ConstraintInstance<?, ?> newInstance() + { + try + { + ValueConstraintsInstance compositeConstraintsInstance = constraintsModel.newInstance(); + Constraint<?, ?> constraint = new CompositeConstraintInstance( compositeConstraintsInstance ); + return new ConstraintInstance( constraint, annotation ); + } + catch( Exception e ) + { + throw new ConstructionException( "Could not instantiate constraint implementation", e ); + } + } + + private static class CompositeConstraintInstance + implements Constraint<Annotation, Object> + { + private final ValueConstraintsInstance valueConstraintsInstance; + + private CompositeConstraintInstance( ValueConstraintsInstance valueConstraintsInstance ) + { + this.valueConstraintsInstance = valueConstraintsInstance; + } + + @Override + public boolean isValid( Annotation annotation, Object value ) + throws NullPointerException + { + return valueConstraintsInstance.checkConstraints( value ).isEmpty(); + } + } +} \ 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/CompositeMethodInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeMethodInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeMethodInstance.java new file mode 100644 index 0000000..1867bf1 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeMethodInstance.java @@ -0,0 +1,83 @@ +/* + * 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 CompositeMethodInstance +{ + private final InvocationHandler invoker; + private final FragmentInvocationHandler mixinInvoker; + private final Method method; + private final int methodIdx; + + private CompositeMethodInstance next; + + public CompositeMethodInstance( InvocationHandler invoker, + FragmentInvocationHandler mixinInvoker, + Method method, int methodIdx + ) + { + this.invoker = invoker; + this.method = method; + this.mixinInvoker = mixinInvoker; + this.methodIdx = methodIdx; + } + + public Method method() + { + return method; + } + + public Object getMixinFrom( Object[] mixins ) + { + return mixins[ methodIdx ]; + } + + public Object invoke( Object composite, Object[] params, Object mixin ) + throws Throwable + { + mixinInvoker.setFragment( mixin ); + + try + { + return invoker.invoke( composite, method, params ); + } + finally + { + mixinInvoker.setFragment( null ); + } + } + + public CompositeMethodInstance getNext() + { + return next; + } + + public void setNext( CompositeMethodInstance next ) + { + this.next = next; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeMethodModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeMethodModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeMethodModel.java new file mode 100644 index 0000000..267c065 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeMethodModel.java @@ -0,0 +1,324 @@ +/* + * 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.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.polygene.api.common.ConstructionException; +import org.apache.polygene.api.composite.MethodDescriptor; +import org.apache.polygene.api.structure.ModuleDescriptor; +import org.apache.polygene.api.util.HierarchicalVisitor; +import org.apache.polygene.api.util.NullArgumentException; +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 CompositeMethodModel + implements MethodDescriptor, Dependencies, VisitableHierarchy<Object, Object> +{ + // Model + private final Method method; + private Method invocationMethod; // This will be the _ prefixed method on typed mixins + private final ConstraintsModel constraints; + private final ConcernsModel concerns; + private final SideEffectsModel sideEffects; + private final MixinsModel mixins; + private AnnotatedElement annotations; + + // Context +// private final SynchronizedCompositeMethodInstancePool instancePool = new SynchronizedCompositeMethodInstancePool(); + private final AtomicInstancePool instancePool = new AtomicInstancePool(); + private final ConstraintsInstance constraintsInstance; + + public CompositeMethodModel( Method method, + ConstraintsModel constraintsModel, + ConcernsModel concernsModel, + SideEffectsModel sideEffectsModel, + MixinsModel mixinsModel + ) + { + this.method = method; + mixins = mixinsModel; + concerns = concernsModel; + sideEffects = sideEffectsModel; + constraints = constraintsModel; + constraintsInstance = constraints.newInstance(); + initialize(); + } + + private void initialize() + { + annotations = new CompositeMethodAnnotatedElement(); + this.method.setAccessible( true ); +// instancePool = new SynchronizedCompositeMethodInstancePool(); + } + + // Model + + @Override + public Method method() + { + return method; + } + + public MixinModel mixin() + { + return mixins.mixinFor( method ); + } + + @Override + @SuppressWarnings( "unchecked" ) + public Stream<DependencyModel> dependencies() + { + return Stream.of( this.concerns, sideEffects ).filter( Objects::nonNull ).flatMap( Dependencies::dependencies ); + } + + // Context + public Object invoke( Object composite, Object[] params, MixinsInstance mixins, ModuleDescriptor module ) + throws Throwable + { + constraintsInstance.checkValid( composite, method, params ); + + CompositeMethodInstance methodInstance = getInstance( module ); + try + { + return mixins.invoke( composite, params, methodInstance ); + } + finally + { + instancePool.releaseInstance( methodInstance ); + } + } + + private CompositeMethodInstance getInstance( ModuleDescriptor module ) + { + CompositeMethodInstance methodInstance = instancePool.obtainInstance(); + if( methodInstance == null ) + { + methodInstance = newCompositeMethodInstance( module ); + } + + return methodInstance; + } + + private CompositeMethodInstance newCompositeMethodInstance( ModuleDescriptor module ) + throws ConstructionException + { + FragmentInvocationHandler mixinInvocationHandler = mixins.newInvocationHandler( method ); + InvocationHandler invoker = mixinInvocationHandler; + if( concerns != ConcernsModel.EMPTY_CONCERNS ) + { + ConcernsInstance concernsInstance = concerns.newInstance( method, module, mixinInvocationHandler ); + invoker = concernsInstance; + } + if( sideEffects != SideEffectsModel.EMPTY_SIDEEFFECTS ) + { + SideEffectsInstance sideEffectsInstance = sideEffects.newInstance( method, module, invoker ); + invoker = sideEffectsInstance; + } + + if( invocationMethod == null ) + { + MixinModel model = mixins.mixinFor( method ); + if( !InvocationHandler.class.isAssignableFrom( model.mixinClass() ) ) + { + try + { + invocationMethod = model.instantiationClass() + .getMethod( "_" + method.getName(), method.getParameterTypes() ); + } + catch( NoSuchMethodException e ) + { + invocationMethod = method; +// throw new ConstructionException( "Could not find the subclass method", e ); + } + } + else + { + invocationMethod = method; + } + } + + mixinInvocationHandler.setMethod( invocationMethod ); + + return new CompositeMethodInstance( invoker, mixinInvocationHandler, method, mixins.methodIndex.get( method ) ); + } + + public AnnotatedElement annotatedElement() + { + return annotations; + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> modelVisitor ) + throws ThrowableType + { + if( modelVisitor.visitEnter( this ) ) + { + constraints.accept( modelVisitor ); + concerns.accept( modelVisitor ); + sideEffects.accept( modelVisitor ); + } + return modelVisitor.visitLeave( this ); + } + + @Override + public String toString() + { + return method.toGenericString(); + } + + public Iterable<Method> invocationsFor( Class<?> mixinClass ) + { + return mixins.invocationsFor( mixinClass ).collect( Collectors.toList() ); + } + + public class CompositeMethodAnnotatedElement + implements AnnotatedElement + { + @Override + public boolean isAnnotationPresent( Class<? extends Annotation> annotationClass ) + { + // Check method + if( method.isAnnotationPresent( annotationClass ) ) + { + return true; + } + + // Check mixin + try + { + MixinModel model = mixins.mixinFor( method ); + if( Genericpredicate.INSTANCE.test( model.mixinClass() ) ) + { + return false; + } + return ( model.mixinClass() + .getMethod( method.getName(), method.getParameterTypes() ) + .isAnnotationPresent( annotationClass ) ); + } + catch( NoSuchMethodException e ) + { + return false; + } + } + + @Override + public <T extends Annotation> T getAnnotation( Class<T> annotationClass ) + { + // Check mixin + try + { + MixinModel model = mixins.mixinFor( method ); + if( !Genericpredicate.INSTANCE.test( model.mixinClass() ) ) + { + T annotation = annotationClass.cast( model.mixinClass() + .getMethod( method.getName(), method.getParameterTypes() ) + .getAnnotation( annotationClass ) ); + if( annotation != null ) + { + return annotation; + } + } + } + catch( NoSuchMethodException e ) + { + // Ignore + } + + // Check method + return method.getAnnotation( annotationClass ); + } + + @Override + public Annotation[] getAnnotations() + { + // Add mixin annotations + List<Annotation> annotations = new ArrayList<Annotation>(); + MixinModel model = mixins.mixinFor( method ); + Annotation[] mixinAnnotations = new Annotation[ 0 ]; + if( !Genericpredicate.INSTANCE.test( model.mixinClass() ) ) + { + mixinAnnotations = model.mixinClass().getAnnotations(); + annotations.addAll( Arrays.asList( mixinAnnotations ) ); + } + + // Add method annotations, but don't include duplicates + Annotation[] methodAnnotations = method.getAnnotations(); + next: + for( Annotation methodAnnotation : methodAnnotations ) + { + for( int i = 0; i < mixinAnnotations.length; i++ ) + { + if( annotations.get( i ).annotationType().equals( methodAnnotation.annotationType() ) ) + { + continue next; + } + } + + annotations.add( methodAnnotation ); + } + + return annotations.toArray( new Annotation[ annotations.size() ] ); + } + + @Override + public Annotation[] getDeclaredAnnotations() + { + return new Annotation[ 0 ]; + } + + // @Override (Since JDK 8) + @SuppressWarnings( "unchecked" ) + public <T extends Annotation> T[] getAnnotationsByType( Class<T> annotationClass ) + { + NullArgumentException.validateNotNull( "annotationClass", annotationClass ); + return (T[]) Array.newInstance( annotationClass, 0 ); + } + + // @Override (Since JDK 8) + public <T extends Annotation> T getDeclaredAnnotation( Class<T> annotationClass ) + { + NullArgumentException.validateNotNull( "annotationClass", annotationClass ); + return null; + } + + // @Override (Since JDK 8) + @SuppressWarnings( "unchecked" ) + public <T extends Annotation> T[] getDeclaredAnnotationsByType( Class<T> annotationClass ) + { + NullArgumentException.validateNotNull( "annotationClass", annotationClass ); + return (T[]) Array.newInstance( annotationClass, 0 ); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeMethodsModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeMethodsModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeMethodsModel.java new file mode 100644 index 0000000..8dc2495 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeMethodsModel.java @@ -0,0 +1,134 @@ +/* + * 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.util.Collection; +import java.util.LinkedHashMap; +import java.util.stream.Stream; +import org.apache.polygene.api.composite.MissingMethodException; +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; + +/** + * Model for Composite methods. This includes both private and public methods. + */ +public final class CompositeMethodsModel + implements VisitableHierarchy<Object, Object>, Dependencies +{ + private final LinkedHashMap<Method, CompositeMethodModel> methods; + private final MixinsModel mixinsModel; + + public CompositeMethodsModel( MixinsModel mixinsModel ) + { + methods = new LinkedHashMap<>(); + this.mixinsModel = mixinsModel; + } + + public Stream<DependencyModel> dependencies() + { + Collection<CompositeMethodModel> compositeMethods = methods.values(); + return compositeMethods.stream().flatMap( Dependencies.DEPENDENCIES_FUNCTION ); + } + + // Context + public Object invoke( MixinsInstance mixins, + Object proxy, + Method method, + Object[] args, + ModuleDescriptor moduleInstance + ) + throws Throwable + { + CompositeMethodModel compositeMethod = methods.get( method ); + + if( compositeMethod == null ) + { + if( method.getDeclaringClass().equals( Object.class ) ) + { + return mixins.invokeObject( proxy, args, method ); + } + + // TODO: Figure out what was the intention of this code block, added by Rickard in 2009. It doesn't do anything useful. + if( !method.getDeclaringClass().isInterface() ) + { + compositeMethod = mixinsModel.mixinTypes().map( aClass -> { + try + { + Method realMethod = aClass.getMethod( method.getName(), method.getParameterTypes() ); + return methods.get( realMethod ); + } + catch( NoSuchMethodException | SecurityException e ) + { + + } + return null; + }).filter( model -> model != null ).findFirst().orElse( null ); + } + throw new MissingMethodException( "Method '" + method + "' is not implemented" ); + } + else + { + return compositeMethod.invoke( proxy, args, mixins, moduleInstance ); + } + } + + public void addMethod( CompositeMethodModel methodModel ) + { + methods.put( methodModel.method(), methodModel ); + } + + public boolean isImplemented( Method method ) + { + return methods.containsKey( method ); + } + + public Iterable<Method> methods() + { + return methods.keySet(); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> modelVisitor ) + throws ThrowableType + { + if( modelVisitor.visitEnter( this ) ) + { + for( CompositeMethodModel compositeMethodModel : methods.values() ) + { + if( !compositeMethodModel.accept( modelVisitor ) ) + { + break; + } + } + } + return modelVisitor.visitLeave( this ); + } + + @Override + public String toString() + { + return methods().toString(); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeModel.java new file mode 100644 index 0000000..2f1e81d --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeModel.java @@ -0,0 +1,278 @@ +/* + * 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.InvocationHandler; +import java.lang.reflect.Method; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; +import org.apache.polygene.api.common.ConstructionException; +import org.apache.polygene.api.common.MetaInfo; +import org.apache.polygene.api.common.Visibility; +import org.apache.polygene.api.composite.Composite; +import org.apache.polygene.api.composite.CompositeDescriptor; +import org.apache.polygene.api.composite.InvalidCompositeException; +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; + +import static java.lang.reflect.Proxy.newProxyInstance; + +/** + * JAVADOC + */ +public abstract class CompositeModel + implements VisitableHierarchy<Object, Object>, Dependencies, CompositeDescriptor +{ + protected final MixinsModel mixinsModel; + protected final CompositeMethodsModel compositeMethodsModel; + private final Set<Class<?>> types; + private final Visibility visibility; + private final MetaInfo metaInfo; + protected final StateModel stateModel; + private volatile Class<?> primaryType; + protected Class<? extends Composite> proxyClass; + protected Constructor<? extends Composite> proxyConstructor; + protected ModuleDescriptor module; + + protected CompositeModel( final ModuleDescriptor module, + final List<Class<?>> types, + final Visibility visibility, + final MetaInfo metaInfo, + final MixinsModel mixinsModel, + final StateModel stateModel, + final CompositeMethodsModel compositeMethodsModel + ) + { + this.module = module; + this.types = new LinkedHashSet<>( types ); + this.visibility = visibility; + this.metaInfo = metaInfo; + this.stateModel = stateModel; + this.compositeMethodsModel = compositeMethodsModel; + this.mixinsModel = mixinsModel; + + // Create proxy class + createProxyClass(); + primaryType = mixinTypes() + .reduce( null, ( primary, type ) -> + { + if( primary == null ) + { + return type; + } + else if( primary.isAssignableFrom( type ) ) + { + return type; + } + return primary; + } ); + } + + // Model + @Override + public Stream<Class<?>> types() + { + return types.stream(); + } + + public StateModel state() + { + return stateModel; + } + + @Override + public <T> T metaInfo( Class<T> infoType ) + { + return metaInfo.get( infoType ); + } + + @Override + public Visibility visibility() + { + return visibility; + } + + @Override + public boolean isAssignableTo( Class<?> type ) + { + for( Class<?> aClass : types ) + { + if( type.isAssignableFrom( aClass ) ) + { + return true; + } + } + return false; + } + + public MixinsModel mixinsModel() + { + return mixinsModel; + } + + @Override + @SuppressWarnings( { "raw", "unchecked" } ) + public Class<?> primaryType() + { + return primaryType; + } + + @Override + public Stream<Class<?>> mixinTypes() + { + return mixinsModel.mixinTypes(); + } + + @Override + public Stream<DependencyModel> dependencies() + { + Stream<Dependencies> models = Stream.of( this.mixinsModel, compositeMethodsModel ); + 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( compositeMethodsModel.accept( visitor ) ) + { + if( stateModel.accept( visitor ) ) + { + mixinsModel.accept( visitor ); + } + } + } + return visitor.visitLeave( this ); + } + + @SuppressWarnings( { "raw", "unchecked" } ) + private void createProxyClass() + { + Class<?> mainType = types.stream().findFirst().get(); + if( mainType.isInterface() ) + { + ClassLoader proxyClassloader = mainType.getClassLoader(); + + Class<?>[] interfaces = types.stream().map( Class.class::cast ).toArray( Class[]::new ); + proxyClass = (Class<? extends Composite>) ProxyGenerator.createProxyClass( proxyClassloader, interfaces ); + + try + { + proxyConstructor = proxyClass.getConstructor( InvocationHandler.class ); + } + catch( NoSuchMethodException e ) + { + throw (InvalidCompositeException) new InvalidCompositeException( "Could not get proxy constructor" ).initCause( e ); + } + proxyConstructor.setAccessible( true ); + } + else + { + try + { + proxyClass = new TransientClassLoader( getClass().getClassLoader() ).loadFragmentClass( mainType ); + proxyConstructor = (Constructor<? extends Composite>) proxyClass.getConstructors()[ 0 ]; + } + catch( ClassNotFoundException e ) + { + throw (InvalidCompositeException) new InvalidCompositeException( "Could not get proxy constructor" ).initCause( e ); + } + } + } + + // Context + public final Object invoke( MixinsInstance mixins, + Object proxy, + Method method, + Object[] args + ) + throws Throwable + { + return compositeMethodsModel.invoke( mixins, proxy, method, args, module ); + } + + @Override + public ModuleDescriptor module() + { + return module; + } + + public Composite newProxy( InvocationHandler invocationHandler ) + throws ConstructionException + { + Class<?> mainType = types.stream().findFirst().get(); + if( mainType.isInterface() ) + { + try + { + return Composite.class.cast( proxyConstructor.newInstance( invocationHandler ) ); + } + catch( Exception e ) + { + throw new ConstructionException( e ); + } + } + else + { + try + { + Object[] args = new Object[ proxyConstructor.getParameterTypes().length ]; + Composite composite = Composite.class.cast( proxyConstructor.newInstance( args ) ); + proxyClass.getField( "_instance" ).set( composite, invocationHandler ); + return composite; + } + catch( Exception e ) + { + throw new ConstructionException( e ); + } + } + } + + @SuppressWarnings( "raw" ) + public <T> T newProxy( InvocationHandler invocationHandler, Class<T> mixinType ) + throws IllegalArgumentException + { + +// if (!matchesAny( isAssignableFrom( mixinType ), types )) + if( !mixinsModel.isImplemented( mixinType ) ) + { + String message = "Composite " + primaryType().getName() + " does not implement type " + mixinType.getName(); + throw new IllegalArgumentException( message ); + } + + // Instantiate proxy for given mixin interface + return mixinType.cast( newProxyInstance( mixinType.getClassLoader(), new Class[]{ mixinType }, invocationHandler ) ); + } + + @Override + public String toString() + { + return types.toString(); + } +} \ 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/ConcernModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConcernModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConcernModel.java new file mode 100644 index 0000000..b583aaf --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConcernModel.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 org.apache.polygene.api.concern.ConcernDescriptor; + +/** + * JAVADOC + */ +public final class ConcernModel extends AbstractModifierModel + implements ConcernDescriptor +{ + public ConcernModel( Class concernClass, Class instantiationClass ) + { + super( concernClass, instantiationClass ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConcernsInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConcernsInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConcernsInstance.java new file mode 100644 index 0000000..da2fe20 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConcernsInstance.java @@ -0,0 +1,70 @@ +/* + * 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 ConcernsInstance + implements InvocationHandler +{ + private final InvocationHandler firstConcern; + private final FragmentInvocationHandler mixinInvocationHandler; + private final ProxyReferenceInvocationHandler proxyHandler; + + public ConcernsInstance( InvocationHandler firstConcern, + FragmentInvocationHandler mixinInvocationHandler, + ProxyReferenceInvocationHandler proxyHandler + ) + { + this.firstConcern = firstConcern; + this.mixinInvocationHandler = mixinInvocationHandler; + this.proxyHandler = proxyHandler; + } + + public boolean isEmpty() + { + return firstConcern == mixinInvocationHandler; + } + + @Override + public Object invoke( Object proxy, Method method, Object[] params ) + throws Throwable + { + proxyHandler.setProxy( proxy ); + try + { + return firstConcern.invoke( proxy, method, params ); + } + catch( InvocationTargetException e ) + { + throw e.getTargetException(); + } + finally + { + proxyHandler.clearProxy(); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConcernsModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConcernsModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConcernsModel.java new file mode 100644 index 0000000..aa8d3ce --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConcernsModel.java @@ -0,0 +1,89 @@ +/* + * 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.Collections; +import java.util.List; +import java.util.stream.Stream; +import org.apache.polygene.api.concern.ConcernsDescriptor; +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 ConcernsModel + implements ConcernsDescriptor, Dependencies, VisitableHierarchy<Object, Object> +{ + public static final ConcernsModel EMPTY_CONCERNS = new ConcernsModel( Collections.<ConcernModel>emptyList() ); + + private List<ConcernModel> concernsFor; + + public ConcernsModel( List<ConcernModel> concernsFor ) + { + this.concernsFor = concernsFor; + } + + @Override + public Stream<DependencyModel> dependencies() + { + return concernsFor.stream().flatMap( ConcernModel::dependencies ); + } + + // Context + public ConcernsInstance newInstance( Method method, ModuleDescriptor module, + FragmentInvocationHandler mixinInvocationHandler + ) + { + ProxyReferenceInvocationHandler proxyHandler = new ProxyReferenceInvocationHandler(); + InvocationHandler nextConcern = mixinInvocationHandler; + for( int i = concernsFor.size() - 1; i >= 0; i-- ) + { + ConcernModel concernModel = concernsFor.get( i ); + + nextConcern = concernModel.newInstance( module, nextConcern, proxyHandler, method ); + } + + return new ConcernsInstance( nextConcern, mixinInvocationHandler, proxyHandler ); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> modelVisitor ) + throws ThrowableType + { + if( modelVisitor.visitEnter( this ) ) + { + for( ConcernModel concernModel : concernsFor ) + { + if( !concernModel.accept( modelVisitor ) ) + { + break; + } + } + } + return modelVisitor.visitLeave( this ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintDeclaration.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintDeclaration.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintDeclaration.java new file mode 100644 index 0000000..9826681 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintDeclaration.java @@ -0,0 +1,75 @@ +/* + * 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.annotation.Annotation; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import org.apache.polygene.api.constraint.Constraint; +import org.apache.polygene.api.util.Classes; + +/** + * JAVADOC + */ +public final class ConstraintDeclaration +{ + private final Class<? extends Constraint<?, ?>> constraintClass; + @SuppressWarnings( "raw" ) + private final Class constraintAnnotationType; + private final Type constraintValueType; + + @SuppressWarnings( "unchecked" ) + public ConstraintDeclaration( Class<? extends Constraint<?, ?>> constraintClass ) + { + this.constraintClass = constraintClass; + + constraintAnnotationType = (Class<? extends Annotation>) ( (ParameterizedType) constraintClass.getGenericInterfaces()[ 0 ] ) + .getActualTypeArguments()[ 0 ]; + constraintValueType = ( (ParameterizedType) constraintClass.getGenericInterfaces()[ 0 ] ).getActualTypeArguments()[ 1 ]; + } + + public Class<? extends Constraint<?, ?>> constraintClass() + { + return constraintClass; + } + + @SuppressWarnings( {"raw", "unchecked"} ) + public boolean appliesTo( Class annotationType, Type valueType ) + { + if( constraintValueType instanceof Class ) + { + Class constraintValueClass = (Class) constraintValueType; + Class valueClass = Classes.RAW_CLASS.apply( valueType ); + return constraintAnnotationType.equals( annotationType ) && constraintValueClass.isAssignableFrom( valueClass ); + } + else if( constraintValueType instanceof ParameterizedType ) + { + // TODO Handle nested generics + Class constraintValueClass = Classes.RAW_CLASS.apply( constraintValueType ); + Class valueClass = Classes.RAW_CLASS.apply( valueType ); + return constraintAnnotationType.equals( annotationType ) && constraintValueClass.isAssignableFrom( valueClass ); + } + else + { + return false; // TODO Handles more cases. What are they? + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintInstance.java new file mode 100644 index 0000000..40645f9 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintInstance.java @@ -0,0 +1,49 @@ +/* + * 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.annotation.Annotation; +import org.apache.polygene.api.constraint.Constraint; + +/** + * JAVADOC + */ +public final class ConstraintInstance<A extends Annotation, T> +{ + private final Constraint<A, T> constraint; + private final A annotation; + + public ConstraintInstance( Constraint<A, T> constraint, A annotation ) + { + this.constraint = constraint; + this.annotation = annotation; + } + + public A annotation() + { + return annotation; + } + + public boolean isValid( T value ) + { + return constraint.isValid( annotation, value ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintModel.java new file mode 100644 index 0000000..1452eeb --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintModel.java @@ -0,0 +1,55 @@ +/* + * 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.annotation.Annotation; +import org.apache.polygene.api.common.ConstructionException; +import org.apache.polygene.api.constraint.Constraint; + +/** + * JAVADOC + */ +public final class ConstraintModel + extends AbstractConstraintModel +{ + private final Class<? extends Constraint<?, ?>> constraintClass; + + public ConstraintModel( Annotation annotation, Class<? extends Constraint<?, ?>> constraintClass ) + { + super( annotation ); + this.constraintClass = constraintClass; + } + + @Override + @SuppressWarnings( "unchecked" ) + public ConstraintInstance<?, ?> newInstance() + { + try + { + Constraint<?, ?> constraint = constraintClass.newInstance(); + return new ConstraintInstance( constraint, annotation ); + } + catch( Exception e ) + { + throw new ConstructionException( "Could not instantiate constraint implementation", e ); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintsCheck.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintsCheck.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintsCheck.java new file mode 100644 index 0000000..1f72dc3 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintsCheck.java @@ -0,0 +1,32 @@ +/* + * 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.constraint.ConstraintViolationException; + +/** + * Call this to perform a value constraint check + */ +public interface ConstraintsCheck +{ + public void checkConstraints( Object value ) + throws ConstraintViolationException; +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintsInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintsInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintsInstance.java new file mode 100644 index 0000000..23e8f98 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintsInstance.java @@ -0,0 +1,84 @@ +/* + * 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.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; +import org.apache.polygene.api.composite.Composite; +import org.apache.polygene.api.composite.CompositeInstance; +import org.apache.polygene.api.constraint.ConstraintViolation; +import org.apache.polygene.api.constraint.ConstraintViolationException; + +/** + * JAVADOC + */ +public final class ConstraintsInstance +{ + private final List<ValueConstraintsInstance> valueConstraintsInstances; + + public ConstraintsInstance( List<ValueConstraintsInstance> parameterConstraints ) + { + valueConstraintsInstances = parameterConstraints; + } + + @SuppressWarnings( "unchecked" ) + public void checkValid( Object instance, Method method, Object[] params ) + throws ConstraintViolationException + { + if( valueConstraintsInstances.isEmpty() ) + { + return; // No constraints to check + } + + // Check constraints + List<ConstraintViolation> violations = null; + for( int i = 0; i < params.length; i++ ) + { + Object param = params[ i ]; + List<ConstraintViolation> paramViolations = valueConstraintsInstances.get( i ).checkConstraints( param ); + if( !paramViolations.isEmpty() ) + { + if( violations == null ) + { + violations = new ArrayList<>(); + } + violations.addAll( paramViolations ); + } + } + + // Check if any constraint failed + if( violations != null ) + { + if( instance instanceof Composite ) + { + throw new ConstraintViolationException( (Composite) instance, method, violations ); + } + if( instance instanceof CompositeInstance ) + { + throw new ConstraintViolationException( ( (CompositeInstance) instance ).proxy(), method, violations ); + } + Stream<Class<?>> types = Stream.of( instance.getClass() ); + throw new ConstraintViolationException( instance.toString(), types, method, violations ); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintsModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintsModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintsModel.java new file mode 100644 index 0000000..542217e --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstraintsModel.java @@ -0,0 +1,82 @@ +/* + * 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.ArrayList; +import java.util.Collections; +import java.util.List; +import org.apache.polygene.api.constraint.ConstraintsDescriptor; +import org.apache.polygene.api.util.HierarchicalVisitor; +import org.apache.polygene.api.util.VisitableHierarchy; + +/** + * JAVADOC + */ +public final class ConstraintsModel + implements ConstraintsDescriptor, VisitableHierarchy<Object, Object> +{ + private List<ValueConstraintsModel> parameterConstraintModels; + + private static ConstraintsInstance EMPTY_CONSTRAINTS = new ConstraintsInstance( Collections.<ValueConstraintsInstance>emptyList() ); + + public ConstraintsModel( List<ValueConstraintsModel> parameterConstraintModels ) + { + this.parameterConstraintModels = parameterConstraintModels; + } + + public ConstraintsInstance newInstance() + { + if( parameterConstraintModels.isEmpty() ) + { + return EMPTY_CONSTRAINTS; + } + else + { + List<ValueConstraintsInstance> parameterConstraintsInstances = new ArrayList<ValueConstraintsInstance>( parameterConstraintModels + .size() ); + for( ValueConstraintsModel parameterConstraintModel : parameterConstraintModels ) + { + parameterConstraintsInstances.add( parameterConstraintModel.newInstance() ); + } + return new ConstraintsInstance( parameterConstraintsInstances ); + } + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> modelVisitor ) + throws ThrowableType + { + if( modelVisitor.visitEnter( this ) ) + { + if( parameterConstraintModels != null ) + { + for( ValueConstraintsModel parameterConstraintModel : parameterConstraintModels ) + { + if( !parameterConstraintModel.accept( modelVisitor ) ) + { + break; + } + } + } + } + return modelVisitor.visitLeave( this ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstructorModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstructorModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstructorModel.java new file mode 100644 index 0000000..3df1ad5 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstructorModel.java @@ -0,0 +1,112 @@ +/* + * 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.InvocationTargetException; +import java.util.Arrays; +import java.util.stream.Stream; +import org.apache.polygene.api.common.ConstructionException; +import org.apache.polygene.api.composite.ConstructorDescriptor; +import org.apache.polygene.api.composite.InvalidCompositeException; +import org.apache.polygene.api.util.HierarchicalVisitor; +import org.apache.polygene.api.util.VisitableHierarchy; +import org.apache.polygene.runtime.injection.DependencyModel; +import org.apache.polygene.runtime.injection.InjectedParametersModel; +import org.apache.polygene.runtime.injection.InjectionContext; + +/** + * JAVADOC + */ +public final class ConstructorModel + implements ConstructorDescriptor, VisitableHierarchy<Object, Object> +{ + private Constructor<?> constructor; + + private InjectedParametersModel parameters; + + public ConstructorModel( Constructor<?> constructor, InjectedParametersModel parameters ) + { + this.constructor = constructor; + this.parameters = parameters; + this.constructor.setAccessible( true ); + } + + @Override + public Constructor<?> constructor() + { + return constructor; + } + + public Stream<DependencyModel> dependencies() + { + return parameters.dependencies(); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> modelVisitor ) + throws ThrowableType + { + if( modelVisitor.visitEnter( this ) ) + { + parameters.accept( modelVisitor ); + } + + return modelVisitor.visitLeave( this ); + } + + // Context + + public Object newInstance( InjectionContext context ) + throws ConstructionException + { + // Create parameters + Object[] parametersInstance = parameters.newParametersInstance( context ); + // Invoke constructor + try + { + return constructor.newInstance( parametersInstance ); + } + catch( InvocationTargetException e ) + { + Throwable targetException = e.getTargetException(); + if( targetException instanceof InvalidCompositeException ) + { + throw (InvalidCompositeException) targetException; + } + String message = "Could not instantiate \n " + constructor.getDeclaringClass() + "\nusing constructor:\n " + constructor + .toGenericString(); + throw new ConstructionException( message, targetException ); + } + catch( Throwable e ) + { + System.err.println( constructor.toGenericString() ); + System.err.println( Arrays.asList( parametersInstance ) ); + throw new ConstructionException( "Could not instantiate " + constructor.getDeclaringClass(), e ); + } + } + + @Override + public String toString() + { + return constructor.toGenericString(); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstructorsModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstructorsModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstructorsModel.java new file mode 100644 index 0000000..bc4be4a --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ConstructorsModel.java @@ -0,0 +1,298 @@ +/* + * 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.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Stream; +import org.apache.polygene.api.common.ConstructionException; +import org.apache.polygene.api.composite.CompositeDescriptor; +import org.apache.polygene.api.composite.InvalidCompositeException; +import org.apache.polygene.api.injection.InjectionScope; +import org.apache.polygene.api.injection.scope.Uses; +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.InjectedParametersModel; +import org.apache.polygene.runtime.injection.InjectionContext; +import org.apache.polygene.runtime.injection.ParameterizedTypeInstance; +import org.apache.polygene.runtime.model.Binder; +import org.apache.polygene.runtime.model.Resolution; + +import static org.apache.polygene.api.util.Annotations.typeHasAnnotation; + +/** + * JAVADOC + */ +public final class ConstructorsModel + implements Binder, Dependencies, VisitableHierarchy<Object, Object> +{ + @SuppressWarnings( "raw" ) + private final Class<?> fragmentClass; + private final List<ConstructorModel> constructorModels; + private List<ConstructorModel> boundConstructors; + + @SuppressWarnings( { "raw", "unchecked" } ) + public ConstructorsModel( Class<?> fragmentClass ) + { + this.fragmentClass = fragmentClass; + validate( fragmentClass ); + constructorModels = new ArrayList<>(); + Constructor<?>[] realConstructors = this.fragmentClass.getDeclaredConstructors(); + Class<?> injectionClass = FragmentClassLoader.getSourceClass( fragmentClass ); + for( Constructor<?> constructor : realConstructors ) + { + constructor.setAccessible( true ); + try + { + Constructor<?> injectionConstructor = injectionClass.getDeclaredConstructor( constructor.getParameterTypes() ); + injectionConstructor.setAccessible( true ); + ConstructorModel constructorModel = newConstructorModel( this.fragmentClass, constructor, + injectionConstructor ); + if( constructorModel != null ) + { + constructorModels.add( constructorModel ); + } + } + catch( NoSuchMethodException e ) + { + // Ignore and continue + e.printStackTrace(); + } + } + } + + @SuppressWarnings( "raw" ) + private void validate( Class<?> fragmentClass ) + { + // Ensure that the fragment class is not an inner class, in which case we should give a reasonable exception + if( fragmentClass.getDeclaringClass() == null ) + { + return; + } + if( Modifier.isStatic( fragmentClass.getModifiers() ) ) + { + return; + } + throw new InvalidCompositeException( "Inner classes can not be used. Use static nested classes instead: " + fragmentClass ); + } + + @Override + public Stream<DependencyModel> dependencies() + { + if( boundConstructors == null ) + { + return constructorModels.stream().flatMap( ConstructorModel::dependencies ); + } + return boundConstructors.stream().flatMap( ConstructorModel::dependencies ); + } + + @SuppressWarnings( "raw" ) + private ConstructorModel newConstructorModel( Class<?> fragmentClass, + Constructor<?> realConstructor, + Constructor<?> injectedConstructor + ) + { + int idx = 0; + InjectedParametersModel parameters = new InjectedParametersModel(); + Annotation[][] parameterAnnotations = injectedConstructor.getParameterAnnotations(); + for( Type type : injectedConstructor.getGenericParameterTypes() ) + { + Annotation injectionAnnotation = Stream.of( parameterAnnotations[ idx ] ) + .filter( typeHasAnnotation( InjectionScope.class ) ) + .findFirst().orElse( null ); + + if( injectionAnnotation == null ) + { + if( fragmentClass.getSuperclass().isMemberClass() ) + { + injectionAnnotation = new Uses() + { + @Override + public Class<? extends Annotation> annotationType() + { + return Uses.class; + } + }; + } + else + { + return null; // invalid constructor parameter + } + } + + boolean optional = DependencyModel.isOptional( injectionAnnotation, parameterAnnotations[ idx ] ); + + Type genericType = type; + if( genericType instanceof ParameterizedType ) + { + genericType = new ParameterizedTypeInstance( ( (ParameterizedType) genericType ).getActualTypeArguments(), ( (ParameterizedType) genericType ) + .getRawType(), ( (ParameterizedType) genericType ).getOwnerType() ); + + for( int i = 0; i < ( (ParameterizedType) genericType ).getActualTypeArguments().length; i++ ) + { + Type typeArg = ( (ParameterizedType) genericType ).getActualTypeArguments()[ i ]; + if( typeArg instanceof TypeVariable ) + { + typeArg = Classes.resolveTypeVariable( (TypeVariable) typeArg, realConstructor.getDeclaringClass(), fragmentClass ); + ( (ParameterizedType) genericType ).getActualTypeArguments()[ i ] = typeArg; + } + } + } + + DependencyModel dependencyModel = new DependencyModel( injectionAnnotation, genericType, fragmentClass, optional, + parameterAnnotations[ idx ] ); + parameters.addDependency( dependencyModel ); + idx++; + } + return new ConstructorModel( realConstructor, parameters ); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> visitor ) + throws ThrowableType + { + if( visitor.visitEnter( this ) ) + { + if( boundConstructors != null ) + { + for( ConstructorModel constructorModel : boundConstructors ) + { + if( !constructorModel.accept( visitor ) ) + { + break; + } + } + } + else + { + for( ConstructorModel constructorModel : constructorModels ) + { + if( !constructorModel.accept( visitor ) ) + { + break; + } + } + } + } + return visitor.visitLeave( this ); + } + + // Binding + @Override + public void bind( final Resolution resolution ) + throws BindingException + { + boundConstructors = new ArrayList<>(); + for( ConstructorModel constructorModel : constructorModels ) + { + try + { + constructorModel.accept( new HierarchicalVisitorAdapter<Object, Object, BindingException>() + { + @Override + public boolean visit( Object visitor ) + throws BindingException + { + if( visitor instanceof Binder ) + { + ( (Binder) visitor ).bind( resolution ); + } + return true; + } + } ); + boundConstructors.add( constructorModel ); + } + catch( Exception e ) + { + // Ignore + e.printStackTrace(); + } + } + + if( boundConstructors.isEmpty() ) + { + StringBuilder messageBuilder = new StringBuilder( "Found no constructor that could be bound: " ); + if( resolution.model() instanceof CompositeDescriptor ) + { + messageBuilder.append( fragmentClass.getName() ) + .append( " in " ) + .append( resolution.model().toString() ); + } + else + { + messageBuilder.append( resolution.model().toString() ); + } + + if( messageBuilder.indexOf( "$" ) >= 0 ) + { + // This could be ok if instance is created manually + return; +// messageBuilder.append( "\nInner classes can not be used." ); + } + String message = messageBuilder.toString(); + throw new BindingException( message ); + } + + // Sort based on parameter count + Collections.sort( boundConstructors, new Comparator<ConstructorModel>() + { + @Override + public int compare( ConstructorModel o1, ConstructorModel o2 ) + { + Integer model2ParametersCount = o2.constructor().getParameterTypes().length; + int model1ParametersCount = o1.constructor().getParameterTypes().length; + return model2ParametersCount.compareTo( model1ParametersCount ); + } + } ); + } + + public Object newInstance( InjectionContext injectionContext ) + { + // Try all bound constructors, in order + ConstructionException exception = null; + for( ConstructorModel constructorModel : boundConstructors ) + { + try + { + return constructorModel.newInstance( injectionContext ); + } + catch( ConstructionException e ) + { + exception = e; + } + } + + throw exception; + } +}
