Weld - pull from MC
kabirkhan Jun 28, 2010 7:22 AMI have experimented with an alternate implementation of the weld-mc integration as discussed in Boston. I still need to write more tests but wanted to let the Weld guys know where I am at.
The idea is to "pull" data from the MC in special cases, which is different from the "push" we do at the moment for MC beans annotated with @WeldEnabled. I had a look at using the InjectionServices interface, but this has the following problems:
-It does not get triggered when calling constructors
-If using a separate @McInject annotation instead of @Inject to do injection the annotated field does not appear in invocation context's injection points meaning all the fields etc. need to be parsed from invocation context's target's class (I am not sure if the target will always be non-null?)
-If using the @Inject annotation with an additional @Mc annotation (to look up the particular injection point in the MC) falls over when calling proceed(), also again a problem is that you need to iterate over all fields etc. to look for the members. This also fails in the validation phase.
As mentioned the ability to do this injection from MC is something that most Weld applications would not want to do. What I have locally now is an extra service that can handle this:
package org.jboss.temp.weld.spi; import java.lang.annotation.Annotation; import java.util.Iterator; import org.jboss.weld.bootstrap.api.Service; public interface ExternalLookupRegistryService extends Service { Iterable<ExternalLookup<?>> getExternalLookups(); void addExternalLookup(ExternalLookup<?> externalLookup); boolean removeExternalLookup(Class<? extends Annotation> clazz); }
And then a service to handle particular annotations
package org.jboss.temp.weld.spi; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.Set; public interface ExternalLookup<T extends Annotation> { Class<T> getAnnotation(); boolean isValid(Type type, Set<Annotation> qualifiers); Object lookupValue(Type type, Set<Annotation> qualifiers); }
I'm hoping to be able to push these interfaces into the main spi, the temp location I am using is just while playing.
Then I have modified the Validator and Field-/ParameterInjectionPoint to look for this service when validating/injecting:
[kabir ~/sourcecontrol/weld/core/subversion]$svn diff impl/src/main/java/org/jboss/weld/bootstrap/Validator.javaIndex: impl/src/main/java/org/jboss/weld/bootstrap/Validator.java===================================================================--- impl/src/main/java/org/jboss/weld/bootstrap/Validator.java (revision 6577)+++ impl/src/main/java/org/jboss/weld/bootstrap/Validator.java (working copy)@@ -83,6 +83,8 @@import javax.inject.Scope;import org.jboss.interceptor.model.InterceptionModel;+import org.jboss.temp.weld.spi.ExternalLookup;+import org.jboss.temp.weld.spi.ExternalLookupRegistryService;import org.jboss.weld.bean.AbstractClassBean;import org.jboss.weld.bean.AbstractProducerBean;import org.jboss.weld.bean.DisposalMethod;@@ -288,6 +290,16 @@Set<?> resolvedBeans = beanManager.getBeanResolver().resolve(beanManager.getBeans(ij));if (!isInjectionPointSatisfied(ij, resolvedBeans, beanManager)){+ ExternalLookupRegistryService service = beanManager.getServices().get(ExternalLookupRegistryService.class);+ if (service != null)+ {+ for (ExternalLookup<?> lookup : service.getExternalLookups())+ {+ if (ij.getAnnotated().isAnnotationPresent(lookup.getAnnotation()))+ if (lookup.isValid(ij.getType(), ij.getQualifiers()))+ return;+ }+ }throw new DeploymentException(INJECTION_POINT_HAS_UNSATISFIED_DEPENDENCIES, ij, Arrays.toString(bindings));}if (resolvedBeans.size() > 1 && !ij.isDelegate())[kabir ~/sourcecontrol/weld/core/subversion]$svn diff impl/src/main/java/org/jboss/weld/injection/Index: impl/src/main/java/org/jboss/weld/injection/FieldInjectionPoint.java===================================================================--- impl/src/main/java/org/jboss/weld/injection/FieldInjectionPoint.java (revision 6577)+++ impl/src/main/java/org/jboss/weld/injection/FieldInjectionPoint.java (working copy)@@ -36,6 +36,8 @@import javax.inject.Inject;import org.jboss.interceptor.util.InterceptionUtils;+import org.jboss.temp.weld.spi.ExternalLookup;+import org.jboss.temp.weld.spi.ExternalLookupRegistryService;import org.jboss.weld.exceptions.IllegalStateException;import org.jboss.weld.exceptions.InvalidObjectException;import org.jboss.weld.introspector.ForwardingWeldField;@@ -114,7 +116,23 @@// if declaringInstance is a proxy, unwrap itinstanceToInject = InterceptionUtils.getRawInstance(declaringInstance);}- delegate().set(instanceToInject, manager.getInjectableReference(this, creationalContext));++ Object value = null;+ ExternalLookupRegistryService service = manager.getServices().get(ExternalLookupRegistryService.class);+ if (service != null)+ {+ for (ExternalLookup<?> lookup : service.getExternalLookups())+ {+ if (getAnnotation(lookup.getAnnotation()) != null)+ value = lookup.lookupValue(getBaseType(), getQualifiers());+ }+ }++ if (value == null)+ value = manager.getInjectableReference(this, creationalContext);+++ delegate().set(instanceToInject, value);}catch (IllegalArgumentException e){Index: impl/src/main/java/org/jboss/weld/injection/ParameterInjectionPoint.java===================================================================--- impl/src/main/java/org/jboss/weld/injection/ParameterInjectionPoint.java (revision 6577)+++ impl/src/main/java/org/jboss/weld/injection/ParameterInjectionPoint.java (working copy)@@ -35,6 +35,8 @@import javax.enterprise.inject.spi.Bean;import javax.enterprise.inject.spi.Decorator;+import org.jboss.temp.weld.spi.ExternalLookup;+import org.jboss.temp.weld.spi.ExternalLookupRegistryService;import org.jboss.weld.exceptions.IllegalStateException;import org.jboss.weld.exceptions.InvalidObjectException;import org.jboss.weld.exceptions.UnsupportedOperationException;@@ -119,6 +121,15 @@@SuppressWarnings("unchecked")public T getValueToInject(BeanManagerImpl manager, CreationalContext<?> creationalContext){+ ExternalLookupRegistryService service = manager.getServices().get(ExternalLookupRegistryService.class);+ if (service != null)+ {+ for (ExternalLookup<?> lookup : service.getExternalLookups())+ {+ if (getAnnotation(lookup.getAnnotation()) != null)+ return (T)lookup.lookupValue(getBaseType(), getQualifiers());+ }+ }return (T) manager.getInjectableReference(this, creationalContext);}
In my test I have this implementation of the service classes
public class TestExternalLookup implements ExternalLookup<External> { @Override public boolean isValid(Type type, Set<Annotation> qualifiers) { if (type == Bar.class) return true; return false; } @Override public Class<External> getAnnotation() { return External.class; } @Override public Object lookupValue(Type type, Set<Annotation> qualifiers) { if (type == Bar.class) return new BarImpl(); return null; } } public class TestExternalLookupRegistryService implements ExternalLookupRegistryService { private Map<Class<? extends Annotation>, ExternalLookup<?>> lookups = Collections.synchronizedMap(new HashMap<Class<? extends Annotation>, ExternalLookup<?>>()); public void addExternalLookup(ExternalLookup<?> externalLookup) { lookups.put(externalLookup.getAnnotation(), externalLookup); } public Iterable<ExternalLookup<?>> getExternalLookups() { synchronized (lookups) { return new HashSet<ExternalLookup<?>>(lookups.values()); } } public boolean removeExternalLookup(Class<? extends Annotation> clazz) { return lookups.remove(clazz) != null; } public void cleanup() { lookups.clear(); } }
I register this service with the deployment and add the TestExternalLookup to it, meaning that when I create an instance of ExternalConstructorInjected the constructor parameter is found in TestExternalLookup.
public class ExternalConstructorInjected { Bar bar; @Inject public ExternalConstructorInjected(@External Bar bar) { this.bar = bar; } }
I think Pete is on vacation, so after adding a few more tests I'll add this to a branch somewhere so that it can be included once he okays it.