package org.kevoree.kcl.impl; import org.kevoree.kcl.api.FlexyClassLoader; import org.kevoree.kcl.api.IndexDB; import org.kevoree.kcl.api.ResolutionPriority; import org.kevoree.log.Log; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.*; import java.util.concurrent.ConcurrentHashMap; public class FlexyClassLoaderImpl extends FlexyClassLoader { static { ClassLoader.registerAsParallelCapable(); } @Override public Class loadClass(String className) throws ClassNotFoundException { return loadClass(className, true); } public Class loadLocalOnly(String className) throws ClassNotFoundException { Class result = getLoadedClass(className); if (result == null) { byte[] bytes = loadClassBytes(className); if (bytes != null) { result = getLoadedClass(className); if (result == null) { result = internal_defineClass(className, bytes); } } } return result; } @Override public Class loadClass(String className, boolean resolveIt) throws ClassNotFoundException { KlassLoadRequest request = new KlassLoadRequest(); request.className = className; Class result = internal_loadClass(request); //Still not found, if thread current thread is a FlexyClassLoader try also if (result == null) { ClassLoader threadContextCL = Thread.currentThread().getContextClassLoader(); if (threadContextCL instanceof FlexyClassLoaderImpl) { FlexyClassLoaderImpl castedCL = (FlexyClassLoaderImpl) threadContextCL; if (!request.passedKlassLoader.contains((castedCL).getKey())) { result = (castedCL).internal_loadClass(request); } } } if (result == null && resolutionPriority.equals(ResolutionPriority.CHILDS)) { ClassLoader parentCL = getParent(); if (parentCL != null && parentCL != ClassLoader.getSystemClassLoader()) { if (parentCL instanceof FlexyClassLoaderImpl) { FlexyClassLoaderImpl parentCastedCL = (FlexyClassLoaderImpl) parentCL; result = parentCastedCL.internal_loadClass(request); } else { try { result = parentCL.loadClass(request.className); } catch (ClassNotFoundException e) { } } } if (result == null) { ClassLoader current = this.getClass().getClassLoader(); if (current != parentCL) { if (current instanceof FlexyClassLoaderImpl) { FlexyClassLoaderImpl currentCasted = (FlexyClassLoaderImpl) current; result = currentCasted.internal_loadClass(request); } else { try { result = current.loadClass(request.className); } catch (ClassNotFoundException e) { } } } } } //If still not found and not system basic class, lets try again if (result == null && !request.className.startsWith("java") && !request.className.startsWith("javax")/*dangerous optimization*/) { try { result = findSystemClass(request.className); } catch (ClassNotFoundException e) { } } if (result == null) { if (Log.TRACE) { Log.trace("KCL Class not resolved " + className + " from " + this.key); Log.trace("Passed FlexClassLoader, childs : " + getSubClassLoaders().size()); for (String klassLoader : request.passedKlassLoader) { Log.trace("-->" + klassLoader); } if (Thread.currentThread().getContextClassLoader() instanceof FlexyClassLoader) { Log.trace("Thread current KCL: {}", ((FlexyClassLoader) Thread.currentThread().getContextClassLoader()).getKey()); } else { if (Thread.currentThread().getContextClassLoader() != null) { Log.trace("Thread current : {}", Thread.currentThread().getContextClassLoader().toString()); } else { Log.trace("Thread current CL is null !"); } } } throw new ClassNotFoundException(className); } return result; } @Override public void load(File directory) throws IOException { if (directory != null) { classpathResources.loadJar(directory); } else { Log.error("Can't add null stream"); } } @Override public void attachChild(FlexyClassLoader child) { if (child != null) { if (!locked) { if (!subClassLoaders.contains(child)) { subClassLoaders.add(child); } } } else { Log.error("Can't add null child classloader"); } } @Override public void detachChild(FlexyClassLoader child) { if (child != null) { cleanupLinks(child); } } protected IndexDB classpathResources = null; public FlexyClassLoaderImpl() { classpathResources = new JarIndexDB(this); } public FlexyClassLoaderImpl(IndexDB db) { classpathResources = db; } /* Native Library Management */ private HashMap<String, String> nativeMap = new HashMap<String, String>(); /* Explicitly declare Native Mapping */ public void addNativeMapping(String name, String url) { nativeMap.put(name, url); } /* Check if explicit mapping found, otherwise call standard method */ @Override public String findLibrary(String p1) { System.err.println("FindLib=" + p1); //TODO automatic resolution of all .so, .dll, dylib, etc .... if (nativeMap.containsKey(p1)) { return nativeMap.get(p1); } else { return super.findLibrary(p1); } } /* End Native Library Management */ /* Special Loader management */ public ArrayList<SpecialLoader> specialloaders = new ArrayList<SpecialLoader>(); protected void addSpecialLoaders(SpecialLoader l) { specialloaders.add(l); } public ArrayList<SpecialLoader> getSpecialLoaders() { return specialloaders; } /* End Special Loader management */ protected ArrayList<FlexyClassLoader> subClassLoaders = new ArrayList<FlexyClassLoader>(); public void cleanupLinks(ClassLoader c) { //TODO CHECK USED subClassLoaders.remove(c); } public byte[] loadClassBytes(String className) { String className2 = formatClassName(className); return classpathResources.getClassBytes(className2); } private String formatClassName(String className) { String classNameT = className.replace('/', '~'); classNameT = classNameT.replace('.', '/') + ".class"; classNameT = classNameT.replace('~', '/'); return classNameT; } private boolean locked = false; public void lockLinks() { locked = true; } public boolean isLocked() { return locked; } @Override public List<FlexyClassLoader> getSubClassLoaders() { return subClassLoaders; } private Map<Integer, Integer> scoreMap = new ConcurrentHashMap<Integer, Integer>(); private int getScore(ClassLoader kcl) { if (scoreMap.containsKey(kcl.hashCode())) { return scoreMap.get(kcl.hashCode()); } else { return 0; } } private Integer incScore(ClassLoader kcl) { scoreMap.put(kcl.hashCode(), getScore(kcl) + 1); return scoreMap.get(kcl.hashCode()); } public Class getLoadedClass(String className) { return findLoadedClass(className); } protected synchronized Class internal_defineClass(String className, byte[] bytes) { if (className.contains(".")) { String packageName = className.substring(0, className.lastIndexOf('.')); if (getPackage(packageName) == null) { try { definePackage(packageName, null, null, null, null, null, null, null); } catch (Throwable e) { Log.debug("Error while defining package ", e); } } } Class clazz = getLoadedClass(className); if (clazz == null) { return defineClass(className, bytes, 0, bytes.length); } return clazz; } private Comparator scoreSorter = new Comparator<FlexyClassLoader>() { public boolean equals(Object p0) { throw new UnsupportedOperationException(); } public int compare(FlexyClassLoader p0, FlexyClassLoader p1) { if (getScore(p0) == getScore(p1)) { return 0; } if (getScore(p0) > getScore(p1)) { return 1; } return -1; } }; /* private void checkSubClassloadersSorted() { if(subClassLoaderModified) { Collections.sort(subClassLoaders, scoreSorter); subClassLoaderModified = false; } }*/ public Class graphLoadClass(KlassLoadRequest request) { //cut graph cyclic search Class result = null; ArrayList<FlexyClassLoader> tempSubs = new ArrayList(subClassLoaders); Collections.sort(tempSubs, scoreSorter); for (ClassLoader subCL : tempSubs) { if (subCL instanceof FlexyClassLoader) { if (!request.passedKlassLoader.contains(((FlexyClassLoader) subCL).getKey())) { FlexyClassLoaderImpl subKCL = (FlexyClassLoaderImpl) subCL; result = subKCL.internal_loadClass(request); } } else { try { result = subCL.loadClass(request.className); } catch (ClassNotFoundException nf) { //workaround take hashCode as Key request.passedKlassLoader.add("" + subCL.hashCode()); return null; } } if (result != null) { incScore(subCL); return result; } } return result; } public Class internal_loadClass(KlassLoadRequest request) { Class result = null; //if system class try directly if (request.className.startsWith("java") || request.className.startsWith("javax")) { try { result = findSystemClass(request.className); } catch (ClassNotFoundException e) { } } //if still not found try local load if (result == null) { result = getLoadedClass(request.className); if (result == null) { byte[] bytes = loadClassBytes(request.className); if (bytes != null) { result = getLoadedClass(request.className); if (result == null) { result = internal_defineClass(request.className, bytes); } } } } request.passedKlassLoader.add(getKey()); //TODO check if there no risk of cycle if (result == null && resolutionPriority.equals(ResolutionPriority.PARENT)) { ClassLoader parentCL = getParent(); if (parentCL != null) { if (parentCL instanceof FlexyClassLoaderImpl) { FlexyClassLoaderImpl parentCastedCL = (FlexyClassLoaderImpl) parentCL; result = parentCastedCL.internal_loadClass(request); } else { try { result = parentCL.loadClass(request.className); } catch (ClassNotFoundException e) { } } } if (result == null) { ClassLoader current = this.getClass().getClassLoader(); if (current != parentCL) { if (current instanceof FlexyClassLoaderImpl) { FlexyClassLoaderImpl currentCasted = (FlexyClassLoaderImpl) current; result = currentCasted.internal_loadClass(request); } else { try { result = current.loadClass(request.className); } catch (ClassNotFoundException e) { } } } } } //if still not found try to call to entire graph if (result == null) { result = graphLoadClass(request); } return result; } public InputStream getResourceAsStream(String name) { FlexyClassLoaderImpl resolved = resourceOwnerResolution(name); if (resolved != null) { return resolved.internal_getResourceAsStream(name); } else { return null; } } @Override protected URL findResource(String name) { FlexyClassLoaderImpl resolved = resourceOwnerResolution(name); if (resolved != null) { URL resolvedURL = resolved.internal_getResource(name); return resolvedURL; } else { return null; } } public URL getResource(String s) { return findResource(s); } protected InputStream internal_getResourceAsStream(String name) { List<URL> urls = this.classpathResources.get(name); if (urls != null) { try { return urls.get(0).openStream(); } catch (IOException e) { Log.error("Error in KCL while opening URL ", e); return null; } } else { return null; } } protected URL internal_getResource(String name) { List<URL> urls = this.classpathResources.get(name); if (urls != null) { return urls.get(0); } else { return null; } } private List<URL> internal_getResources(String name) { List<URL> urls = this.classpathResources.get(name); if (urls == null) { urls = new ArrayList<URL>(); } return urls; } @Override public java.util.Enumeration<URL> findResources(String name) throws IOException { List<URL> selfRes = new ArrayList<URL>(); List<FlexyClassLoaderImpl> potentials = resourcesOwnerResolution(name); for (FlexyClassLoaderImpl sub : potentials) { selfRes.addAll(sub.internal_getResources(name)); } /* System.out.println("findResources "+name +" - "+getKey()+"-"+Thread.currentThread().getName()); for(int i=0;i<selfRes.size();i++){ System.out.println(" ->"+selfRes.get(i).toString()+"-"+selfRes.get(i).openStream().available()); } System.out.println(" endFindResources "+name); */ return Collections.enumeration(selfRes); } /* Key Management */ private String key = UUID.randomUUID().toString(); public void setKey(String k) { key = k; } public String getKey() { return key; } private FlexyClassLoaderImpl resourceOwnerResolution(String name) { KlassLoadRequest request = new KlassLoadRequest(); request.className = name; return graphResourceOwnerResolution(request); } public FlexyClassLoaderImpl graphResourceOwnerResolution(KlassLoadRequest request) { request.passedKlassLoader.add(this.getKey()); if ((classpathResources).contains(request.className)) { return this; } FlexyClassLoaderImpl result = null; ClassLoader threadContextCL = Thread.currentThread().getContextClassLoader(); if (threadContextCL instanceof FlexyClassLoaderImpl) { FlexyClassLoaderImpl castedCL = (FlexyClassLoaderImpl) threadContextCL; if (!request.passedKlassLoader.contains((castedCL).getKey())) { result = castedCL.graphResourceOwnerResolution(request); } } if(result != null){ return result; } ArrayList<FlexyClassLoader> tempSubs = new ArrayList(subClassLoaders); Collections.sort(tempSubs, scoreSorter); for (FlexyClassLoader subCL : tempSubs) { if (!request.passedKlassLoader.contains((subCL).getKey())) { FlexyClassLoaderImpl subKCL = (FlexyClassLoaderImpl) subCL; result = subKCL.graphResourceOwnerResolution(request); } if (result != null) { incScore(subCL); return result; } } return null; } private List<FlexyClassLoaderImpl> resourcesOwnerResolution(String name) { KlassLoadRequest request = new KlassLoadRequest(); request.className = name; return graphResourcesOwnerResolution(request); } public List<FlexyClassLoaderImpl> graphResourcesOwnerResolution(KlassLoadRequest request) { List<FlexyClassLoaderImpl> result = new ArrayList<FlexyClassLoaderImpl>(); request.passedKlassLoader.add(this.getKey()); if ((classpathResources).contains(request.className)) { result.add(this); } //go to current thread class loader ClassLoader threadContextCL = Thread.currentThread().getContextClassLoader(); if (threadContextCL instanceof FlexyClassLoaderImpl) { FlexyClassLoaderImpl castedCL = (FlexyClassLoaderImpl) threadContextCL; if (!request.passedKlassLoader.contains((castedCL).getKey())) { result.addAll(castedCL.graphResourcesOwnerResolution(request)); } } ArrayList<FlexyClassLoader> tempSubs = new ArrayList(subClassLoaders); Collections.sort(tempSubs, scoreSorter); for (FlexyClassLoader subCL : tempSubs) { if (!request.passedKlassLoader.contains((subCL).getKey())) { FlexyClassLoaderImpl subKCL = (FlexyClassLoaderImpl) subCL; result.addAll(subKCL.graphResourcesOwnerResolution(request)); } } return result; } }