package org.hotswap.agent.plugin.watchResources; import org.hotswap.agent.config.PluginManager; import org.hotswap.agent.annotation.Init; import org.hotswap.agent.annotation.Plugin; import org.hotswap.agent.config.PluginConfiguration; import org.hotswap.agent.logging.AgentLogger; import org.hotswap.agent.util.classloader.WatchResourcesClassLoader; import org.hotswap.agent.util.classloader.URLClassLoaderHelper; import org.hotswap.agent.watch.Watcher; import java.net.*; /** * Support for watchResources configuration property. * * This plugin creates special WatchResourcesClassLoader witch returns only modified resources on watchResources * path. It then modifies application classloader to look for resources first in WatchResourcesClassLoader and * only if the resource is not found, standard execution proceeds. * * Works for any java.net.URLClassLoader which delegates to URLClassPath property to findResource() (typical * scenario). */ @Plugin(name = "WatchResources", description = "Support for watchResources configuration property.", testedVersions = {"JDK 1.7.0_45"}, expectedVersions = {"JDK 1.6+"}) public class WatchResourcesPlugin { private static AgentLogger LOGGER = AgentLogger.getLogger(WatchResourcesPlugin.class); @Init Watcher watcher; @Init ClassLoader appClassLoader; // Classloader to return only modified resources on watchResources path. WatchResourcesClassLoader watchResourcesClassLoader = new WatchResourcesClassLoader(false); /** * For each classloader check for watchResources configuration instance with hotswapper. */ @Init public static void init(PluginManager pluginManager, PluginConfiguration pluginConfiguration, ClassLoader appClassLoader) { LOGGER.debug("Init plugin at classLoader {}", appClassLoader); // synthetic classloader, skip if (appClassLoader instanceof WatchResourcesClassLoader.UrlOnlyClassLoader) return; // init only if the classloader contains directly the property file (not in parent classloader) if (!pluginConfiguration.containsPropertyFile()) { LOGGER.debug("ClassLoader {} does not contain hotswap-agent.properties file, WatchResources skipped.", appClassLoader); return; } // and watch resources are set URL[] watchResources = pluginConfiguration.getWatchResources(); if (watchResources.length == 0) { LOGGER.debug("ClassLoader {} has hotswap-agent.properties watchResources empty.", appClassLoader); return; } if (!(appClassLoader instanceof URLClassLoader)) { LOGGER.warning("Unable to modify application classloader. Classloader '{}' is of type '{}'," + "but only URLClassLoader is supported.\n" + "*** watchResources configuration property will not be handled on JVM level ***", appClassLoader, appClassLoader.getClass()); return; } // create new plugin instance WatchResourcesPlugin plugin = (WatchResourcesPlugin) pluginManager.getPluginRegistry() .initializePlugin(WatchResourcesPlugin.class.getName(), appClassLoader); // and init it with watchResources path plugin.init(watchResources); } /** * Init the plugin instance for resources. * * @param watchResources resources to watch */ private void init(URL[] watchResources) { // configure the classloader to return only changed resources on watchResources path watchResourcesClassLoader.initWatchResources(watchResources, watcher); // modify the application classloader to look for resources first in watchResourcesClassLoader URLClassLoaderHelper.setWatchResourceLoader((URLClassLoader)appClassLoader, watchResourcesClassLoader); } }