/******************************************************************************* * Copyright (c) 1998, 2016 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.tools.workbench.test.utility; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.eclipse.persistence.tools.workbench.utility.CollectionTools; import org.eclipse.persistence.tools.workbench.utility.io.NullOutputStream; import org.eclipse.persistence.tools.workbench.utility.iterators.FilteringIterator; import org.eclipse.persistence.tools.workbench.utility.iterators.TransformationIterator; import org.eclipse.persistence.tools.workbench.utility.string.StringTools; /** * Some tools for executing, compiling, JARing, etc. * Possibly obviously, this stuff might not work on all platforms. It seems * to work OK on Windows and Linux. */ public class JavaTools { private static final String CR = System.getProperty("line.separator"); private static final String FS = System.getProperty("file.separator"); private static final String JAVA_HOME = System.getProperty("java.home"); private static final String JAVA_CLASSPATH = System.getProperty("java.class.path"); private static final String[] EMPTY_STRING_ARRAY = new String[0]; /** * Compile the specified source file, using the current system classpath. */ public static void compile(File sourceFile) throws IOException, InterruptedException { compile(sourceFile, JAVA_CLASSPATH); } /** * Compile the specified source file with the specified classpath. * The resulting class file will be put in the same directory as the source file. * Throw a RuntimeException for any non-zero exit value. */ public static void compile(File sourceFile, String classpath) throws IOException, InterruptedException { List cmd = new ArrayList(); cmd.add(javaCompiler()); if ((classpath != null) && (classpath.length() != 0)) { cmd.add("-classpath"); cmd.add(classpath); } cmd.add(sourceFile.getAbsolutePath()); exec((String[]) cmd.toArray(new String[cmd.size()])); } /** * Compile all the specified source files (must be of type File), * using the current system classpath. */ public static void compile(Collection sourceFiles) throws IOException, InterruptedException { compile(sourceFiles.iterator()); } /** * Compile all the specified source files (must be of type File), * using the current system classpath. */ public static void compile(Collection sourceFiles, String classpath) throws IOException, InterruptedException { compile(sourceFiles.iterator(), classpath); } /** * Compile all the specified source files (must be of type File), * using the current system classpath. */ public static void compile(Iterator sourceFiles) throws IOException, InterruptedException { compile(sourceFiles, JAVA_CLASSPATH); } /** * Compile all the specified source files (must be of type File) with * the specified classpath. The resulting class files will be put in * the same directories as the source files. * Throw a RuntimeException for any non-zero exit value. */ public static void compile(Iterator sourceFiles, String classpath) throws IOException, InterruptedException { List cmd = new ArrayList(); cmd.add(javaCompiler()); if ((classpath != null) && (classpath.length() != 0)) { cmd.add("-classpath"); cmd.add(classpath); } CollectionTools.addAll(cmd, javaFileNames(sourceFiles)); exec((String[]) cmd.toArray(new String[cmd.size()])); } /** * Return the names of the files in the collection that end with .java. */ private static Iterator javaFileNames(Iterator files) { return new TransformationIterator(javaFiles(files)) { @Override protected Object transform(Object next) { return ((File) next).getAbsolutePath(); } }; } /** * Return the files in the collection whose names that end with .java. */ private static Iterator javaFiles(Iterator files) { return new FilteringIterator(files) { @Override protected boolean accept(Object next) { File file = (File) next; return file.isFile() && file.getPath().endsWith(".java"); } }; } /** * Add to the specified archive all the files in and below * the specified directory. * Throw a RuntimeException for any non-zero exit value. * c = create * f = file */ public static void jar(File jarFile, File directory) throws IOException, InterruptedException { jar("cf", jarFile, directory); } /** * Add to the specified zip file all the files in and below * the specified directory. * Throw a RuntimeException for any non-zero exit value. * c = create * M = no manifest * f = file */ public static void zip(File zipFile, File directory) throws IOException, InterruptedException { jar("cMf", zipFile, directory); } private static void jar(String jarOptions, File jarFile, File directory) throws IOException, InterruptedException { exec( new String[] { jarUtility(), jarOptions, jarFile.getAbsolutePath(), "-C", directory.getAbsolutePath(), "." } ); } /** * Execute the specified class's main method, using the current system classpath. */ public static void java(String className) throws IOException, InterruptedException { java(className, JAVA_CLASSPATH); } /** * Execute the specified class's main method with the specified classpath. * Throw a RuntimeException for any non-zero exit value. */ public static void java(String className, String classpath) throws IOException, InterruptedException { java(className, classpath, EMPTY_STRING_ARRAY); } /** * Execute the specified class's main method with the specified classpath. * Throw a RuntimeException for any non-zero exit value. */ public static void java(String className, String classpath, String[] args) throws IOException, InterruptedException { java(className, classpath, EMPTY_STRING_ARRAY, args); } /** * Execute the specified class's main method with the specified classpath. * Throw a RuntimeException for any non-zero exit value. */ public static void java(String className, String classpath, String[] javaOptions, String[] args) throws IOException, InterruptedException { List cmd = new ArrayList(); cmd.add(javaVM()); if ((classpath != null) && (classpath.length() != 0)) { cmd.add("-classpath"); cmd.add(classpath); } CollectionTools.addAll(cmd, javaOptions); cmd.add(className); CollectionTools.addAll(cmd, args); exec((String[]) cmd.toArray(new String[cmd.size()])); } /** * Execute the specified command line. * Throw a RuntimeException for any non-zero exit value. */ public static void exec(String[] cmd) throws IOException, InterruptedException { // print(cmd); Process process = Runtime.getRuntime().exec(cmd); // fork a thread to consume stderr ByteArrayOutputStream stderrStream = new ByteArrayOutputStream(1000); Runnable stderrRunnable = new StreamReader( new BufferedInputStream(process.getErrorStream()), new BufferedOutputStream(stderrStream) ); Thread stderrThread = new Thread(stderrRunnable, "stderr stream reader"); stderrThread.start(); // fork a thread to consume stdout ByteArrayOutputStream stdoutStream = new ByteArrayOutputStream(1000); Runnable stdoutRunnable = new StreamReader( new BufferedInputStream(process.getInputStream()), new BufferedOutputStream(stdoutStream) ); Thread stdoutThread = new Thread(stdoutRunnable, "stdout stream reader"); stdoutThread.start(); // wait for all the threads to die stderrThread.join(); stdoutThread.join(); int exitValue = process.waitFor(); stderrStream.close(); stdoutStream.close(); if (!(exitValue == 0 || exitValue == 2)) { StringBuffer sb = new StringBuffer(2000); sb.append(CR); sb.append("**** exit value: "); sb.append(exitValue); sb.append(CR); sb.append("**** stderr: "); sb.append(CR); sb.append(stderrStream.toString()); sb.append(CR); sb.append("**** stdout: "); sb.append(CR); sb.append(stdoutStream.toString()); throw new RuntimeException(sb.toString()); } } /** * Return the name of the standard Java Home directory. */ public static String javaHomeDirectoryName() { return JAVA_HOME; } /** * Return the standard Java Home directory. */ public static File javaHomeDirectory() { return new File(javaHomeDirectoryName()); } /** * Return the directory that holds the various Java tools * (e.g. java, javac, jar). */ public static File javaToolDirectory() { return new File(javaHomeDirectory().getParentFile(), "bin"); } /** * Return the name of the directory that holds the various Java tools * (e.g. java, javac, jar). */ public static String javaToolDirectoryName() { return javaToolDirectory().getPath(); } /** * Return the fully-qualified name of the Java VM. */ public static String javaVM() { return javaToolDirectoryName() + FS + "java"; } /** * Return the fully-qualified name of the Java compiler. */ public static String javaCompiler() { return javaToolDirectoryName() + FS + "javac"; } /** * Return the fully-qualified name of the JAR utility. */ public static String jarUtility() { return javaToolDirectoryName() + FS + "jar"; } /** * Print the specified "command line". */ static void print(String[] cmd) { for (int i = 0; i < cmd.length; i++) { System.out.print(cmd[i]); if (i + 1 < cmd.length) { System.out.print(" "); } } System.out.println(); } /** * Suppress default constructor, ensuring non-instantiability. */ private JavaTools() { super(); throw new UnsupportedOperationException(); } // ********** member class ********** /** * This class allows you to fork a thread to read an input stream * asynchronously. */ private static class StreamReader implements Runnable { private InputStream inputStream; private OutputStream outputStream; /** * Construct a stream reader that reads the specified input stream * and copies its data to the specified output stream. */ StreamReader(InputStream inputStream, OutputStream outputStream) { super(); this.inputStream = inputStream; this.outputStream = outputStream; } /** * Construct a stream reader that reads the specified stream * and discards the data. */ StreamReader(InputStream inputStream) { this(inputStream, NullOutputStream.instance()); } /** * @see Runnable#run() */ @Override public void run() { try { for (int b = -1; (b = this.inputStream.read()) != -1; ) { this.outputStream.write(b); } } catch (IOException ex) { // hmmm - not sure what to do here, but this seems good enough throw new RuntimeException(ex); } } /** * @see Object#toString() */ @Override public String toString() { return StringTools.buildToStringFor(this); } } }