package com.sandwich.util.io.filecompiler; import java.io.File; import java.io.IOException; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import com.sandwich.util.ExceptionUtils; import com.sandwich.util.io.ForEachFileAction; import com.sandwich.util.io.StreamUtils; public class FileCompilerAction extends ForEachFileAction { private final String destinationPath; private final String[] classPaths; private CompilationListener compilationListener; private final long timeout; public static final CompilationListener LOGGING_HANDLER = new CompilationFailureLogger(); public FileCompilerAction(File destination, CompilationListener errorHandler, String...classPaths){ this(destination, errorHandler, 1000, classPaths); } public FileCompilerAction(File destination, CompilationListener errorHandler, long timeout, String[] classPaths) { if(destination == null){ throw new IllegalArgumentException("the destination path is required"); } this.destinationPath = destination.getAbsolutePath(); this.classPaths = classPaths; this.compilationListener = errorHandler == null ? LOGGING_HANDLER : errorHandler; this.timeout = timeout; } public void onFile(File src) throws IOException { String fileName = src.getName(); if (CompilerConfig.isSourceFile(fileName)) { String[] command = CompilerConfig.getCompilationCommand(src, destinationPath, getClasspath()); try{ Process p = Runtime.getRuntime().exec(command); try { executeWithTimeout(src, command, p, timeout); } catch (IllegalThreadStateException x) { compilationListener.compilationFailed(src, command, 255, "Compilation took longer than "+timeout+" ms.\n" + "It is likely that the compiler has locked up compiling this file.\n" + "Please revert your last change and try something different.", x); } catch (Exception x) { x.printStackTrace(); compilationListener.compilationFailed(src, command, p.exitValue(), StreamUtils.convertStreamToString(p.getErrorStream()), x); } }catch(IOException x){ if(x.getMessage().contains("Cannot run program")){ String commandString = ""; for(String segment : command){ commandString += segment + " "; } commandString = commandString.trim(); compilationListener.compilationFailed(src, command, 2, "Cannot execute:" + System.getProperty("line.separator") + commandString + System.getProperty("line.separator") + "Please check that the appropriate compiler (" + command[0] + ") is installed, is executable and is listed in your PATH environment variable value.", x); } } } } private void executeWithTimeout(final File src, final String[] command, final Process p, final long timeout) throws InterruptedException { ExecutorService executor = Executors.newCachedThreadPool(); Callable<Object> task = new Callable<Object>() { public Object call() { try { if (p.waitFor() != 0) { compilationListener.compilationFailed(src, command, p .exitValue(), StreamUtils.convertStreamToString(p.getErrorStream()), null); } else { compilationListener.compilationSucceeded(src, command, StreamUtils.convertStreamToString(p.getInputStream()), null); } } catch (InterruptedException e) { compilationListener.compilationFailed(src, command, p.exitValue(), ExceptionUtils.convertToPopulatedStackTraceString(e), null); } return null; } }; Future<Object> future = executor.submit(task); try { future.get(timeout, TimeUnit.MILLISECONDS); } catch (Exception e) { compilationListener.compilationFailed(src, command, p.exitValue(), ExceptionUtils.convertToPopulatedStackTraceString(e), null); } } private String getClasspath() { String classPath = ""; for(String jar : classPaths) { classPath += jar + File.pathSeparatorChar; } return classPath; } }