3 Replies Latest reply on Aug 10, 2012 11:17 AM by clebert.suconic

    ObjectMessage serialization

    jmesnil

      This week I have been working on a few head-scratching AS7 issues[1] related to ObjectMessage.

       

      Good news is that most of them are due to incorrectly deployed Java EE modules and can be fixed.

      Bad news is that some are due to Java Serialization and ClassLoader issues.

       

      Sometimes ago, Jason wrote a article about Modular serialization that explains how it works in AS7[2].

      This directly relates to ObjectMessage since an Object created inside a modular environment can not be deserialized using standard Java serialization. The modules does not make visible their dependencies. This is a *good thing* in general but gets in the way for deserialization.

       

      It's easy to fix serialization/deserialization inside AS7 by using JBoss marshalling.

      However for HornetQ, we can expect a mix of environment. Normal use cases is to have some clients in a modular environment (eg producer in a Servlet, MDB) and some in a non-modular environment (consumer/producer in a regular Java application relying on the classpath).

      To make things worse, we can not negotiate which serialization to use when sending the message. A same ObjectMessage can be delivered to clients in modular and non-modular environments.

       

      Another thing to take into account is that HornetQ is not aware of JBoss modules. It does not depend on it and does not need to... except for ObjectMessage.

       

      The last 2 days I have played with HornetQ to reach a possible solution.

      I did the dumbest thing that could possibly work. I added dependencies to JBoss marshalling and modules jar to HornetQ. When a ObjectMessage's body is serialized, I serialize it:

      1. a first time using Java serialization

      2. a second time using JBoss marshalling module-aware serialization (if it's in a modular environment)

      Yes, the payload object is serialized twice

       

      When a message is deserialized:

      1. I deserialize it using standard Java serialization (using ObjectInputStreamWithClassLoader)

      2. if that fails (ClassNotFoundException) and I am in a modular environment, I try to deserialize it using JBoss marshalling

       

      This solve the AS7 issue I am facing but not all of them. For example if a message is produced in a non-modular environment, there is no way to have it deserialized in a modular environment whithout tweaking the modules.

       

      It also introduces a dependency from HornetQ to JBoss modules and marshalling. This can be circumvent by having in HornetQ a way to register an additional handler for object message serialization that I could set when it is running inside AS7.

       

      Frankly, this looks ugly and I have a hard time finding a solution which solves all these cases ( I'll think more about it over the week-end as I'd like to find a solution that fixes all the edge cases).

       

      I am really wondering if all this additional complexity is worth for a few edge use cases. Best thing is to educate user and move away from ObjectMessage[3].

       

      wdyt?

       

       

      [1] https://issues.jboss.org/browse/AS7-1271

      [2] https://community.jboss.org/wiki/ModularSerialization

      [3] http://www.jmesnil.net/weblog/2012/07/27/on-jms-objectmessage-and-its-pitfalls/

        • 1. Re: ObjectMessage serialization
          clebert.suconic

          I don't get this. All we need is the user's application to have the class available. it's simple serialization. We use ObjectInputStreamWithClassLoader and that should use the same user's classLoader.

          • 2. Re: ObjectMessage serialization
            jmesnil

            I wish that was that simple.

             

            unfortunately, Java serialization will only work if the same classloader strategy is used by the producer and the consumer.

             

            If a regular Java client uses the classpath to load their classes, Java serialization will serialize its whole graph.

            If another regulat Java clients wants to deserialize the payload, it must have all the classes in the graph available in its classloader too. This is easy, add them to the classpath.

             

            On the other hand, AS7 uses modules instead of classpath to load its classes. AS7 uses a modular classloader which splits the classloader in a hierarchy where some classes are visible (API interface) while some are not (e.g. implementation of an API).

            In that case, we can not deserialize the payload coming from a regular Java client. We don't know which "modules" to use to query the classes.

             

            Does that make sense?

             

            I am trying to find a way to have Obejct serialization to work in all cases

            Java producer => AS7 consumer & Java consumer

            AS7 producer => AS7 consumer & Java consumer

             

            i.e. we can have valid use cases with a mix of Java / modular applications sending and receiving Object messages.

             

            I'm brainstorming it because I don't see a simple way to have it work without changing HornetQ Object serialization to always use JBoss marshalling.

            • 3. Re: ObjectMessage serialization
              clebert.suconic

              unfortunately, Java serialization will only work if the same classloader strategy is used by the producer and the consumer.

              There's no classloading involved on producing. When you write an object, any serialization mechnism will just use plain reflection what means... no classloader at all.

               

              you just get object.getClass().getMethods()... simple like that!

               

               

               

              On the other hand, AS7 uses modules instead of classpath to load its classes. AS7 uses a modular classloader which splits the classloader in a hierarchy where some classes are visible (API interface) while some are not (e.g. implementation of an API).

              In that case, we can not deserialize the payload coming from a regular Java client. We don't know which "modules" to use to query the classes.

               

              Yes.. if you use serialization, you need access to the same classes when you deserialize.

               

               

              I'm brainstorming it because I don't see a simple way to have it work without changing HornetQ Object serialization to always use JBoss marshalling.

               

              All we need is a proper ClassLoading. JBoss Marshalling will have to do reflections in the same way through a classloader.

              If there's a special way to solve classes, we just need to implement it on ObjectInputStreamWithClassLoader. The getClass() would have to use the proper mechanism... I'm surprised the simple classLoader can't do it here.

               

              anyway, if there's anything to be changed it would be ObjectInputStreamWithClassLoader.