Deploy Class

package javaxt.express;
import com.yahoo.platform.yui.compressor.JavaScriptCompressor;
import com.yahoo.platform.yui.compressor.CssCompressor;
import org.mozilla.javascript.ErrorReporter;
import org.mozilla.javascript.EvaluatorException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.logging.Level;
import javaxt.utils.Console;

//******************************************************************************
//**  WebDeploy
//******************************************************************************
/**
 *   Command line app used to package and deploy a web application. Assumes
 *   jar files are found in the /dist folder of the input directory and
 *   web-related files are in the /web directory.
 *
 ******************************************************************************/

public class Deploy {

    private static javaxt.io.Directory input;
    private static javaxt.io.Directory output;
    private java.util.HashSet<javaxt.io.File> minifiedFiles = new java.util.HashSet<>();


  //**************************************************************************
  //** Command Line Interface
  //**************************************************************************
  /** Used to instantiate the class via command line args.
   */
    public static void main(String[] arr) {
        java.util.HashMap<String, String> args = Console.parseArgs(arr);
        

        input = new javaxt.io.Directory(args.get("-deploy"));
        output = new javaxt.io.Directory(args.get("-target"));
        
        
        System.out.print(
            "\r\n"+
            "------------------------------------------\r\n"+
            " Please select from the following options:\r\n"+
            "------------------------------------------\r\n"+
            "  1. Deploy Web App\r\n"+
            "  2. Deploy Web Services\r\n"+
            "  3. Both\r\n"
        );
        while (true){
            System.out.print("\r\n> ");
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            try {
                int x = Integer.parseInt(br.readLine());
                if (x<1 || x>3) throw new IllegalArgumentException();
                
                new Deploy(
                    input,
                    output,
                    x
                );
                break;
            }
            catch (IllegalArgumentException e) { //NumberFormatException
                System.out.println("  ERROR: Invalid Entry!");
            }
            catch (Exception e) {
                e.printStackTrace();
                System.exit(1);
            }
        }
    }


  //**************************************************************************
  //** Constructor
  //**************************************************************************
  /** Used to instantiate the class and deploy the web applications.
   *
   *  @param input Input directory containing both the WebApp and WebServices
   *  projects (e.g. "C:\Users\Peter\Java\WebApp\").
   *
   *  @param output Output directory (e.g. "X:\WebApp\").
   */
    public Deploy(javaxt.io.Directory input, javaxt.io.Directory output, int options){
        long buildNumber = new javaxt.utils.Date().toLong();

      //Deploy web app
        if (options==1 || options==3){
            System.out.println("\r\nDeploying Web App...");
            
            
          //Copy web directory
            java.util.HashSet<javaxt.io.File> filters = new java.util.HashSet<>();
            javaxt.io.Directory inputWeb = new javaxt.io.Directory(input+"web");
            javaxt.io.Directory outputWeb = new javaxt.io.Directory(output+"web");
            for (String fileName : new String[]{"index.html","login.html","sites.html","main.html"}){
                javaxt.io.File inputFile = new javaxt.io.File(inputWeb + fileName);
                javaxt.io.File outputFile = new javaxt.io.File(outputWeb + fileName);                
                
                if (!inputFile.exists()) continue;
                
                java.util.HashSet<javaxt.io.File> filter = updateHtml(inputFile, outputFile);
                java.util.Iterator<javaxt.io.File> it = filter.iterator();
                while (it.hasNext()){
                    filters.add(it.next());
                }
            }
            copyFiles(inputWeb, outputWeb, filters);
            
            

          //Copy apps
            java.util.HashSet<javaxt.io.File> filter = updateApps(input, output);
            javaxt.io.Directory inputAppDir = new javaxt.io.Directory(input + "dist/apps");
            javaxt.io.Directory outputAppDir = new javaxt.io.Directory(output + "apps");
            copyApps(inputAppDir, outputAppDir, filter);
            
            
          //Create build file
            new javaxt.io.File(outputWeb, "build.txt").write(buildNumber + "");
        }

        
      //Deploy web services
        if (options==2 || options==3){
            System.out.println("\r\nDeploying Web Services...");
            copyJars(input, output);
            new javaxt.io.File(output, "build.txt").write(buildNumber + "");
        }

        
        
        
        System.out.println("\r\nDone!");
    }


