Beehive
  1. Beehive
  2. BEEHIVE-804

Cannot invoke JMS System Control from JWS

    Details

    • Type: Bug Bug
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: v1m1
    • Fix Version/s: 1.0.1
    • Component/s: Web Services (181)
    • Labels:
      None
    • Environment:
      Windows XP

      Description

      When attempting to create and use a JMSControl within a JWS, the first invocation of the control interface results in the following exception:

      A reproduction webapp example will be attached.

      java.lang.NullPointerException
      at ws.JmsContainer.broken(JmsContainer.java:20)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:585)
      at org.apache.axis.providers.java.RPCProvider.invokeMethod(RPCProvider.java:388)
      at org.apache.axis.providers.java.RPCProvider.processMessage(RPCProvider.java:283)
      at org.apache.axis.providers.java.JavaProvider.invoke(JavaProvider.java:319)
      at org.apache.axis.strategies.InvocationStrategy.visit(InvocationStrategy.java:32)
      at org.apache.axis.SimpleChain.doVisiting(SimpleChain.java:118)
      at org.apache.axis.SimpleChain.invoke(SimpleChain.java:83)
      at org.apache.axis.handlers.soap.SOAPService.invoke(SOAPService.java:453)
      at org.apache.axis.server.AxisServer.invoke(AxisServer.java:281)
      at org.apache.axis.transport.http.QSMethodHandler.invokeEndpointFromGet(QSMethodHandler.java:130)
      at org.apache.axis.transport.http.QSMethodHandler.invoke(QSMethodHandler.java:95)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:585)
      at org.apache.axis.transport.http.AxisServlet.processQuery(AxisServlet.java:1217)
      at org.apache.axis.transport.http.AxisServlet.doGet(AxisServlet.java:249)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:697)
      at org.apache.axis.transport.http.AxisServletBase.service(AxisServletBase.java:327)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:810)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:237)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:157)
      at org.apache.beehive.controls.runtime.servlet.ControlFilter.doFilter(ControlFilter.java:131)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:186)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:157)
      at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:214)
      at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
      at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
      at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:198)
      at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:152)
      at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
      at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
      at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:137)
      at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
      at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117)
      at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:102)
      at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
      at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
      at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
      at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
      at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:929)
      at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:160)
      at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:793)
      at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:702)
      at org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:571)
      at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:644)
      at java.lang.Thread.run(Thread.java:595)

      1. jmsControlError.zip
        6.01 MB
        Julian Klappenbach
      2. BEEHIVE-804.diff
        29 kB
        Chad Schoettger
      3. BEEHIVE-804.zip
        7 kB
        Chad Schoettger

        Issue Links

          Activity

          Hide
          Julian Klappenbach added a comment -

          Modification to build.xml in WEB-INF will be necessary to map to your particular environment. These will be clearly indicated with comments.

          Show
          Julian Klappenbach added a comment - Modification to build.xml in WEB-INF will be necessary to map to your particular environment. These will be clearly indicated with comments.
          Hide
          daryoush mehrtash added a comment -

          This is not a JWS bug. The problem is that

          @Control private JmsQueueCtrl m_ctrlQueue;

          is private, it must be a public variable. If you change this to:

          @Control public JmsQueueCtrl m_ctrlQueue;

          it should work..

          I think this bug should be move to Controls as a new feature request. This is a common mistake (I know I have made this mistake) and there are no error messages to warn you of it. It would be nice if the control's APT process could detect this as an error or at least warning.

          Julian, Please verify that this fix would work for you and move it reassign it as new feature for Controls

          Show
          daryoush mehrtash added a comment - This is not a JWS bug. The problem is that @Control private JmsQueueCtrl m_ctrlQueue; is private, it must be a public variable. If you change this to: @Control public JmsQueueCtrl m_ctrlQueue; it should work.. I think this bug should be move to Controls as a new feature request. This is a common mistake (I know I have made this mistake) and there are no error messages to warn you of it. It would be nice if the control's APT process could detect this as an error or at least warning. Julian, Please verify that this fix would work for you and move it reassign it as new feature for Controls
          Hide
          Eddie O'Neil added a comment -

          Actually, I don't think that this is a Controls issue – initialization of a control field is owned by the control container (one for a JWS in this case) and as such, it's the responsibility of this container to provide initialization code to instantiate control fields. In JPF, for example, controls are initialized regardless of their level of protection (public / protected / private). I'd expect that JWS would do the same.

          In the end, this seems like a bug (enhancement?) in WSM's ControlProvider as it needs to ensure that fields marked with @Control are initialized correctly even if they are private.

          Certainly wouldn't want to require that classes declare controls as public fields.

          Show
          Eddie O'Neil added a comment - Actually, I don't think that this is a Controls issue – initialization of a control field is owned by the control container (one for a JWS in this case) and as such, it's the responsibility of this container to provide initialization code to instantiate control fields. In JPF, for example, controls are initialized regardless of their level of protection (public / protected / private). I'd expect that JWS would do the same. In the end, this seems like a bug (enhancement?) in WSM's ControlProvider as it needs to ensure that fields marked with @Control are initialized correctly even if they are private. Certainly wouldn't want to require that classes declare controls as public fields.
          Hide
          daryoush mehrtash added a comment -

          The documentation on the wiki seems to suggest that the control has to be public.

          http://incubator.apache.org/beehive/controls/controlsProgramming.html#instantiating

          This is no longer the requirement?

          Show
          daryoush mehrtash added a comment - The documentation on the wiki seems to suggest that the control has to be public. http://incubator.apache.org/beehive/controls/controlsProgramming.html#instantiating This is no longer the requirement?
          Hide
          Rich Feit added a comment -

          Hey Daryoush, I don't see where it says that a control field has to be public (although there's an example of a public control field in there). Am I missing something?

          Show
          Rich Feit added a comment - Hey Daryoush, I don't see where it says that a control field has to be public (although there's an example of a public control field in there). Am I missing something?
          Hide
          Kyle Marvin added a comment -

          The code-generated ClientInitializer should have code that can deal with both public and private fields on the target container instance. In the private case, it uses java.lang.reflect.Field.setAccessible() , followed by Field.set() to assign the value. This should work, unless the JVM security policy is configured to disable the setAccessible (not the default behavior).

          So the basic rule is: if the container will be used in an environment where setAccessible is disabled by security policy, then @Control fields must be public, otherwise, they may be private.

          Is the JWS container using the generated control client initializer? I know that late in m1 we ran into an issue where pageflow container was not, and was doing manual initialization directly....

          Show
          Kyle Marvin added a comment - The code-generated ClientInitializer should have code that can deal with both public and private fields on the target container instance. In the private case, it uses java.lang.reflect.Field.setAccessible() , followed by Field.set() to assign the value. This should work, unless the JVM security policy is configured to disable the setAccessible (not the default behavior). So the basic rule is: if the container will be used in an environment where setAccessible is disabled by security policy, then @Control fields must be public, otherwise, they may be private. Is the JWS container using the generated control client initializer? I know that late in m1 we ran into an issue where pageflow container was not, and was doing manual initialization directly....
          Hide
          daryoush mehrtash added a comment -

          Rich,

          You are right I haven't seen an explicit statement that the controls has to be public, but AFAIK every example that I have seen does put the control in public field. I think we need to be clear on this with the caveats that Kyle pointed out.

          Here is how we initialize the control:

          for(Field field : clientClass.getFields()) {
          if(null != field.getAnnotation(Control.class))

          { /* attempt to instantiate a Beehive control using the control's ClientInitializer */ ControlContainerContext ccc = ControlThreadContext.getContext(); if(null == ccc) throw new Exception("no control container context found"); Class clientInitializer = clientClass.getClassLoader().loadClass(clientClass.getName() + "ClientInitializer"); Method init = clientInitializer.getMethod("initialize", ControlBeanContext.class, clientClass); /* note, the first parameter here is null because the ClientInitializer.initialize(...) method is static */ init.invoke(null, ccc, client); }

          }
          The problem is that If the control field is private it would not have any fields in the getFields(), so the loop never runs.

          I am not sure how netui does the initialization, but it seems that we should not really care about the field, all we should check is if there is "clientClass.getName() + "ClientInitializer"" class on the class path., if there is, then try to run it. Would that work?

          Any other suggestions?

          Show
          daryoush mehrtash added a comment - Rich, You are right I haven't seen an explicit statement that the controls has to be public, but AFAIK every example that I have seen does put the control in public field. I think we need to be clear on this with the caveats that Kyle pointed out. Here is how we initialize the control: for(Field field : clientClass.getFields()) { if(null != field.getAnnotation(Control.class)) { /* attempt to instantiate a Beehive control using the control's ClientInitializer */ ControlContainerContext ccc = ControlThreadContext.getContext(); if(null == ccc) throw new Exception("no control container context found"); Class clientInitializer = clientClass.getClassLoader().loadClass(clientClass.getName() + "ClientInitializer"); Method init = clientInitializer.getMethod("initialize", ControlBeanContext.class, clientClass); /* note, the first parameter here is null because the ClientInitializer.initialize(...) method is static */ init.invoke(null, ccc, client); } } The problem is that If the control field is private it would not have any fields in the getFields(), so the loop never runs. I am not sure how netui does the initialization, but it seems that we should not really care about the field, all we should check is if there is "clientClass.getName() + "ClientInitializer"" class on the class path., if there is, then try to run it. Would that work? Any other suggestions?
          Hide
          Rich Feit added a comment -

          I think you can actually use the same type of call we use when initializing controls in a page flow:

          Controls.initializeClient(null, controlClient, beanContext);

          This should initialize all the controls. Would that work for you?

          Show
          Rich Feit added a comment - I think you can actually use the same type of call we use when initializing controls in a page flow: Controls.initializeClient(null, controlClient, beanContext); This should initialize all the controls. Would that work for you?
          Hide
          Julian Klappenbach added a comment -

          After changing access on the control to public, the code worked as advertised.

          Show
          Julian Klappenbach added a comment - After changing access on the control to public, the code worked as advertised.
          Hide
          Zach Smith added a comment -

          eddie, can you take a look at this or pass it to somebody who can?

          Show
          Zach Smith added a comment - eddie, can you take a look at this or pass it to somebody who can?
          Hide
          Chad Schoettger added a comment -

          Fix for this bug, new additions include a WsmBeanContext and enhanced support for controls lifecycle based on the AXIS scope of the webservice (request, session, application or factory).

          Patch instructions:
          1) Apply patch file
          2) Add new files contained in the attached zip file.

          Show
          Chad Schoettger added a comment - Fix for this bug, new additions include a WsmBeanContext and enhanced support for controls lifecycle based on the AXIS scope of the webservice (request, session, application or factory). Patch instructions: 1) Apply patch file 2) Add new files contained in the attached zip file.
          Hide
          Chad Schoettger added a comment -

          This is a duplicate of BEEHIVE-804. This bug should still be verified seperatly as fixed once the patch for 804 has been commited due to runtime environment differences.

          Show
          Chad Schoettger added a comment - This is a duplicate of BEEHIVE-804 . This bug should still be verified seperatly as fixed once the patch for 804 has been commited due to runtime environment differences.
          Hide
          Eddie O'Neil added a comment -

          This patch was committed in SVN 293057 and SVN 292603. The change adds a ControlContainerContext subclass for WSM. It's still require and needs to be optional, but at least controls work in web services now.

          And, there's an automated test that verifies that this works.

          Show
          Eddie O'Neil added a comment - This patch was committed in SVN 293057 and SVN 292603. The change adds a ControlContainerContext subclass for WSM. It's still require and needs to be optional, but at least controls work in web services now. And, there's an automated test that verifies that this works.

            People

            • Assignee:
              Eddie O'Neil
              Reporter:
              Julian Klappenbach
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development