/* * MicroJIAC - A Lightweight Agent Framework * This file is part of MicroJIAC Java6-Platform. * * Copyright (c) 2007-2012 DAI-Labor, Technische Universität Berlin * * This library includes software developed at DAI-Labor, Technische * Universität Berlin (http://www.dai-labor.de) * * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see <http://www.gnu.org/licenses/>. */ /* * $Id$ */ package de.jiac.micro.internal; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import javax.tools.Diagnostic; import javax.tools.DiagnosticListener; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import javax.tools.Diagnostic.Kind; import javax.tools.JavaCompiler.CompilationTask; import org.codehaus.plexus.util.cli.CommandLineException; import org.codehaus.plexus.util.cli.CommandLineUtils; import org.codehaus.plexus.util.cli.Commandline; import org.codehaus.plexus.util.cli.StreamConsumer; import org.slf4j.LoggerFactory; import de.jiac.micro.cl.ClassPath; import de.jiac.micro.cl.ContainerClassLoader; import de.jiac.micro.config.generator.AbstractConfiguration; import de.jiac.micro.config.generator.ConfigurationGenerator; import de.jiac.micro.config.generator.NodeConfiguration; import de.jiac.micro.internal.compile.ByteArrayJavaFileObject; import de.jiac.micro.internal.compile.ByteFileManager; import de.jiac.micro.internal.compile.CharSequenceJavaFileObject; /** * @author Marcel Patzlaff * @version $Revision$ */ public final class ApplicationLauncher { private static final class PrefixedConsumer implements StreamConsumer { private final PrintStream _out; private final String _prefix; PrefixedConsumer(PrintStream out, String prefix) { _out= out; _prefix= prefix; } public void consumeLine(String line) { _out.println("[" + _prefix + "]: " + line); } } private static class ConfigurationDiagnosticListener implements DiagnosticListener<JavaFileObject> { protected int errors= 0; protected ConfigurationDiagnosticListener() {} public void report(Diagnostic<? extends JavaFileObject> diagnostic) { if(diagnostic.getKind() == Kind.ERROR) { errors++; System.err.println(diagnostic.getMessage(null)); } } } /** * The main method of the launcher accepts one * namespace that contains an application definition. * * @param args */ public static void main(String[] args) { if(args.length != 1) { System.out.println("usage: " + ApplicationLauncher.class.getName() + " namespace"); System.exit(0); return; } // generate configuration sources String namespace= args[0]; AbstractConfiguration[] configs; try { configs= ConfigurationGenerator.execute( namespace, ApplicationLauncher.class.getClassLoader(), LoggerFactory.getLogger(ApplicationLauncher.class)); } catch (Exception e) { e.printStackTrace(); System.err.println("namespace does not contain a valid application definition: " + e.getMessage()); System.exit(1); return; } // prepare compilation JavaCompiler compiler= ToolProvider.getSystemJavaCompiler(); if(compiler == null) { System.err.println("Java compiler could not be found. Possibly you are running a JRE instead of a JDK!"); System.exit(1); return; } final List<NodeConfiguration> nodes= new ArrayList<NodeConfiguration>(); ArrayList<JavaFileObject> sources= new ArrayList<JavaFileObject>(); for(AbstractConfiguration config : configs) { sources.add(new CharSequenceJavaFileObject(config.className, config.source)); // collect nodes for later execution if(config instanceof NodeConfiguration) { nodes.add((NodeConfiguration) config); } } StandardJavaFileManager stdFileManager= compiler.getStandardFileManager(null, null, null); ByteFileManager<StandardJavaFileManager> byteFileManager= new ByteFileManager<StandardJavaFileManager>(stdFileManager); ConfigurationDiagnosticListener listener= new ConfigurationDiagnosticListener(); CompilationTask task= compiler.getTask(null, byteFileManager, listener, null, null, sources); // compile boolean success= task.call().booleanValue(); try { byteFileManager.close(); } catch (IOException e1) { e1.printStackTrace(); System.err.println("could not close file manager"); } if(!success) { System.err.println("could not compile node configurations"); System.err.println(" trying to dump generated source files..."); File targetFolder= dumpSources(configs); if(targetFolder != null) { System.err.println(" dumped into " + targetFolder.toString()); } else { System.err.println(" failed"); } System.exit(1); return; } // launch the nodes Map<String, ByteArrayJavaFileObject> classes= byteFileManager.getStore(); if(nodes.size() <= 0) { System.err.println("no nodes configured"); System.exit(1); } else if(nodes.size() == 1) { // start the node in the current vm launchInCurrentVM(nodes.get(0).className, classes); } else { // write the configuration launchInNewVMs(nodes, classes); } } private static void launchInCurrentVM(final String nodeConfigClassName, final Map<String, ByteArrayJavaFileObject> classes) { ByteArrayJavaFileCache cache= new ByteArrayJavaFileCache(classes.values()); ClassPath cp= new ClassPath(); for(URL url : cache.getURLs()) { cp.addURL(url); } // FIXME: this class loader sucks... create it more specific!!! final ContainerClassLoader nodeLoader= new ContainerClassLoader(cp, ApplicationLauncher.class.getClassLoader()); try { NodeLauncher.instantiateAndLaunch(nodeLoader, nodeConfigClassName); } catch (Exception e) { e.printStackTrace(System.err); System.err.println("could not launch node in current vm"); System.exit(1); return; } } private static void launchInNewVMs(List<NodeConfiguration> nodes, Map<String, ByteArrayJavaFileObject> classes){ File temporyClassesFolder= new File(System.getProperty("java.io.tmpdir"), "microjiac_classes_" + System.currentTimeMillis()); if(!temporyClassesFolder.exists()) { temporyClassesFolder.mkdirs(); } for(int i= 0; i < nodes.size(); ++i) { final NodeConfiguration nodeConfiguration= nodes.get(i); File nodeFolder= new File(temporyClassesFolder, String.valueOf(i)); if(!nodeFolder.exists()) { nodeFolder.mkdir(); } System.out.println("write configurations for '" + nodeConfiguration.className + "' to '" + nodeFolder); final ArrayList<String> relevantClassNames= new ArrayList<String>(Arrays.asList(nodeConfiguration.fullQualifiedAgentConfigurationNames)); relevantClassNames.add(nodeConfiguration.className); for(String relevantClassName : relevantClassNames) { String packageName= relevantClassName.substring(0, relevantClassName.lastIndexOf('.')); File targetFolder= new File(nodeFolder, packageName.replace('.', File.separatorChar)); if(!targetFolder.exists()) { targetFolder.mkdirs(); } File classFile= new File(targetFolder, relevantClassName.substring(relevantClassName.lastIndexOf('.') + 1) + ".class"); try { FileOutputStream output= new FileOutputStream(classFile); output.write(classes.get(relevantClassName).getByteArray()); output.flush(); output.close(); } catch (IOException ioe) { ioe.printStackTrace(System.err); System.err.println("could not create class file '" + classFile + "'"); System.exit(1); return; } } } final File workingDir= new File(System.getProperty("user.dir")); final File javaCommand= getJavaCommand(); final CountDownLatch allDone= new CountDownLatch(nodes.size()); for(int i= 0; i < nodes.size(); ++i) { final String classpath= (System.getProperty("java.class.path") + File.pathSeparator + new File(temporyClassesFolder, String.valueOf(i)).getAbsolutePath()); String[] entries= classpath.split(File.pathSeparator); String launcherPath= null; StringBuilder nodePath= new StringBuilder(); for(int e= 0; e < entries.length; ++e) { // // FIXME: dirty hack // if(entries[e].contains("-launcher")) { // launcherPath= entries[e]; // } else { if(nodePath.length() > 0) { nodePath.append(File.pathSeparatorChar); } nodePath.append(entries[e]); // } } final String className= nodes.get(i).className; final Commandline cl= new Commandline( javaCommand.getPath() + " -classpath \"" + nodePath.toString() + "\"" + // " -classpath \"" + launcherPath + "\"" + " " + NodeLauncher.class.getName() + " -c " + className ); cl.setWorkingDirectory(workingDir.getPath()); final String nodeName= className.substring(className.lastIndexOf('.') + 1); new Thread(nodeName) { public void run() { try { System.out.println(cl.toString()); CommandLineUtils.executeCommandLine(cl, new PrefixedConsumer(System.out, nodeName), new PrefixedConsumer(System.err, nodeName)); } catch (CommandLineException e) { e.printStackTrace(); } finally { allDone.countDown(); } System.out.println(nodeName + " finished execution"); } }.start(); } while(true) { try { allDone.await(); break; } catch (InterruptedException e) { e.printStackTrace(); } } } private static File getJavaCommand() { String osName = System.getProperty("os.name"); System.out.println("osName = " + osName); String javaCommand = "java"; boolean isWindowsBased = osName.startsWith("Windows"); System.out.println("os is windows based: " + isWindowsBased); if (isWindowsBased) { javaCommand = javaCommand + ".exe"; System.out.println("adjusted java command to meet windows naming conventions"); } File cmd = new File(System.getProperty("java.home") + File.separator + "bin" + File.separator + javaCommand); System.out.println("cmd = " + cmd); if (!cmd.exists() || !cmd.isFile()) { throw new IllegalStateException("the java command cannot be found"); } if (!cmd.canRead() || !cmd.canExecute()) { throw new IllegalStateException("you have no access rights to the java command"); } return cmd; } private static File dumpSources(AbstractConfiguration[] configs ) { File temporySourcesFolder= new File(System.getProperty("java.io.tmpdir"), "microjiac_sources_" + System.currentTimeMillis()); if(!temporySourcesFolder.exists()) { temporySourcesFolder.mkdirs(); } for(AbstractConfiguration config : configs) { int lastPoint= config.className.lastIndexOf('.'); String packName= lastPoint > 0 ? config.className.substring(0, lastPoint) : null; String fileName= lastPoint > 0 ? config.className.substring(lastPoint + 1) : config.className; File packFolder= packName != null ? new File(temporySourcesFolder, packName.replace('.', File.separatorChar)) : temporySourcesFolder; if(!packFolder.exists()) { packFolder.mkdirs(); } File sourceFile= new File(packFolder, fileName + ".java"); try { PrintStream printer= new PrintStream(new FileOutputStream(sourceFile)); printer.print(config.source); printer.flush(); printer.close(); } catch (IOException e) { System.err.println(" cannot dump " + sourceFile.toString()); e.printStackTrace(System.err); } } return temporySourcesFolder; } private ApplicationLauncher() {} }