    private class Script {
        private java.util.Date lastModified;
        private StringBuffer src = new StringBuffer();
        private boolean minified = false;
    }


    
  //**************************************************************************
  //** updateHtml
  //**************************************************************************
  /** Used to consolidate javascripts and style sheets sourced in a given html
   *  file. Goal is to reduce server load by combining files found in a common
   *  directories. HTML is updated to source file paths to the consolidated
   *  scripts and style sheets. 
   */
    private java.util.HashSet<javaxt.io.File> updateHtml(javaxt.io.File inputFile, javaxt.io.File outputFile){

        javaxt.io.Directory inputDir = inputFile.getDirectory();
        javaxt.io.Directory outputDir = outputFile.getDirectory();
        
        
        org.w3c.dom.Document xhtml = inputFile.getXML();
        org.w3c.dom.Node head = javaxt.xml.DOM.getElementsByTagName("head", xhtml)[0];


        
        
        java.util.HashSet<javaxt.io.File> filter = new java.util.HashSet<javaxt.io.File>();
        int x = inputDir.toString().length();

        

        
      //Consolidate javascript includes
        java.util.LinkedHashMap<String, Script> scripts = new java.util.LinkedHashMap<String, Script>();
        for (org.w3c.dom.Node node : javaxt.xml.DOM.getElementsByTagName("script", xhtml)){
            String src = javaxt.xml.DOM.getAttributeValue(node.getAttributes(), "src").trim();
            if (src.length()>0){
                javaxt.io.File js = new javaxt.io.File(inputFile.MapPath(src));
                if (js.exists()){
                    String path = js.getPath().substring(x);

                    if (path.length()==0) path = js.getName();
                    else  path = path.substring(0, path.length() - 1) + ".js";
                    //System.out.println(js.getName() + " --> " + path);

                    Script script = scripts.get(path);
                    if (script==null){
                        script = new Script();
                        scripts.put(path, script);
                    }

                    
                    StringBuffer str = script.src;
                    str.append(js.getText());
                    str.append("\r\n");


                    java.util.Date lastModified = script.lastModified;
                    if (lastModified==null || js.getLastModifiedTime().after(lastModified)){
                        script.lastModified = js.getLastModifiedTime();
                    }

                    script.minified = js.getName().endsWith(".min.js");
                    
                    filter.add(js);
                }
                
                node.getParentNode().removeChild(node);
            }
        }



        
        
      //Update links to javascripts
        java.util.Iterator<String> it = scripts.keySet().iterator();
        while (it.hasNext()){
            
            String path = it.next();
            Script script = scripts.get(path);
            String src = script.src.toString();
            java.util.Date lastModified = script.lastModified;
            javaxt.io.File js = new javaxt.io.File(outputDir + path);
            
            //if (!minifiedFiles.contains(js)){
            
                if (!script.minified){
                    System.out.println("Compressing " + path + "...");
                    java.io.Reader in = new java.io.StringReader(src);
                    java.io.Writer out = new java.io.StringWriter();
                    try {
                        JavaScriptCompressor compressor = new JavaScriptCompressor(in, new YuiCompressorErrorReporter());
                        in.close();

                        //Writer out, int linebreak, boolean munge, boolean verbose, boolean preserveAllSemiColons, boolean disableOptimizations
                        compressor.compress(out, -1, true, false, false, false);
                        src = out.toString();
                        out.close();

                        minifiedFiles.add(js);
                    } 
                    catch(Exception e){
                        e.printStackTrace();
                        try{in.close();}catch(Exception ex){}
                        try{out.close();}catch(Exception ex){}
                    }
                }


              //Create/Update javascript file as needed
                System.out.println(path + " --> " + js);
                if (!js.exists() || lastModified.after(js.getLastModifiedTime()) || !js.getText().equals(src)){
                    js.write(src);
                    js.setLastModifiedTime(lastModified);
                }
            
            //}
            
          //Insert path to consolidated javascript file
            org.w3c.dom.Node node = xhtml.createElement("script");
            org.w3c.dom.NamedNodeMap attr = node.getAttributes();
            org.w3c.dom.Attr att = xhtml.createAttribute("type");
            att.setValue("text/javascript");
            attr.setNamedItem(att);
            att = xhtml.createAttribute("src");
            att.setValue(path.replace("\\", "/"));
            attr.setNamedItem(att);
            head.appendChild(node);
        }


      //Remove comments
        org.w3c.dom.NodeList nodes = head.getChildNodes();
        for (int i=0; i<nodes.getLength(); i++){
            org.w3c.dom.Node node = nodes.item(i);
            if (node.getNodeType()==8){
                node.getParentNode().removeChild(node);
            }
        }


      //Remove extra line spaces
        String text = javaxt.xml.DOM.getText(xhtml);
        while (text.contains("\r\n\r\n")) text = text.replace("\r\n\r\n", "\r\n");
        while (text.contains("/><")) text = text.replace("/><", "/>\r\n<");

      //Remove XML header
        if (text.startsWith("<?xml")) text = text.substring(text.indexOf(">")+1);


      //Update script tags. Browsers don't understand tags that end in "/>"
        StringBuffer html = new StringBuffer();
        for (String str : text.split("<script ")){
            if (html.length()==0) html.append(str);
            else{
                String a = str.substring(0, str.indexOf(">")+1);
                String b = str.substring(str.indexOf(">")+1);

                if (a.substring(a.length()-2).equals("/>")){
                    a = a.substring(0, a.length()-2) + "></script>";
                }

                str = "<script " + a + b;
                html.append(str);
            }
        }
        text = html.toString();


      //Save html to file
        outputFile.write(text);


      //Return list of files that were consolidated
        return filter;
    }


