-
15. Re: Failed to deserialize object error
ataylor Sep 14, 2009 5:21 AM (in response to rnicholson10)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 Sep 14, 2009 6:26 AM (in response to rnicholson10)@rnicholson10 which version of the application server are you using?
-
17. Re: Failed to deserialize object error
ataylor Sep 14, 2009 7:20 AM (in response to rnicholson10)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 Sep 14, 2009 8:04 AM (in response to 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 Sep 14, 2009 8:30 AM (in response to rnicholson10)"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 Sep 14, 2009 9:20 AM (in response to rnicholson10)@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 Sep 14, 2009 9:23 AM (in response to rnicholson10)@clebert.suconic@jboss.com
The bit you are mis understanding isThis 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 Sep 14, 2009 9:36 AM (in response to rnicholson10)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 Sep 14, 2009 9:50 AM (in response to rnicholson10)"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 Sep 14, 2009 10:20 AM (in response to rnicholson10)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::onMessageSystem.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 Sep 14, 2009 10:26 AM (in response to rnicholson10)"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 Sep 14, 2009 10:38 AM (in response to rnicholson10)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 followingThis 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 Sep 16, 2009 3:34 AM (in response to rnicholson10)Andy - If this is a bug in the JBoss AS EJB3 implementation, can you add a JIRA bug report against the EJB3 project?