9 Replies Latest reply on Dec 6, 2007 5:02 PM by alesj

    Case sensitivity in injecting a bean's property

    brian.stansberry

      There seems to have been a recent change that affects the handling of property injection.

      I want to inject the value of the naming service port into another bean. Previously this worked:

      <property name="namingServicePort"><inject bean="jboss:service=Naming" property="port"/></property>


      Now it doesn't. I have to use 'property="Port"' with a capital P on Port. Otherwise I get:

      javax.management.AttributeNotFoundException: not found: port
       at org.jboss.mx.server.AbstractMBeanInvoker.getAttribute(AbstractMBeanIn
      voker.java:335)
       at org.jboss.mx.server.MBeanServerImpl.getAttribute(MBeanServerImpl.java
      :565)
       at org.jboss.system.microcontainer.ServiceControllerContext.get(ServiceC
      ontrollerContext.java:129)
       at org.jboss.beans.metadata.plugins.AbstractDependencyValueMetaData.getV
      alue(AbstractDependencyValueMetaData.java:150)
      ....


        • 1. Re: Case sensitivity in injecting a bean's property
          alesj

          We added direct MBeanServer lookup via attribute for MBeans.

          Look-up behaves differently for ServiceControllerContext then for KernelControllerContext.

          Look at: http://www.jboss.org/index.html?module=bb&op=viewtopic&t=98875

          • 2. Re: Case sensitivity in injecting a bean's property
            starksm64

            I noted the issue between bean and mbean property case a while ago:
            http://www.jboss.com/index.html?module=bb&op=viewtopic&t=92469

            so I'm surprised this is a regression. I don't know why it would have worked because even though the correct case ala java beans for a setPort/getPort is port (based on java.beans.Introspector behavior/javadoc), jmx 1.2, page 40 states:


            Case Sensitivity
            All attribute and operation names derived from these design patterns are casesensitive.
            For example, this means that the methods getstate and setState
            define two attributes, one called state that is read-only, and one called State that
            is write-only.

            While case sensitivity applies directly to component names of standard MBeans, it is
            also applicable to all component names of all types of MBeans, standard or dynamic.
            In general, all names of classes, attributes, operations, methods, and internal
            elements defined in the JMX specification are case sensitive, whether they appear as
            data or as functional code when they are manipulated by management operations.


            so there are conflicting naming conventions. All I can see that we do is to allow a property matching strictness setting that can be specified when referencing a bean property that would relax the case sensitivity issue.


            • 3. Re: Case sensitivity in injecting a bean's property
              starksm64

               

              "alesj" wrote:
              We added direct MBeanServer lookup via attribute for MBeans.

              Look-up behaves differently for ServiceControllerContext then for KernelControllerContext.

              Look at: http://www.jboss.org/index.html?module=bb&op=viewtopic&t=98875


              Injection of mbean properties had been working before this change though. How was the mbean property name being resolved previously?


              • 4. Re: Case sensitivity in injecting a bean's property
                alesj

                 

                "scott.stark@jboss.org" wrote:
                How was the mbean property name being resolved previously?


                Simple POJO property look-up - via Configurator.

                Looks like it worked since there was already a pojo target underlying in ControllerContext.

                • 5. Re: Case sensitivity in injecting a bean's property
                  alesj

                  Before:

                   Object result = context.getTarget();
                   if (result != null && property != null)
                   {
                   KernelConfigurator configurator = controller.getKernel().getConfigurator();
                   BeanInfo beanInfo = configurator.getBeanInfo(result.getClass());
                   TargettedJoinpoint joinpoint = configurator.getPropertyGetterJoinPoint(beanInfo, property);
                   joinpoint.setTarget(result);
                   result = joinpoint.dispatch();
                   }
                  



                  • 6. Re: Case sensitivity in injecting a bean's property
                    starksm64

                    Then we need to look at how the kernel behaves with beans like:

                    public class BeanWithStates
                    {
                     public String getstate()
                     {
                     return "state";
                     }
                     public void setstate(String s)
                     {
                     }
                    
                     public int getState()
                     {
                     return 0;
                     }
                     public void setState(int s)
                     {
                     }
                    }
                    


                    The java.beans.Introspector does not pickup different properties state and State for this:
                    package javabean;
                    
                    import java.beans.BeanInfo;
                    import java.beans.Introspector;
                    import java.beans.PropertyDescriptor;
                    
                    import org.junit.Test;
                    
                    public class TestIntrospector
                    {
                     @Test
                     public void testBeanPropertyCase()
                     throws Exception
                     {
                     BeanInfo beanWithStatesInfo = Introspector.getBeanInfo(BeanWithStates.class);
                     PropertyDescriptor[] props = beanWithStatesInfo.getPropertyDescriptors();
                     System.out.println("BeanWithStates properties:");
                     for(PropertyDescriptor p : props)
                     {
                     System.out.println(p.getName()+", read: "+p.getReadMethod()+", write: "+p.getWriteMethod());
                     }
                     }
                    }
                    
                    BeanWithStates properties:
                    class, read: public final native java.lang.Class java.lang.Object.getClass(), write: null
                    state, read: public java.lang.String javabean.BeanWithStates.getstate(), write: public void javabean.BeanWithStates.setstate(java.lang.String)
                    


                    Instead, it only finds state, but does use the setter/getter that is consistent with the mbean naming conventions. I guess one would have to supply a class level java.beans.BeanInfo to have access to both state and State as properties.


                    • 7. Re: Case sensitivity in injecting a bean's property

                      Ok, so first of all, we need a test for this in system-jmx.

                      The only test for cross injection just uses the object, it does not test
                      accessing a "property" of an MBean.

                      org.jboss.test.system.controller.integration.test.ConfigureMCFromJMXTestCase-mc.xml

                      <deployment xmlns="urn:jboss:bean-deployer:2.0">
                       <bean name="Test" class="org.jboss.test.system.controller.integration.support.SimpleBean">
                       <property name="simple"><inject bean="jboss.test:type=test"/></property>
                       </bean>
                      </deployment>
                      


                      Previously, this just treated the MBean like a javabean
                      (i.e. the property name was port)
                      looking directly at the MBean object.

                      But now it has been changed to go through the JMX api
                      (so it needs to use the JMX attribute name Port).

                      Alternatively, we could do as Scott says and try both in ServiceControllerContext
                       public Object get(String name) throws Throwable
                       {
                       try
                       {
                       return getMBeanServer().getAttribute(objectName, name);
                       }
                       catch (AttributeNotFoundException orignal)
                       {
                       try
                       {
                       name = switchToUpperOrLowerCase(name);
                       return getMBeanServer().getAttribute(objectName, name);
                       }
                       catch (AttributeNotFoundException e)
                       {
                       throw original;
                       }
                       }
                       }
                      


                      • 8. Re: Case sensitivity in injecting a bean's property
                        starksm64

                        A task to create the interaction tests
                        http://jira.jboss.com/jira/browse/JBAS-4009

                        • 9. Re: Case sensitivity in injecting a bean's property
                          alesj

                          I already added this check while working on ProfileService:

                           protected static String getAttributeName(String name)
                           {
                           if (name == null || name.length() == 0)
                           throw new IllegalArgumentException("Illegal name: " + name);
                          
                           char firstCharacter = name.charAt(0);
                           if (Character.isLowerCase(firstCharacter))
                           {
                           String attributeName = String.valueOf(Character.toUpperCase(firstCharacter));
                           if (name.length() > 1)
                           attributeName += name.substring(1);
                           return attributeName;
                           }
                           return name;
                           }
                          
                           public Object get(String name) throws Throwable
                           {
                           return getMBeanServer().getAttribute(objectName, getAttributeName(name));
                           }
                          
                           public void set(String name, Object value) throws Throwable
                           {
                           Attribute attribute = new Attribute(getAttributeName(name), value);
                           getMBeanServer().setAttribute(objectName, attribute);
                           }
                          


                          Since we explicitly go over MBeanServer, when accessing MBean's property in MC --> ServiceControllerContext, I see no need to do a switch check.

                          The test - ConfigureMCFromJMXWithPropertyUnitTestCase - was added to the system-jmx tests.
                          It checks access to the MBean's attribute both ways - uppercase and lower case first letter.