/* * Copyright (C) 2013 Intel Corporation * All rights reserved. */ package com.intel.mtwilson.launcher; import com.intel.dcsg.cpg.io.file.DirectoryFilter; import com.intel.dcsg.cpg.performance.AlarmClock; import com.intel.dcsg.cpg.util.ArrayIterator; import com.intel.dcsg.cpg.util.BreadthFirstTreeIterator; import com.intel.dcsg.cpg.util.FileTree; import com.intel.dcsg.cpg.util.Filter; import com.intel.dcsg.cpg.module.Container; import com.intel.dcsg.cpg.module.Module; import com.intel.dcsg.cpg.module.ModuleRepository; import com.intel.dcsg.cpg.module.ModuleUtil; import com.intel.dcsg.cpg.classpath.ClassLoadingStrategy; import com.intel.dcsg.cpg.classpath.DirectoryResolver; import com.intel.dcsg.cpg.classpath.FencedClassLoadingStrategy; import com.intel.dcsg.cpg.classpath.JarUtil; import com.intel.dcsg.cpg.io.file.FilenameEndsWithFilter; import com.intel.dcsg.cpg.module.ContainerException; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.jar.Manifest; /** * Given a directory containing some jar files of which at least one is a Module, this launcher starts the container and * activates all the modules in the directory. * * The requirements on a container are that it have these methods: start() stop() register(Module) * * @author jbuhacoff */ public class ModuleDirectoryLauncher { private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ModuleDirectoryLauncher.class); private static final FilenameEndsWithFilter jarfilter = new FilenameEndsWithFilter(".jar"); private Container container = new Container(); private ClassLoadingStrategy classLoadingStrategy = new FencedClassLoadingStrategy(); // a reasonable default until we get semantic versioning working /** * Where the module jar files and their dependencies are stored, for example /opt/mtwilson/java */ private File directory; private boolean continueEventLoop = true; public File getDirectory() { return directory; } public void setDirectory(File directory) { this.directory = directory; } public Container getContainer() { return container; } public void setContainer(Container container) { this.container = container; } public ClassLoadingStrategy getClassLoadingStrategy() { return classLoadingStrategy; } public void setClassLoadingStrategy(ClassLoadingStrategy classLoadingStrategy) { this.classLoadingStrategy = classLoadingStrategy; } /** * Initialize everything but do NOT start the event loop (caller must start it and stop it as needed) */ public void launch() throws IOException, ContainerException { loadModules(); // add a shutdown hook so we can automatically shut down the container if the VM is exiting addShutdownHook(); // now start all modules we loaded and registered with the container container.start(); // now list the registered modules log.debug("There are {} registered modules", container.getModules().size()); for (Module module : container.getModules()) { log.debug("Module: {};active={}", module.getImplementationTitle() + "-" + module.getImplementationVersion(), (module.isActive() ? "yes" : "no")); } } /** * Note: this method never returns! Call stopEventLoop() from another thread to terminate. */ public void startEventLoop() { AlarmClock alarm = new AlarmClock(1, TimeUnit.SECONDS); while (continueEventLoop) { try { alarm.sleep(); } catch (Exception e) { log.trace("Interrupted sleep", e); } } } public void stopEventLoop() { continueEventLoop = false; } private void addShutdownHook() { Runtime.getRuntime().addShutdownHook(new Thread("MtWilson Shutdown Hook") { @Override public void run() { try { if (container != null) { log.debug("Waiting for modules to deactivate"); container.stop(); } } catch (Exception ex) { System.err.println("Error stopping container: " + ex); } } }); } public void loadModules() throws IOException { DirectoryResolver resolver = new DirectoryResolver(directory); // JarFileIterator it = new JarFileIterator(directory); // scans directory and its subdirectories for jar files Iterator<File> it = new ArrayIterator<File>(directory.listFiles(jarfilter)); // only scans directory for jar files; does NOT scan subdirectories while (it.hasNext()) { File jar = it.next(); if (ModuleUtil.isModule(jar)) { Manifest manifest = JarUtil.readManifest(jar); Module module = new Module(jar, manifest, classLoadingStrategy.getClassLoader(jar, manifest, resolver)); log.debug("Module: {}", module.getImplementationTitle() + "-" + module.getImplementationVersion()); log.debug("Class-Path: {}", (Object)module.getClasspath()); log.debug("Module-Components: {}", (Object)module.getComponentNames()); // before we try to activate the module, make sure that all its dependencies are present and if not try to download them automatically List<String> missingArtifacts = listMissingArtifacts(module); // if any are missing we gquit if (missingArtifacts.isEmpty()) { log.debug("Classpath ok, registering module"); container.register(module); } else { log.warn("Module {} is missing {} jars from classpath", module.getImplementationTitle(), missingArtifacts.size()); } } } log.debug("Found {} modules", container.getModules().size()); } public List<String> listMissingArtifacts(Module module) { ArrayList<String> missing = new ArrayList<String>(); String[] jars = module.getClasspath(); for (String jar : jars) { if (!contains(jar)) { missing.add(jar); log.debug("Repository missing jar: {}", jar); } } return missing; } public boolean contains(String artifact) { // in java7 this would be: // return directory.toPath().resolve(artifact).toFile().exists(); File file = new File(directory.getAbsolutePath(), artifact); return file.exists(); } // this inner class scans the directory and any subdirectories // for jar files ... // if you need a non-recursive list of jars you can use simply: // Iterator<File> it = new ArrayIterator<File>( directory.listFiles(jarfilter) ); public static class RecursiveJarFileIterator implements Iterator<File> { private final FileTree tree = new FileTree(); private final Iterator<File> folders; // iterates over all subdirectories (including directory itself) private Iterator<File> files = null; // iterates over files in current folder ; when it runs out we need to move on to next folder public RecursiveJarFileIterator(File directory) { folders = new BreadthFirstTreeIterator<File>(tree, directory, new DirectoryFilter()); } @Override public boolean hasNext() { if (files != null && files.hasNext()) { return true; } while (folders.hasNext()) { File nextFolder = folders.next(); files = new ArrayIterator<File>(nextFolder.listFiles(jarfilter)); if (files.hasNext()) { return true; } } return false; } @Override public File next() { return files.next(); } @Override public void remove() { throw new UnsupportedOperationException(); //"Not supported yet."); //To change body of generated methods, choose Tools | Templates. } } }