/* * Copyright 2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.hotswap.agent.plugin.wildfly.el; import org.hotswap.agent.annotation.Init; import org.hotswap.agent.annotation.LoadEvent; import org.hotswap.agent.annotation.Maven; import org.hotswap.agent.annotation.OnClassLoadEvent; import org.hotswap.agent.annotation.Plugin; import org.hotswap.agent.annotation.Versions; import org.hotswap.agent.command.Scheduler; import org.hotswap.agent.command.Scheduler.DuplicateSheduleBehaviour; import org.hotswap.agent.config.PluginConfiguration; import org.hotswap.agent.javassist.CannotCompileException; import org.hotswap.agent.javassist.ClassPool; import org.hotswap.agent.javassist.CtClass; import org.hotswap.agent.javassist.CtConstructor; import org.hotswap.agent.javassist.CtMethod; import org.hotswap.agent.javassist.NotFoundException; import org.hotswap.agent.logging.AgentLogger; import org.hotswap.agent.util.PluginManagerInvoker; /** * Clear javax.el.BeanELResolver cache after any class redefinition. * * @author alpapad@gmail.com * */ @Plugin(name = "WildFlyELResolver", group = "groupELResolver", description = "Purge WildFlyELResolver class cache on any class redefinition.", testedVersions = { "1.0.5.Final" }, expectedVersions = { "1.0.5.Final" }) @Versions(maven = { @Maven(value = "[1.0,)", artifactId = "jboss-el-api_3.0_spec", groupId = "org.jboss.spec.javax.el") }) public class WildFlyELResolverPlugin { /** The logger. */ private static AgentLogger LOGGER = AgentLogger.getLogger(WildFlyELResolverPlugin.class); /** The Constant PURGE_CLASS_CACHE_METHOD_NAME. */ public static final String PURGE_CLASS_CACHE_METHOD_NAME = "__resetCache"; /** The scheduler. */ @Init Scheduler scheduler; /** The app class loader. */ @Init ClassLoader appClassLoader; /** * Hook on BeanELResolver class and for each instance: - ensure plugin is * initialized - register instances using registerBeanELResolver() method. * * @param ctClass * the ct class * @param classPool * the class pool * @throws CannotCompileException * the cannot compile exception * @throws NotFoundException * the not found exception */ @OnClassLoadEvent(classNameRegexp = "javax.el.BeanELResolver") public static void beanELResolverRegisterVariable(CtClass ctClass, ClassPool classPool) throws CannotCompileException, NotFoundException { for (CtConstructor constructor : ctClass.getDeclaredConstructors()) { constructor.insertAfter("java.lang.ClassLoader $$cl = Thread.currentThread().getContextClassLoader();" + PluginManagerInvoker.buildInitializePlugin(WildFlyELResolverPlugin.class, "$$cl")); } LOGGER.info("Patched JbossELResolver"); } /** * Hook on BeanELResolver class and for each instance: - ensure plugin is * initialized - register instances using registerBeanELResolver() method. * * @param ctClass * the ct class * @param classPool * the class pool * @throws CannotCompileException * the cannot compile exception * @throws NotFoundException * the not found exception */ @OnClassLoadEvent(classNameRegexp = "org.jboss.el.cache.BeanPropertiesCache") public static void beanPropertiesCache(CtClass ctClass, ClassPool classPool) throws CannotCompileException, NotFoundException { for (CtConstructor constructor : ctClass.getDeclaredConstructors()) { constructor.insertAfter("java.lang.ClassLoader $$cl = Thread.currentThread().getContextClassLoader();" + PluginManagerInvoker.buildInitializePlugin(WildFlyELResolverPlugin.class, "$$cl")); } LOGGER.info("Patched org.jboss.el.cache.BeanPropertiesCache"); } /** * Hook on org.jboss.el.cache.BeanPropertiesCache.SoftConcurrentHashMap * class and for each instance: - ensure plugin is initialized - register * instances using registerBeanELResolver() method * * @param ctClass * the ct class * @param classPool * the class pool * @throws CannotCompileException * the cannot compile exception * @throws NotFoundException * the not found exception */ @OnClassLoadEvent(classNameRegexp = "org.jboss.el.cache.BeanPropertiesCache.SoftConcurrentHashMap") public static void beanPropertiesCacheSoftConcurrentHashMap(CtClass ctClass, ClassPool classPool) throws CannotCompileException, NotFoundException { ctClass.addMethod(CtMethod.make("public java.util.Set entrySet() { return map.entrySet();}", ctClass)); LOGGER.info("Patched org.jboss.el.cache.BeanPropertiesCache$SoftConcurrentHashMap"); } /** * Hook on BeanELResolver class and for each instance: - ensure plugin is * initialized - register instances using registerBeanELResolver() method. * * @param ctClass * the ct class * @param classPool * the class pool * @throws CannotCompileException * the cannot compile exception * @throws NotFoundException * the not found exception */ @OnClassLoadEvent(classNameRegexp = "org.jboss.el.cache.FactoryFinderCache") public static void factoryFinderCache(CtClass ctClass, ClassPool classPool) throws CannotCompileException, NotFoundException { for (CtConstructor constructor : ctClass.getDeclaredConstructors()) { constructor.insertAfter("java.lang.ClassLoader $$cl = Thread.currentThread().getContextClassLoader();" + // PluginManagerInvoker.buildInitializePlugin(WildFlyELResolverPlugin.class, "$$cl")); } LOGGER.info("Patched org.jboss.el.cache.FactoryFinderCache"); } /** * Invalidate class cache. * * @param original * the original * @throws Exception * the exception */ @OnClassLoadEvent(classNameRegexp = ".*", events = LoadEvent.REDEFINE) public void invalidateClassCache(Class<?> original) throws Exception { LOGGER.trace("Running invalidateClassCache {}", appClassLoader); PurgeWildFlyBeanELResolverCacheCommand cmd = new PurgeWildFlyBeanELResolverCacheCommand(appClassLoader, original.getName()); scheduler.scheduleCommand(cmd, 250, DuplicateSheduleBehaviour.SKIP); } /** * Initialize instance. * * @param pluginConfiguration * the plugin configuration */ @Init public void initializeInstance(PluginConfiguration pluginConfiguration) { LOGGER.info("WildFlyELResolverPlugin Initializing"); } }