package org.kevoree.microkernel.impl; import org.kevoree.kcl.api.FlexyClassLoader; import org.kevoree.kcl.api.FlexyClassLoaderFactory; import org.kevoree.kcl.api.ResolutionPriority; import org.kevoree.kcl.impl.FlexyClassLoaderImpl; import org.kevoree.log.Log; import org.kevoree.microkernel.BootInfo; import org.kevoree.microkernel.BootInfoLine; import org.kevoree.microkernel.KevoreeKernel; import org.kevoree.resolver.MavenResolver; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * Created by duke on 8/12/14. */ public class KevoreeMicroKernelImpl implements KevoreeKernel { private Map<String, FlexyClassLoader> classloaders = new ConcurrentHashMap<String, FlexyClassLoader>(); private MavenResolver resolver = new MavenResolver(); private static final String centralURL = "http://repo1.maven.org/maven2"; private static final String ossURL = "https://oss.sonatype.org/content/groups/public"; private FlexyClassLoaderImpl system; private ThreadGroup threadGroup; public KevoreeMicroKernelImpl() { system = new FlexyClassLoaderImpl(); threadGroup = new ThreadGroup("KevoreeKernel.TG"); system.lockLinks(); system.setKey("kcl://system"); } @Override public FlexyClassLoader get(String key) { if (key.contains("org.kevoree.log:org.kevoree.log:") || key.contains("org.kevoree.kcl:org.kevoree.kcl:") || key.contains("org.kevoree.kcl:org.kevoree.kcl.microkernel:") || key.contains("org.kevoree:org.kevoree.maven.resolver:")) { return system; } return classloaders.get(key); } @Override public FlexyClassLoader put(String key, File in) { FlexyClassLoader cached = get(key); if (cached != null) { return cached; } FlexyClassLoader newKCL = FlexyClassLoaderFactory.INSTANCE.create(); newKCL.resolutionPriority = ResolutionPriority.CHILDS; newKCL.setKey(key); if (in.isDirectory()) { try { newKCL.load(in); } catch (IOException e) { Log.error("Error while open param file in KevoreeMicroKernel", e); return null; } } else { try { newKCL.load(in); } catch (Exception e) { Log.error("Error while open param file in KevoreeMicroKernel", e); return null; } } classloaders.put(key, newKCL); return newKCL; } @Override public void drop(String key) { FlexyClassLoaderImpl kcl = (FlexyClassLoaderImpl) classloaders.get(key); if (kcl != null) { if (kcl.isLocked()) { return; } else { classloaders.remove(key); for (FlexyClassLoader subs : getClassLoaders()) { subs.detachChild(kcl); } } } } public Set<String> getSnapshotURLS() { Set<String> inUseURLS = new HashSet<String>(); inUseURLS.add(ossURL); inUseURLS.add(centralURL); return inUseURLS; } public Set<String> getReleaseURLS() { Set<String> inUseURLS = new HashSet<String>(); inUseURLS.add(centralURL); return inUseURLS; } @Override public FlexyClassLoader install(String key, String mavenURL) { if (mavenURL.equals(system.getKey())) { classloaders.put(key, system); } FlexyClassLoader cached = get(key); if (cached != null) { return cached; } File resolved; //resolve file: if (mavenURL.startsWith("file:")) { resolved = new File(mavenURL.substring(5)); if (resolved.exists()) { return put(key, resolved); } else { Log.error("Bad file descriptor {}", mavenURL.substring(5)); } } //resolve mvn: if (mavenURL.endsWith("SNAPSHOT")) { resolved = resolver.resolve(mavenURL, getSnapshotURLS()); } else { resolved = resolver.resolve(mavenURL, getReleaseURLS()); } if (resolved != null) { return put(key, resolved); } else { return null; } } @Override public MavenResolver getResolver() { return resolver; } @Override public java.util.Collection<FlexyClassLoader> getClassLoaders() { return classloaders.values(); } @Override public void boot() { try { InputStream is = this.getClass().getClassLoader().getResourceAsStream("KEV-INF/bootinfo"); boot(is); } catch (Exception e) { e.printStackTrace(); } } @Override public void boot(InputStream is) { final BootInfo bootInfo = BootInfoBuilder.read(is); boot(bootInfo); } @Override public void boot(final BootInfo bootInfo) { try { //we install deploy units for (BootInfoLine line : bootInfo.getLines()) { if (get(line.getURL()) == null) { FlexyClassLoader fcl = install(line.getURL(), line.getURL()); if (fcl == null) { Log.error("Not resolved during boot sequence: {}", line.getURL()); } else { Log.trace("install {}", line.getURL()); } } } //we link everything for (BootInfoLine line : bootInfo.getLines()) { FlexyClassLoader kcl = get(line.getURL()); for (String dep : line.getDependencies()) { Log.trace("Link {} -> {}", kcl.getKey(), dep); FlexyClassLoader resolvedDep = get(dep); if (resolvedDep == null) { Log.warn("Error during boot sequence dependency {} cannot be linked to {}", dep, kcl.getKey()); } else { kcl.attachChild(resolvedDep); } } } //finally we lock everything to avoid to kill Kevoree from outside for (BootInfoLine line : bootInfo.getLines()) { FlexyClassLoaderImpl kcl = (FlexyClassLoaderImpl) get(line.getURL()); if (kcl == null) { Log.warn("Boot procedure is incomplete, the following dependency has not been installed : {}", line.getURL()); } else { kcl.lockLinks(); } } if (bootInfo.getMain() != null) { final Boolean[] resl = new Boolean[1]; resl[0] = false; for (final FlexyClassLoader loader : getClassLoaders()) { if (!resl[0]) { try { final KevoreeKernel self = this; Thread t = new Thread(threadGroup, threadGroup.getName() + ".boot") { @Override public void run() { Thread.currentThread().setContextClassLoader(loader); KevoreeKernel.self.set(self); try { Class cls = ((FlexyClassLoaderImpl) loader).loadLocalOnly(bootInfo.getMain()); if (cls != null) { if (classloaders.values().contains(cls.getClassLoader())) { Method meth = cls.getMethod("main", String[].class); String[] params = new String[0]; Log.trace("KevoreeKernel will execute main method on {} from {}", cls.getName(), loader.getKey()); meth.invoke(null, (Object) params); resl[0] = true; } } } catch (java.lang.ClassNotFoundException e) { resl[0] = false; //NOP, we are looking for all } catch (Exception e) { e.printStackTrace(); resl[0] = true; //we still have tried, so quite boot mode } } }; t.start(); t.join(); } catch (Exception e) { e.printStackTrace(); } } } if (!resl[0]) { Log.error("Boot main not found {} in {} installed classloaders", bootInfo.getMain(), classloaders.size()); } } } catch (Exception e) { e.printStackTrace(); } } @Override public List<FlexyClassLoader> locate(String className) { List<FlexyClassLoader> selected = new ArrayList<FlexyClassLoader>(); for (final FlexyClassLoader loader : getClassLoaders()) { try { Class cls = ((FlexyClassLoaderImpl) loader).loadLocalOnly(className); if (cls != null) { selected.add(loader); } } catch (ClassNotFoundException ignore) { } } return selected; } @Override public void stop() { Thread[] subThread = new Thread[Integer.MAX_VALUE]; threadGroup.enumerate(subThread, true); for (Thread t : subThread) { if (t != null) { t.interrupt(); if (t.isAlive()) { //still alive, kill it t.stop(); } } } } @Override public void reboot(BootInfo bootInfo) { stop(); classloaders.clear(); boot(bootInfo); } @Override public void reboot(InputStream is) { reboot(BootInfoBuilder.read(is)); } }