Arc - make it possible to get interceptor bindings from

InvocationContext

- resolves #29
This commit is contained in:
Martin Kouba
2018-09-21 12:05:38 +02:00
parent b85be0f34a
commit 48df6cf952
18 changed files with 416 additions and 131 deletions

View File

@@ -14,6 +14,8 @@ import org.jboss.jandex.AnnotationValue;
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 @@ public class AnnotationLiteralGenerator extends AbstractGenerator {
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:

View File

@@ -5,7 +5,6 @@ import static org.objectweb.asm.Opcodes.ACC_FINAL;
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.InitializedInterceptor;
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<Resource> generate(BeanInfo bean, AnnotationLiteralProcessor annotationLiterals, ReflectionRegistration reflectionRegistration) {
Collection<Resource> 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<Resource> generateClassBean(BeanInfo bean, ClassInfo beanClass, AnnotationLiteralProcessor annotationLiterals,
ReflectionRegistration reflectionRegistration) {
Collection<Resource> generateClassBean(BeanInfo bean, ClassInfo beanClass, ReflectionRegistration reflectionRegistration) {
String baseName;
if (beanClass.enclosingClass() != null) {
@@ -135,7 +138,8 @@ public class BeanGenerator extends AbstractGenerator {
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 @@ public class BeanGenerator extends AbstractGenerator {
return classOutput.getResources();
}
Collection<Resource> generateProducerMethodBean(BeanInfo bean, MethodInfo producerMethod, AnnotationLiteralProcessor annotationLiterals,
ReflectionRegistration reflectionRegistration) {
Collection<Resource> generateProducerMethodBean(BeanInfo bean, MethodInfo producerMethod, ReflectionRegistration reflectionRegistration) {
ClassInfo declaringClass = producerMethod.declaringClass();
String declaringClassBase;
@@ -196,7 +199,7 @@ public class BeanGenerator extends AbstractGenerator {
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 @@ public class BeanGenerator extends AbstractGenerator {
return classOutput.getResources();
}
Collection<Resource> generateProducerFieldBean(BeanInfo bean, FieldInfo producerField, AnnotationLiteralProcessor annotationLiterals,
ReflectionRegistration reflectionRegistration) {
Collection<Resource> generateProducerFieldBean(BeanInfo bean, FieldInfo producerField, ReflectionRegistration reflectionRegistration) {
ClassInfo declaringClass = producerField.declaringClass();
String declaringClassBase;
@@ -251,7 +253,7 @@ public class BeanGenerator extends AbstractGenerator {
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 @@ public class BeanGenerator extends AbstractGenerator {
/**
*
* @param classOutput
* @param beanCreator
* @param bean
* @param baseName
@@ -548,7 +551,7 @@ public class BeanGenerator extends AbstractGenerator {
* @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<InjectionPointInfo, String> injectionPointToProviderField, Map<InterceptorInfo, String> interceptorToProviderField,
ReflectionRegistration reflectionRegistration) {
@@ -565,13 +568,13 @@ public class BeanGenerator extends AbstractGenerator {
if (bean.hasLifecycleInterceptors()) {
// Note that we must share the interceptors instances with the intercepted subclass, if present
List<InterceptorInfo> postConstructs = bean.getLifecycleInterceptors(InterceptionType.POST_CONSTRUCT);
List<InterceptorInfo> 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<InterceptorInfo> 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 @@ public class BeanGenerator extends AbstractGenerator {
if (!postConstructs.isEmpty()) {
// postConstructs = new ArrayList<InterceptorInvocation>()
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 @@ public class BeanGenerator extends AbstractGenerator {
if (!aroundConstructs.isEmpty()) {
// aroundConstructs = new ArrayList<InterceptorInvocation>()
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 @@ public class BeanGenerator extends AbstractGenerator {
}
// AroundConstruct lifecycle callback interceptors
if (!bean.getLifecycleInterceptors(InterceptionType.AROUND_CONSTRUCT).isEmpty()) {
InterceptionInfo aroundConstructs = bean.getLifecycleInterceptors(InterceptionType.AROUND_CONSTRUCT);
if (!aroundConstructs.isEmpty()) {
Optional<Injection> constructorInjection = bean.getConstructorInjection();
ResultHandle constructorHandle;
if (constructorInjection.isPresent()) {
@@ -648,15 +656,21 @@ public class BeanGenerator extends AbstractGenerator {
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 @@ public class BeanGenerator extends AbstractGenerator {
}
// 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();

View File

@@ -44,9 +44,9 @@ class BeanInfo {
private final DisposerInfo disposer;
private Map<MethodInfo, List<InterceptorInfo>> interceptedMethods;
private Map<MethodInfo, InterceptionInfo> interceptedMethods;
private Map<InterceptionType, List<InterceptorInfo>> lifecycleInterceptors;
private Map<InterceptionType, InterceptionInfo> lifecycleInterceptors;
private final Integer alternativePriority;
@@ -159,12 +159,12 @@ class BeanInfo {
return injections.isEmpty() ? Optional.empty() : injections.stream().filter(i -> i.isConstructor()).findAny();
}
Map<MethodInfo, List<InterceptorInfo>> getInterceptedMethods() {
Map<MethodInfo, InterceptionInfo> getInterceptedMethods() {
return interceptedMethods;
}
List<InterceptorInfo> 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 @@ class BeanInfo {
*/
List<InterceptorInfo> getBoundInterceptors() {
List<InterceptorInfo> bound = new ArrayList<>();
for (List<InterceptorInfo> 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 @@ class BeanInfo {
}
}
private Map<MethodInfo, List<InterceptorInfo>> initInterceptedMethods() {
private Map<MethodInfo, InterceptionInfo> initInterceptedMethods() {
if (!isInterceptor() && isClassBean()) {
Map<MethodInfo, List<InterceptorInfo>> interceptedMethods = new HashMap<>();
Map<MethodInfo, InterceptionInfo> interceptedMethods = new HashMap<>();
Map<MethodKey, Set<AnnotationInstance>> candidates = new HashMap<>();
// TODO interceptor bindings are transitive!!!
@@ -260,7 +260,7 @@ class BeanInfo {
for (Entry<MethodKey, Set<AnnotationInstance>> entry : candidates.entrySet()) {
List<InterceptorInfo> 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 @@ class BeanInfo {
}
}
private Map<InterceptionType, List<InterceptorInfo>> initLifecycleInterceptors() {
private Map<InterceptionType, InterceptionInfo> initLifecycleInterceptors() {
if (!isInterceptor() && isClassBean()) {
Map<InterceptionType, List<InterceptorInfo>> lifecycleInterceptors = new HashMap<>();
Map<InterceptionType, InterceptionInfo> lifecycleInterceptors = new HashMap<>();
Set<AnnotationInstance> classLevelBindings = new HashSet<>();
addClassLevelBindings(target.asClass(), classLevelBindings);
putLifecycleInterceptors(lifecycleInterceptors, classLevelBindings, InterceptionType.POST_CONSTRUCT);
@@ -283,11 +283,11 @@ class BeanInfo {
}
}
private void putLifecycleInterceptors(Map<InterceptionType, List<InterceptorInfo>> lifecycleInterceptors, Set<AnnotationInstance> classLevelBindings,
private void putLifecycleInterceptors(Map<InterceptionType, InterceptionInfo> lifecycleInterceptors, Set<AnnotationInstance> classLevelBindings,
InterceptionType interceptionType) {
List<InterceptorInfo> interceptors = beanDeployment.getInterceptorResolver().resolve(interceptionType, classLevelBindings);
if (!interceptors.isEmpty()) {
lifecycleInterceptors.put(interceptionType, interceptors);
lifecycleInterceptors.put(interceptionType, new InterceptionInfo(interceptors, classLevelBindings));
}
}
@@ -303,6 +303,25 @@ class BeanInfo {
}
}
static class InterceptionInfo {
static final InterceptionInfo EMPTY = new InterceptionInfo(Collections.emptyList(), Collections.emptySet());
final List<InterceptorInfo> interceptors;
final Set<AnnotationInstance> bindings;
InterceptionInfo(List<InterceptorInfo> interceptors, Set<AnnotationInstance> 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 + "]";

View File

@@ -74,19 +74,18 @@ public class BeanProcessor {
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<BeanInfo, String> beanToGeneratedName = new HashMap<>();
Map<ObserverInfo, String> observerToGeneratedName = new HashMap<>();
Map<InterceptorInfo, String> interceptorToGeneratedName = new HashMap<>();
AnnotationLiteralProcessor annotationLiterals = new AnnotationLiteralProcessor(name, sharedAnnotationLiterals);
long start = System.currentTimeMillis();
List<Resource> resources = new ArrayList<>();
@@ -103,7 +102,7 @@ public class BeanProcessor {
// 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 class BeanProcessor {
// 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());

View File

@@ -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() {
}

View File

@@ -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 @@ public class InterceptorGenerator extends BeanGenerator {
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 @@ public class InterceptorGenerator extends BeanGenerator {
}
protected void createConstructor(ClassOutput classOutput, ClassCreator creator, InterceptorInfo interceptor, String baseName,
Map<InjectionPointInfo, String> injectionPointToProviderField, Map<InterceptorInfo, String> interceptorToProviderField,
AnnotationLiteralProcessor annotationLiterals, FieldDescriptor bindings) {
Map<InjectionPointInfo, String> injectionPointToProviderField, Map<InterceptorInfo, String> interceptorToProviderField, FieldDescriptor bindings) {
MethodCreator constructor = initConstructor(classOutput, creator, interceptor, baseName, injectionPointToProviderField, interceptorToProviderField,
annotationLiterals);
@@ -107,8 +115,7 @@ public class InterceptorGenerator extends BeanGenerator {
// 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 @@ public class InterceptorGenerator extends BeanGenerator {
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()));

View File

@@ -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.CreationalContextImpl;
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() {
}

View File

@@ -51,13 +51,22 @@ public class ObserverGenerator extends AbstractGenerator {
private static final AtomicInteger OBSERVER_INDEX = new AtomicInteger();
private final AnnotationLiteralProcessor annotationLiterals;
/**
*
* @param annotationLiterals
*/
public ObserverGenerator(AnnotationLiteralProcessor annotationLiterals) {
this.annotationLiterals = annotationLiterals;
}
/**
*
* @param observer
* @param annotationLiterals
* @return a collection of resources
*/
Collection<Resource> generate(ObserverInfo observer, AnnotationLiteralProcessor annotationLiterals, ReflectionRegistration reflectionRegistration) {
Collection<Resource> generate(ObserverInfo observer, ReflectionRegistration reflectionRegistration) {
ClassInfo declaringClass = observer.getObserverMethod().declaringClass();
String declaringClassBase;

View File

@@ -7,6 +7,7 @@ import java.lang.reflect.Method;
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.context.spi.CreationalContext;
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.jandex.Type;
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 @@ public class SubclassGenerator extends AbstractGenerator {
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 @@ public class SubclassGenerator extends AbstractGenerator {
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<String> parameterTypes = new ArrayList<>();
@@ -122,7 +136,7 @@ public class SubclassGenerator extends AbstractGenerator {
// PreDestroy interceptors
FieldCreator preDestroysField = null;
List<InterceptorInfo> preDestroys = bean.getLifecycleInterceptors(InterceptionType.PRE_DESTROY);
InterceptionInfo preDestroys = bean.getLifecycleInterceptors(InterceptionType.PRE_DESTROY);
if (!preDestroys.isEmpty()) {
// private final List<InvocationContextImpl.InterceptorInvocation> preDestroys
preDestroysField = subclass.getFieldCreator("preDestroys", DescriptorUtils.extToInt(ArrayList.class.getName()))
@@ -130,7 +144,7 @@ public class SubclassGenerator extends AbstractGenerator {
// 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 @@ public class SubclassGenerator extends AbstractGenerator {
// Init intercepted methods and interceptor chains
// private final Map<String, List<InvocationContextImpl.InterceptorInvocation>> 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 @@ public class SubclassGenerator extends AbstractGenerator {
ResultHandle methodsHandle = constructor.readInstanceField(methodsField.getFieldDescriptor(), constructor.getThis());
int methodIdx = 1;
for (Entry<MethodInfo, List<InterceptorInfo>> entry : bean.getInterceptedMethods().entrySet()) {
for (Entry<MethodInfo, InterceptionInfo> entry : bean.getInterceptedMethods().entrySet()) {
String methodId = "m" + methodIdx++;
MethodInfo method = entry.getKey();
ResultHandle methodIdHandle = constructor.load(methodId);
@@ -169,7 +182,8 @@ public class SubclassGenerator extends AbstractGenerator {
// First create interceptor chains
// List<InvocationContextImpl.InterceptorInvocation> 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 @@ public class SubclassGenerator extends AbstractGenerator {
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 @@ public class SubclassGenerator extends AbstractGenerator {
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 @@ public class SubclassGenerator extends AbstractGenerator {
/**
*
* @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);
}
}

View File

@@ -26,9 +26,9 @@ public class BeanGeneratorTest {
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 class BeanGeneratorTest {
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
}

View File

@@ -24,11 +24,11 @@ public class ClientProxyGeneratorTest {
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);
}
});

View File

@@ -36,9 +36,9 @@ public class InterceptorGeneratorTest {
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
}

View File

@@ -33,11 +33,12 @@ public class SubclassGeneratorTest {
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

View File

@@ -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 @@ import javax.interceptor.InvocationContext;
*/
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<InterceptorInvocation> chain,
Function<InvocationContext, Object> aroundInvokeForward) {
return new InvocationContextImpl(target, method, null, args, chain, aroundInvokeForward, null);
Function<InvocationContext, Object> aroundInvokeForward, Set<Annotation> 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<InterceptorInvocation> chain) {
return new InvocationContextImpl(target, null, null, null, chain, null, null);
public static InvocationContextImpl postConstruct(Object target, List<InterceptorInvocation> chain, Set<Annotation> 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<InterceptorInvocation> chain) {
return new InvocationContextImpl(target, null, null, null, chain, null, null);
public static InvocationContextImpl preDestroy(Object target, List<InterceptorInvocation> chain, Set<Annotation> 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<InterceptorInvocation> chain,
Supplier<Object> aroundConstructForward) {
return new InvocationContextImpl(null, null, constructor, null, chain, null, aroundConstructForward);
public static InvocationContextImpl aroundConstruct(Constructor<?> constructor, List<InterceptorInvocation> chain, Supplier<Object> aroundConstructForward,
Set<Annotation> interceptorBindings) {
return new InvocationContextImpl(null, null, constructor, null, chain, null, aroundConstructForward, interceptorBindings);
}
private final AtomicReference<Object> target;
@@ -82,24 +90,31 @@ public class InvocationContextImpl implements InvocationContext {
private final Supplier<Object> aroundConstructForward;
private final Set<Annotation> 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<InterceptorInvocation> chain,
Function<InvocationContext, Object> aroundInvokeForward, Supplier<Object> aroundConstructForward) {
Function<InvocationContext, Object> aroundInvokeForward, Supplier<Object> aroundConstructForward, Set<Annotation> 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 class InvocationContextImpl implements InvocationContext {
return null;
}
public Set<Annotation> getInterceptorBindings() {
return interceptorBindings;
}
public static class InterceptorInvocation {
public static InterceptorInvocation aroundInvoke(InjectableInterceptor<?> interceptor, Object interceptorInstance) {

View File

@@ -10,6 +10,8 @@ import javax.interceptor.AroundConstruct;
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();

View File

@@ -6,6 +6,8 @@ import javax.interceptor.AroundInvoke;
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();
}
}

View File

@@ -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();
}
}
}

View File

@@ -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 {};
}