  //**************************************************************************
  //** updateApps
  //**************************************************************************
  /** Used to consolidate javascripts and style sheets sourced in a xml config
   *  file. Goal is to reduce server load by combining files found in a common
   *  directories. XML is updated to source file paths to the consolidated
   *  scripts and style sheets. 
   */
    private java.util.HashSet<javaxt.io.File> updateApps(javaxt.io.Directory input, javaxt.io.Directory output){
        

        javaxt.io.Directory dir = new javaxt.io.Directory(input + "dist/apps");
        java.util.HashSet<javaxt.io.File> filter = new java.util.HashSet<javaxt.io.File>();

        
      //Find xml and consolidate 
        java.util.LinkedHashMap<String, Script> scripts = new java.util.LinkedHashMap<>();
        java.util.List files = dir.getChildren(true, "*.xml", false);
        Object obj;
        while (true){
            synchronized (files) {
                while (files.isEmpty()) {
                  try {
                      files.wait();
                  }
                  catch (InterruptedException e) {
                      break;
                  }
                }
                obj = files.remove(0);
                files.notifyAll();
            }

            if (obj==null){
                break;
            }
            else{

                if (obj instanceof javaxt.io.File){
                    javaxt.io.File file = (javaxt.io.File) obj;
                    
                    javaxt.io.Directory parentDir = file.getDirectory();
                    if (parentDir.getName().equalsIgnoreCase("web")){
                        
                        javaxt.io.Directory appDir = parentDir.getParentDirectory();
                        String appName = appDir.getName();
                        javaxt.io.Directory webDir = parentDir;
                        int x = webDir.toString().length();
                        
                        System.out.println(appName);
                        
                        String relPath = "apps" + javaxt.io.Directory.PathSeparator + parentDir.toString().substring(dir.toString().length());
                        System.out.println(relPath);

                        javaxt.io.Directory outputDir = new javaxt.io.Directory(output + relPath);
                        
                        
                        org.w3c.dom.Document xml = file.getXML();
                        org.w3c.dom.Node outerNode = javaxt.xml.DOM.getOuterNode(xml);
                        if (outerNode.getNodeName().equals("application")){
                            
                            org.w3c.dom.Node[] includes = javaxt.xml.DOM.getElementsByTagName("includes", outerNode);
                            if (includes.length>0){
                                org.w3c.dom.Node include = includes[0];
                                for (org.w3c.dom.Node node : javaxt.xml.DOM.getElementsByTagName("script", include)){
                                    
                                    String src = javaxt.xml.DOM.getAttributeValue(node, "src");
                                    
                                    if (src.length()>0){
                                        
                                        if (src.toLowerCase().startsWith(appName.toLowerCase() + "/")){ //ex. "locomotive/Application.js"
                                            src = src.substring(appName.length() + 1);
                                        }
                                        else{
                                            //???
                                        }
                                        
                                        System.out.println(src);
                                        
                                        javaxt.io.File js = new javaxt.io.File(webDir + src);
                                        if (js.exists()){

                                            
                                            String path = js.getPath().substring(x);
                                            if (path.length()==0) path = appName + ".js";
                                            else  path = path.substring(0, path.length() - 1) + ".js";
                                            

                                            System.out.println(js.getName() + " --> " + path);

                                            Script script = scripts.get(path);
                                            if (script==null){
                                                script = new Script();
                                                scripts.put(path, script);
                                            }


                                            StringBuffer str = script.src;
                                            str.append(js.getText());
                                            str.append("\r\n");


                                            java.util.Date lastModified = script.lastModified;
                                            if (lastModified==null || js.getLastModifiedTime().after(lastModified)){
                                                script.lastModified = js.getLastModifiedTime();
                                            }

                                            script.minified = js.getName().endsWith(".min.js");

                                            filter.add(js);
                                        }

                                        node.getParentNode().removeChild(node);
                                    }                                    

                                    
                                }
                                
                                
                              //Update links to javascripts
                                java.util.Iterator<String> it = scripts.keySet().iterator();
                                while (it.hasNext()){

                                    String path = it.next();
                                    Script script = scripts.get(path);
                                    String src = script.src.toString();
                                    java.util.Date lastModified = script.lastModified;
                                    javaxt.io.File js = new javaxt.io.File(outputDir + path);


                                    if (!script.minified){
                                        System.out.println("Compressing " + path + "...");
                                        java.io.Reader in = new java.io.StringReader(src);
                                        java.io.Writer out = new java.io.StringWriter();
                                        try {
                                            JavaScriptCompressor compressor = new JavaScriptCompressor(in, new YuiCompressorErrorReporter());
                                            in.close();

                                            //Writer out, int linebreak, boolean munge, boolean verbose, boolean preserveAllSemiColons, boolean disableOptimizations
                                            compressor.compress(out, -1, true, false, false, false);
                                            src = out.toString();
                                            out.close();

                                        } 
                                        catch(Exception e){
                                            e.printStackTrace();
                                            try{in.close();}catch(Exception ex){}
                                            try{out.close();}catch(Exception ex){}
                                        }
                                    }


                                  //Create/Update javascript file as needed
                                    System.out.println(path + " --> " + js);
                                    if (!js.exists() || lastModified.after(js.getLastModifiedTime()) || !js.getText().equals(src)){
                                        js.write(src);
                                        js.setLastModifiedTime(lastModified);
                                    }

                                  //Insert path to consolidated javascript file
                                    org.w3c.dom.Node node = xml.createElement("script");
                                    org.w3c.dom.NamedNodeMap attr = node.getAttributes();
                                    org.w3c.dom.Attr att = xml.createAttribute("type");
                                    att.setValue("text/javascript");
                                    attr.setNamedItem(att);
                                    att = xml.createAttribute("src");
                                    att.setValue(appName.toLowerCase() + "/" + path.replace("\\", "/"));
                                    attr.setNamedItem(att);
                                    include.appendChild(node);
                                }


                                
                                javaxt.io.File outputFile = new javaxt.io.File(outputDir, file.getName());
                                System.out.println(outputFile);
                                outputFile.write(xml);
                            }
                            

                            
                            
                        }
                    }
                }
            }
        }
        
        return filter;
    }

    
  //**************************************************************************
  //** copyFiles
  //**************************************************************************
  /** Copies files
   */
    private void copyFiles(javaxt.io.Directory input, javaxt.io.Directory output, java.util.HashSet<javaxt.io.File> filter){

        int x = input.toString().length();

        java.util.List files = input.getChildren(true, null, false);
        Object obj;
        while (true){
            synchronized (files) {
                while (files.isEmpty()) {
                  try {
                      files.wait();
                  }
                  catch (InterruptedException e) {
                      break;
                  }
                }
                obj = files.remove(0);
                files.notifyAll();
            }

            if (obj==null){
                break;
            }
            else{

                if (obj instanceof javaxt.io.File){
                    javaxt.io.File file = (javaxt.io.File) obj;
                    String path = file.getPath();
                    String ext = file.getExtension().toLowerCase();
                    String parentDir = path.substring(0, path.length()-1);
                    parentDir = parentDir.substring(parentDir.lastIndexOf(file.PathSeparator)+1);
                    
                    System.out.println(filter.contains(file) + "\t" + file);

                    if (!filter.contains(file) &&
                        !(parentDir.equalsIgnoreCase("test") || parentDir.equalsIgnoreCase("src") || parentDir.equalsIgnoreCase(".svn")) &&
                        !(ext.equals("org") || ext.equals("svn-base"))) {


                        javaxt.io.File copyTo = new javaxt.io.File(output + path.substring(x) + file.getName());
                        if (!copyTo.exists() || file.getLastModifiedTime().after(copyTo.getLastModifiedTime())) {
                            //System.out.println(copyTo);
                            file.copyTo(copyTo, true);
                        }
                    }
                }
                else{

                }

            }
        }
    }

    
  //**************************************************************************
  //** copyApps
  //**************************************************************************
  /** Copies "web" directories associated with apps.
   */
    private void copyApps(javaxt.io.Directory input, javaxt.io.Directory output, java.util.HashSet<javaxt.io.File> filter){

        int x = input.toString().length();
        
        for (javaxt.io.Directory appDir : input.getSubDirectories()){
            javaxt.io.Directory webDir = new javaxt.io.Directory(appDir + "web");
            java.util.List files = webDir.getChildren(true, null, false);
            Object obj;
            while (true){
                synchronized (files) {
                    while (files.isEmpty()) {
                      try {
                          files.wait();
                      }
                      catch (InterruptedException e) {
                          break;
                      }
                    }
                    obj = files.remove(0);
                    files.notifyAll();
                }

                if (obj==null){
                    break;
                }
                else{

                    if (obj instanceof javaxt.io.File){
                        javaxt.io.File file = (javaxt.io.File) obj;
                        String path = file.getPath();
                        String ext = file.getExtension().toLowerCase();
                        String parentDir = path.substring(0, path.length()-1);
                        parentDir = parentDir.substring(parentDir.lastIndexOf(file.PathSeparator)+1);

                        System.out.println(filter.contains(file) + "\t" + file);

                        if (!filter.contains(file) &&
                            !(parentDir.equalsIgnoreCase("test") || parentDir.equalsIgnoreCase("src") || parentDir.equalsIgnoreCase(".svn")) &&
                            !(ext.equals("org") || ext.equals("svn-base"))) {


                            javaxt.io.File copyTo = new javaxt.io.File(output + path.substring(x) + file.getName());
                            if (!copyTo.exists() || file.getLastModifiedTime().after(copyTo.getLastModifiedTime())) {
                                //System.out.println(copyTo);
                                file.copyTo(copyTo, true);
                            }
                        }
                    }
                    else{

                    }

                }
            }
        }
    }
    
    

