/************************************************************************************** * Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. * * http://aspectwerkz.codehaus.org * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the LGPL license * * a copy of which has been included with this distribution in the license.txt file. * **************************************************************************************/ package org.codehaus.aspectwerkz.hook.impl; import org.codehaus.aspectwerkz.hook.ClassPreProcessor; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.WeakHashMap; /** * A simple implementation of class preprocessor. <p/>It does not modify the bytecode. It just prints on stdout some * messages. * * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a> */ public class StdoutPreProcessor implements ClassPreProcessor { /** * Classloaders repository, based on a synchronized weak hashmap key = classloader value = List of URL[] * representing the local search path for .class files of the classloader value is completed at each class loading */ private static Map classloaders; /** * ms interval betwee classloader hierarchy printing */ private static final long stepms = 15000; private static transient long lastPrinted = 0; private void log(String s) { System.out.println(Thread.currentThread().getName() + ": StdoutPreProcessor: " + s); } public void initialize() { log("initialize"); log("loaded by " + this.getClass().getClassLoader()); classloaders = Collections.synchronizedMap(new WeakHashMap()); // register the classloader - except bootstrapCL registerClassLoader(this.getClass().getClassLoader(), this.getClass().getName()); } public byte[] preProcess(String klass, byte[] abyte, ClassLoader caller) { // emulate a -verbose:class mode klass = klass.replace('.', '/') + ".class"; URL u = caller.getResource(klass); log("> " + klass + " [" + ((u == null) ? "?" : u.toString()) + "] [" + caller + "]"); /* * URL uRoot = null; if (u!=null) { // u = * jar:file:/C:/bea/weblogic81/server/lib/weblogic.jar!/weblogic/t3/srvr/T3Srvr.class // getPath = * file:/C:/bea/weblogic81/server/lib/weblogic.jar!/weblogic/t3/srvr/T3Srvr.class // getFile = * file:/C:/bea/weblogic81/server/lib/weblogic.jar!/weblogic/t3/srvr/T3Srvr.class int i = * u.toString().indexOf('!'); if (i > 0) { try { uRoot = new URL(u.toString().substring(0, i)+"!/"); } catch * (MalformedURLException e) { ; } } } */ // register the classloader registerClassLoader(caller, klass); // complete the url path of the classloader registerSearchPath(caller, klass); // dump the hierarchy if needed if (System.currentTimeMillis() > (lastPrinted + stepms)) { lastPrinted = System.currentTimeMillis(); log("*******************************"); log("size=" + classloaders.size()); dumpHierarchy(null, ""); log("*******************************"); } return abyte; } /** * Register a weak reference on the classloader Looks for META-INF/manifest.mf resource and log a line * * @param loader * @param firstClassLoaded */ private void registerClassLoader(ClassLoader loader, String firstClassLoaded) { if (loader != null) { if (!classloaders.containsKey(loader)) { // register the loader and the parent hierarchy if not already registered registerClassLoader(loader.getParent(), loader.getClass().getName()); registerSearchPath(loader.getParent(), loader.getClass().getName()); URL u = null; // *** THIS IS NOT WORKING for other than .class files // *** since manifest.mf can be several time // *** but getResource follow parent first delegation model // try to locate the first META-INF/manifest.mf (should be the aw.xml) // case sensitive of META-INF, meta-inf, Meta-Inf, Meta-inf ? YES IT IS //u = loader.getResource("META-INF/MANIFEST.MF"); // *** THIS could be enough for early registration // add resources in some struct, allow multiple deploy of same resource // if in parallel CL hierarchy - got it ? try { //@todo - case sensitive stuff is dangerous // we could merge all aw.xml in META-INF and WEB-INF and meta-inf ... Enumeration ue = loader.getResources("META-INF/MANIFEST.MF"); if (ue.hasMoreElements()) { log("--- in scope for " + loader); } while (ue.hasMoreElements()) { log("--- " + ue.nextElement().toString()); } } catch (IOException e) { ; } // register this loader log("****" + loader + " [" + ((u == null) ? "?" : u.toString()) + "] [" + firstClassLoaded + ']'); classloaders.put(loader, new ArrayList()); } // register search path based on firstClassLoaded } } /** * Dumps on stdout the registered classloader hierarchy child of "parent" Using the depth to track recursivity level */ private void dumpHierarchy(ClassLoader parent, String depth) { // do a copy of the registered CL to allow access on classloaders structure List cl = new ArrayList(classloaders.keySet()); ClassLoader current = null; for (Iterator i = cl.iterator(); i.hasNext();) { current = (ClassLoader) i.next(); if (current.getParent() == parent) { log(depth + current + '[' + classloaders.get(current)); // handcheck for duplicate path (?) List path = (List) classloaders.get(current); ClassLoader currentParent = current.getParent(); while (currentParent != null) { for (Iterator us = path.iterator(); us.hasNext();) { URL u = (URL) us.next(); if (((List) classloaders.get(currentParent)).contains(u)) { log("!!!! duplicate detected for " + u + " in " + current); } } currentParent = currentParent.getParent(); } dumpHierarchy(current, depth + " "); } } } private void registerSearchPath(final ClassLoader loader, final String klass) { // bootCL if (loader == null) { return; } // locate the klass String klassFile = klass.replace('.', '/') + ".class"; URL uKlass = loader.getResource(klassFile); if (uKlass == null) { return; } // retrieve the location root (jar/zip or directory) URL uRoot = null; int i = uKlass.toString().indexOf('!'); if (i > 0) { // jar/zip try { // remove the jar: zip: prefix //@todo !! zip: seems to be BEA specific uRoot = (new File(uKlass.toString().substring(4, i))).getCanonicalFile().toURL(); //uRoot = new URL(uKlass.toString().substring(0, i)+"!/"); } catch (MalformedURLException e) { e.printStackTrace(); return; } catch (IOException e2) { e2.printStackTrace(); return; } } else { // directory i = uKlass.toString().indexOf(klassFile); try { uRoot = (new File(uKlass.toString().substring(0, i))).getCanonicalFile().toURL(); } catch (MalformedURLException e) { e.printStackTrace(); return; } catch (IOException e2) { e2.printStackTrace(); return; } } // check if the location is not in a parent ClassLoader parent = loader.getParent(); while (parent != null) { if (((List) classloaders.get(parent)).contains(uRoot)) { return; } parent = parent.getParent(); } // add the location if not already registered // @todo !! not thread safe List path = (List) classloaders.get(loader); if (!path.contains(uRoot)) { log("adding path " + uRoot + " to " + loader); path.add(uRoot); } } public static void main(String[] args) throws Exception { URL u = new URL( "jar:file:/C:/bea/user_projects/domains/mydomain/myserver/.wlnotdelete/gallery/gallery-rar.jar!/" ); // differ from a "/./" URL u2 = new URL( "jar:file:/C:/bea/user_projects/domains/mydomain/./myserver/.wlnotdelete/gallery/gallery-rar.jar!/" ); if (u.sameFile(u2)) { System.out.println("same"); } else { System.out.println("differ"); } } }