private String fixTypes2( ArrayList<TypeVar> scc, HashSet<String> lowersSet, org.hotswap.agent.javassist.ClassPool cp) throws org.hotswap.agent.javassist.NotFoundException { Iterator<String> it = lowersSet.iterator(); if (lowersSet.size() == 0) { return null; // only NullType } else if (lowersSet.size() == 1) { return it.next(); } else { org.hotswap.agent.javassist.CtClass cc = cp.get(it.next()); while (it.hasNext()) { cc = commonSuperClassEx(cc, cp.get(it.next())); } if (cc.getSuperclass() == null || isObjectArray(cc)) { cc = fixByUppers(scc, cp, new HashSet<TypeVar>(), cc); } if (cc.isArray()) { return org.hotswap.agent.javassist.bytecode.Descriptor.toJvmName(cc); } else { return cc.getName(); } } }
private org.hotswap.agent.javassist.CtClass fixByUppers( ArrayList<TypeVar> users, org.hotswap.agent.javassist.ClassPool cp, HashSet<TypeVar> visited, org.hotswap.agent.javassist.CtClass type) throws org.hotswap.agent.javassist.NotFoundException { if (users == null) { return type; } int size = users.size(); for (int i = 0; i < size; i++) { TypeVar t = users.get(i); if (!visited.add(t)) { return type; } if (t.uppers != null) { int s = t.uppers.size(); for (int k = 0; k < s; k++) { org.hotswap.agent.javassist.CtClass cc = cp.get(t.uppers.get(k)); if (cc.subtypeOf(type)) { type = cc; } } } type = fixByUppers(t.usedBy, cp, visited, type); } return type; }
@OnClassLoadEvent(classNameRegexp = "org.jboss.resteasy.plugins.server.servlet.FilterDispatcher") public static void patchFilterDispatcher(CtClass ctClass, ClassPool classPool) throws NotFoundException, CannotCompileException { CtClass fltCfgClass = classPool.get("javax.servlet.FilterConfig"); CtField configField = new CtField(fltCfgClass, FIELD_NAME, ctClass); ctClass.addField(configField); CtClass setClass = classPool.get(java.util.Set.class.getName()); CtField paramsField = new CtField(setClass, PARAMETER_FIELD_NAME, ctClass); ctClass.addField(paramsField); CtMethod methInit = ctClass.getDeclaredMethod("init"); methInit.insertBefore( "{" + " if(this." + PARAMETER_FIELD_NAME + " == null) {" + PluginManagerInvoker.buildInitializePlugin(ResteasyPlugin.class) + PluginManagerInvoker.buildCallPluginMethod( ResteasyPlugin.class, "registerDispatcher", "this", "java.lang.Object") + " }" + " this." + FIELD_NAME + " = $1;" + " this." + PARAMETER_FIELD_NAME + " = " + ResteasyContextParams.class.getName() + ".init($1.getServletContext(), this." + PARAMETER_FIELD_NAME + "); " + "}"); }
// same as above for older jetty versions @OnClassLoadEvent(classNameRegexp = "org.mortbay.jetty.webapp.WebXmlConfiguration") public static void patchWebXmlConfiguration6x(CtClass ctClass) throws NotFoundException, CannotCompileException, ClassNotFoundException { try { // after application context initialized, but before processing started CtMethod doStart = ctClass.getDeclaredMethod("configureWebApp"); // init the plugin String src = PluginManagerInvoker.buildInitializePlugin( JettyPlugin.class, "getWebAppContext().getClassLoader()"); src += PluginManagerInvoker.buildCallPluginMethod( "getWebAppContext().getClassLoader()", JettyPlugin.class, "init", "getWebAppContext()", "java.lang.Object"); doStart.insertBefore(src); } catch (NotFoundException e) { LOGGER.warning( "org.mortbay.jetty.webapp.WebXmlConfiguration does not contain startContext method. Jetty plugin will be disabled.\n" + "*** This is Ok, Jetty plugin handles only special properties ***"); return; } }
@OnClassLoadEvent(classNameRegexp = ".*", events = LoadEvent.REDEFINE) public void entityReload(ClassLoader classLoader, CtClass clazz, Class original) { if (AnnotationHelper.hasAnnotation(original, PATH_ANNOTATION) || AnnotationHelper.hasAnnotation(clazz, PATH_ANNOTATION)) { LOGGER.debug( "Reload @Path annotated class {}, original classloader {}", clazz.getName(), original.getClassLoader()); refresh(classLoader, 500); } }
/** * Before actual webapp initialization starts in ContextHandler.doStart(), do some enhancements: * * <ul> * <li>Initialize this plugin on the webapp classloader * <li>Call plugin method initExtraPathClassLoader add urls and to start watching changed * resources * <li>Call plugin method registerExtraPathClassLoader to inject enhanced resource loader to the * webapp classloader. * </ul> */ @Transform(classNameRegexp = "org.apache.catalina.core.StandardContext") public static void patchStandardContext(CtClass ctClass) throws NotFoundException, CannotCompileException, ClassNotFoundException { try { // force disable caching ctClass.getDeclaredMethod("isCachingAllowed").setBody("return false;"); } catch (NotFoundException e) { LOGGER.debug( "org.apache.catalina.core.StandardContext does not contain isCachingAllowed() method. Probably Ok."); } try { ctClass .getDeclaredMethod("stopInternal") .insertBefore( PluginManagerInvoker.buildCallCloseClassLoader("getLoader().getClassLoader()")); } catch (NotFoundException e) { LOGGER.debug( "org.apache.catalina.core.StandardContext does not contain stopInternal() method. Hotswap agent will not be able to free Tomcat plugin resources."); } }
/** Before app context is stopped, clean the classloader (and associated plugin instance). */ @OnClassLoadEvent( classNameRegexp = "(org.mortbay.jetty.webapp.WebAppContext)|(org.eclipse.jetty.webapp.WebAppContext)") public static void patchContextHandler6x(CtClass ctClass) throws NotFoundException, CannotCompileException, ClassNotFoundException { try { ctClass .getDeclaredMethod("doStop") .insertBefore(PluginManagerInvoker.buildCallCloseClassLoader("getClassLoader()")); } catch (NotFoundException e) { LOGGER.debug( "org.eclipse.jetty.webapp.WebAppContext does not contain doStop() method. Hotswap agent will not be able to free Jetty plugin resources."); } }
/** Init the plugin from start method. */ @Transform(classNameRegexp = "org.apache.catalina.loader.WebappLoader") public static void patchWebappLoader(CtClass ctClass) throws NotFoundException, CannotCompileException, ClassNotFoundException { try { CtMethod startInternalMethod = ctClass.getDeclaredMethod("startInternal"); // init the plugin String src = PluginManagerInvoker.buildInitializePlugin(TomcatPlugin.class, "classLoader"); startInternalMethod.insertAfter(src); } catch (NotFoundException e) { LOGGER.warning( "org.apache.catalina.loader.WebappLoader does not contain startInternal method. Tomcat plugin will be disabled.\n" + "*** This is Ok, Tomcat plugin handles only special properties ***"); return; } }
/** Run plugin the method. */ public void onWatchEvent( PluginAnnotation<T> pluginAnnotation, WatchFileEvent event, ClassLoader classLoader) { final T annot = pluginAnnotation.getAnnotation(); Object plugin = pluginAnnotation.getPlugin(); // regular files filter if (watchEventDTO.isOnlyRegularFiles() && !event.isFile()) { LOGGER.trace("Skipping URI {} because it is not a regular file.", event.getURI()); return; } // watch type filter if (!Arrays.asList(watchEventDTO.getEvents()).contains(event.getEventType())) { LOGGER.trace("Skipping URI {} because it is not a requested event.", event.getURI()); return; } // resource name filter regexp if (watchEventDTO.getFilter() != null && watchEventDTO.getFilter().length() > 0) { if (!event.getURI().toString().matches(watchEventDTO.getFilter())) { LOGGER.trace( "Skipping URI {} because it does not match filter.", event.getURI(), watchEventDTO.getFilter()); return; } } // we may need to crate CtClass on behalf of the client and close it after invocation. CtClass ctClass = null; // class file regexp if (watchEventDTO.isClassFileEvent()) { try { // TODO creating class only to check name may slow down if lot of handlers is in use. ctClass = createCtClass(event.getURI(), classLoader); } catch (Exception e) { LOGGER.error("Unable create CtClass for URI '{}'.", e, event.getURI()); return; } // unable to create CtClass or it's name does not match if (ctClass == null || !ctClass.getName().matches(watchEventDTO.getClassNameRegexp())) return; } LOGGER.debug( "Executing resource changed method {} on class {} for event {}", pluginAnnotation.getMethod().getName(), plugin.getClass().getName(), event); List<Object> args = new ArrayList<Object>(); for (Class<?> type : pluginAnnotation.getMethod().getParameterTypes()) { if (type.isAssignableFrom(ClassLoader.class)) { args.add(classLoader); } else if (type.isAssignableFrom(URI.class)) { args.add(event.getURI()); } else if (type.isAssignableFrom(URL.class)) { try { args.add(event.getURI().toURL()); } catch (MalformedURLException e) { LOGGER.error("Unable to convert URI '{}' to URL.", e, event.getURI()); return; } } else if (type.isAssignableFrom(ClassPool.class)) { args.add(ClassPool.getDefault()); } else if (type.isAssignableFrom(FileEvent.class)) { args.add(event.getEventType()); } else if (watchEventDTO.isClassFileEvent() && type.isAssignableFrom(CtClass.class)) { args.add(ctClass); } else if (watchEventDTO.isClassFileEvent() && type.isAssignableFrom(String.class)) { args.add(ctClass.getName()); } else { LOGGER.error( "Unable to call method {} on plugin {}. Method parameter type {} is not recognized.", pluginAnnotation.getMethod().getName(), plugin.getClass().getName(), type); return; } } try { pluginAnnotation.getMethod().invoke(plugin, args.toArray()); // close CtClass if created from here if (ctClass != null) { ctClass.detach(); } } catch (IllegalAccessException e) { LOGGER.error( "IllegalAccessException in method {} on plugin {}", e, pluginAnnotation.getMethod().getName(), plugin.getClass().getName()); } catch (InvocationTargetException e) { LOGGER.error( "InvocationTargetException in method {} on plugin {}", e, pluginAnnotation.getMethod().getName(), plugin.getClass().getName()); } }
static boolean eq( org.hotswap.agent.javassist.CtClass one, org.hotswap.agent.javassist.CtClass two) { return one == two || one != null && two != null && one.getName().equals(two.getName()); }
/** * Finds the most specific common super class of the given classes. This method is a copy from * Type. */ public static org.hotswap.agent.javassist.CtClass commonSuperClass( org.hotswap.agent.javassist.CtClass one, org.hotswap.agent.javassist.CtClass two) throws org.hotswap.agent.javassist.NotFoundException { org.hotswap.agent.javassist.CtClass deep = one; org.hotswap.agent.javassist.CtClass shallow = two; org.hotswap.agent.javassist.CtClass backupShallow = shallow; org.hotswap.agent.javassist.CtClass backupDeep = deep; // Phase 1 - Find the deepest hierarchy, set deep and shallow correctly for (; ; ) { // In case we get lucky, and find a match early if (eq(deep, shallow) && deep.getSuperclass() != null) { return deep; } org.hotswap.agent.javassist.CtClass deepSuper = deep.getSuperclass(); org.hotswap.agent.javassist.CtClass shallowSuper = shallow.getSuperclass(); if (shallowSuper == null) { // right, now reset shallow shallow = backupShallow; break; } if (deepSuper == null) { // wrong, swap them, since deep is now useless, its our tmp // before we swap it deep = backupDeep; backupDeep = backupShallow; backupShallow = deep; deep = shallow; shallow = backupShallow; break; } deep = deepSuper; shallow = shallowSuper; } // Phase 2 - Move deepBackup up by (deep end - deep) for (; ; ) { deep = deep.getSuperclass(); if (deep == null) { break; } backupDeep = backupDeep.getSuperclass(); } deep = backupDeep; // Phase 3 - The hierarchy positions are now aligned // The common super class is easy to find now while (!eq(deep, shallow)) { deep = deep.getSuperclass(); shallow = shallow.getSuperclass(); } return deep; }
/** Finds the most specific common super class of the given classes by considering array types. */ public static org.hotswap.agent.javassist.CtClass commonSuperClassEx( org.hotswap.agent.javassist.CtClass one, org.hotswap.agent.javassist.CtClass two) throws org.hotswap.agent.javassist.NotFoundException { if (one == two) { return one; } else if (one.isArray() && two.isArray()) { org.hotswap.agent.javassist.CtClass ele1 = one.getComponentType(); org.hotswap.agent.javassist.CtClass ele2 = two.getComponentType(); org.hotswap.agent.javassist.CtClass element = commonSuperClassEx(ele1, ele2); if (element == ele1) { return one; } else if (element == ele2) { return two; } else { return one.getClassPool() .get(element == null ? "java.lang.Object" : element.getName() + "[]"); } } else if (one.isPrimitive() || two.isPrimitive()) { return null; // TOP } else if (one.isArray() || two.isArray()) { // two.isArray()) return one.getClassPool().get("java.lang.Object"); } else { return commonSuperClass(one, two); } }
private static boolean isObjectArray(org.hotswap.agent.javassist.CtClass cc) throws org.hotswap.agent.javassist.NotFoundException { return cc.isArray() && cc.getComponentType().getSuperclass() == null; }