12 Replies Latest reply on Sep 2, 2008 6:58 AM by Manik Surtani

    ClassNotFoundException during deserialisation

    Marco Brade Newbie

      Hello

      I want to use JBoss Cache to store the result of different method calls (The objects are all serializable). The results and the method-data (method-name, class-name, parameters) are stored in a database.

      I created some test classes with test methods which return different objects. The storage in the memory cache works fine.

      When I activate the jdbc config it writes the method data into the database. But when I try to write the result into the same node but under a different key. I get the following Exception:


      org.jboss.cache.CacheException: java.lang.Exception: Unable to load to deserialize result:
      at org.jboss.cache.interceptors.InterceptorChain.invoke(InterceptorChain.java:227)
      at org.jboss.cache.invocation.CacheInvocationDelegate.getNode(CacheInvocationDelegate.java:420)
      at org.jboss.cache.invocation.CacheInvocationDelegate.getNode(CacheInvocationDelegate.java:61)
      at de.arvatomobile.bdev.cache.methodresultcacher.MethodResultCacheManager.executeMethod(MethodResultCacheManager.java:104)
      at de.arvatomobile.bdev.cache.methodresultcacher.TestLock.main(TestLock.java:60)
      Caused by: java.lang.Exception: Unable to load to deserialize result:
      at org.jboss.cache.loader.AdjListJDBCCacheLoader.loadNode(AdjListJDBCCacheLoader.java:378)
      at org.jboss.cache.loader.AdjListJDBCCacheLoader.exists(AdjListJDBCCacheLoader.java:292)
      at org.jboss.cache.interceptors.CacheLoaderInterceptor.loadIfNeeded(CacheLoaderInterceptor.java:296)
      at org.jboss.cache.interceptors.CacheLoaderInterceptor.visitGetNodeCommand(CacheLoaderInterceptor.java:147)
      at org.jboss.cache.commands.read.GetNodeCommand.acceptVisitor(GetNodeCommand.java:42)
      at org.jboss.cache.interceptors.base.CommandInterceptor.invokeNextInterceptor(CommandInterceptor.java:142)
      at org.jboss.cache.interceptors.PessimisticLockInterceptor.handleGetNodeCommand(PessimisticLockInterceptor.java:317)
      at org.jboss.cache.interceptors.base.PostProcessingCommandInterceptor.visitGetNodeCommand(PostProcessingCommandInterceptor.java:257)
      at org.jboss.cache.commands.read.GetNodeCommand.acceptVisitor(GetNodeCommand.java:42)
      at org.jboss.cache.interceptors.base.CommandInterceptor.invokeNextInterceptor(CommandInterceptor.java:142)
      at org.jboss.cache.interceptors.base.CommandInterceptor.handleDefault(CommandInterceptor.java:157)
      at org.jboss.cache.commands.AbstractVisitor.visitGetNodeCommand(AbstractVisitor.java:94)
      at org.jboss.cache.commands.read.GetNodeCommand.acceptVisitor(GetNodeCommand.java:42)
      at org.jboss.cache.interceptors.base.CommandInterceptor.invokeNextInterceptor(CommandInterceptor.java:142)
      at org.jboss.cache.interceptors.MarshalledValueInterceptor.visitGetNodeCommand(MarshalledValueInterceptor.java:93)
      at org.jboss.cache.commands.read.GetNodeCommand.acceptVisitor(GetNodeCommand.java:42)
      at org.jboss.cache.interceptors.base.CommandInterceptor.invokeNextInterceptor(CommandInterceptor.java:142)
      at org.jboss.cache.interceptors.TxInterceptor.attachGtxAndPassUpChain(TxInterceptor.java:266)
      at org.jboss.cache.interceptors.TxInterceptor.handleDefault(TxInterceptor.java:253)
      at org.jboss.cache.commands.AbstractVisitor.visitGetNodeCommand(AbstractVisitor.java:94)
      at org.jboss.cache.commands.read.GetNodeCommand.acceptVisitor(GetNodeCommand.java:42)
      at org.jboss.cache.interceptors.base.CommandInterceptor.invokeNextInterceptor(CommandInterceptor.java:142)
      at org.jboss.cache.interceptors.base.CommandInterceptor.handleDefault(CommandInterceptor.java:157)
      at org.jboss.cache.commands.AbstractVisitor.visitGetNodeCommand(AbstractVisitor.java:94)
      at org.jboss.cache.commands.read.GetNodeCommand.acceptVisitor(GetNodeCommand.java:42)
      at org.jboss.cache.interceptors.base.CommandInterceptor.invokeNextInterceptor(CommandInterceptor.java:142)
      at org.jboss.cache.interceptors.InvocationContextInterceptor.handleAll(InvocationContextInterceptor.java:155)
      at org.jboss.cache.interceptors.InvocationContextInterceptor.handleDefault(InvocationContextInterceptor.java:108)
      at org.jboss.cache.commands.AbstractVisitor.visitGetNodeCommand(AbstractVisitor.java:94)
      at org.jboss.cache.commands.read.GetNodeCommand.acceptVisitor(GetNodeCommand.java:42)
      at org.jboss.cache.interceptors.InterceptorChain.invoke(InterceptorChain.java:215)
      ... 4 more
      Caused by: java.lang.ClassNotFoundException: int
      at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
      at java.security.AccessController.doPrivileged(Native Method)
      at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
      at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
      at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:276)
      at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
      at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
      at java.lang.Class.forName0(Native Method)
      at java.lang.Class.forName(Class.java:247)
      at org.jboss.util.stream.MarshalledValueInputStream.resolveClass(MarshalledValueInputStream.java:60)
      at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1575)
      at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1496)
      at java.io.ObjectInputStream.readClass(ObjectInputStream.java:1462)
      at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1312)
      at java.io.ObjectInputStream.readArray(ObjectInputStream.java:1667)
      at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1323)
      at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1945)
      at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1869)
      at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1753)
      at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
      at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
      at org.jboss.cache.marshall.CacheMarshaller200.unmarshallObject(CacheMarshaller200.java:549)
      at org.jboss.cache.marshall.CacheMarshaller200.populateFromStream(CacheMarshaller200.java:759)
      at org.jboss.cache.marshall.CacheMarshaller200.unmarshallHashMap(CacheMarshaller200.java:722)
      at org.jboss.cache.marshall.CacheMarshaller200.unmarshallObject(CacheMarshaller200.java:580)
      at org.jboss.cache.marshall.CacheMarshaller200.unmarshallObject(CacheMarshaller200.java:512)
      at org.jboss.cache.marshall.CacheMarshaller200.objectFromObjectStream(CacheMarshaller200.java:140)
      at org.jboss.cache.marshall.VersionAwareMarshaller.objectFromByteBuffer(VersionAwareMarshaller.java:189)
      at org.jboss.cache.marshall.VersionAwareMarshaller.objectFromStream(VersionAwareMarshaller.java:223)
      at org.jboss.cache.loader.AdjListJDBCCacheLoader.unmarshall(AdjListJDBCCacheLoader.java:681)
      at org.jboss.cache.loader.AdjListJDBCCacheLoader.loadNode(AdjListJDBCCacheLoader.java:374)
      ... 34 more


      I found out that there is an old bug in suns ObjectInputStream.
      http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4171142

      Is there something I can configure to fix that? Or do I have to write my own Marshaller?

      Thanks

      Marco


        • 1. Re: ClassNotFoundException during deserialisation
          Marco Brade Newbie

          Hello

          I looked at the source code and it is impossible to fix that bug by writing your own Marshaller.
          The VersionAwareMarshaller is used to work as a wrapper for the Marshaller you define. It creates a org.jboss.util.stream.MarshalledValueInputStream as an replacement for Suns java.io.ObjectInputStream. The MarshalledValueInputStream seems to have problems with primitives. It should take aware of a ClassNotFoundException and check if the name of the class which should be returned matches one of the primitives.

          I looked at the source code of ObjectInputStream of the JDK 1.6 and noticed that they have fixed the bug.

          I could fix this bug by writting and setting my own classloader in the DefaultCacheFactory.

          DefaultCacheFactory factory = new DefaultCacheFactory();
          factory.setDefaultClassLoader( new MyClassLoader() );
          ...
          import java.util.HashMap;
          
          
          @SuppressWarnings("unchecked")
          public class MyClassLoader extends ClassLoader {
           //from java.io.ObjectInputStream
           private static final HashMap primClasses = new HashMap();
           static {
           primClasses.put("boolean", boolean.class);
           primClasses.put("byte", byte.class);
           primClasses.put("char", char.class);
           primClasses.put("short", short.class);
           primClasses.put("int", int.class);
           primClasses.put("long", long.class);
           primClasses.put("float", float.class);
           primClasses.put("double", double.class);
           primClasses.put("void", void.class);
           }
          
          
           public MyClassLoader() {
           super(PrimitiveAwareClassLoader.class.getClassLoader());
           }
          
           public MyClassLoader(ClassLoader parent) {
           super(parent);
           }
          
           @Override
           public Class<?> loadClass(String name) throws ClassNotFoundException {
           try{
           return super.loadClass(name);
           }catch(ClassNotFoundException cnfe){
           Class cl = (Class) primClasses.get(name);
           if (cl != null) {
           return cl;
           } else {
           throw cnfe;
           }
           }
           }
          
          }
          


          Maybee I can help someone with that.



          • 2. Re: ClassNotFoundException during deserialisation
            Marco Brade Newbie

            The failure is still present. And my try to fix it with setting the classloader doesn't work in all cases. In some cases the currentThread - ClassLoader is used instead of the default one.

            To fix that I wrote my own Marshaller that wraps the incoming ObjectInputStream to take aware of the ClassNotFoundException but the VersionAwareMarshaller does not use the set Marshaller. The Method getMarshaller always creates a new CacheMarshaller200 if you have set a custome one.

            I don't think this is the way configuration should work.

            I'm stuck now.

            • 3. Re: ClassNotFoundException during deserialisation
              Manik Surtani Master

              Did you set useRegionBasedMarshalling to true? Set that to true, and then get the region for which your nodes are in and register your class loader onto the region. That will ensure your class loader being used.

              Weird that you still see the bug though, the MarshalledValueInputStream delegates to an ObjectInputStream. :/

              Do you have a repeatable unit test that recreates this, perhaps it is something I can add to our testsuite and make sure we have a proper solution for it.

              • 4. Re: ClassNotFoundException during deserialisation
                Marco Brade Newbie

                I'm affraid I don't have a real JUnit Test for that. But it's easy to reproduce you need an DB or an File Cache to serialize and persist the objects.

                Now you can store for example Integer.TYPE or another primitive. Evict it from memory and load it again. You should get an Exception during deserialisation. (In my case I try to store a methodname, classname and an array of classes. All together in one class)
                The Test should run with Suns defaultClassloader. If you have a special one (Maybee one from an ApplicationServer) this bug might be fixed by using another Classloader.

                Btw: If you compare MarshalledValueInputStream and ObjectInputStream would see that the MarshalledValueInputStream overrides resolveClass:

                 protected Class resolveClass(ObjectStreamClass v)
                 throws IOException, ClassNotFoundException
                 {
                 ClassLoader loader = Thread.currentThread().getContextClassLoader();
                 String className = v.getName();
                 return loader.loadClass(className);
                 }
                


                Thats the code from ObjectInputStream in Java 1.6:
                ...
                 private static final HashMap primClasses = new HashMap(8, 1.0F);
                 static {
                 primClasses.put("boolean", boolean.class);
                 primClasses.put("byte", byte.class);
                 primClasses.put("char", char.class);
                 primClasses.put("short", short.class);
                 primClasses.put("int", int.class);
                 primClasses.put("long", long.class);
                 primClasses.put("float", float.class);
                 primClasses.put("double", double.class);
                 primClasses.put("void", void.class);
                 }
                ...
                
                 protected Class<?> resolveClass(ObjectStreamClass desc)
                 throws IOException, ClassNotFoundException
                 {
                 String name = desc.getName();
                 try {
                 return Class.forName(name, false, latestUserDefinedLoader());
                 } catch (ClassNotFoundException ex) {
                 Class cl = (Class) primClasses.get(name);
                 if (cl != null) {
                 return cl;
                 } else {
                 throw ex;
                 }
                 }
                 }
                


                The fallback for the primitives is missing. loadClass() doesn't work for primitives with Suns Classloaders.

                I'll try to set the classloader on the region and give feedback but as you can see the ContextClassLoader of the current Thread is used (I hope it's always the right one).



                • 5. Re: ClassNotFoundException during deserialisation
                  Marco Brade Newbie

                  ok it's working if I set useRegionBasedMarshalling or useLazyDeserialization to true (or both). What is the prefered way to use? I noticed that useRegionBasedMarshalling is deprecated.

                  • 6. Re: ClassNotFoundException during deserialisation
                    Manik Surtani Master

                    The plan was to replace region based marshalling with lazy deserialization. Try that as well, if that works for you then that is preferred.

                    • 7. Re: ClassNotFoundException during deserialisation
                      Manik Surtani Master

                      What version of JBoss Cache are you using? More specifically, what version of jboss-common-core do you have, since this is where MarshalledValue streams come from.

                      • 9. Re: ClassNotFoundException during deserialisation
                        Marco Brade Newbie

                        I forget the version of the cache.
                        Its: jbosscache-core-2.2.0.CR7

                        • 10. Re: ClassNotFoundException during deserialisation
                          Manik Surtani Master

                          Have you tried the latest version of common-core - 2.2.7? You can grab it from http://repository.jboss.org/maven2/org/jboss/jboss-common-core/2.2.7.GA/.

                          Let me know how it goes, this is a fix that should be in common-core.

                          • 11. Re: ClassNotFoundException during deserialisation
                            Manik Surtani Master

                            Ok, this is still a bug in common-core 2.2.7. I've reported (and fixed) this in JBCOMMON-64.

                            I've also created a note to upgrade to common-core 2.2.8 when it is released before I cut 2.2.0.GA. See JBCACHE-1403.

                            Thanks for spotting this.

                            • 12. Re: ClassNotFoundException during deserialisation
                              Manik Surtani Master

                              2.2.0.GA is released, this is fixed there.