1 2 Previous Next 27 Replies Latest reply on Sep 16, 2009 3:34 AM by timfox Go to original post
      • 15. Re: Failed to deserialize object error
        ataylor

        chapter 12.5.6 says there are 2 options as to how this can work, one of these options, option A, *doesnt* call before/afterdelivery. You can see this quite clearly on the sequence diagram figure 12-8.

        we changed this so we can have more control over the transactions.

        • 16. Re: Failed to deserialize object error
          ataylor

          @rnicholson10 which version of the application server are you using?

          • 17. Re: Failed to deserialize object error
            ataylor

            Also i've just runa small test to send an object message and it worked fine for me, can you post your exatc code, MDB settings etc, so i can recreate the issue.

            • 18. Re: Failed to deserialize object error
              rnicholson10

              I'm using JBoss-5.1.0.GA. I have no specific MDB settings, they are all contained in the MDB as annotations.

              I start all MDBs with Delivery disabled. This is then enabled by another bean when it is ready to receive packets.

              Note: If the object you send is a standard JDK 6 object it will work fine. It's only when you use a custom object that you get a serialization error.

              My MDB (contains the workaround given by clebert):

              package com.paddypower.phase.engine.bean.mdb;
              
              import javax.ejb.ActivationConfigProperty;
              import javax.ejb.MessageDriven;
              import javax.jms.JMSException;
              import javax.jms.Message;
              import javax.jms.MessageListener;
              import javax.jms.ObjectMessage;
              
              import org.apache.log4j.Logger;
              
              import com.paddypower.phase.common.internal.BridgeCheck;
              import com.paddypower.phase.common.internal.MultiDestDataPacket;
              import com.paddypower.phase.common.internal.Packet;
              import com.paddypower.phase.engine.core.EngineMultiDestPacketContainer;
              import com.paddypower.phase.engine.core.RoutingEngine;
              
              @MessageDriven(
               activationConfig = {
               @ActivationConfigProperty(
               propertyName="destinationType",
               propertyValue="javax.jms.Queue"),
               @ActivationConfigProperty(
               propertyName="destination",
               propertyValue="queue/phaseQueueFromInput"),
               @ActivationConfigProperty(
               propertyName="DeliveryActive",
               propertyValue="false")
               }
              )
              
              public class EngineMDB implements MessageListener
              {
               private static final Logger log = Logger.getLogger(EngineMDB.class);
              
               public void onMessage(Message message)
               {
               log.debug("GotMessage");
              
               ClassLoader original = Thread.currentThread().getContextClassLoader(); //Workaround
               try //Workaround
               { //Workaround
               Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); //Workaround
               try
               {
               if (message instanceof ObjectMessage)
               {
               ObjectMessage msg = (ObjectMessage) message;
               Object object = msg.getObject(); //<-- Serialization error happens here
              
               if (object instanceof Packet)
               {
               Packet packet = (Packet) ((ObjectMessage) message).getObject();
               //... Process Packet
               }
               else
               {
               log.debug(object);
               }
               }
               else
               {
               log.debug("OTherTypeOfMessage");
               }
               }
               catch (JMSException jmse)
               {
               jmse.printStackTrace();
               }
              
               } //Workaround
               finally //Workaround
               { //Workaround
               Thread.currentThread().setContextClassLoader(original); //Workaround
               } //Workaround
               }
              }
              


              • 19. Re: Failed to deserialize object error
                clebert.suconic

                 

                "ataylor" wrote:
                chapter 12.5.6 says there are 2 options as to how this can work, one of these options, option A, *doesnt* call before/afterdelivery. You can see this quite clearly on the sequence diagram figure 12-8.

                we changed this so we can have more control over the transactions.



                IMHO, That's said in the context of Transactions.

                "There are two delivery options available to the resource adapter for transacted
                deliveries:"


                a little bit further on the same page, you will see:
                " he beforeDelivery and afterDelivery method calls are considered part of
                a single message delivery call. For each message delivery, the beforeDelivery,
                afterDelivery methods and the actual message delivery method, must be
                called from a single thread of control.
                
                Further, the application server must set the thread context class loader to the
                endpoint application class loader during the beforeDelivery call and must
                reset it during a corresponding afterDelivery call. This allows a resource
                adapter to use the application class loader to load application specific classes
                while deserializing, or reconstructing, a message object. Note, setting of the
                thread context class loader during the beforeDelivery call is independent of
                whether an XAResource instance is provided by the resource adapter."



                So, the before and after delivery are not used only for transactions.

                Also, you don't need serialization/objectmessage to create a failing test. All you need is to test if the ClassLoader == this.getClass().getClassLoader();


                I could easily see on the EJB3 code that they will respect this rule. (I posted that code a few messages back on this thread):


                "Application server must set the thread context class loader to the
                endpoint application class loader during the beforeDelivery call and must
                reset it during a corresponding afterDelivery call."

                • 20. Re: Failed to deserialize object error
                  ataylor

                  @rnicholson10 My test did send a custom object and it deserialized it fine. I'm packaging my application as an ear file with a jar inside.

                  • 21. Re: Failed to deserialize object error
                    ataylor

                    @clebert.suconic@jboss.com

                    The bit you are mis understanding is

                    This allows a resource
                    adapter to use the application class loader to load application specific classes
                    while deserializing, or reconstructing, a message object. Note, setting of the
                    thread context class loader during the beforeDelivery call is independent of
                    whether an XAResource instance is provided by the resource adapter.


                    This is there so if the resource adapter (not the application) needs to reconstruct an object where the objects class belongs to the applications classloader. The calls will set the classloader early so we can do what we need to do before delivering the message. Since wer don't need to reconstruct the message we don't have to call it. When the onMessage is called the classloader will still be set correctly for the onMessage method.

                    • 22. Re: Failed to deserialize object error
                      ataylor

                      If i add this line of code to my onMessage method

                      message.getObject().getClass().getClassLoader().toString()


                      it prints

                      BaseClassLoader@178e964{vfszip:/home/andy/devtools/jboss-5.0.1.GA/server/mdb-cmt-tx-required-example-profile/deploy/mdb-cmt-tx-required-example.ear/}

                      which looks like the correct classloader for my app

                      • 23. Re: Failed to deserialize object error
                        ataylor

                         

                        "rnicholson10" wrote:
                        I'm using JBoss-5.1.0.GA. I have no specific MDB settings, they are all contained in the MDB as annotations.

                        I start all MDBs with Delivery disabled. This is then enabled by another bean when it is ready to receive packets.

                        Note: If the object you send is a standard JDK 6 object it will work fine. It's only when you use a custom object that you get a serialization error.

                        My MDB (contains the workaround given by clebert):

                        package com.paddypower.phase.engine.bean.mdb;
                        
                        import javax.ejb.ActivationConfigProperty;
                        import javax.ejb.MessageDriven;
                        import javax.jms.JMSException;
                        import javax.jms.Message;
                        import javax.jms.MessageListener;
                        import javax.jms.ObjectMessage;
                        
                        import org.apache.log4j.Logger;
                        
                        import com.paddypower.phase.common.internal.BridgeCheck;
                        import com.paddypower.phase.common.internal.MultiDestDataPacket;
                        import com.paddypower.phase.common.internal.Packet;
                        import com.paddypower.phase.engine.core.EngineMultiDestPacketContainer;
                        import com.paddypower.phase.engine.core.RoutingEngine;
                        
                        @MessageDriven(
                         activationConfig = {
                         @ActivationConfigProperty(
                         propertyName="destinationType",
                         propertyValue="javax.jms.Queue"),
                         @ActivationConfigProperty(
                         propertyName="destination",
                         propertyValue="queue/phaseQueueFromInput"),
                         @ActivationConfigProperty(
                         propertyName="DeliveryActive",
                         propertyValue="false")
                         }
                        )
                        
                        public class EngineMDB implements MessageListener
                        {
                         private static final Logger log = Logger.getLogger(EngineMDB.class);
                        
                         public void onMessage(Message message)
                         {
                         log.debug("GotMessage");
                        
                         ClassLoader original = Thread.currentThread().getContextClassLoader(); //Workaround
                         try //Workaround
                         { //Workaround
                         Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); //Workaround
                         try
                         {
                         if (message instanceof ObjectMessage)
                         {
                         ObjectMessage msg = (ObjectMessage) message;
                         Object object = msg.getObject(); //<-- Serialization error happens here
                        
                         if (object instanceof Packet)
                         {
                         Packet packet = (Packet) ((ObjectMessage) message).getObject();
                         //... Process Packet
                         }
                         else
                         {
                         log.debug(object);
                         }
                         }
                         else
                         {
                         log.debug("OTherTypeOfMessage");
                         }
                         }
                         catch (JMSException jmse)
                         {
                         jmse.printStackTrace();
                         }
                        
                         } //Workaround
                         finally //Workaround
                         { //Workaround
                         Thread.currentThread().setContextClassLoader(original); //Workaround
                         } //Workaround
                         }
                        }
                        


                        If i use the same activation config as this, i.e. I add deliveryActive, i get an error complaining with

                        @ActivationConfigProperty(
                         propertyName="DeliveryActive",
                         propertyValue="false")
                        


                        I'm not sure how you are deploying this and getting it to work.

                        could you provide me with a full working test so that i can recreate it along with some instructions as to how you run it. you can attach it to the JIRA

                        • 24. Re: Failed to deserialize object error
                          clebert.suconic

                           

                          could you provide me with a full working test so that i can recreate it along with some instructions as to how you run it. you can attach it to the JIRA



                          Add this to any of the MDB examples::onMessage

                          System.out.println("Thread.contextClassLoader() == " + Thread.currentThread().getContextClassLoader() + " while the class " + this.getClass().getClassLoader());
                          
                           if (Thread.currentThread().getContextClassLoader() != this.getClass().getClassLoader())
                           {
                           System.out.println("Wrong classLoader being used!!!!");
                           }
                          
                          



                          If you see the message, "Wrong classLoader being used!!!".. you consider that as a failure.

                          The classLoader I saw above was the RAR classLoader

                          • 25. Re: Failed to deserialize object error
                            clebert.suconic

                             

                            "ataylor" wrote:
                            @clebert.suconic@jboss.com
                            This is there so if the resource adapter (not the application) needs to reconstruct an object where the objects class belongs to the applications classloader. The calls will set the classloader early so we can do what we need to do before delivering the message. Since wer don't need to reconstruct the message we don't have to call it. When the onMessage is called the classloader will still be set correctly for the onMessage method.



                            You need the Thread Context ClassLoader (TCL) to be correct.


                            The TCL is not only used by us. I could list a number of useCases where the user will perform classLoader operations inside the onMessage call. Object Serialization is just one of them.

                            • 26. Re: Failed to deserialize object error
                              ataylor

                               

                              You need the Thread Context ClassLoader (TCL) to be correct.


                              The TCL is not only used by us. I could list a number of useCases where the user will perform classLoader operations inside the onMessage call. Object Serialization is just one of them.


                              That may be true, but my point is that the JCA spec says nothing about doing this in the contract between the ra adapter and the Application Server. only in option B does it specify that we should make calls to beforeDelivery and afterDelivery as a pair as part of the message delivery.

                              Like i said before when it specifies the following

                              This allows a resource
                              adapter to use the application class loader to load application specific classes
                              while deserializing, or reconstructing, a message object


                              This is so we, the resource adapter, can load application classes if we need to to help in constructing the message. But since we don't need to we shouldn't need to call it.

                              • 27. Re: Failed to deserialize object error
                                timfox

                                Andy - If this is a bug in the JBoss AS EJB3 implementation, can you add a JIRA bug report against the EJB3 project?

                                1 2 Previous Next