  //**************************************************************************
  //** copyJars
  //**************************************************************************
  /** Copies jar files found in the input directory or any subdirectories.
   */
    private void copyJars(javaxt.io.Directory input, javaxt.io.Directory output){
        
        System.out.println(input);
        
        
      //Copy keystore
        for (javaxt.io.File file : input.getFiles("*.jks")){
            javaxt.io.File copyTo = new javaxt.io.File(output + file.getName());
            if (!copyTo.exists() || file.getLastModifiedTime().after(copyTo.getLastModifiedTime())) {
                System.out.println(copyTo);
                file.copyTo(copyTo, true);
            }
        }
        
        
        javaxt.io.Directory dist = new javaxt.io.Directory(input + "dist");
        int x = dist.toString().length();
        
        
        
      //Copy jars from the lib directory
        java.util.HashMap<String, javaxt.io.File> libs = new java.util.HashMap<String, javaxt.io.File>();
        javaxt.io.Directory lib = new javaxt.io.Directory(dist + "lib");
        for (javaxt.io.File file : lib.getFiles("*.jar")){
            String fileName = file.getName();
            libs.put(fileName, file);

            String path = file.toString();

            javaxt.io.File copyTo = new javaxt.io.File(output + path.substring(x));
            if (!copyTo.exists() || file.getLastModifiedTime().after(copyTo.getLastModifiedTime())) {
                System.out.println(copyTo);
                file.copyTo(copyTo, true);
            }
        }
        
        
        
        
      //Copy jars 
        java.util.List files = dist.getChildren(true, null, false);
        Object obj;
        while (true){
            synchronized (files) {
                while (files.isEmpty()) {
                  try {
                      files.wait();
                  }
                  catch (InterruptedException e) {
                      break;
                  }
                }
                obj = files.remove(0);
                files.notifyAll();
            }

            if (obj==null){
                break;
            }
            else{

                if (obj instanceof javaxt.io.File){
                    javaxt.io.File file = (javaxt.io.File) obj;
                    String fileName = file.getName();
                    String fileExt = file.getExtension().toLowerCase();
                    if (fileExt.equals("jar")){ //what else?
                        if (!libs.containsKey(fileName)){
                            
                            String path = file.getPath();

                            javaxt.io.File copyTo = new javaxt.io.File(output + path.substring(x) + file.getName());
                            if (!copyTo.exists() || file.getLastModifiedTime().after(copyTo.getLastModifiedTime())) {
                                System.out.println(copyTo);
                                file.copyTo(copyTo, true);
                            }
                            
                            
                        }
                    }
                }
            }
        }
        

    }
    
    
    
    private static class YuiCompressorErrorReporter implements ErrorReporter {
        public void warning(String message, String sourceName, int line, String lineSource, int lineOffset) {
            if (line < 0) {
                log(Level.WARNING, message);
            } else {
                log(Level.WARNING, line + ':' + lineOffset + ':' + message);
            }
        }

        public void error(String message, String sourceName, int line, String lineSource, int lineOffset) {
            if (line < 0) {
                log(Level.SEVERE, message);
            } else {
                log(Level.SEVERE, line + ':' + lineOffset + ':' + message);
            }
        }

        public EvaluatorException runtimeError(String message, String sourceName, int line, String lineSource, int lineOffset) {
            error(message, sourceName, line, lineSource, lineOffset);
            return new EvaluatorException(message);
        }

        private static void log(Level l, String msg){
            System.out.println(l + " " + msg);
        }
    }
}