Announcement Announcement Module
Collapse
No announcement yet.
Serialization error using <lookup-method> Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Serialization error using <lookup-method>

    I have just downloaded the release version of Spring 1.1. I am using lookup method injection using <lookup-method name="..." bean="..." />. It works perfectly.

    However I receive a "Not serializable: net.sf.cglib.proxy.NoOp$1" error with the bean containing the injected method. As a result my HttpSessions won't serialize.

    Is there a workaround?

    I see that the Dynaop project https://dynaop.dev.java.net can make CGLIB proxies serializable - see its dynaop.CglibProxyHandle class. Is there any way to apply this method using Spring?

    Thanks in advance for any suggestions or comments.

  • #2
    This is an oversight. I'll fix it promptly.

    Btw Spring AOP proxies in 1.1 are serializable so long as the relevant application code is serializable. The lookup method functionality uses CGLIB directly, rather than going through Spring's AOP framework.

    Comment


    • #3
      I've been looking into this more deeply and it's not actually so straightforward. The purpose of lookup method functionality is to enable the overridden method to return a prototype bean from the owning BeanFactory. This implies that the subclass created by the factory holds a reference to the factory. Serializing that subclass would imply serializing the whole factory (and the beans and bean definitions it contains), or a sophisticated tracking of the dependencies required to create the target prototype, which could still result in serializing a large number of beans.

      With respect to MethodReplace overrides, rather than lookups, if the MethodReplacer implementation is a singleton and isn't BeanFactoryAware, ApplicationContextAware or the like, serialization might be possible with implementation changes.

      But the lookup method functionality is of course most commonly used.

      Thus I'm not sure that serializability is really feasible. So I'd like to open this to discussion, rather than leap in and change code at this point...

      Comment


      • #4
        Originally posted by Rod Johnson
        Thus I'm not sure that serializability is really feasible. So I'd like to open this to discussion, rather than leap in and change code at this point...
        My vote is for serializability:
        1. The Java convention is that containers/collections should be serializable e.g. HashMap, although their contents may not be serializable.
        2. Not enabling serializability will reduce functionality in applications using serializable beans, whereas enabling it will provide increased functionality whenever the application's beans are serializable.
        3. I am currently using a bean which holds a reference to my BeanFactory - the bean is serializable because all my beans are serializable. When I eliminate the BeanFactory reference using lookup method injection, my bean is no longer serializable.

        Comment


        • #5
          3. I am currently using a bean which holds a reference to my BeanFactory - the bean is serializable because all my beans are serializable. When I eliminate the BeanFactory reference using lookup method injection, my bean is no longer serializable.
          I didn't think Spring offered any BeanFactory that was Serializable? I know at least that DefaultListableBeanFactory and XmlBeanFactory are not serializable. And even if any BeanFactory was, would it really make sense to serialize it and create a new BeanFactory when you unserialize your bean?

          Comment


          • #6
            Originally posted by gpoirier
            I didn't think Spring offered any BeanFactory that was Serializable?
            My mistake - you are correct - I did not receive serialization errors because my bean inadvertently held a static reference to a BeanFactory.

            It is now clear to me that any bean holding a direct or indirect reference to a BeanFactory cannot be serializable, unless the reference is static. This is probably not a serious problem, as all instances of the bean are likely to be created by the same BeanFactory. However it is at odds with the "statics are bad" philosophy.

            Unfortunately <lookup-method> does not seem to work for static methods, so the only way I can think of to achieve serializability is to use a static reference to a BeanFactory. Is there any other way?

            Thanks in advance.

            Comment


            • #7
              The Java convention is that containers/collections should be serializable e.g. HashMap, although their contents may not be serializable.
              True, but a Spring factory is a rather different animal. Something that resulted in serializing a whole factory could be dangerous, as people could innocently put huge amounts of state into an HTTP Session or throw it over the wire. Unlike a typical list, the chances are very high that a Spring factory will contain objects that are not likely to be serializable, like DataSources.

              One option might be the make the bean serializable but have the lookup methods throw an UnsupportedOperationException on deserialization. It would be better to be able to connect to a new factory, but how would that factory be located? Perhaps the lookup methods could throw UnsupportedOperationException until a method such as attachToFactory(BeanFactory bf) was invoked on deserialization. Would that address your requirement?

              Comment


              • #8
                Originally posted by Rod Johnson
                One option might be the make the bean serializable but have the lookup methods throw an UnsupportedOperationException on deserialization. It would be better to be able to connect to a new factory, but how would that factory be located? Perhaps the lookup methods could throw UnsupportedOperationException until a method such as attachToFactory(BeanFactory bf) was invoked on deserialization. Would that address your requirement?
                Serializability is desirable for beans stored in HttpSessions. That is why I am trying to make my beans Serializable.

                If you are suggesting that an attachToFactory(BeanFactory bf) method could be called in a custom readObject() method, that would work. However I am unclear on how the new BeanFactory would be located.

                Comment


                • #9
                  I think Serialization is important...

                  Instead of serializing the entire factory and all the bean definitions, can we somehow just serialize what is required for deserialize. Maybe even just allow serailization of the file name that stores the bean definition.

                  Just a thought.

                  Comment


                  • #10
                    Maybe even just allow serailization of the file name that stores the bean definition
                    A clever idea, but you'd end up with n instances of the factory if you serialized and deserialized n beans. This might well not be what you want.

                    Still, good to have some suggestions coming in...

                    Comment


                    • #11
                      How about using a Singleton context factory on deserialization (based on the serialized file name)....so if there are n beans that are deserialized they will be able to share the same sigleton context factory.

                      Also, how about forcing the use of Singleton context factory for the entire app if you need lookup-method and Serialization capability.

                      Comment


                      • #12
                        ram_2000,

                        Using filename isn't really a good option, because ApplicationContext can be created from multiple files, or from another source than a file. And concering a single singleton context, in most case there won't be a single context, but a context hiearchy, e.g. business logic in one context, web MVC in another.


                        Rod & MHarhen,

                        I was thinking of how to make those beans Serializable, and realised it's much more complicated than just how to find the BeanFactory when you unserialize. If it was just the BeanFactory, it could easily be handled by providing a static method, serializating its definition and finding the BeanFactory with that.

                        However, it's not that simple, the CGLIB enhanced class might not have the same name in a different JVM instance (or even not exist at all). My first thought to get around that, is to use writeReplace and serlialize a data holder, and then the data holder would define a readResolve to re-create the enhanced class and instance. But I see two problems with that.

                        First, every instance of the same prototype of the factory should probably be of the same class. That means when you unserialize, you cannot just create the CGLIB enhanced class, you have to somehow locate it and reuse the existing class (or at least make the CGLIB cache kick in). It's possible to do that, but I'm not sure how simple that could be...

                        The other issue I see is how to create the instance. The original instance might not have a no-args constructor, and even if it has one, conventional serialization would skip any constructor for classes that implements Serializable. I don't think it's possible to do that, unless using sun.* classes, changing the accessibly of package-private methods or manually generating bytecode (i.e. not from javac). A possible workaround for this one could be to ask for a new prototype from the newly found BeanFactory and then set the the private fields on the protype, but that wouldn't be any better than Hessian/Burlap serilization, it wouldn't handle very well custom serialization of the super class. Basically, to re-create the instance, you would have to somehow emulate/recreate the whole serialization. Again, should be possible, but sound like a pain.

                        The second problem is pretty similar to the problem with the JDK's proxy serialization. However, they solved it by introducing a custom serialization for the java.lang.reflect.Proxy instances. Doing that with CBLIG might work, but that would invole subclassing ObjectOutputStream/ObjectInputStream, which is hardly and option when it's not you that creates those instance, such as when the container serialize the HttpSession content.

                        By the way, the problem is similar when serializing any CGLIB classes, it doesn't work either for CGLIB AOP proxies. They will get serialized without any problem, but it will fail if you ever try to unserialize in a different JVM, unless CGLIB gave them the same name. It's possible that they happen to have the same name in both JVM instances, but highly unreliable.


                        Basically, I am not sure serializing CGLIB enhanced classes is a very good idea. However, I personally cannot really see any use case where I would want to have a Spring-managed bean in the HttpSession. In what situation would you want to do that?

                        Guillaume

                        Comment


                        • #13
                          Originally posted by gpoirier
                          Basically, I am not sure serializing CGLIB enhanced classes is a very good idea. However, I personally cannot really see any use case where I would want to have a Spring-managed bean in the HttpSession. In what situation would you want to do that?
                          In my HttpSession, I have a prototype bean "userBean" containing information about the currently logged on user. This bean has a reference to a singleton bean "userManager" which manages all user beans.

                          "userManager" uses <lookup-method> to create a new instance of "userBean" whenever a new user logs in. As "userManager" cannot be serializable because it is proxied using CGLIB, it follows that "userBean" cannot be serializable.

                          After much refactoring, I was able to remove the reference to "userManager" from "userBean", allowing it to be Serializable.

                          I have solved my current problem, but I still believe that using <lookup-method> on a bean in a HttpSession is a useful capability.

                          Thanks for all the help - I now have a better design.

                          Comment


                          • #14
                            I have not worked yet with lookup-method. However, as far as I understand, the idea is, to receive prototypes from the owning factory.
                            So instead of holding a reference to that factory, I propose a different approach:

                            One prototype is received from the factory and stored in the enhanced subclass. From this instance other instances could be created using serialization (if the prototype itself is serializable, of course). The factory itself is then not necessary anymore.

                            One issue I see, is to determine the singleton flag of the looked-up bean. Normally it should be expected to be a non-singleton, however this is not enforced. So that case should be handled.

                            Regards,
                            Andreas

                            Comment


                            • #15
                              Rod,

                              I am not able to understand this statement you made in this post :-
                              'This implies that the subclass created by the factory holds a reference to the factory. Serializing that subclass would imply serializing the whole factory (and the beans and bean definitions it contains), or a sophisticated tracking of the dependencies required to create the target prototype, which could still result in serializing a large number of beans.'

                              Well, would the bean also have a reference to the container to which it belongs to ? My understanding was that the container would know the beans its contains.

                              Due to this i am not very clear as to why would we need the entire BeanFactory to be Serializable?

                              Also, while using AOP, a proxy of the bean is created , which is nothing but subclassing the beans but yet these beans are serializable.

                              Can you please help me understand this concept?

                              -Hetal

                              Comment

                              Working...
                              X