private static void shallowCopyFieldState(final WebappClassLoader src, final WebappClassLoader dest) { Class<?> targetClass = WebappClassLoader.class; // Keep backing up the inheritance hierarchy. do { Field[] fields = targetClass.getDeclaredFields(); for (Field field : fields) { // Do not copy resourceEntries - it's a cache that holds class entries. if (!(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()) || field.getName().equals("resourceEntries"))) { try { field.setAccessible(true); Object srcValue = field.get(src); field.set(dest, srcValue); } catch (IllegalAccessException ex) { throw new IllegalStateException( "Shouldn't be illegal to access field '" + field.getName() + "': " + ex); } } } targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); }
/** * Attempt to identify the contexts that have a class loader memory leak. * This is usually triggered on context reload. Note: This method attempts * to force a full garbage collection. This should be used with extreme * caution on a production system. */ public String[] findReloadedContextMemoryLeaks() { System.gc(); List<String> result = new ArrayList<String>(); for (Map.Entry<ClassLoader, String> entry : childClassLoaders.entrySet()) { ClassLoader cl = entry.getKey(); if (cl instanceof WebappClassLoader) { if (!((WebappClassLoader) cl).isStarted()) { result.add(entry.getValue()); } } } return result.toArray(new String[result.size()]); }
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); PrintWriter out = resp.getWriter(); ClassLoader system = ClassLoader.getSystemClassLoader(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); while (cl != null) { if (system == cl) { out.print("SYSTEM,"); } else if (custom == cl) { out.print("CUSTOM,"); } else if (cl instanceof WebappClassLoader) { out.print("WEBAPP,"); } else { out.print("OTHER,"); } cl = cl.getParent(); } }
/** * * @return the ContextPath */ private static String retreiveWebAppName(final String aName) { String wWebAppNane = aName; ClassLoader wCurrentClassLoader = CWebAppBase.class.getClassLoader(); boolean wIsWebappClassLoader = (wCurrentClassLoader instanceof WebappClassLoader); if (wIsWebappClassLoader) { wWebAppNane = ((WebappClassLoader) wCurrentClassLoader).getContextName(); } // keep only alphanumeric chararcters and underscore '_' // @see // http://stackoverflow.com/questions/1805518/replacing-all-non-alphanumeric-characters-with-empty-strings wWebAppNane = wWebAppNane.replaceAll("[^A-Za-z0-9_]", ""); CComponentLoggerFile.logInMain(Level.INFO, CWebAppBase.class, "retreiveWebAppName", "IsWebappClassLoader=[%s] WebAppNane=[%s]", wIsWebappClassLoader, wWebAppNane); return wWebAppNane; }
/** * Delegate for LoadTimeWeaver's {@code getThrowawayClassLoader} method. * Typically called through ReflectiveLoadTimeWeaver. * @see org.springframework.instrument.classloading.LoadTimeWeaver#getThrowawayClassLoader * @see org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver */ public ClassLoader getThrowawayClassLoader() { WebappClassLoader tempLoader = new WebappClassLoader(); // Use reflection to copy all the fields since they are not exposed any other way. shallowCopyFieldState(this, tempLoader); return tempLoader; }
/** * Delegate for LoadTimeWeaver's {@code getThrowawayClassLoader} method. * Typically called through ReflectiveLoadTimeWeaver. * @see org.springframework.instrument.classloading.LoadTimeWeaver#getThrowawayClassLoader * @see org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver */ public ClassLoader getThrowawayClassLoader() { WebappClassLoader tempLoader = new WebappClassLoader(); // Use reflection to copy all the fields since most of them are private on pre-5.5 Tomcat. shallowCopyFieldState(this, tempLoader); return tempLoader; }
@Override public void onResourceInit(Object... args) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (!WebappClassLoader.class.isAssignableFrom(cl.getClass())) { return; } /** * for Application Starting's Resource Init */ InterceptSupport iSupport = InterceptSupport.instance(); InterceptContext context = iSupport.getThreadLocalContext(Event.WEBCONTAINER_RESOURCE_INIT); StandardContext sc = (StandardContext) context.get(InterceptConstants.CONTEXTOBJ); /** * NOTE: spring boot rewrite the tomcat webappclassloader, makes the addURL for nothing, then we can't do * anything on this we may use its webappclassloader's parent as the classloader */ context.put(InterceptConstants.WEBAPPLOADER, sc.getLoader().getClassLoader().getParent()); context.put(InterceptConstants.WEBWORKDIR, sc.getWorkPath()); String contextPath = (String) ReflectionHelper.getField(StandardContext.class, sc, "encodedPath", true); context.put(InterceptConstants.CONTEXTPATH, contextPath); context.put(InterceptConstants.APPNAME, ReflectionHelper.getField(StandardContext.class, sc, "displayName", true)); ServletContext sContext = (ServletContext) ReflectionHelper.getField(StandardContext.class, sc, "context", true); context.put(InterceptConstants.SERVLET_CONTEXT, sContext); String basePath = sContext.getRealPath(""); /* * NOTE: springboot couldn't get the basePath through method "getRealPath", temporary process */ if (basePath == null) { basePath = ""; } else if (basePath.lastIndexOf("/") == (basePath.length() - 1) || basePath.lastIndexOf("\\") == (basePath.length() - 1)) { basePath = basePath.substring(0, basePath.length() - 1); } context.put(InterceptConstants.BASEPATH, basePath); iSupport.doIntercept(context); }
@Override public Object onResourceCreate(Object... args) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (!WebappClassLoader.class.isAssignableFrom(cl.getClass())) { return args[0]; } /** * for Application Starting's Resource Create */ InterceptSupport iSupport = InterceptSupport.instance(); InterceptContext context = iSupport.getThreadLocalContext(Event.WEBCONTAINER_RESOURCE_CREATE); StandardContext sc = (StandardContext) context.get(InterceptConstants.CONTEXTOBJ); /** * NOTE: spring boot rewrite the tomcat webappclassloader, makes the addURL for nothing, then we can't do * anything on this we may use its webappclassloader's parent as the classloader */ context.put(InterceptConstants.WEBAPPLOADER, sc.getLoader().getClassLoader().getParent()); context.put(InterceptConstants.WEBWORKDIR, sc.getWorkPath()); String contextPath = (String) ReflectionHelper.getField(StandardContext.class, sc, "encodedPath", true); context.put(InterceptConstants.CONTEXTPATH, contextPath); context.put(InterceptConstants.APPNAME, ReflectionHelper.getField(StandardContext.class, sc, "displayName", true)); ServletContext sContext = (ServletContext) ReflectionHelper.getField(StandardContext.class, sc, "context", true); context.put(InterceptConstants.SERVLET_CONTEXT, sContext); String basePath = sContext.getRealPath(""); /* * NOTE: springboot couldn't get the basePath through method "getRealPath", temporary process */ if (basePath == null) { basePath = ""; } else if (basePath.lastIndexOf("/") == (basePath.length() - 1) || basePath.lastIndexOf("\\") == (basePath.length() - 1)) { basePath = basePath.substring(0, basePath.length() - 1); } context.put(InterceptConstants.BASEPATH, basePath); context.put(InterceptConstants.RESOURCEOBJ, args[0]); context.put(InterceptConstants.RESOURCECFG, args[1]); iSupport.doIntercept(context); return context.get(InterceptConstants.RESOURCEOBJ); }
/** * on Resource Init * * @param args */ public void onResourceInit(Object... args) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); try { /** * after tomcat8, tomcat use ParallelWebappClassLoader instead of WebappClassLoader as it's webapp's * classloader, both of them are extends WebappClassLoaderBase */ Class<?> cls = cl.loadClass("org.apache.catalina.loader.WebappClassLoaderBase"); if (!cls.isAssignableFrom(cl.getClass())) { return; } } catch (ClassNotFoundException e) { /** * before tomcat7.0.64(include), WebappClassLoaderBase doesn't exist */ if (!WebappClassLoader.class.isAssignableFrom(cl.getClass())) { return; } } /** * for Application Starting's Resource Init */ InterceptSupport iSupport = InterceptSupport.instance(); InterceptContext context = iSupport.getThreadLocalContext(Event.WEBCONTAINER_RESOURCE_INIT); if (context == null) { return; } StandardContext sc = (StandardContext) context.get(InterceptConstants.CONTEXTOBJ); if (sc == null) { return; } context.put(InterceptConstants.WEBAPPLOADER, sc.getLoader().getClassLoader()); context.put(InterceptConstants.WEBWORKDIR, sc.getWorkPath()); context.put(InterceptConstants.CONTEXTPATH, ReflectionHelper.getField(StandardContext.class, sc, "encodedPath", true)); context.put(InterceptConstants.APPNAME, ReflectionHelper.getField(StandardContext.class, sc, "displayName", true)); ServletContext sContext = (ServletContext) ReflectionHelper.getField(StandardContext.class, sc, "context", true); context.put(InterceptConstants.SERVLET_CONTEXT, sContext); getBasePath(context, sContext); iSupport.doIntercept(context); InterceptContext ic = iSupport.getThreadLocalContext(Event.WEBCONTAINER_RESOURCE_INIT); ic.put(InterceptConstants.CONTEXTOBJ, sc); }
/** * on Resource Create */ public Object onResourceCreate(Object... args) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); try { /** * after tomcat8, tomcat use ParallelWebappClassLoader instead of WebappClassLoader as it's webapp's * classloader, both of them are extends WebappClassLoaderBase */ Class<?> cls = cl.loadClass("org.apache.catalina.loader.WebappClassLoaderBase"); if (!cls.isAssignableFrom(cl.getClass())) { return args[0]; } } catch (ClassNotFoundException e) { /** * before tomcat7.0.64(include), WebappClassLoaderBase doesn't exist */ if (!WebappClassLoader.class.isAssignableFrom(cl.getClass())) { return args[0]; } } /** * for Application Starting's Resource Create */ InterceptSupport iSupport = InterceptSupport.instance(); InterceptContext context = iSupport.getThreadLocalContext(Event.WEBCONTAINER_RESOURCE_CREATE, false); if (context == null) { return args[0]; } StandardContext sc = (StandardContext) context.get(InterceptConstants.CONTEXTOBJ); if (sc == null) { return args[0]; } context.put(InterceptConstants.WEBAPPLOADER, sc.getLoader().getClassLoader()); context.put(InterceptConstants.WEBWORKDIR, sc.getWorkPath()); context.put(InterceptConstants.CONTEXTPATH, ReflectionHelper.getField(StandardContext.class, sc, "encodedPath", true)); context.put(InterceptConstants.APPNAME, ReflectionHelper.getField(StandardContext.class, sc, "displayName", true)); ServletContext sContext = (ServletContext) ReflectionHelper.getField(StandardContext.class, sc, "context", true); context.put(InterceptConstants.SERVLET_CONTEXT, sContext); getBasePath(context, sContext); context.put(InterceptConstants.RESOURCEOBJ, args[0]); context.put(InterceptConstants.RESOURCECFG, args[1]); iSupport.doIntercept(context); InterceptContext ic = iSupport.getThreadLocalContext(Event.WEBCONTAINER_RESOURCE_CREATE); ic.put(InterceptConstants.CONTEXTOBJ, sc); return context.get(InterceptConstants.RESOURCEOBJ); }
public static void main(String[] args) { //invoke: http://localhost:8080/Modern or http://localhost:8080/Primitive System.setProperty("catalina.base", System.getProperty("user.dir")); Connector connector = new HttpConnector(); Wrapper wrapper1 = new SimpleWrapper(); wrapper1.setName("Primitive"); wrapper1.setServletClass("PrimitiveServlet"); Wrapper wrapper2 = new SimpleWrapper(); wrapper2.setName("Modern"); wrapper2.setServletClass("ModernServlet"); Context context = new StandardContext(); // StandardContext's start method adds a default mapper context.setPath("/myApp"); context.setDocBase("myApp"); context.addChild(wrapper1); context.addChild(wrapper2); // context.addServletMapping(pattern, name); context.addServletMapping("/Primitive", "Primitive"); context.addServletMapping("/Modern", "Modern"); // add ContextConfig. This listener is important because it configures // StandardContext (sets configured to true), otherwise StandardContext // won't start LifecycleListener listener = new SimpleContextConfig(); ((Lifecycle) context).addLifecycleListener(listener); // here is our loader Loader loader = new WebappLoader(); // associate the loader with the Context context.setLoader(loader); connector.setContainer(context); try { connector.initialize(); ((Lifecycle) connector).start(); ((Lifecycle) context).start(); // now we want to know some details about WebappLoader WebappClassLoader classLoader = (WebappClassLoader) loader.getClassLoader(); System.out.println("Resources' docBase: " + ((ProxyDirContext)classLoader.getResources()).getDocBase()); String[] repositories = classLoader.findRepositories(); for (int i=0; i<repositories.length; i++) { System.out.println(" repository: " + repositories[i]); } // make the application wait until we press a key. System.in.read(); ((Lifecycle) context).stop(); } catch (Exception e) { e.printStackTrace(); } }