/**
 * JaDOrT: JASMINe Deployment Orchestration Tool
 * Copyright (C) 2008-2009 Bull S.A.S.
 * Copyright (C) 2008-2009 France Telecom R&D
 * Contact: jasmine@ow2.org
 *
 * 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 2.1 of the License, or 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id: JavaLauncher.java 3072 2009-02-04 13:22:06Z alitokmen $
 * --------------------------------------------------------------------------
 */
package org.ow2.jasmine.jadort.service.action.launchers;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

/**
 * Launches a Java program. <br/>
 * <br/>
 * JaDOrT needs this class since some IIOP implementations (WebLogic's private
 * IIOPS, T3 implementations but also WebSphere's private IIOP implementation)
 * seem not to interoperate well with JOnAS' IIOP implementation.
 * 
 * @author Malek Chahine
 * @author Remy Bresson
 * @author S. Ali Tokmen
 */
public class JavaLauncher {

    /**
     * Launch a java program.
     * 
     * @param mainClass Java class with main method
     * @param classpath Files in Classpath, cannot be null
     * @param jvmargs VM arguments, may be null
     * @param lines Lines to pass to the program, may be null
     * @param timeout Number of milliseconds the program is allowed to run
     * @return Whatever the program has output into the stdio
     * @throws IOException If any I/O exception occurs when launching or reading
     *         response from program
     * @throws InterruptedException If waiting for process termination fails
     */
    public static String exec(final String mainClass, final File[] classpath, final String[] jvmargs, final String[] lines,
        final long timeout) throws IOException, InterruptedException {

        // Get the JVM
        String jvm = null;
        jvm = System.getProperty("java.home");
        if (jvm == null) {
            throw new IllegalStateException("java.home system property not set");
        }
        jvm = jvm + File.separator + "bin" + File.separator + "java";
        File jvmFile = new File(jvm);
        if (!jvmFile.isFile()) {
            jvm = jvm + ".exe";
            jvmFile = new File(jvm);
        }
        if (!jvmFile.isFile()) {
            throw new IllegalStateException("Cannot find the java executable");
        }

        // Create the command
        StringBuffer exec = new StringBuffer(jvmFile.getAbsolutePath());

        // Append classpath elements
        exec.append(" -classpath \"");
        boolean first = true;
        for (File classpathElement : classpath) {
            if (!first) {
                exec.append(';');
            } else {
                first = false;
            }
            exec.append(classpathElement.getAbsolutePath());
        }
        exec.append('"');

        // Append JVM arguments
        if (jvmargs != null) {
            for (String jvmarg : jvmargs) {
                exec.append(" -");
                exec.append(jvmarg);
            }
        }

        // Append main class
        exec.append(' ');
        exec.append(mainClass);

        // Launch process
        final String command = exec.toString();
        Process proc = Runtime.getRuntime().exec(command);
        InputStreamReader isr = new InputStreamReader(proc.getInputStream());
        BufferedReader br = new BufferedReader(isr);

        // try ... finally block ensured proc.destroy() is always called
        try {
            // Output the lines to the program
            if (lines != null) {
                OutputStreamWriter osw = new OutputStreamWriter(proc.getOutputStream());
                BufferedWriter bw = new BufferedWriter(osw);
                for (String line : lines) {
                    bw.write(line);
                    bw.write("\n");
                }
            }

            // Wait for the program to terminate
            final long finishTime = System.currentTimeMillis() + timeout;
            while (System.currentTimeMillis() < finishTime) {
                try {
                    proc.exitValue();
                    break;
                } catch (IllegalThreadStateException ignored) {
                    // Thrown if process has not yet terminated, ignore
                    Thread.sleep(1000);
                }
            }
        } finally {
            // ALWAYS destroy the process
            proc.destroy();
        }

        // Read response from program
        StringBuffer isb = new StringBuffer();
        String line;
        while ((line = br.readLine()) != null) {
            isb.append(line);
            isb.append("\n");
        }
        return isb.toString();
    }
}
