diff --git a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/AnnotationLiteralGenerator.java b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/AnnotationLiteralGenerator.java index 8aea39feb4caf..0bfb73734f30a 100644 --- a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/AnnotationLiteralGenerator.java +++ b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/AnnotationLiteralGenerator.java @@ -14,6 +14,8 @@ import org.jboss.jandex.ArrayType; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.PrimitiveType; +import org.jboss.jandex.Type; import org.jboss.logging.Logger; import org.jboss.protean.arc.ComputingCache; import org.jboss.protean.arc.processor.AnnotationLiteralProcessor.CacheKey; @@ -72,47 +74,94 @@ static void createAnnotationLiteral(ClassOutput classOutput, ClassInfo annotatio value = method.defaultValue(); } ResultHandle retValue = null; - if (value != null) { + if (value == null) { + switch (method.returnType().kind()) { + case CLASS: + case ARRAY: + retValue = valueMethod.loadNull(); + break; + case PRIMITIVE: + PrimitiveType primitiveType = method.returnType().asPrimitiveType(); + switch (primitiveType.primitive()) { + case BOOLEAN: + retValue = valueMethod.load(false); + break; + case BYTE: + case SHORT: + case INT: + retValue = valueMethod.load(0); + break; + case LONG: + retValue = valueMethod.load(0L); + break; + case FLOAT: + retValue = valueMethod.load(0.0f); + break; + case DOUBLE: + retValue = valueMethod.load(0.0d); + break; + case CHAR: + retValue = valueMethod.load('\u0000'); + break; + } + break; + default: + break; + } + } else { switch (value.kind()) { case BOOLEAN: - retValue = value != null ? valueMethod.load(value.asBoolean()) : valueMethod.load(false); + retValue = valueMethod.load(value.asBoolean()); break; case STRING: - retValue = value != null ? valueMethod.load(value.asString()) : valueMethod.loadNull(); + retValue = valueMethod.load(value.asString()); break; case BYTE: - retValue = value != null ? valueMethod.load(value.asByte()) : valueMethod.load(0); + retValue = valueMethod.load(value.asByte()); break; case SHORT: - retValue = value != null ? valueMethod.load(value.asShort()) : valueMethod.load(0); + retValue = valueMethod.load(value.asShort()); break; case LONG: - retValue = value != null ? valueMethod.load(value.asLong()) : valueMethod.load(0L); + retValue = valueMethod.load(value.asLong()); break; case INTEGER: - retValue = value != null ? valueMethod.load(value.asInt()) : valueMethod.load(0); + retValue = valueMethod.load(value.asInt()); break; case FLOAT: - retValue = value != null ? valueMethod.load(value.asFloat()) : valueMethod.load(0.0f); + retValue = valueMethod.load(value.asFloat()); break; case DOUBLE: - retValue = value != null ? valueMethod.load(value.asDouble()) : valueMethod.load(0.0d); + retValue = valueMethod.load(value.asDouble()); break; case CHARACTER: - retValue = value != null ? valueMethod.load(value.asChar()) : valueMethod.load('\u0000'); + retValue = valueMethod.load(value.asChar()); break; case CLASS: - retValue = value != null ? valueMethod.loadClass(value.asClass().toString()) : valueMethod.loadNull(); + retValue = valueMethod.loadClass(value.asClass().toString()); break; case ARRAY: - // Always return an empty array - // Array members must be Nonbinding - retValue = value != null ? valueMethod.newArray(componentType(method), valueMethod.load(0)) : valueMethod.loadNull(); + switch (value.componentKind()) { + case CLASS: + Type[] classArray = value.asClassArray(); + retValue = valueMethod.newArray(componentType(method), valueMethod.load(classArray.length)); + for (int i = 0; i < classArray.length; i++) { + valueMethod.writeArrayValue(retValue, valueMethod.load(i), valueMethod.loadClass(classArray[i].name().toString())); + } + break; + // TODO other types of array components + // Note that array members should be Nonbinding in CDI + default: + // For an empty array component kind is UNKNOWN + if (!method.returnType().name().equals(DotNames.CLASS)) { + LOGGER.warnf("Unsupported array component type %s on %s - literal returns an empty array", method, annotationClass); + } + retValue = valueMethod.newArray(componentType(method), valueMethod.load(0)); + } break; case ENUM: - retValue = value != null - ? valueMethod.readStaticField(FieldDescriptor.of(value.asEnumType().toString(), value.asEnum(), value.asEnumType().toString())) - : valueMethod.loadNull(); + retValue = valueMethod + .readStaticField(FieldDescriptor.of(value.asEnumType().toString(), value.asEnum(), value.asEnumType().toString())); break; case NESTED: default: diff --git a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanGenerator.java b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanGenerator.java index 0088a17b1cd89..52ecd280b9185 100644 --- a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanGenerator.java +++ b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanGenerator.java @@ -5,7 +5,6 @@ import static org.objectweb.asm.Opcodes.ACC_PRIVATE; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; -import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; @@ -43,9 +42,9 @@ import org.jboss.protean.arc.InjectableBean; import org.jboss.protean.arc.InjectableInterceptor; import org.jboss.protean.arc.InjectableReferenceProvider; -import org.jboss.protean.arc.InvocationContextImpl; import org.jboss.protean.arc.LazyValue; import org.jboss.protean.arc.Subclass; +import org.jboss.protean.arc.processor.BeanInfo.InterceptionInfo; import org.jboss.protean.arc.processor.ResourceOutput.Resource; import org.jboss.protean.arc.processor.ResourceOutput.Resource.SpecialType; import org.jboss.protean.gizmo.BytecodeCreator; @@ -78,25 +77,29 @@ public class BeanGenerator extends AbstractGenerator { private static final String FIELD_NAME_DECLARING_PROVIDER = "declaringProvider"; + protected final AnnotationLiteralProcessor annotationLiterals; + + public BeanGenerator(AnnotationLiteralProcessor annotationLiterals) { + this.annotationLiterals = annotationLiterals; + } + /** * * @param bean - * @param annotationLiterals * @return a collection of resources */ - Collection generate(BeanInfo bean, AnnotationLiteralProcessor annotationLiterals, ReflectionRegistration reflectionRegistration) { + Collection generate(BeanInfo bean, ReflectionRegistration reflectionRegistration) { if (Kind.CLASS.equals(bean.getTarget().kind())) { - return generateClassBean(bean, bean.getTarget().asClass(), annotationLiterals, reflectionRegistration); + return generateClassBean(bean, bean.getTarget().asClass(), reflectionRegistration); } else if (Kind.METHOD.equals(bean.getTarget().kind())) { - return generateProducerMethodBean(bean, bean.getTarget().asMethod(), annotationLiterals, reflectionRegistration); + return generateProducerMethodBean(bean, bean.getTarget().asMethod(), reflectionRegistration); } else if (Kind.FIELD.equals(bean.getTarget().kind())) { - return generateProducerFieldBean(bean, bean.getTarget().asField(), annotationLiterals, reflectionRegistration); + return generateProducerFieldBean(bean, bean.getTarget().asField(), reflectionRegistration); } throw new IllegalArgumentException("Unsupported bean type"); } - Collection generateClassBean(BeanInfo bean, ClassInfo beanClass, AnnotationLiteralProcessor annotationLiterals, - ReflectionRegistration reflectionRegistration) { + Collection generateClassBean(BeanInfo bean, ClassInfo beanClass, ReflectionRegistration reflectionRegistration) { String baseName; if (beanClass.enclosingClass() != null) { @@ -135,7 +138,8 @@ Collection generateClassBean(BeanInfo bean, ClassInfo beanClass, Annot if (!bean.hasDefaultDestroy()) { createDestroy(bean, beanCreator, providerTypeName, injectionPointToProviderField, reflectionRegistration); } - createCreate(beanCreator, bean, providerTypeName, baseName, injectionPointToProviderField, interceptorToProviderField, reflectionRegistration); + createCreate(classOutput, beanCreator, bean, providerTypeName, baseName, injectionPointToProviderField, interceptorToProviderField, + reflectionRegistration); createGet(bean, beanCreator, providerTypeName); createGetTypes(beanCreator, beanTypes.getFieldDescriptor()); @@ -153,8 +157,7 @@ Collection generateClassBean(BeanInfo bean, ClassInfo beanClass, Annot return classOutput.getResources(); } - Collection generateProducerMethodBean(BeanInfo bean, MethodInfo producerMethod, AnnotationLiteralProcessor annotationLiterals, - ReflectionRegistration reflectionRegistration) { + Collection generateProducerMethodBean(BeanInfo bean, MethodInfo producerMethod, ReflectionRegistration reflectionRegistration) { ClassInfo declaringClass = producerMethod.declaringClass(); String declaringClassBase; @@ -196,7 +199,7 @@ Collection generateProducerMethodBean(BeanInfo bean, MethodInfo produc if (!bean.hasDefaultDestroy()) { createDestroy(bean, beanCreator, providerTypeName, injectionPointToProviderField, reflectionRegistration); } - createCreate(beanCreator, bean, providerTypeName, baseName, injectionPointToProviderField, Collections.emptyMap(), reflectionRegistration); + createCreate(classOutput, beanCreator, bean, providerTypeName, baseName, injectionPointToProviderField, Collections.emptyMap(), reflectionRegistration); createGet(bean, beanCreator, providerTypeName); createGetTypes(beanCreator, beanTypes.getFieldDescriptor()); @@ -212,8 +215,7 @@ Collection generateProducerMethodBean(BeanInfo bean, MethodInfo produc return classOutput.getResources(); } - Collection generateProducerFieldBean(BeanInfo bean, FieldInfo producerField, AnnotationLiteralProcessor annotationLiterals, - ReflectionRegistration reflectionRegistration) { + Collection generateProducerFieldBean(BeanInfo bean, FieldInfo producerField, ReflectionRegistration reflectionRegistration) { ClassInfo declaringClass = producerField.declaringClass(); String declaringClassBase; @@ -251,7 +253,7 @@ Collection generateProducerFieldBean(BeanInfo bean, FieldInfo producer if (!bean.hasDefaultDestroy()) { createDestroy(bean, beanCreator, providerTypeName, null, reflectionRegistration); } - createCreate(beanCreator, bean, providerTypeName, baseName, Collections.emptyMap(), Collections.emptyMap(), reflectionRegistration); + createCreate(classOutput, beanCreator, bean, providerTypeName, baseName, Collections.emptyMap(), Collections.emptyMap(), reflectionRegistration); createGet(bean, beanCreator, providerTypeName); createGetTypes(beanCreator, beanTypes.getFieldDescriptor()); @@ -541,6 +543,7 @@ protected void createDestroy(BeanInfo bean, ClassCreator beanCreator, String pro /** * + * @param classOutput * @param beanCreator * @param bean * @param baseName @@ -548,7 +551,7 @@ protected void createDestroy(BeanInfo bean, ClassCreator beanCreator, String pro * @param interceptorToProviderField * @see Contextual#create(CreationalContext) */ - protected void createCreate(ClassCreator beanCreator, BeanInfo bean, String providerTypeName, String baseName, + protected void createCreate(ClassOutput classOutput, ClassCreator beanCreator, BeanInfo bean, String providerTypeName, String baseName, Map injectionPointToProviderField, Map interceptorToProviderField, ReflectionRegistration reflectionRegistration) { @@ -565,13 +568,13 @@ protected void createCreate(ClassCreator beanCreator, BeanInfo bean, String prov if (bean.hasLifecycleInterceptors()) { // Note that we must share the interceptors instances with the intercepted subclass, if present - List postConstructs = bean.getLifecycleInterceptors(InterceptionType.POST_CONSTRUCT); - List aroundConstructs = bean.getLifecycleInterceptors(InterceptionType.AROUND_CONSTRUCT); + InterceptionInfo postConstructs = bean.getLifecycleInterceptors(InterceptionType.POST_CONSTRUCT); + InterceptionInfo aroundConstructs = bean.getLifecycleInterceptors(InterceptionType.AROUND_CONSTRUCT); // Wrap InjectableInterceptors using InitializedInterceptor Set wraps = new HashSet<>(); - wraps.addAll(aroundConstructs); - wraps.addAll(postConstructs); + wraps.addAll(aroundConstructs.interceptors); + wraps.addAll(postConstructs.interceptors); for (InterceptorInfo interceptor : wraps) { ResultHandle interceptorProvider = create.readInstanceField( FieldDescriptor.of(beanCreator.getClassName(), interceptorToProviderField.get(interceptor), InjectableInterceptor.class.getName()), @@ -586,14 +589,16 @@ protected void createCreate(ClassCreator beanCreator, BeanInfo bean, String prov if (!postConstructs.isEmpty()) { // postConstructs = new ArrayList() postConstructsHandle = create.newInstance(MethodDescriptor.ofConstructor(ArrayList.class)); - for (InterceptorInfo interceptor : postConstructs) { + for (InterceptorInfo interceptor : postConstructs.interceptors) { ResultHandle interceptorHandle = create.readInstanceField(FieldDescriptor.of(beanCreator.getClassName(), interceptorToProviderField.get(interceptor), InjectableInterceptor.class.getName()), create.getThis()); ResultHandle childCtxHandle = create.invokeStaticMethod(MethodDescriptors.CREATIONAL_CTX_CHILD, create.getMethodParam(0)); ResultHandle interceptorInstanceHandle = create.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, interceptorHandle, childCtxHandle); + ResultHandle interceptorInvocationHandle = create.invokeStaticMethod(MethodDescriptors.INTERCEPTOR_INVOCATION_POST_CONSTRUCT, interceptorHandle, interceptorInstanceHandle); + // postConstructs.add(InterceptorInvocation.postConstruct(interceptor,interceptor.get(CreationalContextImpl.child(ctx)))) create.invokeInterfaceMethod(MethodDescriptors.LIST_ADD, postConstructsHandle, interceptorInvocationHandle); } @@ -601,14 +606,16 @@ protected void createCreate(ClassCreator beanCreator, BeanInfo bean, String prov if (!aroundConstructs.isEmpty()) { // aroundConstructs = new ArrayList() aroundConstructsHandle = create.newInstance(MethodDescriptor.ofConstructor(ArrayList.class)); - for (InterceptorInfo interceptor : aroundConstructs) { + for (InterceptorInfo interceptor : aroundConstructs.interceptors) { ResultHandle interceptorHandle = create.readInstanceField(FieldDescriptor.of(beanCreator.getClassName(), interceptorToProviderField.get(interceptor), InjectableInterceptor.class.getName()), create.getThis()); ResultHandle childCtxHandle = create.invokeStaticMethod(MethodDescriptors.CREATIONAL_CTX_CHILD, create.getMethodParam(0)); ResultHandle interceptorInstanceHandle = create.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, interceptorHandle, childCtxHandle); + ResultHandle interceptorInvocationHandle = create.invokeStaticMethod(MethodDescriptors.INTERCEPTOR_INVOCATION_AROUND_CONSTRUCT, interceptorHandle, interceptorInstanceHandle); + // aroundConstructs.add(InterceptorInvocation.aroundConstruct(interceptor,interceptor.get(CreationalContextImpl.child(ctx)))) create.invokeInterfaceMethod(MethodDescriptors.LIST_ADD, aroundConstructsHandle, interceptorInvocationHandle); } @@ -616,7 +623,8 @@ protected void createCreate(ClassCreator beanCreator, BeanInfo bean, String prov } // AroundConstruct lifecycle callback interceptors - if (!bean.getLifecycleInterceptors(InterceptionType.AROUND_CONSTRUCT).isEmpty()) { + InterceptionInfo aroundConstructs = bean.getLifecycleInterceptors(InterceptionType.AROUND_CONSTRUCT); + if (!aroundConstructs.isEmpty()) { Optional constructorInjection = bean.getConstructorInjection(); ResultHandle constructorHandle; if (constructorInjection.isPresent()) { @@ -648,15 +656,21 @@ protected void createCreate(ClassCreator beanCreator, BeanInfo bean, String prov ResultHandle retHandle = newInstanceHandle(bean, beanCreator, funcBytecode, create, providerTypeName, baseName, providerHandles, reflectionRegistration); funcBytecode.returnValue(retHandle); + // Interceptor bindings + ResultHandle bindingsHandle = create.newInstance(MethodDescriptor.ofConstructor(HashSet.class)); + for (AnnotationInstance binding : aroundConstructs.bindings) { + // Create annotation literals first + ClassInfo bindingClass = bean.getDeployment().getInterceptorBinding(binding.name()); + String literalType = annotationLiterals.process(classOutput, bindingClass, binding, Types.getPackageName(beanCreator.getClassName())); + create.invokeInterfaceMethod(MethodDescriptors.SET_ADD, bindingsHandle, create.newInstance(MethodDescriptor.ofConstructor(literalType))); + } // InvocationContextImpl.aroundConstruct(constructor,aroundConstructs,forward).proceed() - ResultHandle invocationContextHandle = create.invokeStaticMethod(MethodDescriptor.ofMethod(InvocationContextImpl.class, "aroundConstruct", - InvocationContextImpl.class, Constructor.class, List.class, Supplier.class), constructorHandle, aroundConstructsHandle, - func.getInstance()); + ResultHandle invocationContextHandle = create.invokeStaticMethod(MethodDescriptors.INVOCATION_CONTEXT_AROUND_CONSTRUCT, constructorHandle, + aroundConstructsHandle, func.getInstance(), bindingsHandle); ExceptionTable tryCatch = create.addTryCatch(); CatchBlockCreator exceptionCatch = tryCatch.addCatchClause(Exception.class); // throw new RuntimeException(e) - // TODO existing exception param exceptionCatch.throwException(RuntimeException.class, "Error invoking aroundConstructs", exceptionCatch.getCaughtException()); instanceHandle = create.invokeInterfaceMethod(MethodDescriptor.ofMethod(InvocationContext.class, "proceed", Object.class), invocationContextHandle); @@ -716,17 +730,25 @@ protected void createCreate(ClassCreator beanCreator, BeanInfo bean, String prov } // PostConstruct lifecycle callback interceptors - if (!bean.getLifecycleInterceptors(InterceptionType.POST_CONSTRUCT).isEmpty()) { + InterceptionInfo postConstructs = bean.getLifecycleInterceptors(InterceptionType.POST_CONSTRUCT); + if (!postConstructs.isEmpty()) { + + // Interceptor bindings + ResultHandle bindingsHandle = create.newInstance(MethodDescriptor.ofConstructor(HashSet.class)); + for (AnnotationInstance binding : postConstructs.bindings) { + // Create annotation literals first + ClassInfo bindingClass = bean.getDeployment().getInterceptorBinding(binding.name()); + String literalType = annotationLiterals.process(classOutput, bindingClass, binding, Types.getPackageName(beanCreator.getClassName())); + create.invokeInterfaceMethod(MethodDescriptors.SET_ADD, bindingsHandle, create.newInstance(MethodDescriptor.ofConstructor(literalType))); + } // InvocationContextImpl.postConstruct(instance,postConstructs).proceed() - ResultHandle invocationContextHandle = create.invokeStaticMethod( - MethodDescriptor.ofMethod(InvocationContextImpl.class, "postConstruct", InvocationContextImpl.class, Object.class, List.class), - instanceHandle, postConstructsHandle); + ResultHandle invocationContextHandle = create.invokeStaticMethod(MethodDescriptors.INVOCATION_CONTEXT_POST_CONSTRUCT, instanceHandle, + postConstructsHandle, bindingsHandle); ExceptionTable tryCatch = create.addTryCatch(); CatchBlockCreator exceptionCatch = tryCatch.addCatchClause(Exception.class); // throw new RuntimeException(e) - // TODO existing exception param exceptionCatch.throwException(RuntimeException.class, "Error invoking postConstructs", exceptionCatch.getCaughtException()); create.invokeInterfaceMethod(MethodDescriptor.ofMethod(InvocationContext.class, "proceed", Object.class), invocationContextHandle); tryCatch.complete(); diff --git a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanInfo.java b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanInfo.java index 4e6e0c828d178..584ac7bbacd2a 100644 --- a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanInfo.java +++ b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanInfo.java @@ -44,9 +44,9 @@ class BeanInfo { private final DisposerInfo disposer; - private Map> interceptedMethods; + private Map interceptedMethods; - private Map> lifecycleInterceptors; + private Map lifecycleInterceptors; private final Integer alternativePriority; @@ -159,12 +159,12 @@ Optional getConstructorInjection() { return injections.isEmpty() ? Optional.empty() : injections.stream().filter(i -> i.isConstructor()).findAny(); } - Map> getInterceptedMethods() { + Map getInterceptedMethods() { return interceptedMethods; } - List getLifecycleInterceptors(InterceptionType interceptionType) { - return lifecycleInterceptors.containsKey(interceptionType) ? lifecycleInterceptors.get(interceptionType) : Collections.emptyList(); + InterceptionInfo getLifecycleInterceptors(InterceptionType interceptionType) { + return lifecycleInterceptors.containsKey(interceptionType) ? lifecycleInterceptors.get(interceptionType) : InterceptionInfo.EMPTY; } boolean hasLifecycleInterceptors() { @@ -193,11 +193,11 @@ boolean hasDefaultDestroy() { */ List getBoundInterceptors() { List bound = new ArrayList<>(); - for (List interceptors : lifecycleInterceptors.values()) { - bound.addAll(interceptors); + for (InterceptionInfo interception : lifecycleInterceptors.values()) { + bound.addAll(interception.interceptors); } if (!interceptedMethods.isEmpty()) { - bound.addAll(interceptedMethods.values().stream().flatMap(list -> list.stream()).collect(Collectors.toList())); + bound.addAll(interceptedMethods.values().stream().map(m -> m.interceptors).flatMap(list -> list.stream()).collect(Collectors.toList())); } return bound.isEmpty() ? Collections.emptyList() : bound.stream().distinct().sorted().collect(Collectors.toList()); } @@ -241,9 +241,9 @@ protected String getType() { } } - private Map> initInterceptedMethods() { + private Map initInterceptedMethods() { if (!isInterceptor() && isClassBean()) { - Map> interceptedMethods = new HashMap<>(); + Map interceptedMethods = new HashMap<>(); Map> candidates = new HashMap<>(); // TODO interceptor bindings are transitive!!! @@ -260,7 +260,7 @@ private Map> initInterceptedMethods() { for (Entry> entry : candidates.entrySet()) { List interceptors = beanDeployment.getInterceptorResolver().resolve(InterceptionType.AROUND_INVOKE, entry.getValue()); if (!interceptors.isEmpty()) { - interceptedMethods.put(entry.getKey().method, interceptors); + interceptedMethods.put(entry.getKey().method, new InterceptionInfo(interceptors, entry.getValue())); } } return interceptedMethods; @@ -269,9 +269,9 @@ private Map> initInterceptedMethods() { } } - private Map> initLifecycleInterceptors() { + private Map initLifecycleInterceptors() { if (!isInterceptor() && isClassBean()) { - Map> lifecycleInterceptors = new HashMap<>(); + Map lifecycleInterceptors = new HashMap<>(); Set classLevelBindings = new HashSet<>(); addClassLevelBindings(target.asClass(), classLevelBindings); putLifecycleInterceptors(lifecycleInterceptors, classLevelBindings, InterceptionType.POST_CONSTRUCT); @@ -283,11 +283,11 @@ private Map> initLifecycleInterceptors() } } - private void putLifecycleInterceptors(Map> lifecycleInterceptors, Set classLevelBindings, + private void putLifecycleInterceptors(Map lifecycleInterceptors, Set classLevelBindings, InterceptionType interceptionType) { List interceptors = beanDeployment.getInterceptorResolver().resolve(interceptionType, classLevelBindings); if (!interceptors.isEmpty()) { - lifecycleInterceptors.put(interceptionType, interceptors); + lifecycleInterceptors.put(interceptionType, new InterceptionInfo(interceptors, classLevelBindings)); } } @@ -303,6 +303,25 @@ private void addClassLevelBindings(ClassInfo classInfo, Collection interceptors; + + final Set bindings; + + InterceptionInfo(List interceptors, Set bindings) { + this.interceptors = interceptors; + this.bindings = bindings; + } + + boolean isEmpty() { + return interceptors.isEmpty(); + } + + } + @Override public String toString() { return getType() + " bean [types=" + types + ", qualifiers=" + qualifiers + ", target=" + target + "]"; diff --git a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanProcessor.java b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanProcessor.java index 9acd08586dea1..75ebdf6a24103 100644 --- a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanProcessor.java +++ b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanProcessor.java @@ -74,19 +74,18 @@ public BeanDeployment process() throws IOException { BeanDeployment beanDeployment = new BeanDeployment(new IndexWrapper(index), additionalBeanDefiningAnnotations, annotationTransformers); beanDeployment.init(); - BeanGenerator beanGenerator = new BeanGenerator(); + AnnotationLiteralProcessor annotationLiterals = new AnnotationLiteralProcessor(name, sharedAnnotationLiterals); + BeanGenerator beanGenerator = new BeanGenerator(annotationLiterals); ClientProxyGenerator clientProxyGenerator = new ClientProxyGenerator(); - InterceptorGenerator interceptorGenerator = new InterceptorGenerator(); - SubclassGenerator subclassGenerator = new SubclassGenerator(); - ObserverGenerator observerGenerator = new ObserverGenerator(); + InterceptorGenerator interceptorGenerator = new InterceptorGenerator(annotationLiterals); + SubclassGenerator subclassGenerator = new SubclassGenerator(annotationLiterals); + ObserverGenerator observerGenerator = new ObserverGenerator(annotationLiterals); AnnotationLiteralGenerator annotationLiteralsGenerator = new AnnotationLiteralGenerator(); Map beanToGeneratedName = new HashMap<>(); Map observerToGeneratedName = new HashMap<>(); Map interceptorToGeneratedName = new HashMap<>(); - AnnotationLiteralProcessor annotationLiterals = new AnnotationLiteralProcessor(name, sharedAnnotationLiterals); - long start = System.currentTimeMillis(); List resources = new ArrayList<>(); @@ -103,7 +102,7 @@ public BeanDeployment process() throws IOException { // Generate beans for (BeanInfo bean : beanDeployment.getBeans()) { - for (Resource resource : beanGenerator.generate(bean, annotationLiterals, reflectionRegistration)) { + for (Resource resource : beanGenerator.generate(bean, reflectionRegistration)) { resources.add(resource); if (SpecialType.BEAN.equals(resource.getSpecialType())) { if (bean.getScope().isNormal()) { @@ -120,7 +119,7 @@ public BeanDeployment process() throws IOException { // Generate observers for (ObserverInfo observer : beanDeployment.getObservers()) { - for (Resource resource : observerGenerator.generate(observer, annotationLiterals, reflectionRegistration)) { + for (Resource resource : observerGenerator.generate(observer, reflectionRegistration)) { resources.add(resource); if (SpecialType.OBSERVER.equals(resource.getSpecialType())) { observerToGeneratedName.put(observer, resource.getName()); diff --git a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/DotNames.java b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/DotNames.java index c66348c37a909..a6823a5f6ffa7 100644 --- a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/DotNames.java +++ b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/DotNames.java @@ -54,6 +54,7 @@ final class DotNames { static final DotName ALTERNATIVE = DotName.createSimple(Alternative.class.getName()); static final DotName STEREOTYPE = DotName.createSimple(Stereotype.class.getName()); static final DotName TYPED = DotName.createSimple(Typed.class.getName()); + static final DotName CLASS = DotName.createSimple(Class.class.getName()); private DotNames() { } diff --git a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/InterceptorGenerator.java b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/InterceptorGenerator.java index 49a7a60076223..ef5ce1bddd701 100644 --- a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/InterceptorGenerator.java +++ b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/InterceptorGenerator.java @@ -40,6 +40,14 @@ public class InterceptorGenerator extends BeanGenerator { private static final Logger LOGGER = Logger.getLogger(InterceptorGenerator.class); + /** + * + * @param annotationLiterals + */ + public InterceptorGenerator(AnnotationLiteralProcessor annotationLiterals) { + super(annotationLiterals); + } + /** * * @param interceptor bean @@ -75,8 +83,9 @@ Collection generate(InterceptorInfo interceptor, AnnotationLiteralProc createProviderFields(interceptorCreator, interceptor, injectionPointToProviderField, interceptorToProviderField); createConstructor(classOutput, interceptorCreator, interceptor, baseName, injectionPointToProviderField, interceptorToProviderField, - annotationLiterals, bindings.getFieldDescriptor()); - createCreate(interceptorCreator, interceptor, providerTypeName, baseName, injectionPointToProviderField, interceptorToProviderField, reflectionRegistration); + bindings.getFieldDescriptor()); + createCreate(classOutput, interceptorCreator, interceptor, providerTypeName, baseName, injectionPointToProviderField, interceptorToProviderField, + reflectionRegistration); createGet(interceptor, interceptorCreator, providerTypeName); createGetTypes(interceptorCreator, beanTypes.getFieldDescriptor()); // Interceptors are always @Dependent and have always default qualifiers @@ -93,8 +102,7 @@ Collection generate(InterceptorInfo interceptor, AnnotationLiteralProc } protected void createConstructor(ClassOutput classOutput, ClassCreator creator, InterceptorInfo interceptor, String baseName, - Map injectionPointToProviderField, Map interceptorToProviderField, - AnnotationLiteralProcessor annotationLiterals, FieldDescriptor bindings) { + Map injectionPointToProviderField, Map interceptorToProviderField, FieldDescriptor bindings) { MethodCreator constructor = initConstructor(classOutput, creator, interceptor, baseName, injectionPointToProviderField, interceptorToProviderField, annotationLiterals); @@ -107,8 +115,7 @@ protected void createConstructor(ClassOutput classOutput, ClassCreator creator, // Create annotation literal first ClassInfo bindingClass = interceptor.getDeployment().getInterceptorBinding(bindingAnnotation.name()); String literalType = annotationLiterals.process(classOutput, bindingClass, bindingAnnotation, Types.getPackageName(creator.getClassName())); - constructor.invokeInterfaceMethod(MethodDescriptors.SET_ADD, bindingsHandle, - constructor.newInstance(MethodDescriptor.ofConstructor(literalType))); + constructor.invokeInterfaceMethod(MethodDescriptors.SET_ADD, bindingsHandle, constructor.newInstance(MethodDescriptor.ofConstructor(literalType))); } constructor.writeInstanceField(bindings, constructor.getThis(), bindingsHandle); constructor.returnValue(null); @@ -171,7 +178,8 @@ protected void createIntercept(ClassCreator creator, InterceptorInfo interceptor intercept.returnValue(intercept.loadNull()); } - private void addIntercept(MethodCreator intercept, MethodInfo interceptorMethod, InterceptionType interceptionType, String providerTypeName, ReflectionRegistration reflectionRegistration) { + private void addIntercept(MethodCreator intercept, MethodInfo interceptorMethod, InterceptionType interceptionType, String providerTypeName, + ReflectionRegistration reflectionRegistration) { if (interceptorMethod != null) { ResultHandle enumValue = intercept .readStaticField(FieldDescriptor.of(InterceptionType.class.getName(), interceptionType.name(), InterceptionType.class.getName())); diff --git a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/MethodDescriptors.java b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/MethodDescriptors.java index 8f82328010ba9..b1bfc41b47ed7 100644 --- a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/MethodDescriptors.java +++ b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/MethodDescriptors.java @@ -1,9 +1,12 @@ package org.jboss.protean.arc.processor; import java.lang.reflect.Constructor; +import java.lang.reflect.Method; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.inject.spi.EventContext; @@ -14,6 +17,7 @@ import org.jboss.protean.arc.InjectableBean; import org.jboss.protean.arc.InjectableInterceptor; import org.jboss.protean.arc.InjectableReferenceProvider; +import org.jboss.protean.arc.InvocationContextImpl; import org.jboss.protean.arc.InvocationContextImpl.InterceptorInvocation; import org.jboss.protean.arc.Reflections; import org.jboss.protean.gizmo.MethodDescriptor; @@ -72,6 +76,18 @@ final class MethodDescriptors { static final MethodDescriptor EVENT_CONTEXT_GET_METADATA = MethodDescriptor.ofMethod(EventContext.class, "getMetadata", EventMetadata.class); + static final MethodDescriptor INVOCATION_CONTEXT_AROUND_INVOKE = MethodDescriptor.ofMethod(InvocationContextImpl.class, "aroundInvoke", + InvocationContextImpl.class, Object.class, Method.class, Object[].class, List.class, Function.class, Set.class); + + static final MethodDescriptor INVOCATION_CONTEXT_AROUND_CONSTRUCT = MethodDescriptor.ofMethod(InvocationContextImpl.class, "aroundConstruct", + InvocationContextImpl.class, Constructor.class, List.class, Supplier.class, Set.class); + + static final MethodDescriptor INVOCATION_CONTEXT_POST_CONSTRUCT = MethodDescriptor.ofMethod(InvocationContextImpl.class, "postConstruct", + InvocationContextImpl.class, Object.class, List.class, Set.class); + + static final MethodDescriptor INVOCATION_CONTEXT_PRE_DESTROY = MethodDescriptor.ofMethod(InvocationContextImpl.class, "preDestroy", + InvocationContextImpl.class, Object.class, List.class, Set.class); + private MethodDescriptors() { } diff --git a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/ObserverGenerator.java b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/ObserverGenerator.java index a0b3ff8750b20..33888889166dd 100644 --- a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/ObserverGenerator.java +++ b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/ObserverGenerator.java @@ -51,13 +51,22 @@ public class ObserverGenerator extends AbstractGenerator { private static final AtomicInteger OBSERVER_INDEX = new AtomicInteger(); + private final AnnotationLiteralProcessor annotationLiterals; + /** * - * @param observer * @param annotationLiterals + */ + public ObserverGenerator(AnnotationLiteralProcessor annotationLiterals) { + this.annotationLiterals = annotationLiterals; + } + + /** + * + * @param observer * @return a collection of resources */ - Collection generate(ObserverInfo observer, AnnotationLiteralProcessor annotationLiterals, ReflectionRegistration reflectionRegistration) { + Collection generate(ObserverInfo observer, ReflectionRegistration reflectionRegistration) { ClassInfo declaringClass = observer.getObserverMethod().declaringClass(); String declaringClassBase; diff --git a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/SubclassGenerator.java b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/SubclassGenerator.java index f77e845d43101..70bba2eb14104 100644 --- a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/SubclassGenerator.java +++ b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/SubclassGenerator.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.ListIterator; import java.util.Map; @@ -19,6 +20,7 @@ import javax.enterprise.inject.spi.InterceptionType; import javax.interceptor.InvocationContext; +import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; import org.jboss.jandex.MethodInfo; @@ -26,14 +28,15 @@ import org.jboss.protean.arc.CreationalContextImpl; import org.jboss.protean.arc.InjectableInterceptor; import org.jboss.protean.arc.InjectableReferenceProvider; -import org.jboss.protean.arc.InvocationContextImpl; import org.jboss.protean.arc.InvocationContextImpl.InterceptorInvocation; import org.jboss.protean.arc.Reflections; import org.jboss.protean.arc.Subclass; +import org.jboss.protean.arc.processor.BeanInfo.InterceptionInfo; import org.jboss.protean.arc.processor.ResourceOutput.Resource; import org.jboss.protean.gizmo.BytecodeCreator; import org.jboss.protean.gizmo.CatchBlockCreator; import org.jboss.protean.gizmo.ClassCreator; +import org.jboss.protean.gizmo.ClassOutput; import org.jboss.protean.gizmo.DescriptorUtils; import org.jboss.protean.gizmo.ExceptionTable; import org.jboss.protean.gizmo.FieldCreator; @@ -55,6 +58,16 @@ static String generatedName(DotName providerTypeName, String baseName) { return DotNames.packageName(providerTypeName).replace(".", "/") + "/" + baseName + SUBCLASS_SUFFIX; } + private final AnnotationLiteralProcessor annotationLiterals; + + /** + * + * @param annotationLiterals + */ + public SubclassGenerator(AnnotationLiteralProcessor annotationLiterals) { + this.annotationLiterals = annotationLiterals; + } + /** * * @param bean @@ -75,14 +88,15 @@ Collection generate(BeanInfo bean, String beanClassName, ReflectionReg ClassCreator subclass = ClassCreator.builder().classOutput(classOutput).className(generatedName).superClass(providerTypeName).interfaces(Subclass.class) .build(); - FieldDescriptor preDestroyField = createConstructor(bean, subclass, providerTypeName, reflectionRegistration); - createDestroy(subclass, preDestroyField); + FieldDescriptor preDestroyField = createConstructor(classOutput, bean, subclass, providerTypeName, reflectionRegistration); + createDestroy(classOutput, bean, subclass, preDestroyField); subclass.close(); return classOutput.getResources(); } - protected FieldDescriptor createConstructor(BeanInfo bean, ClassCreator subclass, String providerTypeName, ReflectionRegistration reflectionRegistration) { + protected FieldDescriptor createConstructor(ClassOutput classOutput, BeanInfo bean, ClassCreator subclass, String providerTypeName, + ReflectionRegistration reflectionRegistration) { List parameterTypes = new ArrayList<>(); @@ -122,7 +136,7 @@ protected FieldDescriptor createConstructor(BeanInfo bean, ClassCreator subclass // PreDestroy interceptors FieldCreator preDestroysField = null; - List preDestroys = bean.getLifecycleInterceptors(InterceptionType.PRE_DESTROY); + InterceptionInfo preDestroys = bean.getLifecycleInterceptors(InterceptionType.PRE_DESTROY); if (!preDestroys.isEmpty()) { // private final List preDestroys preDestroysField = subclass.getFieldCreator("preDestroys", DescriptorUtils.extToInt(ArrayList.class.getName())) @@ -130,7 +144,7 @@ protected FieldDescriptor createConstructor(BeanInfo bean, ClassCreator subclass // preDestroys = new ArrayList<>() constructor.writeInstanceField(preDestroysField.getFieldDescriptor(), constructor.getThis(), constructor.newInstance(MethodDescriptor.ofConstructor(ArrayList.class))); - for (InterceptorInfo interceptor : preDestroys) { + for (InterceptorInfo interceptor : preDestroys.interceptors) { // preDestroys.add(InvocationContextImpl.InterceptorInvocation.preDestroy(provider1,provider1.get(CreationalContextImpl.child(ctx)))) ResultHandle creationalContext = constructor.invokeStaticMethod( MethodDescriptor.ofMethod(CreationalContextImpl.class, "child", CreationalContextImpl.class, CreationalContext.class), @@ -148,8 +162,7 @@ protected FieldDescriptor createConstructor(BeanInfo bean, ClassCreator subclass // Init intercepted methods and interceptor chains // private final Map> interceptorChains - FieldCreator interceptorChainsField = subclass.getFieldCreator("interceptorChains", DescriptorUtils.extToInt(Map.class.getName())) - .setModifiers(ACC_PRIVATE | ACC_FINAL); + FieldCreator interceptorChainsField = subclass.getFieldCreator("interceptorChains", Map.class.getName()).setModifiers(ACC_PRIVATE | ACC_FINAL); // interceptorChains = new HashMap<>() constructor.writeInstanceField(interceptorChainsField.getFieldDescriptor(), constructor.getThis(), constructor.newInstance(MethodDescriptor.ofConstructor(HashMap.class))); @@ -161,7 +174,7 @@ protected FieldDescriptor createConstructor(BeanInfo bean, ClassCreator subclass ResultHandle methodsHandle = constructor.readInstanceField(methodsField.getFieldDescriptor(), constructor.getThis()); int methodIdx = 1; - for (Entry> entry : bean.getInterceptedMethods().entrySet()) { + for (Entry entry : bean.getInterceptedMethods().entrySet()) { String methodId = "m" + methodIdx++; MethodInfo method = entry.getKey(); ResultHandle methodIdHandle = constructor.load(methodId); @@ -169,7 +182,8 @@ protected FieldDescriptor createConstructor(BeanInfo bean, ClassCreator subclass // First create interceptor chains // List m1Chain = new ArrayList<>() ResultHandle chainHandle = constructor.newInstance(MethodDescriptor.ofConstructor(ArrayList.class)); - for (InterceptorInfo interceptor : entry.getValue()) { + InterceptionInfo interceptedMethod = entry.getValue(); + for (InterceptorInfo interceptor : interceptedMethod.interceptors) { // m1Chain.add(InvocationContextImpl.InterceptorInvocation.aroundInvoke(p3,p3.get(CreationalContextImpl.child(ctx)))) ResultHandle creationalContext = constructor.invokeStaticMethod( MethodDescriptor.ofMethod(CreationalContextImpl.class, "child", CreationalContextImpl.class, CreationalContext.class), @@ -208,16 +222,16 @@ protected FieldDescriptor createConstructor(BeanInfo bean, ClassCreator subclass reflectionRegistration.registerMethod(method); // Finally create the forwarding method - createForwardingMethod(method, methodId, subclass, providerTypeName, interceptorChainsField.getFieldDescriptor(), - methodsField.getFieldDescriptor()); + createForwardingMethod(classOutput, bean, method, methodId, subclass, providerTypeName, interceptorChainsField.getFieldDescriptor(), + methodsField.getFieldDescriptor(), interceptedMethod); } constructor.returnValue(null); return preDestroysField != null ? preDestroysField.getFieldDescriptor() : null; } - private void createForwardingMethod(MethodInfo method, String methodId, ClassCreator subclass, String providerTypeName, - FieldDescriptor interceptorChainsField, FieldDescriptor methodsField) { + private void createForwardingMethod(ClassOutput classOutput, BeanInfo bean, MethodInfo method, String methodId, ClassCreator subclass, + String providerTypeName, FieldDescriptor interceptorChainsField, FieldDescriptor methodsField, InterceptionInfo interceptedMethod) { MethodCreator forwardMethod = subclass.getMethodCreator(MethodDescriptor.of(method)); @@ -259,10 +273,18 @@ private void createForwardingMethod(MethodInfo method, String methodId, ClassCre forwardMethod.readInstanceField(methodsField, forwardMethod.getThis()), methodIdHandle); ResultHandle interceptedChainHandle = forwardMethod.invokeInterfaceMethod(MethodDescriptors.MAP_GET, forwardMethod.readInstanceField(interceptorChainsField, forwardMethod.getThis()), methodIdHandle); - ResultHandle invocationContext = forwardMethod.invokeStaticMethod( - MethodDescriptor.ofMethod(InvocationContextImpl.class, "aroundInvoke", InvocationContextImpl.class, Object.class, Method.class, Object[].class, - List.class, Function.class), - forwardMethod.getThis(), interceptedMethodHandle, paramsHandle, interceptedChainHandle, func.getInstance()); + // Interceptor bindings + ResultHandle bindingsHandle = forwardMethod.newInstance(MethodDescriptor.ofConstructor(HashSet.class)); + for (AnnotationInstance binding : interceptedMethod.bindings) { + // Create annotation literals first + ClassInfo bindingClass = bean.getDeployment().getInterceptorBinding(binding.name()); + String literalType = annotationLiterals.process(classOutput, bindingClass, binding, Types.getPackageName(subclass.getClassName())); + forwardMethod.invokeInterfaceMethod(MethodDescriptors.SET_ADD, bindingsHandle, + forwardMethod.newInstance(MethodDescriptor.ofConstructor(literalType))); + } + + ResultHandle invocationContext = forwardMethod.invokeStaticMethod(MethodDescriptors.INVOCATION_CONTEXT_AROUND_INVOKE, forwardMethod.getThis(), + interceptedMethodHandle, paramsHandle, interceptedChainHandle, func.getInstance(), bindingsHandle); // InvocationContext.proceed() ResultHandle ret = forwardMethod.invokeInterfaceMethod(MethodDescriptor.ofMethod(InvocationContext.class, "proceed", Object.class), invocationContext); forwardMethod.returnValue(superResult != null ? ret : null); @@ -271,28 +293,41 @@ private void createForwardingMethod(MethodInfo method, String methodId, ClassCre /** * + * @param classOutput + * @param bean * @param subclass * @param preDestroysField * @see Subclass#destroy() */ - protected void createDestroy(ClassCreator subclass, FieldDescriptor preDestroysField) { + protected void createDestroy(ClassOutput classOutput, BeanInfo bean, ClassCreator subclass, FieldDescriptor preDestroysField) { if (preDestroysField != null) { - MethodCreator destroyMethod = subclass.getMethodCreator(MethodDescriptor.ofMethod(Subclass.class, "destroy", void.class)); - ResultHandle predestroysHandle = destroyMethod.readInstanceField(preDestroysField, destroyMethod.getThis()); + MethodCreator destroy = subclass.getMethodCreator(MethodDescriptor.ofMethod(Subclass.class, "destroy", void.class)); + ResultHandle predestroysHandle = destroy.readInstanceField(preDestroysField, destroy.getThis()); + + // Interceptor bindings + ResultHandle bindingsHandle = destroy.newInstance(MethodDescriptor.ofConstructor(HashSet.class)); + for (AnnotationInstance binding : bean.getLifecycleInterceptors(InterceptionType.PRE_DESTROY).bindings) { + // Create annotation literals first + ClassInfo bindingClass = bean.getDeployment().getInterceptorBinding(binding.name()); + String literalType = annotationLiterals.process(classOutput, bindingClass, binding, Types.getPackageName(subclass.getClassName())); + destroy.invokeInterfaceMethod(MethodDescriptors.SET_ADD, bindingsHandle, destroy.newInstance(MethodDescriptor.ofConstructor(literalType))); + } + // try - ExceptionTable tryCatch = destroyMethod.addTryCatch(); + ExceptionTable tryCatch = destroy.addTryCatch(); // catch (Exception e) CatchBlockCreator exception = tryCatch.addCatchClause(Exception.class); // throw new RuntimeException(e) exception.throwException(RuntimeException.class, "Error destroying subclass", exception.getCaughtException()); + // InvocationContextImpl.preDestroy(this,predestroys) - ResultHandle invocationContext = destroyMethod.invokeStaticMethod( - MethodDescriptor.ofMethod(InvocationContextImpl.class, "preDestroy", InvocationContextImpl.class, Object.class, List.class), - destroyMethod.getThis(), predestroysHandle); + ResultHandle invocationContext = destroy.invokeStaticMethod(MethodDescriptors.INVOCATION_CONTEXT_PRE_DESTROY, destroy.getThis(), predestroysHandle, + bindingsHandle); + // InvocationContext.proceed() - destroyMethod.invokeInterfaceMethod(MethodDescriptor.ofMethod(InvocationContext.class, "proceed", Object.class), invocationContext); + destroy.invokeInterfaceMethod(MethodDescriptor.ofMethod(InvocationContext.class, "proceed", Object.class), invocationContext); tryCatch.complete(); - destroyMethod.returnValue(null); + destroy.returnValue(null); } } diff --git a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanGeneratorTest.java b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanGeneratorTest.java index beba57566578b..905e99602a173 100644 --- a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanGeneratorTest.java +++ b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanGeneratorTest.java @@ -26,9 +26,9 @@ public void testGenerator() throws IOException { BeanDeployment deployment = new BeanDeployment(index, null, null); deployment.init(); - BeanGenerator generator = new BeanGenerator(); + BeanGenerator generator = new BeanGenerator(new AnnotationLiteralProcessor(BeanProcessor.DEFAULT_NAME, true)); - deployment.getBeans().forEach(bean -> generator.generate(bean, new AnnotationLiteralProcessor(BeanProcessor.DEFAULT_NAME, true), ReflectionRegistration.NOOP)); + deployment.getBeans().forEach(bean -> generator.generate(bean, ReflectionRegistration.NOOP)); // TODO test generated bytecode } @@ -39,9 +39,9 @@ public void testGeneratorForNormalScopedProducer() throws IOException { BeanDeployment deployment = new BeanDeployment(index, null, null); deployment.init(); - BeanGenerator generator = new BeanGenerator(); + BeanGenerator generator = new BeanGenerator(new AnnotationLiteralProcessor(BeanProcessor.DEFAULT_NAME, true)); - deployment.getBeans().forEach(bean -> generator.generate(bean, new AnnotationLiteralProcessor(BeanProcessor.DEFAULT_NAME, true), ReflectionRegistration.NOOP)); + deployment.getBeans().forEach(bean -> generator.generate(bean, ReflectionRegistration.NOOP)); // TODO test generated bytecode } diff --git a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/ClientProxyGeneratorTest.java b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/ClientProxyGeneratorTest.java index 453246fb9ccfc..8e45dc5b48c95 100644 --- a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/ClientProxyGeneratorTest.java +++ b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/ClientProxyGeneratorTest.java @@ -24,11 +24,11 @@ public void testGenerator() throws IOException { BeanDeployment deployment = new BeanDeployment(index, null, null); deployment.init(); - BeanGenerator beanGenerator = new BeanGenerator(); + BeanGenerator beanGenerator = new BeanGenerator( new AnnotationLiteralProcessor(BeanProcessor.DEFAULT_NAME, true)); ClientProxyGenerator proxyGenerator = new ClientProxyGenerator(); deployment.getBeans().stream().filter(bean -> bean.getScope().isNormal()).forEach(bean -> { - for (Resource resource : beanGenerator.generate(bean, new AnnotationLiteralProcessor(BeanProcessor.DEFAULT_NAME, true), ReflectionRegistration.NOOP)) { + for (Resource resource : beanGenerator.generate(bean, ReflectionRegistration.NOOP)) { proxyGenerator.generate(bean, resource.getFullyQualifiedName(), ReflectionRegistration.NOOP); } }); diff --git a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/InterceptorGeneratorTest.java b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/InterceptorGeneratorTest.java index 9959dadf77d75..5aafefd5a9ed9 100644 --- a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/InterceptorGeneratorTest.java +++ b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/InterceptorGeneratorTest.java @@ -36,9 +36,9 @@ public void testGenerator() throws IOException { assertEquals(1, myInterceptor.getBindings().size()); assertNotNull(myInterceptor.getAroundInvoke()); - InterceptorGenerator generator = new InterceptorGenerator(); + InterceptorGenerator generator = new InterceptorGenerator(new AnnotationLiteralProcessor(BeanProcessor.DEFAULT_NAME, true)); - deployment.getInterceptors().forEach(interceptor -> generator.generate(interceptor, new AnnotationLiteralProcessor(BeanProcessor.DEFAULT_NAME, true), ReflectionRegistration.NOOP)); + deployment.getInterceptors().forEach(interceptor -> generator.generate(interceptor, ReflectionRegistration.NOOP)); // TODO test generated bytecode } diff --git a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/SubclassGeneratorTest.java b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/SubclassGeneratorTest.java index 25ab9f5ce9730..86d9758f4994a 100644 --- a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/SubclassGeneratorTest.java +++ b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/SubclassGeneratorTest.java @@ -33,11 +33,12 @@ public void testGenerator() throws IOException { BeanDeployment deployment = new BeanDeployment(index, null, null); deployment.init(); - BeanGenerator beanGenerator = new BeanGenerator(); - SubclassGenerator generator = new SubclassGenerator(); + AnnotationLiteralProcessor annotationLiteralProcessor = new AnnotationLiteralProcessor(BeanProcessor.DEFAULT_NAME, true); + BeanGenerator beanGenerator = new BeanGenerator(annotationLiteralProcessor); + SubclassGenerator generator = new SubclassGenerator(annotationLiteralProcessor); BeanInfo simpleBean = deployment.getBeans().stream() .filter(b -> b.getTarget().asClass().name().equals(DotName.createSimple(SimpleBean.class.getName()))).findAny().get(); - for (Resource resource : beanGenerator.generate(simpleBean, new AnnotationLiteralProcessor(BeanProcessor.DEFAULT_NAME, true), ReflectionRegistration.NOOP)) { + for (Resource resource : beanGenerator.generate(simpleBean, ReflectionRegistration.NOOP)) { generator.generate(simpleBean, resource.getFullyQualifiedName(), ReflectionRegistration.NOOP); } // TODO test generated bytecode diff --git a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/InvocationContextImpl.java b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/InvocationContextImpl.java index e1df08f3fa9f5..ae5ff8644e6c6 100644 --- a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/InvocationContextImpl.java +++ b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/InvocationContextImpl.java @@ -1,11 +1,13 @@ package org.jboss.protean.arc; +import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.function.Supplier; @@ -19,6 +21,8 @@ */ public class InvocationContextImpl implements InvocationContext { + public static final String KEY_INTERCEPTOR_BINDINGS = "org.jboss.protean.arc.interceptorBindings"; + /** * * @param target @@ -26,42 +30,46 @@ public class InvocationContextImpl implements InvocationContext { * @param args * @param chain * @param aroundInvokeForward + * @param interceptorBindings * @return a new {@link javax.interceptor.AroundInvoke} invocation context */ public static InvocationContextImpl aroundInvoke(Object target, Method method, Object[] args, List chain, - Function aroundInvokeForward) { - return new InvocationContextImpl(target, method, null, args, chain, aroundInvokeForward, null); + Function aroundInvokeForward, Set interceptorBindings) { + return new InvocationContextImpl(target, method, null, args, chain, aroundInvokeForward, null, interceptorBindings); } /** * * @param target * @param chain + * @param interceptorBindings * @return a new {@link javax.annotation.PostConstruct} invocation context */ - public static InvocationContextImpl postConstruct(Object target, List chain) { - return new InvocationContextImpl(target, null, null, null, chain, null, null); + public static InvocationContextImpl postConstruct(Object target, List chain, Set interceptorBindings) { + return new InvocationContextImpl(target, null, null, null, chain, null, null, interceptorBindings); } /** * * @param target * @param chain + * @param interceptorBindings * @return a new {@link javax.annotation.PreDestroy} invocation context */ - public static InvocationContextImpl preDestroy(Object target, List chain) { - return new InvocationContextImpl(target, null, null, null, chain, null, null); + public static InvocationContextImpl preDestroy(Object target, List chain, Set interceptorBindings) { + return new InvocationContextImpl(target, null, null, null, chain, null, null, interceptorBindings); } /** * * @param target * @param chain + * @param interceptorBindings * @return a new {@link javax.interceptor.AroundConstruct} invocation context */ - public static InvocationContextImpl aroundConstruct(Constructor constructor, List chain, - Supplier aroundConstructForward) { - return new InvocationContextImpl(null, null, constructor, null, chain, null, aroundConstructForward); + public static InvocationContextImpl aroundConstruct(Constructor constructor, List chain, Supplier aroundConstructForward, + Set interceptorBindings) { + return new InvocationContextImpl(null, null, constructor, null, chain, null, aroundConstructForward, interceptorBindings); } private final AtomicReference target; @@ -82,24 +90,31 @@ public static InvocationContextImpl aroundConstruct(Constructor constructor, private final Supplier aroundConstructForward; + private final Set interceptorBindings; + /** * @param target * @param method + * @param constructor * @param args * @param chain * @param aroundInvokeForward + * @param aroundConstructForward + * @param interceptorBindings */ InvocationContextImpl(Object target, Method method, Constructor constructor, Object[] args, List chain, - Function aroundInvokeForward, Supplier aroundConstructForward) { + Function aroundInvokeForward, Supplier aroundConstructForward, Set interceptorBindings) { this.target = new AtomicReference<>(target); this.method = method; this.constructor = constructor; this.args = args; - this.contextData = new HashMap<>(); this.position = 0; this.chain = chain; this.aroundInvokeForward = aroundInvokeForward; this.aroundConstructForward = aroundConstructForward; + this.interceptorBindings = interceptorBindings; + contextData = new HashMap<>(); + contextData.put(KEY_INTERCEPTOR_BINDINGS, interceptorBindings); } boolean hasNextInterceptor() { @@ -192,6 +207,10 @@ public Object getTimer() { return null; } + public Set getInterceptorBindings() { + return interceptorBindings; + } + public static class InterceptorInvocation { public static InterceptorInvocation aroundInvoke(InjectableInterceptor interceptor, Object interceptorInstance) { diff --git a/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/LifecycleInterceptor.java b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/LifecycleInterceptor.java index bdba6be30b10e..84e1e9dc1a311 100644 --- a/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/LifecycleInterceptor.java +++ b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/LifecycleInterceptor.java @@ -10,6 +10,8 @@ import javax.interceptor.Interceptor; import javax.interceptor.InvocationContext; +import org.jboss.protean.arc.InvocationContextImpl; + @Lifecycle @Priority(1) @Interceptor @@ -21,16 +23,28 @@ public class LifecycleInterceptor { @PostConstruct void simpleInit(InvocationContext ctx) { + Object bindings = ctx.getContextData().get(InvocationContextImpl.KEY_INTERCEPTOR_BINDINGS); + if (bindings == null) { + throw new IllegalArgumentException("No bindings found"); + } POST_CONSTRUCTS.add(ctx.getTarget()); } @PreDestroy void simpleDestroy(InvocationContext ctx) { + Object bindings = ctx.getContextData().get(InvocationContextImpl.KEY_INTERCEPTOR_BINDINGS); + if (bindings == null) { + throw new IllegalArgumentException("No bindings found"); + } PRE_DESTROYS.add(ctx.getTarget()); } @AroundConstruct void simpleAroundConstruct(InvocationContext ctx) throws Exception { + Object bindings = ctx.getContextData().get(InvocationContextImpl.KEY_INTERCEPTOR_BINDINGS); + if (bindings == null) { + throw new IllegalArgumentException("No bindings found"); + } try { AROUND_CONSTRUCTS.add(ctx.getConstructor()); ctx.proceed(); diff --git a/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/SimpleInterceptor.java b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/SimpleInterceptor.java index 3819301691b3d..5c29e16f9784a 100644 --- a/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/SimpleInterceptor.java +++ b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/SimpleInterceptor.java @@ -6,6 +6,8 @@ import javax.interceptor.Interceptor; import javax.interceptor.InvocationContext; +import org.jboss.protean.arc.InvocationContextImpl; + @Simple @Priority(1) @Interceptor @@ -16,6 +18,10 @@ public class SimpleInterceptor { @AroundInvoke Object mySuperCoolAroundInvoke(InvocationContext ctx) throws Exception { + Object bindings = ctx.getContextData().get(InvocationContextImpl.KEY_INTERCEPTOR_BINDINGS); + if (bindings == null) { + throw new IllegalArgumentException("No bindings found"); + } return "" + counter.get() + ctx.proceed() + counter.incrementAndGet(); } } diff --git a/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/bindings/InvocationContextBindingsTest.java b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/bindings/InvocationContextBindingsTest.java new file mode 100644 index 0000000000000..3757dd6f8f714 --- /dev/null +++ b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/bindings/InvocationContextBindingsTest.java @@ -0,0 +1,63 @@ +package org.jboss.protean.arc.test.interceptors.bindings; + +import static org.junit.Assert.assertTrue; + +import javax.annotation.Priority; +import javax.inject.Singleton; +import javax.interceptor.AroundInvoke; +import javax.interceptor.Interceptor; +import javax.interceptor.InvocationContext; + +import org.jboss.protean.arc.Arc; +import org.jboss.protean.arc.ArcContainer; +import org.jboss.protean.arc.InvocationContextImpl; +import org.jboss.protean.arc.test.ArcTestContainer; +import org.jboss.protean.arc.test.interceptors.Simple; +import org.junit.Rule; +import org.junit.Test; + +public class InvocationContextBindingsTest { + + @Rule + public ArcTestContainer container = new ArcTestContainer(Simple.class, MyTransactional.class, SimpleBean.class, SimpleInterceptor.class); + + @Test + public void testInterception() { + ArcContainer arc = Arc.container(); + SimpleBean simpleBean = arc.instance(SimpleBean.class).get(); + // [@org.jboss.protean.arc.test.interceptors.Simple(), + // @org.jboss.protean.arc.test.interceptors.bindings.MyTransactional(value={java.lang.String.class})]::foo + String ret = simpleBean.foo(); + assertTrue(ret.contains(Simple.class.getName())); + assertTrue(ret.contains(MyTransactional.class.getName())); + assertTrue(ret.contains(String.class.getName())); + } + + @Singleton + static class SimpleBean { + + @MyTransactional({ String.class }) + @Simple + String foo() { + return "foo"; + } + + } + + @Simple + @MyTransactional + @Priority(1) + @Interceptor + public static class SimpleInterceptor { + + @AroundInvoke + Object mySuperCoolAroundInvoke(InvocationContext ctx) throws Exception { + Object bindings = ctx.getContextData().get(InvocationContextImpl.KEY_INTERCEPTOR_BINDINGS); + if (bindings != null) { + return bindings.toString() + "::" + ctx.proceed(); + } + return ctx.proceed(); + } + } + +} diff --git a/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/bindings/MyTransactional.java b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/bindings/MyTransactional.java new file mode 100644 index 0000000000000..4d5e6262deb43 --- /dev/null +++ b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/bindings/MyTransactional.java @@ -0,0 +1,24 @@ +package org.jboss.protean.arc.test.interceptors.bindings; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.util.Nonbinding; +import javax.interceptor.InterceptorBinding; + +@Target({ TYPE, METHOD }) +@Retention(RUNTIME) +@Documented +@InterceptorBinding +public @interface MyTransactional { + + @SuppressWarnings("rawtypes") + @Nonbinding + Class[] value() default {}; + +}