-
1. Re: Supporting qualifiers in MC
alesj Nov 17, 2009 11:07 AM (in response to kabirkhan)Actually, I think we should consider MDR as a proper place for qualifiers.
At least that's what Adrian and me are discussing wrt LDAP filtering and service properties in OSGi.
- http://www.jboss.org/index.html?module=bb&op=viewtopic&p=4265098#4265098
Where our qualifiers are nothing more then properties,
it's just that they are not all strings.
This way we would have a single mechanism for lookup,
regardless of what and where qualifiers/properties are/came,
or what the filtering mechanism is.
e.g.
Weld/jsr330 would have different filtering of MDR data than OSGi LDAP. -
2. Re: Supporting qualifiers in MC
alesj Nov 17, 2009 11:35 AM (in response to kabirkhan)"kabir.khan@jboss.com" wrote:
And there is a bean in the controller of type Something, but it does not supply @Test? I think coarse-grained needs to work slightly differently from fine-grained. Fine-grained needs a bean with all the qualifiers specified at the injection point. Coarse-grained needs a bean with as many as possible of the qualifiers specified at bean level.
We just need to make sure we have exact rules, whatever those might be.
The stuff we discussed could be categorized as pseudo. :-)
I guess the real examples and commons sense will flush those rules out."kabir.khan@jboss.com" wrote:
When looking into this, I noticed that AbstractInjectionValueMetaData takes the LookupStrategy into account when searching for the context, while the SearchClassContextDependencyItem does not appear to do so.
Could be an oversight. -
3. Re: Supporting qualifiers in MC
kabirkhan Nov 19, 2009 10:43 AM (in response to kabirkhan)I have something working for qualifiers now. The demanded qualifiers are currently only on bean-level (coarse-grained). I've added these methods to BeanMetaData:
/** * Get the qualifers exposed by this bean * * @return a set of the qualifiers */ Set<QualifierMetaData> getQualifiers(); /** * Get the qualifiers that will be used by default for properties and parameters * that don't have explicit qualifiers on their value dependency metadata * * @return a set of the qualifiers */ Set<QualifierMetaData> getDefaultQualifiers();
QuailifierMetaData:/** * MetaData describing the qualifiers * * @author <a href="kabir.khan@jboss.com">Kabir Khan</a> * @version $Revision: 1.1 $ */ public interface QualifierMetaData extends JBossInterface, MetaDataVisitorNode { /** * Get the qualifier * @return the qualifier */ Object getQualifier(); }
If a bean uses autowiring for it's injection and has "default qualifiers" a ClassAndQualifierKey is used to do the lookups from AbstractInjectionValue and SearchQualifiedClassDependencyItem (similar to SearchClassDependencyItem, but takes the ClassAndQualifierKey instead of the plain class. The calls from AIV and SQCPI end up here in AbstractKernelControllerpublic KernelRegistryEntry getEntry(Object name) { List<KernelControllerContext> list; + if (name instanceof ClassAndQualifierKey) + return ((ClassAndQualifierKey)name).search(this); if (name instanceof Matcher) list = matchSupplies((Matcher)name); else list = suppliers.get(name); if (list != null && list.isEmpty() == false) return list.get(0); else if (name instanceof Class) return getContextByClass((Class<?>) name); else return null; }
The ClassAndQualifierKey contains the logic to find the context with the qualifiers, and here briefly is what it doesclass ClassAndQualifierKey { Class<?> type; ControllerState dependentState; Set<Object> qualifiers; public KernelControllerContext search(KernelController controller) { Set<KernelControllerContext> contexts = controller.getContexts(type, dependentState); for (KernelControllerContext context : contexts) { for (Object myqualifier : qualifiers) { for (QualifierMetaData qualifier : context.getQualifierMetaData()) { //check myqualifier vs qualifier.getQualifier() } } } } }
As you can see I am not using MDR at all yet at this stage. Where do you see MDR fitting in? Is the idea to add all contexts that have a qualifier to somewhere in MDR and to be able to query that to avoid iterating over all contexts, or do you have something else in mind? I think I am missing what you guys mean by filtering in that other thread -
4. Re: Supporting qualifiers in MC
kabirkhan Nov 19, 2009 11:03 AM (in response to kabirkhan)"kabir.khan@jboss.com" wrote:
I
As you can see I am not using MDR at all yet at this stage. Where do you see MDR fitting in? Is the idea to add all contexts that have a qualifier to somewhere in MDR and to be able to query that to avoid iterating over all contexts, or do you have something else in mind? I think I am missing what you guys mean by filtering in that other thread
Aha :-) I found page 2&3 of the discussion. I guess you mean something similar to ServiceTracker? To clarify, do you have any examples of what OSGi needs? -
5. Re: Supporting qualifiers in MC
alesj Nov 19, 2009 3:57 PM (in response to kabirkhan)"kabir.khan@jboss.com" wrote:
do you have any examples of what OSGi needs?
OSGi has a notion of LDAP filtering; class + ldap-string filter.
e.g. org.acme.FooBar + (a=b)
Which means we're looking for service registry with FooBar impl
which also contains explicit property; key a, value b.
* http://anonsvn.jboss.org/repos/jbossas/projects/jboss-osgi/trunk/reactor/framework/src/test/java/org/jboss/test/osgi/service/GetServiceReferencesUnitTestCase.java -
6. Re: Supporting qualifiers in MC
kabirkhan Nov 20, 2009 11:51 AM (in response to kabirkhan)The requirements for the usage of MDR are clearer after our team call. For the record, supplied qualifiers should be read from the MDR so that qualifiers can be applied on for example deployment level.
I've stumbled upon this though when trying to populate the MDR with qualifers, that the mutable scope has not been created yet:public void describeVisit(MetaDataVisitor vistor) { super.describeVisit(vistor); KernelControllerContext context = vistor.getControllerContext(); MetaDataRetrieval retrieval = context.getKernel().getMetaDataRepository().getMetaDataRepository().getMetaDataRetrieval(context.getScopeInfo().getMutableScope()); if (retrieval instanceof MutableMetaData) { /At this stage retrieval is null }
I'm still a bit wooly on the MDR stuff, so I might be doing something wrong -
7. Re: Supporting qualifiers in MC
kabirkhan Nov 20, 2009 11:52 AM (in response to kabirkhan)Forgot to mention, the above method is from a MetaDataVisitorNode class
-
8. Re: Supporting qualifiers in MC
kabirkhan Nov 20, 2009 12:02 PM (in response to kabirkhan)It seems the metadata for the context is created after the context is described. From some breakpoints
QualifierMetaData.describeVisit(MetaDataVisitor) line: 59 DescribedMetaDataVisitor(AbstractMetaDataVisitor).internalDescribeVisit(MetaDataVisitorNode) line: 137 DescribedMetaDataVisitor(AbstractMetaDataVisitor).describeVisit(MetaDataVisitorNode) line: 87 AbstractBeanMetaData(AbstractFeatureMetaData).describeVisit(MetaDataVisitor) line: 106 DescribedMetaDataVisitor.run() line: 53 AccessController.doPrivileged(PrivilegedAction<T>) line: not available [native method] AbstractKernelControllerContext.infoprocessMetaData() line: 249 AbstractKernelControllerContext.setBeanInfo(BeanInfo) line: 180 PreInstallAction.installActionInternal(KernelControllerContext) line: 89 <<< PreInstallAction(InstallsAwareAction).installAction(KernelControllerContext) line: 54
BasicMetaDataRepository.addMetaDataRetrieval(MetaDataRetrieval) line: 109 KernelScopeInfo(AbstractScopeInfo).addMetaData(MutableMetaDataRepository, ControllerContext) line: 126 BasicKernelMetaDataRepository.addMetaData(ControllerContext) line: 70 PreInstallAction.installActionInternal(KernelControllerContext) line: 95 <<< PreInstallAction(InstallsAwareAction).installAction(KernelControllerContext) line: 54
-
9. Re: Supporting qualifiers in MC
kabirkhan Nov 23, 2009 9:54 AM (in response to kabirkhan)The problem is that in PreInstallAction, KernelControllerContext.setBeanInfo() adds the bean info and also describeVisits the metadata, while KernelMetaDataRepository.addMetaData() creates the MDR entry and updates the annotations. So with the way it is there is no MDR at the time we are in describeVisit.
protected void installActionInternal(KernelControllerContext context) throws Throwable { KernelController controller = (KernelController)context.getController(); Kernel kernel = controller.getKernel(); KernelConfigurator configurator = kernel.getConfigurator(); BeanMetaData metaData = context.getBeanMetaData(); if (metaData.getBean() != null) { BeanInfo info = configurator.getBeanInfo(metaData); context.setBeanInfo(info); KernelMetaDataRepository repository = controller.getKernel().getMetaDataRepository(); ClassLoader oldCL = SecurityActions.setContextClassLoader(context); try { repository.addMetaData(context); //1 } finally { SecurityActions.resetContextClassLoader(oldCL); } try { applyScoping(context); } catch (Throwable t) { removeMetaData(context); throw t; } } }
If I call KCC.setBeanInfo() after KMDR.addMetaData() some of the exisiting tests like ClassAnnotationTestCase break. Now, the problem is that the internal calls to update the annotations return early since there is no bean info. I have worked around this with the following change locally:protected void installActionInternal(KernelControllerContext context) throws Throwable { KernelController controller = (KernelController)context.getController(); Kernel kernel = controller.getKernel(); KernelConfigurator configurator = kernel.getConfigurator(); BeanMetaData metaData = context.getBeanMetaData(); if (metaData.getBean() != null) { BeanInfo info = configurator.getBeanInfo(metaData); context.setBeanInfo(info); KernelMetaDataRepository repository = controller.getKernel().getMetaDataRepository(); ClassLoader oldCL = SecurityActions.setContextClassLoader(context); try { repository.addMetaData(context); } finally { SecurityActions.resetContextClassLoader(oldCL); } try { applyScoping(context); } catch (Throwable t) { removeMetaData(context); throw t; } context.processMetaData(); } }
KCC.setBeanInfo() now does not describeVisit() the metadata, instead an explicit call is needed to KCC.processMetaData()$svn diff kernel/src/main/java/org/jboss/kernel/plugins/dependency/AbstractKernelControllerContext.java Index: kernel/src/main/java/org/jboss/kernel/plugins/dependency/AbstractKernelControllerContext.java =================================================================== --- kernel/src/main/java/org/jboss/kernel/plugins/dependency/AbstractKernelControllerContext.java (revision 96585) +++ kernel/src/main/java/org/jboss/kernel/plugins/dependency/AbstractKernelControllerContext.java (working copy) @@ -177,10 +177,15 @@ public void setBeanInfo(BeanInfo info) { this.info = info; - infoprocessMetaData(); flushJBossObjectCache(); } + public void processMetaData() + { + infoprocessMetaData(); + flushJBossObjectCache(); + } + public BeanMetaData getBeanMetaData() { return metaData;
-
10. Re: Supporting qualifiers in MC
kabirkhan Nov 24, 2009 7:03 AM (in response to kabirkhan)Following Ales's suggestions what I now have is:
Qualifier metadata is implemented as RelatedClassMetaData, where the qualifier objects are stored in RCMD.getEnabled(). To pick out the RCMDs that specify qualifiers I use 'RCMB#SUPPLIED_QUALIFIER' as the classname, and 'RCMB#WANTED_QUALIFIER' as the classname for wanted qualifiers specified at bean level.
Following the population of the metadata and beaninfo in PreInstallAction (and in InstallAction if beaninfo was null) I populate the MDR with sets of qualifiers from the metadata. These are stored in the INSTANCE level metadata under 'RCMB#SUPPLIED_QUALIFIER' and 'RCMB#WANTED_QUALIFIER' respectively.
The search algorithm works much the same as before. ClassAndQualifierKey queries for all the beans implementing a class, and then narrows it down depending on the wanted qualifiers and the required qualifiers.
EDIT: The qualifiers now are looked up from the MDR for the contexts involved
Before I commit, I want to add some xml configuration of qualifiers. I propose<bean name="bean1" class="SomeBean"> <qualifier>a</qualifier> </bean> <bean name="bean2"class="SomeBean"> <qualifier>a</qualifier> </qualifier>b</qualifier> </bean> <bean name="bean3" class="OtherBean"> <qualifier type="default">a</qualifier> </bean>
Meaning that the bean1 supplies qualifier 'a', bean2 supplies qualifiers 'a', 'b' and bean3 uses 'a' by default when looking for other beans to inject into it. So when injecting SomeBean instances into bean3, bean1 will be chosen.
Next, I want to look at somehow supporting fine-grained qualifiers injection. If we have two instances ofclass Bean{}
where instance 1 supplies qualifier 'A' and instance 2 supplies qualifier 'B' and we haveclass Target { Bean a; Bean b; @Constructor Target(@Inject Bean a) { this.a = a; } @Inject void setB() { this.b = b; } }
It should be possible to specify qualifiers for injected parameters and properties, so that in this example we can say the constructor wants the Bean with qualifier 'A' while the setter wants the Bean with the qualifier 'B'. I'll see if I can use/expand AbstractInjectionValueMetaData to use qualifiers. -
11. Re: Supporting qualifiers in MC
kabirkhan Nov 24, 2009 11:12 AM (in response to kabirkhan)"kabir.khan@jboss.com" wrote:
The search algorithm works much the same as before. ClassAndQualifierKey queries for all the beans implementing a class, and then narrows it down depending on the wanted qualifiers and the required qualifiers.
EDIT: The qualifiers now are looked up from the MDR for the contexts involved
I forgot to mention that in the case of qualifier metadata existing at a higher level in MDR than instance level, the search takes that into account and merges whatever it can find at all levels. e.g., if ["a", "b"] exists at JVM level and ["c", "d"] at INSTANCE level, when doing the lookup for an instance ["a", "b", "c", "d"] is returned."kabir.khan@jboss.com" wrote:
It should be possible to specify qualifiers for injected parameters and properties, so that in this example we can say the constructor wants the Bean with qualifier 'A' while the setter wants the Bean with the qualifier 'B'. I'll see if I can use/expand AbstractInjectionValueMetaData to use qualifiers.
I've done this. This differs from the bean-level qualifiers in that if a qualifier exists on an injection point, then the bean-level qualifiers are ignored, so the property level ones completely override the bean level ones e.g.class TargetBean { Bean bean1; Bean bean2; }
If the bean has RelatedClassMetaData specifying that it's wanted qualifiers are 'A', and the property bean2 specifies that the qualifiers are 'B', for bean1 we will inject a bean that supplies the qualifier 'A', while for bean2 a bean that supplies the qualifier 'B' will be injected.
I don't know if the qualifiers for the injection points should be read from the MDR or not? At first glance doing that does not make sense since the component metadata lives below instance level, but I might be wrong. Especially if my previous assumption that injection point qualifiers should completely override the bean-level wanted qualifiers is wrong?
I'll add some xml configuration for everything before I commit -
12. Re: Supporting qualifiers in MC
kabirkhan Nov 24, 2009 2:39 PM (in response to kabirkhan)I am able to add qualifiers to the bean level via xml easily, e.g.:
<bean xmlns="urn:jboss:bean-deployer:2.0" class="Dummy"> <qualifier> <javabean xmlns="urn:jboss:javabean:1.0" class="java.util.Date"/> <javabean xmlns="urn:jboss:javabean:1.0" class="java.lang.String"/> </qualifier> </bean> <bean xmlns="urn:jboss:bean-deployer:2.0" class="Dummy"> <qualifier type="Wanted">aop</qualifier> </bean>
This is handled by a sub class of AbstractRelatedClassMetadata which hardcodes the name of the class depending on the qualifier type being Wanted or Supplied (Default).
Now, trying to add this for injection points, I get the following to parse:<bean xmlns="urn:jboss:bean-deployer:2.0" class="Dummy"> <property name="test"> <inject> <qualifier> <javabean xmlns="urn:jboss:javabean:1.0" class="java.util.Date"/> <javabean xmlns="urn:jboss:javabean:1.0" class="java.lang.String"/> </qualifier> </inject> </property> </bean>
But since I also want to be able to add these qualifiers directly programatically whereby I have been doing:AbstractInjectionValueMetaData inject = new AbstractInjectionValueMetaData(); inject.setInjectionPointQualifiers(new HashSet<Object>(Arrays.asList(qualifiers)));
I end up with two sets of properties in AbstractInjectionValueMetaData for the same thing:/** * Get the injectionPointQualifiers * @return the injectionPointQualifiers */ public Set<Object> getInjectionPointQualifiers() { return injectionPointQualifiers; } /** * Set the injectionPointQualifiers * @param injectionPointQualifiers the injectionPointQualifiers to set */ public void setInjectionPointQualifiers(Set<Object> injectionPointQualifiers) { this.injectionPointQualifiers = injectionPointQualifiers; } /** * Get the injectionPointQualifierMetaData * @return the injectionPointQualifierMetaData */ public Set<RelatedClassMetaData> getInjectionPointQualifierMetaData() { return injectionPointQualifierMetaData; } /** * Set the injectionPointQualifierMetaData * @param injectionPointQualifierMetaData the injectionPointQualifierMetaData to set */ @XmlElement(name="qualifier", type=AbstractQualifierMetaData.class) //Sub-class of AbstractRelatedClassMetaData public void setInjectionPointQualifierMetaData(Set<RelatedClassMetaData> injectionPointQualifierMetaData) { this.injectionPointQualifierMetaData = injectionPointQualifierMetaData; }
I like the convenience of being able to call setInjectionPointQualifiers() directly when creating this programatically without having to wrap it up in an AbstractQualifierMetaData, but I guess that can be hidden in the BeanMetaDataBuilder. So unless somebody knows how to avoid that, I'll take that route. -
13. Re: Supporting qualifiers in MC
kabirkhan Nov 25, 2009 6:20 AM (in response to kabirkhan)What I have so far: https://jira.jboss.org/jira/browse/JBKERNEL-63
-
14. Re: Supporting qualifiers in MC
kabirkhan Nov 25, 2009 12:11 PM (in response to kabirkhan)Speaking to Ales he mentioned that:
"@Inject should pick up qualifiers from annotations"
I think this should also be done when inject is done via xml/beanmetadata. Since most qualifiers I've seen (in jsr-299 and jsr-330) are picked out using annotations on the annotation, I propose adding something to BeanAnnotationAdapter to handle meta-annotations.