package org.hotswap.agent.plugin.jsf; import java.lang.reflect.Method; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import org.hotswap.agent.annotation.Init; import org.hotswap.agent.annotation.OnClassLoadEvent; import org.hotswap.agent.annotation.OnResourceFileEvent; import org.hotswap.agent.annotation.Plugin; import org.hotswap.agent.command.Command; import org.hotswap.agent.command.Scheduler; import org.hotswap.agent.javassist.CannotCompileException; 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; @Plugin(name = "Mojarra", description = "JSF/Mojarra. Clear resource bundle cache when *.properties files are changed.", testedVersions = {"2.1.23, 2.2.8"}, expectedVersions = {"2.1", "2.2"}) public class MojarraPlugin { private static AgentLogger LOGGER = AgentLogger.getLogger(MojarraPlugin.class); @Init Scheduler scheduler; @Init ClassLoader appClassLoader; Set<Object> registeredRBMaps = Collections.newSetFromMap(new WeakHashMap<Object, Boolean>()); @OnClassLoadEvent(classNameRegexp = "com.sun.faces.config.ConfigManager") public static void facesConfigManagerInitialized(CtClass ctClass) throws NotFoundException, CannotCompileException { CtMethod init = ctClass.getDeclaredMethod("initialize"); init.insertAfter(PluginManagerInvoker.buildInitializePlugin(MojarraPlugin.class)); LOGGER.debug("com.sun.faces.config.ConfigManager enhanced with plugin initialization."); } @OnClassLoadEvent(classNameRegexp = "com.sun.faces.application.ApplicationResourceBundle") public static void facesApplicationAssociateInitialized(CtClass ctClass) throws NotFoundException, CannotCompileException { String registerResourceBundle = PluginManagerInvoker.buildCallPluginMethod(MojarraPlugin.class, "registerApplicationResourceBundle", "baseName", "java.lang.String", "resources", "java.lang.Object"); String buildInitializePlugin = PluginManagerInvoker.buildInitializePlugin(MojarraPlugin.class); for (CtConstructor constructor : ctClass.getDeclaredConstructors()) { constructor.insertAfter(buildInitializePlugin); constructor.insertAfter(registerResourceBundle); } LOGGER.debug("com.sun.faces.application.ApplicationAssociate enhanced with resource bundles registration."); } public void registerApplicationResourceBundle(String baseName, Object resourceBundle) { registeredRBMaps.add(resourceBundle); LOGGER.debug("JsfPlugin - resource bundle '" + baseName + "' registered"); } @OnResourceFileEvent(path = "/", filter = ".*.properties") public void refreshJsfResourceBundles() { scheduler.scheduleCommand(refreshResourceBundles); } private Command refreshResourceBundles = new Command() { public void executeCommand() { LOGGER.debug("Refreshing JSF resource bundles."); try { Class<?> clazz = resolveClass("java.util.ResourceBundle"); Method clearCacheMethod = clazz.getDeclaredMethod("clearCache", ClassLoader.class); clearCacheMethod.invoke(null, appClassLoader); for (Object resourceMap : registeredRBMaps) { if (resourceMap instanceof Map) { ((Map) resourceMap).clear(); } } } catch (Exception e) { LOGGER.error("Error clear JSF resource bundles cache", e); } } }; private Class<?> resolveClass(String name) throws ClassNotFoundException { return Class.forName(name, true, appClassLoader); } }