Java tutorial
/* * Comvai maven optimizer plugin * Copyright (C) 2015 Comvai, s.r.o. All Rights Reserved. * * 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 (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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.ctoolkit.maven.plugins.optimizer; import com.google.common.base.Charsets; import com.google.common.io.Files; import com.google.javascript.jscomp.CommandLineRunner; import org.apache.maven.settings.Settings; import org.ctoolkit.maven.plugins.util.FileHelper; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.File; import java.security.Permission; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; /** * <p>JsOptimizer does followings:</p> * <ul> * <li>minify and merge all js files defined in group</li> * </ul> * <p/> * <p>Example of configuration xml</p> * <pre> * <groups> * <common> * <!-- Path to common project java-script. This file will be appended to every group --> * <js>src/main/webapp/scripts/script.js</js> * </common> * <group name="script.min.css"> <!-- Output java-script file name --> * <!-- Path to project java-script --> * <js>src/main/webapp/scripts/script.js</js> * </group> * </groups> * </pre> * * @author <a href="mailto:pohorelec@comvai.com">Jozef Pohorelec</a> */ public class JsOptimizer { private static Logger log = Logger.getLogger(JsOptimizer.class.getName()); /** * Closure root directory */ private static String closureRoot = new File(JsOptimizer.class .getResource("/org/ctoolkit/maven/plugins/optimizer/gc_closure/goog/base.js").getPath()).getParent(); /** * Closure library holder list */ private static List<String> closureLibraryList = new ArrayList<String>(); /** * Init closure libraries */ static { addClosureLib("base.js"); addClosureLib("dom" + File.separator + "dom.js"); addClosureLib("array" + File.separator + "array.js"); addClosureLib("dom" + File.separator + "tagname.js"); addClosureLib("dom" + File.separator + "classes.js"); addClosureLib("math" + File.separator + "coordinate.js"); addClosureLib("math" + File.separator + "size.js"); addClosureLib("object" + File.separator + "object.js"); addClosureLib("string" + File.separator + "string.js"); addClosureLib("useragent" + File.separator + "useragent.js"); addClosureLib("asserts" + File.separator + "asserts.js"); addClosureLib("debug" + File.separator + "error.js"); addClosureLib("net" + File.separator + "xhrio.js"); addClosureLib("debug" + File.separator + "logger.js"); addClosureLib("debug" + File.separator + "entrypointregistry.js"); addClosureLib("debug" + File.separator + "errorhandlerweakdep.js"); addClosureLib("events" + File.separator + "eventtarget.js"); addClosureLib("json" + File.separator + "json.js"); addClosureLib("net" + File.separator + "errorcode.js"); addClosureLib("net" + File.separator + "eventtype.js"); addClosureLib("net" + File.separator + "httpstatus.js"); addClosureLib("net" + File.separator + "xmlhttp.js"); addClosureLib("net" + File.separator + "xhrmonitor.js"); addClosureLib("structs" + File.separator + "structs.js"); addClosureLib("structs" + File.separator + "map.js"); addClosureLib("uri" + File.separator + "utils.js"); addClosureLib("uri" + File.separator + "uri.js"); addClosureLib("debug" + File.separator + "logbuffer.js"); addClosureLib("debug" + File.separator + "logrecord.js"); addClosureLib("disposable" + File.separator + "disposable.js"); addClosureLib("events" + File.separator + "events.js"); addClosureLib("timer" + File.separator + "timer.js"); addClosureLib("net" + File.separator + "wrapperxmlhttpfactory.js"); addClosureLib("iter" + File.separator + "iter.js"); addClosureLib("disposable" + File.separator + "idisposable.js"); addClosureLib("events" + File.separator + "browserevent.js"); addClosureLib("events" + File.separator + "event.js"); addClosureLib("events" + File.separator + "eventwrapper.js"); addClosureLib("events" + File.separator + "pools.js"); addClosureLib("net" + File.separator + "xmlhttpfactory.js"); addClosureLib("debug" + File.separator + "debug.js"); addClosureLib("structs" + File.separator + "set.js"); addClosureLib("dom" + File.separator + "browserfeature.js"); addClosureLib("reflect" + File.separator + "reflect.js"); addClosureLib("events" + File.separator + "listener.js"); addClosureLib("structs" + File.separator + "simplepool.js"); addClosureLib("useragent" + File.separator + "jscript.js"); addClosureLib("events" + File.separator + "browserfeature.js"); addClosureLib("events" + File.separator + "eventtype.js"); addClosureLib("fx" + File.separator + "fx.js"); addClosureLib("fx" + File.separator + "dom.js"); addClosureLib("fx" + File.separator + "animation.js"); addClosureLib("fx" + File.separator + "easing.js"); addClosureLib("color" + File.separator + "color.js"); addClosureLib("style" + File.separator + "style.js"); addClosureLib("color" + File.separator + "names.js"); addClosureLib("math" + File.separator + "math.js"); addClosureLib("math" + File.separator + "box.js"); addClosureLib("math" + File.separator + "rect.js"); addClosureLib("net" + File.separator + "cookies.js"); } // Closure arguments private static final String ARG_MANAGE_CLOSURE_DEPENDENCIES = "--manage_closure_dependencies"; private static final String ARG_COMPILATION_LEVEL = "--compilation_level"; private static final String ARG_JS_OUTPUT_FILE = "--js_output_file"; private static final String ARG_JS = "--js"; /** * Process js optimization * * @param pathToXml path to java-script configuration xml * @param jsOutputPath java-script output path * @param settings maven {@link Settings} */ public static void process(String pathToXml, String jsOutputPath, Settings settings) { String outputDirectory = FileHelper.getOutputDirectory(jsOutputPath); if (pathToXml == null || pathToXml.trim().isEmpty()) { log.info("Javascript path to xml is not set. Skipping javascript optimization."); return; } if (!new File(pathToXml).exists()) { throw new IllegalArgumentException("Javascript path to xml does not exists: " + pathToXml); } log.info("Starting to optimize javascript..."); try { for (JSConfig JSConfig : getJsConfigList(pathToXml)) { String jsFile = JSConfig.getOutputJavascriptName(); final String fullJsFilePath = outputDirectory + jsFile; new File(outputDirectory).mkdirs(); List<String> args = new ArrayList<>(); for (String js : JSConfig.getJsList()) { args.add(ARG_JS); args.add(js); } args.add(ARG_MANAGE_CLOSURE_DEPENDENCIES); args.add(ARG_COMPILATION_LEVEL); args.add("ADVANCED_OPTIMIZATIONS"); args.add(ARG_JS_OUTPUT_FILE); args.add(fullJsFilePath); try { // make an ugly security manager override to be able prevent js closure exit System.setSecurityManager(new MySecurityManager()); CommandLineRunner.main(args.toArray(new String[args.size()])); } catch (NoStopException e) { String jsOutputString = Files.toString(new File(fullJsFilePath), Charsets.UTF_8); log.info("JS minified output:\n===\n" + jsOutputString + "===\n"); log.info( ">>> Javascript optimization finished successfully. Optimized js file can be found at: " + fullJsFilePath); } finally { System.setSecurityManager(null); } } } catch (Exception e) { log.log(Level.SEVERE, "Error occurred during processing js: ", e); } } /** * Return list of {@link JSConfig} objects which contains name of output java-script file and source java-script files * * @param pathToXml path to xml * @return list of {@link JSConfig} objects * @throws Exception if exception occurs */ private static List<JSConfig> getJsConfigList(String pathToXml) throws Exception { List<JSConfig> list = new ArrayList<>(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document document = db.parse(pathToXml); Element groups = (Element) document.getElementsByTagName("groups").item(0); NodeList groupList = groups.getElementsByTagName("group"); for (int i = 0; i < groupList.getLength(); i++) { JSConfig jsConfig = new JSConfig(); Element group = (Element) groupList.item(i); jsConfig.setOutputJavascriptName(group.getAttribute("name")); // add closure js for (String closureJs : closureLibraryList) { jsConfig.addJs(closureJs); } // add common javascripts parseCommonJs(groups, jsConfig); // add custom javascripts NodeList jsList = group.getElementsByTagName("js"); for (int j = 0; j < jsList.getLength(); j++) { Element js = (Element) jsList.item(j); File file = new File(js.getTextContent()); if (file.exists()) { jsConfig.addJs(file.getAbsolutePath()); } else { log.warning("Javascript file does not exists - therefore will be skipped for optimization: " + js.getTextContent()); } } list.add(jsConfig); } return list; } /** * Parse common java-script files * * @param groups groups * @param jsConfig js config */ private static void parseCommonJs(Element groups, JSConfig jsConfig) { Element common = (Element) groups.getElementsByTagName("common").item(0); NodeList commonJsList = common.getElementsByTagName("js"); for (int k = 0; k < commonJsList.getLength(); k++) { Element commonJs = (Element) commonJsList.item(k); File file = new File(commonJs.getTextContent()); if (file.exists()) { jsConfig.addJs(file.getAbsolutePath()); } else { log.warning("Javascript file does not exists - therefore will be skipped for optimization: " + commonJs.getTextContent()); } } } /** * Java-script configuration class */ private static class JSConfig { private String outputJavascriptName; private List<String> jsList = new ArrayList<>(); public void setOutputJavascriptName(String outputJavascriptName) { this.outputJavascriptName = outputJavascriptName; } public String getOutputJavascriptName() { return outputJavascriptName; } public List<String> getJsList() { return jsList; } public void addJs(String js) { jsList.add(js); } } /** * Add closure library java-script file * * @param jsLib relative path to closure java-script file */ private static void addClosureLib(String jsLib) { closureLibraryList.add(closureRoot + File.separator + jsLib); } private static class MySecurityManager extends SecurityManager { @Override public void checkPermission(Permission perm) { // do nothing } @Override public void checkExit(int status) { if (status == 0) { throw new NoStopException(); } } } private static class NoStopException extends RuntimeException { } }