content: /*
   * Copyright (C) 2005 by Quentin Anciaux
   *
   * This library is free software; you can redistribute it and/or modify it
   * under the terms of the GNU Library General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or (at your
   * option) any later version.
   *
   * This library is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
   * for more details.
   *
   * You should have received a copy of the GNU Library General Public License
   * along with this library; if not, write to the Free Software Foundation,
   * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   *
   *	@author Quentin Anciaux
   */
  package org.allcolor.yahp.converter;
  
  import java.beans.Introspector;
  
  import java.io.ByteArrayOutputStream;
  import java.io.File;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  
  import java.lang.ref.WeakReference;
  import java.lang.reflect.Constructor;
  import java.lang.reflect.Field;
  import java.lang.reflect.Method;
  
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.net.URLClassLoader;
  import java.net.URLConnection;
  import java.net.URLStreamHandlerFactory;
  
  import java.security.AccessControlContext;
  import java.security.ProtectionDomain;
  
  import java.sql.Driver;
  import java.sql.DriverManager;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Enumeration;
  import java.util.HashMap;
  import java.util.Hashtable;
  import java.util.IdentityHashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import java.util.StringTokenizer;
  import java.util.Vector;
  import java.util.Map.Entry;
  import java.util.jar.JarEntry;
  import java.util.jar.JarInputStream;
  import java.util.logging.Level;
  import java.util.logging.Logger;
  
  import javax.swing.LookAndFeel;
  import javax.swing.UIManager;
  
  /**
   * This is a custom tree classloader
   * 
   * @author Quentin Anciaux
   * @version 1.0
   */
  public final class CClassLoader extends URLClassLoader {
  	/** classloader namespace */
  	public static final String CCLASSLOADER_NAMESPACE = "org.allcolor::CClassLoader.loadClass::";
  
  	private static ThreadLocal contextLoader = new ThreadLocal();
  
  	/** DEBUG log level */
  	private static final int DEBUG = 1;
  
  	/** FATAL log level */
  	private static final int FATAL = 2;
  
  	/** INFO log level */
  	private static final int INFO = 0;
  
  	/** use for logging */
  	private static final Logger log = Logger.getLogger(CClassLoader.class
  			.getName());
  
  	/** contains all mandatory loaders */
  	private static final Map mandatoryLoadersMap = new HashMap();
  
  	/** name of the rootloader */
  	public static final String ROOT_LOADER = "rootLoader";
  
  	/** handle to the root loader */
  	private static CClassLoader rootLoader = CClassLoader.createLoader(
  			(CClassLoader.class.getClassLoader() != null) ? CClassLoader.class
  					.getClassLoader() : ClassLoader.getSystemClassLoader(),
  			CClassLoader.ROOT_LOADER);
  
  	/**
  	 * Ser the URLClassLoader that will return to calls to getURLs()
  	 */
  	private static volatile URLClassLoader urlLoader = null;
  
  	protected void finalize() throws Throwable {
  		System.out.println("Finalizing classloader : "+this.finalizepath);
  		this.finalizepath = null;
  	}
  	
  	/**
  	 * Create a new loader
  	 * 
  	 * @param parent
  	 *            a reference to the parent loader
  	 * @param name
  	 *            loader name
  	 * 
  	 * @return a new loader
  	 */
  	private static final CClassLoader createLoader(final ClassLoader parent,
  			final String name) {
  		try {
  			return new CClassLoader(parent, name);
  		} catch (final Exception ignore) {
  			return null;
  		}
  	} // end createLoader()
  
  	/**
  	 * Creates and allocates a memory URL
  	 * 
  	 * @param entryName
  	 *            name of the entry
  	 * @param entry
  	 *            byte array of the entry
  	 * @return the created URL or null if an error occurs.
  	 */
  	public static URL createMemoryURL(final String entryName, final byte[] entry) {
  		try {
  			final Class c = ClassLoader.getSystemClassLoader().loadClass(
  					"org.allcolor.yahp.converter.CMemoryURLHandler");
  			final Method m = c.getDeclaredMethod("createMemoryURL",
  					new Class[] { String.class, byte[].class });
  			m.setAccessible(true);
  			return (URL) m.invoke(null, new Object[] { entryName, entry });
  		} catch (final Exception ignore) {
  			ignore.printStackTrace();
  			return null;
  		}
  	}
  	
  	private static final void clearMap(Map map) {
  		for(Iterator it = map.entrySet().iterator();it.hasNext();) {
  			Map.Entry e = (Map.Entry)it.next();
  			Object value = e.getKey();
  			if ((value != null)
  					&& (value.getClass()
  							.getClassLoader() != null)
  					&& (value.getClass()
  							.getClassLoader()
  							.getClass() == CClassLoader.class)) {
  				System.out
  				.println("Resseting thread local "
  						+ value);
  				it.remove();
  				continue;
  			}
  			if (value instanceof Map) {
  				clearMap((Map)value);
  			} else if (value instanceof List) {
  				clearList((List)value);
  			} else if (value instanceof Set) {
  				clearSet((Set)value);
  			}
  			value = e.getValue();
  			if ((value != null)
  					&& (value.getClass()
  							.getClassLoader() != null)
  					&& (value.getClass()
  							.getClassLoader()
  							.getClass() == CClassLoader.class)) {
  				System.out
  				.println("Resseting thread local "
  						+ value);
  				it.remove();
  				continue;
  			}
  			if (value instanceof Map) {
  				clearMap((Map)value);
  			} else if (value instanceof List) {
  				clearList((List)value);
  			} else if (value instanceof Set) {
  				clearSet((Set)value);
  			} else if (value instanceof Object[]) {
  				clearArray((Object[])value);
  			}
  		}
  	}
  	
  	private static final void clearArray(Object [] array) {
  		for (int i=0;i<array.length;i++) {
  			Object value = array[i];
  			if ((value != null)
  					&& (value.getClass()
  							.getClassLoader() != null)
  					&& (value.getClass()
  							.getClassLoader()
  							.getClass() == CClassLoader.class)) {
  				System.out
  				.println("Resseting thread local "
  						+ value);
  				array[i] = null;
  				continue;
  			}
  			if (value instanceof Map) {
  				clearMap((Map)value);
  			} else if (value instanceof List) {
  				clearList((List)value);
  			} else if (value instanceof Set) {
  				clearSet((Set)value);
  			} else if (value instanceof Object[]) {
  				clearArray((Object[])value);
  			}
  		}
  	}
  	
  	private static final void clearList(List list) {
  		for(Iterator it = list.iterator();it.hasNext();) {
  			Object value = it.next();
  			if ((value != null)
  					&& (value.getClass()
  							.getClassLoader() != null)
  					&& (value.getClass()
  							.getClassLoader()
  							.getClass() == CClassLoader.class)) {
  				System.out
  				.println("Resseting thread local "
  						+ value);
  				it.remove();
  				continue;
  			}
  			if (value instanceof Map) {
  				clearMap((Map)value);
  			} else if (value instanceof List) {
  				clearList((List)value);
  			} else if (value instanceof Set) {
  				clearSet((Set)value);
  			} else if (value instanceof Object[]) {
  				clearArray((Object[])value);
  			}
  		}
  	}
  
  	private static final void clearSet(Set list) {
  		for(Iterator it = list.iterator();it.hasNext();) {
  			Object value = it.next();
  			if ((value != null)
  					&& (value.getClass()
  							.getClassLoader() != null)
  					&& (value.getClass()
  							.getClassLoader()
  							.getClass() == CClassLoader.class)) {
  				System.out
  				.println("Resseting thread local "
  						+ value);
  				it.remove();
  				continue;
  			}
  			if (value instanceof Map) {
  				clearMap((Map)value);
  			} else if (value instanceof List) {
  				clearList((List)value);
  			} else if (value instanceof Set) {
  				clearSet((Set)value);
  			} else if (value instanceof Object[]) {
  				clearArray((Object[])value);
  			}
  		}
  	}
  
  	/**
  	 * destroy the loader tree
  	 */
  	public static final void destroy() {
  		if (CClassLoader.rootLoader == null) {
  			return;
  		}
  		System.out.println("Destroying YAHP ClassLoader Tree");
  
  		CClassLoader.urlLoader = null;
  
  		try {
  			Field f = Class.forName("java.lang.Shutdown").getDeclaredField(
  					"hooks");
  			f.setAccessible(true);
  			ArrayList l = (ArrayList) f.get(null);
  			for (Iterator it = l.iterator(); it.hasNext();) {
  				Object o = it.next();
  				if ((o != null)
  						&& (o.getClass().getClassLoader() != null)
  						&& (o.getClass().getClassLoader().getClass() == CClassLoader.class)) {
  					it.remove();
  				}
  			}
  		} catch (Throwable ignore) {
  		}
  
  		try {
  			Field f = Class.forName("java.lang.ApplicationShutdownHooks")
  					.getDeclaredField("hooks");
  			f.setAccessible(true);
  			IdentityHashMap l = (IdentityHashMap) f.get(null);
  			for (Iterator it = l.entrySet().iterator(); it.hasNext();) {
  				Entry e = (Entry) it.next();
  				Thread o = (Thread) e.getKey();
  				if ((o != null)
  						&& (o.getClass().getClassLoader() != null)
  						&& (o.getClass().getClassLoader().getClass() == CClassLoader.class)) {
  					it.remove();
  					continue;
  				}
  				o = (Thread) e.getValue();
  				if ((o != null)
  						&& (o.getClass().getClassLoader() != null)
  						&& (o.getClass().getClassLoader().getClass() == CClassLoader.class)) {
  					it.remove();
  				}
  			}
  		} catch (Throwable ignore) {
  		}
  
  		try {
  			if ((UIManager.getLookAndFeel() != null)
  					&& (UIManager.getLookAndFeel().getClass().getClassLoader() != null)
  					&& (UIManager.getLookAndFeel().getClass().getClassLoader()
  							.getClass() == CClassLoader.class)) {
  				UIManager.setLookAndFeel((LookAndFeel) null);
  			}
  			Field f = UIManager.class.getDeclaredField("currentLAFState");
  			f.setAccessible(true);
  			Object lafstate = f.get(null);
  			if (lafstate != null) {
  				Field fmultiUIDefaults = lafstate.getClass().getDeclaredField(
  						"multiUIDefaults");
  				fmultiUIDefaults.setAccessible(true);
  				Object multiUIDefaults = fmultiUIDefaults.get(lafstate);
  				Method clear = multiUIDefaults.getClass().getDeclaredMethod(
  						"clear", (Class[]) null);
  				clear.setAccessible(true);
  				clear.invoke(multiUIDefaults, (Object[]) null);
  				Field tbl = lafstate.getClass().getDeclaredField("tables");
  				tbl.setAccessible(true);
  				Hashtable[] tables = (Hashtable[]) tbl.get(lafstate);
  				if (tables != null) {
  					for (int i = 0; i < tables.length; i++) {
  						Hashtable element = tables[i];
  						if (element != null) {
  							element.clear();
  						}
  					}
  				}
  			}
  		} catch (Throwable ignore) {
  		}
  
  		try {
  			Hashtable tb = UIManager.getDefaults();
  			Object cl = tb.get("ClassLoader");
  			if (cl.getClass() == CClassLoader.class) {
  				tb.put("ClassLoader", CClassLoader.rootLoader.getParent());
  			}
  		} catch (Throwable ignore) {
  		}
  
  		Method logFactoryRelease = null;
  
  		try {
  			logFactoryRelease = CClassLoader.rootLoader.loadClass(
  					"org.apache.commons.logging.LogFactory").getMethod(
  					"release", new Class[] { ClassLoader.class });
  		} catch (final Throwable ignore) {
  		}
  
  		CClassLoader.rootLoader._destroy(logFactoryRelease);
  		CClassLoader.mandatoryLoadersMap.clear();
  		CClassLoader.rootLoader = null;
  
  		// deregister any sql driver loaded
  		try {
  			final List deregisterList = new ArrayList();
  			for (final Enumeration it = DriverManager.getDrivers(); it
  					.hasMoreElements();) {
  				final Driver d = (Driver) it.nextElement();
  
  				if ((d != null)
  						&& (d.getClass().getClassLoader() != null)
  						&& (d.getClass().getClassLoader().getClass() == CClassLoader.class)) {
  					deregisterList.add(d);
  				}
  			}
  
  			for (int i = 0; i < deregisterList.size(); i++) {
  				final Driver driver = (Driver) deregisterList.get(i);
  				DriverManager.deregisterDriver(driver);
  			}
  		} catch (final Throwable ignore) {
  		}
  
  		// stop dandling thread created with this classloader
  		// tested only on sun jdk
  		ThreadGroup tg = Thread.currentThread().getThreadGroup();
  		while ((tg != null) && (tg.getParent() != null)) {
  			tg = tg.getParent();
  		}
  		List ltg = new ArrayList();
  		ltg.add(tg);
  		CClassLoader.getThreadGroups(tg, ltg);
  		for (int ii = 0; ii < ltg.size(); ii++) {
  			try {
  				final ThreadGroup g = (ThreadGroup) ltg.get(ii);
  				final Field fthreads = ThreadGroup.class
  						.getDeclaredField("threads");
  				fthreads.setAccessible(true);
  
  				final List toStop = new ArrayList();
  				Object threads[] = null;
  
  				if (fthreads.getType() == Vector.class) {
  					// in gnu classpath
  					threads = ((Vector) fthreads.get(g)).toArray();
  				} else {
  					// sun
  					threads = (Object[]) fthreads.get(g);
  				}
  
  				for (int i = 0; i < threads.length; i++) {
  					if (threads[i] == null) {
  						continue;
  					}
  					if ((threads[i] != null)
  							&& (((Thread) threads[i]).getContextClassLoader() != null)
  							&& (((Thread) threads[i]).getContextClassLoader()
  									.getClass() == CClassLoader.class)) {
  						((Thread) threads[i]).setContextClassLoader(null);
  					}
  					if ((threads[i] != null)
  							&& (threads[i].getClass().getClassLoader() != null)
  							&& (threads[i].getClass().getClassLoader()
  									.getClass() == CClassLoader.class)) {
  						toStop.add((Thread) threads[i]);
  					}
  
  					// remove any object in threadLocal referring an object
  					// loaded
  					// by this classloader tree
  					try {
  						final Field fthreadLocals = Thread.class
  								.getDeclaredField("threadLocals");
  						fthreadLocals.setAccessible(true);
  
  						final Object threadLocals = fthreadLocals
  								.get(threads[i]);
  						if (threadLocals != null) {
  							final Field ftable = threadLocals.getClass()
  									.getDeclaredField("table");
  							ftable.setAccessible(true);
  
  							final Object table[] = (Object[]) ftable
  									.get(threadLocals);
  
  							for (int kk = 0; kk < table.length; kk++) {
  								final Object element = table[kk];
  								if (element != null) {
  									final Field fvalue = element.getClass()
  											.getDeclaredField("value");
  									fvalue.setAccessible(true);
  
  									final Object value = fvalue.get(element);
  
  									if ((value != null)
  											&& (value.getClass()
  													.getClassLoader() != null)
  											&& (value.getClass()
  													.getClassLoader()
  													.getClass() == CClassLoader.class)) {
  										fvalue.set(element, null);
  									}
  									if (value instanceof Map) {
  										clearMap((Map)value);
  									} else if (value instanceof List) {
  										clearList((List)value);
  									} else if (value instanceof Set) {
  										clearSet((Set)value);
  									} else if (value instanceof Object[]) {
  										clearArray((Object[])value);
  									}
  
  									fvalue.setAccessible(false);
  								}
  							}
  							ftable.setAccessible(false);
  						}
  						fthreadLocals.setAccessible(false);
  					} catch (final Throwable ignore) {
  						ignore.printStackTrace();
  					}
  
  					// remove any object in threadLocal referring an object
  					// loaded
  					// by this classloader tree
  					try {
  						final Field fthreadLocals = Thread.class
  								.getDeclaredField("inheritableThreadLocals");
  						fthreadLocals.setAccessible(true);
  
  						final Object threadLocals = fthreadLocals
  								.get(threads[i]);
  						if (threadLocals != null) {
  							final Field ftable = threadLocals.getClass()
  									.getDeclaredField("table");
  							ftable.setAccessible(true);
  
  							final Object table[] = (Object[]) ftable
  									.get(threadLocals);
  
  							for (int kk = 0; kk < table.length; kk++) {
  								final Object element = table[kk];
  								if (element != null) {
  									final Field fvalue = element.getClass()
  											.getDeclaredField("value");
  									fvalue.setAccessible(true);
  
  									final Object value = fvalue.get(element);
  
  									if ((value != null)
  											&& (value.getClass()
  													.getClassLoader() != null)
  											&& (value.getClass()
  													.getClassLoader()
  													.getClass() == CClassLoader.class)) {
  										fvalue.set(element, null);
  									}
  									if (value instanceof Map) {
  										clearMap((Map)value);
  									} else if (value instanceof List) {
  										clearList((List)value);
  									} else if (value instanceof Set) {
  										clearSet((Set)value);
  									} else if (value instanceof Object[]) {
  										clearArray((Object[])value);
  									}
  
  									fvalue.setAccessible(false);
  								}
  							}
  
  							ftable.setAccessible(false);
  						}
  						fthreadLocals.setAccessible(false);
  					} catch (final Throwable ignore) {
  						ignore.printStackTrace();
  					}
  
  					// remove any protection domain referring this loader tree
  					try {
  						final Field finheritedAccessControlContext = Thread.class
  								.getDeclaredField("inheritedAccessControlContext");
  						finheritedAccessControlContext.setAccessible(true);
  
  						final Object inheritedAccessControlContext = finheritedAccessControlContext
  								.get(threads[i]);
  
  						if (inheritedAccessControlContext != null) {
  							final Field fcontext = AccessControlContext.class
  									.getDeclaredField("context");
  							fcontext.setAccessible(true);
  
  							final Object context[] = (Object[]) fcontext
  									.get(inheritedAccessControlContext);
  
  							if (context != null) {
  								for (int k = 0; k < context.length; k++) {
  									if (context[k] == null) continue;
  									final Field fclassloader = ProtectionDomain.class
  											.getDeclaredField("classloader");
  									fclassloader.setAccessible(true);
  
  									final Object classloader = fclassloader
  											.get(context[k]);
  
  									if ((classloader != null)
  											&& (classloader.getClass() == CClassLoader.class)) {
  										context[k] = null;
  									}
  
  									fclassloader.setAccessible(false);
  								}
  							}
  
  							fcontext.setAccessible(false);
  						}
  
  						finheritedAccessControlContext.setAccessible(false);
  					} catch (final Throwable ignore) {
  						ignore.printStackTrace();
  					}
  				}
  
  				fthreads.setAccessible(false);
  				for (int i = 0; i < toStop.size(); i++) {
  					try {
  						final Thread t = (Thread) toStop.get(i);
  						final Method stop = t.getClass().getMethod("stop",
  								(Class[]) null);
  						stop.invoke(t, (Object[]) null);
  					} catch (final Throwable ignore) {
  					}
  				}
  			} catch (final Throwable ignore) {
  			}
  		}
  		try{CThreadContext.destroy();}catch(Throwable ignore){}
  		System.runFinalization();
  		System.gc();
  		Introspector.flushCaches();
  		System.out.println("Destroying YAHP ClassLoader Tree : done");
  	} // end destroy()
  
  	/**
  	 * Return a list of classes and jar files
  	 * 
  	 * @param current
  	 *            currently inspected file
  	 * @param list
  	 *            list of url to classes and jar files
  	 * @return list of url to classes and jar files
  	 */
  	private static List getClasses(final File current, final List list) {
  		if (current.isDirectory()) {
  			try {
  				list.add(current.toURI().toURL());
  			} catch (final Exception ignore) {
  			}
  
  			final File children[] = current.listFiles();
  
  			for (int i = 0; i < children.length; i++) {
  				final File element = children[i];
  				CClassLoader.getClasses(element, list);
  			}
  		} else if (current.isFile()) {
  			final String name = current.getName();
  
  			if (name.endsWith(".class")) {
  				try {
  					list.add(current.toURI().toURL());
  				} catch (final Exception ignore) {
  				}
  			} else if (name.endsWith(".jar")) {
  				try {
  					list.add(current.toURI().toURL());
  				} catch (final Exception ignore) {
  				}
  			}
  		}
  
  		return list;
  	} // end getClasses()
  
  	public static final ClassLoader getContextLoader() {
  		return CClassLoader.contextLoader.get() != null ? (CClassLoader) ((WeakReference) CClassLoader.contextLoader
  				.get()).get()
  				: null;
  	}
  
  	/**
  	 * return the loader with the given path
  	 * 
  	 * @param fpath
  	 *            the path to lookup
  	 * 
  	 * @return the loader with the given path
  	 */
  	public static final CClassLoader getLoader(final String fpath) {
  		String path = fpath;
  		CClassLoader currentLoader = CClassLoader.rootLoader;
  
  		if (path == null) {
  			return CClassLoader.getRootLoader();
  		}
  
  		if (path.startsWith(CClassLoader.ROOT_LOADER)) {
  			path = path.substring(10);
  		}
  
  		final StringTokenizer tokenizer = new StringTokenizer(path, "/", false);
  
  		while (tokenizer.hasMoreTokens()) {
  			final String name = tokenizer.nextToken();
  			currentLoader = currentLoader.getLoaderByName(name);
  		}
  
  		return currentLoader;
  	} // end getLoader()
  
  	/**
  	 * return the rootloader
  	 * 
  	 * @return the rootloader
  	 */
  	public static final CClassLoader getRootLoader() {
  		if (CClassLoader.rootLoader == null) {
  			CClassLoader.rootLoader = CClassLoader.createLoader(
  					CClassLoader.class.getClassLoader(),
  					CClassLoader.ROOT_LOADER);
  		}
  		return CClassLoader.rootLoader;
  	} // end getRootLoader()
  
  	private static void getThreadGroups(ThreadGroup tg, List ltg) {
  		try {
  			final Field fgroups = ThreadGroup.class.getDeclaredField("groups");
  			fgroups.setAccessible(true);
  			ThreadGroup[] array = (ThreadGroup[]) fgroups.get(tg);
  			if (array != null) {
  				for (int i = 0; i < array.length; i++) {
  					ThreadGroup element = array[i];
  					if (!ltg.contains(element)) {
  						CClassLoader.getThreadGroups(element, ltg);
  						ltg.add(element);
  					}
  				}
  			}
  		} catch (Throwable ignore) {
  		}
  	}
  
  	/**
  	 * Return a list of jar and classes located in path
  	 * 
  	 * @param path
  	 *            the path in which to search
  	 * @return a list of jar and classes located in path
  	 */
  	public static URL[] getURLs(final String path) {
  		final File topDir = new File(path);
  		final List list = new ArrayList();
  		CClassLoader.getClasses(topDir, list);
  
  		final URL ret[] = new URL[list.size() + 1];
  
  		for (int i = 0; i < list.size(); i++) {
  			ret[i] = (URL) list.get(i);
  		}
  
  		try {
  			ret[list.size()] = topDir.toURI().toURL();
  		} catch (final Exception ignore) {
  		}
  
  		return ret;
  	} // end getURLs()
  
  	/**
  	 * initialize the loaders hierarchy
  	 * 
  	 * @param config
  	 *            the loaders config object
  	 */
  	public static final void init(final CClassLoaderConfig config) {
  		CClassLoader.installURLStreamHandlerFactory();
  		if (CClassLoader.getRootLoader().isInit()) {
  			return;
  		}
  
  		// initialize the loaders default parameters.
  		for (final Iterator it = config.getLoadersInfoMap().entrySet()
  				.iterator(); it.hasNext();) {
  			final Map.Entry entry = (Map.Entry) it.next();
  			final String loaderPath = (String) entry.getKey();
  			final CClassLoaderConfig.CLoaderInfo info = (CClassLoaderConfig.CLoaderInfo) entry
  					.getValue();
  			final CClassLoader loader = CClassLoader.getLoader(loaderPath);
  			loader.config = config;
  			loader.booAlone = info.isAlone();
  			loader.booDoNotForwardToParent = info.isDoNotForwardToParent();
  			loader.booMandatory = info.isMandatory();
  
  			if (loader.isMandatory()) {
  				CClassLoader.mandatoryLoadersMap.put(loader.getPath(), loader);
  			}
  
  			loader.booResourceOnly = info.isResourceOnly();
  		}
  		// lookup classes/resources in filesystem
  		for (final Iterator it = config.getFilesMap().entrySet().iterator(); it
  				.hasNext();) {
  			final Map.Entry entry = (Map.Entry) it.next();
  			final String loaderPath = (String) entry.getKey();
  			final List list = (List) entry.getValue();
  			final CClassLoader loader = CClassLoader.getLoader(loaderPath);
  
  			for (final Iterator f = list.iterator(); f.hasNext();) {
  				final Object obj = f.next();
  				if (obj instanceof URL) {
  					final URL file = (URL) obj;
  					String name = file.toString();
  					final int index = name.indexOf(loaderPath);
  					if (index != -1) {
  						name = name.substring(index + loaderPath.length());
  					}
  					if (name.endsWith(".jar")) {
  						loader.addResource(name, file);
  						loader.readDirectories(file);
  					} else {
  						loader.addResource(name, file);
  						if (!loader.booResourceOnly && name.endsWith(".class")) {
  							name = name.substring(0, name.lastIndexOf('.'));
  							name = name.replace('\\', '/');
  							name = name.replace('/', '.');
  							loader.addClass(name, file);
  						} else if (name.startsWith("native/")) {
  							String system = name.substring(7);
  							system = system.substring(0, system.indexOf('/'));
  							if (!loader.dllMap.containsKey(system)) {
  								loader.dllMap.put(system, file);
  							}
  							if (!loader.resourcesMap.containsKey(name)) {
  								loader.resourcesMap.put(name, file);
  							} else {
  								final Object to = loader.resourcesMap.get(name);
  								if (to instanceof URL) {
  									final URL uo = (URL) to;
  									final List l = new ArrayList();
  									l.add(uo);
  									l.add(file);
  									loader.resourcesMap.put(name, l);
  								} else if (to instanceof List) {
  									final List uo = (List) to;
  									uo.add(file);
  									loader.resourcesMap.put(name, uo);
  								}
  							}
  						}
  					}
  				}
  			}
  		}
  
  		// set init state
  		CClassLoader.setInit(CClassLoader.getRootLoader());
  	} // end init()
  
  	/**
  	 * Install a custom URLStreamHandlerFactory which handle nested jar loading,
  	 * and memory url. The installation is done only if necessary.
  	 * 
  	 */
  	private static void installURLStreamHandlerFactory() {
  		synchronized (URL.class) {
  			try {
  				try {
  					new URL("yahpjarloader://test/test");
  					return;
  				}
  				catch (MalformedURLException e) {}
  				if (CClassLoader.class.getClassLoader() != ClassLoader
  						.getSystemClassLoader()) {
  					try {
  						Class c = ClassLoader
  								.getSystemClassLoader()
  								.loadClass(
  										"org.allcolor.yahp.converter.CYaHPURLStreamHandlerFactory");
  						if (c != null) {
  							return;
  						}
  					} catch (Throwable ignore) {
  					}
  				}
  				final Field factory = URL.class.getDeclaredField("factory");
  				factory.setAccessible(true);
  				final URLStreamHandlerFactory oldFactory = (URLStreamHandlerFactory) factory
  						.get(null);
  				if ((oldFactory == null)
  						|| (oldFactory.getClass().getName().indexOf(
  								"CYaHPURLStreamHandlerFactory") == -1)) {
  					synchronized (factory) {
  						System.out
  								.println("Installing new URLStreamHandlerFactory...");
  						final Method defineClass = ClassLoader.class
  								.getDeclaredMethod("defineClass", new Class[] {
  										String.class, byte[].class, int.class,
  										int.class });
  						defineClass.setAccessible(true);
  						final byte[] bauc = CClassLoader
  								.loadByteArray(CClassLoader.class
  										.getClassLoader()
  										.getResource(
  												"org/allcolor/yahp/converter/CByteArrayUrlConnection.class"));
  						final byte[] crypt = CClassLoader
  								.loadByteArray(CClassLoader.class
  										.getClassLoader()
  										.getResource(
  												"org/allcolor/yahp/converter/CCryptoUtils.class"));
  						final byte[] b64c = CClassLoader
  								.loadByteArray(CClassLoader.class
  										.getClassLoader()
  										.getResource(
  												"org/allcolor/yahp/converter/CBASE64Codec.class"));
  						final byte[] muh = CClassLoader
  								.loadByteArray(CClassLoader.class
  										.getClassLoader()
  										.getResource(
  												"org/allcolor/yahp/converter/CMemoryURLHandler.class"));
  						final byte[] juhgc = CClassLoader
  								.loadByteArray(CClassLoader.class
  										.getClassLoader()
  										.getResource(
  												"org/allcolor/yahp/converter/CJarLoaderURLStreamHandler$CGCCleaner.class"));
  						final byte[] juh = CClassLoader
  								.loadByteArray(CClassLoader.class
  										.getClassLoader()
  										.getResource(
  												"org/allcolor/yahp/converter/CJarLoaderURLStreamHandler.class"));
  						final byte[] hf = CClassLoader
  								.loadByteArray(CClassLoader.class
  										.getClassLoader()
  										.getResource(
  												"org/allcolor/yahp/converter/CYaHPURLStreamHandlerFactory.class"));
  						defineClass
  								.invoke(
  										ClassLoader.getSystemClassLoader(),
  										new Object[] {
  												"org.allcolor.yahp.converter.CByteArrayUrlConnection",
  												bauc, new Integer(0),
  												new Integer(bauc.length) });
  						defineClass.invoke(ClassLoader.getSystemClassLoader(),
  								new Object[] {
  										"org.allcolor.yahp.converter.CCryptoUtils",
  										crypt, new Integer(0),
  										new Integer(crypt.length) });
  						defineClass.invoke(ClassLoader.getSystemClassLoader(),
  								new Object[] {
  										"org.allcolor.yahp.converter.CBASE64Codec",
  										b64c, new Integer(0),
  										new Integer(b64c.length) });
  						defineClass
  								.invoke(
  										ClassLoader.getSystemClassLoader(),
  										new Object[] {
  												"org.allcolor.yahp.converter.CMemoryURLHandler",
  												muh, new Integer(0),
  												new Integer(muh.length) });
  						defineClass
  								.invoke(
  										ClassLoader.getSystemClassLoader(),
  										new Object[] {
  												"org.allcolor.yahp.converter.CJarLoaderURLStreamHandler$CGCCleaner",
  												juhgc, new Integer(0),
  												new Integer(juhgc.length) });
  						defineClass
  								.invoke(
  										ClassLoader.getSystemClassLoader(),
  										new Object[] {
  												"org.allcolor.yahp.converter.CJarLoaderURLStreamHandler",
  												juh, new Integer(0),
  												new Integer(juh.length) });
  						final Class c = (Class) defineClass
  								.invoke(
  										ClassLoader.getSystemClassLoader(),
  										new Object[] {
  												"org.allcolor.yahp.converter.CYaHPURLStreamHandlerFactory",
  												hf, new Integer(0),
  												new Integer(hf.length) });
  						final Constructor cons = c
  								.getConstructor(new Class[] { URLStreamHandlerFactory.class });
  						final URLStreamHandlerFactory fact = (URLStreamHandlerFactory) cons
  								.newInstance(new Object[] { oldFactory });
  						factory.set(null, fact);
  						System.out
  								.println("Installing new URLStreamHandlerFactory DONE.");
  					}
  				}
  			} catch (final Throwable ignore) {
  				ignore.printStackTrace();
  			}
  		}
  	}
  
  	/**
  	 * load the given inputstream in a byte array
  	 * 
  	 * @param in
  	 *            the stream to load
  	 * 
  	 * @return a byte array
  	 */
  	public static final byte[] loadByteArray(final InputStream in) {
  		try {
  			final ByteArrayOutputStream bOut = new ByteArrayOutputStream();
  
  			final byte buffer[] = new byte[2048];
  
  			int iNbByteRead = -1;
  
  			while ((iNbByteRead = in.read(buffer)) != -1) {
  				bOut.write(buffer, 0, iNbByteRead);
  			}
  
  			return bOut.toByteArray();
  		} catch (final IOException ioe) {
  			return null;
  		}
  	} // end loadByteArray()
  
  	/**
  	 * load the given url in a byte array
  	 * 
  	 * @param urlToResource
  	 *            url to load
  	 * 
  	 * @return a byte array
  	 */
  	public static final byte[] loadByteArray(final URL urlToResource) {
  		InputStream in = null;
  
  		try {
  			final URLConnection uc = urlToResource.openConnection();
  			uc.setUseCaches(false);
  			in = uc.getInputStream();
  
  			return CClassLoader.loadByteArray(in);
  		} catch (final IOException ioe) {
  			return null;
  		} finally {
  			try {
  				in.close();
  			} catch (final Exception ignore) {
  			}
  		}
  	} // end loadByteArray()
  
  	/**
  	 * load multiple resources of same name
  	 * 
  	 * @param loader
  	 *            current lookup loader
  	 * @param URLList
  	 *            list of found resources
  	 * @param resourceName
  	 *            name to match
  	 */
  	private static final void loadResources(final CClassLoader loader,
  			final List URLList, final String resourceName) {
  		final List l = loader.getPrivateResource(resourceName);
  
  		if (l != null) {
  			URLList.addAll(l);
  		}
  
  		final List loaderList = new ArrayList();
  
  		for (final Iterator it = loader.childrenMap.entrySet().iterator(); it
  				.hasNext();) {
  			final Entry entry = (Entry) it.next();
  			loaderList.add(entry.getValue());
  		}
  
  		for (int i = 0; i < loaderList.size(); i++) {
  			final Object element = loaderList.get(i);
  			final CClassLoader child = (CClassLoader) element;
  			CClassLoader.loadResources(child, URLList, resourceName);
  		}
  	} // end loadResources()
  
  	/**
  	 * log the message
  	 * 
  	 * @param Message
  	 *            message to log
  	 * @param level
  	 *            log level (INFO,DEBUG,FATAL)
  	 */
  	private static final void log(final String Message, final int level) {
  		if ((level == CClassLoader.INFO)
  				&& CClassLoader.log.isLoggable(Level.INFO)) {
  			CClassLoader.log.info(Message);
  		} else if ((level == CClassLoader.DEBUG)
  				&& CClassLoader.log.isLoggable(Level.FINE)) {
  			CClassLoader.log.fine(Message);
  		} else if ((level == CClassLoader.FATAL)
  				&& CClassLoader.log.isLoggable(Level.SEVERE)) {
  			CClassLoader.log.severe(Message);
  		}
  	} // end log()
  
  	/**
  	 * Release a previously allocated memory URL
  	 * 
  	 * @param u
  	 *            the URL to release memory
  	 */
  	public static void releaseMemoryURL(final URL u) {
  		try {
  			final Class c = ClassLoader.getSystemClassLoader().loadClass(
  					"org.allcolor.yahp.converter.CMemoryURLHandler");
  			final Method m = c.getDeclaredMethod("releaseMemoryURL",
  					new Class[] { URL.class });
  			m.setAccessible(true);
  			m.invoke(null, new Object[] { u });
  		} catch (final Exception ignore) {
  			ignore.printStackTrace();
  		}
  	}
  
  	public static final void setContextLoader(ClassLoader contextLoader) {
  		CClassLoader.contextLoader.set(new WeakReference(contextLoader));
  	}
  
  	/**
  	 * set the init flag of all loaders
  	 * 
  	 * @param loader
  	 *            the current loader
  	 */
  	private static final void setInit(final CClassLoader loader) {
  		loader.booInit = true;
  
  		for (final Iterator it = loader.getChildLoader(); it.hasNext();) {
  			final Map.Entry entry = (Map.Entry) it.next();
  			CClassLoader.setInit((CClassLoader) entry.getValue());
  		}
  	} // end setInit()
  
  	/**
  	 * Set the urlloader used to resolved getURLs
  	 * 
  	 * @param urlLoader
  	 *            the urlloader used to resolved getURLs
  	 */
  	public static void setURLClassLoader(final URLClassLoader urlLoader) {
  		CClassLoader.urlLoader = urlLoader;
  	}
  
  	/**
  	 * test if the logging level is enabled
  	 * 
  	 * @param level
  	 *            the logging level to test
  	 * 
  	 * @return true if enabled
  	 */
  	private static final boolean sl(final int level) {
  		if ((level == CClassLoader.INFO)
  				&& CClassLoader.log.isLoggable(Level.INFO)) {
  			return true;
  		} else if ((level == CClassLoader.DEBUG)
  				&& CClassLoader.log.isLoggable(Level.FINE)) {
  			return true;
  		} else if ((level == CClassLoader.FATAL)
  				&& CClassLoader.log.isLoggable(Level.SEVERE)) {
  			return true;
  		}
  
  		return false;
  	} // end sl()
  
  	/** is alone ? */
  	private boolean booAlone = false;
  
  	/** is not forwarding to parent ? */
  	private boolean booDoNotForwardToParent = false;
  
  	/** is initializing ? */
  	private boolean booInit = false;
  
  	/** is mandatory ? */
  	private boolean booMandatory = false;
  
  	/** is resource only ? */
  	private boolean booResourceOnly = false;
  
  	private final Map cacheMap = new HashMap();
  
  	/** contains reference to children loaders */
  	private final Map childrenMap = new HashMap();
  
  	/** known classes by this loader */
  	private final Map classesMap = new HashMap();
  
  	/** reference to the loader config object */
  	private CClassLoaderConfig config;
  
  	/**
  	 * Contains a name-URL of dll/.so mapping for this loader
  	 */
  	private final Map dllMap = new HashMap();
  
  	/** loader name */
  	private String name = null;
  
  	/** loader path */
  	private String path = null;
  
  	private String finalizepath = null;
  
  	/** known resources by this loader */
  	private final Map resourcesMap = new HashMap();
  
  	/**
  	 * Create a new loader
  	 * 
  	 * @param parent
  	 *            a reference to the parent loader
  	 * @param name
  	 *            loader name
  	 * 
  	 * @throws Exception
  	 *             should not happen
  	 */
  	private CClassLoader(final ClassLoader parent, final String name)
  			throws Exception {
  		super(new URL[0], parent);
  		this.name = name;
  		this.path = this.nGetLoaderPath();
  	} // end CClassLoader()
  
  	/**
  	 * Destroy instance variables.
  	 * 
  	 * @param logFactoryRelease
  	 *            method to release commons logging
  	 */
  	private final void _destroy(final Method logFactoryRelease) {
  		for (final Iterator it = this.childrenMap.entrySet().iterator(); it
  				.hasNext();) {
  			final Map.Entry entry = (Map.Entry) it.next();
  			final CClassLoader loader = (CClassLoader) entry.getValue();
  			loader._destroy(logFactoryRelease);
  			it.remove();
  		}
  
  		try {
  			// remove ref from commons logging
  			logFactoryRelease.invoke(null, new Object[] { this });
  		} catch (final Exception e) {
  		}
  
  		try {
  			// reset parent to system class loader
  			final Field parent = ClassLoader.class.getDeclaredField("parent");
  			parent.setAccessible(true);
  			parent.set(this, ClassLoader.getSystemClassLoader());
  			parent.setAccessible(false);
  		} catch (final Throwable ignore) {
  		}
  
  		this.classesMap.clear();
  		for (final Iterator it = this.dllMap.entrySet().iterator(); it
  				.hasNext();) {
  			final Object element = it.next();
  			final Map.Entry entry = (Map.Entry) element;
  			if (entry.getValue() instanceof File) {
  				((File) entry.getValue()).delete();
  			}
  		}
  		this.cacheMap.clear();
  		this.dllMap.clear();
  		this.resourcesMap.clear();
  		this.config = null;
  		this.name = null;
  		this.finalizepath = this.path;
  		this.path = null;
  		//classes
  		System.runFinalization();
  		System.gc();
  	} // end _destroy()
  
  	/**
  	 * add a class to known class
  	 * 
  	 * @param className
  	 *            class name
  	 * @param urlToClass
  	 *            url to class file
  	 */
  	public final void addClass(final String className, final URL urlToClass) {
  		if ((className == null) || (urlToClass == null)) {
  			return;
  		}
  
  		if (!this.classesMap.containsKey(className)) {
  			this.classesMap.put(className, urlToClass);
  		}
  	} // end addClass()
  
  	/**
  	 * add a resource
  	 * 
  	 * @param resouceName
  	 *            name of the resource to add
  	 * @param urlToResource
  	 *            url to the resource
  	 */
  	public final void addResource(final String resouceName,
  			final URL urlToResource) {
  		if ((urlToResource == null) || (resouceName == null)) {
  			return;
  		}
  		if (this.resourcesMap.containsKey(resouceName.replace('\\', '/'))) {
  			final Object to = this.resourcesMap.get(resouceName.replace('\\',
  					'/'));
  			if (to instanceof URL) {
  				final URL uo = (URL) to;
  				final List l = new ArrayList();
  				l.add(uo);
  				this.resourcesMap.put(resouceName.replace('\\', '/'), l);
  			} else if (to instanceof List) {
  				final List uo = (List) to;
  				uo.add(urlToResource);
  				this.resourcesMap.put(resouceName.replace('\\', '/'), uo);
  			}
  		} else {
  			this.resourcesMap
  					.put(resouceName.replace('\\', '/'), urlToResource);
  		}
  	} // end addResource()
  
  	/*
  	 * (non-Javadoc)
  	 * 
  	 * @see java.lang.ClassLoader#findClass(java.lang.String)
  	 */
  
  	protected final Class findClass(final String name)
  			throws ClassNotFoundException {
  		try {
  			if (name == null) {
  				return null;
  			}
  
  			if (name.startsWith("java.") || name.startsWith("sun.reflect")) {
  				return ClassLoader.getSystemClassLoader().loadClass(name);
  			}
  
  			final String searchClass = name.replace('.', '/') + ".class";
  			final CThreadContext context = CThreadContext.getInstance();
  			final String cpName = CClassLoader.CCLASSLOADER_NAMESPACE + name;
  
  			List lPriorLoader = (List) context.get(cpName);
  
  			if (lPriorLoader == null) {
  				lPriorLoader = new Vector();
  				context.set(cpName, lPriorLoader);
  			}
  
  			if (lPriorLoader.contains(this.toString())) {
  				return null;
  			}
  
  			lPriorLoader.add(this.toString());
  
  			if (CClassLoader.sl(CClassLoader.DEBUG)) {
  				CClassLoader.log("Searching " + name + " in " + this.getPath(),
  						CClassLoader.DEBUG);
  			}
  
  			final WeakReference cache = (WeakReference) this.cacheMap.get(name);
  			if (cache != null) {
  				final Class c = (Class) cache.get();
  				if (c != null) {
  					return c;
  				}
  			}
  
  			// first check if this class as a mandatory loader
  			if (!this.booAlone) {
  				CClassLoader loader = null;
  
  				for (final Iterator it = CClassLoader.mandatoryLoadersMap
  						.entrySet().iterator(); it.hasNext();) {
  					final Entry entry = (Entry) it.next();
  					loader = (CClassLoader) entry.getValue();
  
  					if (loader.classesMap.containsKey(searchClass)) {
  						if (loader != this) {
  							break;
  						}
  						loader = null;
  						break;
  					}
  					loader = null;
  				}
  
  				if (loader != null) {
  					final Class c = loader.findClass(name);
  					if (c != null) {
  						return c;
  					}
  				}
  			}
  
  			// second search in this repository
  			byte buffer[] = null;
  
  			final URL urlToResource = (URL) this.classesMap.get(searchClass);
  
  			if (urlToResource != null) {
  				buffer = CClassLoader.loadByteArray(urlToResource);
  			}
  
  			if (buffer != null) {
  				if (CClassLoader.sl(CClassLoader.DEBUG)) {
  					CClassLoader.log(
  							"Loaded " + name + " in " + this.getPath(),
  							CClassLoader.DEBUG);
  				}
  
  				Class c = null;
  				c = this.findLoadedClass(name);
  
  				if (c == null) {
  					try {
  						c = this.defineClass(name, buffer, 0, buffer.length);
  					} catch (final LinkageError e) {
  						c = this.findLoadedClass(name);
  
  						if (c == null) {
  							throw new ClassNotFoundException(name
  									+ " was not found !");
  						}
  					}
  				}
  
  				if (c != null) {
  					return c;
  				}
  			}
  
  			// then search in each children
  			final List tmpLoader = new ArrayList();
  
  			for (final Iterator it = this.childrenMap.entrySet().iterator(); it
  					.hasNext();) {
  				final Entry entry = (Entry) it.next();
  				final CClassLoader child = (CClassLoader) entry.getValue();
  
  				if (lPriorLoader.contains(child.toString())) {
  					continue;
  				}
  
  				tmpLoader.add(child);
  			}
  
  			for (final Iterator it = tmpLoader.iterator(); it.hasNext();) {
  				final Object element = it.next();
  				final CClassLoader child = (CClassLoader) element;
  				final Class c = child.findClass(name);
  
  				if (c != null) {
  					return c;
  				}
  			}
  
  			// then follow to parents
  			if ((this != CClassLoader.getRootLoader())
  					&& !this.booDoNotForwardToParent) {
  				if (lPriorLoader.contains(this.getParent().toString())) {
  					return null;
  				} else {
  					if (this.getParent() instanceof CClassLoader) {
  						return ((CClassLoader) this.getParent())
  								.findClass(name);
  					} else {
  						return this.getParent().loadClass(name);
  					}
  				}
  			} else {
  				try {
  					ClassLoader contextLoader = CClassLoader.getContextLoader();
  					if (contextLoader == null) {
  						contextLoader = CClassLoader.getRootLoader()
  								.getParent();
  					}
  					final Class c = contextLoader.loadClass(name);
  					if (c != null) {
  						return c;
  					}
  				} catch (Throwable e) {
  					final Class c = CClassLoader.getRootLoader().getParent()
  							.loadClass(name);
  					if (c != null) {
  						return c;
  					}
  				}
  				throw new ClassNotFoundException(name);
  			}
  		} catch (ClassNotFoundException e) {
  			throw e;
  		} catch (NoClassDefFoundError e) {
  			throw e;
  		} catch (Throwable e) {
  			throw new ClassNotFoundException(name, e);
  		}
  	} // end findClass()
  
  	/*
  	 * (non-Javadoc)
  	 * 
  	 * @see java.lang.ClassLoader#findLibrary(java.lang.String)
  	 */
  
  	protected String findLibrary(final String libname) {
  		if (!this.isInit()) {
  			if (CClassLoader.sl(CClassLoader.DEBUG)) {
  				CClassLoader
  						.log("Not initialized, forward to old loader "
  								+ libname + " in " + this.getPath(),
  								CClassLoader.DEBUG);
  			}
  
  			return super.findLibrary(libname);
  		}
  		final String system = CSystem.getName() + "/" + libname;
  		final CThreadContext context = CThreadContext.getInstance();
  		final String cpName = CClassLoader.CCLASSLOADER_NAMESPACE + system;
  		try {
  			List lPriorLoader = (List) context.get(cpName);
  
  			if (lPriorLoader == null) {
  				lPriorLoader = new Vector();
  				context.set(cpName, lPriorLoader);
  			}
  
  			if (lPriorLoader.contains(this.toString())) {
  				return null;
  			}
  
  			lPriorLoader.add(this.toString());
  
  			if (!this.booAlone) {
  				CClassLoader loader = null;
  
  				for (final Iterator it = CClassLoader.mandatoryLoadersMap
  						.entrySet().iterator(); it.hasNext();) {
  					final Entry entry = (Entry) it.next();
  					loader = (CClassLoader) entry.getValue();
  
  					if (loader.dllMap.containsKey(system)) {
  						if (loader != this) {
  							break;
  						}
  						loader = null;
  						break;
  					}
  					loader = null;
  				}
  
  				if (loader != null) {
  					final String c = loader.findLibrary(libname);
  					context.set(cpName, null);
  					return c;
  				}
  			}
  			String path = null;
  			final Object obj = this.dllMap.get(system);
  			if (obj instanceof URL) {
  				final URL urlToResource = (URL) obj;
  				final byte[] buffer = CClassLoader.loadByteArray(urlToResource);
  				final File nfile = File.createTempFile(libname
  						+ this.hashCode(), ".so");
  				final FileOutputStream fout = new FileOutputStream(nfile);
  				fout.write(buffer);
  				fout.close();
  				this.dllMap.put(system, nfile);
  				path = nfile.getAbsolutePath();
  			} else if (obj instanceof File) {
  				final File nfile = (File) obj;
  				path = nfile.getAbsolutePath();
  			}
  			if (path != null) {
  				context.set(cpName, null);
  				return path;
  			}
  
  			// then search in each children
  			final List tmpLoader = new ArrayList();
  
  			for (final Iterator it = this.childrenMap.entrySet().iterator(); it
  					.hasNext();) {
  				final Entry entry = (Entry) it.next();
  				final CClassLoader child = (CClassLoader) entry.getValue();
  
  				if (lPriorLoader.contains(child.toString())) {
  					continue;
  				}
  
  				tmpLoader.add(child);
  			}
  
  			for (final Iterator it = tmpLoader.iterator(); it.hasNext();) {
  				final Object element = it.next();
  				final CClassLoader child = (CClassLoader) element;
  				final String c = child.findLibrary(libname);
  
  				if (c != null) {
  					return c;
  				}
  			}
  
  			// then follow to parents
  			if ((this != CClassLoader.getRootLoader())
  					&& !this.booDoNotForwardToParent) {
  				if (lPriorLoader.contains(this.getParent().toString())) {
  					return null;
  				} else {
  					if (this.getParent() instanceof CClassLoader) {
  						final CClassLoader parent = (CClassLoader) this
  								.getParent();
  						return parent.findLibrary(libname);
  					} else {
  						return super.findLibrary(libname);
  					}
  				}
  			} else {
  				final String c = super.findLibrary(libname);
  				context.set(cpName, null);
  				return c;
  			}
  		} catch (final IOException e) {
  			return super.findLibrary(libname);
  		}
  	}
  
  	/*
  	 * (non-Javadoc)
  	 * 
  	 * @see java.lang.ClassLoader#findResource(java.lang.String)
  	 */
  
  	public final URL findResource(final String fname) {
  		if (!this.isInit()) {
  			if (CClassLoader.sl(CClassLoader.DEBUG)) {
  				CClassLoader.log("Not initialized, forward to old loader "
  						+ fname + " in " + this.getPath(), CClassLoader.DEBUG);
  			}
  
  			if ((Thread.currentThread().getContextClassLoader() != null)
  					&& (Thread.currentThread().getContextClassLoader() != this)) {
  				return Thread.currentThread().getContextClassLoader()
  						.getResource(fname);
  			}
  
  			return CClassLoader.getRootLoader().getParent().getResource(fname);
  		}
  
  		String name = fname;
  
  		if (name.startsWith("/")) {
  			name = name.substring(1);
  		}
  
  		final CThreadContext context = CThreadContext.getInstance();
  		final String cpName = CClassLoader.CCLASSLOADER_NAMESPACE + name;
  		List lPriorLoader = (List) context.get(cpName);
  
  		if (lPriorLoader == null) {
  			lPriorLoader = new Vector();
  			context.set(cpName, lPriorLoader);
  		}
  
  		if (lPriorLoader.contains(this.toString())) {
  			return null;
  		}
  
  		lPriorLoader.add(this.toString());
  
  		if (CClassLoader.sl(CClassLoader.DEBUG)) {
  			CClassLoader.log("Searching " + name + " in " + this.getPath(),
  					CClassLoader.DEBUG);
  		}
  
  		// first check if this resource as a mandatory loader
  		URL urlToResource = null;
  
  		if (!this.booAlone) {
  			CClassLoader loader = null;
  
  			for (final Iterator it = CClassLoader.mandatoryLoadersMap
  					.entrySet().iterator(); it.hasNext();) {
  				final Entry entry = (Entry) it.next();
  				loader = (CClassLoader) entry.getValue();
  
  				if (loader.resourcesMap.containsKey(name)) {
  					if (loader != this) {
  						break;
  					}
  					loader = null;
  					break;
  				}
  				loader = null;
  			}
  
  			if (loader != null) {
  				urlToResource = loader.getResource(name);
  			}
  
  			if (urlToResource != null) {
  				context.set(cpName, null);
  
  				return urlToResource;
  			}
  		}
  
  		final Object to = this.resourcesMap.get(name);
  		if (to instanceof URL) {
  			// second search in this repository
  			urlToResource = (URL) to;
  		} else if (to instanceof List) {
  			final List l = (List) to;
  			urlToResource = (URL) l.get(0);
  		}
  
  		if (urlToResource != null) {
  			if (CClassLoader.sl(CClassLoader.DEBUG)) {
  				CClassLoader.log("Loaded " + name + " in " + this.getPath(),
  						CClassLoader.DEBUG);
  			}
  
  			context.set(cpName, null);
  
  			return urlToResource;
  		}
  
  		// then search in each children
  		final List tmpLoader = new ArrayList();
  
  		for (final Iterator it = this.childrenMap.entrySet().iterator(); it
  				.hasNext();) {
  			final Entry entry = (Entry) it.next();
  			final CClassLoader child = (CClassLoader) entry.getValue();
  
  			if (lPriorLoader.contains(child.toString())) {
  				continue;
  			}
  
  			tmpLoader.add(child);
  		}
  
  		for (final Iterator it = tmpLoader.iterator(); it.hasNext();) {
  			final Object element = it.next();
  			final CClassLoader child = (CClassLoader) element;
  			urlToResource = child.getResource(name);
  
  			if (urlToResource != null) {
  				return urlToResource;
  			}
  		}
  
  		// then follow to parents
  		if ((this != CClassLoader.getRootLoader())
  				&& !this.booDoNotForwardToParent) {
  			if (lPriorLoader.contains(this.getParent().toString())) {
  				return null;
  			} else {
  				if (this.getParent() == null) {
  					return null;
  				}
  
  				return this.getParent().getResource(name);
  			}
  		} else {
  			urlToResource = CClassLoader.getRootLoader().getParent()
  					.getResource(name);
  			context.set(cpName, null);
  
  			return urlToResource;
  		}
  	} // end findResource()
  
  	/*
  	 * (non-Javadoc)
  	 * 
  	 * @see java.lang.ClassLoader#findResources(java.lang.String)
  	 */
  
  	public final Enumeration findResources(final String name)
  			throws IOException {
  		final CClassLoader loader = CClassLoader.getRootLoader();
  		final List URLList = new ArrayList();
  
  		try {
  			CClassLoader.loadResources(loader, URLList, name);
  		} finally {
  			try {
  
  			} catch (final Exception ignore) {
  			}
  		}
  
  		try {
  			final Enumeration eu = CClassLoader.getRootLoader().getParent()
  					.getResources(name);
  			while (eu.hasMoreElements()) {
  				URLList.add(eu.nextElement());
  			}
  		} catch (final Throwable ignore) {
  		}
  
  		final Iterator it = URLList.iterator();
  
  		return new Enumeration() {
  			Iterator internalIt = it;
  
  			public boolean hasMoreElements() {
  				return this.internalIt.hasNext();
  			} // end hasMoreElements()
  
  			public Object nextElement() {
  				return this.internalIt.next();
  			} // end nextElement()
  		} // end new
  		;
  	} // end findResources()
  
  	/**
  	 * return an iterator on children loaders
  	 * 
  	 * @return an iterator on children loaders
  	 */
  	public final Iterator getChildLoader() {
  		return this.childrenMap.entrySet().iterator();
  	} // end getChildLoader()
  
  	/**
  	 * return a copy of the known classes
  	 * 
  	 * @return a copy of the known classes
  	 */
  	public final Map getClassesMap() {
  		return Collections.unmodifiableMap(this.classesMap);
  	} // end getClassesMap()
  
  	/**
  	 * Return the child loader with the given name
  	 * 
  	 * @param name
  	 *            name of the child to get
  	 * 
  	 * @return the child loader with the given name
  	 */
  	private final CClassLoader getLoaderByName(final String name) {
  		try {
  
  			CClassLoader loader = (CClassLoader) this.childrenMap.get(name);
  
  			if (loader == null) {
  				loader = CClassLoader.createLoader(this, name);
  				this.childrenMap.put(name, loader);
  			}
  
  			return loader;
  		} finally {
  			try {
  
  			} catch (final Exception ignore) {
  			}
  		}
  	} // end getLoaderByName()
  
  	/**
  	 * Returns the name.
  	 * 
  	 * @return Returns the name.
  	 */
  	public final String getName() {
  		return this.name;
  	} // end getName()
  
  	/**
  	 * Returns the path.
  	 * 
  	 * @return Returns the path.
  	 */
  	public final String getPath() {
  		return this.path;
  	} // end getPath()
  
  	/**
  	 * get the resource with the given name
  	 * 
  	 * @param name
  	 *            name of the resource to get
  	 * 
  	 * @return the resource with the given name
  	 */
  	private final List getPrivateResource(final String name) {
  		try {
  			final Object to = this.resourcesMap.get(name);
  			final List list = new ArrayList();
  			if (to instanceof URL) {
  				list.add((URL) to);
  				return list;
  			} else if (to instanceof List) {
  				final List l = (List) to;
  				for (int i = 0; i < l.size(); i++) {
  					list.add((URL) l.get(i));
  				}
  				return list;
  			} else {
  				return null;
  			}
  		} finally {
  			try {
  
  			} catch (final Exception ignore) {
  			}
  		}
  	} // end getPrivateResource()
  
  	/*
  	 * (non-Javadoc)
  	 * 
  	 * @see java.lang.ClassLoader#getResource(java.lang.String)
  	 */
  
  	public final URL getResource(final String name) {
  		return this.findResource(name);
  	} // end getResource()
  
  	public final InputStream getResourceAsStream(final String name) {
  		try {
  			return this.getResource(name).openStream();
  		} catch (final Exception ioe) {
  			return null;
  		}
  	} // end getResourceAsStream()
  
  	/*
  	 * 
  	 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!! This method is only possible in 1.5 vm upward.
  	 * SHAME ON SUN
  	 * 
  	public Enumeration getResources(String name) throws IOException {
  		if (this != CClassLoader.getRootLoader()) {
  			return CClassLoader.getRootLoader().getResources(name);
  		}
  		final CThreadContext context = CThreadContext.getInstance();
  		Object obj = context.get(CClassLoader.CCLASSLOADER_NAMESPACE + name
  				+ this.getPath());
  		if (obj != null) {
  			return new Vector().elements();
  		}
  		context
  				.set(CClassLoader.CCLASSLOADER_NAMESPACE + name
  						+ this.getPath(), "");
  		try {
  			return this.findResources(name);
  		} finally {
  			context.set(CClassLoader.CCLASSLOADER_NAMESPACE + name
  					+ this.getPath(), null);
  		}
  	}
  	*/
  
  	/**
  	 * return a copy of the known resources
  	 * 
  	 * @return a copy of the known resources
  	 */
  	public final Map getResourcesMap() {
  		return Collections.unmodifiableMap(this.resourcesMap);
  	} // end getResourcesMap()
  
  	/*
  	 * (non-Javadoc)
  	 * 
  	 * @see java.net.URLClassLoader#getURLs()
  	 */
  
  	public URL[] getURLs() {
  		return CClassLoader.urlLoader != null ? CClassLoader.urlLoader
  				.getURLs() : new URL[0];
  	} // end getURLs()
  
  	/**
  	 * Returns the booAlone.
  	 * 
  	 * @return Returns the booAlone.
  	 */
  	public final boolean isAlone() {
  		return this.booAlone;
  	} // end isAlone()
  
  	/**
  	 * return booDoNotForwardToParent
  	 * 
  	 * @return booDoNotForwardToParent
  	 */
  	public final boolean isForwardingToParent() {
  		return !this.booDoNotForwardToParent;
  	} // end isForwardingToParent()
  
  	/**
  	 * Returns the booInit.
  	 * 
  	 * @return Returns the booInit.
  	 */
  	public final boolean isInit() {
  		return this.booInit;
  	} // end isInit()
  
  	/**
  	 * Returns the booMandatory.
  	 * 
  	 * @return Returns the booMandatory.
  	 */
  	public final boolean isMandatory() {
  		return this.booMandatory;
  	} // end isMandatory()
  
  	/**
  	 * Returns the booResourceOnly.
  	 * 
  	 * @return Returns the booResourceOnly.
  	 */
  	public final boolean isResourceOnly() {
  		return this.booResourceOnly;
  	} // end isResourceOnly()
  
  	/*
  	 * (non-Javadoc)
  	 * 
  	 * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
  	 */
  
  	protected final Class loadClass(final String name, final boolean resolve)
  			throws ClassNotFoundException {
  		final CThreadContext context = CThreadContext.getInstance();
  		try {
  			Class c = null;
  			final WeakReference cache = (WeakReference) this.cacheMap.get(name);
  			if (cache != null) {
  				c = (Class) cache.get();
  			}
  
  			if (c == null) {
  				// First, check if the class has already been loaded
  				c = this.findLoadedClass(name);
  				if (c == null) {
  					c = this.findClass(name);
  					this.cacheMap.put(name, new WeakReference(c));
  				}
  			}
  			if (resolve) {
  				this.resolveClass(c);
  			}
  
  			return c;
  		} finally {
  			context.set(CClassLoader.CCLASSLOADER_NAMESPACE + name, null);
  			try {
  
  			} catch (final Exception ignore) {
  			}
  		}
  	} // end loadClass()
  
  	/**
  	 * calculate the loader path
  	 * 
  	 * @return the calculated path
  	 */
  	private final String nGetLoaderPath() {
  		ClassLoader currentLoader = this;
  		final StringBuffer buffer = new StringBuffer();
  		buffer.append(this.name);
  
  		while ((currentLoader = currentLoader.getParent()) != null) {
  			if (currentLoader.getClass() == CClassLoader.class) {
  				buffer.insert(0, "/");
  				buffer.insert(0, ((CClassLoader) currentLoader).name);
  			} else {
  				break;
  			}
  		}
  
  		return buffer.toString();
  	} // end nGetLoaderPath()
  
  	/**
  	 * analyse the content of the given jar file
  	 * 
  	 * @param jarFile
  	 *            the jar to analise
  	 */
  	private final void readDirectories(final URL jarFile) {
  		JarInputStream jarIn = null;
  
  		try {
  			if (!jarFile.getPath().endsWith(".jar")) {
  				return;
  			}
  			if (CClassLoader.sl(CClassLoader.DEBUG)) {
  				CClassLoader.log("opening jar : " + jarFile.toExternalForm(),
  						CClassLoader.DEBUG);
  			}
  			jarIn = new JarInputStream(jarFile.openStream());
  			JarEntry jarEntry = null;
  
  			while ((jarEntry = jarIn.getNextJarEntry()) != null) {
  				if (jarEntry.isDirectory()) {
  					continue;
  				}
  
  				final URL url = new URL("yahpjarloader://"
  						+ CBASE64Codec.encode(
  								jarFile.toExternalForm().getBytes("utf-8"))
  								.replaceAll("\n", "") + "/"
  						+ jarEntry.getName());
  
  				if (CClassLoader.sl(CClassLoader.DEBUG)) {
  					CClassLoader.log("found entry : " + url.toString(),
  							CClassLoader.DEBUG);
  				}
  
  				if (jarEntry.getName().endsWith(".class")) {
  					if (!this.classesMap.containsKey(jarEntry.getName())) {
  						if (!this.booResourceOnly) {
  							this.classesMap.put(jarEntry.getName(), url);
  						}
  					}
  
  					if (this.resourcesMap.containsKey(jarEntry.getName())) {
  						final Object to = this.resourcesMap.get(jarEntry
  								.getName());
  						if (to instanceof URL) {
  							final URL uo = (URL) to;
  							final List l = new ArrayList();
  							l.add(uo);
  							l.add(url);
  							this.resourcesMap.put(jarEntry.getName(), l);
  						} else if (to instanceof List) {
  							final List uo = (List) to;
  							uo.add(url);
  							this.resourcesMap.put(jarEntry.getName(), uo);
  						}
  					} else {
  						this.resourcesMap.put(jarEntry.getName(), url);
  					}
  				} else if (jarEntry.getName().startsWith("native/")) {
  					String system = jarEntry.getName().substring(7);
  					system = system.substring(0, system.indexOf('/'));
  					if (!this.dllMap.containsKey(system)) {
  						this.dllMap.put(system, url);
  					}
  					if (this.resourcesMap.containsKey(jarEntry.getName())) {
  						final Object to = this.resourcesMap.get(jarEntry
  								.getName());
  						if (to instanceof URL) {
  							final URL uo = (URL) to;
  							final List l = new ArrayList();
  							l.add(uo);
  							l.add(url);
  							this.resourcesMap.put(jarEntry.getName(), l);
  						} else if (to instanceof List) {
  							final List uo = (List) to;
  							uo.add(url);
  							this.resourcesMap.put(jarEntry.getName(), uo);
  						}
  					} else {
  						this.resourcesMap.put(jarEntry.getName(), url);
  					}
  				} else {
  					if (this.resourcesMap.containsKey(jarEntry.getName())) {
  						final Object to = this.resourcesMap.get(jarEntry
  								.getName());
  						if (to instanceof URL) {
  							final URL uo = (URL) to;
  							final List l = new ArrayList();
  							l.add(uo);
  							l.add(url);
  							this.resourcesMap.put(jarEntry.getName(), l);
  						} else if (to instanceof List) {
  							final List uo = (List) to;
  							uo.add(url);
  							this.resourcesMap.put(jarEntry.getName(), uo);
  						}
  					} else {
  						this.resourcesMap.put(jarEntry.getName(), url);
  					}
  				}
  			}
  
  			if (CClassLoader.sl(CClassLoader.DEBUG)) {
  				CClassLoader.log("opening jar : "
  						+ jarFile.getFile().toString() + " done.",
  						CClassLoader.DEBUG);
  			}
  		} catch (final MalformedURLException mue) {
  			mue.printStackTrace();
  
  			if (CClassLoader.sl(CClassLoader.FATAL)) {
  				CClassLoader.log(mue.getMessage(), CClassLoader.FATAL);
  			}
  		} catch (final IOException ioe) {
  			ioe.printStackTrace();
  
  			if (CClassLoader.sl(CClassLoader.FATAL)) {
  				CClassLoader.log(ioe.getMessage(), CClassLoader.FATAL);
  			}
  		} catch (final Exception e) {
  			e.printStackTrace();
  
  			if (CClassLoader.sl(CClassLoader.FATAL)) {
  				CClassLoader.log(e.getMessage(), CClassLoader.FATAL);
  			}
  		} finally {
  			try {
  				jarIn.close();
  				jarIn = null;
  			} catch (final Exception e) {
  			}
  		}
  	} // end readDirectories()
  
  	/**
  	 * reload this loader
  	 */
  	public final void reload() {
  		this.reload(this.config);
  	} // end reload()
  
  	/**
  	 * reload this loader
  	 * 
  	 * @param config
  	 *            a loader config object
  	 */
  	public final void reload(final CClassLoaderConfig config) {
  		if (this == CClassLoader.getRootLoader()) {
  			return;
  		}
  
  		if (config == null) {
  			return;
  		}
  
  		final CClassLoader parent = ((CClassLoader) this.getParent());
  		parent.removeLoader(this.name);
  
  		if (this.isMandatory()) {
  			CClassLoader.mandatoryLoadersMap.remove(this.getPath());
  		}
  
  		final CClassLoader newLoader = CClassLoader.getLoader(parent.getPath()
  				+ "/" + this.name);
  		newLoader.config = this.config;
  		newLoader.booAlone = this.booAlone;
  		newLoader.booMandatory = this.booMandatory;
  		newLoader.booResourceOnly = this.booResourceOnly;
  		newLoader.booDoNotForwardToParent = this.booDoNotForwardToParent;
  
  		final List list = (List) config.getFilesMap().get(this.path);
  
  		for (final Iterator f = list.iterator(); f.hasNext();) {
  			final URL file = (URL) f.next();
  			newLoader.readDirectories(file);
  		}
  
  		final Iterator it = this.childrenMap.keySet().iterator();
  
  		while (it.hasNext()) {
  			final CClassLoader child = (CClassLoader) this.childrenMap.get(it
  					.next());
  			child.reload();
  		}
  
  		this._destroy(null);
  
  		if (newLoader.isMandatory()) {
  			CClassLoader.mandatoryLoadersMap
  					.put(newLoader.getPath(), newLoader);
  		}
  
  		newLoader.booInit = true;
  	} // end reload()
  
  	/**
  	 * remove the given loader from this loader
  	 * 
  	 * @param loaderToRemove
  	 *            name of the loader to remove. NOT NULL.
  	 * 
  	 * @return the removed loader or null.
  	 * 
  	 * @throws NullPointerException
  	 *             if loaderToRemove is null or a zero length/blank string
  	 */
  	public final CClassLoader removeLoader(final String loaderToRemove) {
  		if (loaderToRemove.trim().length() == 0) {
  			throw new NullPointerException();
  		}
  
  		return (CClassLoader) this.childrenMap.remove(loaderToRemove);
  	} // end removeLoader()
  
  	/**
  	 * remove the given resource name from known resource
  	 * 
  	 * @param resourceName
  	 *            name of the resource to remove
  	 */
  	public final void removeResource(final String resourceName) {
  		if (resourceName == null) {
  			return;
  		}
  
  		this.resourcesMap.remove(resourceName.replace('\\', '/'));
  	} // end removeResource()
  
  	public String toString() {
  		return this.getPath();
  	}
  } // end CClassLoader
  
  