All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.javafx.tools.packager.PackagerLib Maven / Gradle / Ivy

/*
 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.javafx.tools.packager;

import com.sun.javafx.tools.ant.Utils;
import com.sun.javafx.tools.packager.DeployParams.Icon;
import com.sun.javafx.tools.packager.JarSignature.InputStreamSource;
import com.sun.javafx.tools.packager.bundlers.BundleParams;
import com.sun.javafx.tools.packager.bundlers.Bundler;
import com.sun.javafx.tools.resource.DeployResource;
import com.sun.javafx.tools.resource.PackagerResource;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Writer;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSigner;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertPath;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import sun.misc.BASE64Encoder;


public class PackagerLib {
    public static final String JAVAFX_VERSION = "2.2";

    private static final ResourceBundle bundle =
            ResourceBundle.getBundle("com/sun/javafx/tools/packager/Bundle");

    private static final String dtFX = "dtjava.js";

    private static final String webfilesDir = "web-files";
    //Note: leading "." is important for IE8
    private static final String EMBEDDED_DT = "./"+webfilesDir+"/"+dtFX;

    private static final String PUBLIC_DT = "http://java.com/js/dtjava.js";

    private CreateJarParams createJarParams;
    private DeployParams deployParams;
    private CreateBSSParams createBssParams;
    private File bssTmpDir;
    private boolean isSignedJNLP;

    private enum Filter {ALL, CLASSES_ONLY, RESOURCES};

    private static String[] launcherFiles = {
        "com/javafx/main/Main.class",
        "com/javafx/main/Main$1.class",
        "com/javafx/main/Main$2.class",
        "com/javafx/main/NoJavaFXFallback.class"
    };
    private static String prefix_in_antjar = "/resources/classes/";

    private ClassLoader classLoader;

    private ClassLoader getClassLoader() throws PackagerException {
        if (classLoader == null) {
            try {
                URL[] urls = {new URL(getJfxrtPath())};
                classLoader = URLClassLoader.newInstance(urls);
            } catch (MalformedURLException ex) {
                throw new PackagerException(ex, "ERR_CantFindRuntime");
            }
        }
        return classLoader;
    }

    public static final String MANIFEST_JAVAFX_MAIN ="JavaFX-Application-Class";

    //  if set of input resources consist of SINGLE element and
    //   this element is jar file then we expect this to be request to
    //   "update" jar file
    //  Input jar file MUST be executable jar file
    //
    // Check if we are in "special case" scenario
    private File jarFileToUpdate(CreateJarParams params) {
        if (params.resources.size() == 1) {
            PackagerResource p = params.resources.get(0);
            File f = p.getFile();
            if (!f.isFile() || !f.getAbsolutePath().toLowerCase().endsWith(".jar")) {
                return null;
            }
            JarFile jf = null;
            try {
                jf = new JarFile(f);
                Manifest m = jf.getManifest(); //try to read manifest to validate it is jar
                return f;
            } catch (Exception e) {
                //treat any excepion as "not a special case" scenario
                Log.verbose(e);
            } finally {
                if (jf != null) {
                    try {
                        jf.close();
                    } catch (IOException ex) {
                    }
                }
            }
        }
        return null;
    }

    public void packageAsJar(CreateJarParams createJarParams) throws PackagerException {
        if (createJarParams == null) {
            throw new IllegalArgumentException("Parameters must not be null");
        }

        if (createJarParams.outfile == null) {
            throw new IllegalArgumentException("Output file is not specified");
        }

        this.createJarParams = createJarParams;

        //Special case: could be request for "update jar file"
        File jarToUpdate = jarFileToUpdate(createJarParams);
        Manifest m = null;

        if (jarToUpdate != null) {
            JarFile jf = null;
            try {
                //extract data we want to preserve
                Log.info("Updating jar file: "+jarToUpdate.getAbsolutePath());
                jf = new JarFile(jarToUpdate);
                m = jf.getManifest();
                if (m != null) {
                    Attributes attrs = m.getMainAttributes();
                    if (createJarParams.applicationClass == null) {
                        createJarParams.applicationClass =
                                attrs.getValue(Attributes.Name.MAIN_CLASS);
                    }
                    if (createJarParams.classpath == null) {
                        createJarParams.classpath =
                                attrs.getValue(Attributes.Name.CLASS_PATH);
                    }
                }
            } catch (IOException ex) {
                throw new PackagerException(
                        ex, "ERR_FileReadFailed", jarToUpdate.getAbsolutePath());
            } finally {
                if (jf != null) {
                    try {
                        jf.close();
                    } catch (IOException ex) {
                    }
                }
            }
        }

        if (createJarParams.applicationClass == null) {
            throw new IllegalArgumentException(
                    "Main application class is not specified");
        }

        //NOTE: This should be a save-to-temp file, then rename operation
        File applicationJar = new File(createJarParams.outdir,
                        createJarParams.outfile.endsWith(".jar")
                              ? createJarParams.outfile
                              : createJarParams.outfile + ".jar");

        if (jarToUpdate != null &&
                applicationJar.getAbsoluteFile().equals(jarToUpdate.getAbsoluteFile())) {
            try {
                File newInputJar = File.createTempFile("tempcopy", ".jar");
                newInputJar.delete();
                jarToUpdate.renameTo(newInputJar);
                jarToUpdate = newInputJar;
            } catch (IOException ioe) {
                throw new PackagerException(
                        ioe, "ERR_FileCopyFailed", jarToUpdate.getAbsolutePath());
            }
        }

        File parentFile = applicationJar.getParentFile();
        if (parentFile != null) {
            parentFile.mkdirs();
        }

        if (m == null) {
            m = new Manifest();
        }
        Attributes attr = m.getMainAttributes();
        attr.put(Attributes.Name.MANIFEST_VERSION, "1.0");
        attr.put(new Attributes.Name("Created-By"), "JavaFX Packager");

        if (createJarParams.manifestAttrs != null) {
            for (Entry e: createJarParams.manifestAttrs.entrySet()) {
                attr.put(new Attributes.Name(e.getKey()), e.getValue());
            }
        }
        if (createJarParams.embedLauncher) {
            //Reset Classpath (make sense if we are updating jar)
            attr.remove(Attributes.Name.CLASS_PATH);

            attr.put(Attributes.Name.MAIN_CLASS, "com/javafx/main/Main");
            attr.put(new Attributes.Name(MANIFEST_JAVAFX_MAIN), createJarParams.applicationClass);
            attr.put(new Attributes.Name("JavaFX-Version"), createJarParams.fxVersion);

            if (createJarParams.preloader != null) {
                attr.put(new Attributes.Name("JavaFX-Preloader-Class"), createJarParams.preloader);
            }

            if (createJarParams.classpath != null) {
                // Allow comma or semicolon as delimeter (turn them into spaces)
                String cp = createJarParams.classpath;
                cp = cp.replace(';', ' ').replace(',', ' ');
                attr.put(new Attributes.Name("JavaFX-Class-Path"), cp);
            }
            //Default fallback class uses "private" interface to
            // get additional error paramters. Do not add it to manifest
            // explicitly to avoid treating it as custom
            if (createJarParams.fallbackClass != null &&
                    !createJarParams.defaultFallbackApp.equals(
                    createJarParams.fallbackClass)) {
                attr.put(new Attributes.Name("JavaFX-Fallback-Class"),
                        createJarParams.fallbackClass);
            }

            if (createJarParams.arguments != null) {
                int idx = 1;
                for (String arg: createJarParams.arguments) {
                    attr.put(new Attributes.Name("JavaFX-Argument-"+idx),
                            encodeAsBase64(arg.getBytes()));
                    idx++;
                }
            }
            if (createJarParams.params != null) {
                int idx = 1;
                for (Param p : createJarParams.params) {
                    if (p.name != null) { //otherwise it is something weird and we skip it
                        attr.put(new Attributes.Name("JavaFX-Parameter-Name-" + idx),
                                encodeAsBase64(p.name.getBytes()));
                        if (p.value != null) { //legal, means not value specified
                            attr.put(new Attributes.Name("JavaFX-Parameter-Value-" + idx),
                                    encodeAsBase64(p.value.getBytes()));
                        }
                        idx++;
                    }
                }
            }
        } else {
            attr.put(Attributes.Name.MAIN_CLASS, createJarParams.applicationClass);
        }

        if (createJarParams.css2bin) {
            try {
                bssTmpDir = File.createTempFile("bssfiles", "");
            } catch (IOException ex) {
                throw new PackagerException(ex, "ERR_CreatingTempFileFailed");
            }
            bssTmpDir.delete();
        }

        if (applicationJar.exists() && !applicationJar.delete()) {
            throw new PackagerException(
                    "ERR_CantDeleteFile", createJarParams.outfile);
        }
        try {
            jar(m, createJarParams.resources, jarToUpdate,
                    new JarOutputStream(new FileOutputStream(applicationJar)),
                    Filter.ALL);
        } catch (IOException ex) {
            throw new PackagerException(
                    ex, "ERR_CreatingJarFailed", createJarParams.outfile);
        }

        // cleanup
        deleteDirectory(bssTmpDir);
        this.createJarParams = null;
    }

    private String readTextFile(File in) throws PackagerException {
        StringBuilder sb = new StringBuilder();
        InputStreamReader isr = null;
        try {
            char[] buf = new char[16384];
            int len;
            isr = new InputStreamReader(new FileInputStream(in));
            while ((len = isr.read(buf)) > 0) {
                sb.append(buf, sb.length(), len);
            }
        } catch (IOException ex) {
            throw new PackagerException(ex, "ERR_FileReadFailed",
                    in.getAbsolutePath());
        } finally {
            if (isr != null) {
                try {
                    isr.close();
                } catch (IOException ex) {
                }
            }
        }
        return sb.toString();
    }

    private String processTemplate(String inpText,
            Map templateStrings) {
        //Core pattern matches
        //   #DT.SCRIPT#
        //   #DT.EMBED.CODE.ONLOAD#
        //   #DT.EMBED.CODE.ONLOAD(App2)#
        String corePattern = "(#[\\w\\.\\(\\)]+#)";
        //This will match
        //   "/*", "//" or "")) ||
                    (match.startsWith("//")) ||
                    (match.startsWith("/*") && match.endsWith(" */"));

            //try to find if we have match
            String coreReplacement = null;
            //map with rules have no template ids
            //int p = coreMatch.indexOf("\\(");
            //strip leading/trailing #, then split of id part
            String parts[] = coreMatch.substring(1, coreMatch.length()-1).split("[\\(\\)]");
            String rulePart = parts[0];
            String idPart = (parts.length == 1) ?
                    //strip trailing ')'
                    null : parts[1];
            if (templateStrings.containsKey(
                    TemplatePlaceholders.fromString(rulePart))
                    && (idPart == null /* it is ok for templeteId to be not null, e.g. DT.SCRIPT.CODE */
                        || idPart.equals(deployParams.appId))) {
                coreReplacement = templateStrings.get(
                        TemplatePlaceholders.fromString(rulePart));
            }

            if (coreReplacement != null) {
                if (inComment || coreMatch.length() == match.length()) {
                    m.appendReplacement(result, coreReplacement);
                } else { // pattern matched something that is not comment
                         // Very unlikely but lets play it safe
                    int pp = match.indexOf(coreMatch);
                    String v = match.substring(0, pp) +
                            coreReplacement +
                            match.substring(pp + coreMatch.length());
                    m.appendReplacement(result, v);
                }
            }
        }
        m.appendTail(result);
        return result.toString();
    }

    private static enum Mode {FX, APPLET, SwingAPP};

    public void generateDeploymentPackages(DeployParams deployParams) throws PackagerException {
        if (deployParams == null) {
            throw new IllegalArgumentException("Parameters must not be null.");
        }
        this.deployParams = deployParams;
        boolean templateOn = !deployParams.templates.isEmpty();
        Map templateStrings = null;
        if (templateOn) {
            templateStrings =
               new EnumMap(TemplatePlaceholders.class);
        }
        try {
            //In case of FX app we will have one JNLP and one HTML
            //In case of Swing with FX we will have 2 JNLP files and one HTML
            String jnlp_filename_webstart = deployParams.outfile + ".jnlp";
            String jnlp_filename_browser
                    = deployParams.isSwingApp ?
                        (deployParams.outfile + "_browser.jnlp") : jnlp_filename_webstart;
            String html_filename = deployParams.outfile + ".html";

            //create out dir
            File odir = deployParams.outdir;
            odir.mkdirs();

            if (deployParams.includeDT) {
                extractWebFiles();
            }

            ByteArrayOutputStream jnlp_bos_webstart = new ByteArrayOutputStream();
            ByteArrayOutputStream jnlp_bos_browser = new ByteArrayOutputStream();

            //for swing case we need to generate 2 JNLP files
            if (deployParams.isSwingApp) {
               PrintStream jnlp_ps = new PrintStream(jnlp_bos_webstart);
               generateJNLP(jnlp_ps, jnlp_filename_webstart, Mode.SwingAPP);
               jnlp_ps.close();
               //save JNLP
               save(jnlp_filename_webstart, jnlp_bos_webstart.toByteArray());

               jnlp_ps = new PrintStream(jnlp_bos_browser);
               generateJNLP(jnlp_ps, jnlp_filename_browser, Mode.APPLET);
               jnlp_ps.close();
               //save JNLP
               save(jnlp_filename_browser, jnlp_bos_browser.toByteArray());

            } else {
                PrintStream jnlp_ps = new PrintStream(jnlp_bos_browser);
                generateJNLP(jnlp_ps, jnlp_filename_browser, Mode.FX);
                jnlp_ps.close();

                //save JNLP
                save(jnlp_filename_browser, jnlp_bos_browser.toByteArray());

                jnlp_bos_webstart = jnlp_bos_browser;
            }

            //we do not need html if this is component and not main app
            if (!deployParams.isExtension) {
                ByteArrayOutputStream html_bos =
                        new ByteArrayOutputStream();
                PrintStream html_ps = new PrintStream(html_bos);
                generateHTML(html_ps,
                        jnlp_bos_browser.toByteArray(), jnlp_filename_browser,
                        jnlp_bos_webstart.toByteArray(), jnlp_filename_webstart,
                        templateStrings, deployParams.isSwingApp);
                html_ps.close();

                //process template file
                if (templateOn) {
                    for (DeployParams.Template t: deployParams.templates) {
                        File out = t.out;
                        if (out == null) {
                            System.out.println(
                                    "Perform inplace substitution for " +
                                    t.in.getAbsolutePath());
                            out = t.in;
                        }
                        save(out, processTemplate(
                                readTextFile(t.in), templateStrings).getBytes());
                    }
                } else {
                    //save HTML
                    save(html_filename, html_bos.toByteArray());
                }
            }

            //copy jar files
            for (DeployResource resource: deployParams.resources) {
                copyFiles(resource, deployParams.outdir);
            }
        } catch (Exception ex) {
            throw new PackagerException(ex, "ERR_DeployFailed");
        }

        BundleParams bp = deployParams.getBundleParams();
        if (bp != null) {
           generateNativeBundles(deployParams.outdir, bp, deployParams.verbose);
        }

        this.deployParams = null;
    }

    private void generateNativeBundles(File outdir, BundleParams bp, boolean verbose) {
        outdir = new File(outdir, "bundles");

        if (bp.getRuntime() != null) {
            Log.info("Using base JDK at: "
                    + bp.getRuntime().getBaseDirectory().getAbsolutePath());
            if (Log.isDebug()) {
                bp.getRuntime().dump();
            }
        } else {
            Log.info("No base JDK. Package will use system JRE.");
        }

        List bundlers = Bundler.get(bp, Log.isDebug() || verbose);
        for (Bundler b: bundlers) {
            b.bundle(bp, outdir);
        }
    }

    private static void copyFiles(DeployResource resource, File outdir) throws IOException, PackagerException {

        if (resource.getFile().isDirectory()) {
            final File baseDir = resource.getBaseDir();
            for (File file: resource.getFile().listFiles()) {
                copyFiles(new DeployResource(baseDir, file), outdir);
            }
        } else {
            final File srcFile = resource.getFile();
            if (srcFile.exists() && srcFile.isFile()) {
                //skip file copying if jar is in the same location
                final File destFile =
                        new File(outdir, resource.getRelativePath());

                if (!srcFile.getCanonicalFile().equals(
                        destFile.getCanonicalFile())) {
                    copyFileToOutDir(new FileInputStream(srcFile),
                                     destFile);
                } else {
                    Log.verbose("Skip jar copy to itself: " +
                            resource.getRelativePath());
                }
            }
        }
    }

    public void generateBSS(CreateBSSParams params) throws PackagerException {
        if (params == null) {
            throw new IllegalArgumentException("Parameters must not be null.");
        }
        this.createBssParams = params;
        createBinaryCss(createBssParams.resources, createBssParams.outdir);
        this.createBssParams = null;
    }

    public void signJar(SignJarParams params) throws PackagerException {
        try {
            JarSignature signature = retrieveSignature(params);

            for (PackagerResource pr: params.resources) {
                signFile(pr, signature, params.outdir, params.verbose);
            }

        } catch (Exception ex) {
            Log.verbose(ex);
            throw new PackagerException("ERR_SignFailed", ex);
        }

    }


    private JarSignature retrieveSignature(SignJarParams params) throws KeyStoreException,
            NoSuchAlgorithmException, UnrecoverableKeyException, IOException,
            CertificateException, InvalidKeyException {
        if (params.keyPass == null) {
            params.keyPass = params.storePass;
        }

        if (params.keyStore == null) {
            throw new IOException("No keystore specified");
        }

        if (params.storePass == null) {
            throw new IOException("No store password specified");
        }

        if (params.storeType == null) {
            throw new IOException("No store type is specified");
        }

        KeyStore store = KeyStore.getInstance(params.storeType);
        store.load(new FileInputStream(params.keyStore), params.storePass.toCharArray());

        Certificate[] chain = store.getCertificateChain(params.alias);
        X509Certificate certChain[] = new X509Certificate[chain.length];
        for (int i=0; i argsList = new ArrayList();
        for (Object a : args) {
            if (a instanceof List) {
                argsList.addAll((List)a);
            } else if (a instanceof String) {
                argsList.add((String)a);
            }
        }
        final Process p = Runtime.getRuntime().exec(argsList.toArray(new String[argsList.size()]));
        final BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    String line;
                    while ((line = in.readLine()) != null) {
                        System.out.println(line);
                    }
                } catch (IOException ioe) {
                    Log.verbose(ioe);
                }
            }
        });
        t.setDaemon(true);
        t.start();
        final BufferedReader err = new BufferedReader(new InputStreamReader(p.getErrorStream()));
        t = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    String line;
                    while ((line = err.readLine()) != null) {
                        System.err.println(line);
                    }
                } catch (IOException ioe) {
                    Log.verbose(ioe);
                }
            }
        });
        t.setDaemon(true);
        t.start();
        return p.waitFor();
    }

    private static void scanAndCopy(PackagerResource dir, Writer out, File outdir) throws PackagerException {
        if (!dir.getFile().exists()) {
            throw new PackagerException("ERR_MissingDirectory", dir.getFile().getName());
        }
        if ((dir.getFile().listFiles() == null) || (dir.getFile().listFiles().length == 0)) {
            throw new PackagerException("ERR_EmptySourceDirectory", dir.getFile().getName());
        }
        try {
            for (File f : dir.getFile().listFiles()) {
                if (f.isDirectory()) {
                    scanAndCopy(new PackagerResource(dir.getBaseDir(), f), out, outdir);
                } else if (f.getName().endsWith(".java")) {
                    out.write('\'' + f.getAbsolutePath().replace('\\', '/') + "\'\n");
                } else {
                    copyFileToOutDir(new FileInputStream(f),
                            new File(outdir.getPath() + File.separator
                            + dir.getRelativePath() + File.separator
                            + f.getName()));
                }
            }
        } catch (IOException ex) {
            throw new PackagerException("ERR_FileCopyFailed", dir.getFile().getName());
        }
    }

    //return null if args are default
    private String getJvmArguments(boolean includeProperties) {
        StringBuilder sb = new StringBuilder();
        for(String v: deployParams.jvmargs) {
            sb.append(v);  //may need to escape if parameter has spaces
            sb.append(" ");
        }
        if (includeProperties) {
            for(String k: deployParams.properties.keySet()) {
                sb.append("-D");
                sb.append(k);
                sb.append("=");
                sb.append(deployParams.properties.get(k)); //may need to escape if value has spaces
                sb.append(" ");
            }
        }
        if (sb.length() > 0) {
            return sb.toString();
        }
        return null;
    }

    private void generateJNLP(PrintStream out, String jnlp_filename, Mode m)
            throws IOException, CertificateEncodingException
    {
        out.println("");
        //have to use "old" spec version or old javaws will fail
        // with "unknown" version exception ...
        out.println("");
        out.println("  ");
        out.println("    " +
                ((deployParams.title != null)
                ? deployParams.title : "Sample JavaFX Application") +
                "");
        out.println("    " +
                ((deployParams.vendor != null)
                ? deployParams.vendor : "Unknown vendor") +
                "");
        out.println("    " +
                ((deployParams.description != null)
                ? deployParams.description : "Sample JavaFX 2.0 application.") +
                "");
        for (Iterator it = deployParams.icons.iterator(); it.hasNext();) {
            DeployParams.Icon i = it.next();
            if (i.mode == DeployParams.RunMode.WEBSTART ||
                    i.mode == DeployParams.RunMode.ALL) {
            out.println("    ");
            }
        }

        if (deployParams.offlineAllowed && !deployParams.isExtension) {
            out.println("    ");
        }
        out.println("  ");

        if (!deployParams.isExtension) {
            //FX is platfrom specific (soon will be available for Mac and Linux too)
            out.println("  ");
            out.println("    ");
            out.println("  ");
        }

        boolean needToCloseResourceTag = false;
        //jre is available for all platforms
        if (!deployParams.isExtension) {
            out.println("  ");
            needToCloseResourceTag = true;

            String vmargs = getJvmArguments(false);
            vmargs = (vmargs == null) ? "" : " java-vm-args=\""+vmargs+"\" ";
            out.println("    ");
            for (String k : deployParams.properties.keySet()) {
                out.println("    ");
            }
        }
        String currentOS = null, currentArch = null;
        //NOTE: This should sort the list by os+arch; it will reduce the number of resource tags
        String pendingPrint = null;
        for (DeployResource resource: deployParams.resources) {
            //if not same OS or arch then open new resources element
            if (!needToCloseResourceTag ||
                ((currentOS == null && resource.getOs() != null) ||
                 currentOS != null && !currentOS.equals(resource.getOs())) ||
                ((currentArch == null && resource.getArch() != null) ||
                 currentArch != null && !currentArch.equals(resource.getArch()))) {

                //we do not print right a way as it may be empty block
                // Not all resources make sense for JNLP (e.g. data or license)
                if (needToCloseResourceTag) {
                   pendingPrint = "  \n";
                } else {
                    pendingPrint = "";
                }
                currentOS = resource.getOs();
                currentArch = resource.getArch();
                pendingPrint += "  \n";
            }
            final File srcFile = resource.getFile();
            if (srcFile.exists() && srcFile.isFile()) {
                final String relativePath = resource.getRelativePath();
                DeployResource.Type type = resource.getType();
                switch (type) {
                    case jar:
                        if (pendingPrint != null) {
                            out.print(pendingPrint);
                            pendingPrint = null;
                            needToCloseResourceTag = true;
                        }
                        out.print("    ");
                        break;
                    case jnlp:
                        if (pendingPrint != null) {
                            out.print(pendingPrint);
                            pendingPrint = null;
                            needToCloseResourceTag = true;
                        }
                        out.println("    ");
                        break;
                    case nativelib:
                        if (pendingPrint != null) {
                            out.print(pendingPrint);
                            needToCloseResourceTag = true;
                            pendingPrint = null;
                        }
                        out.println("    ");
                        break;
                }
            }
        }
        if (needToCloseResourceTag) {
            out.println("  ");
        }

        if (deployParams.allPermissions) {
            out.println("");
            out.println("  ");
            processEmbeddedCertificates(out);
            out.println("");
        }

        if (deployParams.needShortcut) {
            out.println("  ");

//            //TODO: Add support for a more sophisticated shortcut tag.
//   // install no shortcuts, and do not consider "installed"
//   // install no shortcuts, but consider "installed"
//   // install desktop shortcut, but do not consider the app "installed"
//   // install menu shortcut, and consider app "installed"
        }

        if (!deployParams.isExtension) {
            if (m == Mode.APPLET) {
                out.print("  ");
                if (deployParams.params != null) {
                    for (Param p : deployParams.params) {
                        out.println("    ");
                    }
                }
                out.println("  ");
            } else if (m == Mode.SwingAPP) {
                out.print("  ");
                if (deployParams.arguments != null) {
                    for (String a : deployParams.arguments) {
                        out.println("    " + a + "");
                    }
                }
                out.println("  ");
            } else { //JavaFX application
                //embed fallback application
                if (deployParams.fallbackApp != null) {
                    out.print("  ");
                    out.println("    ");
                    out.println("  ");
                }

                //javafx application descriptor
                out.print("  ");
                } else {
                    out.println(">");
                    if (deployParams.params != null) {
                        for (Param p : deployParams.params) {
                            out.println("    ");
                        }
                    }
                    if (deployParams.arguments != null) {
                        for (String a : deployParams.arguments) {
                            out.println("    " + a + "");
                        }
                    }
                    out.println("  ");
                }
            }
        } else {
            out.println("");
        }

        out.println("  ");
        out.println("");
    }



    private void addToList(List l, String name, String value, boolean isString) {
        String s = isString ? "'" : "";
        String v = name +" : " + s + value + s;
        l.add(v);
    }

    private String listToString(List lst, String offset) {
        StringBuilder b = new StringBuilder();
        if (lst == null || lst.isEmpty()) {
            return offset + "{}";
        }

        b.append(offset).append("{\n");
        boolean first = true;
        for (String s : lst) {
            if (!first) {
                b.append(",\n");
            }
            first = false;
            b.append(offset).append("    ");
            b.append(s);
        }
        b.append("\n");
        b.append(offset).append("}");
        return b.toString();
    }

    private String encodeAsBase64(byte inp[]) {
        BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(inp);
    }

    private void generateHTML(PrintStream out,
            byte[] jnlp_bytes_browser, String jnlpfile_browser,
            byte[] jnlp_bytes_webstart, String jnlpfile_webstart,
            Map templateStrings,
            boolean swingMode) {
        String poff = "    ";
        String poff2 = poff + poff;
        String poff3 = poff2 + poff;

        StringBuilder out_embed_dynamic = new StringBuilder();
        StringBuilder out_embed_onload = new StringBuilder();
        StringBuilder out_launch_code = new StringBuilder();

        String appletParams = getAppletParameters();
        String jnlp_content_browser = null;
        String jnlp_content_webstart = null;

        if (deployParams.embedJNLP) {
            jnlp_content_browser =
                    encodeAsBase64(jnlp_bytes_browser).replaceAll("\\r|\\n", "");
            jnlp_content_webstart =
                    encodeAsBase64(jnlp_bytes_webstart).replaceAll("\\r|\\n", "");
        }

        out.println("");
        String dtURL = deployParams.includeDT ? EMBEDDED_DT : PUBLIC_DT;
        String includeDtString = "";
        if (templateStrings != null) {
            templateStrings.put(TemplatePlaceholders.SCRIPT_URL, dtURL);
            templateStrings.put(TemplatePlaceholders.SCRIPT_CODE, includeDtString);
        }
        out.println("  " + includeDtString);

        String webstartError = "System is not setup to launch JavaFX applications. " +
                "Make sure that you have a recent Java runtime, then install JavaFX Runtime 2.0 "+
                "and check that JavaFX is enabled in the Java Control Panel.";

        List w_app = new ArrayList();
        List w_platform = new ArrayList();
        List w_callback = new ArrayList();

        addToList(w_app, "url", jnlpfile_webstart, true);
        if (jnlp_content_webstart != null) {
            addToList(w_app, "jnlp_content", jnlp_content_webstart, true);
        }

        addToList(w_platform, "javafx", deployParams.fxPlatform, true);
        String vmargs = getJvmArguments(true);
        if (vmargs != null) {
            addToList(w_platform, "jvmargs", vmargs, true);
        }

        if (!"".equals(appletParams)) {
            addToList(w_app, "params", "{"+appletParams+"}", false);
        }

        if ((deployParams.callbacks != null) && !deployParams.callbacks.isEmpty()) {
            for (JSCallback cb: deployParams.callbacks) {
                addToList(w_callback, cb.getName(), cb.getCmd(), false);
            }
        }

        //prepare content of launchApp function
        out_launch_code.append(poff2).append("dtjava.launch(");
        out_launch_code.append(listToString(w_app, poff3)).append(",\n");
        out_launch_code.append(listToString(w_platform, poff3)).append(",\n");
        out_launch_code.append(listToString(w_callback, poff3)).append("\n");
        out_launch_code.append(poff2).append(");\n");

        out.println("");

        if (templateStrings != null) {
            templateStrings.put(TemplatePlaceholders.LAUNCH_CODE,
                    out_launch_code.toString());
        }

        //applet deployment
        String appId = deployParams.appId; //if null then it will be autogenerated
        String placeholder = deployParams.placeholder;
        if (placeholder == null) { //placeholder can not be null
            placeholder = "'javafx-app-placeholder'";
        }

        //prepare content of embedApp()
        List p_app = new ArrayList();
        List p_platform = new ArrayList();
        List p_callback = new ArrayList();

        if (appId != null) {
            addToList(p_app, "id", appId, true);
        }
        if (deployParams.isSwingApp) {
          addToList(p_app, "toolkit", "swing", true);
        }
        addToList(p_app, "url", jnlpfile_browser, true);
        addToList(p_app, "placeholder", placeholder, false);
        if (deployParams.embeddedWidth != null && deployParams.embeddedHeight != null) {
          addToList(p_app, "width", ""+deployParams.embeddedWidth, true);
          addToList(p_app, "height", ""+deployParams.embeddedHeight, true);
        } else {
          addToList(p_app, "width", ""+deployParams.width, false);
          addToList(p_app, "height", ""+deployParams.height, false);
        }
        if (jnlp_content_browser != null) {
            addToList(p_app, "jnlp_content", jnlp_content_browser, true);
        }

        addToList(p_platform, "javafx", deployParams.fxPlatform, true);
        if (vmargs != null) {
            addToList(p_platform, "jvmargs", vmargs, true);
        }

        if ((deployParams.callbacks != null) && !deployParams.callbacks.isEmpty()) {
            for (JSCallback cb: deployParams.callbacks) {
                addToList(p_callback, cb.getName(), cb.getCmd(), false);
            }
        }

        if (!"".equals(appletParams)) {
            addToList(p_app, "params", "{"+appletParams+"}", false);
        }

        if (swingMode) {
            //Splash will not work in SwingMode
            //Unless user overwrites onGetSplash handler (and that means he handles splash on his own)
            // we will reset splash function to be "none"
            boolean needOnGetSplashImpl = true;
            if (deployParams.callbacks != null) {
                for (JSCallback c: deployParams.callbacks) {
                    if ("onGetSplash".equals(c.getName())) {
                        needOnGetSplashImpl = false;
                    }
                }
            }

            if (needOnGetSplashImpl) {
                addToList(p_callback, "onGetSplash", "function() {}", false);
            }
        }

        out_embed_dynamic.append("dtjava.embed(\n");
        out_embed_dynamic.append(listToString(p_app, poff3)).append(",\n");
        out_embed_dynamic.append(listToString(p_platform, poff3)).append(",\n");
        out_embed_dynamic.append(listToString(p_callback, poff3)).append("\n");

        out_embed_dynamic.append(poff2).append(");\n");

        //now wrap content with function
        String embedFuncName = "javafxEmbed" +
                ((deployParams.appId != null) ?
                   "_"+deployParams.appId : "");
        out_embed_onload.append("\n\n");

        if (templateStrings != null) {
            templateStrings.put(
                    TemplatePlaceholders.EMBED_CODE_ONLOAD,
                    out_embed_onload.toString());
            templateStrings.put(
                    TemplatePlaceholders.EMBED_CODE_DYNAMIC,
                    out_embed_dynamic.toString());
        }

        out.println(out_embed_onload.toString());

        out.println("");
        out.println("

Test page for "+deployParams.appName+"

"); String launchString = "return launchApplication('" + jnlpfile_webstart + "');"; out.println(" Webstart: " + "click to launch this app as webstart


"); out.println(""); out.println(" "); //placeholder is wrapped with single quotes already out.println("
"); out.println(""); } private void save(String fname, byte[] content) throws IOException { File odir = deployParams.outdir; save(new File(odir, fname), content); } private void save(File f, byte[] content) throws IOException { if (f.exists()) { f.delete(); } FileOutputStream fos = new FileOutputStream(f); fos.write(content); fos.close(); } private static void copyFileToOutDir( InputStream is, File fout) throws PackagerException { OutputStream out = null; final File outDir = fout.getParentFile(); try { if (!outDir.exists() && !outDir.mkdirs()) { throw new PackagerException("ERR_CreatingDirFailed", outDir.getPath()); } out = new FileOutputStream(fout); byte[] buf = new byte[16384]; int len; while ((len = is.read(buf)) > 0) { out.write(buf, 0, len); } } catch (IOException ex) { throw new PackagerException(ex, "ERR_FileCopyFailed", outDir.getPath()); } finally { try { is.close(); } catch (IOException ex) { } if (out != null) { try { out.close(); } catch (IOException ex) { } } } } private String getAppletParameters() { String result = ""; if (deployParams.htmlParams != null) { for (HtmlParam p: deployParams.htmlParams) { if (!result.isEmpty()) { result += ", "; } String escape = p.needEscape ? "\"" : ""; result += "\""+p.name+"\": " + escape + p.value + escape; } } return result; } private void jar( Manifest manifest, List files, File importJarFile, JarOutputStream jar, Filter filter) throws IOException, PackagerException { try { jar.putNextEntry(new ZipEntry("META-INF/")); jar.closeEntry(); jar.putNextEntry(new ZipEntry(JarFile.MANIFEST_NAME)); manifest.write(jar); jar.closeEntry(); alreadyAddedEntries.add("META-INF/"); if (importJarFile != null) { //updating jar file copyFromOtherJar(jar, importJarFile); } else { //normal situation for (PackagerResource pr : files) { jar(pr.getFile(), jar, filter, pr.getBaseDir().getAbsolutePath().length() + 1); } } if (createJarParams.embedLauncher) { addEmbeddedLauncher(jar); } } finally { jar.close(); alreadyAddedEntries.clear(); } } private Set alreadyAddedEntries = new HashSet(); private void createParentEntries(String relativePath, JarOutputStream jar) throws IOException { String[] pathComponents = relativePath.split("/"); StringBuilder pathSB = new StringBuilder(); // iterating over directories only, the last component is the file // or will be created next time. for (int i = 0; i < pathComponents.length - 1; i++) { pathSB.append(pathComponents[i]).append("/"); if (!alreadyAddedEntries.contains(pathSB.toString())) { jar.putNextEntry(new ZipEntry(pathSB.toString())); jar.closeEntry(); } alreadyAddedEntries.add(pathSB.toString()); } } private void addEmbeddedLauncher(JarOutputStream jar) throws IOException { for (String cls : launcherFiles) { String nm = prefix_in_antjar + cls; InputStream in = PackagerLib.class.getResourceAsStream(nm); if (in == null) { System.err.println( "Internal error. Missing embedded resource [" + cls + "]"); } jar.putNextEntry(new JarEntry(cls)); byte b[] = new byte[65000]; int i; try { while ((i = in.read(b)) > 0) { jar.write(b, 0, i); } } finally { in.close(); } jar.closeEntry(); } } //add everything but manifest from given jar file private void copyFromOtherJar(JarOutputStream jar, File inputFile) throws IOException { JarFile inJar = new JarFile(inputFile); Enumeration all = inJar.entries(); while (all.hasMoreElements()) { JarEntry je = all.nextElement(); //skip manifest or root manifest dir entry (can not add duplicate) if ("META-INF/MANIFEST.MF".equals(je.getName().toUpperCase()) || "META-INF/".equals(je.getName().toUpperCase())) { continue; } InputStream in = inJar.getInputStream(je); jar.putNextEntry(new JarEntry(je.getName())); byte b[] = new byte[65000]; int i; try { while ((i = in.read(b)) > 0) { jar.write(b, 0, i); } } finally { in.close(); } jar.closeEntry(); } } private void jar(File f, JarOutputStream jar, Filter filter, int cut) throws IOException, PackagerException { if (!f.exists()) { throw new FileNotFoundException("Input folder does not exist [" +f.getAbsolutePath()+"]"); } if (f.isDirectory()) { for (File innerFile : f.listFiles()) { jar(innerFile, jar, filter, cut); } } else if (filter == Filter.ALL || (filter == Filter.CLASSES_ONLY && f.getName().endsWith(".class")) || (filter == Filter.RESOURCES && isResource(f.getAbsolutePath()))) { final String absPath = f.getAbsolutePath(); if (absPath.endsWith("META-INF\\MANIFEST.MF") || absPath.endsWith("META-INF/MANIFEST.MF")) { return; } createParentEntries(absPath.substring(cut).replace('\\', '/'), jar); if (createJarParams.css2bin && f.getName().endsWith(".css")) { // generate bss file into temporary directory int startOfExt = absPath.lastIndexOf(".") + 1; String bssFileName = absPath .substring(cut, startOfExt) .concat("bss"); File bssFile = new File(bssTmpDir, bssFileName); bssFile.getParentFile().mkdirs(); createBinaryCss(absPath, bssFile.getAbsolutePath()); jar.putNextEntry(new ZipEntry(bssFileName.replace('\\', '/'))); f = bssFile; } else { jar.putNextEntry(new ZipEntry(absPath.substring(cut).replace('\\', '/'))); } byte b[] = new byte[65000]; int i; FileInputStream in = new FileInputStream(f); try { while ((i = in.read(b)) > 0) { jar.write(b, 0, i); } } finally { in.close(); } jar.closeEntry(); } } private void createBinaryCss(List cssResources, File outdir) throws PackagerException { for (PackagerResource cssRes: cssResources) { String relPath = cssRes.getRelativePath(); createBinaryCss(cssRes.getFile(), outdir, relPath); } } private void createBinaryCss(File f, File outdir, String relPath) throws PackagerException { if (f.isDirectory()) { for (File innerFile : f.listFiles()) { createBinaryCss(innerFile, outdir, relPath + '/' + innerFile.getName()); } } else if (f.getName().endsWith(".css")) { String cssFileName = f.getAbsolutePath(); String bssFileName = new File(outdir.getAbsolutePath(), replaceExtensionByBSS(relPath)) .getAbsolutePath(); createBinaryCss(cssFileName, bssFileName); } } // Returns path to jfxrt.jar relatively to jar containing PackagerLib.class private String getJfxrtPath() throws PackagerException { String theClassFile = "PackagerLib.class"; Class theClass = PackagerLib.class; String classUrl = theClass.getResource(theClassFile).toString(); if (!classUrl.startsWith("jar:file:") || classUrl.indexOf("!") == -1){ throw new PackagerException("ERR_CantFindRuntime"); } // Strip everything after and including the "!" classUrl = classUrl.substring(0, classUrl.lastIndexOf("!")); // Strip everything after the last "/" or "\" to get rid of the jar filename int lastIndexOfSlash = Math.max(classUrl.lastIndexOf("/"), classUrl.lastIndexOf("\\")); String jfxrtPath = classUrl.substring(0, lastIndexOfSlash) + "/../rt/lib/ext/jfxrt.jar!/"; return jfxrtPath; } private Class loadClassFromRuntime(String className) throws PackagerException { try { ClassLoader cl = getClassLoader(); return cl.loadClass(className); } catch (ClassNotFoundException ex) { throw new PackagerException(ex, "ERR_CantFindRuntime"); } } private void createBinaryCss(String cssFile, String binCssFile) throws PackagerException { String ifname = cssFile; String ofname = (binCssFile != null) ? binCssFile : replaceExtensionByBSS(ifname); // create parent directories File of = new File(ofname); File parentFile = of.getParentFile(); if (parentFile != null) { parentFile.mkdirs(); } // Using reflection because CSS parser is part of runtime // and we want to avoid dependency on jfxrt during build Class clazz; try { clazz = Class.forName("com.sun.javafx.css.parser.Css2Bin"); } catch (ClassNotFoundException e) { // class was not found with default class loader, trying to // locate it by loading from jfxrt.jar clazz = loadClassFromRuntime("com.sun.javafx.css.parser.Css2Bin"); } try { Method m = clazz.getMethod("convertToBinary", new Class[]{String.class, String.class}); m.invoke(null, ifname, ofname); } catch (Exception ex) { Throwable causeEx = ex.getCause(); String cause = (causeEx != null) ? causeEx.getMessage() : bundle.getString("ERR_UnknownReason"); throw new PackagerException(ex, "ERR_BSSConversionFailed", cssFile, cause); } } private static String replaceExtensionByBSS(String cssName) { return cssName.substring(0, cssName.lastIndexOf(".") + 1).concat("bss"); } private boolean isResource(String name) { if (name.endsWith(".class")) { return false; } if (name.endsWith(".java")) { return false; } if (name.endsWith(".fx")) { return false; } if (name.endsWith(".cvsignore")) { return false; } if (name.endsWith(".hgignore")) { return false; } if (name.endsWith("vssver.scc")) { return false; } if (name.endsWith(".DS_Store")) { return false; } if (name.endsWith("~")) { return false; } name = name.replace('\\', '/'); if (name.indexOf("/CVS/") >= 0) { return false; } if (name.indexOf("/.svn/") >= 0) { return false; } if (name.indexOf("/.hg/") >= 0) { return false; } if (name.indexOf("/.#") >= 0) { return false; } if (name.indexOf("/._") >= 0) { return false; } if (name.endsWith("#") && name.indexOf("/#") >= 0) { return false; } if (name.endsWith("%") && name.indexOf("/%") >= 0) { return false; } if (name.endsWith("MANIFEST.MF")) { return false; } return true; } private static String[] webFiles = { "javafx-loading-100x100.gif", dtFX, "javafx-loading-25x25.gif", "error.png", "upgrade_java.png", "javafx-chrome.png", "get_java.png", "upgrade_javafx.png", "get_javafx.png" }; private static String prefixWebFiles = "/resources/web-files/"; private void extractWebFiles() throws PackagerException { doExtractWebFiles(webFiles); } private void doExtractWebFiles(String lst[]) throws PackagerException { File f = new File(deployParams.outdir, webfilesDir); f.mkdirs(); for (String s: lst) { InputStream is = PackagerLib.class.getResourceAsStream(prefixWebFiles+s); if (is == null) { System.err.println("Internal error. Missing resources [" + (prefixWebFiles+s) + "]"); } else { copyFileToOutDir(is, new File(f, s)); } } } private static boolean deleteDirectory(File dir) { if (dir == null || !dir.exists()) { return false; } if (dir.isDirectory()) { for (String file : dir.list()) { deleteDirectory(new File(dir, file)); } } return dir.delete(); } private void processEmbeddedCertificates(PrintStream out) throws CertificateEncodingException, IOException { if (deployParams.embedCertificates) { Set certPaths = collectCertPaths(); String signed = isSignedJNLP ? " signedjnlp=\"true\">" : ">"; if (certPaths != null && !certPaths.isEmpty()) { out.println(" " + base64 + ""); } out.println(" "); } } } private Set collectCertPaths() throws IOException { Set result = new HashSet(); for (DeployResource resource: deployParams.resources) { final File srcFile = resource.getFile(); if (srcFile.exists() && srcFile.isFile() && srcFile.getName().toLowerCase().endsWith("jar")) { result.addAll(extractCertPaths(srcFile)); } } return result; } private Set extractCertPaths(File jar) throws IOException { Set result = new HashSet(); JarFile jf = new JarFile(jar); // need to fully read jar file to build up internal signer info map Utils.readAllFully(jf); boolean blobSigned = false; Enumeration entries = jf.entries(); while (entries.hasMoreElements()) { JarEntry je = entries.nextElement(); String entryName = je.getName(); CodeSigner[] signers = null; if (entryName.equalsIgnoreCase(JarSignature.BLOB_SIGNATURE)) { byte[] raw = Utils.getBytes(jf.getInputStream(je)); try { JarSignature js = JarSignature.load(raw); blobSigned = true; signers = js.getCodeSigners(); } catch(Exception ex) { throw new IOException(ex); } } else { signers = je.getCodeSigners(); } result.addAll(extractCertPaths(signers)); if (entryName.equalsIgnoreCase("JNLP-INF/APPLICATION.JNLP")) { isSignedJNLP = true; } // if blob and also know signed JNLP, no need to continue if (blobSigned && isSignedJNLP) { break; } } return result; } private static Collection extractCertPaths(CodeSigner[] signers) { Collection result = new ArrayList(); if (signers != null) { for (CodeSigner cs : signers) { CertPath cp = cs.getSignerCertPath(); if (cp != null) { result.add(cp); } } } return result; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy