4 Replies Latest reply on Feb 14, 2014 4:18 PM by Pawel Veselov

    ClassLoaders and "storeAsBinary" caches (5.3)

    Pawel Veselov Newbie



      I'm trying to understand which class loaders, and when, are used for caches marked with "storeAsBinary", specifically in cases when they are also configured as "defensive".


      (1) One question I have - is how is the classloader for deserialization selected? I'm trying to follow MarshalledValue.deserialize0() method, but it's a bit murky for me yet. RiverUnmarshaller is being used, and it's serializable objects that are being used, but I don't see much references to class loaders in this unmarhaller, so I can't quite understand which one's being used to locate the class definitions.


      (2) If "defensive" is turned on on a cache, then, supposedly, the cache should only keep values stored in binary. To ensure this, DefensiveMarshalledValueInterceptor compacts marshalled values right after they have been retrieved from the cache. However, MarshalledValueInterceptor doesn't always call processRetVal() when dealing with key/value objects, at least visitEntrySetCommand() doesn't, so if an entry set is retrieved from a "defensive binary" cache, next get/set operation will return value without it being deserialized (though right after that get, the value will be re-compacted). Would you consider this a bug?


      Thank you.

        • 1. Re: ClassLoaders and "storeAsBinary" caches (5.3)
          Pawel Veselov Newbie

          All right, I dug more into this.


          (UN)Marshallers will get their class loader from invocation context, if present. At least that's what's happening in my case.


          What I'm struggling with - is that I want to *always* use the thread's context classloader for any class look-up operations. And I just can't seem to find a way to do it.

          When an invocation context is being associated with the class loader, the rule is this:

          a) If configuration has a !null class loader, use it

          b) if global configuration has a !null class loader, use it

          c) use thread.contextClassLoader


          I tried creating a classloader that will delegate resolveClass() to Thread.contextClassLoader (and assigning it to individual configuration objects for caches), but that doesn't work because of how Class.forName0() is implemented (classes actually attach to my classloader, instead of the one I delegate to)

          I tried setting 'null' class loader to 'global' configuration, but that causes a NPE when component registry initializes (that's probably a bug as well)

          I thought of providing my own instances of GlobalConfiguration/Configuration objects that will return Thread.currentClassLoader from classLoader() method, but these classes can not be extended.


          It doesn't seem possible without meddling with Infinispan source, which I'd rather avoided...

          • 2. Re: ClassLoaders and "storeAsBinary" caches (5.3)
            Mircea Markus Master

            you can configure the class loader in use directly through the API, by using cache.getAdvancedCache().with(ClassLoader cl)

            1 of 1 people found this helpful
            • 3. Re: ClassLoaders and "storeAsBinary" caches (5.3)
              Pawel Veselov Newbie

              Thank you mircea.markus wish I knew this earlier

              Still facing a couple of issues:

              One, is that entrySet() leaves the objects hanging (so a wrong class loader can get them), but I probably can re-compact the cache when I know class loader is about to change.

              Two, I use Enum keys. It seems when the cache entries are looked up using those keys, and the keys come from Enum values from 2 different class loaders, the keys don't match. That's likely because Enum.equals() returns simply this==other. So I need to provide a custom Equality implementation to compare enums in a more portable way.

              • 4. Re: ClassLoaders and "storeAsBinary" caches (5.3)
                Pawel Veselov Newbie

                Doing both these things helped. Calling cache.compact() after an entry set makes sure no object references are dangling around.

                Using custom configuration object, set my own Equivalence implementation (cb.dataContainer().keyEquivalence(myEquivalence)), so now data moves fine between class loaders, when needed.


                Thank you!