Search code examples
weblogichotswapdcevm

Strange java.beans.Introspector behavior on WebLogic with DCEVM and HotSwapAgent


I am running WebLogic on JVM 1.7 with DCEVM (full implementation) and HotSwapAgent with custom plugin that gets triggered on every onClassLoad.

I run into problems with Freemarker, which uses java.beans.Introspector. What I discovered is the fact that when I call Introspector.flushCaches on the method called by HotSwapAgent (through ReflectionCommand), then the BeanInfo in Introspector is invalidated properly (checked with debugger in that thread). However when I make request to the WLS appserver, then Introspector for the worker thread shows the old values!

This seems like some thread-local implementation, but I was not able to find anything that would point to that assumption in java.beans.Introspector's documentation.

Does anybody have any clue why this is happening and how to resolve that?

Currently I store the information about reloaded classes in separate class and reload everything that's in that cache from the request thread, which works.

Thanks for any clue.


Solution

  • Found this thanks to @ddekany and his answer to related problem at Freemarker removeIntrospectionInfo does not work with DCEVM after model hotswap

    It seems that JVM (at least HotSpot 1.7) caches Introspector's cache per ThreadGroup. This means, that Introspector.flushCaches must be called in a thread that runs in corresponding ThreadGroup.

    When I executed this for all the ThreadGroups in the application then everything started working again properly.

    I wasn't able to find any documentation why the java.beans.Introspector is cached per ThreadGroup so if anybody has an reliable information to this, please add a comment with link.

    Thanks.

    Update:

    Comming from JDK7 source

    /**
         * Introspect on a Java Bean and learn about all its properties, exposed
         * methods, and events.
         * <p>
         * If the BeanInfo class for a Java Bean has been previously Introspected
         * then the BeanInfo class is retrieved from the BeanInfo cache.
         *
         * @param beanClass  The bean class to be analyzed.
         * @return  A BeanInfo object describing the target bean.
         * @exception IntrospectionException if an exception occurs during
         *              introspection.
         * @see #flushCaches
         * @see #flushFromCaches
         */
        public static BeanInfo getBeanInfo(Class<?> beanClass)
            throws IntrospectionException
        {
            if (!ReflectUtil.isPackageAccessible(beanClass)) {
                return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo();
            }
            ThreadGroupContext context = ThreadGroupContext.getContext();
            BeanInfo beanInfo;
            synchronized (declaredMethodCache) {
                beanInfo = context.getBeanInfo(beanClass);
            }
            if (beanInfo == null) {
                beanInfo = new Introspector(beanClass, null, USE_ALL_BEANINFO).getBeanInfo();
                synchronized (declaredMethodCache) {
                    context.putBeanInfo(beanClass, beanInfo);
                }
            }
            return beanInfo;
        }
    

    This has been definitely added in JDK7, because I checked JDK6 code and it's not there!.