13 Replies Latest reply on Feb 11, 2009 10:02 AM by kabirkhan

    How to reliably determine if BeanMetaData contains dependenc

    kabirkhan

      I need a way to reliably determine if a BeanMetaData contains dependencies. aop-mc-int tests in trunk for aspects with dependencies are failing due to my way of determining this no longer working there, and this in turn causes aop failures in AS Branch_5_0 with the latest mc jars.

      The background is: when reading the aop.xml I create different underlying beans in
      https://svn.jboss.org/repos/jbossas/projects/microcontainer/trunk/aop-mc-int/src/main/java/org/jboss/aop/microcontainer/beans/metadata/AspectBeanMetaDataFactory.java
      depending on if the bean has dependencies

      <aop xmlns="urn:jboss:aop-beans:1.0">
       <aspect class="org.jboss.test.microcontainer.beans.TestAspectWithDependency">
       <property name="dependency"><inject bean="Dependency"/></property>
       </aspect>
      </aop>
      

      or not
      <aop xmlns="urn:jboss:aop-beans:1.0">
       <aspect class="org.jboss.test.microcontainer.beans.TestAspect">
       </aspect>
      </aop>
      


      This gets parsed by AspectBMDFactory
       @Override
       public List<BeanMetaData> getBeans()
       {
       ArrayList<BeanMetaData> result = new ArrayList<BeanMetaData>();
      
       if (this.name == null)
       {
       this.name = super.getBean();
       }
      
       //Add the bean factory
       if (!initialisedName)
       {
       aspectName = this.name;
       this.name = "Factory$" + name;
       initialisedName = true;
       }
       List<BeanMetaData> beans = super.getBeans();
       if (beans.size() != 1)
       {
       throw new RuntimeException("Wrong number of beans" + beans);
       }
       BeanMetaData factory = beans.get(0);
       BeanMetaDataBuilder factoryBuilder = BeanMetaDataBuilder.createBuilder(factory);
       factoryBuilder.setBean(ClassLoaderAwareGenericBeanFactory.class.getName());
       result.add(factory);
      
       //Add the Aspect
       BeanMetaDataBuilder aspectBuilder = BeanMetaDataBuilder.createBuilder(aspectName, Aspect.class.getName());
       aspectBuilder.addPropertyMetaData("scope", scope);
       aspectBuilder.addPropertyMetaData("name", aspectName);
       HashMap<String, String> attributes = new HashMap<String, String>();
       attributes.put("name", name);
       if (factory != null)
       {
       attributes.put("factory", this.factory);
       }
       else
       {
       attributes.put("class", bean);
       }
       attributes.put("scope", scope);
       if (elements != null && elements.size() > 0)
       {
       aspectBuilder.addPropertyMetaData("element", XmlLoadableRootElementUtil.getRootElementString(elements, getTagName(), attributes));
       }
      
       setAspectManagerProperty(aspectBuilder);
      
       if (this.factory != null)
       {
       aspectBuilder.addPropertyMetaData("factory", Boolean.TRUE);
       }
       result.add(aspectBuilder.getBeanMetaData());
      
       System.out.println(factory.getRelated());
      
       if (hasInjectedBeans(factory))
       {
       configureWithDependencies(factoryBuilder, aspectBuilder);
       }
       else
       {
       configureNoDependencies(aspectBuilder);
       }
      
       return result;
       }
      

      The code I had, hasInjectedBeans(), to determine whether there are any dependencies for "factory" worked properly in MC Branch_2_0, but always returns false in trunk. I've added some modifications locally to inspect what it is doing
       private boolean hasInjectedBeans(BeanMetaData beanMetaData)
       {
       ArrayList<ValueMetaData> dependencies = new ArrayList<ValueMetaData>();
       System.out.println("======== Getting dependencies");
       getDependencies(dependencies, beanMetaData, 0);
      
       for (ValueMetaData dep : dependencies)
       {
       if(!((String)dep.getUnderlyingValue()).startsWith("jboss.kernel:service="))
       {
       return true;
       }
       }
       return false;
       }
      
       private void getDependencies(ArrayList<ValueMetaData> dependencies, MetaDataVisitorNode node, int i)
       {
       System.out.println(indent(i) + node);
       Iterator<? extends MetaDataVisitorNode> children = node.getChildren();
      
       if (children != null)
       {
       while (children.hasNext())
       {
       MetaDataVisitorNode child = children.next();
       if (child instanceof AbstractDependencyValueMetaData)
       {
       System.out.println(indent(i+1) + "DEPENDENCY: " + child);
       dependencies.add((AbstractDependencyValueMetaData)child);
       }
       getDependencies(dependencies, child, i + 1);
       }
       }
       }
      
       private String indent(int l)
       {
       String s = "-";
       for (int i = 0 ; i < l ; i++)
       {
       s = s + "-";
       }
       return s;
       }
      

      The output when parsing
      <aop xmlns="urn:jboss:aop-beans:1.0">
       <aspect class="org.jboss.test.microcontainer.beans.TestAspectWithDependency">
       <property name="dependency"><inject bean="Dependency"/></property>
       </aspect>
      </aop>
      

      in Branch_2_0
      ======== Getting dependencies
      -AbstractBeanMetaData@c59085{name=Factory$org.jboss.test.microcontainer.beans.TestAspectWithDependency bean=org.jboss.aop.microcontainer.bea
      ns.ClassLoaderAwareGenericBeanFactory properties=[bean, properties] constructor=AbstractConstructorMetaData@1d0462{parameters=[org.jboss.ker
      nel.spi.config.KernelConfigurator]} autowireCandidate=true related=[org.jboss.test.microcontainer.beans.TestAspectWithDependency]}
      --AbstractPropertyMetaData@908404{name=bean value=StringValueMetaData@6cf4f9{value=org.jboss.test.microcontainer.beans.TestAspectWithDepende
      ncy}}
      ---StringValueMetaData@6cf4f9{value=org.jboss.test.microcontainer.beans.TestAspectWithDependency}
      --AbstractPropertyMetaData@cc2f42{name=properties value=AbstractValueMetaData@470a37{value={dependency=AbstractInjectionValueMetaData@efbbb1
      {value=Dependency injectionType=BY_CLASS}}}}
      ---AbstractValueMetaData@470a37{value={dependency=AbstractInjectionValueMetaData@efbbb1{value=Dependency injectionType=BY_CLASS}}}
      ----{dependency=AbstractInjectionValueMetaData@efbbb1{value=Dependency injectionType=BY_CLASS}}
      -----DEPENDENCY: AbstractInjectionValueMetaData@efbbb1{value=Dependency injectionType=BY_CLASS}
      -----AbstractInjectionValueMetaData@efbbb1{value=Dependency injectionType=BY_CLASS}
      --AbstractConstructorMetaData@1d0462{parameters=[org.jboss.kernel.spi.config.KernelConfigurator]}
      ---AbstractParameterMetaData@b73e5{type=org.jboss.kernel.spi.config.KernelConfigurator value=AbstractDependencyValueMetaData@e5e871{value=jb
      oss.kernel:service=KernelConfigurator}}
      ----DEPENDENCY: AbstractDependencyValueMetaData@e5e871{value=jboss.kernel:service=KernelConfigurator}
      ----AbstractDependencyValueMetaData@e5e871{value=jboss.kernel:service=KernelConfigurator}
      --AbstractRelatedClassMetaData@ff6313{name=org.jboss.test.microcontainer.beans.TestAspectWithDependency, enabled=null}
      

      You can see it properly picks out the bean called "Dependency". In trunk it does not work due to a new structure of the bean metadata
      ======== Getting dependencies
      -AbstractBeanMetaData@88f1c5{name=Factory$org.jboss.test.microcontainer.beans.TestAspectWithDependency bean=org.jboss.aop.microcontainer.bea
      ns.ClassLoaderAwareGenericBeanFactory properties=[properties, bean] constructor=AbstractConstructorMetaData@3291db{parameters=[org.jboss.ker
      nel.spi.config.KernelConfigurator]} autowireCandidate=true related=[org.jboss.test.microcontainer.beans.TestAspectWithDependency]}
      --AbstractPropertyMetaData@33b5db{name=properties value=AbstractValueMetaData@ad2911{value={dependency=PropertyMap$ValueInfo@6339b2{}}}}
      ---AbstractValueMetaData@ad2911{value={dependency=PropertyMap$ValueInfo@6339b2{}}}
      ----{dependency=PropertyMap$ValueInfo@6339b2{}}
      -----PropertyMap$ValueInfo@6339b2{}
      --AbstractPropertyMetaData@363bce{name=bean value=StringValueMetaData@61091e{value=org.jboss.test.microcontainer.beans.TestAspectWithDepende
      ncy}}
      ---StringValueMetaData@61091e{value=org.jboss.test.microcontainer.beans.TestAspectWithDependency}
      --AbstractConstructorMetaData@3291db{parameters=[org.jboss.kernel.spi.config.KernelConfigurator]}
      ---AbstractParameterMetaData@9dbc5c{type=org.jboss.kernel.spi.config.KernelConfigurator value=AbstractDependencyValueMetaData@b967ed{value=j
      boss.kernel:service=KernelConfigurator}}
      ----DEPENDENCY: AbstractDependencyValueMetaData@b967ed{value=jboss.kernel:service=KernelConfigurator}
      ----AbstractDependencyValueMetaData@b967ed{value=jboss.kernel:service=KernelConfigurator}
      --AbstractRelatedClassMetaData@562d4b{name=org.jboss.test.microcontainer.beans.TestAspectWithDependency, enabled=null}
      


      That will teach me not to rely on implementation details...

        • 1. Re: How to reliably determine if BeanMetaData contains depen
          kabirkhan

          I have had a look at creating my own BeanMetaDataVisitor implementation for use in AspectBeanMetaDataFactory

           private boolean hasInjectedBeans(BeanMetaData beanMetaData)
           {
           DependencyBeanMetaDataVisitor visitor = new DependencyBeanMetaDataVisitor(beanMetaData);
          
           beanMetaData.describeVisit(visitor);
           beanMetaData.initialVisit(visitor);
          
           return beanMetaData.getHasDependencies();
           }
          
          
           private static class DependencyBeanMetaDataVisitor extends AbstractMetaDataVisitor
           {
           private boolean hasDependencies;
          
           protected DependencyBeanMetaDataVisitor(BeanMetaData bmd)
           {
           // FIXME DependencyBeanMetaDataVisitor constructor
           super(bmd, new DependencyKernelControllerContext(bmd));
           }
          
           public boolean getHasDependencies()
           {
           return hasDependencies;
           }
          
           public void addDependency(DependencyItem dependency)
           {
           hasDependencies = true;
           }
           }
          

          But describeVisit() does not work due to the underlying value not being picked up in AbstractInjectionValueMetaData
           @SuppressWarnings("deprecation")
           public void describeVisit(MetaDataVisitor visitor)
           {
           // no bean and not by_name
           if (getUnderlyingValue() == null)
           {
           ...
           }
           super.describeVisit(visitor);
           }
          

          installVisit() gives NPEs since we don't have a KernelControllerContext for the created bean at this stage. I tried mocking one, but so far that does not work since it needs a reference to the kernel etc., but I will try a bit more.

          • 2. Re: How to reliably determine if BeanMetaData contains depen
            kabirkhan

            With the following hacked KCC initialVisit() works here. I have only showed methods that I was forced to return something from to stop NPEs. The dependencies are picked out correctly now, and the failing tests pass, but it is horrible. I don't think I should need to create KCC's when reading the metadata?

             private boolean hasInjectedBeans(BeanMetaData beanMetaData)
             {
             DependencyBeanMetaDataVisitor visitor = new DependencyBeanMetaDataVisitor(beanMetaData);
            
             beanMetaData.initialVisit(visitor);
             return visitor.getHasDependencies();
             }
            
             private static class DependencyBeanMetaDataVisitor extends AbstractMetaDataVisitor
             {
             private boolean hasDependencies;
            
             protected DependencyBeanMetaDataVisitor(BeanMetaData bmd)
             {
             super(bmd, new DependencyMetaDataKernelControllerContext(bmd));
             }
            
             public boolean getHasDependencies()
             {
             return hasDependencies;
             }
            
             public void addDependency(DependencyItem dependency)
             {
             if (!((String)dependency.getIDependOn()).startsWith("jboss.kernel:service="))
             {
             //TODO revisit
             //Ignore the kernel dependencies
             hasDependencies = true;
             }
             }
             }
            
             private static class DependencyMetaDataKernelControllerContext extends JBossObject implements KernelControllerContext
             {
             private static final Kernel kernel = new Kernel();
             private BeanMetaData beanMetaData;
            
             public DependencyMetaDataKernelControllerContext(BeanMetaData beanMetaData)
             {
             this.beanMetaData = beanMetaData;
             }
            
             public BeanMetaData getBeanMetaData()
             {
             return beanMetaData;
             }
            
             public Kernel getKernel()
             {
             return kernel;
             }
             }
            



            • 3. Re: How to reliably determine if BeanMetaData contains depen
              alesj

              Uf, this is one ugly hack. :-)

              • 4. Re: How to reliably determine if BeanMetaData contains depen
                kabirkhan

                Just there to show what I am after

                • 5. Re: How to reliably determine if BeanMetaData contains depen
                  dimitris

                  So is there any progress here? We don't have all the time in the world...

                  • 6. Re: How to reliably determine if BeanMetaData contains depen
                    kabirkhan

                    It "works", but is really ugly and hacky.

                    I need some input from the MC guys on how to clean this up, or an alternative properly supported approach.

                    In the worst case scenario I can disable the dependencies check, and configure all aspect beans as if there are always dependencies, but I would prefer not to do so.

                    • 7. Re: How to reliably determine if BeanMetaData contains depen
                      alesj

                       

                      "kabir.khan@jboss.com" wrote:

                      I need some input from the MC guys on how to clean this up, or an alternative properly supported approach.

                      I'll pitch in once I'm done with my VFS work.
                      Currently fixing Shelly's findings ...


                      • 8. Re: How to reliably determine if BeanMetaData contains depen
                        kabirkhan

                        https://jira.jboss.org/jira/browse/JBMICROCONT-407
                        After talking to Ales I have come up with a new version of the visitor

                         private boolean hasInjectedBeans(BeanMetaData beanMetaData)
                         {
                         DependencyMetaDataVisitor visitor = new DependencyMetaDataVisitor(beanMetaData);
                         List<ValueMetaData> dependencies = visitor.getDependencies();
                        
                         for (ValueMetaData dep : dependencies)
                         {
                         //Ignore the dependencies from the kernel objects
                         if(!((String)dep.getUnderlyingValue()).startsWith("jboss.kernel:service="))
                         {
                         return true;
                         }
                         }
                         return false;
                         }
                        
                         private static class DependencyMetaDataVisitor extends JBossObject implements MetaDataVisitor
                         {
                         MetaDataVisitorNode node;
                         List<ValueMetaData> dependencies = new ArrayList<ValueMetaData>();
                         protected Stack<MetaDataVisitorNode> visitorNodeStack = new Stack<MetaDataVisitorNode>();
                        
                         public DependencyMetaDataVisitor(MetaDataVisitorNode node)
                         {
                         this.node = node;
                         describeVisit(node);
                         }
                        
                         public List<ValueMetaData> getDependencies()
                         {
                         return dependencies;
                         }
                        
                         public void addDependency(DependencyItem dependency)
                         {
                         }
                        
                         public <T> void addInstallCallback(CallbackItem<T> callback)
                         {
                         }
                        
                         public <T> void addUninstallCallback(CallbackItem<T> callback)
                         {
                         }
                        
                         public ControllerState getContextState()
                         {
                         return null;
                         }
                        
                         public KernelControllerContext getControllerContext()
                         {
                         return null;
                         }
                        
                         public void initialVisit(MetaDataVisitorNode node)
                         {
                         throw new NotImplementedException("Not implemented");
                         }
                        
                         public void describeVisit(MetaDataVisitorNode node)
                         {
                         visitorNodeStack.push(node);
                         try
                         {
                         internalDescribeVisit(node);
                         }
                         finally
                         {
                         visitorNodeStack.pop();
                         }
                         }
                        
                         private void internalDescribeVisit(MetaDataVisitorNode node)
                         {
                         if (node instanceof AbstractDependencyValueMetaData)
                         {
                         dependencies.add((AbstractDependencyValueMetaData)node);
                         }
                         Iterator<? extends MetaDataVisitorNode> children = node.getChildren();
                         if (children != null)
                         {
                         while (children.hasNext())
                         {
                         MetaDataVisitorNode child = children.next();
                         child.describeVisit(this);
                         }
                         }
                         }
                        
                         public void setContextState(ControllerState contextState)
                         {
                         }
                        
                         public Stack<MetaDataVisitorNode> visitorNodeStack()
                         {
                         return visitorNodeStack;
                         }
                        
                         }
                        


                        • 9. Re: How to reliably determine if BeanMetaData contains depen
                          kabirkhan

                          Looking more into this it seems that
                          AbstractCallbackMetaData.describeVisit() and AbstractInjectionMetaData.describeVisit() both need access to MetaDataVisitor.getControllerContext(), so we'll get NPEs unless there is a hacked KCC, which brings us back to where we started.

                          Can we not just add

                          void MetaDataVisitor.structureVisit(MetaDataVisitorNode node);
                          void MetaDataVisitorNode.structureVisit(MetaDataVisitor visitor);
                          

                          where the MetaDataVisitorNode.structureVisit() impls don't ever do anything clever, just delegate to the visitor
                          void structureVisit(MetaDataVisitor visitor)
                          {
                           visitor.structureVisit(this);
                          }
                          

                          Would that not be more in line with a "normal" visitor implementation?



                          • 10. Re: How to reliably determine if BeanMetaData contains depen
                            alesj

                            You're doing it wrong. ;-)

                            This is all you need:

                             void doVisit(MetaDataVisitorNode node)
                             {
                             // check node
                             someNodeCheck(node);
                            
                             Iterator<? extends MetaDataVisitorNode> children = node.getChildren();
                             if (children != null)
                             {
                             while(children.hasNext())
                             {
                             doVisit(children.next());
                             }
                             }
                             }
                            


                            • 11. Re: How to reliably determine if BeanMetaData contains depen
                              kabirkhan

                              That is exactly what I had and which does not work, see my first post...

                              Reorganising the code a bit

                               private void getDependencies(ArrayList<ValueMetaData> dependencies, MetaDataVisitorNode node)
                               {
                               if (node instanceof AbstractDependencyValueMetaData)
                               {
                               dependencies.add((AbstractDependencyValueMetaData)node);
                               }
                              
                               Iterator<? extends MetaDataVisitorNode> children = node.getChildren();
                              
                               if (children != null)
                               {
                               while (children.hasNext())
                               {
                               MetaDataVisitorNode child = children.next();
                               getDependencies(dependencies, child);
                               }
                               }
                               }
                              


                              1) getDependencies() gets called with an AbstractBeanMetaData, this finds a child and calls getDependencies()
                              2) getDependencies() gets called with an AbstractPropertyMetaData (child of 1), this finds a child and calls getDependencies()
                              3) getDependencies() gets called with an AbstractValueMetaData (child of 2), this finds a child and calls getDepedencies()
                              4) getDependencies() gets called with a PropertyMap (child of 3), this finds a child and calls getDepedencies()
                              5) getDependencies() gets called with a PropertyMap$ValueInfo (child of 4), this has no children and the loop returns

                              Looking at the PropertyMap$ValueInfo, it contains the AbstractInjectionValueMetaData I want, but no way to get hold of it. From my debugger:
                              node PropertyMap$ValueInfo (id=113)
                               hashCode -2147483648
                               log Logger (id=118)
                               name "dependency" (id=120)
                               toString SoftReference<T> (id=124)
                               value AbstractInjectionValueMetaData (id=126)
                              


                              PropertyMap$ValueInfo seems wrong, replacing
                               public Iterator<? extends MetaDataVisitorNode> getChildren()
                               {
                               return value.getChildren();
                               }
                              

                              with
                               public Iterator<? extends MetaDataVisitorNode> getChildren()
                               {
                               return Collections.singletonList(value).iterator();
                               }
                              

                              I get this for 5) onwards:
                              5) getDependencies() gets called with a PropertyMap$ValueInfo (child of 4), this finds a child and calls getDepedencies()
                              6) getDependencies() is called with an AbstractInjectionValueMetaData and the dependency is picked out as expected.

                              So either PropertyMap$ValueInfo.getChildren() needs to be fixed, or you need to come up with something else if that fix is not valid.







                              • 12. Re: How to reliably determine if BeanMetaData contains depen
                                kabirkhan

                                 

                                "kabir.khan@jboss.com" wrote:
                                T
                                6) getDependencies() is called with an AbstractInjectionValueMetaData and the dependency is picked out as expected.

                                should be:
                                6) getDependencies() is called with an AbstractInjectionValueMetaData (child of 2) and the dependency is picked out as expected.



                                • 13. Re: How to reliably determine if BeanMetaData contains depen
                                  kabirkhan

                                   

                                  "kabir.khan@jboss.com" wrote:
                                  "kabir.khan@jboss.com" wrote:
                                  T
                                  6) getDependencies() is called with an AbstractInjectionValueMetaData and the dependency is picked out as expected.

                                  should be:
                                  6) getDependencies() is called with an AbstractInjectionValueMetaData (child of 2) and the dependency is picked out as expected.


                                  lol

                                  "6) getDependencies() is called with an AbstractInjectionValueMetaData (child of 5) and the dependency is picked out as expected."