changeset 6495:7339a5bb9864

RT-35635: new bundlers for fxpackager RT-35969: Daemon and Services apps for fxpackager
author Danno Ferrin (shemnon) <danno.ferrin@oracle.com>
date Mon, 17 Mar 2014 09:51:09 -0600
parents c7f043d40430
children 03b02a7bcfb9
files modules/fxpackager/src/main/java/com/oracle/bundlers/AbstractBundler.java modules/fxpackager/src/main/java/com/oracle/bundlers/BasicBundlers.java modules/fxpackager/src/main/java/com/oracle/bundlers/BundlerParamInfo.java modules/fxpackager/src/main/java/com/oracle/bundlers/Bundlers.java modules/fxpackager/src/main/java/com/oracle/bundlers/EnumeratedBundlerParam.java modules/fxpackager/src/main/java/com/oracle/bundlers/InvalidBundlerParamException.java modules/fxpackager/src/main/java/com/oracle/bundlers/JreUtils.java modules/fxpackager/src/main/java/com/oracle/bundlers/StandardBundlerParam.java modules/fxpackager/src/main/java/com/oracle/bundlers/mac/MacAppStoreBundler.java modules/fxpackager/src/main/java/com/oracle/bundlers/mac/MacBaseInstallerBundler.java modules/fxpackager/src/main/java/com/oracle/bundlers/mac/MacDaemonBundler.java modules/fxpackager/src/main/java/com/oracle/bundlers/mac/MacPKGBundler.java modules/fxpackager/src/main/java/com/oracle/bundlers/windows/WindowsBundlerParam.java modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/DeployParams.java modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/Log.java modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/Main.java modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/PackagerLib.java modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/BundleParams.java modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/ConfigException.java modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/LinuxAppBundler.java modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/LinuxDebBundler.java modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/LinuxRPMBundler.java modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/MacAppBundler.java modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/MacDMGBundler.java modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/WinAppBundler.java modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/WinExeBundler.java modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/WinMsiBundler.java modules/fxpackager/src/main/native/launcher/win/WinLauncherSvc.cpp modules/fxpackager/src/main/resources/com/oracle/bundlers/StandardBundlerParam.properties modules/fxpackager/src/main/resources/com/oracle/bundlers/linux/LinuxDebBundler.properties modules/fxpackager/src/main/resources/com/oracle/bundlers/linux/LinuxRpmBundler.properties modules/fxpackager/src/main/resources/com/oracle/bundlers/mac/MacAppBundler.properties modules/fxpackager/src/main/resources/com/oracle/bundlers/mac/MacAppStoreBundler.properties modules/fxpackager/src/main/resources/com/oracle/bundlers/mac/MacBaseInstallerBundler.properties modules/fxpackager/src/main/resources/com/oracle/bundlers/mac/MacDaemonBundler.properties modules/fxpackager/src/main/resources/com/oracle/bundlers/mac/MacPKGBundler.properties modules/fxpackager/src/main/resources/com/oracle/bundlers/windows/WinExeBundler.properties modules/fxpackager/src/main/resources/com/oracle/bundlers/windows/WinMsiBundler.properties modules/fxpackager/src/main/resources/com/sun/javafx/tools/packager/Bundle.properties modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/linux/template.deb.init.script modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/linux/template.postinst modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/linux/template.postrm modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/linux/template.rpm.init.script modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/linux/template.spec modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/mac/GenericAppHiDPI.icns modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/mac/MacAppStore.entitlements modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/mac/MacAppStore_Inherit.entitlements modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/mac/launchd.plist.template modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/mac/postinstall.template modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/mac/preinstall.template modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/windows/template.iss modules/fxpackager/src/test/java/com/oracle/bundlers/BundlersTest.java modules/fxpackager/src/test/java/com/oracle/bundlers/linux/LinuxAppBundlerTest.java modules/fxpackager/src/test/java/com/oracle/bundlers/linux/LinuxRpmBundlerTest.java modules/fxpackager/src/test/java/com/oracle/bundlers/mac/MacAppBundlerTest.java modules/fxpackager/src/test/java/com/oracle/bundlers/mac/MacAppStoreBundlerTest.java modules/fxpackager/src/test/java/com/oracle/bundlers/mac/MacDMGBundlerTest.java modules/fxpackager/src/test/java/com/oracle/bundlers/mac/MacPKGBundlerTest.java modules/fxpackager/src/test/java/com/oracle/bundlers/windows/WinAppBundlerTest.java modules/fxpackager/src/test/java/com/oracle/bundlers/windows/WinEXEBundlerTest.java modules/fxpackager/src/test/java/com/oracle/bundlers/windows/WinMSIBundlerTest.java modules/fxpackager/src/test/java/com/sun/javafx/tools/packager/CLITest.java
diffstat 62 files changed, 4476 insertions(+), 1388 deletions(-) [+]
line wrap: on
line diff
--- a/modules/fxpackager/src/main/java/com/oracle/bundlers/AbstractBundler.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/oracle/bundlers/AbstractBundler.java	Mon Mar 17 09:51:09 2014 -0600
@@ -26,7 +26,6 @@
 package com.oracle.bundlers;
 
 import com.oracle.bundlers.windows.WindowsBundlerParam;
-import com.sun.javafx.tools.ant.DeployFXTask;
 import com.sun.javafx.tools.packager.Log;
 import com.sun.javafx.tools.packager.bundlers.ConfigException;
 import com.sun.javafx.tools.packager.bundlers.IOUtils;
@@ -40,44 +39,32 @@
 import java.text.MessageFormat;
 import java.util.*;
 
+import static com.oracle.bundlers.StandardBundlerParam.*;
+
 public abstract class AbstractBundler implements Bundler {
 
     private static final ResourceBundle I18N =
             ResourceBundle.getBundle("com.oracle.bundlers.AbstractBundler");
 
-    protected boolean verbose = false;
-
     public static final BundlerParamInfo<File> IMAGES_ROOT = new WindowsBundlerParam<>(
             I18N.getString("param.images-root.name"),
             I18N.getString("param.images-root.description"),
-            "imagesRoot", //KEY
+            "imagesRoot",
             File.class, null,
-            params -> {
-                File imagesRoot = new File(StandardBundlerParam.BUILD_ROOT.fetchFrom(params), "images");
-                imagesRoot.mkdirs();
-                return imagesRoot;
-            },
-            false, s -> null);
+            params -> new File(BUILD_ROOT.fetchFrom(params), "images"),
+            false, (s, p) -> null);
 
     //do not use file separator -
-    // we use it for classpath lookup and there / are not platfrom specific
+    // we use it for classpath lookup and there / are not platform specific
     public final static String BUNDLER_PREFIX = "package/";
 
     protected static final String JAVAFX_LAUNCHER_CLASS = "com.javafx.main.Main";
 
     protected Class baseResourceLoader = null;
-
-    public boolean isVerbose() {
-        return verbose;
-    }
-
-    public void setVerbose(boolean verbose) {
-        this.verbose = verbose;
-    }
-
+    
     //helper method to test if required files are present in the runtime
     public void testRuntime(Map<String, ? super Object> p, String[] file) throws ConfigException {
-        RelativeFileSet runtime = StandardBundlerParam.RUNTIME.fetchFrom(p);
+        RelativeFileSet runtime = RUNTIME.fetchFrom(p);
         if (runtime == null) {
             return; //null runtime is ok (request to use system)
         }
@@ -94,9 +81,9 @@
 
     protected void fetchResource(
             String publicName, String category,
-            String defaultName, File result)
+            String defaultName, File result, boolean verbose)
             throws IOException {
-        URL u = locateResource(publicName, category, defaultName);
+        URL u = locateResource(publicName, category, defaultName, verbose);
         if (u != null) {
             IOUtils.copyFromURL(u, result);
         } else {
@@ -108,9 +95,9 @@
 
     protected void fetchResource(
             String publicName, String category,
-            File defaultFile, File result)
+            File defaultFile, File result, boolean verbose)
             throws IOException {
-        URL u = locateResource(publicName, category, null);
+        URL u = locateResource(publicName, category, null, verbose);
         if (u != null) {
             IOUtils.copyFromURL(u, result);
         } else {
@@ -122,7 +109,7 @@
     }
 
     private URL locateResource(String publicName, String category,
-                               String defaultName) throws IOException {
+                               String defaultName, boolean verbose) throws IOException {
         URL u = null;
         boolean custom = false;
         if (publicName != null) {
@@ -145,8 +132,9 @@
     }
 
     protected String preprocessTextResource(String publicName, String category,
-                                            String defaultName, Map<String, String> pairs) throws IOException {
-        URL u = locateResource(publicName, category, defaultName);
+                                            String defaultName, Map<String, String> pairs,
+                                            boolean verbose) throws IOException {
+        URL u = locateResource(publicName, category, defaultName, verbose);
         InputStream inp = u.openStream();
         if (inp == null) {
             throw new RuntimeException("Jar corrupt? No "+defaultName+" resource!");
@@ -177,10 +165,4 @@
             Log.info("    id: " + e.getKey() + " value: " + e.getValue());
         }
     }
-
-    public void validateDynamicArguments(List<DeployFXTask.BundleArgument> dynamicArgs) {
-        for(BundlerParamInfo info: getBundleParameters()) {
-            //TODO
-        }
-    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/main/java/com/oracle/bundlers/BasicBundlers.java	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2014, 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.oracle.bundlers;
+
+import com.oracle.bundlers.mac.MacAppStoreBundler;
+import com.oracle.bundlers.mac.MacPKGBundler;
+import com.sun.javafx.tools.packager.bundlers.BundleType;
+import com.sun.javafx.tools.packager.bundlers.LinuxAppBundler;
+import com.sun.javafx.tools.packager.bundlers.LinuxDebBundler;
+import com.sun.javafx.tools.packager.bundlers.LinuxRPMBundler;
+import com.sun.javafx.tools.packager.bundlers.MacAppBundler;
+import com.sun.javafx.tools.packager.bundlers.MacDMGBundler;
+import com.sun.javafx.tools.packager.bundlers.WinAppBundler;
+import com.sun.javafx.tools.packager.bundlers.WinExeBundler;
+import com.sun.javafx.tools.packager.bundlers.WinMsiBundler;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.ServiceLoader;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * A basic bundlers collection that loads the OpenJFX default bundlers.
+ * Loads the bundlers common to OpenJFX.
+ * <UL>
+ *     <LI>Windows file image</LI>
+ *     <LI>Mac .app</LI>
+ *     <LI>Linux file image</LI>
+ *     <LI>Windows MSI</LI>
+ *     <LI>Windows EXE</LI>
+ *     <LI>Mac DMG</LI>
+ *     <LI>Mac PKG</LI>
+ *     <LI>Linux DEB</LI>
+ *     <LI>Linux RPM</LI>
+ *
+ * </UL>
+ */
+public class BasicBundlers implements Bundlers {
+
+    boolean defaultsLoaded = false;
+
+    private Collection<Bundler> bundlers = new CopyOnWriteArrayList<>();
+
+    public Collection<Bundler> getBundlers() {
+        return Collections.unmodifiableCollection(bundlers);
+    }
+
+    public Collection<Bundler> getBundlers(BundleType type) {
+        if (type == null) return Collections.emptySet();
+        switch (type) {
+            case NONE:
+                return Collections.emptySet();
+            case ALL:
+                return getBundlers();
+            default:
+                return Arrays.asList(getBundlers().stream()
+                        .filter(b -> type.equals(b.getBundleType()))
+                        .toArray(Bundler[]::new));
+        }
+    }
+
+    /**
+     * A list of the "standard" parameters that bundlers should support
+     * or fall back to when their specific parameters are not used.
+     * @return an unmodifieable collection of the standard parameters.
+     */
+    public Collection<BundlerParamInfo> getStandardParameters() {
+        //TODO enumerate the stuff in BundleParams
+        return null;
+    }
+
+    /**
+     * Loads the bundlers common to OpenJFX.
+     * <UL>
+     *     <LI>Windows file image</LI>
+     *     <LI>Mac .app</LI>
+     *     <LI>Linux file image</LI>
+     *     <LI>Windows MSI</LI>
+     *     <LI>Windows EXE</LI>
+     *     <LI>Mac DMG</LI>
+     *     <LI>Mac PKG</LI>
+     *     <LI>Linux DEB</LI>
+     *     <LI>Linux RPM</LI>
+     *
+     * </UL>
+     */
+    public void loadDefaultBundlers() {
+        if (defaultsLoaded) return;
+
+        bundlers.add(new WinAppBundler());
+        bundlers.add(new WinExeBundler());
+        bundlers.add(new WinMsiBundler());
+
+        bundlers.add(new LinuxAppBundler());
+        bundlers.add(new LinuxDebBundler());
+        bundlers.add(new LinuxRPMBundler());
+
+        bundlers.add(new MacAppBundler());
+        bundlers.add(new MacDMGBundler());
+        bundlers.add(new MacPKGBundler());
+        bundlers.add(new MacAppStoreBundler());
+
+        //bundlers.add(new JNLPBundler());
+
+        defaultsLoaded = true;
+    }
+
+    /**
+     * Loads bundlers from the META-INF/services direct
+     */
+    public void loadBundlersFromServices(ClassLoader cl) {
+        ServiceLoader<Bundler> loader = ServiceLoader.load(Bundler.class, cl);
+        for (Bundler aLoader : loader) {
+            bundlers.add(aLoader);
+        }
+    }
+
+    public void loadBundler(Bundler bundler) {
+        bundlers.add(bundler);
+    }
+}
--- a/modules/fxpackager/src/main/java/com/oracle/bundlers/BundlerParamInfo.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/oracle/bundlers/BundlerParamInfo.java	Mon Mar 17 09:51:09 2014 -0600
@@ -26,6 +26,7 @@
 package com.oracle.bundlers;
 
 import java.util.Map;
+import java.util.function.BiFunction;
 import java.util.function.Function;
 
 public class BundlerParamInfo<T> {
@@ -69,7 +70,7 @@
     /**
      * An optional string converter for command line arguments.
      */
-    Function<String, T> stringConverter;
+    BiFunction<String, Map<String, ? super Object>, T> stringConverter;
 
     public String getName() {
         return name;
@@ -127,11 +128,11 @@
         this.requiresUserSetting = requiresUserSetting;
     }
 
-    public Function<String, T> getStringConverter() {
+    public BiFunction<String, Map<String, ? super Object>,T> getStringConverter() {
         return stringConverter;
     }
 
-    public void setStringConverter(Function<String, T> stringConverter) {
+    public void setStringConverter(BiFunction<String, Map<String, ? super Object>, T> stringConverter) {
         this.stringConverter = stringConverter;
     }
 
@@ -139,7 +140,7 @@
     public final T fetchFrom(Map<String, ? super Object> params) {
         Object o = params.get(getID());
         if (o instanceof String && getStringConverter() != null) {
-            return getStringConverter().apply((String)o);
+            return getStringConverter().apply((String)o, params);
         }
 
         Class klass = getValueType();
@@ -159,6 +160,8 @@
                 o = params.get(fallback);
                 if (klass.isInstance(o)) {
                     return (T) o;
+                } else if (o instanceof String) {
+                    return getStringConverter().apply((String)o, params);
                 }
             }
         }
--- a/modules/fxpackager/src/main/java/com/oracle/bundlers/Bundlers.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/oracle/bundlers/Bundlers.java	Mon Mar 17 09:51:09 2014 -0600
@@ -25,70 +25,90 @@
 
 package com.oracle.bundlers;
 
-import com.oracle.bundlers.mac.MacAppStoreBundler;
-import com.oracle.bundlers.mac.MacPKGBundler;
-import com.sun.javafx.tools.packager.bundlers.*;
+import com.sun.javafx.tools.packager.bundlers.BundleType;
 
-import java.util.*;
-import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.ServiceLoader;
 
-public class Bundlers {
-    
+
+public interface Bundlers {
+
+    /**
+     * This convenience method will call {@link #createBundlersInstance(ClassLoader)}
+     * with the classloader that this Bundlers is loaded from.
+     *
+     * @return an instance of Bundlers loaded and configured from the current ClassLoader.
+     */
     public static Bundlers createBundlersInstance() {
         return createBundlersInstance(Bundlers.class.getClassLoader());
     }
-    
+
+    /**
+     * This convenience method will automatically load a Bundlers instance
+     * from either META-INF/services or the default
+     * {@link com.oracle.bundlers.BasicBundlers} if none are found in
+     * the services meta-inf.
+     *
+     * After instantiating the bundlers instance it will load the default
+     * bundlers via {@link #loadDefaultBundlers()} as well as requesting
+     * the services loader to load any other bundelrs via
+     * {@link #loadBundlersFromServices(ClassLoader)}.
+
+     *
+     * @param servicesClassLoader the classloader to search for
+     *                            META-INF/service registered bundlers
+     * @return an instance of Bundlers loaded and configured from the specified ClassLoader
+     */
     public static Bundlers createBundlersInstance(ClassLoader servicesClassLoader) {
-        Bundlers bundlers = new Bundlers();
+        ServiceLoader<Bundlers> bundlersLoader = ServiceLoader.load(Bundlers.class, servicesClassLoader);
+        Bundlers bundlers = null;
+        Iterator<Bundlers> iter = bundlersLoader.iterator();
+        if (iter.hasNext()) {
+            bundlers = iter.next();
+        }
+        if (bundlers == null) {
+            bundlers = new BasicBundlers();
+        }
+
         bundlers.loadDefaultBundlers();
         bundlers.loadBundlersFromServices(servicesClassLoader);
         return bundlers;
     }
 
-    private Collection<Bundler> bundlers = new CopyOnWriteArrayList<>();
+    /**
+     * Returns all of the preconfigured, requested, and manually
+     * configured bundlers loaded with this instance.
+     *
+     * @return  a read-only collection of the requested bundlers
+     */
+    Collection<Bundler> getBundlers();
 
-    public Collection<Bundler> getBundlers() {
-        return Collections.unmodifiableCollection(bundlers);
-    }
-
-    public Collection<Bundler> getBundlers(BundleType type) {
-        if (type == null) return Collections.emptySet();
-        switch (type) {
-            case NONE:
-                return Collections.emptySet();
-            case ALL:
-                return getBundlers();
-            default:
-                Collection<Bundler> results = new LinkedHashSet<>();
-                for (Bundler bundler : getBundlers()) {
-                    if (type.equals(bundler.getBundleType())) {
-                        results.add(bundler);
-                    }
-                }
-                return results;
-                //return Arrays.asList(
-                //    getBundlers().stream()
-                //        .filter(b -> type.equals(b.getBundleType()))
-                //        .toArray(Bundler[]::new));
-        }
-    }
+    /**
+     * Returns all of the preconfigured, requested, and manually
+     * configured bundlers loaded with this instance that are of
+     * a specific BundleType, such as disk images, installers, or
+     * remote installers.
+     *
+     * @return a read-only collection of the requested bundlers
+     */
+    Collection<Bundler> getBundlers(BundleType type);
 
     /**
      * A list of the "standard" parameters that bundlers should support
      * or fall back to when their specific parameters are not used.
-     * @return an unmodifieable collection of the standard parameters.
+     *
+     * @return an unmodifiable collection of the standard parameters.
      */
-    public static Collection<BundlerParamInfo> getStandardParameters() {
-        //TODO enumerate the stuff in BundleParams
-        return null;
-    }
+    Collection<BundlerParamInfo> getStandardParameters();
 
     /**
-     * Loads the bundlers common to the JDK.
+     * Loads the bundlers common to the JDK.  A typical implementation
+     * would load:
      * <UL>
-     *     <LI>Windows file tree</LI>
+     *     <LI>Windows file image</LI>
      *     <LI>Mac .app</LI>
-     *     <LI>Linux file tree</LI>
+     *     <LI>Linux file image</LI>
 
      *     <LI>Windows MSI</LI>
      *     <LI>Windows EXE</LI>
@@ -97,35 +117,25 @@
      *     <LI>Linux RPM</LI>
      *
      * </UL>
+     *
+     * This method is called from the {@link #createBundlersInstance(ClassLoader)}
+     * and {@link #createBundlersInstance()} methods.
      */
-    public void loadDefaultBundlers() {
-        bundlers.add(new WinAppBundler());
-        bundlers.add(new WinExeBundler());
-        bundlers.add(new WinMsiBundler());
-
-        bundlers.add(new LinuxAppBundler());
-        bundlers.add(new LinuxDebBundler());
-        bundlers.add(new LinuxRPMBundler());
-
-        bundlers.add(new MacAppBundler());
-        bundlers.add(new MacDMGBundler());
-        bundlers.add(new MacPKGBundler());
-        bundlers.add(new MacAppStoreBundler());
-
-        //bundlers.add(new JNLPBundler());
-    }
+    void loadDefaultBundlers();
 
     /**
-     * Loads bundlers from the META-INF/services direct
+     * Loads bundlers from the META-INF/services directly.
+     *
+     * This method is called from the {@link #createBundlersInstance(ClassLoader)}
+     * and {@link #createBundlersInstance()} methods.
      */
-    public void loadBundlersFromServices(ClassLoader cl) {
-        ServiceLoader<Bundler> loader = ServiceLoader.load(Bundler.class, cl);
-        for (Bundler aLoader : loader) {
-            bundlers.add(aLoader);
-        }
-    }
+    void loadBundlersFromServices(ClassLoader cl);
 
-    public void loadBundler(Bundler bundler) {
-        bundlers.add(bundler);
-    }
+    /**
+     * Loads a specific bundler into the set of bundlers.
+     * Useful for a manually configured bundler.
+     *
+     * @param bundler the specific bundler to add
+     */
+    void loadBundler(Bundler bundler);
 }
--- a/modules/fxpackager/src/main/java/com/oracle/bundlers/EnumeratedBundlerParam.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/oracle/bundlers/EnumeratedBundlerParam.java	Mon Mar 17 09:51:09 2014 -0600
@@ -26,6 +26,7 @@
 package com.oracle.bundlers;
 
 import java.util.*;
+import java.util.function.BiFunction;
 import java.util.function.Function;
 
 /**
@@ -49,7 +50,7 @@
                                   String[] fallbackIDs,
                                   Function<Map<String, ? super Object>, T> defaultValueFunction,
                                   boolean requiresUserSetting,
-                                  Function<String, T> stringConverter,
+                                  BiFunction<String, Map<String, ? super Object>, T> stringConverter,
                                   Map<String, T> possibleValues,
                                   boolean strict) {
         this.name = name;
--- a/modules/fxpackager/src/main/java/com/oracle/bundlers/InvalidBundlerParamException.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/oracle/bundlers/InvalidBundlerParamException.java	Mon Mar 17 09:51:09 2014 -0600
@@ -25,7 +25,7 @@
 
 package com.oracle.bundlers;
 
-public class InvalidBundlerParamException extends Exception {
+public class InvalidBundlerParamException extends RuntimeException {
     public InvalidBundlerParamException(String message) {
         super(message);
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/main/java/com/oracle/bundlers/JreUtils.java	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2012, 2014, 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.oracle.bundlers;
+
+import com.sun.javafx.tools.packager.bundlers.IOUtils;
+import com.sun.javafx.tools.packager.bundlers.RelativeFileSet;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+
+public class JreUtils {
+
+    public static class Rule {
+        String regex;
+        boolean includeRule;
+        Type type;
+        enum Type {SUFFIX, PREFIX, SUBSTR, REGEX}
+
+        private Rule(String regex, boolean includeRule, Type type) {
+            this.regex = regex;
+            this.type = type;
+            this.includeRule = includeRule;
+        }
+
+        boolean match(String str) {
+            if (type == Type.SUFFIX) {
+                return str.endsWith(regex);
+            }
+            if (type == Type.PREFIX) {
+                return str.startsWith(regex);
+            }
+            if (type == Type.SUBSTR) {
+                return str.contains(regex);
+            }
+            return str.matches(regex);
+        }
+
+        boolean treatAsAccept() {return includeRule;}
+
+        public static Rule suffix(String s) {
+            return new Rule(s, true, Type.SUFFIX);
+        }
+        public static Rule suffixNeg(String s) {
+            return new Rule(s, false, Type.SUFFIX);
+        }
+        static Rule prefix(String s) {
+            return new Rule(s, true, Type.PREFIX);
+        }
+        public static Rule prefixNeg(String s) {
+            return new Rule(s, false, Type.PREFIX);
+        }
+        static Rule substr(String s) {
+            return new Rule(s, true, Type.SUBSTR);
+        }
+        public static Rule substrNeg(String s) {
+            return new Rule(s, false, Type.SUBSTR);
+        }
+    }
+
+    public static boolean shouldExclude(File baseDir, File f, Rule ruleset[]) {
+        if (ruleset == null) {
+            return false;
+        }
+
+        String fname = f.getAbsolutePath().toLowerCase().substring(
+                baseDir.getAbsolutePath().length());
+        //first rule match defines the answer
+        for (Rule r: ruleset) {
+            if (r.match(fname)) {
+                return !r.treatAsAccept();
+            }
+        }
+        //default is include
+        return false;
+    }
+
+    public static void walk(File base, File root, Rule ruleset[], Set<File> files) {
+        if (!root.isDirectory()) {
+            if (root.isFile()) {
+                files.add(root);
+            }
+            return;
+        }
+
+        File[] lst = root.listFiles();
+        if (lst != null) {
+            for (File f : lst) {
+                //ignore symbolic links!
+                if (IOUtils.isNotSymbolicLink(f) && !shouldExclude(base, f, ruleset)) {
+                    if (f.isDirectory()) {
+                        walk(base, f, ruleset, files);
+                    } else if (f.isFile()) {
+                        //add to list
+                        files.add(f);
+                    }
+                }
+            }
+        }
+    }
+
+    public static RelativeFileSet extractJreAsRelativeFileSet(String root, JreUtils.Rule[] ruleset) {
+        File baseDir = new File(root);
+
+        Set<File> lst = new HashSet<>();
+
+        walk(baseDir, baseDir, ruleset, lst);
+
+        return new RelativeFileSet(baseDir, lst);
+    }
+
+}
--- a/modules/fxpackager/src/main/java/com/oracle/bundlers/StandardBundlerParam.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/oracle/bundlers/StandardBundlerParam.java	Mon Mar 17 09:51:09 2014 -0600
@@ -36,6 +36,7 @@
 import java.nio.file.Files;
 import java.text.MessageFormat;
 import java.util.*;
+import java.util.function.BiFunction;
 import java.util.function.Function;
 import java.util.jar.Attributes;
 import java.util.jar.JarFile;
@@ -46,7 +47,11 @@
     private static final ResourceBundle I18N =
             ResourceBundle.getBundle("com.oracle.bundlers.StandardBundlerParam");
 
-    public StandardBundlerParam(String name, String description, String id, Class<T> valueType, String[] fallbackIDs, Function<Map<String, ? super Object>, T> defaultValueFunction, boolean requiresUserSetting, Function<String, T> stringConverter) {
+    public StandardBundlerParam(String name, String description, String id,
+                                Class<T> valueType, String[] fallbackIDs,
+                                Function<Map<String, ? super Object>, T> defaultValueFunction,
+                                boolean requiresUserSetting,
+                                BiFunction<String, Map<String, ? super Object>, T> stringConverter) {
         this.name = name;
         this.description = description;
         this.id = id;
@@ -58,45 +63,16 @@
     }
 
     public static final StandardBundlerParam<RelativeFileSet> RUNTIME =
-        new StandardBundlerParam<>(
-                I18N.getString("param.runtime.name"),
-                I18N.getString("param.runtime.description"),
-                BundleParams.PARAM_RUNTIME,
-                RelativeFileSet.class,
-                null,
-                params -> extractJreAsRelativeFileSet(System.getProperty("java.home")),
-                false,
-                StandardBundlerParam::extractJreAsRelativeFileSet
-        );
-
-    public static RelativeFileSet extractJreAsRelativeFileSet(String root) {
-        File baseDir = new File(root);
-
-        boolean isMac = System.getProperty("os.name").toLowerCase().contains("os x");
-
-        //Normalization: on MacOS we need to point to the top of JDK dir
-        // (other platforms are fine)
-        if (isMac) {
-            //On Mac we need Bundle root, not jdk/Contents/Home
-            baseDir = baseDir.getParentFile().getParentFile().getParentFile();
-        }
-
-        Set<File> lst = new HashSet<>();
-
-        BundleParams.Rule ruleset[];
-        if (System.getProperty("os.name").startsWith("Mac")) {
-            ruleset = BundleParams.macRules;
-        } else if (System.getProperty("os.name").startsWith("Win")) {
-            ruleset = BundleParams.winRules;
-        } else {
-            //must be linux
-            ruleset = BundleParams.linuxRules;
-        }
-
-        BundleParams.walk(baseDir, baseDir, ruleset, lst);
-
-        return new RelativeFileSet(baseDir, lst);
-    }
+            new StandardBundlerParam<>(
+                    I18N.getString("param.runtime.name"),
+                    I18N.getString("param.runtime.description"),
+                    BundleParams.PARAM_RUNTIME,
+                    RelativeFileSet.class,
+                    null,
+                    params -> null,
+                    false,
+                    (s, p) -> null
+            );
 
     public static final StandardBundlerParam<RelativeFileSet> APP_RESOURCES =
             new StandardBundlerParam<>(
@@ -107,10 +83,10 @@
                     null,
                     null, // no default.  Required parameter
                     false,
-                    null // no string translation, tool must provide compelx type
+                    null // no string translation, tool must provide complex type
             );
 
-    public static final StandardBundlerParam<File> ICON  =
+    public static final StandardBundlerParam<File> ICON =
             new StandardBundlerParam<>(
                     I18N.getString("param.icon-file.name"),
                     I18N.getString("param.icon-file.description"),
@@ -119,70 +95,11 @@
                     null,
                     params -> null,
                     false,
-                    File::new
+                    (s, p) -> new File(s)
             );
 
-    public static final StandardBundlerParam<String> NAME  =
-            new StandardBundlerParam<>(
-                    I18N.getString("param.name.name"),
-                    I18N.getString("param.name.description"),
-                    BundleParams.PARAM_NAME,
-                    String.class,
-                    null,
-                    params -> {throw new IllegalArgumentException(MessageFormat.format(I18N.getString("error.required-parameter"), BundleParams.PARAM_NAME));},
-                    true,
-                    s -> s
-            );
 
-    public static final StandardBundlerParam<String> VENDOR  =
-            new StandardBundlerParam<>(
-                    I18N.getString("param.vendor.name"),
-                    I18N.getString("param.vendor.description"),
-                    BundleParams.PARAM_VENDOR,
-                    String.class,
-                    null,
-                    params -> I18N.getString("param.vendor.default"),
-                    false,
-                    s -> s
-            );
-
-    public static final StandardBundlerParam<String> CATEGORY  =
-            new StandardBundlerParam<>(
-                    I18N.getString("param.category.name"),
-                    I18N.getString("param.category.description"),
-                    BundleParams.PARAM_CATEGORY,
-                    String.class,
-                    null,
-                    params -> I18N.getString("param.category.default"),
-                    false,
-                    s -> s
-            );
-
-    public static final StandardBundlerParam<String> DESCRIPTION  =
-            new StandardBundlerParam<>(
-                    I18N.getString("param.description.name"),
-                    I18N.getString("param.description.description"),
-                    BundleParams.PARAM_DESCRIPTION,
-                    String.class,
-                    new String[] {NAME.getID()},
-                    params -> I18N.getString("param.description.default"),
-                    false,
-                    s -> s
-            );
-
-    public static final StandardBundlerParam<String> COPYRIGHT  =
-            new StandardBundlerParam<>(
-                    I18N.getString("param.copyright.name"),
-                    "The copyright for the application.",
-                    BundleParams.PARAM_COPYRIGHT,
-                    String.class,
-                    null,
-                    params -> MessageFormat.format(I18N.getString("param.copyright.default"), Calendar.getInstance().get(Calendar.YEAR)),
-                    false,
-                    s -> s
-            );
-
-    public static final StandardBundlerParam<String> MAIN_CLASS  =
+    public static final StandardBundlerParam<String> MAIN_CLASS =
             new StandardBundlerParam<>(
                     I18N.getString("param.main-class.name"),
                     I18N.getString("param.main-class.description"),
@@ -194,15 +111,84 @@
                         return (String) params.get(BundleParams.PARAM_APPLICATION_CLASS);
                     },
                     false,
-                    s -> s
+                    (s, p) -> s
+            );
+
+    public static final StandardBundlerParam<String> APP_NAME =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.app-name.name"),
+                    I18N.getString("param.app-name.description"),
+                    BundleParams.PARAM_NAME,
+                    String.class,
+                    null,
+                    params -> {
+                        String s = MAIN_CLASS.fetchFrom(params);
+                        if (s == null) return null;
+
+                        int idx = s.lastIndexOf(".");
+                        if (idx >= 0) {
+                            return s.substring(idx+1);
+                        }
+                        return s;
+                    },
+                    true,
+                    (s, p) -> s
+            );
+
+    public static final StandardBundlerParam<String> VENDOR =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.vendor.name"),
+                    I18N.getString("param.vendor.description"),
+                    BundleParams.PARAM_VENDOR,
+                    String.class,
+                    null,
+                    params -> I18N.getString("param.vendor.default"),
+                    false,
+                    (s, p) -> s
+            );
+
+    public static final StandardBundlerParam<String> CATEGORY =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.category.name"),
+                    I18N.getString("param.category.description"),
+                    BundleParams.PARAM_CATEGORY,
+                    String.class,
+                    null,
+                    params -> I18N.getString("param.category.default"),
+                    false,
+                    (s, p) -> s
+            );
+
+    public static final StandardBundlerParam<String> DESCRIPTION =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.description.name"),
+                    I18N.getString("param.description.description"),
+                    BundleParams.PARAM_DESCRIPTION,
+                    String.class,
+                    new String[] {APP_NAME.getID()},
+                    params -> I18N.getString("param.description.default"),
+                    false,
+                    (s, p) -> s
+            );
+
+    public static final StandardBundlerParam<String> COPYRIGHT =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.copyright.name"),
+                    I18N.getString("param.copyright.description"),
+                    BundleParams.PARAM_COPYRIGHT,
+                    String.class,
+                    null,
+                    params -> MessageFormat.format(I18N.getString("param.copyright.default"), new Date()),
+                    false,
+                    (s, p) -> s
             );
 
     // note that each bundler is likely to replace this one with their own converter
-    public static final StandardBundlerParam<RelativeFileSet> MAIN_JAR  =
+    public static final StandardBundlerParam<RelativeFileSet> MAIN_JAR =
             new StandardBundlerParam<>(
                     I18N.getString("param.main-jar.name"),
                     I18N.getString("param.main-jar.description"),
-                    "mainJar", //KEY
+                    "mainJar",
                     RelativeFileSet.class,
                     null,
                     params -> {
@@ -210,33 +196,33 @@
                         return (RelativeFileSet) params.get("mainJar");
                     },
                     false,
-                    s -> {
-                        File f  = new File(s);
+                    (s, p) -> {
+                        File f = new File(s);
                         return new RelativeFileSet(f.getParentFile(), new LinkedHashSet<>(Arrays.asList(f)));
                     }
             );
 
-    public static final StandardBundlerParam<String> MAIN_JAR_CLASSPATH  =
+    public static final StandardBundlerParam<String> MAIN_JAR_CLASSPATH =
             new StandardBundlerParam<>(
                     I18N.getString("param.main-jar-classpath.name"),
                     I18N.getString("param.main-jar-classpath.description"),
-                    "mainJarClasspath", //KEY
+                    "classpath",
                     String.class,
                     null,
                     params -> {
                         extractParamsFromAppResources(params);
-                        String cp = (String) params.get("mainJarClasspath");
+                        String cp = (String) params.get("classpath");
                         return cp == null ? "" : cp;
                     },
                     false,
-                    s -> s
+                    (s, p) -> s
             );
 
-    public static final StandardBundlerParam<Boolean> USE_FX_PACKAGING  =
+    public static final StandardBundlerParam<Boolean> USE_FX_PACKAGING =
             new StandardBundlerParam<>(
                     I18N.getString("param.use-javafx-packaging.name"),
                     I18N.getString("param.use-javafx-packaging.description"),
-                    "fxPackaging", //KEY
+                    "fxPackaging",
                     Boolean.class,
                     null,
                     params -> {
@@ -244,33 +230,33 @@
                         return (Boolean) params.get("fxPackaging");
                     },
                     false,
-                    Boolean::valueOf
+                    (s, p) -> Boolean.valueOf(s)
             );
 
     @SuppressWarnings("unchecked")
-    public static final StandardBundlerParam<List<String>> JVM_OPTIONS  =
+    public static final StandardBundlerParam<List<String>> JVM_OPTIONS =
             new StandardBundlerParam<>(
                     I18N.getString("param.jvm-options.name"),
                     I18N.getString("param.jvm-options.description"),
-                    "jvmOptions", //KEY
+                    "jvmOptions",
                     (Class<List<String>>) (Object) List.class,
                     null,
                     params -> Collections.emptyList(),
                     false,
-                    s -> Arrays.<String>asList(s.split("\\s+"))
+                    (s, p) -> Arrays.asList(s.split("\\s+"))
             );
 
     @SuppressWarnings("unchecked")
-    public static final StandardBundlerParam<Map<String, String>> JVM_PROPERTIES  =
+    public static final StandardBundlerParam<Map<String, String>> JVM_PROPERTIES =
             new StandardBundlerParam<>(
                     I18N.getString("param.jvm-system-properties.name"),
                     I18N.getString("param.jvm-system-properties.description"),
-                    "jvmProperties", //KEY
+                    "jvmProperties",
                     (Class<Map<String, String>>) (Object) Map.class,
                     null,
                     params -> Collections.emptyMap(),
                     false,
-                    s -> {
+                    (s, params) -> {
                         Map<String, String> map = new HashMap<>();
                         try {
                             Properties p = new Properties();
@@ -286,16 +272,16 @@
             );
 
     @SuppressWarnings("unchecked")
-    public static final StandardBundlerParam<Map<String, String>> USER_JVM_OPTIONS  =
+    public static final StandardBundlerParam<Map<String, String>> USER_JVM_OPTIONS =
             new StandardBundlerParam<>(
                     I18N.getString("param.user-jvm-options.name"),
                     I18N.getString("param.user-jvm-options.description"),
-                    "userJvmOptions", //KEY
+                    "userJvmOptions",
                     (Class<Map<String, String>>) (Object) Map.class,
                     null,
                     params -> Collections.emptyMap(),
                     false,
-                    s -> {
+                    (s, params) -> {
                         Map<String, String> map = new HashMap<>();
                         try {
                             Properties p = new Properties();
@@ -310,44 +296,21 @@
                     }
             );
 
-
-
-    public static final StandardBundlerParam<String> APP_NAME  =
-            new StandardBundlerParam<>(
-                    I18N.getString("param.app-name.name"),
-                    I18N.getString("param.app-name.description"),
-                    BundleParams.PARAM_APP_NAME, //KEY
-                    String.class,
-                    new String[] {BundleParams.PARAM_NAME},
-                    params -> {
-                        String s = MAIN_CLASS.fetchFrom(params);
-                        if (s == null) return null;
-
-                        int idx = s.lastIndexOf(".");
-                        if (idx >= 0) {
-                            return s.substring(idx+1);
-                        }
-                        return s;
-                    },
-                    true,
-                    s -> s
-            );
-
-    public static final StandardBundlerParam<String> TITLE  =
+    public static final StandardBundlerParam<String> TITLE =
             new StandardBundlerParam<>(
                     I18N.getString("param.title.name"),
                     I18N.getString("param.title.description"), //?? but what does it do?
                     BundleParams.PARAM_TITLE,
                     String.class,
-                    new String[] {NAME.getID()},
+                    new String[] {APP_NAME.getID()},
                     APP_NAME::fetchFrom,
                     false,
-                    s -> s
+                    (s, p) -> s
             );
 
 
     // note that each bundler is likely to replace this one with their own converter
-    public static final StandardBundlerParam<String> VERSION  =
+    public static final StandardBundlerParam<String> VERSION =
             new StandardBundlerParam<>(
                     I18N.getString("param.version.name"),
                     I18N.getString("param.version.description"),
@@ -356,36 +319,88 @@
                     null,
                     params -> I18N.getString("param.version.default"),
                     false,
-                    s -> s
+                    (s, p) -> s
             );
 
-    public static final StandardBundlerParam<Boolean> SYSTEM_WIDE  =
+    public static final StandardBundlerParam<Boolean> SYSTEM_WIDE =
             new StandardBundlerParam<>(
                     I18N.getString("param.system-wide.name"),
                     I18N.getString("param.system-wide.description"),
-                    BundleParams.PARAM_SYSTEM_WIDE, //KEY
+                    BundleParams.PARAM_SYSTEM_WIDE,
                     Boolean.class,
                     null,
                     params -> null,
                     false,
                     // valueOf(null) is false, and we actually do want null in some cases
-                    s -> (s == null || "null".equalsIgnoreCase(s))? null : Boolean.valueOf(s)
+                    (s, p) -> (s == null || "null".equalsIgnoreCase(s))? null : Boolean.valueOf(s)
             );
 
-    public static final StandardBundlerParam<Boolean> SHORTCUT_HINT  =
+    public static final StandardBundlerParam<Boolean> SERVICE_HINT  =
             new StandardBundlerParam<>(
-                    I18N.getString("param.desktop-shortcut-hint.name"),
-                    I18N.getString("param.desktop-shortcut-hint.description"),
-                    BundleParams.PARAM_SHORTCUT, //KEY
+                    I18N.getString("param.service-hint.name"),
+                    I18N.getString("param.service-hint.description"),
+                    "serviceHint",
                     Boolean.class,
                     null,
                     params -> false,
                     false,
                     // valueOf(null) is false, and we actually do want null in some cases
-                    s -> (s == null || "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s)
+                    (s, p) -> (s == null || "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s)
             );
 
-    public static final StandardBundlerParam<Boolean> MENU_HINT  =
+    public static final StandardBundlerParam<Boolean> START_ON_INSTALL  =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.start-on-install.name"),
+                    I18N.getString("param.start-on-install.description"),
+                    "startOnInstall",
+                    Boolean.class,
+                    null,
+                    params -> false,
+                    false,
+                    // valueOf(null) is false, and we actually do want null in some cases
+                    (s, p) -> (s == null || "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s)
+            );
+
+    public static final StandardBundlerParam<Boolean> STOP_ON_UNINSTALL  =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.stop-on-uninstall.name"),
+                    I18N.getString("param.stop-on-uninstall.description"),
+                    "stopOnUninstall",
+                    Boolean.class,
+                    null,
+                    params -> false,
+                    false,
+                    // valueOf(null) is false, and we actually do want null in some cases
+                    (s, p) -> (s == null || "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s)
+            );
+
+    public static final StandardBundlerParam<Boolean> RUN_AT_STARTUP  =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.run-at-startup.name"),
+                    I18N.getString("param.run-at-startup.description"),
+                    "runAtStartup",
+                    Boolean.class,
+                    null,
+                    params -> false,
+                    false,
+                    // valueOf(null) is false, and we actually do want null in some cases
+                    (s, p) -> (s == null || "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s)
+            );
+
+    public static final StandardBundlerParam<Boolean> SHORTCUT_HINT =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.desktop-shortcut-hint.name"),
+                    I18N.getString("param.desktop-shortcut-hint.description"),
+                    BundleParams.PARAM_SHORTCUT,
+                    Boolean.class,
+                    null,
+                    params -> false,
+                    false,
+                    // valueOf(null) is false, and we actually do want null in some cases
+                    (s, p) -> (s == null || "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s)
+            );
+
+    public static final StandardBundlerParam<Boolean> MENU_HINT =
             new StandardBundlerParam<>(
                     I18N.getString("param.menu-shortcut-hint.name"),
                     I18N.getString("param.menu-shortcut-hint.description"),
@@ -395,7 +410,7 @@
                     params -> true,
                     false,
                     // valueOf(null) is false, and we actually do want null in some cases
-                    s -> (s == null || "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s)
+                    (s, p) -> (s == null || "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s)
             );
 
     @SuppressWarnings("unchecked")
@@ -408,24 +423,24 @@
                     null,
                     params -> Collections.<String>emptyList(),
                     false,
-                    s -> Arrays.asList(s.split(","))
+                    (s, p) -> Arrays.asList(s.split(","))
             );
 
-    public static final BundlerParamInfo<String> LICENSE_TYPE = 
+    public static final BundlerParamInfo<String> LICENSE_TYPE =
             new StandardBundlerParam<> (
                     I18N.getString("param.license-type.name"),
                     I18N.getString("param.license-type.description"),
                     BundleParams.PARAM_LICENSE_TYPE,
                     String.class, null,
                     params -> I18N.getString("param.license-type.default"),
-                    false, s -> s
+                    false, (s, p) -> s
             );
 
     public static final StandardBundlerParam<File> BUILD_ROOT =
             new StandardBundlerParam<>(
                     I18N.getString("param.build-root.name"),
                     I18N.getString("param.build-root.description"),
-                    "buildRoot", //KEY
+                    "buildRoot",
                     File.class,
                     null,
                     params -> {
@@ -436,10 +451,10 @@
                         }
                     },
                     false,
-                    File::new
+                    (s, p) -> new File(s)
             );
 
-    public static final StandardBundlerParam<String> IDENTIFIER  =
+    public static final StandardBundlerParam<String> IDENTIFIER =
             new StandardBundlerParam<>(
                     I18N.getString("param.identifier.name"),
                     I18N.getString("param.identifier.description"),
@@ -457,10 +472,10 @@
                         return s;
                     },
                     false,
-                    s -> s
+                    (s, p) -> s
             );
 
-    public static final StandardBundlerParam<String> PREFERENCES_ID  =
+    public static final StandardBundlerParam<String> PREFERENCES_ID =
             new StandardBundlerParam<>(
                     I18N.getString("param.preferences-id.name"),
                     I18N.getString("param.preferences-id.description"),
@@ -469,7 +484,20 @@
                     new String[] {IDENTIFIER.getID()},
                     params -> null, // todo take the package of the main app class
                     false,
-                    s -> s
+                    (s, p) -> s
+            );
+
+    public static final StandardBundlerParam<Boolean> VERBOSE  =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.verbose.name"),
+                    I18N.getString("param.verbose.description"),
+                    "verbose",
+                    Boolean.class,
+                    null,
+                    params -> false,
+                    false,
+                    // valueOf(null) is false, and we actually do want null in some cases
+                    (s, p) -> (s == null || "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s)
             );
 
     public static void extractParamsFromAppResources(Map<String, ? super Object> params) {
--- a/modules/fxpackager/src/main/java/com/oracle/bundlers/mac/MacAppStoreBundler.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/oracle/bundlers/mac/MacAppStoreBundler.java	Mon Mar 17 09:51:09 2014 -0600
@@ -25,25 +25,326 @@
 
 package com.oracle.bundlers.mac;
 
-import com.oracle.bundlers.AbstractBundler;
 import com.oracle.bundlers.BundlerParamInfo;
-import com.sun.javafx.tools.packager.bundlers.BundleType;
+import com.oracle.bundlers.JreUtils;
+import com.oracle.bundlers.StandardBundlerParam;
+import com.sun.javafx.tools.packager.Log;
 import com.sun.javafx.tools.packager.bundlers.ConfigException;
+import com.sun.javafx.tools.packager.bundlers.IOUtils;
+import com.sun.javafx.tools.packager.bundlers.MacAppBundler;
 import com.sun.javafx.tools.packager.bundlers.UnsupportedPlatformException;
+import com.sun.javafx.tools.resource.mac.MacResources;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.function.Consumer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
-public class MacAppStoreBundler extends AbstractBundler {
+import static com.oracle.bundlers.JreUtils.Rule.suffix;
+import static com.oracle.bundlers.JreUtils.Rule.suffixNeg;
+import static com.oracle.bundlers.StandardBundlerParam.IDENTIFIER;
+import static com.oracle.bundlers.StandardBundlerParam.APP_NAME;
+import static com.oracle.bundlers.StandardBundlerParam.VERBOSE;
+
+public class MacAppStoreBundler extends MacBaseInstallerBundler {
+
+    private static final ResourceBundle I18N =
+            ResourceBundle.getBundle("com.oracle.bundlers.mac.MacAppStoreBundler");
+
+    private static final String TEMPLATE_BUNDLE_ICON_HIDPI = "GenericAppHiDPI.icns";
+    private final static String DEFAULT_ENTITLEMENTS = "MacAppStore.entitlements";
+    private final static String DEFAULT_INHERIT_ENTITLEMENTS = "MacAppStore_Inherit.entitlements";
+
+    //Subsetting of JRE is restricted.
+    //JRE README defines what is allowed to strip:
+    //   http://www.oracle.com/technetwork/java/javase/jre-7-readme-430162.html //TODO update when 8 goes GA
+    //
+    public static final JreUtils.Rule[] MAC_APP_STORE_JDK_RULES =  new JreUtils.Rule[]{
+            suffixNeg("macos/libjli.dylib"),
+            suffixNeg("resources"),
+            suffixNeg("home/bin"),
+            suffixNeg("home/db"),
+            suffixNeg("home/demo"),
+            suffixNeg("home/include"),
+            suffixNeg("home/lib"),
+            suffixNeg("home/man"),
+            suffixNeg("home/release"),
+            suffixNeg("home/sample"),
+            suffixNeg("home/src.zip"),
+            //"home/rt" is not part of the official builds
+            // but we may be creating this symlink to make older NB projects
+            // happy. Make sure to not include it into final artifact
+            suffixNeg("home/rt"),
+            suffixNeg("jre/bin"),
+            suffixNeg("bin/rmiregistry"),
+            suffixNeg("bin/tnameserv"),
+            suffixNeg("bin/keytool"),
+            suffixNeg("bin/klist"),
+            suffixNeg("bin/ktab"),
+            suffixNeg("bin/policytool"),
+            suffixNeg("bin/orbd"),
+            suffixNeg("bin/servertool"),
+            suffixNeg("bin/javaws"),
+            suffixNeg("bin/java"),
+            //Rule.suffixNeg("jre/lib/ext"), //need some of jars there for https to work
+            suffixNeg("jre/lib/nibs"),
+            //keep core deploy APIs but strip plugin dll
+            //Rule.suffixNeg("jre/lib/deploy"),
+            //Rule.suffixNeg("jre/lib/deploy.jar"),
+            //Rule.suffixNeg("jre/lib/javaws.jar"),
+            //Rule.suffixNeg("jre/lib/libdeploy.dylib"),
+            //Rule.suffixNeg("jre/lib/plugin.jar"),
+            suffixNeg("lib/libnpjp2.dylib"),
+            suffixNeg("lib/security/javaws.policy"),
+
+            // jfxmedia uses QuickTime, which is not allowed as of OSX 10.9
+            suffixNeg("lib/libjfxmedia.dylib"),
+
+            // the plist is needed for signing
+            suffix("Info.plist"),
+
+    };
+
+    public static final BundlerParamInfo<String> MAC_APP_STORE_SIGNING_KEY_USER = new StandardBundlerParam<>(
+            I18N.getString("param.signing-key-name.name"),
+            I18N.getString("param.signing-key-name.description"),
+            "mac.signing-key-user-name",
+            String.class,
+            null,
+            params -> {
+                try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(baos)) {
+                    ProcessBuilder pb = new ProcessBuilder(
+                            "dscacheutil",
+                            "-q", "user", "-a", "name", System.getProperty("user.name"));
+
+                    IOUtils.exec(pb, Log.isDebug(), false, ps);
+
+                    String commandOutput = baos.toString();
+
+                    Pattern pattern = Pattern.compile(".*gecos: (.*)");
+                    Matcher matcher = pattern.matcher(commandOutput);
+                    if (matcher.matches()) {
+                        return (matcher.group(1));
+                    }
+                } catch (IOException ioe) {
+                    Log.info("Error retrieving gecos name");
+                    Log.debug(ioe);
+                }
+                return null;
+            },
+            false,
+            null);
+
+    public static final BundlerParamInfo<String> MAC_APP_STORE_APP_SIGNING_KEY = new StandardBundlerParam<>(
+            I18N.getString("param.signing-key-app.name"),
+            I18N.getString("param.signing-key-app.description"),
+            "mac.signing-key-app",
+            String.class,
+            null,
+            params -> "3rd Party Mac Developer Application: " + MAC_APP_STORE_SIGNING_KEY_USER.fetchFrom(params),
+            false,
+            (s, p) -> s);
+
+    public static final BundlerParamInfo<String> MAC_APP_STORE_PKG_SIGNING_KEY = new StandardBundlerParam<>(
+            I18N.getString("param.signing-key-pkg.name"),
+            I18N.getString("param.signing-key-pkg.description"),
+            "mac.signing-key-pkg",
+            String.class,
+            null,
+            params -> "3rd Party Mac Developer Installer: " + MAC_APP_STORE_SIGNING_KEY_USER.fetchFrom(params),
+            false,
+            (s, p) -> s);
+
+    public static final StandardBundlerParam<File> MAC_APP_STORE_ENTITLEMENTS  = new StandardBundlerParam<>(
+            I18N.getString("param.mac-app-store-entitlements.name"),
+            I18N.getString("param.mac-app-store-entitlements.description"),
+            "mac.app-store-entitlements",
+            File.class,
+            null,
+            params -> null,
+            false,
+            (s, p) -> new File(s));
+
+    public MacAppStoreBundler() {
+        super();
+        baseResourceLoader = MacResources.class;
+    }
+
+    //@Override
+    public File bundle(Map<String, ? super Object> p, File outdir) {
+        Log.info("Building Mac App Store Bundle for " + APP_NAME.fetchFrom(p));
+
+        // first, load in some overrides
+        // icns needs @2 versions, so load in the @2 default
+        p.put(MacAppBundler.DEFAULT_ICNS_ICON.getID(), TEMPLATE_BUNDLE_ICON_HIDPI);
+
+        // next we need to change the jdk/jre stripping to strip gstreamer
+        p.put(MacAppBundler.MAC_JDK_RULES.getID(), MAC_APP_STORE_JDK_RULES);
+
+        // now we create the app
+        File appImageDir = APP_IMAGE_BUILD_ROOT.fetchFrom(p);
+        try {
+            appImageDir.mkdirs();
+            File appLocation = prepareAppBundle(p);
+
+            prepareEntitlements(p);
+
+            List<String> args = new ArrayList<>();
+            args.addAll(Arrays.asList(
+                    "codesign",
+                    "-s", MAC_APP_STORE_APP_SIGNING_KEY.fetchFrom(p), // sign with this key
+                    "-f", // replace all existing signatures
+                    "--entitlements", getConfig_Entitlements(p).toString() // entitlements
+            ));
+
+            // sign all dylibs and jars
+            List<String> signTargets = Files.walk(appLocation.toPath())
+                    .map(Path::toString)
+                    .filter(s -> (s.endsWith(".jar")
+                            || s.endsWith(".dylib"))
+                    )
+                    .collect(Collectors.toList());
+
+            args.addAll(signTargets);
+            ProcessBuilder pb = new ProcessBuilder(args);
+            IOUtils.exec(pb, VERBOSE.fetchFrom(p));
+
+            // sign all contained executables with an inherit entitlement
+            Files.find(appLocation.toPath().resolve("Contents"), Integer.MAX_VALUE,
+                    (path, attr) -> (Files.isExecutable(path) && Files.isRegularFile(path)))
+                    .filter(path -> (!path.toString().endsWith(".dylib")))
+                    .forEach(path -> {
+                        try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(baos)) {
+                            ProcessBuilder pb2 = new ProcessBuilder("codesign",
+                                    "-s", MAC_APP_STORE_APP_SIGNING_KEY.fetchFrom(p), // sign with this key
+                                    "-f", // replace all existing signatures
+                                    "--prefix", IDENTIFIER.fetchFrom(p), // use the identifier as a prefix
+                                    "--entitlements", getConfig_Inherit_Entitlements(p).toString(), // entitlements
+                                    path.toString());
+                            IOUtils.exec(pb2, VERBOSE.fetchFrom(p));
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+                    });
+
+            // sign all plugins and frameworks
+            Consumer<? super Path> signIdentifiedByPList = path -> {
+                try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(baos)) {
+                    ProcessBuilder pb2 = new ProcessBuilder("/usr/libexec/PlistBuddy",
+                            "-c", "Print :CFBundleIdentifier", path.resolve("Contents/Info.plist").toString());
+                    IOUtils.exec(pb2, VERBOSE.fetchFrom(p), false, ps);
+                    String bundleID = baos.toString();
+
+                    pb2 = new ProcessBuilder("codesign",
+                            "-s", MAC_APP_STORE_APP_SIGNING_KEY.fetchFrom(p), // sign with this key
+                            "-f", // replace all existing signatures
+                            //"-i", bundleID, // sign the bundle's CFBundleIdentifier
+                            path.toString());
+                    IOUtils.exec(pb2, VERBOSE.fetchFrom(p));
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            };
+            Path pluginsPath = appLocation.toPath().resolve("Contents/PlugIns");
+            if (Files.isDirectory(pluginsPath)) {
+                Files.list(pluginsPath)
+                        .forEach(signIdentifiedByPList);
+            }
+            Path frameworkPath = appLocation.toPath().resolve("Contents/Frameworks");
+            if (Files.isDirectory(frameworkPath)) {
+                Files.list(frameworkPath)
+                        .forEach(signIdentifiedByPList);
+            }
+
+            // sign the app itself
+            pb = new ProcessBuilder("codesign",
+                    "-s", MAC_APP_STORE_APP_SIGNING_KEY.fetchFrom(p), // sign with this key
+                    "-f", // replace all existing signatures
+                    "--entitlements", getConfig_Entitlements(p).toString(), // entitlements
+                    appLocation.toString());
+            IOUtils.exec(pb, VERBOSE.fetchFrom(p));
+
+            // create the final pkg file
+            File finalPKG = new File(outdir, APP_NAME.fetchFrom(p)+".pkg");
+            outdir.mkdirs();
+
+            pb = new ProcessBuilder("productbuild",
+                    "--component", appLocation.toString(), "/Applications",
+                    "--sign", MAC_APP_STORE_PKG_SIGNING_KEY.fetchFrom(p),
+                    "--product", appLocation + "/Contents/Info.plist",
+                    finalPKG.getAbsolutePath());
+            IOUtils.exec(pb, VERBOSE.fetchFrom(p));
+            return finalPKG;
+        } catch (Exception ex) {
+            Log.info("App Store Ready Bundle failed : " + ex.getMessage());
+            ex.printStackTrace();
+            Log.debug(ex);
+            return null;
+        }
+    }
+
+    private File getConfig_Entitlements(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params), APP_NAME.fetchFrom(params) + ".entitlements");
+    }
+
+    private File getConfig_Inherit_Entitlements(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params), APP_NAME.fetchFrom(params) + "_Inherit.entitlements");
+    }
+
+    private void prepareEntitlements(Map<String, ? super Object> params) throws IOException {
+        File entitlements = MAC_APP_STORE_ENTITLEMENTS.fetchFrom(params);
+        if (entitlements == null || !entitlements.exists()) {
+            fetchResource(getEntitlementsFileName(params),
+                    I18N.getString("resource.mac-app-store-entitlements"),
+                    DEFAULT_ENTITLEMENTS,
+                    getConfig_Entitlements(params),
+                    VERBOSE.fetchFrom(params));
+        } else {
+            fetchResource(getEntitlementsFileName(params),
+                    I18N.getString("resource.mac-app-store-entitlements"),
+                    entitlements,
+                    getConfig_Entitlements(params),
+                    VERBOSE.fetchFrom(params));
+        }
+        fetchResource(getInheritEntitlementsFileName(params),
+                I18N.getString("resource.mac-app-store-inherit-entitlements"),
+                DEFAULT_INHERIT_ENTITLEMENTS,
+                getConfig_Inherit_Entitlements(params),
+                VERBOSE.fetchFrom(params));
+    }
+
+    private String getEntitlementsFileName(Map<String, ? super Object> params) {
+        return MacAppBundler.MAC_BUNDLER_PREFIX+ APP_NAME.fetchFrom(params) +".entitlements";
+    }
+
+    private String getInheritEntitlementsFileName(Map<String, ? super Object> params) {
+        return MacAppBundler.MAC_BUNDLER_PREFIX+ APP_NAME.fetchFrom(params) +"_Inherit.entitlements";
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////
+    // Implement Bundler
+    //////////////////////////////////////////////////////////////////////////////////
+
     @Override
     public String getName() {
-        return null;
+        return I18N.getString("bundler.name");
     }
 
     @Override
     public String getDescription() {
-        return null;
+        return I18N.getString("bundler.description");
     }
 
     @Override
@@ -52,22 +353,40 @@
     }
 
     @Override
-    public BundleType getBundleType() {
-        return null;
-    }
-
-    @Override
     public Collection<BundlerParamInfo<?>> getBundleParameters() {
-        return null;
+        //Add PKG Specific parameters as required
+        return super.getBundleParameters();
     }
 
     @Override
     public boolean validate(Map<String, ? super Object> params) throws UnsupportedPlatformException, ConfigException {
-        return false;
+        try {
+            if (params == null) throw new ConfigException(
+                    I18N.getString("error.parameters-null"),
+                    I18N.getString("error.parameters-null.advice"));
+
+            // hdiutil is always available so there's no need to test for availability.
+            //run basic validation to ensure requirements are met
+
+            //run basic validation to ensure requirements are met
+            //we are not interested in return code, only possible exception
+            APP_BUNDLER.fetchFrom(params).doValidate(params);
+
+            // more stringent app store validations
+            // check the icons, make sure it has hidpi icons
+            // check the category, make sure it fits in the list apple has provided
+            // make sure we have settings for signatures
+            // validate bundle identifier is reverse dns
+            //  check for \a+\.\a+\..
+
+            return true;
+        } catch (RuntimeException re) {
+            throw new ConfigException(re);
+        }
     }
 
     @Override
     public File execute(Map<String, ? super Object> params, File outputParentDir) {
-        return null;
+        return bundle(params, outputParentDir);
     }
 }
--- a/modules/fxpackager/src/main/java/com/oracle/bundlers/mac/MacBaseInstallerBundler.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/oracle/bundlers/mac/MacBaseInstallerBundler.java	Mon Mar 17 09:51:09 2014 -0600
@@ -31,84 +31,134 @@
 import com.sun.javafx.tools.packager.bundlers.BundleType;
 import com.sun.javafx.tools.packager.bundlers.ConfigException;
 import com.sun.javafx.tools.packager.bundlers.MacAppBundler;
+import com.sun.javafx.tools.packager.bundlers.UnsupportedPlatformException;
 
 import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
+import java.text.MessageFormat;import java.util.Arrays;
 import java.util.Collection;
 import java.util.LinkedHashSet;
 import java.util.Map;
+import java.util.ResourceBundle;
+
+import static com.oracle.bundlers.StandardBundlerParam.*;
 
 public abstract class MacBaseInstallerBundler extends AbstractBundler {
 
+    private static final ResourceBundle I18N =
+            ResourceBundle.getBundle("com.oracle.bundlers.mac.MacBaseInstallerBundler");
+
     //This could be generalized more to be for any type of Image Bundler
     protected final BundlerParamInfo<MacAppBundler> APP_BUNDLER = new StandardBundlerParam<>(
-            "Mac App Bundler",
-            "Creates a .app bundle for the Mac",
-            "MacAppBundler",
+            I18N.getString("param.app-bundler.name"),
+            I18N.getString("param.app-bundle.description"),
+            "mac.app.bundler",
             MacAppBundler.class,
             null,
             params -> new MacAppBundler(),
             false,
-            s -> null);
+            (s, p) -> null);
 
     protected final BundlerParamInfo<File> APP_IMAGE_BUILD_ROOT = new StandardBundlerParam<>(
-            "",
-            "This is temporary location built by the packager that is the root of the image application",
-            "appImageRoot", File.class, null,
+            I18N.getString("param.app-image-build-root.name"),
+            I18N.getString("param.app-image-build-root.description"),
+            "mac.app.imageRoot",
+            File.class,
+            null,
             params -> {
                 File imageDir = IMAGES_ROOT.fetchFrom(params);
+                if (!imageDir.exists()) imageDir.mkdirs();
                 return new File(imageDir, getID()+ ".image");
             },
-            false, File::new);
+            false,
+            (s, p) -> new File(s));
 
+    public static final StandardBundlerParam<File> MAC_APP_IMAGE = new StandardBundlerParam<>(
+            I18N.getString("param.app-image.name"),
+            I18N.getString("param.app-image.description"),
+            "mac.app.image",
+            File.class,
+            null,
+            params -> null,
+            false,
+            (s, p) -> new File(s));
 
 
-    public static final StandardBundlerParam<File> MAC_APP_IMAGE =
-            new StandardBundlerParam<>(
-                    "Image Directory",
-                    "Location of the image that will be used to build either a DMG or PKG installer.",
-                    "mac.app.image",
-                    File.class,
-                    null,
-                    params -> null,
-                    false,
-                    File::new
-            );
+    protected final BundlerParamInfo<MacDaemonBundler> DAEMON_BUNDLER = new StandardBundlerParam<>(
+            I18N.getString("param.daemon-bundler.name"),
+            I18N.getString("param.daemon-bundler.description"),
+            "mac.daemon.bundler",
+            MacDaemonBundler.class,
+            null,
+            params -> new MacDaemonBundler(),
+            false,
+            (s, p) -> null);
 
 
+    protected final BundlerParamInfo<File> DAEMON_IMAGE_BUILD_ROOT = new StandardBundlerParam<>(
+            I18N.getString("param.daemon-image-build-root.name"),
+            I18N.getString("param.daemon-image-build-root.description"),
+            "mac.daemon.image",
+            File.class,
+            null,
+            params -> {
+                File imageDir = IMAGES_ROOT.fetchFrom(params);
+                if (!imageDir.exists()) imageDir.mkdirs();
+                return new File(imageDir, getID()+ ".daemon");
+            },
+            false,
+            (s, p) -> new File(s));
+
 
     protected final BundlerParamInfo<File> CONFIG_ROOT = new StandardBundlerParam<>(
-            "", "", "configRoot", File.class, null,
+            I18N.getString("param.config-root.name"),
+            I18N.getString("param.config-root.description"),
+            "configRoot",
+            File.class,
+            null,
             params -> {
-                File imagesRoot = new File(StandardBundlerParam.BUILD_ROOT.fetchFrom(params), "macosx");
+                File imagesRoot = new File(BUILD_ROOT.fetchFrom(params), "macosx");
                 imagesRoot.mkdirs();
                 return imagesRoot;
             },
-            false, s -> null);
+            false, (s, p) -> null);
 
-
-    public static File getPredefinedImage(Map<String, ? super Object> p) throws ConfigException {
+    public static File getPredefinedImage(Map<String, ? super Object> p) {
         File applicationImage = null;
         if (MAC_APP_IMAGE.fetchFrom(p) != null) {
             applicationImage = MAC_APP_IMAGE.fetchFrom(p);
             if (!applicationImage.exists()) {
-                throw new ConfigException(
-                        "Specified image directory " + MAC_APP_IMAGE.getID()+ ": " + applicationImage.toString() + " does not exists",
-                        "Confirm that the value for " + MAC_APP_IMAGE.getID()+ " exists");
+                throw new RuntimeException(
+                        MessageFormat.format(I18N.getString("message.app-image-dir-does-not-exist"), MAC_APP_IMAGE.getID(), applicationImage.toString()));
             }
         }
         return applicationImage;
     }
 
-    protected boolean prepareAppBundle(Map<String, ? super Object> p) throws ConfigException {
+    protected void validateAppImageAndBundeler(Map<String, ? super Object> params) throws ConfigException, UnsupportedPlatformException {
+        if (MAC_APP_IMAGE.fetchFrom(params) != null) {
+            File applicationImage = MAC_APP_IMAGE.fetchFrom(params);
+            if (!applicationImage.exists()) {
+                throw new ConfigException(
+                        MessageFormat.format(I18N.getString("message.app-image-dir-does-not-exist"), MAC_APP_IMAGE.getID(), applicationImage.toString()),
+                        MessageFormat.format(I18N.getString("message.app-image-dir-does-not-exist.advice"), MAC_APP_IMAGE.getID()));
+            }
+        } else {
+            APP_BUNDLER.fetchFrom(params).doValidate(params);
+        }
+    }
+
+    protected File prepareAppBundle(Map<String, ? super Object> p) {
         if (getPredefinedImage(p) != null) {
-            return true;
+            return null;
         }
 
         File appImageRoot = APP_IMAGE_BUILD_ROOT.fetchFrom(p);
-        File appDir = APP_BUNDLER.fetchFrom(p).doBundle(p, appImageRoot, true);
-        return appDir != null;
+        return APP_BUNDLER.fetchFrom(p).doBundle(p, appImageRoot, true);
+    }
+
+    protected File prepareDaemonBundle(Map<String, ? super Object> p) throws ConfigException {
+        File daemonImageRoot = DAEMON_IMAGE_BUILD_ROOT.fetchFrom(p);
+        return DAEMON_BUNDLER.fetchFrom(p).doBundle(p, daemonImageRoot, true);        
     }
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/main/java/com/oracle/bundlers/mac/MacDaemonBundler.java	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2014, 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.oracle.bundlers.mac;
+
+import static com.oracle.bundlers.StandardBundlerParam.*;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import com.oracle.bundlers.AbstractBundler;
+import com.oracle.bundlers.BundlerParamInfo;
+import com.oracle.bundlers.StandardBundlerParam;
+
+import com.sun.javafx.tools.packager.Log;
+import com.sun.javafx.tools.packager.bundlers.BundleType;
+import com.sun.javafx.tools.packager.bundlers.ConfigException;
+import com.sun.javafx.tools.packager.bundlers.IOUtils;
+import com.sun.javafx.tools.packager.bundlers.UnsupportedPlatformException;
+import com.sun.javafx.tools.resource.mac.MacResources;
+
+public class MacDaemonBundler extends AbstractBundler {
+
+    private static final ResourceBundle I18N =
+            ResourceBundle.getBundle("com.oracle.bundlers.mac.MacDaemonBundler");
+    
+    private static final String TEMPLATE_LAUNCHD_PLIST  = "launchd.plist.template";
+
+    public final static String MAC_BUNDLER_PREFIX =
+            BUNDLER_PREFIX + "macosx" + File.separator;
+    
+    public static final BundlerParamInfo<File> CONFIG_ROOT = new StandardBundlerParam<>(
+            I18N.getString("param.config-root.name"),
+            I18N.getString("param.config-root.description"),
+            "configRoot",
+            File.class,
+            null,
+            params -> {
+                File configRoot = new File(BUILD_ROOT.fetchFrom(params), "macosx");
+                configRoot.mkdirs();
+                return configRoot;
+            },
+            false,
+            (s, p) -> new File(s));
+
+    public MacDaemonBundler() {
+        super();
+        baseResourceLoader = MacResources.class;
+    }
+    
+    private File getConfig_LaunchdPlist(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params), "launchd.plist");
+    }
+    
+    private void prepareConfigFiles(Map<String, ? super Object> params) throws IOException {
+        File launchdPlistFile = getConfig_LaunchdPlist(params);
+        launchdPlistFile.createNewFile();
+        writeLaunchdPlist(launchdPlistFile, params);
+    }
+
+    private String getDaemonIdentifier(Map<String, ? super Object> params) {
+        return IDENTIFIER.fetchFrom(params).toLowerCase() + ".daemon";
+    }
+
+    public String getAppName(Map<String, ? super Object> params) {
+        return APP_NAME.fetchFrom(params) + ".app";
+    }
+    
+    private String getLauncherName(Map<String, ? super Object> params) {
+        if (APP_NAME.fetchFrom(params) != null) {
+            return APP_NAME.fetchFrom(params);
+        } else {
+            return MAIN_CLASS.fetchFrom(params);
+        }
+    }
+    
+    private String getDaemonLauncherPath(Map<String, ? super Object> params) {
+        return "/Applications/" + getAppName(params) +
+                "/Contents/MacOS/" + getLauncherName(params);
+    }
+
+    private void writeLaunchdPlist(File file, Map<String, ? super Object> params)
+            throws IOException
+    {
+        Log.verbose("Preparing launchd.plist: "+file.getAbsolutePath());
+        
+        Map<String, String> data = new HashMap<>();
+
+        data.put("DEPLOY_DAEMON_IDENTIFIER", getDaemonIdentifier(params));
+        data.put("DEPLOY_DAEMON_LAUNCHER_PATH", getDaemonLauncherPath(params));        
+        data.put("DEPLOY_RUN_AT_LOAD", String.valueOf((START_ON_INSTALL.fetchFrom(params))));
+        data.put("DEPLOY_KEEP_ALIVE", String.valueOf((RUN_AT_STARTUP.fetchFrom(params))));
+
+        Writer w = new BufferedWriter(new FileWriter(file));
+        w.write(preprocessTextResource(
+                MAC_BUNDLER_PREFIX + getConfig_LaunchdPlist(params).getName(),
+                "Bundle launchd config file", TEMPLATE_LAUNCHD_PLIST, data,
+                VERBOSE.fetchFrom(params)));
+        w.close();
+    }
+    
+    protected void cleanupConfigFiles(Map<String, ? super Object> params) {
+        if (CONFIG_ROOT.fetchFrom(params) != null) {
+            if (getConfig_LaunchdPlist(params) != null) {
+                getConfig_LaunchdPlist(params).delete();
+            }
+        }
+    }
+    
+    /*
+     * Creates the following structure
+     * 
+     *  <package-name>
+     *      Library
+     *          LaunchDaemons
+     *              plist file
+     */
+    public File doBundle(Map<String, ? super Object> params, File outputDirectory, boolean dependentTask) {
+        
+        File rootDirectory = null;
+
+        try {            
+            File file = BUILD_ROOT.fetchFrom(params);
+
+            //prepare config resources (we will copy them to the bundle later)
+            // NB: explicitly saving them to simplify customization
+            prepareConfigFiles(params);
+
+            // Create directory structure
+            rootDirectory = new File(outputDirectory, APP_NAME.fetchFrom(params) + ".daemon");
+            IOUtils.deleteRecursive(rootDirectory);
+            rootDirectory.mkdirs();
+
+            if (!dependentTask) {
+                Log.info("Creating daemon component: " + rootDirectory.getAbsolutePath());
+            }
+            
+            File libraryDirectory = new File(rootDirectory, "Library");
+            libraryDirectory.mkdirs();
+
+            File launchDaemonsDirectory = new File(libraryDirectory, "LaunchDaemons");
+            launchDaemonsDirectory.mkdirs();
+
+            // Generate launchd.plist
+            IOUtils.copyFile(getConfig_LaunchdPlist(params),
+                     new File(launchDaemonsDirectory,
+                             IDENTIFIER.fetchFrom(params).toLowerCase() + ".launchd.plist"));
+            
+        } catch(IOException ex) {
+            Log.verbose(ex);
+            return null;
+        } finally {
+            if (!VERBOSE.fetchFrom(params)) {
+                //cleanup
+                cleanupConfigFiles(params);
+            } else {
+                Log.info("Config files are saved to " +
+                        CONFIG_ROOT.fetchFrom(params).getAbsolutePath()  +
+                        ". Use them to customize package.");
+            }
+        }
+        
+        return rootDirectory;
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////
+    // Implement Bundler
+    //////////////////////////////////////////////////////////////////////////////////
+    
+    @Override
+    public String getName() {
+        return "Mac Daemon Component";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Mac Daemon Component - contains configuration files describing daemons.";
+    }
+
+    @Override
+    public String getID() {
+        return "mac.daemon";
+    }
+
+    @Override
+    public BundleType getBundleType() {
+        return BundleType.IMAGE;
+    }
+
+    @Override
+    public Collection<BundlerParamInfo<?>> getBundleParameters() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public boolean validate(Map<String, ? super Object> params)
+            throws UnsupportedPlatformException, ConfigException
+    {
+        try {
+            logParameters(params);
+            return doValidate(params);
+        } catch (RuntimeException re) {
+            throw new ConfigException(re);
+        }
+
+    }
+
+    public boolean doValidate(Map<String, ? super Object> p)
+            throws UnsupportedPlatformException, ConfigException
+    {
+        if (!System.getProperty("os.name").toLowerCase().contains("os x")) {
+            throw new UnsupportedPlatformException();
+        }
+        
+        return true;
+    }
+    
+    @Override
+    public File execute(Map<String, ? super Object> params, File outputParentDir) {
+        return doBundle(params, outputParentDir, false);
+    }
+
+}
--- a/modules/fxpackager/src/main/java/com/oracle/bundlers/mac/MacPKGBundler.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/oracle/bundlers/mac/MacPKGBundler.java	Mon Mar 17 09:51:09 2014 -0600
@@ -26,61 +26,236 @@
 package com.oracle.bundlers.mac;
 
 import com.oracle.bundlers.BundlerParamInfo;
+import com.oracle.bundlers.StandardBundlerParam;
+
 import com.sun.javafx.tools.packager.Log;
 import com.sun.javafx.tools.packager.bundlers.ConfigException;
 import com.sun.javafx.tools.packager.bundlers.IOUtils;
 import com.sun.javafx.tools.packager.bundlers.UnsupportedPlatformException;
+import com.sun.javafx.tools.resource.mac.MacResources;
 
 import java.io.*;
+import java.text.MessageFormat;import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.ResourceBundle;
 
-import static com.oracle.bundlers.StandardBundlerParam.NAME;
+import static com.oracle.bundlers.StandardBundlerParam.APP_NAME;
+import static com.oracle.bundlers.StandardBundlerParam.BUILD_ROOT;
+import static com.oracle.bundlers.StandardBundlerParam.IDENTIFIER;
+import static com.oracle.bundlers.StandardBundlerParam.SERVICE_HINT;
+import static com.oracle.bundlers.StandardBundlerParam.VERBOSE;
 
 public class MacPKGBundler extends MacBaseInstallerBundler {
 
+    private static final ResourceBundle I18N =
+            ResourceBundle.getBundle("com.oracle.bundlers.mac.MacPKGBundler");
+
+    public final static String MAC_BUNDLER_PREFIX =
+            BUNDLER_PREFIX + "macosx" + File.separator;
+    
+    private static final String TEMPLATE_PREINSTALL_SCRIPT = "preinstall.template";
+    private static final String TEMPLATE_POSTINSTALL_SCRIPT = "postinstall.template";
+    
+    private static final BundlerParamInfo<File> PACKAGES_ROOT = new StandardBundlerParam<>(
+            I18N.getString("param.packages-root.name"),
+            I18N.getString("param.packages-root.description"),
+            "mac.pkg.packagesRoot",
+            File.class,
+            null,
+            params -> {
+                File packagesRoot = new File(BUILD_ROOT.fetchFrom(params), "packages");
+                packagesRoot.mkdirs();
+                return packagesRoot;
+            },
+            false,
+            (s, p) -> new File(s));
+
+    
+    protected final BundlerParamInfo<File> SCRIPTS_DIR = new StandardBundlerParam<>(
+            I18N.getString("param.scripts-dir.name"),
+            I18N.getString("param.scripts-dir.description"),
+            "mac.pkg.scriptsDir",
+            File.class,
+            null,
+            params -> {
+                File scriptsDir = new File(CONFIG_ROOT.fetchFrom(params), "scripts");
+                scriptsDir.mkdirs();
+                return scriptsDir;
+            },
+            false,
+            (s, p) -> new File(s));
+    
+    
+    public MacPKGBundler() {
+        super();
+        baseResourceLoader = MacResources.class;
+    }
 
     //@Override
     public File bundle(Map<String, ? super Object> p, File outdir) {
-        Log.info("Building PKG package for " + NAME.fetchFrom(p));
+        Log.info(MessageFormat.format(I18N.getString("message.building-pkg"), APP_NAME.fetchFrom(p)));
 
         File appImageDir = APP_IMAGE_BUILD_ROOT.fetchFrom(p);
+        File daemonImageDir = DAEMON_IMAGE_BUILD_ROOT.fetchFrom(p);
+
         try {
             appImageDir.mkdirs();
             prepareAppBundle(p);
+            
+            if (SERVICE_HINT.fetchFrom(p)) {
+                daemonImageDir.mkdirs();
+                prepareDaemonBundle(p);
+            }
+
             return createPKG(p, outdir);
         } catch (Exception ex) {
             return null;
         }
     }
 
+    private File getPackages_AppPackage(Map<String, ? super Object> params) {
+        return new File(PACKAGES_ROOT.fetchFrom(params), APP_NAME.fetchFrom(params) + "-app.pkg");
+    }
+
+    private File getPackages_DaemonPackage(Map<String, ? super Object> params) {
+        return new File(PACKAGES_ROOT.fetchFrom(params), APP_NAME.fetchFrom(params) + "-daemon.pkg");
+    }
+    
+    private void cleanupPackagesFiles(Map<String, ? super Object> params) {
+        if (getPackages_AppPackage(params) != null) {
+            getPackages_AppPackage(params).delete();
+        }
+        if (getPackages_DaemonPackage(params) != null) {
+            getPackages_DaemonPackage(params).delete();
+        }
+    }
+    
+    private File getScripts_PreinstallFile(Map<String, ? super Object> params) {
+        return new File(SCRIPTS_DIR.fetchFrom(params), "preinstall");
+    }
+
+    private File getScripts_PostinstallFile(Map<String, ? super Object> params) {
+        return new File(SCRIPTS_DIR.fetchFrom(params), "postinstall");
+    }
+
+    private void cleanupPackageScripts(Map<String, ? super Object> params) {
+        if (getScripts_PreinstallFile(params) != null) {
+            getScripts_PreinstallFile(params).delete();
+        }
+        if (getScripts_PostinstallFile(params) != null) {
+            getScripts_PostinstallFile(params).delete();
+        }
+    }
+    
+    private String getDaemonIdentifier(Map<String, ? super Object> params) {
+        return IDENTIFIER.fetchFrom(params).toLowerCase() + ".daemon";
+    }
+    
+    private void preparePackageScripts(Map<String, ? super Object> params) throws IOException
+    {
+        Log.verbose(I18N.getString("message.preparing-scripts"));
+        
+        Map<String, String> data = new HashMap<>();
+
+        data.put("DEPLOY_DAEMON_IDENTIFIER", getDaemonIdentifier(params));
+        data.put("DEPLOY_LAUNCHD_PLIST_FILE",
+                IDENTIFIER.fetchFrom(params).toLowerCase() + ".launchd.plist");
+        
+        Writer w = new BufferedWriter(new FileWriter(getScripts_PreinstallFile(params)));
+        String content = preprocessTextResource(
+                MAC_BUNDLER_PREFIX + getScripts_PreinstallFile(params).getName(),
+                I18N.getString("resource.pkg-preinstall-script"),
+                TEMPLATE_PREINSTALL_SCRIPT,
+                data,
+                VERBOSE.fetchFrom(params));
+        w.write(content);
+        w.close();
+        getScripts_PreinstallFile(params).setExecutable(true, false);
+        
+        w = new BufferedWriter(new FileWriter(getScripts_PostinstallFile(params)));
+        content = preprocessTextResource(
+                MAC_BUNDLER_PREFIX + getScripts_PostinstallFile(params).getName(),
+                I18N.getString("resource.pkg-postinstall-script"),
+                TEMPLATE_POSTINSTALL_SCRIPT,
+                data,
+                VERBOSE.fetchFrom(params));
+        w.write(content);
+        w.close();
+        getScripts_PostinstallFile(params).setExecutable(true, false);        
+    }
+    
     private File createPKG(Map<String, ? super Object> params, File outdir) {
         //generic find attempt
         try {
             String appLocation =
-                    APP_IMAGE_BUILD_ROOT.fetchFrom(params) + "/" + NAME.fetchFrom(params) + ".app";
+                    APP_IMAGE_BUILD_ROOT.fetchFrom(params) + "/" + APP_NAME.fetchFrom(params) + ".app";
             File predefinedImage = getPredefinedImage(params);
             if (predefinedImage != null) {
                 appLocation = predefinedImage.getAbsolutePath();
             }
+            
+            String daemonLocation = DAEMON_IMAGE_BUILD_ROOT.fetchFrom(params) + "/" + APP_NAME.fetchFrom(params) + ".daemon";
+            
+            File appPKG = getPackages_AppPackage(params);
+            File daemonPKG = getPackages_DaemonPackage(params);
 
-            //productbuild --component Smoke.app /Applications  --product Smoke.app/Contents/Info.plist Smoke.pkg
-            File finalPKG = new File(outdir, NAME.fetchFrom(params)+".pkg");
+            // build application package
+            ProcessBuilder pb = new ProcessBuilder("pkgbuild",
+                    "--component",
+                    appLocation,
+                    "--install-location",
+                    "/Applications",
+                    appPKG.getAbsolutePath());
+            IOUtils.exec(pb, VERBOSE.fetchFrom(params));
+
+            // build daemon package if requested
+            if (SERVICE_HINT.fetchFrom(params)) {
+                preparePackageScripts(params);
+                
+                pb = new ProcessBuilder("pkgbuild",
+                        "--identifier",
+                        APP_NAME.fetchFrom(params) + ".daemon",
+                        "--root",
+                        daemonLocation,
+                        "--scripts",
+                        SCRIPTS_DIR.fetchFrom(params).getAbsolutePath(),
+                        daemonPKG.getAbsolutePath());
+                IOUtils.exec(pb, VERBOSE.fetchFrom(params));
+            }
+
+            // build final package
+            File finalPKG = new File(outdir, APP_NAME.fetchFrom(params)+".pkg");
             outdir.mkdirs();
 
-            ProcessBuilder pb = new ProcessBuilder("productbuild",
-                    "--component",
-                    appLocation,
-                    "/Applications",
-                    "--product",
-                    appLocation+"/Contents/Info.plist",
-                    finalPKG.getAbsolutePath());
-            IOUtils.exec(pb, verbose);
+            List<String> commandLine = new ArrayList<>();
+            commandLine.add("productbuild");
+            commandLine.add("--package");
+            commandLine.add(appPKG.getAbsolutePath());
+            if (SERVICE_HINT.fetchFrom(params)) {
+                commandLine.add("--package");
+                commandLine.add(daemonPKG.getAbsolutePath());            
+            }
+            commandLine.add(finalPKG.getAbsolutePath());
+
+            pb = new ProcessBuilder(commandLine);
+            IOUtils.exec(pb, VERBOSE.fetchFrom(params));
+            
             return finalPKG;
         } catch (Exception ignored) {
-            Log.debug("PKG Failed: " + ignored.getMessage());
+            Log.verbose(ignored);
+            return null;
+        } finally {
+            if (!VERBOSE.fetchFrom(params)) {
+                cleanupPackagesFiles(params);
+                
+                if (SERVICE_HINT.fetchFrom(params)) {
+                    cleanupPackageScripts(params);
+                }
+            }
         }
-        return null;
     }
 
     //////////////////////////////////////////////////////////////////////////////////
@@ -89,12 +264,12 @@
 
     @Override
     public String getName() {
-        return "PKG Installer";
+        return I18N.getString("bundler.name");
     }
 
     @Override
     public String getDescription() {
-        return "Mac PKG Installer Bundle.";
+        return I18N.getString("bundler.description");
     }
 
     @Override
@@ -110,15 +285,21 @@
 
     @Override
     public boolean validate(Map<String, ? super Object> params) throws UnsupportedPlatformException, ConfigException {
-        if (params == null) throw new ConfigException("Parameters map is null.", "Pass in a non-null parameters map.");
+        try {
+            if (params == null) throw new ConfigException(
+                    I18N.getString("error.parameters-null"),
+                    I18N.getString("error.parameters-null.advice"));
 
-        // hdiutil is always available so there's no need to test for availability.
-        //run basic validation to ensure requirements are met
+            // hdiutil is always available so there's no need to test for availability.
+            //run basic validation to ensure requirements are met
 
-        //run basic validation to ensure requirements are met
-        //we are not interested in return code, only possible exception
-        APP_BUNDLER.fetchFrom(params).doValidate(params);
-        return true;
+            //run basic validation to ensure requirements are met
+            //we are not interested in return code, only possible exception
+            APP_BUNDLER.fetchFrom(params).doValidate(params);
+            return true;
+        } catch (RuntimeException re) {
+            throw new ConfigException(re);
+        }
     }
 
     @Override
--- a/modules/fxpackager/src/main/java/com/oracle/bundlers/windows/WindowsBundlerParam.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/oracle/bundlers/windows/WindowsBundlerParam.java	Mon Mar 17 09:51:09 2014 -0600
@@ -36,6 +36,7 @@
 import java.io.PrintStream;
 import java.util.Map;
 import java.util.ResourceBundle;
+import java.util.function.BiFunction;
 import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -45,7 +46,7 @@
     
     private static final ResourceBundle I18N = ResourceBundle.getBundle("com.oracle.bundlers.windows.WindowsBundlerParam");
     
-    public WindowsBundlerParam(String name, String description, String id, Class<T> valueType, String[] fallbackIDs, Function<Map<String, ? super Object>, T> defaultValueFunction, boolean requiresUserSetting, Function<String, T> stringConverter) {
+    public WindowsBundlerParam(String name, String description, String id, Class<T> valueType, String[] fallbackIDs, Function<Map<String, ? super Object>, T> defaultValueFunction, boolean requiresUserSetting, BiFunction<String, Map<String, ? super Object>, T> stringConverter) {
         super(name, description, id, valueType, fallbackIDs, defaultValueFunction, requiresUserSetting, stringConverter);
     }
 
@@ -53,36 +54,36 @@
             new StandardBundlerParam<>(
                     I18N.getString("param.menu-group.name"),
                     I18N.getString("param.menu-group.description"),
-                    "winMenuGroup", //KEY
+                    "win.menuGroup",
                     String.class,
-                    new String[] {CATEGORY.getID(), VENDOR.getID()},
+                    new String[] {VENDOR.getID(), CATEGORY.getID(), },
                     params -> I18N.getString("param.menu-group.default"),
                     false,
-                    s -> s
+                    (s, p) -> s
             );
-    
+
     public static final StandardBundlerParam<Boolean> BIT_ARCH_64 =
             new StandardBundlerParam<>(
                     I18N.getString("param.64-bit.name"),
                     I18N.getString("param.64-bit.description"),
-                    "win64Bit", //KEY
+                    "win.64Bit",
                     Boolean.class,
                     null,
                     params -> System.getProperty("os.arch").contains("64"),
                     false,
-                    Boolean::valueOf
+                    (s, p) -> Boolean.valueOf(s)
             );
 
     public static final StandardBundlerParam<Boolean> BIT_ARCH_64_RUNTIME =
             new StandardBundlerParam<>(
                     I18N.getString("param.runtime-64-bit.name"),
                     I18N.getString("param.runtime-64-bit.description"),
-                    "win64BitJreRuntime", //KEY
+                    "win.64BitJreRuntime",
                     Boolean.class,
                     null,
                     params -> {extractFlagsFromRuntime(params); return "64".equals(params.get(".runtime.bit-arc"));},
                     false,
-                    Boolean::valueOf
+                    (s, p) -> Boolean.valueOf(s)
             );
        
     public static void extractFlagsFromRuntime(Map<String, ? super Object> params) {
--- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/DeployParams.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/DeployParams.java	Mon Mar 17 09:51:09 2014 -0600
@@ -37,6 +37,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeSet;
 
 public class DeployParams extends CommonParams {
     public enum RunMode {
@@ -534,6 +535,14 @@
             }
 
             bundleParams.setIcon(appIcon);
+
+            // check for collisions
+            TreeSet<String> keys = new TreeSet<>(bundlerArguments.keySet());
+            keys.retainAll(bundleParams.getBundleParamsAsMap().keySet());
+
+            if (!keys.isEmpty()) {
+                throw new RuntimeException("Deploy Params and Bundler Arguments overlap in the following values:" + keys.toString());
+            }
             
             bundleParams.addAllBundleParams(bundlerArguments);
             
--- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/Log.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/Log.java	Mon Mar 17 09:51:09 2014 -0600
@@ -97,9 +97,13 @@
     }
 
     public static void debug(RuntimeException re) {
+        debug((Throwable) re);
+    }
+
+    public static void debug(Throwable t) {
         try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
             try (PrintStream ps = new PrintStream(baos)) {
-                re.printStackTrace(ps);
+                t.printStackTrace(ps);
             }
             debug(baos.toString());
         } catch (IOException e) {
--- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/Main.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/Main.java	Mon Mar 17 09:51:09 2014 -0600
@@ -120,13 +120,11 @@
 
         List<Param> parameters = new ArrayList<>(properties.size());
 
-        if (properties != null) {
-            for (Map.Entry en : properties.entrySet()) {
-                Param p = new Param();
-                p.setName((String)en.getKey());
-                p.setValue((String)en.getValue());
-                parameters.add(p);
-            }
+        for (Map.Entry en : properties.entrySet()) {
+            Param p = new Param();
+            p.setName((String)en.getKey());
+            p.setValue((String)en.getValue());
+            parameters.add(p);
         }
         return parameters;
     }
@@ -140,13 +138,11 @@
 
         List<HtmlParam> parameters = new ArrayList<>(properties.size());
 
-        if (properties != null) {
-            for (Map.Entry en : properties.entrySet()) {
-                HtmlParam p = new HtmlParam();
-                p.setName((String)en.getKey());
-                p.setValue((String)en.getValue());
-                parameters.add(p);
-            }
+        for (Map.Entry en : properties.entrySet()) {
+            HtmlParam p = new HtmlParam();
+            p.setName((String)en.getKey());
+            p.setValue((String)en.getValue());
+            parameters.add(p);
         }
         return parameters;
     }
@@ -165,7 +161,7 @@
     }
 
 
-    public static void main(String args[]) throws Exception {
+    public static void main(String... args) throws Exception {
         if (args.length == 0 || args.length == 1 && args[0].equals("-help")) {
             System.out.println(help);
         } else if (args.length == 1 && args[0].equals("-version")) {
@@ -464,6 +460,9 @@
                     throw e;
                 } else {
                     System.err.println(e.getMessage());
+                    if (e.getCause() != null && e.getCause() != e) {
+                        System.err.println(e.getCause().getMessage());
+                    }
                     System.exit(-1);
                 }
             }
--- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/PackagerLib.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/PackagerLib.java	Mon Mar 17 09:51:09 2014 -0600
@@ -70,7 +70,6 @@
 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;
@@ -89,7 +88,6 @@
 
 import static com.oracle.bundlers.StandardBundlerParam.*;
 
-
 public class PackagerLib {
     public static final String JAVAFX_VERSION = "2.2";
 
@@ -111,7 +109,7 @@
     private boolean isSignedJNLP;
 
 
-    private enum Filter {ALL, CLASSES_ONLY, RESOURCES};
+    private enum Filter {ALL, CLASSES_ONLY, RESOURCES}
 
     private ClassLoader classLoader;
 
@@ -142,21 +140,12 @@
             if (!f.isFile() || !f.getAbsolutePath().toLowerCase().endsWith(".jar")) {
                 return null;
             }
-            JarFile jf = null;
-            try {
-                jf = new JarFile(f);
+            try (JarFile 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
+                //treat any exception as "not a special case" scenario
                 Log.verbose(e);
-            } finally {
-                if (jf != null) {
-                    try {
-                        jf.close();
-                    } catch (IOException ex) {
-                    }
-                }
             }
         }
         return null;
@@ -178,11 +167,9 @@
         Manifest m = null;
 
         if (jarToUpdate != null) {
-            JarFile jf = null;
-            try {
+            Log.info(MessageFormat.format(bundle.getString("MSG_UpdatingJar"), jarToUpdate.getAbsolutePath()));
+            try (JarFile jf = new JarFile(jarToUpdate)) {
                 //extract data we want to preserve
-                Log.info(MessageFormat.format(bundle.getString("MSG_UpdatingJar"), jarToUpdate.getAbsolutePath()));
-                jf = new JarFile(jarToUpdate);
                 m = jf.getManifest();
                 if (m != null) {
                     Attributes attrs = m.getMainAttributes();
@@ -198,13 +185,6 @@
             } catch (IOException ex) {
                 throw new PackagerException(
                         ex, "ERR_FileReadFailed", jarToUpdate.getAbsolutePath());
-            } finally {
-                if (jf != null) {
-                    try {
-                        jf.close();
-                    } catch (IOException ex) {
-                    }
-                }
             }
         }
 
@@ -318,24 +298,15 @@
 
     private String readTextFile(File in) throws PackagerException {
         StringBuilder sb = new StringBuilder();
-        InputStreamReader isr = null;
-        try {
+        try (InputStreamReader isr = new InputStreamReader(new FileInputStream(in))) {
             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();
     }
@@ -405,7 +376,7 @@
         return result.toString();
     }
 
-    private static enum Mode {FX, APPLET, SwingAPP};
+    private static enum Mode {FX, APPLET, SwingAPP}
 
     public void generateDeploymentPackages(DeployParams deployParams) throws PackagerException {
         if (deployParams == null) {
@@ -416,7 +387,7 @@
         Map<TemplatePlaceholders, String> templateStrings = null;
         if (templateOn) {
             templateStrings =
-               new EnumMap<TemplatePlaceholders, String>(TemplatePlaceholders.class);
+               new EnumMap<>(TemplatePlaceholders.class);
         }
         try {
             //In case of FX app we will have one JNLP and one HTML
@@ -497,13 +468,13 @@
             for (DeployResource resource: deployParams.resources) {
                 copyFiles(resource, deployParams.outdir);
             }
+
+            BundleParams bp = deployParams.getBundleParams();
+            if (bp != null) {
+                generateNativeBundles(deployParams.outdir, bp.getBundleParamsAsMap(), deployParams.getBundleType(), deployParams.getTargetFormat(), deployParams.verbose);
+            }
         } catch (Exception ex) {
-            throw new PackagerException(ex, "ERR_DeployFailed");
-        }
-
-        BundleParams bp = deployParams.getBundleParams();
-        if (bp != null) {
-           generateNativeBundles(deployParams.outdir, bp.getBundleParamsAsMap(), deployParams.getBundleType(), deployParams.getTargetFormat(), deployParams.verbose);
+            throw new PackagerException(ex, "ERR_DeployFailed", ex.getMessage());
         }
 
         this.deployParams = null;
@@ -538,7 +509,11 @@
             } catch (UnsupportedPlatformException e) {
                 Log.debug(MessageFormat.format(bundle.getString("MSG_BundlerPlatformException"), bundler.getName()));
             } catch (ConfigException e) {
-                Log.info(MessageFormat.format(bundle.getString("MSG_BundlerConfigException"), bundler.getName(), e.getMessage(), e.getAdvice()));
+                if (e.getAdvice() != null) {
+                    Log.info(MessageFormat.format(bundle.getString("MSG_BundlerConfigException"), bundler.getName(), e.getMessage(), e.getAdvice()));
+                } else {
+                    Log.info(MessageFormat.format(bundle.getString("MSG_BundlerConfigExceptionNoAdvice"), bundler.getName(), e.getMessage()));
+                }
             } catch (RuntimeException re) {
                 Log.info(MessageFormat.format(bundle.getString("MSG_BundlerRuntimeException"), bundler.getName(), re.toString()));
                 Log.debug(re);
@@ -678,12 +653,7 @@
             throw new IllegalStateException("Should retrieve signature first");
         }
 
-        InputStreamSource in = new InputStreamSource() {
-            @Override
-            public InputStream getInputStream() throws IOException {
-                return new FileInputStream(jar);
-            }
-        };
+        InputStreamSource in = () -> new FileInputStream(jar);
         if (!signedJar.isFile()) {
             signedJar.createNewFile();
         }
@@ -728,11 +698,8 @@
         try {
             final File tmpFile = File.createTempFile("javac", "sources", new File("."));
             tmpFile.deleteOnExit();
-            final FileWriter sources = new FileWriter(tmpFile);
-            try {
+            try (FileWriter sources = new FileWriter(tmpFile)) {
                 scanAndCopy(new PackagerResource(new File(srcDirName), "."), sources, compiledDir);
-            } finally {
-                sources.close();
             }
             String classpath = jfxHome + "/../rt/lib/ext/jfxrt.jar";
             if (makeAllParams.classpath != null) {
@@ -788,8 +755,9 @@
         deleteDirectory(compiledDir);
     }
 
+    @SuppressWarnings("unchecked")
     private static int execute(Object ... args) throws IOException, InterruptedException {
-        final ArrayList<String> argsList = new ArrayList();
+        final ArrayList<String> argsList = new ArrayList<>();
         for (Object a : args) {
             if (a instanceof List) {
                 argsList.addAll((List)a);
@@ -837,11 +805,12 @@
         if (!dir.getFile().exists()) {
             throw new PackagerException("ERR_MissingDirectory", dir.getFile().getName());
         }
-        if ((dir.getFile().listFiles() == null) || (dir.getFile().listFiles().length == 0)) {
+        File[] dirFilesList = dir.getFile().listFiles();
+        if ((dirFilesList == null) || (dirFilesList.length == 0)) {
             throw new PackagerException("ERR_EmptySourceDirectory", dir.getFile().getName());
         }
         try {
-            for (File f : dir.getFile().listFiles()) {
+            for (File f : dirFilesList) {
                 if (f.isDirectory()) {
                     scanAndCopy(new PackagerResource(dir.getBaseDir(), f), out, outdir);
                 } else if (f.getName().endsWith(".java")) {
@@ -903,19 +872,18 @@
                 ((deployParams.description != null)
                 ? deployParams.description : "Sample JavaFX 2.0 application.") +
                 "</description>");
-        for (Iterator<Icon> it = deployParams.icons.iterator(); it.hasNext();) {
-            DeployParams.Icon i = it.next();
+        for (Icon i : deployParams.icons) {
             if (i.mode == DeployParams.RunMode.WEBSTART ||
                     i.mode == DeployParams.RunMode.ALL) {
-            out.println("    <icon href=\"" + i.href+"\" " +
-                ((i.kind != null) ? " kind=\"" + i.kind + "\"" : "") +
-                ((i.width != DeployParams.Icon.UNDEFINED) ?
-                    " width=\"" + i.width + "\"" : "") +
-                ((i.height != DeployParams.Icon.UNDEFINED) ?
-                    " height=\"" + i.height + "\"" : "") +
-                ((i.depth != DeployParams.Icon.UNDEFINED) ?
-                    " depth=\"" + i.depth + "\"" : "") +
-                "/>");
+                out.println("    <icon href=\"" + i.href + "\" " +
+                        ((i.kind != null) ? " kind=\"" + i.kind + "\"" : "") +
+                        ((i.width != Icon.UNDEFINED) ?
+                                " width=\"" + i.width + "\"" : "") +
+                        ((i.height != Icon.UNDEFINED) ?
+                                " height=\"" + i.height + "\"" : "") +
+                        ((i.depth != Icon.UNDEFINED) ?
+                                " depth=\"" + i.depth + "\"" : "") +
+                        "/>");
             }
         }
 
@@ -1176,9 +1144,9 @@
                 "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();
+        List<String> w_app = new ArrayList<>();
+        List<String> w_platform = new ArrayList<>();
+        List<String> w_callback = new ArrayList<>();
 
         addToList(w_app, "url", jnlpfile_webstart, true);
         if (jnlp_content_webstart != null) {
@@ -1228,9 +1196,9 @@
         }
 
         //prepare content of embedApp()
-        List p_app = new ArrayList();
-        List p_platform = new ArrayList();
-        List p_callback = new ArrayList();
+        List<String> p_app = new ArrayList<>();
+        List<String> p_platform = new ArrayList<>();
+        List<String> p_callback = new ArrayList<>();
 
         if (appId != null) {
             addToList(p_app, "id", appId, true);
@@ -1346,16 +1314,13 @@
     }
 
     private static void copyFileToOutDir(
-            InputStream is, File fout) throws PackagerException {
+            InputStream isa, 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);
+        if (!outDir.exists() && !outDir.mkdirs()) {
+            throw new PackagerException("ERR_CreatingDirFailed", outDir.getPath());
+        }
+        try (InputStream is = isa; OutputStream out = new FileOutputStream(fout)) {
             byte[] buf = new byte[16384];
             int len;
             while ((len = is.read(buf)) > 0) {
@@ -1363,18 +1328,6 @@
             }
         } 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) {
-                }
-            }
         }
     }
 
@@ -1419,7 +1372,7 @@
         }
     }
 
-    private Set<String> alreadyAddedEntries = new HashSet<String>();
+    private Set<String> alreadyAddedEntries = new HashSet<>();
     private void createParentEntries(String relativePath, JarOutputStream jar) throws IOException {
         String[] pathComponents = relativePath.split("/");
         StringBuilder pathSB = new StringBuilder();
@@ -1449,17 +1402,14 @@
                 continue;
             }
 
-            InputStream in = inJar.getInputStream(je);
             jar.putNextEntry(new JarEntry(je.getName()));
 
             byte b[] = new byte[65000];
             int i;
-            try {
+            try (InputStream in = inJar.getInputStream(je)) {
                 while ((i = in.read(b)) > 0) {
                     jar.write(b, 0, i);
                 }
-            } finally {
-                in.close();
             }
 
             jar.closeEntry();
@@ -1508,13 +1458,11 @@
 
             byte b[] = new byte[65000];
             int i;
-            FileInputStream in = new FileInputStream(f);
-            try {
+
+            try (FileInputStream in = new FileInputStream(f)) {
                 while ((i = in.read(b)) > 0) {
                     jar.write(b, 0, i);
                 }
-            } finally {
-                in.close();
             }
             jar.closeEntry();
         }
@@ -1553,7 +1501,7 @@
         Class theClass = PackagerLib.class;
         String classUrl = theClass.getResource(theClassFile).toString();
 
-        if (!classUrl.startsWith("jar:file:") || classUrl.indexOf("!") == -1){
+        if (!classUrl.startsWith("jar:file:") || !classUrl.contains("!")){
             throw new PackagerException("ERR_CantFindRuntime");
         }
 
@@ -1561,10 +1509,9 @@
         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)
+
+        return classUrl.substring(0, lastIndexOfSlash)
                     + "/../rt/lib/ext/jfxrt.jar!/";
-
-        return jfxrtPath;
     }
 
     private Class loadClassFromRuntime(String className) throws PackagerException {
@@ -1577,10 +1524,9 @@
     }
 
     private void createBinaryCss(String cssFile, String binCssFile) throws PackagerException {
-        String ifname = cssFile;
         String ofname = (binCssFile != null)
                             ? binCssFile
-                            : replaceExtensionByBSS(ifname);
+                            : replaceExtensionByBSS(cssFile);
 
         // create parent directories
         File of = new File(ofname);
@@ -1591,7 +1537,7 @@
 
         // Using reflection because CSS parser is part of runtime
         // and we want to avoid dependency on jfxrt during build
-        Class clazz;
+        Class<?> clazz;
         try {
             clazz = Class.forName("com.sun.javafx.css.parser.Css2Bin");
         } catch (ClassNotFoundException e) {
@@ -1602,7 +1548,7 @@
 
         try {
             Method m = clazz.getMethod("convertToBinary", new Class[]{String.class, String.class});
-            m.invoke(null, ifname, ofname);
+            m.invoke(null, cssFile, ofname);
         } catch (Exception ex) {
             Throwable causeEx = ex.getCause();
             String cause = (causeEx != null) ? causeEx.getMessage()
@@ -1643,25 +1589,25 @@
             return false;
         }
         name = name.replace('\\', '/');
-        if (name.indexOf("/CVS/") >= 0) {
+        if (name.contains("/CVS/")) {
             return false;
         }
-        if (name.indexOf("/.svn/") >= 0) {
+        if (name.contains("/.svn/")) {
             return false;
         }
-        if (name.indexOf("/.hg/") >= 0) {
+        if (name.contains("/.hg/")) {
             return false;
         }
-        if (name.indexOf("/.#") >= 0) {
+        if (name.contains("/.#")) {
             return false;
         }
-        if (name.indexOf("/._") >= 0) {
+        if (name.contains("/._")) {
             return false;
         }
-        if (name.endsWith("#") && name.indexOf("/#") >= 0) {
+        if (name.endsWith("#") && name.contains("/#")) {
             return false;
         }
-        if (name.endsWith("%") && name.indexOf("/%") >= 0) {
+        if (name.endsWith("%") && name.contains("/%")) {
             return false;
         }
         if (name.endsWith("MANIFEST.MF")) {
@@ -1737,7 +1683,7 @@
     }
 
     private Set<CertPath> collectCertPaths() throws IOException {
-        Set<CertPath> result = new HashSet<CertPath>();
+        Set<CertPath> result = new HashSet<>();
         for (DeployResource resource: deployParams.resources) {
             final File srcFile = resource.getFile();
             if (srcFile.exists() && srcFile.isFile() &&
@@ -1749,7 +1695,7 @@
     }
 
     private Set<CertPath> extractCertPaths(File jar) throws IOException {
-        Set<CertPath> result = new HashSet<CertPath>();
+        Set<CertPath> result = new HashSet<>();
         JarFile jf = new JarFile(jar);
 
         // need to fully read jar file to build up internal signer info map
@@ -1761,7 +1707,7 @@
             JarEntry je = entries.nextElement();
             String entryName = je.getName();
 
-            CodeSigner[] signers = null;
+            CodeSigner[] signers;
             if (entryName.equalsIgnoreCase(JarSignature.BLOB_SIGNATURE)) {
                 byte[] raw = Utils.getBytes(jf.getInputStream(je));
                 try {
@@ -1790,7 +1736,7 @@
     }
 
     private static Collection<CertPath> extractCertPaths(CodeSigner[] signers) {
-        Collection<CertPath> result = new ArrayList<CertPath>();
+        Collection<CertPath> result = new ArrayList<>();
         if (signers != null) {
             for (CodeSigner cs : signers) {
                 CertPath cp = cs.getSignerCertPath();
--- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/BundleParams.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/BundleParams.java	Mon Mar 17 09:51:09 2014 -0600
@@ -26,7 +26,6 @@
 package com.sun.javafx.tools.packager.bundlers;
 
 import com.oracle.bundlers.BundlerParamInfo;
-import com.oracle.bundlers.StandardBundlerParam;
 import com.sun.javafx.tools.packager.Log;
 import com.sun.javafx.tools.packager.PackagerLib;
 
@@ -37,62 +36,62 @@
 import java.util.jar.JarFile;
 import java.util.jar.Manifest;
 
+import static com.oracle.bundlers.StandardBundlerParam.*;
+
 public class BundleParams {
       
     final protected Map<String, ? super Object> params;
     
-    public static final String PARAM_RUNTIME                = "runtime"; // RelativeFileSet //KEY
-    public static final String PARAM_APP_RESOURCES          = "appResources"; // RelativeFileSet //KEY
+    public static final String PARAM_RUNTIME                = "runtime"; // RelativeFileSet
+    public static final String PARAM_APP_RESOURCES          = "appResources"; // RelativeFileSet
     public static final String PARAM_TYPE                   = "type"; // BundlerType
     public static final String PARAM_BUNDLE_FORMAT          = "bundleFormat"; // String
-    public static final String PARAM_ICON                   = "icon"; // String //KEY
+    public static final String PARAM_ICON                   = "icon"; // String
 
-    /* Name of bundle file and native launcher.
-       Also used as CFBundleName on Mac */
-    public static final String PARAM_APP_NAME               = "appName"; // String //KEY
-    public static final String PARAM_NAME                   = "name"; // String //KEY
+    /* Name of bundle file and native launcher */
+    public static final String PARAM_NAME                   = "name"; // String
 
     /* application vendor, used by most of the bundlers */
-    public static final String PARAM_VENDOR                 = "vendor"; // String //KEY
+    public static final String PARAM_VENDOR                 = "vendor"; // String
 
     /* email name and email, only used for debian */
-    public static final String PARAM_EMAIL                  = "email"; // String  //KEY
+    public static final String PARAM_EMAIL                  = "email"; // String
 
     /* Copyright. Used on Mac */
-    public static final String PARAM_COPYRIGHT              = "copyright"; // String //KEY
+    public static final String PARAM_COPYRIGHT              = "copyright"; // String
 
     /* GUID on windows for MSI, CFBundleIdentifier on Mac
        If not compatible with requirements then bundler either do not bundle
        or autogenerate */
-    public static final String PARAM_IDENTIFIER             = "identifier"; // String  //KEY
+    public static final String PARAM_IDENTIFIER             = "identifier"; // String
 
     /* shortcut preferences */
-    public static final String PARAM_SHORTCUT               = "shortcutHint"; // boolean //KEY
-    public static final String PARAM_MENU                   = "menuHint"; // boolean //KEY
+    public static final String PARAM_SHORTCUT               = "shortcutHint"; // boolean
+    public static final String PARAM_MENU                   = "menuHint"; // boolean
 
     /* Application version. Format may differ for different bundlers */
-    public static final String PARAM_VERSION                = "appVersion"; // String //KEY
+    public static final String PARAM_VERSION                = "appVersion"; // String
     /* Application category. Used at least on Mac/Linux. Value is platform specific */
-    public static final String PARAM_CATEGORY               = "applicationCategory"; // String //KEY
+    public static final String PARAM_CATEGORY               = "applicationCategory"; // String
 
     /* Optional short application */
-    public static final String PARAM_TITLE                  = "title"; // String //KEY
+    public static final String PARAM_TITLE                  = "title"; // String
 
     /* Optional application description. Used by MSI and on Linux */
-    public static final String PARAM_DESCRIPTION            = "description"; // String //KEY
+    public static final String PARAM_DESCRIPTION            = "description"; // String
 
     /* License type. Needed on Linux (rpm) */
-    public static final String PARAM_LICENSE_TYPE           = "licenseType"; // String //KEY
+    public static final String PARAM_LICENSE_TYPE           = "licenseType"; // String
 
     /* File(s) with license. Format is OS/bundler specific */
-    public static final String PARAM_LICENSE_FILES          = "licenseFiles"; // List<String> //KEY
+    public static final String PARAM_LICENSE_FILES          = "licenseFiles"; // List<String>
 
     /* user or system level install.
        null means "default" */
-    public static final String PARAM_SYSTEM_WIDE            = "systemWide"; // Boolean //KEY
+    public static final String PARAM_SYSTEM_WIDE            = "systemWide"; // Boolean
 
     /* Main application class. Not used directly but used to derive default values */
-    public static final String PARAM_APPLICATION_CLASS      = "applicationClass"; // String //KEY
+    public static final String PARAM_APPLICATION_CLASS      = "applicationClass"; // String
 
     //list of jvm args (in theory string can contain spaces and need to be escaped
     private List<String> jvmargs = new LinkedList<>();
@@ -180,15 +179,15 @@
     }
 
     public String getApplicationID() {
-        return fetchParam(StandardBundlerParam.IDENTIFIER);        
+        return fetchParam(IDENTIFIER);
     }
 
     public String getPreferencesID() {
-        return fetchParam(StandardBundlerParam.PREFERENCES_ID);
+        return fetchParam(PREFERENCES_ID);
     }
 
     public String getTitle() {
-        return fetchParam(StandardBundlerParam.TITLE); //FIXME title
+        return fetchParam(TITLE);
     }
 
     public void setTitle(String title) {
@@ -196,7 +195,7 @@
     }
 
     public String getApplicationClass() {
-        return fetchParam(StandardBundlerParam.MAIN_CLASS);
+        return fetchParam(MAIN_CLASS);
     }
 
     public void setApplicationClass(String applicationClass) {
@@ -204,7 +203,7 @@
     }
 
     public String getAppVersion() {
-        return fetchParam(StandardBundlerParam.VERSION);
+        return fetchParam(VERSION);
     }
 
     public void setAppVersion(String version) {
@@ -212,7 +211,7 @@
     }
 
     public String getDescription() {
-        return fetchParam(StandardBundlerParam.NAME); //FIXME DESCRIPTION);
+        return fetchParam(DESCRIPTION);
     }
 
     public void setDescription(String s) {
@@ -220,7 +219,7 @@
     }
 
     public String getLicenseType() {
-        return fetchParam(StandardBundlerParam.LICENSE_TYPE); //FIXME
+        return fetchParam(LICENSE_TYPE);
     }
 
     public void setLicenseType(String version) {
@@ -229,8 +228,7 @@
 
     //path is relative to the application root
     public void addLicenseFile(String path) {
-        @SuppressWarnings("unchecked") 
-        List<String> licenseFile = fetchParam(List.class, PARAM_LICENSE_FILES);
+        List<String> licenseFile = fetchParam(LICENSE_FILES);
         if (licenseFile == null) {
             licenseFile = new ArrayList<>();
             params.put(PARAM_LICENSE_FILES, licenseFile);
@@ -239,7 +237,7 @@
     }
 
     public Boolean getSystemWide() {
-        return fetchParam(StandardBundlerParam.SYSTEM_WIDE);
+        return fetchParam(SYSTEM_WIDE);
     }
 
     public void setSystemWide(Boolean b) {
@@ -247,11 +245,11 @@
     }
 
     public RelativeFileSet getRuntime() {
-        return fetchParam(StandardBundlerParam.RUNTIME);
+        return fetchParam(RUNTIME);
     }
 
     public boolean isShortcutHint() {
-        return fetchParam(StandardBundlerParam.SHORTCUT_HINT);
+        return fetchParam(SHORTCUT_HINT);
     }
 
     public void setShortcutHint(boolean v) {
@@ -259,7 +257,7 @@
     }
 
     public boolean isMenuHint() {
-        return fetchParam(StandardBundlerParam.MENU_HINT);
+        return fetchParam(MENU_HINT);
     }
 
     public void setMenuHint(boolean v) {
@@ -267,7 +265,7 @@
     }
 
     public String getName() {
-        return fetchParam(StandardBundlerParam.NAME);
+        return fetchParam(APP_NAME);
     }
 
     public void setName(String name) {
@@ -290,187 +288,15 @@
         putUnlessNull(PARAM_BUNDLE_FORMAT, t);
     }
 
-    public static boolean shouldExclude(File baseDir, File f, Rule ruleset[]) {
-        if (ruleset == null) {
-            return false;
-        }
 
-        String fname = f.getAbsolutePath().toLowerCase().substring(
-                baseDir.getAbsolutePath().length());
-        //first rule match defines the answer
-        for (Rule r: ruleset) {
-            if (r.match(fname)) {
-                return !r.treatAsAccept();
-            }
-        }
-        //default is include
-        return false;
-    }
-
-    public static void walk(File base, File root, Rule ruleset[], Set<File> files) {
-        if (!root.isDirectory()) {
-            if (root.isFile()) {
-                files.add(root);
-            }
-            return;
-        }
-
-        File[] lst = root.listFiles();
-        if (lst != null) {
-            for (File f : lst) {
-                //ignore symbolic links!
-                if (IOUtils.isNotSymbolicLink(f) && !shouldExclude(base, f, ruleset)) {
-                    if (f.isDirectory()) {
-                        walk(base, f, ruleset, files);
-                    } else if (f.isFile()) {
-                        //add to list
-                        files.add(f);
-                    }
-                }
-            }
-        }
-    }
-
-    
-    @SuppressWarnings("unchecked")
     public List<String> getLicenseFile() {
-        return fetchParam(List.class, PARAM_LICENSE_FILES);
+        return fetchParam(LICENSE_FILES);
     }
 
     public List<String> getJvmargs() {
         return jvmargs;
     }
 
-    public static class Rule {
-        String regex;
-        boolean includeRule;
-        Type type;
-        enum Type {SUFFIX, PREFIX, SUBSTR, REGEX}
-
-        private Rule(String regex, boolean includeRule, Type type) {
-            this.regex = regex;
-            this.type = type;
-            this.includeRule = includeRule;
-        }
-
-        boolean match(String str) {
-            if (type == Type.SUFFIX) {
-                return str.endsWith(regex);
-            }
-            if (type == Type.PREFIX) {
-                return str.startsWith(regex);
-            }
-            if (type == Type.SUBSTR) {
-                return str.contains(regex);
-            }
-            return str.matches(regex);
-        }
-
-        boolean treatAsAccept() {return includeRule;}
-
-        static Rule suffix(String s) {
-            return new Rule(s, true, Type.SUFFIX);
-        }
-        static Rule suffixNeg(String s) {
-            return new Rule(s, false, Type.SUFFIX);
-        }
-        static Rule prefix(String s) {
-            return new Rule(s, true, Type.PREFIX);
-        }
-        static Rule prefixNeg(String s) {
-            return new Rule(s, false, Type.PREFIX);
-        }
-        static Rule substr(String s) {
-            return new Rule(s, true, Type.SUBSTR);
-        }
-        static Rule substrNeg(String s) {
-            return new Rule(s, false, Type.SUBSTR);
-        }
-    }
-
-    //Subsetting of JRE is restricted.
-    //JRE README defines what is allowed to strip:
-    //   http://www.oracle.com/technetwork/java/javase/jre-7-readme-430162.html
-    //
-    //In addition to this we may need to keep deploy jars and deploy.dll
-    // because JavaFX apps might need JNLP services
-    //Also, embedded launcher uses deploy.jar to access registry
-    // (although this is not needed)
-    public static Rule macRules[] = {
-        Rule.suffixNeg("macos/libjli.dylib"),
-        Rule.suffixNeg("resources"),
-        Rule.suffixNeg("home/bin"),
-        Rule.suffixNeg("home/db"),
-        Rule.suffixNeg("home/demo"),
-        Rule.suffixNeg("home/include"),
-        Rule.suffixNeg("home/lib"),
-        Rule.suffixNeg("home/man"),
-        Rule.suffixNeg("home/release"),
-        Rule.suffixNeg("home/sample"),
-        Rule.suffixNeg("home/src.zip"),
-        //"home/rt" is not part of the official builds
-        // but we may be creating this symlink to make older NB projects
-        // happy. Make sure to not include it into final artifact
-        Rule.suffixNeg("home/rt"),
-        Rule.suffixNeg("jre/bin"),
-        Rule.suffixNeg("jre/bin/rmiregistry"),
-        Rule.suffixNeg("jre/bin/tnameserv"),
-        Rule.suffixNeg("jre/bin/keytool"),
-        Rule.suffixNeg("jre/bin/klist"),
-        Rule.suffixNeg("jre/bin/ktab"),
-        Rule.suffixNeg("jre/bin/policytool"),
-        Rule.suffixNeg("jre/bin/orbd"),
-        Rule.suffixNeg("jre/bin/servertool"),
-        Rule.suffixNeg("jre/bin/javaws"),
-        Rule.suffixNeg("jre/bin/java"),
-//        Rule.suffixNeg("jre/lib/ext"), //need some of jars there for https to work
-        Rule.suffixNeg("jre/lib/nibs"),
-//keep core deploy APIs but strip plugin dll
-//        Rule.suffixNeg("jre/lib/deploy"),
-//        Rule.suffixNeg("jre/lib/deploy.jar"),
-//        Rule.suffixNeg("jre/lib/javaws.jar"),
-//        Rule.suffixNeg("jre/lib/libdeploy.dylib"),
-//        Rule.suffixNeg("jre/lib/plugin.jar"),
-        Rule.suffixNeg("jre/lib/libnpjp2.dylib"),
-        Rule.suffixNeg("jre/lib/security/javaws.policy")
-    };
-
-    public static Rule[] winRules = {
-        Rule.prefixNeg("\\bin\\new_plugin"),
-        Rule.suffix("deploy.jar"), //take deploy.jar
-        Rule.prefixNeg("\\lib\\deploy"),
-        Rule.suffixNeg(".pdb"),
-        Rule.suffixNeg(".map"),
-        Rule.suffixNeg("axbridge.dll"),
-        Rule.suffixNeg("eula.dll"),
-        Rule.substrNeg("javacpl"),
-        Rule.suffixNeg("wsdetect.dll"),
-        Rule.substrNeg("eployjava1.dll"), //NP and IE versions
-        Rule.substrNeg("bin\\jp2"),
-        Rule.substrNeg("bin\\jpi"),
-//        Rule.suffixNeg("lib\\ext"), //need some of jars there for https to work
-        Rule.suffixNeg("ssv.dll"),
-        Rule.substrNeg("npjpi"),
-        Rule.substrNeg("npoji"),
-        Rule.suffixNeg(".exe"),
-//keep core deploy files as JavaFX APIs use them
-//        Rule.suffixNeg("deploy.dll"),
-//        Rule.suffixNeg("deploy.jar"),
-//        Rule.suffixNeg("javaws.jar"),
-//        Rule.suffixNeg("plugin.jar"),
-        Rule.suffix(".jar")
-    };
-
-    public static Rule[] linuxRules = {
-        Rule.prefixNeg("/bin"),
-        Rule.prefixNeg("/plugin"),
-//        Rule.prefixNeg("/lib/ext"), //need some of jars there for https to work
-        Rule.suffix("deploy.jar"), //take deploy.jar
-        Rule.prefixNeg("/lib/deploy"),
-        Rule.prefixNeg("/lib/desktop"),
-        Rule.substrNeg("libnpjp2.so")
-    };
-
     //Validation approach:
     //  - JRE marker (rt.jar)
     //  - FX marker (jfxrt.jar)
@@ -563,30 +389,7 @@
 
     //input dir "jdk/jre" (i.e. jre folder in the jdk)
     private void doSetRuntime(File baseDir) {
-        boolean isMac = System.getProperty("os.name").toLowerCase().contains("os x");
-
-        //Normalization: on MacOS we need to point to the top of JDK dir
-        // (other platforms are fine)
-        if (isMac) {
-            //On Mac we need Bundle root, not jdk/Contents/Home
-            baseDir = baseDir.getParentFile().getParentFile().getParentFile();
-        }
-
-        Set<File> lst = new HashSet<>();
-
-        Rule ruleset[];
-        if (System.getProperty("os.name").startsWith("Mac")) {
-            ruleset = macRules;
-        } else if (System.getProperty("os.name").startsWith("Win")) {
-            ruleset = winRules;
-        } else {
-            //must be linux
-            ruleset = linuxRules;
-        }
-
-        walk(baseDir, baseDir, ruleset, lst);
-
-        params.put(PARAM_RUNTIME, new RelativeFileSet(baseDir, lst));
+        params.put(PARAM_RUNTIME, baseDir.toString());
     }
 
     //Currently unused?
@@ -596,7 +399,7 @@
     //}
 
     public RelativeFileSet getAppResource() {
-        return fetchParam(StandardBundlerParam.APP_RESOURCES);
+        return fetchParam(APP_RESOURCES);
     }
 
     public void setAppResource(RelativeFileSet fs) {
@@ -604,7 +407,7 @@
     }
 
     public File getIcon() {
-        return fetchParam(StandardBundlerParam.ICON);
+        return fetchParam(ICON);
     }
 
     public void setIcon(File icon) {
@@ -612,7 +415,7 @@
     }
 
     public String getApplicationCategory() {
-        return fetchParam(String.class, PARAM_CATEGORY); //FIXME
+        return fetchParam(CATEGORY);
     }
 
     public void setApplicationCategory(String category) {
@@ -634,7 +437,7 @@
     }
 
     public String getCopyright() {
-        return fetchParam(StandardBundlerParam.COPYRIGHT);
+        return fetchParam(COPYRIGHT);
     }
 
     public void setCopyright(String c) {
@@ -642,7 +445,7 @@
     }
 
     public String getIdentifier() {
-        return fetchParam(StandardBundlerParam.IDENTIFIER);
+        return fetchParam(IDENTIFIER);
     }
 
     public void setIdentifier(String s) {
@@ -720,7 +523,7 @@
     }
 
     public String getVendor() {
-        return fetchParam(StandardBundlerParam.VENDOR);
+        return fetchParam(VENDOR);
     }
 
     public void setVendor(String vendor) {
--- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/ConfigException.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/ConfigException.java	Mon Mar 17 09:51:09 2014 -0600
@@ -26,13 +26,18 @@
 package com.sun.javafx.tools.packager.bundlers;
 
 public class ConfigException extends Exception {
-    String advice = null;
+    final String advice;
 
     public ConfigException(String msg, String advice) {
         super(msg);
         this.advice = advice;
     }
 
+    public ConfigException(Exception cause) {
+        super(cause);
+        this.advice = null;
+    }
+
     public String getAdvice() {
         return advice;
     }
--- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/LinuxAppBundler.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/LinuxAppBundler.java	Mon Mar 17 09:51:09 2014 -0600
@@ -27,6 +27,8 @@
 
 import com.oracle.bundlers.AbstractBundler;
 import com.oracle.bundlers.BundlerParamInfo;
+import com.oracle.bundlers.JreUtils;
+import com.oracle.bundlers.JreUtils.Rule;
 import com.oracle.bundlers.StandardBundlerParam;
 import com.sun.javafx.tools.packager.Log;
 import com.sun.javafx.tools.resource.linux.LinuxResources;
@@ -54,24 +56,63 @@
     public static final BundlerParamInfo<URL> RAW_EXECUTABLE_URL = new StandardBundlerParam<>(
             I18N.getString("param.raw-executable-url.name"),
             I18N.getString("param.raw-executable-url.description"),
-            "linux.launcher.url",  //KEY
+            "linux.launcher.url",
             URL.class, null, params -> LinuxResources.class.getResource(EXECUTABLE_NAME),
-            false, s -> {
-        try {
-            return new URL(s);
-        } catch (MalformedURLException e) {
-            Log.info(e.toString());
-            return null;
-        }
-    });
+            false, (s, p) -> {
+                try {
+                    return new URL(s);
+                } catch (MalformedURLException e) {
+                    Log.info(e.toString());
+                    return null;
+                }
+            });
+
+    //Subsetting of JRE is restricted.
+    //JRE README defines what is allowed to strip:
+    //   http://www.oracle.com/technetwork/java/javase/jre-7-readme-430162.html //TODO update when 8 goes GA
+    //
+    public static final BundlerParamInfo<Rule[]> LINUX_JRE_RULES = new StandardBundlerParam<>(
+            "",
+            "",
+            ".linux.runtime.rules",
+            Rule[].class,
+            null,
+            params -> new Rule[]{
+                    Rule.prefixNeg("/bin"),
+                    Rule.prefixNeg("/plugin"),
+                    //Rule.prefixNeg("/lib/ext"), //need some of jars there for https to work
+                    Rule.suffix("deploy.jar"), //take deploy.jar
+                    Rule.prefixNeg("/lib/deploy"),
+                    Rule.prefixNeg("/lib/desktop"),
+                    Rule.substrNeg("libnpjp2.so")
+            },
+            false,
+            (s, p) ->  null
+    );
+
+    public static final BundlerParamInfo<RelativeFileSet> LINUX_RUNTIME = new StandardBundlerParam<>(
+            RUNTIME.getName(),
+            RUNTIME.getDescription(),
+            RUNTIME.getID(),
+            RelativeFileSet.class,
+            null,
+            params -> JreUtils.extractJreAsRelativeFileSet(System.getProperty("java.home"),
+                    LINUX_JRE_RULES.fetchFrom(params)),
+            false,
+            (s, p) -> JreUtils.extractJreAsRelativeFileSet(s, LINUX_JRE_RULES.fetchFrom(p))
+    );
 
     @Override
     public boolean validate(Map<String, ? super Object> p) throws UnsupportedPlatformException, ConfigException {
-        if (p == null) throw new ConfigException(
-                I18N.getString("error.parameters-null"),
-                I18N.getString("error.parameters-null.advice"));
+        try {
+            if (p == null) throw new ConfigException(
+                    I18N.getString("error.parameters-null"),
+                    I18N.getString("error.parameters-null.advice"));
 
-        return doValidate(p);
+            return doValidate(p);
+        } catch (RuntimeException re) {
+            throw new ConfigException(re);
+        }
     }
 
     //used by chained bundlers to reuse validation logic
@@ -221,7 +262,7 @@
     }
 
     private void copyRuntime(Map<String, ? super Object> params, File runtimeDirectory) throws IOException {
-        RelativeFileSet runtime = RUNTIME.fetchFrom(params);
+        RelativeFileSet runtime = LINUX_RUNTIME.fetchFrom(params);
         if (runtime == null) {
             //request to use system runtime
             return;
@@ -249,7 +290,7 @@
 
     @Override
     public String getID() {
-        return "linux.app";  //KEY
+        return "linux.app";
     }
 
     @Override
@@ -273,7 +314,7 @@
                 MAIN_JAR_CLASSPATH,
                 PREFERENCES_ID,
                 RAW_EXECUTABLE_URL,
-                RUNTIME,
+                LINUX_RUNTIME,
                 USE_FX_PACKAGING,
                 USER_JVM_OPTIONS,
                 VERSION
--- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/LinuxDebBundler.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/LinuxDebBundler.java	Mon Mar 17 09:51:09 2014 -0600
@@ -50,13 +50,13 @@
     public static final BundlerParamInfo<LinuxAppBundler> APP_BUNDLER = new StandardBundlerParam<>(
             I18N.getString("param.app-bundler.name"),
             I18N.getString("param.app-bundler.description"),
-            "linuxAppBundler",  //KEY
-            LinuxAppBundler.class, null, params -> new LinuxAppBundler(), false, s -> null);
+            "linux.app.bundler",
+            LinuxAppBundler.class, null, params -> new LinuxAppBundler(), false, (s, p) -> null);
 
     public static final BundlerParamInfo<String> BUNDLE_NAME = new StandardBundlerParam<> (
             I18N.getString("param.bundle-name.name"),
             I18N.getString("param.bundle-name.description"),
-            "bundleName",  //KEY
+            "linux.bundleName",
             String.class, null, params -> {
                 String nm = APP_NAME.fetchFrom(params);
                 if (nm == null) return null;
@@ -65,47 +65,48 @@
                 nm = nm.replaceAll(" ", "");
                 return nm;
         
-            }, false, s -> s);
+            }, false, (s, p) -> s);
 
     public static final BundlerParamInfo<String> FULL_PACKAGE_NAME = new StandardBundlerParam<> (
             I18N.getString("param.full-package-name.name"),
             I18N.getString("param.full-package-name.description"),
-            "fullPackageName",  //KEY
+            "linux.deb.fullPackageName",
             String.class, null,
             params -> APP_NAME.fetchFrom(params) + "-" + VERSION.fetchFrom(params),
-            false, s -> s);
+            false, (s, p) -> s);
 
     public static final BundlerParamInfo<File> CONFIG_ROOT = new StandardBundlerParam<>(
             I18N.getString("param.config-root.name"),
             I18N.getString("param.config-root.description"),
-            "configRoot", //KEY
+            "configRoot",
             File.class, null, params ->  new File(BUILD_ROOT.fetchFrom(params), "linux"),
-            false, s -> new File(s));
+            false, (s, p) -> new File(s));
 
-    public static final BundlerParamInfo<File> IMAGE_DIR = new StandardBundlerParam<>(
+    public static final BundlerParamInfo<File> DEB_IMAGE_DIR = new StandardBundlerParam<>(
             I18N.getString("param.image-dir.name"), 
             I18N.getString("param.image-dir.description"),
-            "imageDir",  //KEY
+            "linux.deb.imageDir",
             File.class, null, params -> {
                 File imagesRoot = IMAGES_ROOT.fetchFrom(params);
+                if (!imagesRoot.exists()) imagesRoot.mkdirs();
                 return new File(new File(imagesRoot, "linux-deb.image"), FULL_PACKAGE_NAME.fetchFrom(params));
-            }, false, File::new);
+            }, false, (s, p) -> new File(s));
 
     public static final BundlerParamInfo<File> APP_IMAGE_ROOT = new StandardBundlerParam<>(
             I18N.getString("param.app-image-root.name"),
             I18N.getString("param.app-image-root.description"),
-            "appImageRoot",  //KEY
+            "linux.deb.imageRoot",
             File.class, null, params -> {
-                File imageDir = IMAGE_DIR.fetchFrom(params);
+                File imageDir = DEB_IMAGE_DIR.fetchFrom(params);
                 return new File(imageDir, "opt");
-            }, false, File::new);
+            }, false, (s, p) -> new File(s));
 
     public static final BundlerParamInfo<File> CONFIG_DIR = new StandardBundlerParam<>(
             I18N.getString("param.config-dir.name"), 
             I18N.getString("param.config-dir.description"),
-            "configDir",  //KEY
-            File.class, null, params ->  new File(IMAGE_DIR.fetchFrom(params), "DEBIAN"),
-            false, File::new);
+            "linux.deb.configDir",
+            File.class, null, params ->  new File(DEB_IMAGE_DIR.fetchFrom(params), "DEBIAN"),
+            false, (s, p) -> new File(s));
 
     public static final BundlerParamInfo<String> EMAIL = new StandardBundlerParam<> (
             I18N.getString("param.maintainer-email.name"), 
@@ -113,37 +114,28 @@
             BundleParams.PARAM_EMAIL,
             String.class, null,
             params -> "Unknown",
-            false, s -> s);
+            false, (s, p) -> s);
 
     public static final BundlerParamInfo<String> MAINTAINER = new StandardBundlerParam<> (
             I18N.getString("param.maintainer-name.name"), 
             I18N.getString("param.maintainer-name.description"),
-            "maintainer", //KEY
+            "linux.deb.maintainer",
             String.class, null,
             params -> VENDOR.fetchFrom(params) + " <" + EMAIL.fetchFrom(params) + ">",
-            false, s -> s);
-
-    public static final BundlerParamInfo<String> LICENCE_TYPE = new StandardBundlerParam<> (
-            I18N.getString("param.license-type.name"), 
-            I18N.getString("param.license-type.description"),
-            "licenceType", //KEY
-            String.class, null,
-            params -> "Unknown", // FIXME default
-            false, s -> s);
-
+            false, (s, p) -> s);
 
     public static final BundlerParamInfo<String> LICENSE_TEXT = new StandardBundlerParam<> (
             I18N.getString("param.license-text.name"), 
             I18N.getString("param.license-text.description"),
-            "licenceText", //KEY
+            "linux.deb.licenseText",
             String.class, null,
             params -> {
                 try {
                     List<String> licenseFiles = LICENSE_FILES.fetchFrom(params);
                     RelativeFileSet appRoot = APP_RESOURCES.fetchFrom(params);
-                    //need to copy license file to the root of win-app.image
-                    for (String s : licenseFiles) {
-                        return new String(IOUtils.readFully(new File(appRoot.getBaseDirectory(), s)));
+                    //need to copy license file to the root of linux-app.image
+                    if (licenseFiles.size() > 0) {
+                        return new String(IOUtils.readFully(new File(appRoot.getBaseDirectory(), licenseFiles.get(0))));
                     }
                 } catch (Exception e) {
                     if (Log.isDebug()) {
@@ -152,7 +144,7 @@
                 }
                 return LICENSE_TYPE.fetchFrom(params);
             },
-            false, s -> s);
+            false, (s, p) -> s);
 
     private final static String DEFAULT_ICON = "javalogo_white_32.png";
     private final static String DEFAULT_CONTROL_TEMPLATE = "template.control";
@@ -162,6 +154,7 @@
     private final static String DEFAULT_POSTINSTALL_TEMPLATE = "template.postinst";
     private final static String DEFAULT_COPYRIGHT_TEMPLATE = "template.copyright";
     private final static String DEFAULT_DESKTOP_FILE_TEMPLATE = "template.desktop";
+    private final static String DEFAULT_INIT_SCRIPT_TEMPLATE = "template.deb.init.script";
 
     private final static String TOOL_DPKG = "dpkg-deb";
 
@@ -175,7 +168,7 @@
             ProcessBuilder pb = new ProcessBuilder(
                     toolName,
                     "--version");
-            IOUtils.exec(pb, Log.isDebug(), true); //not interested in the output
+            IOUtils.exec(pb, Log.isDebug(), true); //FIXME not interested in the output
         } catch (Exception e) {
             Log.verbose(MessageFormat.format(I18N.getString("message.test-for-tool"), toolName, e.getMessage()));
             return false;
@@ -185,22 +178,26 @@
 
     @Override
     public boolean validate(Map<String, ? super Object> p) throws UnsupportedPlatformException, ConfigException {
-        if (p == null) throw new ConfigException(
-                I18N.getString("error.parameters-null"),
-                I18N.getString("error.parameters-null.advice"));
+        try {
+            if (p == null) throw new ConfigException(
+                    I18N.getString("error.parameters-null"),
+                    I18N.getString("error.parameters-null.advice"));
 
-        //run basic validation to ensure requirements are met
-        //we are not interested in return code, only possible exception
-        APP_BUNDLER.fetchFrom(p).doValidate(p);
+            //run basic validation to ensure requirements are met
+            //we are not interested in return code, only possible exception
+            APP_BUNDLER.fetchFrom(p).doValidate(p);
 
-        //NOTE: Can we validate that the required tools are available before we start?
-        if (!testTool(TOOL_DPKG, "1")){
-            throw new ConfigException(
-                    MessageFormat.format(I18N.getString("error.tool-not-found"), TOOL_DPKG),
-                    I18N.getString("error.tool-not-found.advice"));
+            //NOTE: Can we validate that the required tools are available before we start?
+            if (!testTool(TOOL_DPKG, "1")){
+                throw new ConfigException(
+                        MessageFormat.format(I18N.getString("error.tool-not-found"), TOOL_DPKG),
+                        I18N.getString("error.tool-not-found.advice"));
+            }
+
+            return true;
+        } catch (RuntimeException re) {
+            throw new ConfigException(re);
         }
-
-        return true;
     }
 
     private boolean prepareProto(Map<String, ? super Object> p) {
@@ -223,7 +220,7 @@
         //             app
         //             runtime
 
-        File imageDir = IMAGE_DIR.fetchFrom(p);
+        File imageDir = DEB_IMAGE_DIR.fetchFrom(p);
         File configDir = CONFIG_DIR.fetchFrom(p);
 
         try {
@@ -240,7 +237,7 @@
             return null;
         } finally {
             try {
-                if (verbose) {
+                if (VERBOSE.fetchFrom(p)) {
                     saveConfigFiles(p);
                 }
                 if (imageDir != null && !Log.isDebug()) {
@@ -308,6 +305,12 @@
                 IOUtils.copyFile(getConfig_IconFile(params),
                         new File(configRoot, getConfig_IconFile(params).getName()));
             }
+            if (SERVICE_HINT.fetchFrom(params)) {
+                if (getConfig_InitScriptFile(params).exists()) {
+                    IOUtils.copyFile(getConfig_InitScriptFile(params),
+                            new File(configRoot, getConfig_InitScriptFile(params).getName()));
+                }
+            }
             Log.info(MessageFormat.format(I18N.getString("message.config-save-location"), configRoot.getAbsolutePath()));
         } catch (IOException ioe) {
             ioe.printStackTrace();
@@ -365,6 +368,10 @@
         data.put("APPLICATION_LICENSE_TEXT", LICENSE_TEXT.fetchFrom(params));
         data.put("APPLICATION_ARCH", getArch());
         data.put("APPLICATION_INSTALLED_SIZE", Long.toString(getInstalledSizeKB(params)));
+        data.put("SERVICE_HINT", String.valueOf(SERVICE_HINT.fetchFrom(params)));
+        data.put("START_ON_INSTALL", String.valueOf(START_ON_INSTALL.fetchFrom(params)));
+        data.put("STOP_ON_UNINSTALL", String.valueOf(STOP_ON_UNINSTALL.fetchFrom(params)));
+        data.put("RUN_AT_STARTUP", String.valueOf(RUN_AT_STARTUP.fetchFrom(params)));
 
         //prepare control file
         Writer w = new BufferedWriter(new FileWriter(getConfig_ControlFile(params)));
@@ -372,7 +379,8 @@
                 LinuxAppBundler.LINUX_BUNDLER_PREFIX + getConfig_ControlFile(params).getName(),
                 I18N.getString("resource.deb-control-file"), 
                 DEFAULT_CONTROL_TEMPLATE, 
-                data);
+                data,
+                VERBOSE.fetchFrom(params));
         w.write(content);
         w.close();
 
@@ -381,7 +389,8 @@
                 LinuxAppBundler.LINUX_BUNDLER_PREFIX + getConfig_PreinstallFile(params).getName(),
                 I18N.getString("resource.deb-preinstall-script"),
                 DEFAULT_PREINSTALL_TEMPLATE,
-                data);
+                data,
+                VERBOSE.fetchFrom(params));
         w.write(content);
         w.close();
         setPermissions(getConfig_PreinstallFile(params), "rwxr-xr-x");
@@ -391,7 +400,8 @@
                 LinuxAppBundler.LINUX_BUNDLER_PREFIX + getConfig_PrermFile(params).getName(),
                 I18N.getString("resource.deb-prerm-script"),
                 DEFAULT_PRERM_TEMPLATE,
-                data);
+                data,
+                VERBOSE.fetchFrom(params));
         w.write(content);
         w.close();
         setPermissions(getConfig_PrermFile(params), "rwxr-xr-x");
@@ -401,7 +411,8 @@
                 LinuxAppBundler.LINUX_BUNDLER_PREFIX + getConfig_PostinstallFile(params).getName(),
                 I18N.getString("resource.deb-postinstall-script"),
                 DEFAULT_POSTINSTALL_TEMPLATE,
-                data);
+                data,
+                VERBOSE.fetchFrom(params));
         w.write(content);
         w.close();
         setPermissions(getConfig_PostinstallFile(params), "rwxr-xr-x");
@@ -411,7 +422,8 @@
                 LinuxAppBundler.LINUX_BUNDLER_PREFIX + getConfig_PostrmFile(params).getName(),
                 I18N.getString("resource.deb-postrm-script"),
                 DEFAULT_POSTRM_TEMPLATE,
-                data);
+                data,
+                VERBOSE.fetchFrom(params));
         w.write(content);
         w.close();
         setPermissions(getConfig_PostrmFile(params), "rwxr-xr-x");
@@ -421,7 +433,8 @@
                 LinuxAppBundler.LINUX_BUNDLER_PREFIX + getConfig_CopyrightFile(params).getName(),
                 I18N.getString("resource.deb-copyright-file"), 
                 DEFAULT_COPYRIGHT_TEMPLATE, 
-                data);
+                data,
+                VERBOSE.fetchFrom(params));
         w.write(content);
         w.close();
 
@@ -431,7 +444,8 @@
                 LinuxAppBundler.LINUX_BUNDLER_PREFIX + getConfig_DesktopShortcutFile(params).getName(),
                 I18N.getString("resource.menu-shortcut-descriptor"), 
                 DEFAULT_DESKTOP_FILE_TEMPLATE, 
-                data);
+                data,
+                VERBOSE.fetchFrom(params));
         w.write(content);
         w.close();
 
@@ -442,14 +456,30 @@
             fetchResource(LinuxAppBundler.LINUX_BUNDLER_PREFIX + iconTarget.getName(),
                     I18N.getString("resource.menu-icon"),
                     DEFAULT_ICON,
-                    iconTarget);
+                    iconTarget,
+                    VERBOSE.fetchFrom(params));
         } else {
             fetchResource(LinuxAppBundler.LINUX_BUNDLER_PREFIX + iconTarget.getName(),
                     I18N.getString("resource.menu-icon"),
                     icon,
-                    iconTarget);
+                    iconTarget,
+                    VERBOSE.fetchFrom(params));
         }
 
+        if (SERVICE_HINT.fetchFrom(params)) {
+            //prepare init script
+            w = new BufferedWriter(new FileWriter(getConfig_InitScriptFile(params)));
+            content = preprocessTextResource(
+                    LinuxAppBundler.LINUX_BUNDLER_PREFIX + getConfig_InitScriptFile(params).getName(),
+                    I18N.getString("resource.deb-init-script"), 
+                    DEFAULT_INIT_SCRIPT_TEMPLATE, 
+                    data,
+                    VERBOSE.fetchFrom(params));
+            w.write(content);
+            w.close();
+            setPermissions(getConfig_InitScriptFile(params), "rwxr-xr-x");
+        }
+        
         return true;
     }
 
@@ -465,6 +495,12 @@
                 BUNDLE_NAME.fetchFrom(params) + ".png");
     }
 
+    private File getConfig_InitScriptFile(Map<String, ? super Object> params) {
+        return new File(
+                LinuxAppBundler.getLauncher(APP_IMAGE_ROOT.fetchFrom(params), params).getParentFile(),
+                BUNDLE_NAME.fetchFrom(params) + ".init");
+    }
+
     private File getConfig_ControlFile(Map<String, ? super Object> params) {
         return new File(CONFIG_DIR.fetchFrom(params), "control");
     }
@@ -499,8 +535,8 @@
         ProcessBuilder pb = new ProcessBuilder(
                 "fakeroot", TOOL_DPKG, "-b",  FULL_PACKAGE_NAME.fetchFrom(params),
                 outFile.getAbsolutePath());
-        pb = pb.directory(IMAGE_DIR.fetchFrom(params).getParentFile());
-        IOUtils.exec(pb, verbose);
+        pb = pb.directory(DEB_IMAGE_DIR.fetchFrom(params).getParentFile());
+        IOUtils.exec(pb, VERBOSE.fetchFrom(params));
 
         Log.info(MessageFormat.format(I18N.getString("message.output-to-location"), outFile.getAbsolutePath()));
 
@@ -519,7 +555,7 @@
 
     @Override
     public String getID() {
-        return "deb"; //KEY
+        return "deb";
     }
 
     @Override
@@ -539,7 +575,6 @@
         return Arrays.asList(
                 APP_BUNDLER,
                 APP_IMAGE_ROOT,
-                APP_NAME,
                 APP_RESOURCES,
                 BUNDLE_NAME,
                 CONFIG_DIR,
@@ -549,7 +584,7 @@
                 EMAIL,
                 FULL_PACKAGE_NAME,
                 ICON,
-                IMAGE_DIR,
+                DEB_IMAGE_DIR,
                 IMAGES_ROOT,
                 LICENSE_FILES,
                 LICENSE_TEXT,
--- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/LinuxRPMBundler.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/LinuxRPMBundler.java	Mon Mar 17 09:51:09 2014 -0600
@@ -32,8 +32,13 @@
 import com.sun.javafx.tools.resource.linux.LinuxResources;
 
 import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
 import java.text.MessageFormat;
 import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -47,29 +52,30 @@
     public static final BundlerParamInfo<LinuxAppBundler> APP_BUNDLER = new StandardBundlerParam<>(
             I18N.getString("param.app-bundler.name"), 
             I18N.getString("param.app-bundler.description"),
-            "linuxAppBundler", //KEY
+            "linux.app.bundler",
             LinuxAppBundler.class, null, params -> new LinuxAppBundler(), false, null);
 
-    public static final BundlerParamInfo<File> IMAGE_DIR = new StandardBundlerParam<>(
+    public static final BundlerParamInfo<File> RPM_IMAGE_DIR = new StandardBundlerParam<>(
             I18N.getString("param.image-dir.name"), 
             I18N.getString("param.image-dir.description"),
-            "imageDir", //KEY
+            "linux.rpm.imageDir",
             File.class, null, params -> {
                 File imagesRoot = IMAGES_ROOT.fetchFrom(params);
+                if (!imagesRoot.exists()) imagesRoot.mkdirs();
                 return new File(imagesRoot, "linux-rpm.image");
-            }, false, File::new);
+            }, false, (s, p) -> new File(s));
 
     public static final BundlerParamInfo<File> CONFIG_ROOT = new StandardBundlerParam<>(
             I18N.getString("param.config-root.name"), 
             I18N.getString("param.config-root.description"),
-            "configRoot", //KEY
+            "configRoot",
             File.class, null, params ->  new File(BUILD_ROOT.fetchFrom(params), "linux"),
-            false, File::new);
+            false, (s, p) -> new File(s));
 
     public static final BundlerParamInfo<String> BUNDLE_NAME = new StandardBundlerParam<> (
             I18N.getString("param.bundle-name.name"), 
             I18N.getString("param.bundle-name.description"),
-            "bundleName", //KEY
+            "linux.bundleName",
             String.class, null, params -> {
                 String nm = APP_NAME.fetchFrom(params);
                 if (nm == null) return null;
@@ -77,11 +83,12 @@
                 //spaces are not allowed in RPM package names
                 nm = nm.replaceAll(" ", "");
                 return nm;
-            }, false, s -> s);
+            }, false, (s, p) -> s);
 
     private final static String DEFAULT_ICON = "javalogo_white_32.png";
     private final static String DEFAULT_SPEC_TEMPLATE = "template.spec";
     private final static String DEFAULT_DESKTOP_FILE_TEMPLATE = "template.desktop";
+    private final static String DEFAULT_INIT_SCRIPT_TEMPLATE = "template.rpm.init.script";
 
     public final static String TOOL_RPMBUILD = "rpmbuild";
     public final static double TOOL_RPMBUILD_MIN_VERSION = 4.0d;
@@ -118,32 +125,36 @@
 
     @Override
     public boolean validate(Map<String, ? super Object> p) throws UnsupportedPlatformException, ConfigException {
-        if (p == null) throw new ConfigException(
-                I18N.getString("error.parameters-null"), 
-                I18N.getString("error.parameters-null.advice"));
+        try {
+            if (p == null) throw new ConfigException(
+                    I18N.getString("error.parameters-null"),
+                    I18N.getString("error.parameters-null.advice"));
 
-        //run basic validation to ensure requirements are met
-        //we are not interested in return code, only possible exception
-        APP_BUNDLER.fetchFrom(p).doValidate(p);
+            //run basic validation to ensure requirements are met
+            //we are not interested in return code, only possible exception
+            APP_BUNDLER.fetchFrom(p).doValidate(p);
 
-        //TODO: validate presense of required tools?
-        if (!testTool(TOOL_RPMBUILD, TOOL_RPMBUILD_MIN_VERSION)){
-            throw new ConfigException(
-                    I18N.getString(MessageFormat.format("error.cannot-find-rpmbuild", TOOL_RPMBUILD_MIN_VERSION)),
-                    I18N.getString(MessageFormat.format("error.cannot-find-rpmbuild.advice", TOOL_RPMBUILD_MIN_VERSION)));
+            //TODO: validate presense of required tools?
+            if (!testTool(TOOL_RPMBUILD, TOOL_RPMBUILD_MIN_VERSION)){
+                throw new ConfigException(
+                        I18N.getString(MessageFormat.format("error.cannot-find-rpmbuild", TOOL_RPMBUILD_MIN_VERSION)),
+                        I18N.getString(MessageFormat.format("error.cannot-find-rpmbuild.advice", TOOL_RPMBUILD_MIN_VERSION)));
+            }
+
+            return true;
+        } catch (RuntimeException re) {
+            throw new ConfigException(re);
         }
-
-        return true;
     }
 
     private boolean prepareProto(Map<String, ? super Object> params) {
-        File imageDir = IMAGE_DIR.fetchFrom(params);
+        File imageDir = RPM_IMAGE_DIR.fetchFrom(params);
         File appDir = APP_BUNDLER.fetchFrom(params).doBundle(params, imageDir, true);
         return appDir != null;
     }
 
     public File bundle(Map<String, ? super Object> p, File outdir) {
-        File imageDir = IMAGE_DIR.fetchFrom(p);
+        File imageDir = RPM_IMAGE_DIR.fetchFrom(p);
         try {
 
             imageDir.mkdirs();
@@ -165,7 +176,7 @@
             return null;
         } finally {
             try {
-                if (verbose) {
+                if (VERBOSE.fetchFrom(p)) {
                     saveConfigFiles(p);
                 }
                 if (imageDir != null && !Log.isDebug()) {
@@ -180,6 +191,22 @@
         }
     }
 
+    /*
+     * set permissions with a string like "rwxr-xr-x"
+     * 
+     * This cannot be directly backport to 22u which is unfortunately built with 1.6
+     */
+    private void setPermissions(File file, String permissions) {
+        Set<PosixFilePermission> filePermissions = PosixFilePermissions.fromString(permissions);
+        try {
+            if (file.exists()) {
+                Files.setPosixFilePermissions(file.toPath(), filePermissions);
+            }
+        } catch (IOException ex) {
+            Logger.getLogger(LinuxDebBundler.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+    
     protected void saveConfigFiles(Map<String, ? super Object> params) {
         try {
             File configRoot = CONFIG_ROOT.fetchFrom(params);
@@ -195,6 +222,12 @@
                 IOUtils.copyFile(getConfig_IconFile(params),
                         new File(configRoot, getConfig_IconFile(params).getName()));
             }
+            if (SERVICE_HINT.fetchFrom(params)) {
+                if (getConfig_InitScriptFile(params).exists()) {
+                    IOUtils.copyFile(getConfig_InitScriptFile(params),
+                            new File(configRoot, getConfig_InitScriptFile(params).getName()));
+                }
+            }
             Log.info(MessageFormat.format(I18N.getString("message.config-save-location"), configRoot.getAbsolutePath()));
         } catch (IOException ioe) {
             ioe.printStackTrace();
@@ -224,10 +257,11 @@
         Map<String, String> data = new HashMap<>();
 
         data.put("APPLICATION_NAME", BUNDLE_NAME.fetchFrom(params));
+        data.put("APPLICATION_PACKAGE", BUNDLE_NAME.fetchFrom(params).toLowerCase());
         data.put("APPLICATION_VENDOR", VENDOR.fetchFrom(params));
         data.put("APPLICATION_VERSION", VERSION.fetchFrom(params));
         data.put("APPLICATION_LAUNCHER_FILENAME",
-                LinuxAppBundler.getLauncher(IMAGE_DIR.fetchFrom(params), params).getName());
+                LinuxAppBundler.getLauncher(RPM_IMAGE_DIR.fetchFrom(params), params).getName());
         data.put("APPLICATION_DESKTOP_SHORTCUT", SHORTCUT_HINT.fetchFrom(params) ? "returnTrue" : "returnFalse");
         data.put("APPLICATION_MENU_SHORTCUT", MENU_HINT.fetchFrom(params) ? "returnTrue" : "returnFalse");
         data.put("DEPLOY_BUNDLE_CATEGORY", CATEGORY.fetchFrom(params)); //TODO rpm categories
@@ -235,12 +269,17 @@
         data.put("APPLICATION_SUMMARY", TITLE.fetchFrom(params));
         data.put("APPLICATION_LICENSE_TYPE", LICENSE_TYPE.fetchFrom(params));
         data.put("APPLICATION_LICENSE_FILE", getLicenseFileString(params));
+        data.put("SERVICE_HINT", String.valueOf(SERVICE_HINT.fetchFrom(params)));
+        data.put("START_ON_INSTALL", String.valueOf(START_ON_INSTALL.fetchFrom(params)));
+        data.put("STOP_ON_UNINSTALL", String.valueOf(STOP_ON_UNINSTALL.fetchFrom(params)));
+        data.put("RUN_AT_STARTUP", String.valueOf(RUN_AT_STARTUP.fetchFrom(params)));
 
         //prepare spec file
         Writer w = new BufferedWriter(new FileWriter(getConfig_SpecFile(params)));
         String content = preprocessTextResource(
                 LinuxAppBundler.LINUX_BUNDLER_PREFIX + getConfig_SpecFile(params).getName(),
-                I18N.getString("resource.rpm-spec-file"), DEFAULT_SPEC_TEMPLATE, data);
+                I18N.getString("resource.rpm-spec-file"), DEFAULT_SPEC_TEMPLATE, data,
+                VERBOSE.fetchFrom(params));
         w.write(content);
         w.close();
 
@@ -248,7 +287,8 @@
         w = new BufferedWriter(new FileWriter(getConfig_DesktopShortcutFile(params)));
         content = preprocessTextResource(
                 LinuxAppBundler.LINUX_BUNDLER_PREFIX + getConfig_DesktopShortcutFile(params).getName(),
-                I18N.getString("resource.menu-shortcut-descriptor"), DEFAULT_DESKTOP_FILE_TEMPLATE, data);
+                I18N.getString("resource.menu-shortcut-descriptor"), DEFAULT_DESKTOP_FILE_TEMPLATE, data,
+                VERBOSE.fetchFrom(params));
         w.write(content);
         w.close();
 
@@ -259,29 +299,50 @@
             fetchResource(LinuxAppBundler.LINUX_BUNDLER_PREFIX + iconTarget.getName(),
                     I18N.getString("resource.menu-icon"),
                     DEFAULT_ICON,
-                    iconTarget);
+                    iconTarget,
+                    VERBOSE.fetchFrom(params));
         } else {
             fetchResource(LinuxAppBundler.LINUX_BUNDLER_PREFIX + iconTarget.getName(),
                     I18N.getString("resource.menu-icon"),
                     icon,
-                    iconTarget);
+                    iconTarget,
+                    VERBOSE.fetchFrom(params));
+        }
+
+        if (SERVICE_HINT.fetchFrom(params)) {
+            //prepare init script
+            w = new BufferedWriter(new FileWriter(getConfig_InitScriptFile(params)));
+            content = preprocessTextResource(
+                    LinuxAppBundler.LINUX_BUNDLER_PREFIX + getConfig_InitScriptFile(params).getName(),
+                    I18N.getString("resource.rpm-init-script"), 
+                    DEFAULT_INIT_SCRIPT_TEMPLATE, 
+                    data,
+                    VERBOSE.fetchFrom(params));
+            w.write(content);
+            w.close();
+            setPermissions(getConfig_InitScriptFile(params), "rwxr-xr-x");
         }
 
         return true;
     }
 
     private File getConfig_DesktopShortcutFile(Map<String, ? super Object> params) {
-        return new File(LinuxAppBundler.getLauncher(IMAGE_DIR.fetchFrom(params), params).getParentFile(),
+        return new File(LinuxAppBundler.getLauncher(RPM_IMAGE_DIR.fetchFrom(params), params).getParentFile(),
                 BUNDLE_NAME.fetchFrom(params) + ".desktop");
     }
 
     private File getConfig_IconFile(Map<String, ? super Object> params) {
-        return new File(LinuxAppBundler.getLauncher(IMAGE_DIR.fetchFrom(params), params).getParentFile(),
+        return new File(LinuxAppBundler.getLauncher(RPM_IMAGE_DIR.fetchFrom(params), params).getParentFile(),
                 BUNDLE_NAME.fetchFrom(params) + ".png");
     }
 
+    private File getConfig_InitScriptFile(Map<String, ? super Object> params) {
+        return new File(LinuxAppBundler.getLauncher(RPM_IMAGE_DIR.fetchFrom(params), params).getParentFile(),
+                BUNDLE_NAME.fetchFrom(params) + ".init");
+    }
+
     private File getConfig_SpecFile(Map<String, ? super Object> params) {
-        return new File(IMAGE_DIR.fetchFrom(params),
+        return new File(RPM_IMAGE_DIR.fetchFrom(params),
                 BUNDLE_NAME.fetchFrom(params) + ".spec");
     }
 
@@ -297,19 +358,32 @@
                 TOOL_RPMBUILD,
                 "-bb", getConfig_SpecFile(params).getAbsolutePath(),
 //                "--define", "%__jar_repack %{nil}",  //debug: improves build time (but will require unpack to install?)
-                "--define", "%_sourcedir "+IMAGE_DIR.fetchFrom(params).getAbsolutePath(),
+                "--define", "%_sourcedir "+ RPM_IMAGE_DIR.fetchFrom(params).getAbsolutePath(),
                 "--define", "%_rpmdir " + outdir.getAbsolutePath(), //save result to output dir
                 "--define", "%_topdir " + broot.getAbsolutePath() //do not use other system directories to build as current user
         );
-        pb = pb.directory(IMAGE_DIR.fetchFrom(params));
-        IOUtils.exec(pb, verbose);
+        pb = pb.directory(RPM_IMAGE_DIR.fetchFrom(params));
+        IOUtils.exec(pb, VERBOSE.fetchFrom(params));
 
         IOUtils.deleteRecursive(broot);
 
         Log.info(MessageFormat.format(I18N.getString("message.output-bundle-location"), outdir.getAbsolutePath()));
 
-        //todo look for added files and returnt hat added file.
-        return outdir;
+        // presume the result is the ".rpm" file with the newest modified time
+        // not the best solution, but it is the most reliable
+        File result = null;
+        long lastModified = 0;
+        File[] list = outdir.listFiles();
+        if (list != null) {
+            for (File f : list) {
+                if (f.getName().endsWith(".rpm") && f.lastModified() > lastModified) {
+                    result = f;
+                    lastModified = f.lastModified();
+                }
+            }
+        }
+
+        return result;
     }
 
     @Override
@@ -324,7 +398,7 @@
 
     @Override
     public String getID() {
-        return "rpm"; //KEY
+        return "rpm";
     }
 
     @Override
@@ -350,7 +424,7 @@
                 CATEGORY,
                 DESCRIPTION,
                 ICON,
-                IMAGE_DIR,
+                RPM_IMAGE_DIR,
                 IMAGES_ROOT,
                 LICENSE_FILES,
                 LICENSE_TYPE,
--- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/MacAppBundler.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/MacAppBundler.java	Mon Mar 17 09:51:09 2014 -0600
@@ -25,22 +25,23 @@
 package com.sun.javafx.tools.packager.bundlers;
 
 import com.oracle.bundlers.*;
+import com.oracle.bundlers.JreUtils.Rule;
 import com.sun.javafx.tools.packager.Log;
 import com.sun.javafx.tools.resource.mac.MacResources;
 
 import java.io.*;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.text.MessageFormat;
 import java.util.*;
 
 import static com.oracle.bundlers.StandardBundlerParam.*;
-import static com.oracle.bundlers.StandardBundlerParam.USER_JVM_OPTIONS;
-import static com.oracle.bundlers.StandardBundlerParam.VERSION;
 import static com.oracle.bundlers.mac.MacBaseInstallerBundler.getPredefinedImage;
 
 public class MacAppBundler extends AbstractBundler {
-    private File configRoot = null;
-    private Map<String, ? super Object> params;
+
+    private static final ResourceBundle I18N =
+            ResourceBundle.getBundle("com.oracle.bundlers.mac.MacAppBundler");
 
     public final static String MAC_BUNDLER_PREFIX =
             BUNDLER_PREFIX + "macosx" + File.separator;
@@ -97,35 +98,148 @@
         return map;
     }
 
-    public static final EnumeratedBundlerParam<String> MAC_CATEGORY  =
+    public static final EnumeratedBundlerParam<String> MAC_CATEGORY =
             new EnumeratedBundlerParam<>(
                     "Category",
-                    "Mac Categories. Note that the key is the string to display to the user and the value is the id of the category",
-                    "LSApplicationCategoryType",
+                    "Mac App Store Categories. Note that the key is the string to display to the user and the value is the id of the category",
+                    "mac.category",
                     String.class,
-                    null,
+                    new String[] {CATEGORY.getID()},
                     params -> "Unknown",
                     false,
-                    s -> s,
+                    (s, p) -> s,
                     getMacCategories(),
                     false //strict - for MacStoreBundler this should be strict
             );
 
+    public static final BundlerParamInfo<String> MAC_CF_BUNDLE_NAME =
+            new StandardBundlerParam<>(
+                    "CFBundleName",
+                    "The name of the app as it appears in the Menu Bar.  This can be different from the application name.  This name should be less than 16 characters long and be suitable for displaying in the menu bar and the app’s Info window.",
+                    "mac.CFBundleName",
+                    String.class,
+                    null,
+                    params -> null,
+                    false,
+                    (s, p) -> s);
+
+    public static final BundlerParamInfo<File> CONFIG_ROOT = new StandardBundlerParam<>(
+            I18N.getString("param.config-root.name"),
+            I18N.getString("param.config-root.description"),
+            "configRoot",
+            File.class,
+            null,
+            params -> {
+                File configRoot = new File(BUILD_ROOT.fetchFrom(params), "macosx");
+                configRoot.mkdirs();
+                return configRoot;
+            },
+            false,
+            (s, p) -> new File(s));
+
     public static final BundlerParamInfo<URL> RAW_EXECUTABLE_URL = new StandardBundlerParam<>(
-            "Launcher URL", "Override the packager default launcher with a custom launcher.", "mac.launcher.url",
-            URL.class, null, params -> MacResources.class.getResource(EXECUTABLE_NAME),
-            false, s -> {
-        try {
-            return new URL(s);
-        } catch (MalformedURLException e) {
-            Log.info(e.toString());
-            return null;
+            "Launcher URL",
+            "Override the packager default launcher with a custom launcher.",
+            "mac.launcher.url",
+            URL.class,
+            null,
+            params -> MacResources.class.getResource(EXECUTABLE_NAME),
+            false,
+            (s, p) -> {
+                try {
+                    return new URL(s);
+                } catch (MalformedURLException e) {
+                    Log.info(e.toString());
+                    return null;
+                }
+            });
+
+    public static final BundlerParamInfo<String> DEFAULT_ICNS_ICON = new StandardBundlerParam<>(
+            "Default Icon",
+            "The Default Icon for when a user does not specify an icns file.",
+            ".mac.default.icns",
+            String.class,
+            null,
+            params -> TEMPLATE_BUNDLE_ICON,
+            false,
+            (s, p) -> s);
+
+    //Subsetting of JRE is restricted.
+    //JRE README defines what is allowed to strip:
+    //   http://www.oracle.com/technetwork/java/javase/jre-7-readme-430162.html //TODO update when 8 goes GA
+    //
+    public static final BundlerParamInfo<Rule[]> MAC_JDK_RULES = new StandardBundlerParam<>(
+            "",
+            "",
+            ".mac-jdk.runtime.rules",
+            Rule[].class,
+            null,
+            params -> new Rule[]{
+                    Rule.suffixNeg("macos/libjli.dylib"),
+                    Rule.suffixNeg("resources"),
+                    Rule.suffixNeg("home/bin"),
+                    Rule.suffixNeg("home/db"),
+                    Rule.suffixNeg("home/demo"),
+                    Rule.suffixNeg("home/include"),
+                    Rule.suffixNeg("home/lib"),
+                    Rule.suffixNeg("home/man"),
+                    Rule.suffixNeg("home/release"),
+                    Rule.suffixNeg("home/sample"),
+                    Rule.suffixNeg("home/src.zip"),
+                    //"home/rt" is not part of the official builds
+                    // but we may be creating this symlink to make older NB projects
+                    // happy. Make sure to not include it into final artifact
+                    Rule.suffixNeg("home/rt"),
+                    Rule.suffixNeg("jre/bin"),
+                    Rule.suffixNeg("jre/bin/rmiregistry"),
+                    Rule.suffixNeg("jre/bin/tnameserv"),
+                    Rule.suffixNeg("jre/bin/keytool"),
+                    Rule.suffixNeg("jre/bin/klist"),
+                    Rule.suffixNeg("jre/bin/ktab"),
+                    Rule.suffixNeg("jre/bin/policytool"),
+                    Rule.suffixNeg("jre/bin/orbd"),
+                    Rule.suffixNeg("jre/bin/servertool"),
+                    Rule.suffixNeg("jre/bin/javaws"),
+                    Rule.suffixNeg("jre/bin/java"),
+                    //Rule.suffixNeg("jre/lib/ext"), //need some of jars there for https to work
+                    Rule.suffixNeg("jre/lib/nibs"),
+                    //keep core deploy APIs but strip plugin dll
+                    //Rule.suffixNeg("jre/lib/deploy"),
+                    //Rule.suffixNeg("jre/lib/deploy.jar"),
+                    //Rule.suffixNeg("jre/lib/javaws.jar"),
+                    //Rule.suffixNeg("jre/lib/libdeploy.dylib"),
+                    //Rule.suffixNeg("jre/lib/plugin.jar"),
+                    Rule.suffixNeg("jre/lib/libnpjp2.dylib"),
+                    Rule.suffixNeg("jre/lib/security/javaws.policy"),
+                    Rule.substrNeg("Contents/Info.plist")
+            },
+            false,
+            (s, p) -> null
+    );
+
+    public static final BundlerParamInfo<RelativeFileSet> MAC_RUNTIME = new StandardBundlerParam<>(
+            RUNTIME.getName(),
+            RUNTIME.getDescription(),
+            RUNTIME.getID(),
+            RelativeFileSet.class,
+            null,
+            params -> extractMacRuntime(System.getProperty("java.home"), params),
+            false,
+            MacAppBundler::extractMacRuntime
+    );
+
+    public static RelativeFileSet extractMacRuntime(String base, Map<String, ? super Object> params) {
+        if (base.endsWith("/Home")) {
+            throw new IllegalArgumentException("Currently Macs require a JDK to package");
+        } else if (base.endsWith("/Home/jre")) {
+            File baseDir = new File(base).getParentFile().getParentFile().getParentFile();
+            return JreUtils.extractJreAsRelativeFileSet(baseDir.toString(),
+                    MAC_JDK_RULES.fetchFrom(params));
+        } else {
+            // for now presume we are pointed to the top of a JDK
+            return JreUtils.extractJreAsRelativeFileSet(base,
+                    MAC_JDK_RULES.fetchFrom(params));
         }
-    });
-
-    private void setBuildRoot(File dir) {
-        configRoot = new File(dir, "macosx");
-        configRoot.mkdirs();
     }
 
     public MacAppBundler() {
@@ -135,8 +249,12 @@
 
     @Override
     public boolean validate(Map<String, ? super Object> params) throws UnsupportedPlatformException, ConfigException {
-        logParameters(params);
-        return doValidate(params);
+        try {
+            logParameters(params);
+            return doValidate(params);
+        } catch (RuntimeException re) {
+            throw new ConfigException(re);
+        }
     }
 
     //to be used by chained bundlers, e.g. by EXE bundler to avoid
@@ -150,7 +268,7 @@
             return true;
         }
 
-        if (StandardBundlerParam.MAIN_JAR.fetchFrom(p) == null) {
+        if (MAIN_JAR.fetchFrom(p) == null) {
             throw new ConfigException(
                     "Main application jar is missing.",
                     "Make sure to use fx:jar task to create main application jar.");
@@ -165,21 +283,21 @@
     }
 
 
-    private File getConfig_InfoPlist() {
-        return new File(configRoot, "Info.plist");
+    private File getConfig_InfoPlist(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params), "Info.plist");
     }
 
-    private File getConfig_Icon() {
-        return new File(configRoot, NAME.fetchFrom(params) + ".icns");
+    private File getConfig_Icon(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params), APP_NAME.fetchFrom(params) + ".icns");
     }
 
-    private void prepareConfigFiles() throws IOException {
-        File infoPlistFile = getConfig_InfoPlist();
+    private void prepareConfigFiles(Map<String, ? super Object> params) throws IOException {
+        File infoPlistFile = getConfig_InfoPlist(params);
         infoPlistFile.createNewFile();
-        writeInfoPlist(infoPlistFile);
+        writeInfoPlist(infoPlistFile, params);
 
         // Copy icon to Resources folder
-        prepareIcon();
+        prepareIcon(params);
     }
 
     public File doBundle(Map<String, ? super Object> p, File outputDirectory, boolean dependentTask) {
@@ -189,17 +307,15 @@
             if (predefinedImage != null) {
                 return predefinedImage;
             }
-            params = p;
 
             File file = BUILD_ROOT.fetchFrom(p);
-            setBuildRoot(file);
 
             //prepare config resources (we will copy them to the bundle later)
             // NB: explicitly saving them to simplify customization
-            prepareConfigFiles();
+            prepareConfigFiles(p);
 
             // Create directory structure
-            rootDirectory = new File(outputDirectory, NAME.fetchFrom(p) + ".app");
+            rootDirectory = new File(outputDirectory, APP_NAME.fetchFrom(p) + ".app");
             IOUtils.deleteRecursive(rootDirectory);
             rootDirectory.mkdirs();
 
@@ -227,7 +343,7 @@
             writePkgInfo(pkgInfoFile);
 
             // Copy executable to MacOS folder
-            File executableFile = new File(macOSDirectory, getLauncherName());
+            File executableFile = new File(macOSDirectory, getLauncherName(p));
             IOUtils.copyFromURL(
                     RAW_EXECUTABLE_URL.fetchFrom(p),
                     executableFile);
@@ -235,10 +351,10 @@
             executableFile.setExecutable(true, false);
 
             // Copy runtime to PlugIns folder
-            copyRuntime(plugInsDirectory);
+            copyRuntime(plugInsDirectory, p);
 
             // Copy class path entries to Java folder
-            copyClassPathEntries(javaDirectory);
+            copyClassPathEntries(javaDirectory, p);
 
 //TODO: Need to support adding native libraries.
             // Copy library path entries to MacOS folder
@@ -246,47 +362,45 @@
 
             /*********** Take care of "config" files *******/
             // Copy icon to Resources folder
-            IOUtils.copyFile(getConfig_Icon(),
-                    new File(resourcesDirectory, getConfig_Icon().getName()));
+            IOUtils.copyFile(getConfig_Icon(p),
+                    new File(resourcesDirectory, getConfig_Icon(p).getName()));
             // Generate Info.plist
-            IOUtils.copyFile(getConfig_InfoPlist(),
+            IOUtils.copyFile(getConfig_InfoPlist(p),
                     new File(contentsDirectory, "Info.plist"));
-        } catch (ConfigException e) {
-            Log.info("Bundler " + getName() + " skipped because of a configuration problem: " + e.getMessage() + "\nAdvice to fix: " + e.getAdvice());
         } catch (IOException ex) {
             Log.verbose(ex);
             return null;
         } finally {
-            if (!verbose) {
+            if (!VERBOSE.fetchFrom(p)) {
                 //cleanup
-                cleanupConfigFiles();
+                cleanupConfigFiles(p);
             } else {
                 Log.info("Config files are saved to " +
-                        configRoot.getAbsolutePath()  +
+                        CONFIG_ROOT.fetchFrom(p).getAbsolutePath()  +
                         ". Use them to customize package.");
             }
         }
         return rootDirectory;
     }
 
-    public String getAppName() {
-        return  NAME.fetchFrom(params) + ".app";
+    public String getAppName(Map<String, ? super Object> params) {
+        return APP_NAME.fetchFrom(params) + ".app";
     }
 
-    protected void cleanupConfigFiles() {
+    protected void cleanupConfigFiles(Map<String, ? super Object> params) {
         //Since building the app can be bypassed, make sure configRoot was set
-        if (configRoot != null) {
-            if (getConfig_Icon() != null) {
-                getConfig_Icon().delete();
+        if (CONFIG_ROOT.fetchFrom(params) != null) {
+            if (getConfig_Icon(params) != null) {
+                getConfig_Icon(params).delete();
             }
-            if (getConfig_InfoPlist() != null) {
-                getConfig_InfoPlist().delete();
+            if (getConfig_InfoPlist(params) != null) {
+                getConfig_InfoPlist(params).delete();
             }
         }
     }
 
 
-    private void copyClassPathEntries(File javaDirectory) throws IOException {
+    private void copyClassPathEntries(File javaDirectory, Map<String, ? super Object> params) throws IOException {
         RelativeFileSet classPath = APP_RESOURCES.fetchFrom(params);
         if (classPath == null) {
             throw new RuntimeException("Null app resources?");
@@ -298,8 +412,8 @@
         }
     }
 
-    private void copyRuntime(File plugInsDirectory) throws IOException {
-        RelativeFileSet runTime = RUNTIME.fetchFrom(params);
+    private void copyRuntime(File plugInsDirectory, Map<String, ? super Object> params) throws IOException {
+        RelativeFileSet runTime = MAC_RUNTIME.fetchFrom(params);
         if (runTime == null) {
             //request to use system runtime => do not bundle
             return;
@@ -310,49 +424,47 @@
         File destDir = new File(plugInsDirectory, srcdir.getName());
         Set<String> filesToCopy = runTime.getIncludedFiles();
 
-        // We don't need the symlink to libjli or the JRE's info.plist.
-        // We are going to load it directly.
-        filesToCopy.remove("Contents/MacOS/libjli.dylib");
-        filesToCopy.remove("Contents/Info.plist");
         for (String fname : filesToCopy) {
             IOUtils.copyFile(
                     new File(srcdir, fname), new File(destDir, fname));
         }
     }
 
-
-    // get Name from bundle params
-    private String NAME() {
-        return NAME.fetchFrom(params);
-    }
-
-    private void prepareIcon() throws IOException {
+    private void prepareIcon(Map<String, ? super Object> params) throws IOException {
         File icon = ICON.fetchFrom(params);
         if (icon == null || !icon.exists()) {
-            fetchResource(MAC_BUNDLER_PREFIX+ NAME() +".icns",
+            fetchResource(MAC_BUNDLER_PREFIX+ APP_NAME.fetchFrom(params) +".icns",
                     "icon",
-                    TEMPLATE_BUNDLE_ICON,
-                    getConfig_Icon());
+                    DEFAULT_ICNS_ICON.fetchFrom(params),
+                    getConfig_Icon(params),
+                    VERBOSE.fetchFrom(params));
         } else {
-            fetchResource(MAC_BUNDLER_PREFIX+ NAME() +".icns",
+            fetchResource(MAC_BUNDLER_PREFIX+ APP_NAME.fetchFrom(params) +".icns",
                     "icon",
                     icon,
-                    getConfig_Icon());
+                    getConfig_Icon(params),
+                    VERBOSE.fetchFrom(params));
         }
     }
 
-    private String getLauncherName() {
-        if (NAME() != null) {
-            return NAME();
+    private String getLauncherName(Map<String, ? super Object> params) {
+        if (APP_NAME.fetchFrom(params) != null) {
+            return APP_NAME.fetchFrom(params);
         } else {
             return MAIN_CLASS.fetchFrom(params);
         }
     }
 
-    private String getBundleName() {
+    private String getBundleName(Map<String, ? super Object> params) {
         //TODO: Check to see what rules/limits are in place for CFBundleName
-        if (NAME() != null) {
-            return NAME();
+        if (MAC_CF_BUNDLE_NAME.fetchFrom(params) != null) {
+            String bn = MAC_CF_BUNDLE_NAME.fetchFrom(params);
+            if (bn.length() > 16) {
+                Log.info(MessageFormat.format(I18N.getString("message.bundle-name-too-long-warning"), MAC_CF_BUNDLE_NAME.getID(), bn));
+            }
+            return MAC_CF_BUNDLE_NAME.fetchFrom(params);
+        } else if (APP_NAME.fetchFrom(params) != null) {
+            return APP_NAME.fetchFrom(params);
         } else {
             String nm = MAIN_CLASS.fetchFrom(params);
             if (nm.length() > 16) {
@@ -362,28 +474,28 @@
         }
     }
 
-    private String getBundleIdentifier() {
+    private String getBundleIdentifier(Map<String, ? super Object> params) {
         //TODO: Check to see what rules/limits are in place for CFBundleIdentifier
         return  IDENTIFIER.fetchFrom(params);
     }
 
-    private void writeInfoPlist(File file) throws IOException {
+    private void writeInfoPlist(File file, Map<String, ? super Object> params) throws IOException {
         Log.verbose("Preparing Info.plist: "+file.getAbsolutePath());
 
         //prepare config for exe
         //Note: do not need CFBundleDisplayName if we do not support localization
         Map<String, String> data = new HashMap<>();
-        data.put("DEPLOY_ICON_FILE", getConfig_Icon().getName());
+        data.put("DEPLOY_ICON_FILE", getConfig_Icon(params).getName());
         data.put("DEPLOY_BUNDLE_IDENTIFIER",
-                getBundleIdentifier().toLowerCase());
+                getBundleIdentifier(params));
         data.put("DEPLOY_BUNDLE_NAME",
-                getBundleName());
+                getBundleName(params));
         data.put("DEPLOY_BUNDLE_COPYRIGHT",
                 COPYRIGHT.fetchFrom(params) != null ? COPYRIGHT.fetchFrom(params) : "Unknown");
-        data.put("DEPLOY_LAUNCHER_NAME", getLauncherName());
-        if (RUNTIME.fetchFrom(params) != null) {
+        data.put("DEPLOY_LAUNCHER_NAME", getLauncherName(params));
+        if (MAC_RUNTIME.fetchFrom(params) != null) {
             data.put("DEPLOY_JAVA_RUNTIME_NAME",
-                    RUNTIME.fetchFrom(params).getBaseDirectory().getName());
+                    MAC_RUNTIME.fetchFrom(params).getBaseDirectory().getName());
         } else {
             data.put("DEPLOY_JAVA_RUNTIME_NAME", "");
         }
@@ -391,8 +503,7 @@
                 VERSION.fetchFrom(params) != null ? VERSION.fetchFrom(params) : "1.0.0");
         data.put("DEPLOY_BUNDLE_CATEGORY",
                 //TODO parameters should provide set of values for IDEs
-                CATEGORY.fetchFrom(params) != null ?
-                        CATEGORY.fetchFrom(params) : "unknown");
+                MAC_CATEGORY.validatedFetchFrom(params));
 
         //TODO NOT THE WAY TODO THIS but good enough for first pass
         data.put("DEPLOY_MAIN_JAR_NAME", new BundleParams(params).getMainApplicationJar());
@@ -435,8 +546,9 @@
 
         Writer w = new BufferedWriter(new FileWriter(file));
         w.write(preprocessTextResource(
-                MAC_BUNDLER_PREFIX + getConfig_InfoPlist().getName(),
-                "Bundle config file", TEMPLATE_INFO_PLIST, data));
+                MAC_BUNDLER_PREFIX + getConfig_InfoPlist(params).getName(),
+                "Bundle config file", TEMPLATE_INFO_PLIST, data,
+                VERBOSE.fetchFrom(params)));
         w.close();
 
     }
@@ -492,7 +604,7 @@
                 MAIN_JAR_CLASSPATH,
                 PREFERENCES_ID,
                 RAW_EXECUTABLE_URL,
-                RUNTIME,
+                MAC_RUNTIME,
                 USE_FX_PACKAGING,
                 USER_JVM_OPTIONS,
                 VERSION,
--- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/MacDMGBundler.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/MacDMGBundler.java	Mon Mar 17 09:51:09 2014 -0600
@@ -37,7 +37,6 @@
 
 public class MacDMGBundler extends MacBaseInstallerBundler {
 
-
     static final String DEFAULT_BACKGROUND_IMAGE="background.png";
     static final String DEFAULT_DMG_SETUP_SCRIPT="DMGsetup.scpt";
     static final String TEMPLATE_BUNDLE_ICON = "GenericApp.icns";
@@ -46,7 +45,6 @@
     // when they look for unathorized license files in the build artifacts
     // Use different name to make them happy
     static final String DEFAULT_LICENSE_PLIST="lic_template.plist";
-    private Map<String, ? super Object> params;
 
     public MacDMGBundler() {
         super();
@@ -54,29 +52,24 @@
     }
 
     //@Override
-    public File bundle(Map<String, ? super Object> p, File outdir) {
-        Log.info("Building DMG package for " + NAME.fetchFrom(p));
-
-        params = p;
+    public File bundle(Map<String, ? super Object> params, File outdir) {
+        Log.info("Building DMG package for " + APP_NAME.fetchFrom(params));
 
         File appImageDir = APP_IMAGE_BUILD_ROOT.fetchFrom(params);
         try {
             appImageDir.mkdirs();
 
-            if (prepareAppBundle(p) && prepareConfigFiles()) {
-                File configScript = getConfig_Script();
+            if (prepareAppBundle(params) != null && prepareConfigFiles(params)) {
+                File configScript = getConfig_Script(params);
                 if (configScript.exists()) {
                     Log.info("Running shell script on application image ["
                             + configScript.getAbsolutePath() + "]");
-                    IOUtils.run("bash", configScript, verbose);
+                    IOUtils.run("bash", configScript, VERBOSE.fetchFrom(params));
                 }
 
-                return buildDMG(p, outdir);
+                return buildDMG(params, outdir);
             }
             return null;
-        } catch (ConfigException e) {
-            Log.info("Bundler " + getName() + " skipped because of a configuration problem: " + e.getMessage() + "\nAdvice to fix: " + e.getAdvice());
-            return null;
         } catch (IOException ex) {
             Log.verbose(ex);
             return null;
@@ -88,15 +81,14 @@
                     Log.info("[DEBUG] Intermediate application bundle image: "+
                             appImageDir.getAbsolutePath());
                 }
-                if (!verbose) {
+                if (!VERBOSE.fetchFrom(params)) {
                     //cleanup
-                    cleanupConfigFiles();
+                    cleanupConfigFiles(params);
                 } else {
                     Log.info("  Config files are saved to "
-                            + CONFIG_ROOT.fetchFrom(p).getAbsolutePath()
+                            + CONFIG_ROOT.fetchFrom(params).getAbsolutePath()
                             + ". Use them to customize package.");
                 }
-                appImageDir = null;
             } catch (FileNotFoundException ex) {
                 //noinspection ReturnInsideFinallyBlock
                 return null;
@@ -105,23 +97,23 @@
     }
 
     //remove
-    protected void cleanupConfigFiles() {
-        if (getConfig_VolumeBackground() != null) {
-            getConfig_VolumeBackground().delete();
+    protected void cleanupConfigFiles(Map<String, ? super Object> params) {
+        if (getConfig_VolumeBackground(params) != null) {
+            getConfig_VolumeBackground(params).delete();
         }
-        if (getConfig_VolumeIcon() != null) {
-            getConfig_VolumeIcon().delete();
+        if (getConfig_VolumeIcon(params) != null) {
+            getConfig_VolumeIcon(params).delete();
         }
-        if (getConfig_VolumeScript() != null) {
-            getConfig_VolumeScript().delete();
+        if (getConfig_VolumeScript(params) != null) {
+            getConfig_VolumeScript(params).delete();
         }
-        if (getConfig_Script() != null) {
-            getConfig_Script().delete();
+        if (getConfig_Script(params) != null) {
+            getConfig_Script(params).delete();
         }
-        if (getConfig_LicenseFile() != null) {
-            getConfig_LicenseFile().delete();
+        if (getConfig_LicenseFile(params) != null) {
+            getConfig_LicenseFile(params).delete();
         }
-        APP_BUNDLER.fetchFrom(params).cleanupConfigFiles();
+        APP_BUNDLER.fetchFrom(params).cleanupConfigFiles(params);
     }
 
     @Override
@@ -140,13 +132,13 @@
     private static final String hdiutil = "/usr/bin/hdiutil";
 
     private void prepareDMGSetupScript(String volumeName, Map<String, ? super Object> p) throws IOException {
-        File dmgSetup = getConfig_VolumeScript();
+        File dmgSetup = getConfig_VolumeScript(p);
         Log.verbose("Preparing dmg setup: "+dmgSetup.getAbsolutePath());
 
         //prepare config for exe
         Map<String, String> data = new HashMap<>();
         data.put("DEPLOY_ACTUAL_VOLUME_NAME", volumeName);
-        data.put("DEPLOY_APPLICATION_NAME", NAME.fetchFrom(p));
+        data.put("DEPLOY_APPLICATION_NAME", APP_NAME.fetchFrom(p));
 
         //treat default null as "system wide install"
         boolean systemWide = SYSTEM_WIDE.fetchFrom(p) == null || SYSTEM_WIDE.fetchFrom(p);
@@ -162,27 +154,27 @@
         Writer w = new BufferedWriter(new FileWriter(dmgSetup));
         w.write(preprocessTextResource(
                 com.sun.javafx.tools.packager.bundlers.MacAppBundler.MAC_BUNDLER_PREFIX + dmgSetup.getName(),
-                "DMG setup script", DEFAULT_DMG_SETUP_SCRIPT, data));
+                "DMG setup script", DEFAULT_DMG_SETUP_SCRIPT, data, VERBOSE.fetchFrom(p)));
         w.close();
     }
 
-    private File getConfig_VolumeScript() {
-        return new File(CONFIG_ROOT.fetchFrom(params), NAME.fetchFrom(params) + "-dmg-setup.scpt");
+    private File getConfig_VolumeScript(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params), APP_NAME.fetchFrom(params) + "-dmg-setup.scpt");
     }
 
-    private File getConfig_VolumeBackground() {
-        return new File(CONFIG_ROOT.fetchFrom(params), NAME.fetchFrom(params) + "-background.png");
+    private File getConfig_VolumeBackground(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params), APP_NAME.fetchFrom(params) + "-background.png");
     }
 
-    private File getConfig_VolumeIcon() {
-        return new File(CONFIG_ROOT.fetchFrom(params), NAME.fetchFrom(params) + "-volume.icns");
+    private File getConfig_VolumeIcon(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params), APP_NAME.fetchFrom(params) + "-volume.icns");
     }
 
-    private File getConfig_LicenseFile() {
-        return new File(CONFIG_ROOT.fetchFrom(params), NAME.fetchFrom(params) + "-license.plist");
+    private File getConfig_LicenseFile(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params), APP_NAME.fetchFrom(params) + "-license.plist");
     }
 
-    private void prepareLicense() {
+    private void prepareLicense(Map<String, ? super Object> params) {
         try {
             if (LICENSE_FILES.fetchFrom(params).isEmpty()) {
                 return;
@@ -198,10 +190,10 @@
             Map<String, String> data = new HashMap<>();
             data.put("APPLICATION_LICENSE_TEXT", licenseInBase64);
 
-            Writer w = new BufferedWriter(new FileWriter(getConfig_LicenseFile()));
+            Writer w = new BufferedWriter(new FileWriter(getConfig_LicenseFile(params)));
             w.write(preprocessTextResource(
-                    com.sun.javafx.tools.packager.bundlers.MacAppBundler.MAC_BUNDLER_PREFIX + getConfig_LicenseFile().getName(),
-                    "License setup", DEFAULT_LICENSE_PLIST, data));
+                    com.sun.javafx.tools.packager.bundlers.MacAppBundler.MAC_BUNDLER_PREFIX + getConfig_LicenseFile(params).getName(),
+                    "License setup", DEFAULT_LICENSE_PLIST, data, VERBOSE.fetchFrom(params)));
             w.close();
 
         } catch (IOException ex) {
@@ -210,47 +202,51 @@
 
     }
 
-    private boolean prepareConfigFiles() throws IOException {
-        File bgTarget = getConfig_VolumeBackground();
+    private boolean prepareConfigFiles(Map<String, ? super Object> params) throws IOException {
+        File bgTarget = getConfig_VolumeBackground(params);
         fetchResource(com.sun.javafx.tools.packager.bundlers.MacAppBundler.MAC_BUNDLER_PREFIX + bgTarget.getName(),
                 "dmg background",
                 DEFAULT_BACKGROUND_IMAGE,
-                bgTarget);
+                bgTarget,
+                VERBOSE.fetchFrom(params));
 
-        File iconTarget = getConfig_VolumeIcon();
+        File iconTarget = getConfig_VolumeIcon(params);
         if (ICON.fetchFrom(params) == null || !ICON.fetchFrom(params).exists()) {
             fetchResource(com.sun.javafx.tools.packager.bundlers.MacAppBundler.MAC_BUNDLER_PREFIX + iconTarget.getName(),
                     "volume icon",
                     TEMPLATE_BUNDLE_ICON,
-                    iconTarget);
+                    iconTarget,
+                    VERBOSE.fetchFrom(params));
         } else {
             fetchResource(com.sun.javafx.tools.packager.bundlers.MacAppBundler.MAC_BUNDLER_PREFIX + iconTarget.getName(),
                     "volume icon",
                     ICON.fetchFrom(params),
-                    iconTarget);
+                    iconTarget,
+                    VERBOSE.fetchFrom(params));
         }
 
 
-        fetchResource(com.sun.javafx.tools.packager.bundlers.MacAppBundler.MAC_BUNDLER_PREFIX + getConfig_Script().getName(),
+        fetchResource(com.sun.javafx.tools.packager.bundlers.MacAppBundler.MAC_BUNDLER_PREFIX + getConfig_Script(params).getName(),
                 "script to run after application image is populated",
                 (String) null,
-                getConfig_Script());
+                getConfig_Script(params),
+                VERBOSE.fetchFrom(params));
 
-        prepareLicense();
+        prepareLicense(params);
 
         //In theory we need to extract name from results of attach command
         //However, this will be a problem for customization as name will
         //possibly change every time and developer will not be able to fix it
         //As we are using tmp dir chance we get "different" namr are low =>
         //Use fixed name we used for bundle
-        prepareDMGSetupScript(NAME.fetchFrom(params), params);
+        prepareDMGSetupScript(APP_NAME.fetchFrom(params), params);
 
         return true;
     }
 
     //name of post-image script
-    private File getConfig_Script() {
-        return new File(CONFIG_ROOT.fetchFrom(params), NAME.fetchFrom(params) + "-post-image.sh");
+    private File getConfig_Script(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params), APP_NAME.fetchFrom(params) + "-post-image.sh");
     }
 
     //Location of SetFile utility may be different depending on MacOS version
@@ -287,9 +283,12 @@
 
     private File buildDMG(
             Map<String, ? super Object> p, File outdir)
-            throws IOException, ConfigException {
-        File protoDMG = new File(IMAGES_ROOT.fetchFrom(p), NAME.fetchFrom(p) +"-tmp.dmg");
-        File finalDMG = new File(outdir,  NAME.fetchFrom(p) +".dmg");
+            throws IOException {
+        File imagesRoot = IMAGES_ROOT.fetchFrom(p);
+        if (!imagesRoot.exists()) imagesRoot.mkdirs();
+
+        File protoDMG = new File(imagesRoot, APP_NAME.fetchFrom(p) +"-tmp.dmg");
+        File finalDMG = new File(outdir,  APP_NAME.fetchFrom(p) +".dmg");
 
         File srcFolder = APP_IMAGE_BUILD_ROOT.fetchFrom(p); //new File(imageDir, p.name+".app");
         File predefinedImage = getPredefinedImage(p);
@@ -314,10 +313,10 @@
                 "create",
                 "-quiet",
                 "-srcfolder", srcFolder.getAbsolutePath(),
-                "-volname", NAME.fetchFrom(p),
+                "-volname", APP_NAME.fetchFrom(p),
                 "-ov", protoDMG.getAbsolutePath(),
                 "-format", "UDRW");
-        IOUtils.exec(pb, verbose);
+        IOUtils.exec(pb, VERBOSE.fetchFrom(p));
 
         //mount temp image
         pb = new ProcessBuilder(
@@ -325,25 +324,25 @@
                 "attach",
                 protoDMG.getAbsolutePath(),
                 "-quiet",
-                "-mountroot", IMAGES_ROOT.fetchFrom(p).getAbsolutePath());
-        IOUtils.exec(pb, verbose);
+                "-mountroot", imagesRoot.getAbsolutePath());
+        IOUtils.exec(pb, VERBOSE.fetchFrom(p));
 
-        File mountedRoot = new File(IMAGES_ROOT.fetchFrom(p).getAbsolutePath(), NAME.fetchFrom(p));
+        File mountedRoot = new File(imagesRoot.getAbsolutePath(), APP_NAME.fetchFrom(p));
 
         //background image
         File bgdir = new File(mountedRoot, ".background");
         bgdir.mkdirs();
-        IOUtils.copyFile(getConfig_VolumeBackground(),
+        IOUtils.copyFile(getConfig_VolumeBackground(p),
                 new File(bgdir, "background.png"));
 
         //volume icon
         File volumeIconFile = new File(mountedRoot, ".VolumeIcon.icns");
-        IOUtils.copyFile(getConfig_VolumeIcon(),
+        IOUtils.copyFile(getConfig_VolumeIcon(p),
                 volumeIconFile);
 
         pb = new ProcessBuilder("osascript",
-                getConfig_VolumeScript().getAbsolutePath());
-        IOUtils.exec(pb, verbose);
+                getConfig_VolumeScript(p).getAbsolutePath());
+        IOUtils.exec(pb, VERBOSE.fetchFrom(p));
 
         //Indicate that we want a custom icon
         //NB: attributes of the root directory are ignored when creating the volume
@@ -358,14 +357,14 @@
                     setFileUtility,
                     "-c", "icnC",
                     volumeIconFile.getAbsolutePath());
-            IOUtils.exec(pb, verbose);
+            IOUtils.exec(pb, VERBOSE.fetchFrom(p));
             volumeIconFile.setReadOnly();
 
             pb = new ProcessBuilder(
                     setFileUtility,
                     "-a", "C",
                     mountedRoot.getAbsolutePath());
-            IOUtils.exec(pb, verbose);
+            IOUtils.exec(pb, VERBOSE.fetchFrom(p));
         } else {
             Log.verbose("Skip enabling custom icon as SetFile utility is not found");
         }
@@ -376,7 +375,7 @@
                 "detach",
                 "-quiet",
                 mountedRoot.getAbsolutePath());
-        IOUtils.exec(pb, verbose);
+        IOUtils.exec(pb, VERBOSE.fetchFrom(p));
 
         // Compress it to a new image
         pb = new ProcessBuilder(
@@ -386,17 +385,17 @@
                 "-quiet",
                 "-format", "UDZO",
                 "-o", finalDMG.getAbsolutePath());
-        IOUtils.exec(pb, verbose);
+        IOUtils.exec(pb, VERBOSE.fetchFrom(p));
 
         //add license if needed
-        if (getConfig_LicenseFile().exists()) {
+        if (getConfig_LicenseFile(p).exists()) {
             //hdiutil unflatten your_image_file.dmg
             pb = new ProcessBuilder(
                     hdiutil,
                     "unflatten",
                     finalDMG.getAbsolutePath()
             );
-            IOUtils.exec(pb, verbose);
+            IOUtils.exec(pb, VERBOSE.fetchFrom(p));
 
             //add license
             pb = new ProcessBuilder(
@@ -404,9 +403,9 @@
                     "udifrez",
                     finalDMG.getAbsolutePath(),
                     "-xml",
-                    getConfig_LicenseFile().getAbsolutePath()
+                    getConfig_LicenseFile(p).getAbsolutePath()
             );
-            IOUtils.exec(pb, verbose);
+            IOUtils.exec(pb, VERBOSE.fetchFrom(p));
 
             //hdiutil flatten your_image_file.dmg
             pb = new ProcessBuilder(
@@ -414,14 +413,14 @@
                     "flatten",
                     finalDMG.getAbsolutePath()
             );
-            IOUtils.exec(pb, verbose);
+            IOUtils.exec(pb, VERBOSE.fetchFrom(p));
 
         }
 
         //Delete the temporary image
         protoDMG.delete();
 
-        Log.info("Result DMG installer for " + NAME.fetchFrom(p) +": "
+        Log.info("Result DMG installer for " + APP_NAME.fetchFrom(p) +": "
                 + finalDMG.getAbsolutePath());
 
         return finalDMG;
@@ -455,15 +454,26 @@
 
     @Override
     public boolean validate(Map<String, ? super Object> params) throws UnsupportedPlatformException, ConfigException {
-        if (params == null) throw new ConfigException("Parameters map is null.", "Pass in a non-null parameters map.");
+        try {
+            if (params == null) throw new ConfigException("Parameters map is null.", "Pass in a non-null parameters map.");
 
-        // hdiutil is always available so there's no need to test for availability.
-        //run basic validation to ensure requirements are met
+            // hdiutil is always available so there's no need to test for availability.
+            //run basic validation to ensure requirements are met
 
-        //run basic validation to ensure requirements are met
-        //we are not interested in return code, only possible exception
-        APP_BUNDLER.fetchFrom(params).doValidate(params);
-        return true;
+            //run basic validation to ensure requirements are met
+            //we are not interested in return code, only possible exception
+            validateAppImageAndBundeler(params);
+
+            if (SERVICE_HINT.fetchFrom(params)) {
+                throw new ConfigException(
+                        "DMG bundler doesn't support services.",
+                        "Make sure that the service hint is set to false.");
+            }
+
+            return true;
+        } catch (RuntimeException re) {
+            throw new ConfigException(re);
+        }
     }
 
     @Override
--- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/WinAppBundler.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/WinAppBundler.java	Mon Mar 17 09:51:09 2014 -0600
@@ -41,6 +41,7 @@
 import java.text.MessageFormat;
 import java.util.*;
 
+import static com.oracle.bundlers.JreUtils.*;
 import static com.oracle.bundlers.StandardBundlerParam.*;
 import static com.oracle.bundlers.windows.WindowsBundlerParam.BIT_ARCH_64;
 import static com.oracle.bundlers.windows.WindowsBundlerParam.BIT_ARCH_64_RUNTIME;
@@ -53,22 +54,74 @@
     public static final BundlerParamInfo<File> CONFIG_ROOT = new WindowsBundlerParam<>(
             I18N.getString("param.config-root.name"),
             I18N.getString("param.config-root.description"), 
-            "configRoot", //KEY 
+            "configRoot",
             File.class, null, params -> {
-                File imagesRoot = new File(StandardBundlerParam.BUILD_ROOT.fetchFrom(params), "windows");
+                File imagesRoot = new File(BUILD_ROOT.fetchFrom(params), "windows");
                 imagesRoot.mkdirs();
                 return imagesRoot;
-            }, false, s -> null);
+            }, false, (s, p) -> null);
+
+    //Subsetting of JRE is restricted.
+    //JRE README defines what is allowed to strip:
+    //   http://www.oracle.com/technetwork/java/javase/jre-7-readme-430162.html //TODO update when 8 goes GA
+    public static final BundlerParamInfo<Rule[]> WIN_JRE_RULES = new StandardBundlerParam<>(
+            "",
+            "",
+            ".win.runtime.rules",
+            Rule[].class,
+            null,
+            params -> new Rule[]{
+                Rule.prefixNeg("\\bin\\new_plugin"),
+                Rule.prefixNeg("\\lib\\deploy"),
+                Rule.suffixNeg(".pdb"),
+                Rule.suffixNeg(".map"),
+                Rule.suffixNeg("axbridge.dll"),
+                Rule.suffixNeg("eula.dll"),
+                Rule.substrNeg("javacpl"),
+                Rule.suffixNeg("wsdetect.dll"),
+                Rule.substrNeg("eployjava1.dll"), //NP and IE versions
+                Rule.substrNeg("bin\\jp2"),
+                Rule.substrNeg("bin\\jpi"),
+                //Rule.suffixNeg("lib\\ext"), //need some of jars there for https to work
+                Rule.suffixNeg("ssv.dll"),
+                Rule.substrNeg("npjpi"),
+                Rule.substrNeg("npoji"),
+                Rule.suffixNeg(".exe"),
+                //keep core deploy files as JavaFX APIs use them
+                //Rule.suffixNeg("deploy.dll"),
+                Rule.suffixNeg("deploy.jar"),
+                //Rule.suffixNeg("javaws.jar"),
+                //Rule.suffixNeg("plugin.jar"),
+                Rule.suffix(".jar")
+            },
+            false,
+            (s, p) -> null
+    );
+
+    public static final BundlerParamInfo<RelativeFileSet> WIN_RUNTIME = new StandardBundlerParam<>(
+            RUNTIME.getName(),
+            RUNTIME.getDescription(),
+            RUNTIME.getID(),
+            RelativeFileSet.class,
+            null,
+            params -> extractJreAsRelativeFileSet(System.getProperty("java.home"),
+                    WIN_JRE_RULES.fetchFrom(params)),
+            false,
+            (s, p) -> extractJreAsRelativeFileSet(s,
+                    WIN_JRE_RULES.fetchFrom(p))
+    );
 
     private final static String EXECUTABLE_NAME = "WinLauncher.exe";
+    private final static String EXECUTABLE_SVC_NAME = "WinLauncherSvc.exe";
+
     private static final String TOOL_ICON_SWAP="IconSwap.exe";
 
     public static final BundlerParamInfo<URL> RAW_EXECUTABLE_URL = new WindowsBundlerParam<>(
             I18N.getString("param.raw-executable-url.name"),
             I18N.getString("param.raw-executable-url.description"),
-            "win.launcher.url", //KEY
+            "win.launcher.url",
             URL.class, null, params -> WinResources.class.getResource(EXECUTABLE_NAME), 
-            false, s -> {
+            false, (s, p) -> {
                 try {
                     return new URL(s);
                 } catch (MalformedURLException e) {
@@ -80,9 +133,9 @@
     public static final BundlerParamInfo<Boolean> REBRAND_EXECUTABLE = new WindowsBundlerParam<>(
             I18N.getString("param.rebrand-executable.name"),
             I18N.getString("param.rebrand-executable.description"),
-            "win.launcher.rebrand", //KEY
+            "win.launcher.rebrand",
             Boolean.class, null, params -> Boolean.TRUE, 
-            false, Boolean::valueOf);
+            false, (s, p) -> Boolean.valueOf(s));
 
     public WinAppBundler() {
         super();
@@ -98,11 +151,15 @@
 
     @Override
     public boolean validate(Map<String, ? super Object> params) throws UnsupportedPlatformException, ConfigException {
-        if (params == null) throw new ConfigException(
-                I18N.getString("error.parameters-null"),
-                I18N.getString("error.parameters-null.advice"));
+        try {
+            if (params == null) throw new ConfigException(
+                    I18N.getString("error.parameters-null"),
+                    I18N.getString("error.parameters-null.advice"));
 
-        return doValidate(params);
+            return doValidate(params);
+        } catch (RuntimeException re) {
+            throw new ConfigException(re);
+        }
     }
 
     //to be used by chained bundlers, e.g. by EXE bundler to avoid
@@ -118,7 +175,13 @@
                     I18N.getString("error.no-windows-resources.advice"));
         }
 
-        if (StandardBundlerParam.MAIN_JAR.fetchFrom(p) == null) {
+        if (SERVICE_HINT.fetchFrom(p) && WinResources.class.getResource(EXECUTABLE_SVC_NAME) == null) {
+            throw new ConfigException(
+                    I18N.getString("error.no-windows-resources"),
+                    I18N.getString("error.no-windows-resources.advice"));
+        }
+
+        if (MAIN_JAR.fetchFrom(p) == null) {
             throw new ConfigException(
                     I18N.getString("error.no-application-jar"),
                     I18N.getString("error.no-application-jar.advice"));
@@ -149,7 +212,11 @@
     }
 
     static String getAppName(Map<String, ? super Object>  p) {
-        return StandardBundlerParam.APP_NAME.fetchFrom(p);
+        return APP_NAME.fetchFrom(p);
+    }
+
+    static String getAppSvcName(Map<String, ? super Object>  p) {
+        return APP_NAME.fetchFrom(p) + "Svc";
     }
 
     //it is static for the sake of sharing with "Exe" bundles
@@ -162,6 +229,10 @@
         return new File(getRootDir(outDir, p), getAppName(p)+".exe");
     }
 
+    public static File getLauncherSvc(File outDir, Map<String, ? super Object> p) {
+        return new File(getRootDir(outDir, p), getAppName(p)+"Svc.exe");
+    }
+
     private File getConfig_AppIcon(Map<String, ? super Object> params) {
         return new File(getConfigRoot(params), getAppName(params) + ".ico");
     }
@@ -183,12 +254,14 @@
             fetchResource(WIN_BUNDLER_PREFIX + iconTarget.getName(),
                     I18N.getString("resource.application-icon"),
                     icon,
-                    iconTarget);
+                    iconTarget,
+                    VERBOSE.fetchFrom(params));
         } else {
             fetchResource(WIN_BUNDLER_PREFIX + iconTarget.getName(),
                     I18N.getString("resource.application-icon"),
                     WinAppBundler.TEMPLATE_APP_ICON,
-                    iconTarget);
+                    iconTarget,
+                    VERBOSE.fetchFrom(params));
         }
     }
 
@@ -227,6 +300,15 @@
                     executableFile);
             executableFile.setExecutable(true, false);
 
+            // Copy executable to install application as service
+            if (SERVICE_HINT.fetchFrom(p)) {
+                File executableSvcFile = getLauncherSvc(outputDirectory, p);
+                IOUtils.copyFromURL(
+                        WinResources.class.getResource(EXECUTABLE_SVC_NAME),
+                        executableSvcFile);
+                executableSvcFile.setExecutable(true, false);
+            }
+
             //Update branding of exe file
             if (REBRAND_EXECUTABLE.fetchFrom(p) && getConfig_AppIcon(p).exists()) {
                 //extract helper tool
@@ -244,7 +326,7 @@
                         iconSwapTool.getAbsolutePath(),
                         getConfig_AppIcon(p).getAbsolutePath(),
                         executableFile.getAbsolutePath());
-                IOUtils.exec(pb, verbose);
+                IOUtils.exec(pb, VERBOSE.fetchFrom(p));
                 executableFile.setReadOnly();
                 iconSwapTool.delete();
             }
@@ -266,7 +348,7 @@
             ex.printStackTrace();
             return null;
         } finally {
-            if (verbose) {
+            if (VERBOSE.fetchFrom(p)) {
                 Log.info(MessageFormat.format(I18N.getString("message.config-save-location"), getConfigRoot(p).getAbsolutePath()));
             } else {
                 cleanupConfigFiles(p);
@@ -341,7 +423,7 @@
     }
 
     private void copyRuntime(Map<String, ? super Object> params, File runtimeDirectory) throws IOException {
-        RelativeFileSet runtime = RUNTIME.fetchFrom(params);
+        RelativeFileSet runtime = WIN_RUNTIME.fetchFrom(params);
         if (runtime == null) {
             //its ok, request to use system JRE
             return;
@@ -369,7 +451,7 @@
 
     @Override
     public String getID() {
-        return "windows.app"; //KEY
+        return "windows.app";
     }
 
     @Override
@@ -397,7 +479,7 @@
                 MAIN_JAR_CLASSPATH,
                 PREFERENCES_ID,
                 RAW_EXECUTABLE_URL,
-                RUNTIME,
+                WIN_RUNTIME,
                 USE_FX_PACKAGING,
                 USER_JVM_OPTIONS,
                 VERSION
--- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/WinExeBundler.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/WinExeBundler.java	Mon Mar 17 09:51:09 2014 -0600
@@ -38,6 +38,7 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import static com.oracle.bundlers.StandardBundlerParam.VERBOSE;
 import static com.oracle.bundlers.windows.WindowsBundlerParam.*;
 
 public class WinExeBundler extends AbstractBundler {
@@ -48,18 +49,18 @@
     public static final BundlerParamInfo<WinAppBundler> APP_BUNDLER = new WindowsBundlerParam<>(
             I18N.getString("param.app-bundler.name"),
             I18N.getString("param.app-bundler.description"),
-            "winAppBundler", //KEY
+            "win.app.bundler",
             WinAppBundler.class, null, params -> new WinAppBundler(), false, null);
 
     public static final BundlerParamInfo<File> CONFIG_ROOT = new WindowsBundlerParam<>(
             I18N.getString("param.config-root.name"),
             I18N.getString("param.config-root.description"),
-            "configRoot", //KEY
+            "configRoot",
             File.class, null,params -> {
-                File imagesRoot = new File(StandardBundlerParam.BUILD_ROOT.fetchFrom(params), "windows");
+                File imagesRoot = new File(BUILD_ROOT.fetchFrom(params), "windows");
                 imagesRoot.mkdirs();
                 return imagesRoot;
-            }, false, s -> null);
+            }, false, (s, p) -> null);
 
     //default for .exe is user level installation
     // only do system wide if explicitly requested
@@ -67,28 +68,23 @@
             new StandardBundlerParam<>(
                     I18N.getString("param.system-wide.name"),
                     I18N.getString("param.system-wide.description"),
-                    "winexe" + BundleParams.PARAM_SYSTEM_WIDE, //KEY
+                    "win.exe." + BundleParams.PARAM_SYSTEM_WIDE,
                     Boolean.class,
                     new String[] {BundleParams.PARAM_SYSTEM_WIDE},
                     params -> false, // EXEs default to user local install
                     false,
-                    s -> (s == null || "null".equalsIgnoreCase(s))? null : Boolean.valueOf(s) // valueOf(null) is false, and we actually do want null
+                    (s, p) -> (s == null || "null".equalsIgnoreCase(s))? null : Boolean.valueOf(s) // valueOf(null) is false, and we actually do want null
             );
 
-    public static final BundlerParamInfo<File> IMAGE_DIR = new WindowsBundlerParam<>(
+    public static final BundlerParamInfo<File> EXE_IMAGE_DIR = new WindowsBundlerParam<>(
             I18N.getString("param.image-dir.name"),
             I18N.getString("param.image-dir.description"),
-            "imageDir", //KEY
+            "win.exe.imageDir",
             File.class, null, params -> {
                 File imagesRoot = IMAGES_ROOT.fetchFrom(params);
-                return new File(imagesRoot, "win-app.image");
-            }, false, s -> null);
-
-    public static final BundlerParamInfo<File> OUT_DIR = new WindowsBundlerParam<>(
-            I18N.getString("param.out-dir.name"),
-            I18N.getString("param.out-dir.description"),
-            "outDir", //KEY
-            File.class, null, params -> null, false, s -> null);
+                if (!imagesRoot.exists()) imagesRoot.mkdirs();
+                return new File(imagesRoot, "win-exe.image");
+            }, false, (s, p) -> null);
 
     private final static String DEFAULT_EXE_PROJECT_TEMPLATE = "template.iss";
     private static final String TOOL_INNO_SETUP_COMPILER = "iscc.exe";
@@ -96,7 +92,7 @@
     public static final BundlerParamInfo<String> TOOL_INNO_SETUP_COMPILER_EXECUTABLE = new WindowsBundlerParam<>(
             I18N.getString("param.iscc-path.name"),
             I18N.getString("param.iscc-path.description"),
-            "win.iscc.exe", //KEY
+            "win.exe.iscc.exe",
             String.class, null, params -> {
                 for (String dirString : (System.getenv("PATH") + ";C:\\Program Files (x86)\\Inno Setup 5;C:\\Program Files\\Inno Setup 5").split(";")) {
                     File f = new File(dirString.replace("\"", ""), TOOL_INNO_SETUP_COMPILER);
@@ -124,7 +120,7 @@
 
     @Override
     public String getID() {
-        return "exe"; //KEY
+        return "exe";
     }
 
     @Override
@@ -146,10 +142,11 @@
                 APP_RESOURCES,
                 BUILD_ROOT,
                 //CONFIG_ROOT, // duplicate from getAppBundleParameters
+                DESCRIPTION,
                 COPYRIGHT,
                 EXE_SYSTEM_WIDE,
                 IDENTIFIER,
-                IMAGE_DIR,
+                EXE_IMAGE_DIR,
                 IMAGES_ROOT,
                 LICENSE_FILES,
                 MENU_GROUP,
@@ -209,29 +206,33 @@
 
     @Override
     public boolean validate(Map<String, ? super Object> p) throws UnsupportedPlatformException, ConfigException {
-        if (p == null) throw new ConfigException(I18N.getString("error.parameters-null"), I18N.getString("error.parameters-null.advice"));
+        try {
+            if (p == null) throw new ConfigException(I18N.getString("error.parameters-null"), I18N.getString("error.parameters-null.advice"));
 
-        //run basic validation to ensure requirements are met
-        //we are not interested in return code, only possible exception
-        APP_BUNDLER.fetchFrom(p).validate(p);
+            //run basic validation to ensure requirements are met
+            //we are not interested in return code, only possible exception
+            APP_BUNDLER.fetchFrom(p).validate(p);
 
-        double innoVersion = findToolVersion(TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p));
+            double innoVersion = findToolVersion(TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p));
 
-        //Inno Setup 5+ is required
-        double minVersion = 5.0f;
+            //Inno Setup 5+ is required
+            double minVersion = 5.0f;
 
-        if (innoVersion < minVersion) {
-            Log.info(MessageFormat.format(I18N.getString("message.tool-wrong-version"), TOOL_INNO_SETUP_COMPILER, innoVersion, minVersion));
-            throw new ConfigException(
-                    I18N.getString("error.iscc-not-found"),
-                    I18N.getString("error.iscc-not-found.advice"));
+            if (innoVersion < minVersion) {
+                Log.info(MessageFormat.format(I18N.getString("message.tool-wrong-version"), TOOL_INNO_SETUP_COMPILER, innoVersion, minVersion));
+                throw new ConfigException(
+                        I18N.getString("error.iscc-not-found"),
+                        I18N.getString("error.iscc-not-found.advice"));
+            }
+
+            return true;
+        } catch (RuntimeException re) {
+            throw new ConfigException(re);
         }
-
-        return true;
     }
 
     private boolean prepareProto(Map<String, ? super Object> params) throws IOException {
-        File imageDir = IMAGE_DIR.fetchFrom(params);
+        File imageDir = EXE_IMAGE_DIR.fetchFrom(params);
         File appOutputDir = APP_BUNDLER.fetchFrom(params).doBundle(params, imageDir, true);
         if (appOutputDir == null) {
             return false;
@@ -249,9 +250,16 @@
     }
 
     public File bundle(Map<String, ? super Object> p, File outputDirectory) {
-        File imageDir = IMAGE_DIR.fetchFrom(p);
+        // validate we have valid tools before continuing
+        String iscc = TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p);
+        if (iscc == null || !new File(iscc).isFile()) {
+            Log.info(I18N.getString("error.iscc-not-found"));
+            Log.info(MessageFormat.format(I18N.getString("message.iscc-file-string"), iscc));
+            return null;
+        }
+
+        File imageDir = EXE_IMAGE_DIR.fetchFrom(p);
         try {
-
             imageDir.mkdirs();
 
             boolean menuShortcut = MENU_HINT.fetchFrom(p);
@@ -266,7 +274,7 @@
                 File configScript = getConfig_Script(p);
                 if (configScript.exists()) {
                     Log.info(MessageFormat.format(I18N.getString("message.running-wsh-script"), configScript.getAbsolutePath()));
-                    IOUtils.run("wscript", configScript, verbose);
+                    IOUtils.run("wscript", configScript, VERBOSE.fetchFrom(p));
                 }
                 return buildEXE(p, outputDirectory);
             }
@@ -276,7 +284,7 @@
             return null;
         } finally {
             try {
-                if (verbose) {
+                if (VERBOSE.fetchFrom(p)) {
                     saveConfigFiles(p);
                 }
                 if (imageDir != null && !Log.isDebug()) {
@@ -293,13 +301,12 @@
 
     //name of post-image script
     private File getConfig_Script(Map<String, ? super Object> params) {
-        return new File(IMAGE_DIR.fetchFrom(params), WinAppBundler.getAppName(params) + "-post-image.wsf");
+        return new File(EXE_IMAGE_DIR.fetchFrom(params), WinAppBundler.getAppName(params) + "-post-image.wsf");
     }
 
     protected void saveConfigFiles(Map<String, ? super Object> params) {
         try {
             File configRoot = CONFIG_ROOT.fetchFrom(params);
-            File imagesRoot = IMAGES_ROOT.fetchFrom(params);
             if (getConfig_ExeProjectFile(params).exists()) {
                 IOUtils.copyFile(getConfig_ExeProjectFile(params),
                         new File(configRoot, getConfig_ExeProjectFile(params).getName()));
@@ -352,7 +359,7 @@
         data.put("APPLICATION_VENDOR", VENDOR.fetchFrom(params));
         data.put("APPLICATION_VERSION", VERSION.fetchFrom(params)); // TODO make our own version paraminfo?
         data.put("APPLICATION_LAUNCHER_FILENAME",
-                WinAppBundler.getLauncher(IMAGE_DIR.fetchFrom(params), params).getName());
+                WinAppBundler.getLauncher(EXE_IMAGE_DIR.fetchFrom(params), params).getName());
         data.put("APPLICATION_DESKTOP_SHORTCUT", SHORTCUT_HINT.fetchFrom(params) ? "returnTrue" : "returnFalse");
         data.put("APPLICATION_MENU_SHORTCUT", MENU_HINT.fetchFrom(params) ? "returnTrue" : "returnFalse");
         data.put("APPLICATION_GROUP", MENU_GROUP.fetchFrom(params));
@@ -375,12 +382,23 @@
             data.put("ARCHITECTURE_BIT_MODE", "");
         }
 
-
+        if (SERVICE_HINT.fetchFrom(params)) {
+            data.put("RUN_FILENAME", WinAppBundler.getAppSvcName(params));
+        } else {
+            data.put("RUN_FILENAME", WinAppBundler.getAppName(params));
+        }
+        data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params));
+        data.put("APPLICATION_SERVICE", SERVICE_HINT.fetchFrom(params) ? "returnTrue" : "returnFalse");
+        data.put("APPLICATION_NOT_SERVICE", SERVICE_HINT.fetchFrom(params) ? "returnFalse" : "returnTrue");
+        data.put("START_ON_INSTALL", START_ON_INSTALL.fetchFrom(params) ? "-startOnInstall" : "");
+        data.put("STOP_ON_UNINSTALL", STOP_ON_UNINSTALL.fetchFrom(params) ? "-stopOnUninstall" : "");
+        data.put("RUN_AT_STARTUP", RUN_AT_STARTUP.fetchFrom(params) ? "-runAtStartup" : "");        
 
         Writer w = new BufferedWriter(new FileWriter(getConfig_ExeProjectFile(params)));
         String content = preprocessTextResource(
                 WinAppBundler.WIN_BUNDLER_PREFIX + getConfig_ExeProjectFile(params).getName(),
-                I18N.getString("resource.inno-setup-project-file"), DEFAULT_EXE_PROJECT_TEMPLATE, data);
+                I18N.getString("resource.inno-setup-project-file"), DEFAULT_EXE_PROJECT_TEMPLATE, data,
+                VERBOSE.fetchFrom(params));
         w.write(content);
         w.close();
         return true;
@@ -396,22 +414,24 @@
         fetchResource(WinAppBundler.WIN_BUNDLER_PREFIX + iconTarget.getName(),
                 I18N.getString("resource.setup-icon"),
                 DEFAULT_INNO_SETUP_ICON,
-                iconTarget);
+                iconTarget,
+                VERBOSE.fetchFrom(params));
 
         fetchResource(WinAppBundler.WIN_BUNDLER_PREFIX + getConfig_Script(params).getName(),
                 I18N.getString("resource.post-install-script"),
                 (String) null,
-                getConfig_Script(params));
+                getConfig_Script(params),
+                VERBOSE.fetchFrom(params));
         return true;
     }
 
     private File getConfig_SmallInnoSetupIcon(Map<String, ? super Object> params) {
-        return new File(IMAGE_DIR.fetchFrom(params),
+        return new File(EXE_IMAGE_DIR.fetchFrom(params),
                 WinAppBundler.getAppName(params) + "-setup-icon.bmp");
     }
 
     private File getConfig_ExeProjectFile(Map<String, ? super Object> params) {
-        return new File(IMAGE_DIR.fetchFrom(params),
+        return new File(EXE_IMAGE_DIR.fetchFrom(params),
                 WinAppBundler.getAppName(params) + ".iss");
     }
 
@@ -426,12 +446,25 @@
                 TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(params),
                 "/o"+outdir.getAbsolutePath(),
                 getConfig_ExeProjectFile(params).getAbsolutePath());
-        pb = pb.directory(IMAGE_DIR.fetchFrom(params));
-        IOUtils.exec(pb, verbose);
+        pb = pb.directory(EXE_IMAGE_DIR.fetchFrom(params));
+        IOUtils.exec(pb, VERBOSE.fetchFrom(params));
 
         Log.info(MessageFormat.format(I18N.getString("message.output-location"), outdir.getAbsolutePath()));
 
-        return outdir;
+        // presume the result is the ".exe" file with the newest modified time
+        // not the best solution, but it is the most reliable
+        File result = null;
+        long lastModified = 0;
+        File[] list = outdir.listFiles();
+        if (list != null) {
+            for (File f : list) {
+                if (f.getName().endsWith(".exe") && f.lastModified() > lastModified) {
+                    result = f;
+                    lastModified = f.lastModified();
+                }
+            }
+        }
+
+        return result;
     }
 }
- 
--- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/WinMsiBundler.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/WinMsiBundler.java	Mon Mar 17 09:51:09 2014 -0600
@@ -38,6 +38,7 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import static com.oracle.bundlers.StandardBundlerParam.DESCRIPTION;
 import static com.oracle.bundlers.windows.WindowsBundlerParam.*;
 
 public class WinMsiBundler  extends AbstractBundler {
@@ -48,65 +49,60 @@
     public static final BundlerParamInfo<WinAppBundler> APP_BUNDLER = new WindowsBundlerParam<>(
             I18N.getString("param.app-bundler.name"),
             I18N.getString("param.app-bundler.description"),
-            "winAppBundler", //KEY
+            "win.app.bundler",
             WinAppBundler.class, null, params -> new WinAppBundler(), false, null);
 
     public static final BundlerParamInfo<Boolean> CAN_USE_WIX36 = new WindowsBundlerParam<>(
             I18N.getString("param.can-use-wix36.name"),
             I18N.getString("param.can-use-wix36.description"),
-            "canUseWix36", //KEY
-            Boolean.class, null, params -> false, false, Boolean::valueOf);
-
-    public static final BundlerParamInfo<File> OUT_DIR = new WindowsBundlerParam<>(
-            I18N.getString("param.out-dir.name"),
-            I18N.getString("param.out-dir.description"),
-            "outDir", //KEY
-            File.class, null, params -> null, false, s -> null);
+            "win.msi.canUseWix36",
+            Boolean.class, null, params -> false, false, (s, p) -> Boolean.valueOf(s));
 
     public static final BundlerParamInfo<File> CONFIG_ROOT = new WindowsBundlerParam<>(
             I18N.getString("param.config-root.name"),
             I18N.getString("param.config-root.description"),
-            "configRoot", //KEY
+            "configRoot",
             File.class, null,params -> {
-                File imagesRoot = new File(StandardBundlerParam.BUILD_ROOT.fetchFrom(params), "windows");
+                File imagesRoot = new File(BUILD_ROOT.fetchFrom(params), "windows");
                 imagesRoot.mkdirs();
                 return imagesRoot;
-            }, false, s -> null);
+            }, false, (s, p) -> null);
 
-    public static final BundlerParamInfo<File> IMAGE_DIR = new WindowsBundlerParam<>(
+    public static final BundlerParamInfo<File> MSI_IMAGE_DIR = new WindowsBundlerParam<>(
             I18N.getString("param.image-dir.name"),
             I18N.getString("param.image-dir.description"),
-            "imageDir", //KEY
+            "win.msi.imageDir",
             File.class, null, params -> {
                 File imagesRoot = IMAGES_ROOT.fetchFrom(params);
-                return new File(imagesRoot, "win-msi");
-            }, false, s -> null);
+                if (!imagesRoot.exists()) imagesRoot.mkdirs();
+                return new File(imagesRoot, "win-msi.image");
+            }, false, (s, p) -> null);
 
-    public static final BundlerParamInfo<File> APP_DIR = new WindowsBundlerParam<>(
+    public static final BundlerParamInfo<File> WIN_APP_IMAGE = new WindowsBundlerParam<>(
             I18N.getString("param.app-dir.name"),
             I18N.getString("param.app-dir.description"),
-            "appDir",
-            File.class, null, null, false, s -> null);
+            "win.app.image",
+            File.class, null, null, false, (s, p) -> null);
 
     public static final StandardBundlerParam<Boolean> MSI_SYSTEM_WIDE  =
             new StandardBundlerParam<>(
                     I18N.getString("param.system-wide.name"),
                     I18N.getString("param.system-wide.description"),
-                    "winmsi" + BundleParams.PARAM_SYSTEM_WIDE, //KEY
+                    "win.msi." + BundleParams.PARAM_SYSTEM_WIDE,
                     Boolean.class,
                     new String[] {BundleParams.PARAM_SYSTEM_WIDE},
                     params -> true, // MSIs default to system wide
                     false,
-                    s -> (s == null || "null".equalsIgnoreCase(s))? null : Boolean.valueOf(s) // valueOf(null) is false, and we actually do want null
+                    (s, p) -> (s == null || "null".equalsIgnoreCase(s))? null : Boolean.valueOf(s) // valueOf(null) is false, and we actually do want null
             );
 
 
     public static final BundlerParamInfo<UUID> UPGRADE_UUID = new WindowsBundlerParam<>(
             I18N.getString("param.upgrade-uuid.name"),
             I18N.getString("param.upgrade-uuid.description"),
-            "upgradeUUID", //KEY
+            "win.msi.upgradeUUID",
             UUID.class, null, params -> UUID.randomUUID(), // TODO check to see if identifier is a valid UUID during default 
-            false, UUID::fromString);
+            false, (s, p) -> UUID.fromString(s));
 
     private static final String TOOL_CANDLE = "candle.exe";
     private static final String TOOL_LIGHT = "light.exe";
@@ -116,7 +112,7 @@
     public static final BundlerParamInfo<String> TOOL_CANDLE_EXECUTABLE = new WindowsBundlerParam<>(
             I18N.getString("param.candle-path.name"),
             I18N.getString("param.candle-path.description"),
-            "win.candle.exe", //KEY
+            "win.msi.candle.exe",
             String.class, null, params -> {
                 for (String dirString : (System.getenv("PATH") + AUTODETECT_DIRS).split(";")) {
                     File f = new File(dirString.replace("\"", ""), TOOL_CANDLE);
@@ -130,7 +126,7 @@
     public static final BundlerParamInfo<String> TOOL_LIGHT_EXECUTABLE = new WindowsBundlerParam<>(
             I18N.getString("param.light-path.name"),
             I18N.getString("param.light-path.descrption"),
-            "win.light.exe", //KEY
+            "win.msi.light.exe",
             String.class, null, params -> {
                 for (String dirString : (System.getenv("PATH") + AUTODETECT_DIRS).split(";")) {
                     File f = new File(dirString.replace("\"", ""), TOOL_LIGHT);
@@ -159,7 +155,7 @@
 
     @Override
     public String getID() {
-        return "msi"; //KEY
+        return "msi";
     }
 
     @Override
@@ -178,12 +174,12 @@
     public static Collection<BundlerParamInfo<?>> getMsiBundleParameters() {
         return Arrays.asList(
                 APP_BUNDLER,
-                APP_DIR,
+                WIN_APP_IMAGE,
                 BUILD_ROOT,
                 CAN_USE_WIX36,
                 //CONFIG_ROOT, // duplicate from getAppBundleParameters
                 DESCRIPTION,
-                IMAGE_DIR,
+                MSI_IMAGE_DIR,
                 IMAGES_ROOT,
                 MENU_GROUP,
                 MENU_HINT,
@@ -243,51 +239,55 @@
 
     @Override
     public boolean validate(Map<String, ? super Object> p) throws UnsupportedPlatformException, ConfigException {
-        if (p == null) throw new ConfigException(
-                I18N.getString("error.parameters-null"), 
-                I18N.getString("error.parameters-null.advice"));
+        try {
+            if (p == null) throw new ConfigException(
+                    I18N.getString("error.parameters-null"),
+                    I18N.getString("error.parameters-null.advice"));
 
-        //run basic validation to ensure requirements are met
-        //we are not interested in return code, only possible exception
-        APP_BUNDLER.fetchFrom(p).doValidate(p);
+            //run basic validation to ensure requirements are met
+            //we are not interested in return code, only possible exception
+            APP_BUNDLER.fetchFrom(p).doValidate(p);
 
-        double candleVersion = findToolVersion(TOOL_CANDLE_EXECUTABLE.fetchFrom(p));
-        double lightVersion = findToolVersion(TOOL_LIGHT_EXECUTABLE.fetchFrom(p));
+            double candleVersion = findToolVersion(TOOL_CANDLE_EXECUTABLE.fetchFrom(p));
+            double lightVersion = findToolVersion(TOOL_LIGHT_EXECUTABLE.fetchFrom(p));
 
-        //WiX 3.0+ is required
-        double minVersion = 3.0f;
-        boolean bad = false;
+            //WiX 3.0+ is required
+            double minVersion = 3.0f;
+            boolean bad = false;
 
-        if (candleVersion < minVersion) {
-            Log.verbose(MessageFormat.format(I18N.getString("message.wrong-tool-version"), TOOL_CANDLE, candleVersion, minVersion));
-            bad = true;
+            if (candleVersion < minVersion) {
+                Log.verbose(MessageFormat.format(I18N.getString("message.wrong-tool-version"), TOOL_CANDLE, candleVersion, minVersion));
+                bad = true;
+            }
+            if (lightVersion < minVersion) {
+                Log.verbose(MessageFormat.format(I18N.getString("message.wrong-tool-version"), TOOL_LIGHT, lightVersion, minVersion));
+                bad = true;
+            }
+
+            if (bad){
+                throw new ConfigException(
+                        I18N.getString("error.no-wix-tools"),
+                        I18N.getString("error.no-wix-tools.advice"));
+            }
+
+            if (lightVersion >= 3.6f) {
+                Log.verbose(I18N.getString("message.use-wix36-features"));
+                p.put(CAN_USE_WIX36.getID(), Boolean.TRUE);
+            }
+
+            /********* validate bundle parameters *************/
+
+            String version = VERSION.fetchFrom(p);
+            if (!isVersionStringValid(version)) {
+                throw new ConfigException(
+                        MessageFormat.format(I18N.getString("error.version-string-wrong-format"), version),
+                        I18N.getString("error.version-string-wrong-format.advice"));
+            }
+
+            return true;
+        } catch (RuntimeException re) {
+            throw new ConfigException(re);
         }
-        if (lightVersion < minVersion) {
-            Log.verbose(MessageFormat.format(I18N.getString("message.wrong-tool-version"), TOOL_LIGHT, lightVersion, minVersion));
-            bad = true;
-        }
-
-        if (bad){
-            throw new ConfigException(
-                    I18N.getString("error.no-wix-tools"),
-                    I18N.getString("error.no-wix-tools.advice"));
-        }
-
-        if (lightVersion >= 3.6f) {
-            Log.verbose(I18N.getString("message.use-wix36-features"));
-            p.put(CAN_USE_WIX36.getID(), Boolean.TRUE);
-        }
-
-        /********* validate bundle parameters *************/
-
-        String version = VERSION.fetchFrom(p);
-        if (!isVersionStringValid(version)) {
-            throw new ConfigException(
-                    MessageFormat.format(I18N.getString("error.version-string-wrong-format"), version),
-                    I18N.getString("error.version-string-wrong-format.advice"));
-        }
-
-        return true;
     }
 
     //http://msdn.microsoft.com/en-us/library/aa370859%28v=VS.85%29.aspx
@@ -338,15 +338,25 @@
     }
 
     private boolean prepareProto(Map<String, ? super Object> p) {
-        File bundleRoot = IMAGE_DIR.fetchFrom(p);
+        File bundleRoot = MSI_IMAGE_DIR.fetchFrom(p);
         File appDir = APP_BUNDLER.fetchFrom(p).doBundle(p, bundleRoot, true);
-        p.put(APP_DIR.getID(), appDir);
+        p.put(WIN_APP_IMAGE.getID(), appDir);
         return appDir != null;
     }
 
     public File bundle(Map<String, ? super Object> p, File outdir) {
-        File appDir = APP_DIR.fetchFrom(p);
-        File imageDir = IMAGE_DIR.fetchFrom(p);
+        // validate we have valid tools before continuing
+        String light = TOOL_LIGHT_EXECUTABLE.fetchFrom(p);
+        String candle = TOOL_CANDLE_EXECUTABLE.fetchFrom(p);
+        if (light == null || !new File(light).isFile() ||
+            candle == null || !new File(candle).isFile()) {
+            Log.info(I18N.getString("error.no-wix-tools"));
+            Log.info(MessageFormat.format(I18N.getString("message.light-file-string"), light));
+            Log.info(MessageFormat.format(I18N.getString("message.candle-file-string"), candle));
+            return null;
+        }
+
+        File imageDir = MSI_IMAGE_DIR.fetchFrom(p);
         try {
             imageDir.mkdirs();
 
@@ -371,7 +381,7 @@
                     File configScript = new File(imageDir, configScriptSrc.getName());
                     IOUtils.copyFile(configScriptSrc, configScript);
                     Log.info(MessageFormat.format(I18N.getString("message.running-wsh-script"), configScript.getAbsolutePath()));
-                    IOUtils.run("wscript", configScript, verbose);
+                    IOUtils.run("wscript", configScript, VERBOSE.fetchFrom(p));
                 }
                 return buildMSI(p, outdir);
             }
@@ -386,7 +396,7 @@
                 } else if (imageDir != null) {
                     Log.info(MessageFormat.format(I18N.getString("message.debug-working-directory"), imageDir.getAbsolutePath()));
                 }
-                if (verbose) {
+                if (VERBOSE.fetchFrom(p)) {
                     Log.info(MessageFormat.format(I18N.getString("message.config-save-location"), CONFIG_ROOT.fetchFrom(p).getAbsolutePath()));
                 } else {
                     cleanupConfigFiles(p);
@@ -422,7 +432,8 @@
         fetchResource(WinAppBundler.WIN_BUNDLER_PREFIX + getConfig_Script(params).getName(),
                 I18N.getString("resource.post-install-script"),
                 (String) null,
-                getConfig_Script(params));
+                getConfig_Script(params),
+                VERBOSE.fetchFrom(params));
         return true;
     }
 
@@ -451,7 +462,7 @@
         data.put("APPLICATION_VERSION", VERSION.fetchFrom(params));
 
         //WinAppBundler will add application folder again => step out
-        File imageRootDir = APP_DIR.fetchFrom(params);
+        File imageRootDir = WIN_APP_IMAGE.fetchFrom(params);
         File launcher = WinAppBundler.getLauncher(
                 imageRootDir.getParentFile(), params);
 
@@ -488,13 +499,14 @@
         w.write(preprocessTextResource(
                 WinAppBundler.WIN_BUNDLER_PREFIX + getConfig_ProjectFile(params).getName(),
                 I18N.getString("resource.wix-config-file"), 
-                MSI_PROJECT_TEMPLATE, data));
+                MSI_PROJECT_TEMPLATE, data, VERBOSE.fetchFrom(params)));
         w.close();
         return true;
     }
     private int id;
     private int compId;
     private final static String LAUNCHER_ID = "LauncherId";
+    private final static String LAUNCHER_SVC_ID = "LauncherSvcId";
 
     private void walkFileTree(Map<String, ? super Object> params, File root, PrintStream out, String prefix) {
         List<File> dirs = new ArrayList<>();
@@ -525,13 +537,17 @@
         out.println("  <RemoveFolder Id=\"RemoveDir" + (id++) + "\" On=\"uninstall\" />");
 
         boolean needRegistryKey = !MSI_SYSTEM_WIDE.fetchFrom(params);
-        File imageRootDir = APP_DIR.fetchFrom(params);
+        File imageRootDir = WIN_APP_IMAGE.fetchFrom(params);
         File launcherFile = WinAppBundler.getLauncher(
                 /* Step up as WinAppBundler will add app folder */
                 imageRootDir.getParentFile(), params);
+        File launcherSvcFile = WinAppBundler.getLauncherSvc(
+                                imageRootDir.getParentFile(), params);
+
         //Find out if we need to use registry. We need it if
         //  - we doing user level install as file can not serve as KeyPath
         //  - if we adding shortcut in this component
+
         for (File f: files) {
             boolean isLauncher = f.equals(launcherFile);
             if (isLauncher) {
@@ -555,7 +571,15 @@
         boolean desktopShortcut = SHORTCUT_HINT.fetchFrom(params);
         for (File f : files) {
             boolean isLauncher = f.equals(launcherFile);
+            boolean isLauncherSvc = f.equals(launcherSvcFile);
+
+            // skip executable for service, will be covered by new component entry
+            if (isLauncherSvc) {
+                continue;
+            }
+
             boolean doShortcuts = isLauncher && (menuShortcut || desktopShortcut);
+
             out.println(prefix + "   <File Id=\"" +
                     (isLauncher ? LAUNCHER_ID : ("FileId" + (id++))) + "\""
                     + " Name=\"" + f.getName() + "\" "
@@ -575,6 +599,52 @@
         }
         out.println(prefix + " </Component>");
 
+        // Two components cannot share the same key path value.
+        // We already have HKCU created with key path set and
+        // we need to create separate component for ServiceInstall element
+        // to ensure that key path is also set to the service executable.
+        //
+        // http://wixtoolset.org/documentation/manual/v3/xsd/wix/serviceinstall.html
+
+        boolean needServiceEntries = false;
+        for (File f: files) {
+            boolean isLauncherSvc = f.equals(launcherSvcFile);
+            if (isLauncherSvc && SERVICE_HINT.fetchFrom(params)) {
+                needServiceEntries = true;
+            }
+        }
+
+        if (needServiceEntries) {
+            out.println(prefix + " <Component Id=\"comp" + (compId++) + "\" DiskId=\"1\""
+                    + " Guid=\"" + UUID.randomUUID().toString() + "\""
+                    + (BIT_ARCH_64.fetchFrom(params) ? " Win64=\"yes\"" : "") + ">");
+            out.println("  <CreateFolder/>");
+            out.println("  <RemoveFolder Id=\"RemoveDir" + (id++) + "\" On=\"uninstall\" />");
+
+            out.println(prefix + "   <File Id=\"" + LAUNCHER_SVC_ID + "\""
+                    + " Name=\"" + launcherSvcFile.getName() + "\" "
+                    + " Source=\"" + relativePath(imageRootDir, launcherSvcFile) + "\""
+                    + (BIT_ARCH_64.fetchFrom(params) ? " ProcessorArchitecture=\"x64\"" : "")
+                    + " KeyPath=\"yes\">");
+            out.println(prefix + "   </File>");
+            out.println(prefix + "   <ServiceInstall Id=\"" + WinAppBundler.getAppName(params) + "\""
+                    + " Name=\"" + WinAppBundler.getAppName(params) + "\""
+                    + " Description=\"" + DESCRIPTION.fetchFrom(params) + "\""
+                    + " ErrorControl=\"normal\""
+                    + " Start=\"" + (RUN_AT_STARTUP.fetchFrom(params) ? "auto" : "demand") + "\""
+                    + " Type=\"ownProcess\" Vital=\"yes\" Account=\"LocalSystem\""
+                    + " Arguments=\"-mainExe " + launcherFile.getName() + "\"/>");
+
+            out.println(prefix + "   <ServiceControl Id=\""+ WinAppBundler.getAppName(params) + "\""
+                    + " Name=\"" + WinAppBundler.getAppName(params) + "\""
+                    + (START_ON_INSTALL.fetchFrom(params) ? " Start=\"install\"" : "")
+                    + (STOP_ON_UNINSTALL.fetchFrom(params) ? " Stop=\"uninstall\"" : "")
+                    + " Remove=\"uninstall\""
+                    + " Wait=\"yes\" />");
+
+            out.println(prefix + " </Component>");
+        }
+
         for (File d : dirs) {
             out.println(prefix + " <Directory Id=\"dirid" + (id++)
                     + "\" Name=\"" + d.getName() + "\">");
@@ -617,7 +687,7 @@
         //dynamic part
         id = 0;
         compId = 0; //reset counters
-        walkFileTree(params, APP_DIR.fetchFrom(params), out, "    ");
+        walkFileTree(params, WIN_APP_IMAGE.fetchFrom(params), out, "    ");
 
         //closing
         out.println("   </Directory>");
@@ -690,8 +760,8 @@
                 getConfig_ProjectFile(params).getAbsolutePath(),
                 "-ext", "WixUtilExtension",
                 "-out", candleOut.getAbsolutePath());
-        pb = pb.directory(APP_DIR.fetchFrom(params));
-        IOUtils.exec(pb, verbose);
+        pb = pb.directory(WIN_APP_IMAGE.fetchFrom(params));
+        IOUtils.exec(pb, VERBOSE.fetchFrom(params));
 
         Log.verbose(MessageFormat.format(I18N.getString("message.generating-msi"), msiOut.getAbsolutePath()));
 
@@ -704,8 +774,8 @@
                 candleOut.getAbsolutePath(),
                 "-ext", "WixUtilExtension",
                 "-out", msiOut.getAbsolutePath());
-        pb = pb.directory(APP_DIR.fetchFrom(params));
-        IOUtils.exec(pb, verbose);
+        pb = pb.directory(WIN_APP_IMAGE.fetchFrom(params));
+        IOUtils.exec(pb, VERBOSE.fetchFrom(params));
 
         candleOut.delete();
         IOUtils.deleteRecursive(tmpDir);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/main/native/launcher/win/WinLauncherSvc.cpp	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,884 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+/*
+ * WinLauncherSvc.cpp : Defines the entry points for the service application.
+ *
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/bb540475(v=vs.85).aspx
+ */
+
+#include <windows.h>
+#include <tchar.h>
+#include <strsafe.h>
+#include <shellapi.h>
+#include <stdlib.h>
+
+#pragma comment(lib, "advapi32.lib")
+
+SERVICE_STATUS          gSvcStatus; 
+SERVICE_STATUS_HANDLE   gSvcStatusHandle; 
+HANDLE                  ghSvcStopEvent = NULL;
+
+VOID SvcInstall(TCHAR *svcName, TCHAR *svcDesc, TCHAR *mainExe,
+                BOOL startOnInstall, BOOL runAtStartup);
+VOID SvcStartOnInstall(SC_HANDLE schService);
+
+VOID SvcUninstall(TCHAR *svcName, BOOL stopOnUninstall);
+VOID SvcStopOnUninstall(SC_HANDLE schSCManager, SC_HANDLE schService);
+BOOL StopDependentServices(SC_HANDLE schSCManager, SC_HANDLE schService);
+
+VOID SvcStartup(TCHAR *mainExe);
+
+VOID WINAPI SvcMain(DWORD argc, LPTSTR *argv);
+VOID WINAPI SvcCtrlHandler(DWORD); 
+
+VOID ReportSvcStatus(DWORD, DWORD, DWORD);
+VOID SvcInit(DWORD, LPTSTR *); 
+VOID SvcReportEvent(LPTSTR);
+
+HANDLE CreateMainProcess();
+
+#define SVC_ERROR                        ((DWORD)0xC0020001L)
+#define SVCNAME  _T("")
+
+static bool isDebug = FALSE;
+
+/*
+ * Set env variable JAVAFX_LAUNCHER_DEBUG to enable debugging output
+ */
+static void enableDebugIfNeeded() {
+    TCHAR* buffer = NULL;
+    size_t bufferLen = 0;
+
+    //Check if env variable is set
+    _tdupenv_s(&buffer, &bufferLen, _T("JAVAFX_LAUNCHER_DEBUG"));
+    if (buffer != NULL) { //env variable set
+        isDebug = true;
+        free(buffer);
+    }
+}
+
+static void debug(LPCTSTR pszFormat, ...) {
+    TCHAR szOutput[MAX_PATH];
+    size_t cchDest = MAX_PATH;
+    va_list ap;
+
+    if (isDebug) {
+        va_start(ap, cchDest);
+        StringCchPrintf(szOutput, cchDest, pszFormat);
+	va_end(ap);
+
+        OutputDebugString(szOutput);
+    }
+}
+
+/*
+ * internal parameters coming from the bundler
+ */
+static TCHAR *gMainExe = NULL; // relative path to the main executable
+
+/*
+ * Entry point for the executable
+ *
+ * Usages:
+ *
+ *   - install new service in the SCM database
+ *
+ *     WinLauncherSrv.exe -install -svcName Name -svcDesc Description -mainExe WinLauncher.exe
+ *                        [-startOnInstall] [-runAtStartup]
+ *
+ *   - uninstall service from the SCM database
+ *
+ *     WinLauncherSrv.exe -uninstall -svcName Name
+ *                        [-stopOnUninstall]
+ *
+ *   - the service will be started by the SCM
+ *
+ *     WinLauncherSrv.exe -mainExe WinLauncher.exe
+ */
+int APIENTRY _tWinMain(HINSTANCE hInstance,
+                       HINSTANCE hPrevInstance,
+                       LPTSTR    lpCmdLine,
+                       int       nCmdShow)
+{
+    LPWSTR *szArgList;
+    int argCount;
+
+    BOOL isInstall = FALSE;
+    BOOL isUninstall = FALSE;
+    BOOL startOnInstall = FALSE;
+    BOOL stopOnUninstall = FALSE;
+    BOOL runAtStartup = FALSE;
+
+    TCHAR *mainExe = NULL;
+    TCHAR *svcName = NULL;
+    TCHAR *svcDesc = NULL;
+
+    enableDebugIfNeeded();
+
+    // Parse command line arguments
+    szArgList = CommandLineToArgvW(GetCommandLine(), &argCount);
+
+    int i;
+    for (i = 1; i < argCount; i++) {
+        if (lstrcmpi(szArgList[i], TEXT("-install")) == 0) {
+            isInstall = TRUE;
+        } else if (lstrcmpi(szArgList[i], TEXT("-uninstall")) == 0) {
+            isUninstall = TRUE;
+        } else if (lstrcmpi(szArgList[i], TEXT("-mainExe")) == 0) {
+            mainExe = szArgList[++i];
+        } else if (lstrcmpi(szArgList[i], TEXT("-svcName")) == 0) {
+            svcName = szArgList[++i];
+        } else if (lstrcmpi(szArgList[i], TEXT("-svcDesc")) == 0) {
+            svcDesc = szArgList[++i];
+        } else if (lstrcmpi(szArgList[i], TEXT("-startOnInstall")) == 0) {
+            startOnInstall = TRUE;
+        } else if (lstrcmpi(szArgList[i], TEXT("-stopOnUninstall")) == 0) {
+            stopOnUninstall = TRUE;
+        } else if (lstrcmpi(szArgList[i], TEXT("-runAtStartup")) == 0) {
+            runAtStartup = TRUE;
+        } else {
+            // unrecognized option
+        }
+    }
+
+    if (isInstall) {
+        SvcInstall(svcName, svcDesc, mainExe, startOnInstall, runAtStartup);
+    } else if (isUninstall) {
+        SvcUninstall(svcName, stopOnUninstall);
+    } else {
+        SvcStartup(mainExe);
+    }
+    LocalFree(szArgList);
+
+    return 1;
+}
+
+/*
+ * Installs a service in the SCM database
+ */
+VOID SvcInstall(TCHAR *svcName, TCHAR *svcDesc, TCHAR *mainExe,
+                BOOL startOnInstall, BOOL runAtStartup)
+{
+    SC_HANDLE schSCManager;
+    SC_HANDLE schService;
+    TCHAR szModuleName[MAX_PATH];
+    TCHAR szPath[MAX_PATH]; // also includes arguments for the service
+    SERVICE_DESCRIPTION sd = {0};
+    DWORD dwStartType;
+
+    if (svcName == NULL) {
+        debug(TEXT("SvcInstall failed - svcName cannot be NULL"));
+        return;
+    }
+
+    if (mainExe == NULL) {
+        debug(TEXT("SvcInstall failed - mainExe cannot be NULL"));
+        return;
+    }
+
+    if(!GetModuleFileName(NULL, szModuleName, MAX_PATH )) {
+        debug(TEXT("Cannot install service (%d)"), GetLastError());
+        return;
+    }
+
+    // append the service arguments to the service executable
+    StringCchPrintf(szPath, MAX_PATH, _T("%s -mainExe %s"), szModuleName, mainExe);
+
+    // Get a handle to the SCM database. 
+    schSCManager = OpenSCManager( 
+        NULL,                    // local computer
+        NULL,                    // ServicesActive database 
+        SC_MANAGER_ALL_ACCESS);  // full access rights 
+ 
+    if (NULL == schSCManager) {
+        debug(TEXT("OpenSCManager failed (%d)"), GetLastError());
+        return;
+    }
+
+    if (runAtStartup) {
+        dwStartType = SERVICE_AUTO_START;
+    } else {
+        dwStartType = SERVICE_DEMAND_START;
+    }
+
+    // Create the service
+    schService = CreateService( 
+        schSCManager,              // SCM database 
+        svcName,                   // name of service 
+        svcName,                   // service name to display 
+        SERVICE_ALL_ACCESS,        // desired access 
+        SERVICE_WIN32_OWN_PROCESS, // service type 
+        dwStartType,               // start type 
+        SERVICE_ERROR_NORMAL,      // error control type 
+        szPath,                    // path to service's binary 
+        NULL,                      // no load ordering group 
+        NULL,                      // no tag identifier 
+        NULL,                      // no dependencies 
+        NULL,                      // LocalSystem account 
+        NULL);                     // no password 
+ 
+    if (schService == NULL) {
+        debug(TEXT("CreateService failed (%d)"), GetLastError());
+        CloseServiceHandle(schSCManager);
+        return;
+    }
+
+    debug(TEXT("Service installed successfully"));
+
+    // Change the service description
+    if (svcDesc != NULL) {
+        sd.lpDescription = svcDesc;
+
+        if(!ChangeServiceConfig2(
+            schService,                 // handle to service
+            SERVICE_CONFIG_DESCRIPTION, // change: description
+            &sd))                       // new description
+        {
+            debug(TEXT("ChangeServiceConfig2 failed"));
+        }
+        else
+        {
+            debug(TEXT("Service description updated successfully"));
+        }
+    }
+
+    // Start the service
+    if (startOnInstall) {
+        SvcStartOnInstall(schService);
+    }
+
+    CloseServiceHandle(schService); 
+    CloseServiceHandle(schSCManager);
+}
+
+/*
+ * Attempts to start the service.
+ */
+VOID SvcStartOnInstall(SC_HANDLE schService)
+{
+    SERVICE_STATUS_PROCESS ssStatus;
+    DWORD dwOldCheckPoint; 
+    DWORD dwStartTickCount;
+    DWORD dwWaitTime;
+    DWORD dwBytesNeeded;
+
+    if (!StartService(
+                      schService,  // handle to service 
+                      0,           // number of arguments 
+                      NULL) )      // no arguments 
+    {
+        debug(TEXT("StartService failed (%d)"), GetLastError());
+        return; 
+    }
+    else
+    {
+        debug(TEXT("Service start pending..."));
+    }
+
+    // Check the status until the service is no longer start pending.
+    if (!QueryServiceStatusEx(
+                              schService,                     // handle to service 
+                              SC_STATUS_PROCESS_INFO,         // info level
+                              (LPBYTE) &ssStatus,             // address of structure
+                              sizeof(SERVICE_STATUS_PROCESS), // size of structure
+                              &dwBytesNeeded ) )              // if buffer too small
+    {
+        debug(TEXT("QueryServiceStatusEx failed (%d)"), GetLastError());
+        return; 
+    }
+ 
+    // Save the tick count and initial checkpoint.
+    dwStartTickCount = GetTickCount();
+    dwOldCheckPoint = ssStatus.dwCheckPoint;
+
+    while (ssStatus.dwCurrentState == SERVICE_START_PENDING) 
+    { 
+        // Do not wait longer than the wait hint. A good interval is 
+        // one-tenth the wait hint, but no less than 1 second and no 
+        // more than 10 seconds. 
+        dwWaitTime = ssStatus.dwWaitHint / 10;
+
+        if(dwWaitTime < 1000) {
+            dwWaitTime = 1000;
+        } else if (dwWaitTime > 10000) {
+            dwWaitTime = 10000;
+        }
+
+        Sleep(dwWaitTime);
+
+        // Check the status again. 
+        if (!QueryServiceStatusEx( 
+                                  schService,                     // handle to service 
+                                  SC_STATUS_PROCESS_INFO,         // info level
+                                  (LPBYTE) &ssStatus,             // address of structure
+                                  sizeof(SERVICE_STATUS_PROCESS), // size of structure
+                                  &dwBytesNeeded ) )              // if buffer too small
+        {
+            debug(TEXT("QueryServiceStatusEx failed (%d)"), GetLastError());
+            break;
+        }
+ 
+        if (ssStatus.dwCheckPoint > dwOldCheckPoint)
+        {
+            // Continue to wait and check.
+            dwStartTickCount = GetTickCount();
+            dwOldCheckPoint = ssStatus.dwCheckPoint;
+        } else {
+            if(GetTickCount() - dwStartTickCount > ssStatus.dwWaitHint) {
+                // No progress made within the wait hint.
+                break;
+            }
+        }
+    }
+
+    // Determine whether the service is running.
+    if (ssStatus.dwCurrentState == SERVICE_RUNNING) {
+        debug(TEXT("Service started successfully."));
+    } else {
+        debug(TEXT("Service not started."));
+	debug(TEXT("Current State: %d"), ssStatus.dwCurrentState);
+	debug(TEXT("Exit Code: %d"), ssStatus.dwWin32ExitCode);
+	debug(TEXT("Check Point: %d"), ssStatus.dwCheckPoint);
+	debug(TEXT("Wait Hint: %d"), ssStatus.dwWaitHint);
+    }
+}
+
+/*
+ * Deletes a service from the SCM database
+ */
+VOID SvcUninstall(TCHAR *svcName, BOOL stopOnUninstall)
+{
+    SC_HANDLE schSCManager;
+    SC_HANDLE schService;
+
+    if (svcName == NULL) {
+        debug(TEXT("SvcUninstall failed - svcName cannot be NULL"));
+        return;
+    }
+
+    // Get a handle to the SCM database. 
+    schSCManager = OpenSCManager( 
+        NULL,                    // local computer
+        NULL,                    // ServicesActive database 
+        SC_MANAGER_ALL_ACCESS);  // full access rights 
+ 
+    if (schSCManager == NULL) {
+        debug(TEXT("OpenSCManager failed (%d)"), GetLastError());
+        return;
+    }
+
+    // Get a handle to the service.
+    schService = OpenService( 
+        schSCManager,                                      // SCM database 
+        svcName,                                           // name of service 
+        DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);     // need stop/delete access
+ 
+    if (schService == NULL) {
+        debug(TEXT("OpenService failed (%d)"), GetLastError());
+        CloseServiceHandle(schSCManager);
+        return;
+    }
+
+    // Stop the service
+    if (stopOnUninstall) {
+        SvcStopOnUninstall(schSCManager, schService);
+    }
+
+    // Delete the service.
+    if (!DeleteService(schService)) {
+        debug(TEXT("DeleteService failed (%d)"), GetLastError());
+    } else {
+        debug(TEXT("Service deleted successfully"));
+    }
+ 
+    CloseServiceHandle(schService); 
+    CloseServiceHandle(schSCManager);
+}
+
+/*
+ * Stops the service
+ */
+VOID SvcStopOnUninstall(SC_HANDLE schSCManager, SC_HANDLE schService)
+{
+    SERVICE_STATUS_PROCESS ssp;
+    DWORD dwStartTime = GetTickCount();
+    DWORD dwBytesNeeded;
+    DWORD dwTimeout = 30000; // 30-second time-out
+    DWORD dwWaitTime;
+
+    // Make sure the service is not already stopped.
+    if ( !QueryServiceStatusEx(
+                               schService,
+                               SC_STATUS_PROCESS_INFO,
+                               (LPBYTE)&ssp,
+                               sizeof(SERVICE_STATUS_PROCESS),
+                               &dwBytesNeeded))
+    {
+        debug(TEXT("QueryServiceStatusEx failed (%d)"), GetLastError());
+	return;
+    }
+
+    if (ssp.dwCurrentState == SERVICE_STOPPED) {
+        debug(TEXT("Service is already stopped."));
+        return;
+    }
+
+    // If a stop is pending, wait for it.
+    while (ssp.dwCurrentState == SERVICE_STOP_PENDING)
+    {
+        debug(TEXT("Service stop pending..."));
+
+        // Do not wait longer than the wait hint. A good interval is 
+        // one-tenth of the wait hint but not less than 1 second  
+        // and not more than 10 seconds.
+        dwWaitTime = ssp.dwWaitHint / 10;
+
+        if(dwWaitTime < 1000) {
+            dwWaitTime = 1000;
+        } else if (dwWaitTime > 10000) {
+            dwWaitTime = 10000;
+        }
+
+        Sleep(dwWaitTime);
+
+        if (!QueryServiceStatusEx( 
+                                  schService, 
+                                  SC_STATUS_PROCESS_INFO,
+                                  (LPBYTE)&ssp, 
+                                  sizeof(SERVICE_STATUS_PROCESS),
+                                  &dwBytesNeeded ) )
+        {
+            debug(TEXT("QueryServiceStatusEx failed (%d)"), GetLastError());
+            return;
+        }
+
+        if (ssp.dwCurrentState == SERVICE_STOPPED) {
+            debug(TEXT("Service stopped successfully."));
+            return;
+        }
+
+        if (GetTickCount() - dwStartTime > dwTimeout) {
+            debug(TEXT("Service stop timed out."));
+            return;
+        }
+    }
+
+    // If the service is running, dependencies must be stopped first.
+    StopDependentServices(schSCManager, schService);
+
+    // Send a stop code to the service.
+    if (!ControlService(
+                        schService,
+                        SERVICE_CONTROL_STOP,
+                        (LPSERVICE_STATUS)&ssp))
+    {
+        debug(TEXT("ControlService failed (%d)"), GetLastError());
+        return;
+    }
+
+    // Wait for the service to stop.
+    while (ssp.dwCurrentState != SERVICE_STOPPED)
+    {
+        Sleep( ssp.dwWaitHint );
+        if (!QueryServiceStatusEx(
+                                  schService,
+                                  SC_STATUS_PROCESS_INFO,
+                                  (LPBYTE)&ssp,
+                                  sizeof(SERVICE_STATUS_PROCESS),
+                                  &dwBytesNeeded))
+        {
+            debug(TEXT("QueryServiceStatusEx failed (%d)"), GetLastError());
+            return;
+        }
+
+        if (ssp.dwCurrentState == SERVICE_STOPPED) {
+            break;
+	}
+
+        if (GetTickCount() - dwStartTime > dwTimeout) {
+            debug(TEXT("Wait timed out."));
+            return;
+        }
+    }
+
+    debug(TEXT("Service stopped successfully"));
+}
+
+BOOL StopDependentServices(SC_HANDLE schSCManager, SC_HANDLE schService) {
+    DWORD i;
+    DWORD dwBytesNeeded;
+    DWORD dwCount;
+
+    LPENUM_SERVICE_STATUS   lpDependencies = NULL;
+    ENUM_SERVICE_STATUS     ess;
+    SC_HANDLE               hDepService;
+    SERVICE_STATUS_PROCESS  ssp;
+
+    DWORD dwStartTime = GetTickCount();
+    DWORD dwTimeout = 30000; // 30-second time-out
+
+    // Pass a zero-length buffer to get the required buffer size.
+    if (EnumDependentServices(schService, SERVICE_ACTIVE,
+                              lpDependencies, 0, &dwBytesNeeded, &dwCount))
+    {
+        // If the Enum call succeeds, then there are no dependent
+        // services, so do nothing.
+        return TRUE;
+    } 
+    else 
+    {
+        if (GetLastError() != ERROR_MORE_DATA) {
+            return FALSE; // Unexpected error
+        }
+
+        // Allocate a buffer for the dependencies.
+        lpDependencies = (LPENUM_SERVICE_STATUS) HeapAlloc(
+                      GetProcessHeap(), HEAP_ZERO_MEMORY, dwBytesNeeded);
+  
+        if (!lpDependencies) {
+            return FALSE;
+	}
+
+        __try {
+            // Enumerate the dependencies.
+            if (!EnumDependentServices(schService, SERVICE_ACTIVE,
+                                       lpDependencies, dwBytesNeeded, &dwBytesNeeded,
+                                       &dwCount))
+            {
+                return FALSE;
+            }
+
+            for (i = 0; i < dwCount; i++) {
+                ess = *(lpDependencies + i);
+                // Open the service.
+                hDepService = OpenService(schSCManager, 
+                                          ess.lpServiceName, 
+                                          SERVICE_STOP | SERVICE_QUERY_STATUS);
+
+                if (!hDepService) {
+                    return FALSE;
+		}
+
+                __try {
+                    // Send a stop code.
+                    if (!ControlService(hDepService,
+                                        SERVICE_CONTROL_STOP,
+                                        (LPSERVICE_STATUS) &ssp)) {
+                        return FALSE;
+                    }
+
+                    // Wait for the service to stop.
+                    while (ssp.dwCurrentState != SERVICE_STOPPED)
+                    {
+                        Sleep( ssp.dwWaitHint );
+                        if (!QueryServiceStatusEx(
+                                                  hDepService, 
+                                                  SC_STATUS_PROCESS_INFO,
+                                                  (LPBYTE)&ssp,
+                                                  sizeof(SERVICE_STATUS_PROCESS),
+                                                  &dwBytesNeeded))
+                        {
+                            return FALSE;
+                        }
+
+                        if (ssp.dwCurrentState == SERVICE_STOPPED) {
+                            break;
+                        }
+
+                        if (GetTickCount() - dwStartTime > dwTimeout) {
+                            return FALSE;
+                        }
+                    }
+                }
+                __finally
+                {
+                    // Always release the service handle.
+                    CloseServiceHandle(hDepService);
+                }
+            }
+        }
+        __finally 
+        {
+            // Always free the enumeration buffer.
+            HeapFree( GetProcessHeap(), 0, lpDependencies );
+        }
+    } 
+    return TRUE;
+}
+
+/*
+ * The service is being started by the SCM.
+ */
+VOID SvcStartup(TCHAR *mainExe)
+{
+    if (mainExe == NULL) {
+        debug(TEXT("SvcStartup failed - mainExe cannot be NULL"));
+        return;
+    }
+
+    // store the path to the main executable as global variable
+    TCHAR szMainExe[MAX_PATH];
+    StringCchPrintf(szMainExe, MAX_PATH, _T("%s"), mainExe);
+    gMainExe = szMainExe;
+
+    // If the service is installed with the SERVICE_WIN32_OWN_PROCESS
+    // service type, the lpServiceName member of lpServiceTable is ignored.
+    // This member cannot be NULL but it can be an empty string (SVCNAME).
+
+    SERVICE_TABLE_ENTRY DispatchTable[] = 
+    {
+        { SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain },
+        { NULL, NULL }
+    };
+
+    // This call returns when the service has stopped. 
+    // The process should simply terminate when the call returns.
+    if (!StartServiceCtrlDispatcher (DispatchTable)) {
+        SvcReportEvent(TEXT("StartServiceCtrlDispatcher"));
+    }
+}
+
+/*
+ * Entry point for the service
+ *
+ * lpszArgv are parameters that come from the "Start Parameters" text field in
+ * the properties dialog for the service (the Services snap-in from the Control Panel)
+ * Ignore the params.
+ */
+VOID WINAPI SvcMain(DWORD dwArgc, LPTSTR *lpszArgv)
+{
+    DWORD Status = E_FAIL;
+
+    enableDebugIfNeeded();
+
+    if (gMainExe == NULL) {
+        debug(TEXT("SvcMain failed - gMainExe cannot be NULL"));
+        return;
+    }
+
+    // Register the handler function for the service
+    gSvcStatusHandle = RegisterServiceCtrlHandler (SVCNAME, SvcCtrlHandler);
+
+    if(!gSvcStatusHandle) { 
+        SvcReportEvent(TEXT("RegisterServiceCtrlHandler")); 
+        return; 
+    }
+
+    // These SERVICE_STATUS members remain as set here
+    gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 
+    gSvcStatus.dwServiceSpecificExitCode = 0;    
+
+    // Report initial status to the SCM
+    ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
+
+    // Perform service-specific initialization and work.
+    SvcInit(dwArgc, lpszArgv);
+}
+
+/*
+ * Performs the service code
+ *
+ * @param dwArgc - Number of arguments in the lpszArgv array
+ * @param lpszArgv - Array of strings. The first string is the name of
+ *                the service and subsequent strings are passed by the process
+ *                that called the StartService function to start the service.
+ */
+VOID SvcInit(DWORD dwArgc, LPTSTR *lpszArgv)
+{
+    // Create an event. The control handler function, SvcCtrlHandler,
+    // signals this event when it receives the stop control code.
+    ghSvcStopEvent = CreateEvent(
+                         NULL,    // default security attributes
+                         TRUE,    // manual reset event
+                         FALSE,   // not signaled
+                         NULL);   // no name
+
+    if (ghSvcStopEvent == NULL) {
+        ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
+        return;
+    }
+
+    // Report running status when initialization is complete.
+    ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 );
+
+    // Perform work until service stops.
+    HANDLE hProcess = CreateMainProcess();
+
+    while(1)
+    {
+        // Check whether to stop the service.
+        WaitForSingleObject(ghSvcStopEvent, INFINITE);
+
+        // REMIND: better way to stop execution of the main process?
+        if (hProcess) {
+            TerminateProcess(hProcess, NULL);
+        }
+
+        ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
+        return;
+    }
+}
+
+/*
+ * Starts the main process
+ *
+ * ASSUMPTION - the main and the service executables are in the same directory
+ */
+HANDLE CreateMainProcess() {
+    TCHAR szBaseDir[MAX_PATH] = {0};
+    TCHAR szMainExe[MAX_PATH] = {0};
+
+    GetModuleFileName(NULL, szBaseDir, MAX_PATH);
+    TCHAR* end = _tcsrchr(szBaseDir, '\\');
+    if (end != NULL) {
+        *end = 0;
+    }
+
+    StringCchPrintf(szMainExe, MAX_PATH, _T("%s\\%s"), szBaseDir, gMainExe);
+
+    STARTUPINFO si = {0};
+    si.cb          = sizeof(STARTUPINFO);
+    si.dwFlags     = STARTF_USESHOWWINDOW;
+    si.wShowWindow = FALSE;
+
+    PROCESS_INFORMATION pi = {0};
+    BOOL ret = CreateProcess(
+                        szMainExe,
+                        szMainExe,
+                        NULL,
+                        NULL,
+                        FALSE,
+                        CREATE_NO_WINDOW,
+                        NULL,
+                        szBaseDir,
+                        &si,
+                        &pi
+                );
+
+    if (!ret) {
+        debug(TEXT("CreateProcess failed (%d)"), GetLastError());
+        debug(TEXT("    szBaseDir=%s"), szBaseDir);
+        debug(TEXT("    szMainExe=%s"), szMainExe);
+    }
+
+    return pi.hProcess;
+}
+
+/*
+ *
+ * Called by SCM whenever a control code is sent to the service
+ * using the ControlService function.
+ *
+ * @param dwCtrl - control code
+ */
+VOID WINAPI SvcCtrlHandler( DWORD dwCtrl )
+{
+    switch(dwCtrl) 
+    {  
+        case SERVICE_CONTROL_STOP: 
+            ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
+            // Signal the service to stop.
+            SetEvent(ghSvcStopEvent);
+            ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
+            return; 
+        case SERVICE_CONTROL_INTERROGATE: 
+            break; 
+        default: 
+            break;
+   }   
+}
+
+/*
+ * Sets the current service status and reports it to the SCM.
+ *
+ * @param dwCurrentState - The current state (see SERVICE_STATUS)
+ * @param dwWin32ExitCode - The system error code
+ * @param dwWaitHint - Estimated time for pending operation, 
+ *                     in milliseconds
+ */
+VOID ReportSvcStatus( DWORD dwCurrentState,
+                      DWORD dwWin32ExitCode,
+                      DWORD dwWaitHint)
+{
+    static DWORD dwCheckPoint = 1;
+
+    // Fill in the SERVICE_STATUS structure.
+    gSvcStatus.dwCurrentState = dwCurrentState;
+    gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
+    gSvcStatus.dwWaitHint = dwWaitHint;
+
+    if (dwCurrentState == SERVICE_START_PENDING) {
+        gSvcStatus.dwControlsAccepted = 0;
+    } else {
+        gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+    }
+
+    if ((dwCurrentState == SERVICE_RUNNING) ||
+        (dwCurrentState == SERVICE_STOPPED))
+    {
+        gSvcStatus.dwCheckPoint = 0;
+    } else {
+        gSvcStatus.dwCheckPoint = dwCheckPoint++;
+    }
+
+    // Report the status of the service to the SCM.
+    SetServiceStatus( gSvcStatusHandle, &gSvcStatus );
+}
+
+/*
+ * Logs messages to the event log
+ *
+ * @param szFunction - name of function that failed
+ *
+ * The service must have an entry in the Application event log.
+ */
+VOID SvcReportEvent(LPTSTR szFunction) 
+{ 
+    HANDLE hEventSource;
+    LPCTSTR lpszStrings[2];
+    TCHAR Buffer[80];
+
+    hEventSource = RegisterEventSource(NULL, SVCNAME);
+
+    if(hEventSource != NULL) {
+        StringCchPrintf(Buffer, 80, TEXT("%s failed with %d"), szFunction, GetLastError());
+
+        lpszStrings[0] = SVCNAME;
+        lpszStrings[1] = Buffer;
+
+        ReportEvent(hEventSource,        // event log handle
+                    EVENTLOG_ERROR_TYPE, // event type
+                    0,                   // event category
+                    SVC_ERROR,           // event identifier
+                    NULL,                // no security identifier
+                    2,                   // size of lpszStrings array
+                    0,                   // no binary data
+                    lpszStrings,         // array of strings
+                    NULL);               // no binary data
+
+        DeregisterEventSource(hEventSource);
+    }
+}
--- a/modules/fxpackager/src/main/resources/com/oracle/bundlers/StandardBundlerParam.properties	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/resources/com/oracle/bundlers/StandardBundlerParam.properties	Mon Mar 17 09:51:09 2014 -0600
@@ -1,88 +1,103 @@
-param.app-name.name=App Name
-param.app-name.description=The name of the application.
-
-param.app-resource.description=All of the files to place in the resources directory.  Including all needed jars as assets.
-param.app-resources.name=Resources
-
-param.build-root.name=Build Root
-param.build-root.description=The directory in which to use and place temporary files.
-
-param.category.name=Category
-param.category.description=The category oor group of the application.  Generally speaking you will also want to specify application specific categories as well.
-param.category.default=Unknown
-
-param.copyright.name=Copyright
-param.copyright.description=The copyright for the application.
-param.copyright.default=Copyright (C) {0}
-
-param.description.name=Description
-param.description.description=A longer description of the application
-param.description.default=none
-
-param.desktop-shortcut-hint.name=Shortcut Hint
-param.desktop-shortcut-hint.description=If the bundler can create desktop shortcuts, should it make one?
-
-param.icon-file.name=Icon
-param.icon-file.description=The main icon of the application bundle.
-
-param.identifier.name=Identifier
-param.identifier.description=What is the machine readable identifier of this application?  The format should be a DNS name in reverse order, such as com.example.myapplication.
-
-param.jvm-options.name=JVM Options
-param.jvm-options.description=JVM flags and options to be passed in.
-
-param.jvm-system-properties.name=JVM System Properties
-param.jvm-system-properties.description=JVM System Properties (of the -Dname\=value variety).
-
-param.license-files.name=License
-param.license-files.description=Text of the end user license presented during interactive installers.
-
-param.license-type.name=
-param.license-type.description=
-param.license-type.default=Unknown
-
-param.main-class.name=Main Class
-param.main-class.description=The main class for the application.  Either a javafx.application.Application instance or a java class with a main method.
-
-param.main-jar-classpath.description=The classpath from the main jar of the application.
-param.main-jar-classpath.name=Main Jar Classpath
-
-param.main-jar.name=Main Jar
-param.main-jar.description=The main jar of the application.  This jar should have the main-class
-
-param.menu-shortcut-hint.name=Menu Hint
-param.menu-shortcut-hint.description=If the bundler can add the application to the system menu, should it?
-
-param.name.name=Name
-param.name.description=The name of the application.
-
-param.preferences-id.description=When storing and retrieving user preferences, what is the identifier that should be used?  The format should be a DNS name in reverse order, such as com.example.myapplication.
-param.preferences-id.name=Preferences ID
-
-param.runtime.name=JRE
-param.runtime.description=The Java Runtime to co-bundle.  The default value is the current JRE running the bundler.  A value of null will cause no JRE to be co-bundled and the system JRE will be used to launch the application.
-
-param.system-wide.name=System Wide
-param.system-wide.description=Should this application attempt to install itself system wide, or only for each user?  Null means use the system default.
-
-param.title.name=Title
-param.title.description=A title for the application.
-
-param.use-javafx-packaging.name=FX Packaging
-param.use-javafx-packaging.description=Should we use the JavaFX packaging conventions?
-
-param.user-jvm-options.name=User JVM Options
-param.user-jvm-options.description=JVM Options the user may override, along with their default values.
-
-param.vendor.name=Vendor
-param.vendor.description=The vendor of the application.
-param.vendor.default=Unknown
-
-param.version.name=Version
-param.version.description=The version of this application.
-param.version.default=1.0
-
-error.required-parameter={0} is a required parameter.
-
-message.fx-app-does-not-match-specified-main=The jar {0} has an FX Application class{1} that does not match the declared main {2}
-message.main-class-does-not-match-specified-main=The jar {0} has a main class {1} that does not match the declared main {2}
+param.app-name.name=App Name
+param.app-name.description=The name of the application.
+
+param.app-resource.description=All of the files to place in the resources directory.  Including all needed jars as assets.
+param.app-resources.name=Resources
+
+param.build-root.name=Build Root
+param.build-root.description=The directory in which to use and place temporary files.
+
+param.category.name=Category
+param.category.description=The category oor group of the application.  Generally speaking you will also want to specify application specific categories as well.
+param.category.default=Unknown
+
+param.copyright.name=Copyright
+param.copyright.description=The copyright for the application.
+param.copyright.default=Copyright (C) {0,date,YYYY}
+
+param.description.name=Description
+param.description.description=A longer description of the application
+param.description.default=none
+
+param.desktop-shortcut-hint.name=Shortcut Hint
+param.desktop-shortcut-hint.description=If the bundler can create desktop shortcuts, should it make one?
+
+param.icon-file.name=Icon
+param.icon-file.description=The main icon of the application bundle.
+
+param.identifier.name=Identifier
+param.identifier.description=What is the machine readable identifier of this application?  The format should be a DNS name in reverse order, such as com.example.myapplication.
+
+param.jvm-options.name=JVM Options
+param.jvm-options.description=JVM flags and options to be passed in.
+
+param.jvm-system-properties.name=JVM System Properties
+param.jvm-system-properties.description=JVM System Properties (of the -Dname\=value variety).
+
+param.license-files.name=License
+param.license-files.description=Text of the end user license presented during interactive installers.
+
+param.license-type.name=
+param.license-type.description=
+param.license-type.default=Unknown
+
+param.main-class.name=Main Class
+param.main-class.description=The main class for the application.  Either a javafx.application.Application instance or a java class with a main method.
+
+param.main-jar-classpath.description=The classpath from the main jar of the application.
+param.main-jar-classpath.name=Main Jar Classpath
+
+param.main-jar.name=Main Jar
+param.main-jar.description=The main jar of the application.  This jar should have the main-class
+
+param.service-hint.name=Service Hint
+param.service-hint.description=The bundler should register the application as service/daemon
+
+param.start-on-install.name=Start On Install
+param.start-on-install.description=Controls whether the service/daemon should be started on install
+
+param.stop-on-uninstall.name=Stop On Uninstall
+param.stop-on-uninstall.description=Controls whether the service/daemon should be stopped on uninstall
+
+param.run-at-startup.name=Run At Startup
+param.run-at-startup.description=Controls whether the service/daemon should be started during system startup
+
+param.menu-shortcut-hint.name=Menu Hint
+param.menu-shortcut-hint.description=If the bundler can add the application to the system menu, should it?
+
+param.name.name=Name
+param.name.description=The name of the application.
+
+param.preferences-id.description=When storing and retrieving user preferences, what is the identifier that should be used?  The format should be a DNS name in reverse order, such as com.example.myapplication.
+param.preferences-id.name=Preferences ID
+
+param.runtime.name=JRE
+param.runtime.description=The Java Runtime to co-bundle.  The default value is the current JRE running the bundler.  A value of null will cause no JRE to be co-bundled and the system JRE will be used to launch the application.
+
+param.system-wide.name=System Wide
+param.system-wide.description=Should this application attempt to install itself system wide, or only for each user?  Null means use the system default.
+
+param.title.name=Title
+param.title.description=A title for the application.
+
+param.use-javafx-packaging.name=FX Packaging
+param.use-javafx-packaging.description=Should we use the JavaFX packaging conventions?
+
+param.user-jvm-options.name=User JVM Options
+param.user-jvm-options.description=JVM Options the user may override, along with their default values.
+
+param.vendor.name=Vendor
+param.vendor.description=The vendor of the application.
+param.vendor.default=Unknown
+
+param.version.name=Version
+param.version.description=The version of this application.
+param.version.default=1.0
+
+param.verbose.name=Verbose
+param.verbose.description=Flag to print out more information and saves configuration files for bundlers.
+
+error.required-parameter={0} is a required parameter.
+
+message.fx-app-does-not-match-specified-main=The jar {0} has an FX Application class{1} that does not match the declared main {2}
+message.main-class-does-not-match-specified-main=The jar {0} has a main class {1} that does not match the declared main {2}
--- a/modules/fxpackager/src/main/resources/com/oracle/bundlers/linux/LinuxDebBundler.properties	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/resources/com/oracle/bundlers/linux/LinuxDebBundler.properties	Mon Mar 17 09:51:09 2014 -0600
@@ -40,6 +40,7 @@
 resource.deb-postinstall-script=DEB postinstall script
 resource.deb-postrm-script=DEB postrm script
 resource.deb-copyright-file=DEB copyright file
+resource.deb-init-script=DEB init script
 resource.menu-shortcut-descriptor=Menu shortcut descriptor
 resource.menu-icon=menu icon
 
--- a/modules/fxpackager/src/main/resources/com/oracle/bundlers/linux/LinuxRpmBundler.properties	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/resources/com/oracle/bundlers/linux/LinuxRpmBundler.properties	Mon Mar 17 09:51:09 2014 -0600
@@ -14,6 +14,7 @@
 param.config-root.description=
 
 resource.rpm-spec-file=RPM spec file
+resource.rpm-init-script=RPM init script
 resource.menu-shortcut-descriptor=Menu shortcut descriptor
 resource.menu-icon=menu icon
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/main/resources/com/oracle/bundlers/mac/MacAppBundler.properties	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,4 @@
+param.config-root.name=
+param.config-root.description=
+
+message.bundle-name-too-long-warning={0} is set to ''{1}'', which is longer than 16 characters.  For a better Mac experience consider shortening it.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/main/resources/com/oracle/bundlers/mac/MacAppStoreBundler.properties	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,20 @@
+bundler.name=Mac App Store Ready Bundler
+bundler.description=Creates a binary bundle ready for deployment into the Mac App Store."
+
+param.signing-key-name.name=Signing Key User Name
+param.signing-key-name.description=The user name portion of the typical "3rd Party Mac Developer Application: <user name>" sigining key
+
+param.signing-key-app.name=Application Signing Key
+param.signing-key-app.description=The full name of the signing key to sign the application with.
+
+param.signing-key-pkg.name=Installer Signing Key
+param.signing-key-pkg.description=The full name of the signing key to sign the PKG Installer with.
+
+param.mac-app-store-entitlements.name=Entitlements File
+param.mac-app-store-entitlements.description=File location of a custom mac app store entitlements file
+
+resource.mac-app-store-entitlements=Mac App Store Entitlements
+resource.mac-app-store-inherit-entitlements=Mac App Store Inherit Entitlements
+
+error.parameters-null=Parameters map is null.
+error.parameters-null.advice=Pass in a non-null parameters map.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/main/resources/com/oracle/bundlers/mac/MacBaseInstallerBundler.properties	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,26 @@
+bundler.name=Mac App Store
+bundler.description=Creates a binary bundle ready for deployment into the Mac App Store.
+
+param.app-bundler.name=Mac App Bundler
+param.app-bundle.description=Creates a .app bundle for the Mac
+
+param.app-image-build-root.name=
+param.app-image-build-root.description=This is temporary location built by the packager that is the root of the image application
+
+param.app-image.name=Image Directory
+param.app-image.description=Location of the image that will be used to build either a DMG or PKG installer.
+
+param.daemon-bundler.name=Mac Daemon Bundler
+param.daemon-bundler.description=Creates daemon image for the Mac
+
+param.daemon-image-build-root.name=
+param.daemon-image-build-root.description=This is temporary location built by the packager that is the root of the daemon image
+
+param.config-root.name=
+param.config-root.description=
+
+error.parameters-null=Parameters map is null.
+error.parameters-null.advice=Pass in a non-null parameters map.
+
+message.app-image-dir-does-not-exist=Specified image directory {0}\: {1} does not exists
+message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/main/resources/com/oracle/bundlers/mac/MacDaemonBundler.properties	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,2 @@
+param.config-root.name=
+param.config-root.description=
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/main/resources/com/oracle/bundlers/mac/MacPKGBundler.properties	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,17 @@
+bundler.name=PKG Installer
+bundler.description=Mac PKG Installer Bundle.
+
+param.packages-root.name=
+param.packages-root.description=This is temporary location for component packages (application and daemon).  The packages are incorporated into final product package.
+
+param.scripts-dir.name=
+param.scripts-dir.description=This is temporary location for package scripts
+
+resource.pkg-preinstall-script=PKG preinstall script
+resource.pkg-postinstall-script=PKG postinstall script
+
+error.parameters-null=Parameters map is null.
+error.parameters-null.advice=Pass in a non-null parameters map.
+
+message.building-pkg=Building PKG package for {0}
+message.preparing-scripts=Preparing package scripts
--- a/modules/fxpackager/src/main/resources/com/oracle/bundlers/windows/WinExeBundler.properties	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/resources/com/oracle/bundlers/windows/WinExeBundler.properties	Mon Mar 17 09:51:09 2014 -0600
@@ -36,6 +36,7 @@
 message.tool-version=\  Detected [{0}] version [{1}]
 message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut.
 message.running-wsh-script=Running WSH script on application image [{0}]
+message.iscc-file-string=\  InnoSetup compiler set to {0}
 
 
 
--- a/modules/fxpackager/src/main/resources/com/oracle/bundlers/windows/WinMsiBundler.properties	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/resources/com/oracle/bundlers/windows/WinMsiBundler.properties	Mon Mar 17 09:51:09 2014 -0600
@@ -57,3 +57,5 @@
 message.preparing-msi-config=Preparing MSI config\: {0}
 message.generating-msi=Generating MSI\: {0}
 message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut.
+message.light-file-string=\  WiX light tool set to {0}
+message.candle-file-string=\  WiX candle tool set to {0}
--- a/modules/fxpackager/src/main/resources/com/sun/javafx/tools/packager/Bundle.properties	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/resources/com/sun/javafx/tools/packager/Bundle.properties	Mon Mar 17 09:51:09 2014 -0600
@@ -204,5 +204,6 @@
 MSG_BundlerPlatformException=Bundler {0} skipped because the bundler does not support bundling on this platform.
 MSG_BundlerConfigException=Bundler {0} skipped because of a configuration problem\: {1}\
 Advice to fix\: {2}
+MSG_BundlerConfigExceptionNoAdvice=Bundler {0} skipped because of a configuration problem\: {1}
 MSG_BundlerRuntimeException=Bundler {0} failed because of {1}
 MSG_JarNoSelfCopy=Skip jar copy to itself\: {0}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/linux/template.deb.init.script	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,154 @@
+#! /bin/sh
+### BEGIN INIT INFO
+# Provides:          APPLICATION_PACKAGE
+# Required-Start:    
+# Required-Stop:     
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Description:       APPLICATION_DESCRIPTION
+### END INIT INFO
+
+# Do NOT "set -e"
+
+# PATH should only include /usr/* if it runs after the mountnfs.sh script
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+DESC=APPLICATION_DESCRIPTION
+NAME=APPLICATION_PACKAGE
+DAEMON=/opt/APPLICATION_NAME/APPLICATION_LAUNCHER_FILENAME
+DAEMON_ARGS=""
+PIDFILE=/var/run/$NAME.pid
+SCRIPTNAME=/etc/init.d/$NAME
+
+# Exit if the package is not installed
+[ -x "$DAEMON" ] || exit 0
+
+# Read configuration variable file if it is present
+[ -r /etc/default/$NAME ] && . /etc/default/$NAME
+
+# Load the VERBOSE setting and other rcS variables
+. /lib/init/vars.sh
+
+# Define LSB log_* functions.
+# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
+# and status_of_proc is working.
+. /lib/lsb/init-functions
+
+#
+# Function that starts the daemon/service
+#
+# Note that start-stop-daemon runs in the background as native launcher don't detach
+#
+do_start()
+{
+	# Return
+	#   0 if daemon has been started
+	#   1 if daemon was already running
+	#   2 if daemon could not be started
+	start-stop-daemon --start --background --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
+		|| return 1
+	start-stop-daemon --start --background --quiet --pidfile $PIDFILE --exec $DAEMON -- \
+		$DAEMON_ARGS \
+		|| return 2
+	# Add code here, if necessary, that waits for the process to be ready
+	# to handle requests from services started subsequently which depend
+	# on this one.  As a last resort, sleep for some time.
+}
+
+#
+# Function that stops the daemon/service
+#
+do_stop()
+{
+	# Return
+	#   0 if daemon has been stopped
+	#   1 if daemon was already stopped
+	#   2 if daemon could not be stopped
+	#   other if a failure occurred
+	start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
+	RETVAL="$?"
+	[ "$RETVAL" = 2 ] && return 2
+	# Wait for children to finish too if this is a daemon that forks
+	# and if the daemon is only ever run from this initscript.
+	# If the above conditions are not satisfied then add some other code
+	# that waits for the process to drop all resources that could be
+	# needed by services started subsequently.  A last resort is to
+	# sleep for some time.
+	start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
+	[ "$?" = 2 ] && return 2
+	# Many daemons don't delete their pidfiles when they exit.
+	rm -f $PIDFILE
+	return "$RETVAL"
+}
+
+#
+# Function that sends a SIGHUP to the daemon/service
+#
+do_reload() {
+	#
+	# If the daemon can reload its configuration without
+	# restarting (for example, when it is sent a SIGHUP),
+	# then implement that here.
+	#
+	start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
+	return 0
+}
+
+case "$1" in
+  start)
+	[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
+	do_start
+	case "$?" in
+		0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+		2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+	esac
+	;;
+  stop)
+	[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
+	do_stop
+	case "$?" in
+		0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+		2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+	esac
+	;;
+  status)
+       status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
+       ;;
+  #reload|force-reload)
+	#
+	# If do_reload() is not implemented then leave this commented out
+	# and leave 'force-reload' as an alias for 'restart'.
+	#
+	#log_daemon_msg "Reloading $DESC" "$NAME"
+	#do_reload
+	#log_end_msg $?
+	#;;
+  restart|force-reload)
+	#
+	# If the "reload" option is implemented then remove the
+	# 'force-reload' alias
+	#
+	log_daemon_msg "Restarting $DESC" "$NAME"
+	do_stop
+	case "$?" in
+	  0|1)
+		do_start
+		case "$?" in
+			0) log_end_msg 0 ;;
+			1) log_end_msg 1 ;; # Old process is still running
+			*) log_end_msg 1 ;; # Failed to start
+		esac
+		;;
+	  *)
+	  	# Failed to stop
+		log_end_msg 1
+		;;
+	esac
+	;;
+  *)
+	#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
+	echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
+	exit 3
+	;;
+esac
+
+:
--- a/modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/linux/template.postinst	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/linux/template.postinst	Mon Mar 17 09:51:09 2014 -0600
@@ -17,11 +17,28 @@
 # for details, see http://www.debian.org/doc/debian-policy/ or
 # the debian-policy package
 
-
 case "$1" in
     configure)
         echo Adding shortcut to the menu
         cp /opt/APPLICATION_NAME/APPLICATION_NAME.desktop /usr/share/applications/
+
+        if [ "SERVICE_HINT" = "true" ]; then
+            echo Installing daemon
+            cp /opt/APPLICATION_NAME/APPLICATION_NAME.init /etc/init.d/APPLICATION_PACKAGE
+
+            if [ -x "/etc/init.d/APPLICATION_PACKAGE" ]; then
+                update-rc.d APPLICATION_PACKAGE defaults
+
+                if [ "START_ON_INSTALL" = "true" ]; then
+                    if which invoke-rc.d >/dev/null 2>&1; then
+                        invoke-rc.d APPLICATION_PACKAGE start
+                    else
+                        /etc/init.d/APPLICATION_PACKAGE start
+                    fi
+                fi
+	        fi
+
+        fi
     ;;
 
     abort-upgrade|abort-remove|abort-deconfigure)
--- a/modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/linux/template.postrm	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/linux/template.postrm	Mon Mar 17 09:51:09 2014 -0600
@@ -18,11 +18,18 @@
 # for details, see http://www.debian.org/doc/debian-policy/ or
 # the debian-policy package
 
-
 case "$1" in
     purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
         echo Removing shortcut
         rm -f /usr/share/applications/APPLICATION_NAME.desktop
+        if [ "$1" = "purge" ] ; then
+            if [ "SERVICE_HINT" = "true" ]; then
+                echo Uninstalling daemon
+                rm -f /etc/init.d/APPLICATION_PACKAGE
+
+                update-rc.d APPLICATION_PACKAGE remove
+            fi
+        fi
     ;;
 
     *)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/linux/template.rpm.init.script	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,102 @@
+#!/bin/sh
+#
+# APPLICATION_PACKAGE APPLICATION_DESCRIPTION
+#
+# chkconfig:   2345 20 80
+# description: APPLICATION_DESCRIPTION
+#
+
+### BEGIN INIT INFO
+# Provides: 
+# Required-Start: 
+# Required-Stop: 
+# Should-Start: 
+# Should-Stop: 
+# Default-Start: 
+# Default-Stop: 
+# Short-Description: 
+# Description:      
+### END INIT INFO
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+exec=/opt/APPLICATION_NAME/APPLICATION_LAUNCHER_FILENAME
+prog=APPLICATION_LAUNCHER_FILENAME
+
+lockfile=/var/lock/subsys/$prog
+
+start() {
+    [ -x $exec ] || exit 5
+    echo -n $"Starting $prog: "
+    # trailing ampersand backgrounds the process
+    daemon $exec &
+    retval=$?
+    echo
+    [ $retval -eq 0 ] && touch $lockfile
+    return $retval
+}
+
+stop() {
+    echo -n $"Stopping $prog: "
+    killproc $prog
+    retval=$?
+    echo
+    [ $retval -eq 0 ] && rm -f $lockfile
+    return $retval
+}
+
+restart() {
+    stop
+    start
+}
+
+reload() {
+    restart
+}
+
+force_reload() {
+    restart
+}
+
+rh_status() {
+    # run checks to determine if the service is running or use generic status
+    status $prog
+}
+
+rh_status_q() {
+    rh_status >/dev/null 2>&1
+}
+
+
+case "$1" in
+    start)
+        rh_status_q && exit 0
+        $1
+        ;;
+    stop)
+        rh_status_q || exit 0
+        $1
+        ;;
+    restart)
+        $1
+        ;;
+    reload)
+        rh_status_q || exit 7
+        $1
+        ;;
+    force-reload)
+        force_reload
+        ;;
+    status)
+        rh_status
+        ;;
+    condrestart|try-restart)
+        rh_status_q || exit 0
+        restart
+        ;;
+    *)
+        echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
+        exit 2
+esac
+exit $?
--- a/modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/linux/template.spec	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/linux/template.spec	Mon Mar 17 09:51:09 2014 -0600
@@ -36,8 +36,26 @@
 
 %post
 cp /opt/APPLICATION_NAME/APPLICATION_NAME.desktop /usr/share/applications/
+if [ "SERVICE_HINT" = "true" ]; then
+    cp /opt/APPLICATION_NAME/APPLICATION_NAME.init /etc/init.d/APPLICATION_PACKAGE
+    if [ -x "/etc/init.d/APPLICATION_PACKAGE" ]; then
+        /sbin/chkconfig --add APPLICATION_PACKAGE
+        if [ "START_ON_INSTALL" = "true" ]; then
+            /etc/init.d/APPLICATION_PACKAGE start
+        fi
+    fi
+fi
 
 %preun
 rm -f /usr/share/applications/APPLICATION_NAME.desktop
+if [ "SERVICE_HINT" = "true" ]; then
+    if [ -x "/etc/init.d/APPLICATION_PACKAGE" ]; then
+        if [ "STOP_ON_UNINSTALL" = "true" ]; then
+            /etc/init.d/APPLICATION_PACKAGE stop
+        fi
+        /sbin/chkconfig --del APPLICATION_PACKAGE
+        rm -f /etc/init.d/APPLICATION_PACKAGE
+    fi
+fi
 
 %clean
Binary file modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/mac/GenericAppHiDPI.icns has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/mac/MacAppStore.entitlements	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+    <dict>
+        <key>com.apple.security.app-sandbox</key>
+        <true/>
+    </dict>
+</plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/mac/MacAppStore_Inherit.entitlements	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+    <dict>
+        <key>com.apple.security.app-sandbox</key>
+        <true/>
+        <key>com.apple.security.inherit</key>
+        <true/>
+    </dict>
+</plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/mac/launchd.plist.template	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+    <key>Label</key>
+    <string>DEPLOY_DAEMON_IDENTIFIER</string>
+    <key>ProgramArguments</key>
+    <array>
+        <string>DEPLOY_DAEMON_LAUNCHER_PATH</string>
+    </array>
+    <key>RunAtLoad</key><DEPLOY_RUN_AT_LOAD/>
+    <key>KeepAlive</key><DEPLOY_KEEP_ALIVE/>
+</dict>
+</plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/mac/postinstall.template	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,6 @@
+#!/usr/bin/env sh
+
+set -e
+launchctl load "/Library/LaunchDaemons/DEPLOY_LAUNCHD_PLIST_FILE"
+
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/mac/preinstall.template	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,8 @@
+#!/usr/bin/env sh
+
+set -e
+if launchctl list "DEPLOY_DAEMON_IDENTIFIER" &> /dev/null; then
+    launchctl unload "/Library/LaunchDaemons/DEPLOY_LAUNCHD_PLIST_FILE"
+fi
+
+exit 0
--- a/modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/windows/template.iss	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/main/resources/com/sun/javafx/tools/resource/windows/template.iss	Mon Mar 17 09:51:09 2014 -0600
@@ -46,7 +46,11 @@
 Name: "{commondesktop}\APPLICATION_NAME"; Filename: "{app}\APPLICATION_NAME.exe";  IconFilename: "{app}\APPLICATION_NAME.ico"; Check: APPLICATION_DESKTOP_SHORTCUT()
 
 [Run]
-Filename: "{app}\APPLICATION_NAME.exe"; Description: "{cm:LaunchProgram,APPLICATION_NAME}"; Flags: nowait postinstall skipifsilent
+Filename: "{app}\RUN_FILENAME.exe"; Description: "{cm:LaunchProgram,APPLICATION_NAME}"; Flags: nowait postinstall skipifsilent; Check: APPLICATION_NOT_SERVICE()
+Filename: "{app}\RUN_FILENAME.exe"; Parameters: "-install -svcName ""APPLICATION_NAME"" -svcDesc ""APPLICATION_DESCRIPTION"" -mainExe ""APPLICATION_LAUNCHER_FILENAME"" START_ON_INSTALL RUN_AT_STARTUP"; Check: APPLICATION_SERVICE()
+
+[UninstallRun]
+Filename: "{app}\RUN_FILENAME.exe "; Parameters: "-uninstall -svcName APPLICATION_NAME STOP_ON_UNINSTALL"; Check: APPLICATION_SERVICE()
 
 [Code]
 function returnTrue(): Boolean;
--- a/modules/fxpackager/src/test/java/com/oracle/bundlers/BundlersTest.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/test/java/com/oracle/bundlers/BundlersTest.java	Mon Mar 17 09:51:09 2014 -0600
@@ -27,6 +27,8 @@
 
 import com.sun.javafx.tools.packager.bundlers.ConfigException;
 import com.sun.javafx.tools.packager.bundlers.UnsupportedPlatformException;
+import com.sun.javafx.tools.packager.bundlers.WinExeBundler;
+import com.sun.javafx.tools.packager.bundlers.WinMsiBundler;
 import org.junit.AfterClass;
 import org.junit.Test;
 
@@ -236,4 +238,23 @@
         return Arrays.<String>asList(
                 bundlers.stream().map(Bundler::getID).toArray(String[]::new));
     }
+
+    @Test
+    public void customParamFallbackTests() {
+        Map<String, ? super Object> params;
+
+        params = new TreeMap<>();
+        assertTrue(WinMsiBundler.MSI_SYSTEM_WIDE.fetchFrom(params));
+        assertFalse(WinExeBundler.EXE_SYSTEM_WIDE.fetchFrom(params));
+
+        params = new TreeMap<>();
+        params.put(StandardBundlerParam.SYSTEM_WIDE.getID(), "false");
+        assertFalse(WinMsiBundler.MSI_SYSTEM_WIDE.fetchFrom(params));
+        assertFalse(WinExeBundler.EXE_SYSTEM_WIDE.fetchFrom(params));
+
+        params = new TreeMap<>();
+        params.put(StandardBundlerParam.SYSTEM_WIDE.getID(), "true");
+        assertTrue(WinMsiBundler.MSI_SYSTEM_WIDE.fetchFrom(params));
+        assertTrue(WinExeBundler.EXE_SYSTEM_WIDE.fetchFrom(params));
+    }
 }
--- a/modules/fxpackager/src/test/java/com/oracle/bundlers/linux/LinuxAppBundlerTest.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/test/java/com/oracle/bundlers/linux/LinuxAppBundlerTest.java	Mon Mar 17 09:51:09 2014 -0600
@@ -26,7 +26,6 @@
 package com.oracle.bundlers.linux;
 
 import com.oracle.bundlers.Bundler;
-import com.oracle.bundlers.StandardBundlerParam;
 import com.sun.javafx.tools.packager.Log;
 import com.sun.javafx.tools.packager.bundlers.ConfigException;
 import com.sun.javafx.tools.packager.bundlers.LinuxAppBundler;
@@ -44,6 +43,7 @@
 import java.nio.file.Files;
 import java.util.*;
 
+import static com.oracle.bundlers.StandardBundlerParam.*;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -116,8 +116,7 @@
     @Test
     public void smokeTest() throws IOException, ConfigException, UnsupportedPlatformException {
         Bundler bundler = new LinuxAppBundler();
-        ((LinuxAppBundler)bundler).setVerbose(true);
-        
+
         assertNotNull(bundler.getName());
         assertNotNull(bundler.getID());
         assertNotNull(bundler.getDescription());
@@ -125,11 +124,12 @@
 
         Map<String, Object> bundleParams = new HashMap<>();
 
-        bundleParams.put(StandardBundlerParam.BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 
-        bundleParams.put(StandardBundlerParam.NAME.getID(), "Smoke");
-        bundleParams.put(StandardBundlerParam.MAIN_CLASS.getID(), "hello.TestPackager");
-        bundleParams.put(StandardBundlerParam.APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(APP_NAME.getID(), "Smoke");
+        bundleParams.put(MAIN_CLASS.getID(), "hello.TestPackager");
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(VERBOSE.getID(), true);
         
         File output = bundler.execute(bundleParams, new File(workDir, "smoke"));
         validatePackageCfg(output);
@@ -147,13 +147,12 @@
     @Test
     public void minimumConfig() throws IOException, ConfigException, UnsupportedPlatformException {
         Bundler bundler = new LinuxAppBundler();
-        ((LinuxAppBundler)bundler).setVerbose(true);
 
         Map<String, Object> bundleParams = new HashMap<>();
 
-        bundleParams.put(StandardBundlerParam.BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 
-        bundleParams.put(StandardBundlerParam.APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 
         File output = bundler.execute(bundleParams, new File(workDir, "BareMinimum"));
         validatePackageCfg(output);
--- a/modules/fxpackager/src/test/java/com/oracle/bundlers/linux/LinuxRpmBundlerTest.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/test/java/com/oracle/bundlers/linux/LinuxRpmBundlerTest.java	Mon Mar 17 09:51:09 2014 -0600
@@ -26,7 +26,6 @@
 package com.oracle.bundlers.linux;
 
 import com.oracle.bundlers.Bundler;
-import com.oracle.bundlers.StandardBundlerParam;
 import com.sun.javafx.tools.packager.Log;
 import com.sun.javafx.tools.packager.bundlers.ConfigException;
 import com.sun.javafx.tools.packager.bundlers.LinuxRPMBundler;
@@ -43,7 +42,9 @@
 import java.nio.file.Files;
 import java.util.*;
 
+import static com.oracle.bundlers.StandardBundlerParam.*;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 public class LinuxRpmBundlerTest {
 
@@ -65,7 +66,7 @@
 
         retain = Boolean.parseBoolean(System.getProperty("RETAIN_PACKAGER_TESTS"));
 
-        workDir = new File("build/tmp/tests", "linuxrrpm");
+        workDir = new File("build/tmp/tests", "linuxrpm");
         appResourcesDir = new File("build/tmp/tests", "appResources");
         fakeMainJar = new File(appResourcesDir, "mainApp.jar");
 
@@ -115,8 +116,7 @@
     @Test
     public void smokeTest() throws IOException, ConfigException, UnsupportedPlatformException {
         Bundler bundler = new LinuxRPMBundler();
-        ((LinuxRPMBundler)bundler).setVerbose(true);
-        
+
         assertNotNull(bundler.getName());
         assertNotNull(bundler.getID());
         assertNotNull(bundler.getDescription());
@@ -124,14 +124,18 @@
 
         Map<String, Object> bundleParams = new HashMap<>();
 
-        bundleParams.put(StandardBundlerParam.BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 
-        bundleParams.put(StandardBundlerParam.NAME.getID(), "Smoke");
-        bundleParams.put(StandardBundlerParam.MAIN_CLASS.getID(), "hello.TestPackager");
-        bundleParams.put(StandardBundlerParam.APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(APP_NAME.getID(), "Smoke");
+        bundleParams.put(MAIN_CLASS.getID(), "hello.TestPackager");
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(VERBOSE.getID(), true);
 
         bundler.validate(bundleParams);
-        bundler.execute(bundleParams, new File(workDir, "smoke"));
+        File result = bundler.execute(bundleParams, new File(workDir, "smoke"));
+        System.err.println("Bundle at - " + result);
+        assertNotNull(result);
+        assertTrue(result.exists());
     }
 
     /**
@@ -146,18 +150,18 @@
     @Test
     public void minimumConfig() throws IOException, ConfigException, UnsupportedPlatformException {
         Bundler bundler = new LinuxRPMBundler();
-        ((LinuxRPMBundler)bundler).setVerbose(true);
 
         Map<String, Object> bundleParams = new HashMap<>();
 
-        bundleParams.put(StandardBundlerParam.BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 
-        bundleParams.put(StandardBundlerParam.APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 
         bundler.validate(bundleParams);
         File output = bundler.execute(bundleParams, new File(workDir, "BareMinimum"));
-        System.err.println("Bundle written to " + output);
-        Assume.assumeTrue(output.isFile());
+        System.err.println("Bundle at - " + output);
+        assertNotNull(output);
+        assertTrue(output.exists());
     }
 
 }
--- a/modules/fxpackager/src/test/java/com/oracle/bundlers/mac/MacAppBundlerTest.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/test/java/com/oracle/bundlers/mac/MacAppBundlerTest.java	Mon Mar 17 09:51:09 2014 -0600
@@ -27,7 +27,6 @@
 
 import com.oracle.bundlers.AbstractBundler;
 import com.oracle.bundlers.Bundler;
-import com.oracle.bundlers.StandardBundlerParam;
 import com.sun.javafx.tools.packager.Log;
 import com.sun.javafx.tools.packager.bundlers.*;
 import org.junit.After;
@@ -41,6 +40,8 @@
 import java.nio.file.Files;
 import java.util.*;
 
+import static com.oracle.bundlers.StandardBundlerParam.*;
+import static com.sun.javafx.tools.packager.bundlers.MacAppBundler.MAC_CF_BUNDLE_NAME;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -112,7 +113,6 @@
     @Test
     public void smokeTest() throws IOException, ConfigException, UnsupportedPlatformException {
         AbstractBundler bundler = new MacAppBundler();
-        bundler.setVerbose(true);
 
         assertNotNull(bundler.getName());
         assertNotNull(bundler.getID());
@@ -121,19 +121,21 @@
 
         Map<String, Object> bundleParams = new HashMap<>();
 
-        bundleParams.put(StandardBundlerParam.BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 
-        bundleParams.put(StandardBundlerParam.NAME.getID(), "Smoke");
-        bundleParams.put(StandardBundlerParam.MAIN_CLASS.getID(), "hello.TestPackager");
-        bundleParams.put(StandardBundlerParam.APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
-//        bundleParams.put(StandardBundlerParam.MAIN_JAR.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(APP_NAME.getID(), "Smoke Test App");
+        bundleParams.put(MAC_CF_BUNDLE_NAME.getID(), "Smoke");
+        bundleParams.put(MAIN_CLASS.getID(), "hello.TestPackager");
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(VERBOSE.getID(), true);
 
-        bundleParams.put(StandardBundlerParam.RUNTIME.getID(),
-                StandardBundlerParam.extractJreAsRelativeFileSet("~/tools/jdk1.8.0.jdk/Contents/Home/jre"));
         boolean valid = bundler.validate(bundleParams);
         assertTrue(valid);
 
-        bundler.execute(bundleParams, new File(workDir, "smoke"));
+        File result = bundler.execute(bundleParams, new File(workDir, "smoke"));
+        System.err.println("Bundle at - " + result);
+        assertNotNull(result);
+        assertTrue(result.exists());
     }
 
     /**
@@ -148,18 +150,17 @@
     @Test
     public void minimumConfig() throws IOException, ConfigException, UnsupportedPlatformException {
         Bundler bundler = new MacAppBundler();
-        ((AbstractBundler)bundler).setVerbose(true);
 
         Map<String, Object> bundleParams = new HashMap<>();
 
         // not part of the typical setup, for testing
-        bundleParams.put(StandardBundlerParam.BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 
-        bundleParams.put(StandardBundlerParam.NAME.getID(), "Smoke");
-        bundleParams.put(StandardBundlerParam.APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
-
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 
         File output = bundler.execute(bundleParams, new File(workDir, "BareMinimum"));
-        assertTrue(output.isDirectory());
+        System.err.println("Bundle at - " + output);
+        assertNotNull(output);
+        assertTrue(output.exists());
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/test/java/com/oracle/bundlers/mac/MacAppStoreBundlerTest.java	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2014, 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.oracle.bundlers.mac;
+
+import com.oracle.bundlers.AbstractBundler;
+import com.sun.javafx.tools.packager.Log;
+import com.sun.javafx.tools.packager.bundlers.*;
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.util.*;
+
+import static com.oracle.bundlers.StandardBundlerParam.*;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class MacAppStoreBundlerTest {
+
+    static File tmpBase;
+    static File workDir;
+    static File appResourcesDir;
+    static File fakeMainJar;
+    static Set<File> appResources;
+    static boolean retain = false;
+
+    @BeforeClass
+    public static void prepareApp() throws IOException {
+        // only run on mac
+        Assume.assumeTrue(System.getProperty("os.name").toLowerCase().contains("os x"));
+
+        // make sure we have a default signing key
+        String signingKeyName = MacAppStoreBundler.MAC_APP_STORE_APP_SIGNING_KEY.fetchFrom(new TreeMap<>());
+        Assume.assumeNotNull(signingKeyName);
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(baos)) {
+            System.err.println("Checking for valid certificate");
+            ProcessBuilder pb = new ProcessBuilder(
+                    "security",
+                    "find-certificate", "-c", signingKeyName);
+
+            IOUtils.exec(pb, Log.isDebug(), false, ps);
+
+            String commandOutput = baos.toString();
+            Assume.assumeTrue(commandOutput.contains(signingKeyName));
+            System.err.println("Valid certificate present");
+        }
+
+
+        Log.setLogger(new Log.Logger(true));
+
+        retain = Boolean.parseBoolean(System.getProperty("RETAIN_PACKAGER_TESTS"));
+
+        workDir = new File("build/tmp/tests", "macappstore");
+        appResourcesDir = new File("build/tmp/tests", "appResources");
+        fakeMainJar = new File(appResourcesDir, "mainApp.jar");
+
+        appResources = new HashSet<>(Arrays.asList(fakeMainJar));
+    }
+
+    @Before
+    public void createTmpDir() throws IOException {
+        if (retain) {
+            tmpBase = new File("build/tmp/tests/macappstore");
+        } else {
+            tmpBase = Files.createTempDirectory("fxpackagertests").toFile();
+        }
+        tmpBase.mkdir();
+    }
+
+    @After
+    public void maybeCleanupTmpDir() {
+        if (!retain) {
+            attemptDelete(tmpBase);
+        }
+    }
+
+    private void attemptDelete(File tmpBase) {
+        if (tmpBase.isDirectory()) {
+            File[] children = tmpBase.listFiles();
+            if (children != null) {
+                for (File f : children) {
+                    attemptDelete(f);
+                }
+            }
+        }
+        boolean success;
+        try {
+            success = !tmpBase.exists() || tmpBase.delete();
+        } catch (SecurityException se) {
+            success = false;
+        }
+        if (!success) {
+            System.err.println("Could not clean up " + tmpBase.toString());
+        }
+    }
+
+    @Test
+    public void showSigningKeyNames() {
+        System.err.println(MacAppStoreBundler.MAC_APP_STORE_SIGNING_KEY_USER.fetchFrom(new TreeMap<>()));
+        System.err.println(MacAppStoreBundler.MAC_APP_STORE_APP_SIGNING_KEY.fetchFrom(new TreeMap<>()));
+    }
+
+    /**
+     * See if smoke comes out
+     */
+    @Test
+    public void smokeTest() throws IOException, ConfigException, UnsupportedPlatformException {
+        AbstractBundler bundler = new MacAppStoreBundler();
+
+        assertNotNull(bundler.getName());
+        assertNotNull(bundler.getID());
+        assertNotNull(bundler.getDescription());
+
+        Map<String, Object> bundleParams = new HashMap<>();
+
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
+
+        bundleParams.put(APP_NAME.getID(), "Smoke");
+        bundleParams.put(MAIN_CLASS.getID(), "hello.TestPackager");
+        bundleParams.put(IDENTIFIER.getID(), "com.example.javapacakger.hello.TestPackager");
+        bundleParams.put(MacAppBundler.MAC_CATEGORY.getID(), "public.app-category.developer-tools");
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(VERBOSE.getID(), true);
+
+        File result = bundler.execute(bundleParams, new File(workDir, "smoke"));
+        System.err.println("Bundle at - " + result);
+        assertNotNull(result);
+        assertTrue(result.exists());
+    }
+
+//    /**
+//     * The bare minimum configuration needed to make it work
+//     * <ul>
+//     *     <li>Where to build it</li>
+//     *     <li>The jar containing the application (with a main-class attribute)</li>
+//     * </ul>
+//     *
+//     * All other values will be driven off of those two values.
+//     */
+//    @Test
+//    public void minimumConfig() throws IOException, ConfigException, UnsupportedPlatformException {
+//        Bundler bundler = new MacAppStoreBundler();
+//
+//        Map<String, Object> bundleParams = new HashMap<>();
+//
+//        bundleParams.put(StandardBundlerParam.BUILD_ROOT.getID(), tmpBase);
+//
+//        bundleParams.put(StandardBundlerParam.APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+//
+//        File output = bundler.execute(bundleParams, new File(workDir, "BareMinimum"));
+//        System.err.println("Bundle written to " + output);
+//        assertTrue(output.isFile());
+//        //TODO assert file name
+//    }
+}
--- a/modules/fxpackager/src/test/java/com/oracle/bundlers/mac/MacDMGBundlerTest.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/test/java/com/oracle/bundlers/mac/MacDMGBundlerTest.java	Mon Mar 17 09:51:09 2014 -0600
@@ -27,7 +27,6 @@
 
 import com.oracle.bundlers.AbstractBundler;
 import com.oracle.bundlers.Bundler;
-import com.oracle.bundlers.StandardBundlerParam;
 import com.sun.javafx.tools.packager.Log;
 import com.sun.javafx.tools.packager.bundlers.*;
 import org.junit.After;
@@ -41,6 +40,7 @@
 import java.nio.file.Files;
 import java.util.*;
 
+import static com.oracle.bundlers.StandardBundlerParam.*;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -112,7 +112,6 @@
     @Test
     public void smokeTest() throws IOException, ConfigException, UnsupportedPlatformException {
         AbstractBundler bundler = new MacDMGBundler();
-        bundler.setVerbose(true);
 
         assertNotNull(bundler.getName());
         assertNotNull(bundler.getID());
@@ -121,17 +120,22 @@
 
         Map<String, Object> bundleParams = new HashMap<>();
 
-        bundleParams.put(StandardBundlerParam.BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 
-        bundleParams.put(StandardBundlerParam.NAME.getID(), "Smoke");
-        bundleParams.put(StandardBundlerParam.MAIN_CLASS.getID(), "hello.TestPackager");
-        bundleParams.put(StandardBundlerParam.APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(APP_NAME.getID(), "Smoke");
+        bundleParams.put(MAIN_CLASS.getID(), "hello.TestPackager");
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(VERBOSE.getID(), true);
 //        bundleParams.put(StandardBundlerParam.MAIN_JAR.getID(), new RelativeFileSet(appResourcesDir, appResources));
 
-        bundleParams.put(StandardBundlerParam.RUNTIME.getID(),
-                StandardBundlerParam.extractJreAsRelativeFileSet("~/tools/jdk1.8.0.jdk/Contents/Home/jre"));
+//        bundleParams.put(StandardBundlerParam.RUNTIME.getID(),
+//                JreUtils.extractJreAsRelativeFileSet(MacAppBundler.adjustMacRuntimePath(System.getProperty("java.home")),
+//                        MacAppBundler.macJDKRules));
 
-        bundler.execute(bundleParams, new File(workDir, "smoke"));
+        File result = bundler.execute(bundleParams, new File(workDir, "smoke"));
+        System.err.println("Bundle at - " + result);
+        assertNotNull(result);
+        assertTrue(result.exists());
     }
 
     /**
@@ -146,18 +150,16 @@
     @Test
     public void minimumConfig() throws IOException, ConfigException, UnsupportedPlatformException {
         Bundler bundler = new MacDMGBundler();
-        ((AbstractBundler)bundler).setVerbose(true);
 
         Map<String, Object> bundleParams = new HashMap<>();
 
-        bundleParams.put(StandardBundlerParam.BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 
-        bundleParams.put(StandardBundlerParam.NAME.getID(), "Smoke");
-        bundleParams.put(StandardBundlerParam.APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 
         File output = bundler.execute(bundleParams, new File(workDir, "BareMinimum"));
-        System.err.println("Bundle written to " + output);
-        assertTrue(output.isFile());
-        //TODO assert file name
+        System.err.println("Bundle at - " + output);
+        assertNotNull(output);
+        assertTrue(output.exists());
     }
 }
--- a/modules/fxpackager/src/test/java/com/oracle/bundlers/mac/MacPKGBundlerTest.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/test/java/com/oracle/bundlers/mac/MacPKGBundlerTest.java	Mon Mar 17 09:51:09 2014 -0600
@@ -40,6 +40,7 @@
 import java.nio.file.Files;
 import java.util.*;
 
+import static com.oracle.bundlers.StandardBundlerParam.*;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -111,7 +112,6 @@
     @Test
     public void smokeTest() throws IOException, ConfigException, UnsupportedPlatformException {
         AbstractBundler bundler = new MacPKGBundler();
-        bundler.setVerbose(true);
 
         assertNotNull(bundler.getName());
         assertNotNull(bundler.getID());
@@ -120,16 +120,21 @@
 
         Map<String, Object> bundleParams = new HashMap<>();
 
-        bundleParams.put(StandardBundlerParam.BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 
-        bundleParams.put(StandardBundlerParam.NAME.getID(), "Smoke");
-        bundleParams.put(StandardBundlerParam.MAIN_CLASS.getID(), "hello.TestPackager");
-        bundleParams.put(StandardBundlerParam.APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(APP_NAME.getID(), "Smoke");
+        bundleParams.put(MAIN_CLASS.getID(), "hello.TestPackager");
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(VERBOSE.getID(), true);
 
-        bundleParams.put(StandardBundlerParam.RUNTIME.getID(),
-                StandardBundlerParam.extractJreAsRelativeFileSet("~/tools/jdk1.8.0.jdk/Contents/Home/jre"));
+//        bundleParams.put(StandardBundlerParam.RUNTIME.getID(),
+//                JreUtils.extractJreAsRelativeFileSet(MacAppBundler.adjustMacRuntimePath(System.getProperty("java.home")),
+//                        MacAppBundler.macJDKRules));
 
-        bundler.execute(bundleParams, new File(workDir, "smoke"));
+        File result = bundler.execute(bundleParams, new File(workDir, "smoke"));
+        System.err.println("Bundle at - " + result);
+        assertNotNull(result);
+        assertTrue(result.exists());
     }
 
     /**
@@ -144,18 +149,16 @@
     @Test
     public void minimumConfig() throws IOException, ConfigException, UnsupportedPlatformException {
         Bundler bundler = new MacPKGBundler();
-        ((AbstractBundler)bundler).setVerbose(true);
 
         Map<String, Object> bundleParams = new HashMap<>();
 
-        bundleParams.put(StandardBundlerParam.BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 
-        bundleParams.put(StandardBundlerParam.NAME.getID(), "Smoke");
-        bundleParams.put(StandardBundlerParam.APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 
         File output = bundler.execute(bundleParams, new File(workDir, "BareMinimum"));
-        System.err.println(output);
-        assertTrue(output.isFile());
-        //TODO assert file name
+        System.err.println("Bundle at - " + output);
+        assertNotNull(output);
+        assertTrue(output.exists());
     }
 }
--- a/modules/fxpackager/src/test/java/com/oracle/bundlers/windows/WinAppBundlerTest.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/test/java/com/oracle/bundlers/windows/WinAppBundlerTest.java	Mon Mar 17 09:51:09 2014 -0600
@@ -26,7 +26,6 @@
 package com.oracle.bundlers.windows;
 
 import com.oracle.bundlers.Bundler;
-import com.oracle.bundlers.StandardBundlerParam;
 import com.sun.javafx.tools.packager.Log;
 import com.sun.javafx.tools.packager.bundlers.ConfigException;
 import com.sun.javafx.tools.packager.bundlers.RelativeFileSet;
@@ -44,6 +43,7 @@
 import java.nio.file.Files;
 import java.util.*;
 
+import static com.oracle.bundlers.StandardBundlerParam.*;
 import static org.junit.Assert.*;
 
 public class WinAppBundlerTest {
@@ -114,8 +114,7 @@
     @Test
     public void smokeTest() throws IOException, ConfigException, UnsupportedPlatformException {
         Bundler bundler = new WinAppBundler();
-        ((WinAppBundler)bundler).setVerbose(true);
-        
+
         assertNotNull(bundler.getName());
         assertNotNull(bundler.getID());
         assertNotNull(bundler.getDescription());
@@ -123,11 +122,12 @@
 
         Map<String, Object> bundleParams = new HashMap<>();
 
-        bundleParams.put(StandardBundlerParam.BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 
-        bundleParams.put(StandardBundlerParam.NAME.getID(), "Smoke");
-        bundleParams.put(StandardBundlerParam.MAIN_CLASS.getID(), "hello.TestPackager");
-        bundleParams.put(StandardBundlerParam.APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(APP_NAME.getID(), "Smoke");
+        bundleParams.put(MAIN_CLASS.getID(), "hello.TestPackager");
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(VERBOSE.getID(), true);
 
         File output = bundler.execute(bundleParams, new File(workDir, "smoke"));
         validatePackageCfg(output);
@@ -145,13 +145,12 @@
     @Test
     public void minimumConfig() throws IOException, ConfigException, UnsupportedPlatformException {
         Bundler bundler = new WinAppBundler();
-        ((WinAppBundler)bundler).setVerbose(true);
 
         Map<String, Object> bundleParams = new HashMap<>();
 
-        bundleParams.put(StandardBundlerParam.BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 
-        bundleParams.put(StandardBundlerParam.APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 
         File output = bundler.execute(bundleParams, new File(workDir, "BareMinimum"));
         validatePackageCfg(output);
--- a/modules/fxpackager/src/test/java/com/oracle/bundlers/windows/WinEXEBundlerTest.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/test/java/com/oracle/bundlers/windows/WinEXEBundlerTest.java	Mon Mar 17 09:51:09 2014 -0600
@@ -26,7 +26,6 @@
 package com.oracle.bundlers.windows;
 
 import com.oracle.bundlers.Bundler;
-import com.oracle.bundlers.StandardBundlerParam;
 import com.sun.javafx.tools.packager.Log;
 import com.sun.javafx.tools.packager.bundlers.ConfigException;
 import com.sun.javafx.tools.packager.bundlers.RelativeFileSet;
@@ -43,7 +42,10 @@
 import java.nio.file.Files;
 import java.util.*;
 
+import static com.oracle.bundlers.StandardBundlerParam.*;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 public class WinEXEBundlerTest {
 
@@ -116,8 +118,7 @@
     @Test
     public void smokeTest() throws IOException, ConfigException, UnsupportedPlatformException {
         Bundler bundler = new WinExeBundler();
-        ((WinExeBundler)bundler).setVerbose(true);
-        
+
         assertNotNull(bundler.getName());
         assertNotNull(bundler.getID());
         assertNotNull(bundler.getDescription());
@@ -125,13 +126,17 @@
 
         Map<String, Object> bundleParams = new HashMap<>();
 
-        bundleParams.put(StandardBundlerParam.BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 
-        bundleParams.put(StandardBundlerParam.NAME.getID(), "Smoke");
-        bundleParams.put(StandardBundlerParam.MAIN_CLASS.getID(), "hello.TestPackager");
-        bundleParams.put(StandardBundlerParam.APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(APP_NAME.getID(), "Smoke");
+        bundleParams.put(MAIN_CLASS.getID(), "hello.TestPackager");
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(VERBOSE.getID(), true);
 
-        bundler.execute(bundleParams, new File(workDir, "smoke"));
+        File result = bundler.execute(bundleParams, new File(workDir, "smoke"));
+        System.err.println("Bundle at - " + result);
+        assertNotNull(result);
+        assertTrue(result.exists());
     }
 
     /**
@@ -146,18 +151,56 @@
     @Test
     public void minimumConfig() throws IOException, ConfigException, UnsupportedPlatformException {
         Bundler bundler = new WinExeBundler();
-        ((WinExeBundler)bundler).setVerbose(true);
 
         Map<String, Object> bundleParams = new HashMap<>();
 
-        bundleParams.put(StandardBundlerParam.BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 
-        bundleParams.put(StandardBundlerParam.APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 
         File output = bundler.execute(bundleParams, new File(workDir, "BareMinimum"));
-        System.err.println(output);
-        //assertTrue(output.isFile());
-        Assume.assumeTrue(output.isFile());
+        System.err.println("Bundle at - " + output);
+        assertNotNull(output);
+        assertTrue(output.exists());
     }
 
+    /**
+     * Test a misconfiguration where the iscc tools are misconfigured.
+     */
+    @Test
+    public void configISSCBad() throws IOException, ConfigException, UnsupportedPlatformException {
+        Bundler bundler = new WinExeBundler();
+
+        Map<String, Object> bundleParams = new HashMap<>();
+
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(VERBOSE.getID(), true);
+
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+
+        bundleParams.put(WinExeBundler.TOOL_INNO_SETUP_COMPILER_EXECUTABLE.getID(), "c:\\MissingTool.exe");
+
+        File output = bundler.execute(bundleParams, new File(workDir, "BadISSC"));
+        assertNull(output);
+    }
+
+    /**
+     * Test a misconfiguration where the iscc tools are misconfigured.
+     */
+    @Test
+    public void configISSCNull() throws IOException, ConfigException, UnsupportedPlatformException {
+        Bundler bundler = new WinExeBundler();
+
+        Map<String, Object> bundleParams = new HashMap<>();
+
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(VERBOSE.getID(), true);
+
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+
+        bundleParams.put(WinExeBundler.TOOL_INNO_SETUP_COMPILER_EXECUTABLE.getID(), null);
+
+        File output = bundler.execute(bundleParams, new File(workDir, "NullISSC"));
+        assertNull(output);
+    }
 }
--- a/modules/fxpackager/src/test/java/com/oracle/bundlers/windows/WinMSIBundlerTest.java	Mon Mar 17 16:56:40 2014 +0400
+++ b/modules/fxpackager/src/test/java/com/oracle/bundlers/windows/WinMSIBundlerTest.java	Mon Mar 17 09:51:09 2014 -0600
@@ -26,7 +26,6 @@
 package com.oracle.bundlers.windows;
 
 import com.oracle.bundlers.Bundler;
-import com.oracle.bundlers.StandardBundlerParam;
 import com.sun.javafx.tools.packager.Log;
 import com.sun.javafx.tools.packager.bundlers.ConfigException;
 import com.sun.javafx.tools.packager.bundlers.RelativeFileSet;
@@ -43,7 +42,9 @@
 import java.nio.file.Files;
 import java.util.*;
 
+import static com.oracle.bundlers.StandardBundlerParam.*;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 public class WinMSIBundlerTest {
@@ -118,8 +119,7 @@
     @Test
     public void smokeTest() throws IOException, ConfigException, UnsupportedPlatformException {
         Bundler bundler = new WinMsiBundler();
-        ((WinMsiBundler)bundler).setVerbose(true);
-        
+
         assertNotNull(bundler.getName());
         assertNotNull(bundler.getID());
         assertNotNull(bundler.getDescription());
@@ -127,13 +127,17 @@
 
         Map<String, Object> bundleParams = new HashMap<>();
 
-        bundleParams.put(StandardBundlerParam.BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
         
-        bundleParams.put(StandardBundlerParam.NAME.getID(), "Smoke");
-        bundleParams.put(StandardBundlerParam.MAIN_CLASS.getID(), "hello.TestPackager");
-        bundleParams.put(StandardBundlerParam.APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(APP_NAME.getID(), "Smoke");
+        bundleParams.put(MAIN_CLASS.getID(), "hello.TestPackager");
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(VERBOSE.getID(), true);
 
-        bundler.execute(bundleParams, new File(workDir, "smoke"));
+        File result = bundler.execute(bundleParams, new File(workDir, "smoke"));
+        System.err.println("Bundle at - " + result);
+        assertNotNull(result);
+        assertTrue(result.exists());
     }
 
     /**
@@ -148,17 +152,98 @@
     @Test
     public void minimumConfig() throws IOException, ConfigException, UnsupportedPlatformException {
         Bundler bundler = new WinMsiBundler();
-        ((WinMsiBundler)bundler).setVerbose(true);
 
         Map<String, Object> bundleParams = new HashMap<>();
 
-        bundleParams.put(StandardBundlerParam.BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 
-        bundleParams.put(StandardBundlerParam.APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 
         File output = bundler.execute(bundleParams, new File(workDir, "BareMinimum"));
-        System.err.println("Bundle written to " + output);
-        assertTrue(output.isFile());
+        System.err.println("Bundle at - " + output);
+        assertNotNull(output);
+        assertTrue(output.exists());
     }
 
+    /**
+     * Test a misconfiguration where the wix tools are misconfigured.
+     */
+    @Test
+    public void configLightBad() throws IOException, ConfigException, UnsupportedPlatformException {
+        Bundler bundler = new WinMsiBundler();
+
+        Map<String, Object> bundleParams = new HashMap<>();
+
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(VERBOSE.getID(), true);
+
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+
+        bundleParams.put(WinMsiBundler.TOOL_LIGHT_EXECUTABLE.getID(), "c:\\MissingTool.exe");
+
+        File output = bundler.execute(bundleParams, new File(workDir, "BadLight"));
+        assertNull(output);
+    }
+
+    /**
+     * Test a misconfiguration where the wix tools are misconfigured.
+     */
+    @Test
+    public void configLightNull() throws IOException, ConfigException, UnsupportedPlatformException {
+        Bundler bundler = new WinMsiBundler();
+
+        Map<String, Object> bundleParams = new HashMap<>();
+
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(VERBOSE.getID(), true);
+
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+
+        bundleParams.put(WinMsiBundler.TOOL_LIGHT_EXECUTABLE.getID(), null);
+
+        File output = bundler.execute(bundleParams, new File(workDir, "NullLight"));
+        assertNull(output);
+    }
+
+    /**
+     * Test a misconfiguration where the Wix tools are misconfigured.
+     */
+    @Test
+    public void configCandleBad() throws IOException, ConfigException, UnsupportedPlatformException {
+        Bundler bundler = new WinMsiBundler();
+
+        Map<String, Object> bundleParams = new HashMap<>();
+
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(VERBOSE.getID(), true);
+
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+
+        bundleParams.put(WinMsiBundler.TOOL_CANDLE_EXECUTABLE.getID(), "c:\\MissingTool.exe");
+
+        File output = bundler.execute(bundleParams, new File(workDir, "BadCandle"));
+        assertNull(output);
+    }
+
+    /**
+     * Test a misconfiguration where the wix tools are misconfigured.
+     */
+    @Test
+    public void configCandleNull() throws IOException, ConfigException, UnsupportedPlatformException {
+        Bundler bundler = new WinMsiBundler();
+
+        Map<String, Object> bundleParams = new HashMap<>();
+
+        bundleParams.put(BUILD_ROOT.getID(), tmpBase);
+        bundleParams.put(VERBOSE.getID(), true);
+
+        bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
+
+        bundleParams.put(WinMsiBundler.TOOL_CANDLE_EXECUTABLE.getID(), null);
+
+        File output = bundler.execute(bundleParams, new File(workDir, "NullCandle"));
+        assertNull(output);
+    }
+
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/fxpackager/src/test/java/com/sun/javafx/tools/packager/CLITest.java	Mon Mar 17 09:51:09 2014 -0600
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2014, 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 org.junit.*;
+
+import java.io.File;
+
+public class CLITest {
+
+    static File tmpBase;
+    static File workDir;
+    static File appResourcesDir;
+    static File fakeMainJar;
+
+    @BeforeClass
+    public static void prepareApp() {
+        Log.setLogger(new Log.Logger(true));
+
+        String tmpBasePath = System.getProperty("tmpBase");
+        if (tmpBasePath != null) {
+            tmpBase = new File(System.getProperty("tmpBase"));
+        } else {
+            tmpBase = new File("build/tmp/tests");
+        }
+        tmpBase.mkdirs();
+
+        workDir = new File(tmpBase, "cliapp");
+        appResourcesDir = new File(tmpBase, "appResources");
+        fakeMainJar = new File(appResourcesDir, "mainApp.jar");
+    }
+
+    @Test
+    public void simpleTest() throws Exception {
+        com.sun.javafx.tools.packager.Main.main("-deploy",
+                "-verbose", // verbose is required or test will call System.exit() on failures and break the build
+                "-srcfiles", fakeMainJar.getCanonicalPath(),
+                "-outdir", workDir.getCanonicalPath(),
+                "-outfile", "SimpleTest",
+                "-appclass", "hello.TestPackager",
+                "-native",
+                "-name", "SimpleTest");
+    }
+
+    @Test(expected = PackagerException.class)
+    public void duplicateNameClash() throws Exception {
+        com.sun.javafx.tools.packager.Main.main("-deploy",
+                "-verbose", // verbose is required or test will call System.exit() on failures and break the build
+                "-srcfiles", fakeMainJar.getCanonicalPath(),
+                "-outdir", workDir.getCanonicalPath(),
+                "-outfile", "SimpleTest",
+                "-appclass", "hello.TestPackager",
+                "-native",
+                "-name", "SimpleTest",
+                "-Bname=DuplicateTest");
+    }
+
+    @Test(expected = PackagerException.class)
+    public void duplicateNameMatch() throws Exception {
+        com.sun.javafx.tools.packager.Main.main("-deploy",
+                "-verbose", // verbose is required or test will call System.exit() on failures and break the build
+                "-srcfiles", fakeMainJar.getCanonicalPath(),
+                "-outdir", workDir.getCanonicalPath(),
+                "-outfile", "SimpleTest",
+                "-appclass", "hello.TestPackager",
+                "-native",
+                "-name", "SimpleTest",
+                "-Bname=SimpleTest");
+    }
+
+}