changeset 14810:b31d3940b546

jlink API rework and option reduction. Introduced vm and strip-debug options. help and xhelp to split main and extended options.
author jfdenise
date Mon, 21 Dec 2015 14:42:19 +0100
parents 74110610664b
children 4521cd38a834
files make/launcher/Launcher-jdk.jlink.gmk src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage.properties src/jdk.jlink/share/classes/jdk/tools/jlink/Jlink.java src/jdk.jlink/share/classes/jdk/tools/jlink/JlinkPermission.java src/jdk.jlink/share/classes/jdk/tools/jlink/JlinkTask.java src/jdk.jlink/share/classes/jdk/tools/jlink/Main.java src/jdk.jlink/share/classes/jdk/tools/jlink/TaskHelper.java src/jdk.jlink/share/classes/jdk/tools/jlink/api/Jlink.java src/jdk.jlink/share/classes/jdk/tools/jlink/api/JlinkPermission.java src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/Plugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/PluginException.java src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/builder/DefaultImageBuilder.java src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/builder/DefaultImageBuilderProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/builder/ExecutableImage.java src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/builder/ImageBuilder.java src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/postprocessor/PostProcessorPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/transformer/Pool.java src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/transformer/TransformerPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java src/jdk.jlink/share/classes/jdk/tools/jlink/builder/ImageBuilder.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JvmHandler.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Main.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/PluginRepository.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/PoolImpl.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePrevisitor.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Utils.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultCompressPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeFilesPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludePlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeVMPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/FileCopierPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/FileReplacerPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/InstalledModuleDescriptorPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SortResourcesPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StringSharingPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripDebugPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripNativeCommandsPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ZipPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPool.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPoolImpl.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPools.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/ExecutableImage.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/Plugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/PluginException.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/PluginOption.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/Pool.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/PostProcessorPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/TransformerPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties src/jdk.jlink/share/classes/module-info.java test/jdk/jigsaw/tools/jimage/JImageTest.java test/jdk/jigsaw/tools/jlink/CustomImageBuilderTest.java test/jdk/jigsaw/tools/jlink/CustomPluginTest.java test/jdk/jigsaw/tools/jlink/DefaultProviderTest.java test/jdk/jigsaw/tools/jlink/ImageFileCreatorTest.java test/jdk/jigsaw/tools/jlink/ImageFilePoolTest.java test/jdk/jigsaw/tools/jlink/IntegrationTest.java test/jdk/jigsaw/tools/jlink/JLink2Test.java test/jdk/jigsaw/tools/jlink/JLinkNegativeTest.java test/jdk/jigsaw/tools/jlink/JLinkOptimTest.java test/jdk/jigsaw/tools/jlink/JLinkOptionsTest.java test/jdk/jigsaw/tools/jlink/JLinkPluginsTest.java test/jdk/jigsaw/tools/jlink/JLinkPostProcessingTest.java test/jdk/jigsaw/tools/jlink/JLinkTest.java test/jdk/jigsaw/tools/jlink/JvmHandlerTest.java test/jdk/jigsaw/tools/jlink/NativeTest.java test/jdk/jigsaw/tools/jlink/ResourcePoolTest.java test/jdk/jigsaw/tools/jlink/SecurityTest.java test/jdk/jigsaw/tools/jlink/asmplugin/AddForgetResourcesTest.java test/jdk/jigsaw/tools/jlink/asmplugin/AsmPluginTestBase.java test/jdk/jigsaw/tools/jlink/asmplugin/BasicTest.java test/jdk/jigsaw/tools/jlink/asmplugin/IdentityPluginTest.java test/jdk/jigsaw/tools/jlink/asmplugin/NegativeTest.java test/jdk/jigsaw/tools/jlink/asmplugin/PackageMappingTest.java test/jdk/jigsaw/tools/jlink/asmplugin/SortingTest.java test/jdk/jigsaw/tools/jlink/asmplugin/VisitorTest.java test/jdk/jigsaw/tools/jlink/basic/BasicTest.java test/jdk/jigsaw/tools/jlink/customplugin/module-info.java test/jdk/jigsaw/tools/jlink/customplugin/plugin/CustomImageBuilder.java test/jdk/jigsaw/tools/jlink/customplugin/plugin/CustomImageBuilderProvider.java test/jdk/jigsaw/tools/jlink/customplugin/plugin/CustomImageFileProvider.java test/jdk/jigsaw/tools/jlink/customplugin/plugin/CustomResourcePluginProvider.java test/jdk/jigsaw/tools/jlink/customplugin/plugin/HelloPlugin.java test/jdk/jigsaw/tools/jlink/customplugin/plugin/HelloProvider.java test/jdk/jigsaw/tools/jlink/customplugin/plugin/SameNamedImageBuilderProvider.java test/jdk/jigsaw/tools/jlink/customplugin/plugin/SecondImageBuilderProvider.java test/jdk/jigsaw/tools/jlink/hashes/HashesTest.java test/jdk/jigsaw/tools/jlink/plugins/CompressorPluginTest.java test/jdk/jigsaw/tools/jlink/plugins/ExcludeFilesPluginTest.java test/jdk/jigsaw/tools/jlink/plugins/ExcludePluginTest.java test/jdk/jigsaw/tools/jlink/plugins/ExcludeVMPluginTest.java test/jdk/jigsaw/tools/jlink/plugins/FileCopierPluginTest.java test/jdk/jigsaw/tools/jlink/plugins/FileReplacerPluginTest.java test/jdk/jigsaw/tools/jlink/plugins/LastSorterTest.java test/jdk/jigsaw/tools/jlink/plugins/OptionsTest.java test/jdk/jigsaw/tools/jlink/plugins/PluginOrderTest.java test/jdk/jigsaw/tools/jlink/plugins/PluginsNegativeTest.java test/jdk/jigsaw/tools/jlink/plugins/PrevisitorTest.java test/jdk/jigsaw/tools/jlink/plugins/SorterPluginTest.java test/jdk/jigsaw/tools/jlink/plugins/StringSharingPluginTest.java test/jdk/jigsaw/tools/jlink/plugins/StripDebugPluginTest.java test/jdk/jigsaw/tools/lib/tests/Helper.java test/jdk/jigsaw/tools/lib/tests/JImageGenerator.java
diffstat 112 files changed, 4890 insertions(+), 6425 deletions(-) [+]
line wrap: on
line diff
--- a/make/launcher/Launcher-jdk.jlink.gmk	Mon Dec 21 14:42:17 2015 +0100
+++ b/make/launcher/Launcher-jdk.jlink.gmk	Mon Dec 21 14:42:19 2015 +0100
@@ -31,7 +31,7 @@
 ))
 
 $(eval $(call SetupBuildLauncher, jlink,\
-    MAIN_CLASS := jdk.tools.jlink.Main, \
+    MAIN_CLASS := jdk.tools.jlink.internal.Main, \
     CFLAGS := -DENABLE_ARG_FILES \
         -DEXPAND_CLASSPATH_WILDCARDS \
         -DNEVER_ACT_AS_SERVER_CLASS_MACHINE, \
--- a/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java	Mon Dec 21 14:42:19 2015 +0100
@@ -46,12 +46,12 @@
 import jdk.tools.jlink.internal.ImageResourcesTree;
 import jdk.tools.jlink.internal.ImagePluginConfiguration;
 import jdk.tools.jlink.internal.ImagePluginStack;
-import jdk.tools.jlink.TaskHelper;
-import jdk.tools.jlink.TaskHelper.BadArgs;
-import jdk.tools.jlink.TaskHelper.HiddenOption;
-import static jdk.tools.jlink.TaskHelper.JIMAGE_BUNDLE;
-import jdk.tools.jlink.TaskHelper.Option;
-import jdk.tools.jlink.TaskHelper.OptionsHelper;
+import jdk.tools.jlink.internal.TaskHelper;
+import jdk.tools.jlink.internal.TaskHelper.BadArgs;
+import jdk.tools.jlink.internal.TaskHelper.HiddenOption;
+import static jdk.tools.jlink.internal.TaskHelper.JIMAGE_BUNDLE;
+import jdk.tools.jlink.internal.TaskHelper.Option;
+import jdk.tools.jlink.internal.TaskHelper.OptionsHelper;
 
 class JImageTask {
 
@@ -65,6 +65,9 @@
         new Option<JImageTask>(false, (task, opt, arg) -> {
             task.options.help = true;
         }, "--help"),
+        new Option<JImageTask>(false, (task, opt, arg) -> {
+            task.options.xhelp = true;
+        }, "--xhelp"),
         new Option<JImageTask>(true, (task, opt, arg) -> {
             task.options.flags = arg;
         }, "--flags"),
@@ -85,6 +88,7 @@
         String directory = ".";
         boolean fullVersion;
         boolean help;
+        boolean xhelp;
         String flags;
         boolean verbose;
         boolean version;
@@ -162,14 +166,14 @@
                 }
             }
             if (options.help) {
-                optionsHelper.showHelp(PROGNAME, "recreate only options:", false);
+                optionsHelper.showHelp(PROGNAME);
+            }
+            if (options.xhelp) {
+                optionsHelper.showXHelp(PROGNAME, false);
             }
             if (options.version || options.fullVersion) {
                 taskHelper.showVersion(options.fullVersion);
             }
-            if(optionsHelper.listPlugins()) {
-                optionsHelper.showPlugins(log, false);
-            }
             boolean ok = run();
             return ok ? EXIT_OK : EXIT_ERROR;
         } catch (BadArgs e) {
@@ -201,7 +205,8 @@
         Path jimage = options.jimages.get(0).toPath();
 
         if (jimage.toFile().createNewFile()) {
-            ImagePluginStack pc = ImagePluginConfiguration.parseConfiguration(taskHelper.getPluginsConfig(null));
+            ImagePluginStack pc = ImagePluginConfiguration.parseConfiguration(taskHelper.
+                    getPluginsConfig(null, false));
             ExtractedImage img = new ExtractedImage(dirPath, pc, log, options.verbose);
             img.recreateJImage(jimage);
         } else {
--- a/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage.properties	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage.properties	Mon Dec 21 14:42:19 2015 +0100
@@ -16,6 +16,10 @@
 \n\
 Possible options include:
 
+main.extended.help=\
+jimage recreate is extensible by the main of plugins. Following plugins have been discovered \
+thanks to ServiceLoader and can be used when re-creating a jimage.
+
 error.prefix=Error:
 warn.prefix=Warning:
 
@@ -28,6 +32,9 @@
 main.opt.help=\
 \  --help                               Print this usage message
 
+main.opt.xhelp=\
+\  --xhelp                              Print advanced options
+
 main.opt.verbose=\
 \  --verbose                            Verbose listing
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/Jlink.java	Mon Dec 21 14:42:19 2015 +0100
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 2015, 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 jdk.tools.jlink;
+
+import java.lang.reflect.Layer;
+import java.nio.ByteOrder;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import jdk.tools.jlink.internal.JlinkTask;
+import jdk.tools.jlink.plugin.Plugin;
+import jdk.tools.jlink.plugin.PluginException;
+import jdk.tools.jlink.plugin.ExecutableImage;
+import jdk.tools.jlink.builder.ImageBuilder;
+import jdk.tools.jlink.plugin.PluginOption;
+import jdk.tools.jlink.internal.PluginRepository;
+
+/**
+ * API for jlink tool.
+ */
+public final class Jlink {
+
+    /**
+     * A plugin located inside a stack of plugins. Ordered plugin has an index
+     * in the stack.
+     */
+    public static final class OrderedPlugin {
+
+        private final int index;
+        private final boolean absIndex;
+        private final Plugin plugin;
+        /**
+         * A plugin inside the stack configuration.
+         *
+         * @param plugin Plugin.
+         * @param index index in the plugin stack. Must be > 0.
+         * @param absIndex true, the index is absolute otherwise index is within
+         * the plugin category.
+         */
+        public OrderedPlugin(Plugin plugin, int index, boolean absIndex) {
+            Objects.requireNonNull(plugin);
+            if (index < 0) {
+                throw new IllegalArgumentException("negative index");
+            }
+            this.plugin = plugin;
+            this.index = index;
+            this.absIndex = absIndex;
+        }
+
+        /**
+         * A builtin plugin inside the stack configuration.
+         *
+         * @param name Plugin name. The name of the plugin,
+         * will be resolved thanks to ServiceLoader.
+         * @param index index in the plugin stack. Must be > 0.
+         * @param absIndex true, the index is absolute otherwise index is within
+         * the plugin category.
+         * @param configuration
+         */
+        public OrderedPlugin(String name, int index,
+                boolean absIndex, Map<PluginOption, String> configuration) {
+            this(createPlugin(name, configuration, Layer.boot()), index, absIndex);
+        }
+
+        /**
+         * A plugin inside the stack configuration.
+         *
+         * @param pluginsLayer Layer to resolve plugins.
+         * @param name Plugin name. The name of the plugin,
+         * will be resolved thanks to ServiceLoader.
+         * @param index index in the plugin stack. Must be > 0.
+         * @param absIndex true, the index is absolute otherwise index is within
+         * the plugin category.
+         * @param configuration
+         */
+        public OrderedPlugin(Layer pluginsLayer, String name, int index,
+                boolean absIndex, Map<PluginOption, String> configuration) {
+            this(createPlugin(name, configuration, pluginsLayer), index, absIndex);
+        }
+
+        private static Plugin createPlugin(String name,
+                Map<PluginOption, String> configuration, Layer pluginsLayer) {
+            Objects.requireNonNull(name);
+            Objects.requireNonNull(configuration);
+            Objects.requireNonNull(pluginsLayer);
+            return PluginRepository.newPlugin(configuration, name, pluginsLayer);
+        }
+
+        /**
+         * Get the plugin index.
+         *
+         * @return the index
+         */
+        public int getIndex() {
+            return index;
+        }
+
+        @Override
+        public String toString() {
+            return plugin.getName() + "[" + index + "]";
+        }
+
+        public Plugin getPlugin() {
+            return plugin;
+        }
+
+        /**
+         * The plugin index.
+         *
+         * @return the absolute index.
+         */
+        public boolean isAbsoluteIndex() {
+            return absIndex;
+        }
+    }
+
+    /**
+     * A complete plugin configuration. Instances of this class are used to
+     * configure jlink.
+     */
+    public static final class PluginsConfiguration {
+
+        private final List<OrderedPlugin> plugins;
+        private final ImageBuilder imageBuilder;
+        private final String lastSorterPluginName;
+
+        /**
+         * Empty plugins configuration.
+         */
+        public PluginsConfiguration() {
+            this(Collections.emptyList());
+        }
+
+        /**
+         * Plugins configuration.
+         *
+         * @param plugins List of  plugins.
+         */
+        public PluginsConfiguration(List<OrderedPlugin> plugins) {
+            this(plugins, null, null);
+        }
+
+        /**
+         * Plugins configuration with a last sorter. No sorting can occur after
+         * the last sorter plugin.
+         *
+         * @param plugins List of transformer plugins.
+         * @param imageBuilder Image builder (null default builder).
+         * @param lastSorterPluginName Name of last sorter plugin, no sorting
+         * can occur after it.
+         */
+        public PluginsConfiguration(List<OrderedPlugin> plugins,
+                ImageBuilder imageBuilder, String lastSorterPluginName) {
+            this.plugins = plugins == null ? Collections.emptyList()
+                    : plugins;
+            this.imageBuilder = imageBuilder;
+            this.lastSorterPluginName = lastSorterPluginName;
+        }
+
+        /**
+         * @return the plugins
+         */
+        public List<OrderedPlugin> getPlugins() {
+            return plugins;
+        }
+
+        /**
+         * @return the imageBuilder
+         */
+        public ImageBuilder getImageBuilder() {
+            return imageBuilder;
+        }
+
+        /**
+         * @return the lastSorterPluginName
+         */
+        public String getLastSorterPluginName() {
+            return lastSorterPluginName;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append("imagebuilder=").append(imageBuilder).append("\n");
+            StringBuilder pluginsBuilder = new StringBuilder();
+            for (OrderedPlugin p : plugins) {
+                pluginsBuilder.append(p).append(",");
+            }
+            builder.append("plugins=").append(pluginsBuilder).append("\n");
+            builder.append("lastsorter=").append(lastSorterPluginName).append("\n");
+
+            return builder.toString();
+        }
+    }
+
+    /**
+     * Jlink configuration. Instances of this class are used to configure jlink.
+     */
+    public static final class JlinkConfiguration {
+
+        private final List<Path> modulepaths;
+        private final Path output;
+        private final Set<String> modules;
+        private final Set<String> limitmods;
+
+        private final ByteOrder endian;
+
+        /**
+         * jlink configuration,
+         *
+         * @param output Output directory, must not exist.
+         * @param modulepaths Modules paths
+         * @param modules Root modules to resolve
+         * @param limitmods Limit the universe of observable modules
+         * @param endian Jimage byte order. Native order by default
+         */
+        public JlinkConfiguration(Path output,
+                List<Path> modulepaths,
+                Set<String> modules,
+                Set<String> limitmods,
+                ByteOrder endian) {
+            this.output = output;
+            this.modulepaths = modulepaths == null ? Collections.emptyList() : modulepaths;
+            this.modules = modules == null ? Collections.emptySet() : modules;
+            this.limitmods = limitmods == null ? Collections.emptySet() : limitmods;
+            this.endian = endian == null ? ByteOrder.nativeOrder() : endian;
+        }
+
+        /**
+         * jlink configuration,
+         *
+         * @param output Output directory, must not exist.
+         * @param modulepaths Modules paths
+         * @param modules Root modules to resolve
+         * @param limitmods Limit the universe of observable modules
+         */
+        public JlinkConfiguration(Path output,
+                List<Path> modulepaths,
+                Set<String> modules,
+                Set<String> limitmods) {
+            this(output, modulepaths, modules, limitmods,
+                    ByteOrder.nativeOrder());
+        }
+
+        /**
+         * @return the modulepaths
+         */
+        public List<Path> getModulepaths() {
+            return modulepaths;
+        }
+
+        /**
+         * @return the byte ordering
+         */
+        public ByteOrder getByteOrder() {
+            return endian;
+        }
+
+        /**
+         * @return the output
+         */
+        public Path getOutput() {
+            return output;
+        }
+
+        /**
+         * @return the modules
+         */
+        public Set<String> getModules() {
+            return modules;
+        }
+
+        /**
+         * @return the limitmods
+         */
+        public Set<String> getLimitmods() {
+            return limitmods;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+
+            builder.append("output=").append(output).append("\n");
+            StringBuilder pathsBuilder = new StringBuilder();
+            for (Path p : modulepaths) {
+                pathsBuilder.append(p).append(",");
+            }
+            builder.append("modulepaths=").append(pathsBuilder).append("\n");
+
+            StringBuilder modsBuilder = new StringBuilder();
+            for (String p : modules) {
+                modsBuilder.append(p).append(",");
+            }
+            builder.append("modules=").append(modsBuilder).append("\n");
+
+            StringBuilder limitsBuilder = new StringBuilder();
+            for (String p : limitmods) {
+                limitsBuilder.append(p).append(",");
+            }
+            builder.append("limitmodules=").append(limitsBuilder).append("\n");
+            builder.append("endian=").append(endian).append("\n");
+            return builder.toString();
+        }
+    }
+
+    /**
+     * Jlink instance constructor, if a security manager is set, the jlink
+     * permission is checked.
+     */
+    public Jlink() {
+        if (System.getSecurityManager() != null) {
+            System.getSecurityManager().
+                    checkPermission(new JlinkPermission("jlink"));
+        }
+    }
+
+    /**
+     * Build the image.
+     *
+     * @param config Jlink config, must not be null.
+     * @throws PluginException
+     */
+    public void build(JlinkConfiguration config) {
+        build(config, null);
+    }
+
+    /**
+     * Build the image with a plugin configuration.
+     *
+     * @param config Jlink config, must not be null.
+     * @param pluginsConfig Plugins config, can be null
+     * @throws PluginException
+     */
+    public void build(JlinkConfiguration config, PluginsConfiguration pluginsConfig) {
+        Objects.requireNonNull(config);
+        try {
+            JlinkTask.createImage(config, pluginsConfig);
+        } catch (Exception ex) {
+            throw new PluginException(ex);
+        }
+    }
+
+    /**
+     * Post process the image with a plugin configuration.
+     *
+     * @param image Existing image.
+     * @param plugins Plugins config, cannot be null
+     */
+    public void postProcess(ExecutableImage image, List<OrderedPlugin> plugins) {
+        Objects.requireNonNull(image);
+        Objects.requireNonNull(plugins);
+        try {
+            JlinkTask.postProcessImage(image, plugins);
+        } catch (Exception ex) {
+            throw new PluginException(ex);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/JlinkPermission.java	Mon Dec 21 14:42:19 2015 +0100
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015, 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 jdk.tools.jlink;
+
+import java.security.BasicPermission;
+
+/**
+ * The permission required to use jlink API. The permission target_name is
+ * "jlink". e.g.: permission jdk.tools.jlink.plugins.JlinkPermission "jlink";
+ *
+ */
+public final class JlinkPermission extends BasicPermission {
+
+    private static final long serialVersionUID = -3687912306077727801L;
+
+    public JlinkPermission(String name) {
+        super(name);
+    }
+
+}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/JlinkTask.java	Mon Dec 21 14:42:17 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,592 +0,0 @@
-/*
- * Copyright (c) 2015, 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 jdk.tools.jlink;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.UncheckedIOException;
-import java.lang.module.Configuration;
-import java.lang.module.ModuleReference;
-import java.lang.module.ModuleFinder;
-import java.lang.module.ModuleDescriptor;
-import java.lang.module.ResolutionException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Layer;
-import java.net.URI;
-import java.nio.ByteOrder;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Date;
-import java.util.Formatter;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import jdk.tools.jlink.internal.Archive;
-import jdk.internal.module.ConfigurableModuleFinder;
-import jdk.internal.module.ConfigurableModuleFinder.Phase;
-import jdk.tools.jlink.TaskHelper.BadArgs;
-import jdk.tools.jlink.TaskHelper.HiddenOption;
-import static jdk.tools.jlink.TaskHelper.JLINK_BUNDLE;
-import jdk.tools.jlink.TaskHelper.Option;
-import jdk.tools.jlink.TaskHelper.OptionsHelper;
-import jdk.tools.jlink.api.Jlink;
-import jdk.tools.jlink.internal.ModularJarArchive;
-import jdk.tools.jlink.internal.JmodArchive;
-import jdk.tools.jlink.internal.DirArchive;
-import jdk.tools.jlink.internal.ImageFileCreator;
-import jdk.tools.jlink.internal.ImagePluginConfiguration;
-import jdk.tools.jlink.internal.ImagePluginStack;
-import jdk.tools.jlink.internal.ImagePluginStack.ImageProvider;
-import jdk.tools.jlink.api.plugin.postprocessor.ExecutableImage;
-import jdk.tools.jlink.api.Jlink.JlinkConfiguration;
-import jdk.tools.jlink.api.Jlink.PluginsConfiguration;
-import jdk.tools.jlink.api.plugin.PluginException;
-import jdk.tools.jlink.api.plugin.builder.DefaultImageBuilderProvider;
-
-
-/**
- * Implementation for the jlink tool.
- *
- * ## Should use jdk.joptsimple some day.
- */
-public class JlinkTask {
-
-    private static <T extends Throwable> void fail(Class<T> type,
-            String format,
-            Object... args) throws T {
-        String msg = new Formatter().format(format, args).toString();
-        try {
-            T t = type.getConstructor(String.class).newInstance(msg);
-            throw t;
-        } catch (InstantiationException |
-                 InvocationTargetException |
-                 NoSuchMethodException |
-                 IllegalAccessException e) {
-            throw new InternalError("Unable to create an instance of " + type, e);
-        }
-    }
-
-    private static final TaskHelper taskHelper
-            = new TaskHelper(JLINK_BUNDLE);
-
-    static Option<?>[] recognizedOptions = {
-        new Option<JlinkTask>(false, (task, opt, arg) -> {
-            task.options.help = true;
-        }, "--help"),
-        new Option<JlinkTask>(true, (task, opt, arg) -> {
-            String[] dirs = arg.split(File.pathSeparator);
-            task.options.modulePath = new Path[dirs.length];
-            int i = 0;
-            for (String dir : dirs) {
-                task.options.modulePath[i++] = Paths.get(dir);
-            }
-        }, "--modulepath", "--mp"),
-        new Option<JlinkTask>(true, (task, opt, arg) -> {
-            for (String mn : arg.split(",")) {
-                if (mn.isEmpty()) {
-                    throw taskHelper.newBadArgs("err.mods.must.be.specified",
-                                                "--limitmods");
-                }
-                task.options.limitMods.add(mn);
-            }
-        }, "--limitmods"),
-        new Option<JlinkTask>(true, (task, opt, arg) -> {
-            for (String mn : arg.split(",")) {
-                if (mn.isEmpty()) {
-                    throw taskHelper.newBadArgs("err.mods.must.be.specified",
-                                                "--addmods");
-                }
-                task.options.addMods.add(mn);
-            }
-        }, "--addmods"),
-        new Option<JlinkTask>(true, (task, opt, arg) -> {
-            Path path = Paths.get(arg);
-            task.options.output = path;
-        }, "--output"),
-        new Option<JlinkTask>(true, (task, opt, arg) -> {
-            Path path = Paths.get(arg);
-            if (!Files.exists(path) || !Files.isDirectory(path)) {
-                throw taskHelper.newBadArgs("err.existing.image.must.exist");
-            }
-            task.options.existingImage = path.toAbsolutePath();
-        }, "--post-process-path"),
-        new Option<JlinkTask>(true, (task, opt, arg) -> {
-            if ("little".equals(arg)) {
-                task.options.endian = ByteOrder.LITTLE_ENDIAN;
-            } else {
-                if ("big".equals(arg)) {
-                    task.options.endian = ByteOrder.BIG_ENDIAN;
-                } else {
-                    throw taskHelper.newBadArgs("err.unknown.byte.order", arg);
-                }
-            }
-        }, "--endian"),
-        new Option<JlinkTask>(false, (task, opt, arg) -> {
-            task.options.version = true;
-        }, "--version"),
-        new HiddenOption<JlinkTask>(false, (task, opt, arg) -> {
-            task.options.fullVersion = true;
-        }, "--fullversion"),
-    };
-
-    private static final String PROGNAME = "jlink";
-    private final OptionsValues options = new OptionsValues();
-
-    private static final OptionsHelper<JlinkTask> optionsHelper =
-            taskHelper.newOptionsHelper(JlinkTask.class, recognizedOptions);
-    private PrintWriter log;
-    void setLog(PrintWriter out) {
-        log = out;
-        taskHelper.setLog(log);
-    }
-
-    /**
-     * Result codes.
-     */
-    static final int EXIT_OK = 0, // Completed with no errors.
-                     EXIT_ERROR = 1, // Completed but reported errors.
-                     EXIT_CMDERR = 2, // Bad command-line arguments
-                     EXIT_SYSERR = 3, // System error or resource exhaustion.
-                     EXIT_ABNORMAL = 4;// terminated abnormally
-
-    static class OptionsValues {
-        boolean help;
-        boolean version;
-        boolean fullVersion;
-        Path[] modulePath;
-        Set<String> limitMods = new HashSet<>();
-        Set<String> addMods = new HashSet<>();
-        Path output;
-        Path existingImage;
-        ByteOrder endian = ByteOrder.nativeOrder();
-    }
-
-    int run(String[] args) {
-        if (log == null) {
-            setLog(new PrintWriter(System.err));
-        }
-        try {
-            optionsHelper.handleOptions(this, args);
-            if (options.help) {
-                optionsHelper.showHelp(PROGNAME, "jimage creation only options:", true);
-                return EXIT_OK;
-            }
-            if (options.version || options.fullVersion) {
-                taskHelper.showVersion(options.fullVersion);
-                return EXIT_OK;
-            }
-            if(optionsHelper.listPlugins()) {
-                optionsHelper.showPlugins(log, true);
-                 return EXIT_OK;
-            }
-            if (options.existingImage == null) {
-                if (options.modulePath == null || options.modulePath.length == 0) {
-                    throw taskHelper.newBadArgs("err.modulepath.must.be.specified").showUsage(true);
-                }
-                createImage();
-            } else {
-                postProcessOnly(options.existingImage);
-            }
-
-            return EXIT_OK;
-        } catch (UncheckedIOException | PluginException | IOException | ResolutionException e) {
-            e.printStackTrace();
-            log.println(taskHelper.getMessage("error.prefix") + " " + e.getMessage());
-            log.println(taskHelper.getMessage("main.usage.summary", PROGNAME));
-            return EXIT_ERROR;
-        } catch (BadArgs e) {
-            e.printStackTrace();
-            taskHelper.reportError(e.key, e.args);
-            if (e.showUsage) {
-                log.println(taskHelper.getMessage("main.usage.summary", PROGNAME));
-            }
-            return EXIT_CMDERR;
-        } catch (Throwable x) {
-            log.println(taskHelper.getMessage("main.msg.bug"));
-            x.printStackTrace(log);
-            return EXIT_ABNORMAL;
-        } finally {
-            log.flush();
-        }
-    }
-
-    private static Map<String, Path> modulesToPath(ModuleFinder finder,
-                                                   Set<ModuleDescriptor> modules)
-    {
-        Map<String,Path> modPaths = new HashMap<>();
-        for (ModuleDescriptor m : modules) {
-            String name = m.name();
-
-            Optional<ModuleReference> omref = finder.find(name);
-            if (!omref.isPresent()) {
-                // this should not happen, module path bug?
-                fail(InternalError.class,
-                     "Selected module %s not on module path",
-                     name);
-            }
-
-            URI location = omref.get().location().get();
-            String scheme = location.getScheme();
-            if (!scheme.equalsIgnoreCase("jmod") && !scheme.equalsIgnoreCase("jar")
-                    && !scheme.equalsIgnoreCase("file")) {
-                fail(RuntimeException.class,
-                        "Selected module %s (%s) not in jmod, modular jar or directory format",
-                        name,
-                        location);
-            }
-
-            // convert to file URIs
-            URI fileURI;
-            if (scheme.equalsIgnoreCase("jmod")) {
-                // jmod:file:/home/duke/duke.jmod!/ -> file:/home/duke/duke.jmod
-                String s = location.toString();
-                fileURI = URI.create(s.substring(5, s.length()-2));
-            } else {
-                if (scheme.equalsIgnoreCase("jar")) {
-                    // jar:file:/home/duke/duke.jar!/ -> file:/home/duke/duke.jar
-                    String s = location.toString();
-                    fileURI = URI.create(s.substring(4, s.length() - 2));
-                } else {
-                    fileURI = URI.create(location.toString());
-                }
-            }
-
-            modPaths.put(name, Paths.get(fileURI));
-        }
-        return modPaths;
-    }
-
-    /*
-     * Jlink API entry point.
-     */
-    public static void createImage(JlinkConfiguration config,
-            PluginsConfiguration plugins)
-            throws Exception
-    {
-        Objects.requireNonNull(config);
-        Objects.requireNonNull(config.getOutput());
-        plugins = plugins == null ? new PluginsConfiguration() : plugins;
-
-        if (config.getModulepaths().isEmpty()) {
-            throw new Exception("Empty module paths");
-        }
-        Path[] arr = new Path[config.getModulepaths().size()];
-        arr = config.getModulepaths().toArray(arr);
-        ModuleFinder finder
-            = newModuleFinder(arr, config.getLimitmods(), config.getModules());
-
-        // First create the image provider
-        ImageProvider imageProvider
-                = createImageProvider(finder,
-                        checkAddMods(config.getModules()),
-                        config.getLimitmods(), config.getByteOrder());
-
-        // Then create the Plugin Stack
-        ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(plugins,
-                        genBOMContent(config, plugins));
-
-        //Ask the stack to proceed;
-        stack.operate(imageProvider);
-    }
-
-    /*
-     * Jlink API entry point.
-     */
-    public static void postProcessImage(ExecutableImage image, List<Jlink.OrderedPlugin> postProcessorPlugins)
-            throws Exception {
-        Objects.requireNonNull(image);
-        Objects.requireNonNull(postProcessorPlugins);
-        PluginsConfiguration config = new PluginsConfiguration(postProcessorPlugins);
-        ImagePluginStack stack = ImagePluginConfiguration.
-                parseConfiguration(config);
-
-        stack.operate((ImagePluginStack stack1) -> image);
-    }
-
-    private void postProcessOnly(Path existingImage) throws Exception {
-        PluginsConfiguration config = taskHelper.getPluginsConfig(null);
-        ExecutableImage img = DefaultImageBuilderProvider.getExecutableImage(existingImage);
-        if(img == null) {
-            throw taskHelper.newBadArgs("err.existing.image.invalid");
-        }
-        postProcessImage(img, config.getPlugins());
-    }
-
-    private void createImage() throws Exception {
-        if (options.output == null) {
-            throw taskHelper.newBadArgs("err.output.must.be.specified").showUsage(true);
-        }
-        ModuleFinder finder
-            = newModuleFinder(options.modulePath, options.limitMods, options.addMods);
-        try {
-            options.addMods = checkAddMods(options.addMods);
-        } catch (IllegalArgumentException ex) {
-            throw taskHelper.newBadArgs("err.mods.must.be.specified", "--addmods")
-                    .showUsage(true);
-        }
-        // First create the image provider
-        ImageProvider imageProvider
-                = createImageProvider(finder,
-                        options.addMods,
-                        options.limitMods,
-                        options.endian);
-
-        // Then create the Plugin Stack
-        ImagePluginStack stack = ImagePluginConfiguration.
-                parseConfiguration(taskHelper.getPluginsConfig(options.output),
-                        genBOMContent());
-
-        //Ask the stack to proceed
-        stack.operate(imageProvider);
-    }
-
-    private static Set<String> checkAddMods(Set<String> addMods) {
-        if (addMods.isEmpty()) {
-            throw new IllegalArgumentException("no modules to add");
-        }
-        return addMods;
-    }
-
-    private static ModuleFinder newModuleFinder(Path[] paths,
-                                                Set<String> limitMods,
-                                                Set<String> addMods)
-    {
-        ModuleFinder finder = ModuleFinder.of(paths);
-
-        // jmods are located at link-time
-        if (finder instanceof ConfigurableModuleFinder)
-            ((ConfigurableModuleFinder)finder).configurePhase(Phase.LINK_TIME);
-
-        // if limitmods is specified then limit the universe
-        if (!limitMods.isEmpty()) {
-            finder = limitFinder(finder, limitMods, addMods);
-        }
-        return finder;
-    }
-
-    private static ImageProvider createImageProvider(ModuleFinder finder,
-            Set<String> addMods,
-            Set<String> limitMods,
-            ByteOrder order)
-            throws IOException {
-        if (addMods.isEmpty()) {
-            throw new IllegalArgumentException("empty modules and limitmods");
-        }
-        Configuration cf = Configuration.resolve(finder,
-                Configuration.empty(),
-                ModuleFinder.empty(),
-                addMods);
-        Map<String, Path> mods = modulesToPath(finder, cf.descriptors());
-        return new ImageHelper(cf, mods, order);
-    }
-
-
-    /**
-     * Returns a ModuleFinder that limits observability to the given root
-     * modules, their transitive dependences, plus a set of other modules.
-     */
-    private static ModuleFinder limitFinder(ModuleFinder finder,
-                                            Set<String> roots,
-                                            Set<String> otherMods)
-    {
-        // resolve all root modules
-        Configuration cf = Configuration.resolve(finder,
-                Configuration.empty(),
-                ModuleFinder.empty(),
-                roots);
-
-        // module name -> reference
-        Map<String, ModuleReference> map = new HashMap<>();
-        cf.descriptors().forEach(md -> {
-            String name = md.name();
-            map.put(name, finder.find(name).get());
-        });
-
-        // set of modules that are observable
-        Set<ModuleReference> mrefs = new HashSet<>(map.values());
-
-        // add the other modules
-        for (String mod : otherMods) {
-            Optional<ModuleReference> omref = finder.find(mod);
-            if (omref.isPresent()) {
-                ModuleReference mref = omref.get();
-                map.putIfAbsent(mod, mref);
-                mrefs.add(mref);
-            } else {
-                // no need to fail
-            }
-        }
-
-        return new ModuleFinder() {
-            @Override
-            public Optional<ModuleReference> find(String name) {
-                return Optional.ofNullable(map.get(name));
-            }
-            @Override
-            public Set<ModuleReference> findAll() {
-                return mrefs;
-            }
-        };
-    }
-
-    private static String getBomHeader() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("#").append(new Date()).append("\n");
-        sb.append("#Please DO NOT Modify this file").append("\n");
-        return sb.toString();
-    }
-
-    private String genBOMContent() throws IOException {
-        StringBuilder sb = new StringBuilder();
-        sb.append(getBomHeader());
-        StringBuilder command = new StringBuilder();
-        for (String c : optionsHelper.getInputCommand()) {
-            command.append(c).append(" ");
-        }
-        sb.append("command").append(" = ").append(command);
-        sb.append("\n");
-
-        // Expanded command
-        String[] expanded = optionsHelper.getExpandedCommand();
-        if (expanded != null) {
-            String defaults = optionsHelper.getDefaults();
-            sb.append("\n").append("#Defaults").append("\n");
-            sb.append("defaults = ").append(defaults).append("\n");
-
-            StringBuilder builder = new StringBuilder();
-            for (String c : expanded) {
-                builder.append(c).append(" ");
-            }
-            sb.append("expanded command").append(" = ").append(builder);
-            sb.append("\n");
-        }
-
-        return sb.toString();
-    }
-
-    private static String genBOMContent(JlinkConfiguration config,
-                                        PluginsConfiguration plugins)
-        throws IOException
-    {
-        StringBuilder sb = new StringBuilder();
-        sb.append(getBomHeader());
-        sb.append(config);
-        sb.append(plugins);
-        return sb.toString();
-    }
-
-    private static class ImageHelper implements ImageProvider {
-        final Set<Archive> archives;
-        final ByteOrder order;
-
-        ImageHelper(Configuration cf,
-                Map<String, Path> modsPaths,
-                ByteOrder order)
-                throws IOException {
-            archives = modsPaths.entrySet().stream()
-                    .map(e -> newArchive(e.getKey(), e.getValue()))
-                    .collect(Collectors.toSet());
-            this.order = order;
-        }
-
-        private Archive newArchive(String module, Path path) {
-            if (path.toString().endsWith(".jmod")) {
-                return new JmodArchive(module, path);
-            } else {
-                if (path.toString().endsWith(".jar")) {
-                    return new ModularJarArchive(module, path);
-                } else {
-                    if (Files.isDirectory(path)) {
-                        return new DirArchive(path);
-                    } else {
-                        fail(RuntimeException.class,
-                                "Selected module %s (%s) not in jmod or modular jar format",
-                                module,
-                                path);
-                    }
-                }
-            }
-            return null;
-        }
-
-        @Override
-        public ExecutableImage retrieve(ImagePluginStack stack) throws IOException {
-            return ImageFileCreator.create(archives, order, stack);
-        }
-    }
-
-    private static enum Section {
-        NATIVE_LIBS("native", nativeDir()),
-        NATIVE_CMDS("bin", "bin"),
-        CLASSES("classes", "classes"),
-        CONFIG("conf", "conf"),
-        UNKNOWN("unknown", "unknown");
-
-        private static String nativeDir() {
-            if (System.getProperty("os.name").startsWith("Windows")) {
-                return "bin";
-            } else {
-                return "lib";
-            }
-        }
-
-        private final String jmodDir;
-        private final String imageDir;
-
-        Section(String jmodDir, String imageDir) {
-            this.jmodDir = jmodDir;
-            this.imageDir = imageDir;
-        }
-
-        String imageDir() { return imageDir; }
-        String jmodDir() { return jmodDir; }
-
-        boolean matches(String path) {
-            return path.startsWith(jmodDir);
-        }
-
-        static Section getSectionFromName(String dir) {
-            if (Section.NATIVE_LIBS.matches(dir))
-                return Section.NATIVE_LIBS;
-            else if (Section.NATIVE_CMDS.matches(dir))
-                return Section.NATIVE_CMDS;
-            else if (Section.CLASSES.matches(dir))
-                return Section.CLASSES;
-            else if (Section.CONFIG.matches(dir))
-                return Section.CONFIG;
-            else
-                return Section.UNKNOWN;
-        }
-    }
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/Main.java	Mon Dec 21 14:42:17 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.tools.jlink;
-
-import java.io.*;
-
-public class Main {
-    public static void main(String... args) throws Exception {
-        JlinkTask t = new JlinkTask();
-        int rc = t.run(args);
-        System.exit(rc);
-    }
-
-
-    /**
-     * Entry point that does <i>not</i> call System.exit.
-     *
-     * @param args command line arguments
-     * @param out output stream
-     * @return an exit code. 0 means success, non-zero means an error occurred.
-     */
-    public static int run(String[] args, PrintWriter out) {
-        JlinkTask t = new JlinkTask();
-        t.setLog(out);
-        return t.run(args);
-    }
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/TaskHelper.java	Mon Dec 21 14:42:17 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,976 +0,0 @@
-/*
- * Copyright (c) 2015, 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 jdk.tools.jlink;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.lang.module.Configuration;
-import java.lang.module.ModuleFinder;
-import java.lang.reflect.Layer;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.MissingResourceException;
-import java.util.Properties;
-import java.util.ResourceBundle;
-
-import jdk.internal.module.ConfigurableModuleFinder;
-import jdk.internal.module.ConfigurableModuleFinder.Phase;
-import jdk.tools.jlink.internal.PluginRepository;
-import jdk.tools.jlink.internal.ImagePluginConfiguration;
-import jdk.tools.jlink.api.plugin.builder.DefaultImageBuilderProvider;
-import jdk.tools.jlink.api.Jlink;
-import jdk.tools.jlink.api.Jlink.OrderedPlugin;
-import jdk.tools.jlink.api.Jlink.PluginsConfiguration;
-import jdk.tools.jlink.api.plugin.Plugin;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption.Builder;
-import jdk.tools.jlink.api.plugin.Plugin.CATEGORY;
-import jdk.tools.jlink.api.plugin.Plugin.ORDER;
-import jdk.tools.jlink.api.plugin.builder.DefaultImageBuilder;
-import jdk.tools.jlink.api.plugin.builder.ImageBuilder;
-import jdk.tools.jlink.internal.Utils;
-
-/**
- *
- * JLink and JImage tools shared helper.
- */
-public final class TaskHelper {
-
-    public static final String JLINK_BUNDLE = "jdk.tools.jlink.resources.jlink";
-    public static final String JIMAGE_BUNDLE = "jdk.tools.jimage.resources.jimage";
-
-    private static final String DEFAULTS_PROPERTY = "jdk.jlink.defaults";
-    private static final String CONFIGURATION = "configuration";
-
-    public final class BadArgs extends Exception {
-
-        static final long serialVersionUID = 8765093759964640721L;
-
-        private BadArgs(String key, Object... args) {
-            super(bundleHelper.getMessage(key, args));
-            this.key = key;
-            this.args = args;
-        }
-
-        public BadArgs showUsage(boolean b) {
-            showUsage = b;
-            return this;
-        }
-        public final String key;
-        public final Object[] args;
-        public boolean showUsage;
-    }
-
-    public static class Option<T> {
-
-        public interface Processing<T> {
-
-            void process(T task, String opt, String arg) throws BadArgs;
-        }
-
-        final boolean hasArg;
-        final String[] aliases;
-        final Processing<T> processing;
-
-        public Option(boolean hasArg, Processing<T> processing, String... aliases) {
-            this.hasArg = hasArg;
-            this.processing = processing;
-            this.aliases = aliases;
-        }
-
-        public boolean isHidden() {
-            return false;
-        }
-
-        public boolean matches(String opt) {
-            for (String a : aliases) {
-                if (a.equals(opt)) {
-                    return true;
-                } else if (opt.startsWith("--")
-                        && (hasArg && opt.startsWith(a + "="))) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        public boolean ignoreRest() {
-            return false;
-        }
-
-        void process(T task, String opt, String arg) throws BadArgs {
-            processing.process(task, opt, arg);
-        }
-    }
-
-    private static class PlugOption extends Option<PluginsOptions> {
-
-        public PlugOption(boolean hasArg,
-                Processing<PluginsOptions> processing, String... aliases) {
-            super(hasArg, processing, aliases);
-        }
-
-        @Override
-        public boolean matches(String opt) {
-            return super.matches(removeIndex(opt));
-        }
-    }
-
-    private int getIndex(String opt) throws BadArgs {
-        String orig = opt;
-        int i = opt.indexOf(":");
-        int index = -1;
-        if (i != -1) {
-            opt = opt.substring(i + 1);
-            if (opt.equals(Plugin.ORDER.FIRST.getName())) {
-                index = 0;
-            } else if (opt.equals(Plugin.ORDER.LAST.getName())) {
-                index = Integer.MAX_VALUE;
-            } else {
-                try {
-                    index = Integer.parseInt(opt);
-                } catch (NumberFormatException ex) {
-                    throw newBadArgs("err.invalid.index", orig);
-                }
-            }
-        }
-        return index;
-    }
-
-    private static String removeIndex(String opt) {
-        //has index? remove it
-        int i = opt.indexOf(":");
-        if (i != -1) {
-            opt = opt.substring(0, i);
-        }
-        return opt;
-    }
-
-    private static class HiddenPluginOption extends PlugOption {
-
-        public HiddenPluginOption(boolean hasArg,
-                Processing<PluginsOptions> processing, String... aliases) {
-            super(hasArg, processing, aliases);
-        }
-
-        @Override
-        public boolean isHidden() {
-            return true;
-        }
-    }
-
-    private final class PluginsOptions {
-
-        private static final String PLUGINS_PATH = "--plugins-modulepath";
-
-        private Layer pluginsLayer = Layer.boot();
-        private String lastSorter;
-        private boolean listPlugins;
-        private final Map<Plugin, Map<PluginOption, String>> plugins = new HashMap<>();
-        private final Map<PluginOption, String> imgBuilder = new HashMap<>();
-        private final List<PlugOption> pluginsOptions = new ArrayList<>();
-
-        // The order in which options are declared is the stack order.
-        // Order is within plugin category
-        private final Map<CATEGORY, List<Plugin>> pluginsOrder = new HashMap<>();
-        private final Map<Plugin, Integer> pluginsIndexes = new HashMap<>();
-
-        private PluginsOptions(String pp) throws BadArgs {
-
-            if (pp != null) {
-                String[] dirs = pp.split(File.pathSeparator);
-                List<Path> paths = new ArrayList<>(dirs.length);
-                for (String dir : dirs) {
-                    paths.add(Paths.get(dir));
-                }
-
-                pluginsLayer = createPluginsLayer(paths);
-            }
-
-            Map<String, List<String>> seen = new HashMap<>();
-            for (Plugin plugin : PluginRepository.
-                    getPlugins(pluginsLayer)) {
-                addOrderedPuginOptions(plugin, seen);
-            }
-            pluginsOptions.add(new PlugOption(true,
-                    (task, opt, arg) -> {
-                        lastSorter = arg;
-                    },
-                    "--resources-last-sorter "));
-            pluginsOptions.add(new HiddenPluginOption(false,
-                    (task, opt, arg) -> {
-                        listPlugins = true;
-                    },
-                    "--list-plugins"));
-
-            DefaultImageBuilderProvider provider = new DefaultImageBuilderProvider();
-            List<PluginOption> options = provider.getAdditionalOptions();
-            if (options != null && !options.isEmpty()) {
-                for (PluginOption o : options) {
-                    PlugOption option
-                            = new PlugOption(o.getArgumentDescription() != null,
-                                    (task, opt, arg) -> {
-                                        imgBuilder.put(o, arg);
-                                    },
-                                    "--" + o.getName());
-                    pluginsOptions.add(option);
-                }
-            }
-        }
-
-        private void addOrderedPuginOptions(Plugin plugin,
-                Map<String, List<String>> seen) throws BadArgs {
-            PluginOption option = plugin.getOption();
-            if (option != null) {
-                for (Entry<String, List<String>> entry : seen.entrySet()) {
-                    if (entry.getKey().equals(option.getName())
-                            || entry.getValue().contains(option.getName())) {
-                        throw new BadArgs("err.plugin.mutiple.options",
-                                option.getName());
-                    }
-                }
-                List<String> optional = new ArrayList<>();
-                seen.put(option.getName(), optional);
-                PlugOption plugOption
-                        = new PlugOption(option.getArgumentDescription() != null,
-                                (task, opt, arg) -> {
-                                    if (!Utils.isFunctional(plugin)) {
-                                        throw newBadArgs("err.provider.not.functional",
-                                                option.getName());
-                                    }
-                                    Map<PluginOption, String> m = plugins.get(plugin);
-                                    if (m == null) {
-                                        m = new HashMap<>();
-                                        plugins.put(plugin, m);
-                                    }
-                                    m.put(option, arg);
-                                    int index = computeIndex(plugin);
-                                    // Overriden index?
-                                    if (index == -1) {
-                                        index = getIndex(opt);
-                                    }
-                                    pluginsIndexes.put(plugin, index);
-                                },
-                                "--" + option.getName());
-                pluginsOptions.add(plugOption);
-                if (plugin.getAdditionalOptions() != null && !plugin.getAdditionalOptions().isEmpty()) {
-                    for (PluginOption other : plugin.getAdditionalOptions()) {
-                        optional.add(other.getName());
-                        PlugOption otherOption = new PlugOption(true,
-                                (task, opt, arg) -> {
-                                    Map<PluginOption, String> m = plugins.get(plugin);
-                                    if (m == null) {
-                                        m = new HashMap<>();
-                                        plugins.put(plugin, m);
-                                    }
-                                    m.put(other, arg);
-                                },
-                                "--" + other.getName());
-                        pluginsOptions.add(otherOption);
-                    }
-                }
-                // On/Off enabled by default plugin
-                // Command line option can override it
-                if (option.hasOnOffArgument()) {
-                    boolean edefault = Utils.isEnabled(plugin)
-                            && Utils.isFunctional(plugin) && option.isEnabled();
-                    if (edefault) {
-                        Map<PluginOption, String> m = new HashMap<>();
-                        m.put(option, Builder.ON_ARGUMENT);
-                        plugins.put(plugin, m);
-                    }
-                }
-            }
-        }
-
-        private int computeIndex(Plugin plugin) {
-            CATEGORY category = Utils.getCategory(plugin);
-            List<Plugin> order = pluginsOrder.get(category);
-            if (order == null) {
-                order = new ArrayList<>();
-                pluginsOrder.put(category, order);
-            }
-            order.add(plugin);
-            int index = -1;
-            ORDER defaultOrder = Utils.getOrder(plugin);
-            if (defaultOrder == ORDER.FIRST) {
-                index = 0;
-            } else if (defaultOrder == ORDER.LAST) {
-                index = Integer.MAX_VALUE;
-            }
-            return index;
-        }
-
-        private PlugOption getOption(String name) throws BadArgs {
-            for (PlugOption o : pluginsOptions) {
-                if (o.matches(name)) {
-                    return o;
-                }
-            }
-            return null;
-        }
-
-        private PluginsConfiguration getPluginsConfig(Path output) throws IOException {
-            List<OrderedPlugin> pluginsList = new ArrayList<>();
-            for (Entry<Plugin, Map<PluginOption, String>> entry : plugins.entrySet()) {
-                Plugin plugin = entry.getKey();
-                if (plugin.getOption() != null) {
-                    PluginOption opt = plugin.getOption();
-                    if (opt.hasOnOffArgument()) {
-                        Object val = entry.getValue().get(opt);
-                        if (Builder.OFF_ARGUMENT.equals(val)) {
-                            // Disabled plugin, no need to add it.
-                            continue;
-                        }
-                    }
-                }
-                // User defined index?
-                Integer i = pluginsIndexes.get(plugin);
-                if (i == null) {
-                    // Enabled by default plugin. Find it an index.
-                    i = computeIndex(plugin);
-                }
-                boolean absolute = false;
-                if (i == -1) {
-                    CATEGORY category = Utils.getCategory(plugin);
-                    if (category == null) {
-                        absolute = true;
-                    }
-                    List<Plugin> lstPlugins
-                            = pluginsOrder.get(category);
-                    i = lstPlugins.indexOf(plugin);
-                }
-                if (i == -1) {
-                    throw new IllegalArgumentException("Invalid index " + i);
-                }
-                Map<PluginOption, String> config = new HashMap<>();
-                config.putAll(entry.getValue());
-                plugin.configure(config);
-                OrderedPlugin conf
-                        = new Jlink.OrderedPlugin(plugin,
-                                i, absolute, Utils.getCategory(plugin));
-                pluginsList.add(conf);
-            }
-
-            // recreate or postprocessing don't require an output directory.
-            ImageBuilder builder = null;
-            if (output != null) {
-                Map<PluginOption, String> builderConfig = new HashMap<>();
-                builderConfig.put(DefaultImageBuilderProvider.IMAGE_PATH_OPTION, output.toString());
-                builderConfig.putAll(imgBuilder);
-                builder = new DefaultImageBuilder(builderConfig, output);
-            }
-            return new Jlink.PluginsConfiguration(pluginsList,
-                    builder, lastSorter);
-        }
-    }
-
-    public static class HiddenOption<T> extends Option<T> {
-
-        public HiddenOption(boolean hasArg, Processing<T> processing,
-                String... aliases) {
-            super(hasArg, processing, aliases);
-        }
-
-        @Override
-        public boolean isHidden() {
-            return true;
-        }
-    }
-
-    private static final class ResourceBundleHelper {
-
-        private final ResourceBundle bundle;
-        private final ResourceBundle pluginBundle;
-
-        ResourceBundleHelper(String path) {
-            Locale locale = Locale.getDefault();
-            try {
-                bundle = ResourceBundle.getBundle(path, locale);
-                pluginBundle = ResourceBundle.getBundle("jdk.tools.jlink.resources.plugins", locale);
-            } catch (MissingResourceException e) {
-                throw new InternalError("Cannot find jlink resource bundle for locale " + locale);
-            }
-        }
-
-        String getMessage(String key, Object... args) {
-            String val;
-            try {
-                val = bundle.getString(key);
-            } catch (MissingResourceException e) {
-                // XXX OK, check in plugin bundle
-                val = pluginBundle.getString(key);
-            }
-            return MessageFormat.format(val, args);
-        }
-
-    }
-
-    public final class OptionsHelper<T> {
-
-        private final List<Option<T>> options;
-        private String[] expandedCommand;
-        private String[] command;
-        private String defaults;
-
-        OptionsHelper(List<Option<T>> options) {
-            this.options = options;
-        }
-
-        private boolean hasArgument(String optionName) throws BadArgs {
-            Option<?> opt = getOption(optionName);
-            if (opt == null) {
-                opt = pluginOptions.getOption(optionName);
-                if (opt == null) {
-                    throw new BadArgs("err.unknown.option", optionName).
-                            showUsage(true);
-                }
-            }
-            return opt.hasArg;
-        }
-
-        private String[] handleDefaults(String[] args) throws BadArgs {
-            String[] ret = args;
-            List<String> defArgs = null;
-
-            List<String> override = new ArrayList<>();
-            boolean expanded = false;
-            for (int i = 0; i < args.length; i++) {
-                if (args[i].equals("--" + CONFIGURATION)) {
-                    i++;
-                    String path = args[i];
-                    Properties p = new Properties();
-                    try (FileInputStream fs = new FileInputStream(path)) {
-                        p.load(fs);
-                    } catch (IOException ex) {
-                        throw new RuntimeException(ex);
-                    }
-                    defaults = p.getProperty(DEFAULTS_PROPERTY);
-                    if (defaults == null) {
-                        throw new BadArgs("err.config.defaults",
-                                DEFAULTS_PROPERTY);
-                    }
-                    try {
-                        defArgs = parseDefaults(defaults);
-                    } catch (Exception ex) {
-                        throw new BadArgs("err.config.defaults", defaults);
-                    }
-                    expanded = true;
-                } else {
-                    override.add(args[i]);
-                }
-            }
-            if (defArgs != null) {
-                List<String> output = new ArrayList<>();
-                for (int i = 0; i < defArgs.size(); i++) {
-                    String arg = defArgs.get(i);
-                    if (arg.charAt(0) == '-') {
-                        output.add(arg);
-                        boolean hasArgument = hasArgument(arg);
-                        String a = null;
-                        if (hasArgument) {
-                            i++;
-                            a = defArgs.get(i);
-                        }
-
-                        int overIndex = -1;
-                        // Retrieve the command line option
-                        // compare by erasing possible index
-                        for (int j = 0; j < override.size(); j++) {
-                            if (removeIndex(override.get(j)).equals(removeIndex(arg))) {
-                                overIndex = j;
-                                break;
-                            }
-                        }
-
-                        if (overIndex >= 0) {
-                            if (hasArgument) {
-                                a = override.get(overIndex + 1);
-                                override.remove(a);
-                            }
-                            override.remove(arg);
-                        }
-                        if (hasArgument) {
-                            if (a == null) {
-                                throw newBadArgs("err.unknown.option", arg).
-                                        showUsage(true);
-                            }
-                            output.add(a);
-                        }
-                    } else {
-                        throw newBadArgs("err.unknown.option", arg).
-                                showUsage(true);
-                    }
-                }
-                //Add remaining
-                output.addAll(override);
-                ret = new String[output.size()];
-                output.toArray(ret);
-            }
-            if (expanded) {
-                expandedCommand = ret;
-            }
-            return ret;
-        }
-
-        private List<String> getDefaults(String[] args) throws BadArgs {
-            List<String> defArgs = null;
-
-            for (int i = 0; i < args.length; i++) {
-                if (args[i].equals("--" + CONFIGURATION)) {
-                    i++;
-                    String path = args[i];
-                    Properties p = new Properties();
-                    try (FileInputStream fs = new FileInputStream(path)) {
-                        p.load(fs);
-                    } catch (IOException ex) {
-                        throw new RuntimeException(ex);
-                    }
-                    defaults = p.getProperty(DEFAULTS_PROPERTY);
-                    if (defaults == null) {
-                        throw new BadArgs("err.config.defaults",
-                                DEFAULTS_PROPERTY);
-                    }
-                    try {
-                        defArgs = parseDefaults(defaults);
-                    } catch (Exception ex) {
-                        throw new BadArgs("err.config.defaults", defaults);
-                    }
-                    break;
-                }
-            }
-            return defArgs;
-        }
-
-        private String getPluginsPath(String[] args) throws BadArgs {
-            String pp = null;
-            List<String> defaults = getDefaults(args);
-            if (defaults != null) {
-                int ppIndex = defaults.indexOf(PluginsOptions.PLUGINS_PATH);
-                if (ppIndex > 0) {
-                    if (ppIndex == defaults.size() - 1) {
-                        throw new BadArgs("err.no.plugins.path").showUsage(true);
-                    }
-                    pp = defaults.get(ppIndex + 1);
-                    if (!pp.isEmpty() && pp.charAt(0) == '-') {
-                        throw new BadArgs("err.no.plugins.path").showUsage(true);
-                    }
-                }
-            }
-            for (int i = 0; i < args.length; i++) {
-                if (args[i].equals(PluginsOptions.PLUGINS_PATH)) {
-                    if (i == args.length - 1) {
-                        throw new BadArgs("err.no.plugins.path").showUsage(true);
-                    } else {
-                        i += 1;
-                        pp = args[i];
-                        if (!pp.isEmpty() && pp.charAt(0) == '-') {
-                            throw new BadArgs("err.no.plugins.path").showUsage(true);
-                        }
-                        break;
-                    }
-                }
-            }
-            return pp;
-        }
-
-        public List<String> handleOptions(T task, String[] args) throws BadArgs {
-            // findbugs warning, copy instead of keeping a reference.
-            command = Arrays.copyOf(args, args.length);
-
-            // The plugins path can be set in defaults or in args.
-            // Must extract it prior to do any option analysis.
-            // Required to interpret custom plugin options.
-            // Unit tests can call Task multiple time in same JVM.
-            pluginOptions = new PluginsOptions(getPluginsPath(args));
-
-            args = handleDefaults(args);
-
-            // First extract plugins path if any
-            String pp = null;
-            List<String> filteredArgs = new ArrayList<>();
-            for (int i = 0; i < args.length; i++) {
-                if (args[i].equals(PluginsOptions.PLUGINS_PATH)) {
-                    if (i == args.length - 1) {
-                        throw new BadArgs("err.no.plugins.path").showUsage(true);
-                    } else {
-                        warning("warn.thirdparty.plugins.enabled");
-                        log.println(bundleHelper.getMessage("warn.thirdparty.plugins"));
-                        i += 1;
-                        String arg = args[i];
-                        if (!arg.isEmpty() && arg.charAt(0) == '-') {
-                            throw new BadArgs("err.no.plugins.path").showUsage(true);
-                        }
-                        pp = args[i];
-                    }
-                } else {
-                    filteredArgs.add(args[i]);
-                }
-            }
-            String[] arr = new String[filteredArgs.size()];
-            args = filteredArgs.toArray(arr);
-
-            List<String> rest = new ArrayList<>();
-            // process options
-            for (int i = 0; i < args.length; i++) {
-                if (!args[i].isEmpty() && args[i].charAt(0) == '-') {
-                    String name = args[i];
-                    PlugOption pluginOption = null;
-                    Option<T> option = getOption(name);
-                    if (option == null) {
-                        pluginOption = pluginOptions.getOption(name);
-                        if (pluginOption == null) {
-
-                            throw new BadArgs("err.unknown.option", name).
-                                    showUsage(true);
-                        }
-                    }
-                    Option<?> opt = pluginOption == null ? option : pluginOption;
-                    String param = null;
-                    if (opt.hasArg) {
-                        if (name.startsWith("--") && name.indexOf('=') > 0) {
-                            param = name.substring(name.indexOf('=') + 1,
-                                    name.length());
-                        } else if (i + 1 < args.length) {
-                            param = args[++i];
-                        }
-                        if (param == null || param.isEmpty()
-                                || (param.length() >= 2 && param.charAt(0) == '-'
-                                && param.charAt(1) == '-')) {
-                            throw new BadArgs("err.missing.arg", name).
-                                    showUsage(true);
-                        }
-                    }
-                    if (pluginOption != null) {
-                        pluginOption.process(pluginOptions, name, param);
-                    } else {
-                        option.process(task, name, param);
-                    }
-                    if (opt.ignoreRest()) {
-                        i = args.length;
-                    }
-                } else {
-                    rest.add(args[i]);
-                }
-            }
-            return rest;
-        }
-
-        private Option<T> getOption(String name) {
-            for (Option<T> o : options) {
-                if (o.matches(name)) {
-                    return o;
-                }
-            }
-            return null;
-        }
-
-        public void showHelp(String progName, String pluginsHeader,
-                boolean showsImageBuilder) {
-            log.println(bundleHelper.getMessage("main.usage", progName));
-            // First configuration
-            log.println(bundleHelper.getMessage("main.opt." + CONFIGURATION));
-            for (Option<?> o : options) {
-                String name = o.aliases[0].substring(1); // there must always be at least one name
-                name = name.charAt(0) == '-' ? name.substring(1) : name;
-                if (o.isHidden() || name.equals("h")) {
-                    continue;
-                }
-                log.println(bundleHelper.getMessage("main.opt." + name));
-            }
-            log.println(bundleHelper.getMessage("main.plugins-modulepath")
-                    + ". " + bundleHelper.getMessage("warn.prefix") + " "
-                    + bundleHelper.getMessage("warn.thirdparty.plugins"));
-            log.println(bundleHelper.getMessage("main.command.files"));
-
-            log.println("\n" + pluginsHeader + "\n");
-            DefaultImageBuilderProvider imgBuilder = new DefaultImageBuilderProvider();
-            logBuilderOptions(imgBuilder.getAdditionalOptions());
-            List<Plugin> pluginList = PluginRepository.
-                    getPlugins(pluginOptions.pluginsLayer);
-            for (Plugin plugin : Utils.getPreProcessors(pluginList)) {
-                printHelp(plugin, showsImageBuilder);
-            }
-            if (showsImageBuilder) {
-                List<Plugin> post = Utils.getPostProcessors(PluginRepository.
-                        getPlugins(pluginOptions.pluginsLayer));
-                if (post.size() > 0) {
-                    log.println(bundleHelper.getMessage("main.plugin.post.processors"));
-                    for (Plugin plugin : post) {
-                        printHelp(plugin, showsImageBuilder);
-                    }
-                }
-            }
-        }
-
-        private void printHelp(Plugin plugin, boolean showsImageBuilder) {
-            if (showsPlugin(plugin, showsImageBuilder)) {
-                PluginOption opt = plugin.getOption();
-                if (opt != null) {
-                    StringBuilder line = new StringBuilder();
-                    line.append(" --").append(opt.getName());
-                    if (opt.getArgumentDescription() != null) {
-                        line.append(" ").append(opt.getArgumentDescription());
-                    }
-                    line.append("\n ").append(plugin.getDescription());
-                    line.append("\n ").append(bundleHelper.getMessage("main.plugin.state")).
-                            append(": ").append(plugin.getStateDescription());
-                    if (plugin.getAdditionalOptions() != null && !plugin.getAdditionalOptions().isEmpty()) {
-                        line.append("\n ").append(bundleHelper.
-                                getMessage("main.plugin.additional.options")).
-                                append(": ");
-                        for (PluginOption option : plugin.getAdditionalOptions()) {
-                            line.append(" --").append(option.getName()).append(" ").
-                                    append(option.getDescription());
-                        }
-                    }
-                    log.println(line.toString() + "\n");
-                }
-            }
-        }
-
-        public void showPlugins(PrintWriter log, boolean showsImageBuilder) {
-            for (Plugin plugin : Utils.
-                    getPreProcessors(PluginRepository.
-                            getPlugins(getPluginsLayer()))) {
-                showPlugin(plugin, log, showsImageBuilder);
-            }
-
-            if (showsImageBuilder) {
-                for (Plugin plugin : Utils.
-                        getPostProcessors(PluginRepository.
-                                getPlugins(getPluginsLayer()))) {
-                    showPlugin(plugin, log, showsImageBuilder);
-                }
-            }
-        }
-
-        private void showPlugin(Plugin plugin, PrintWriter log, boolean showsImageBuilder) {
-            if (showsPlugin(plugin, showsImageBuilder)) {
-                log.println("\n" + bundleHelper.getMessage("main.plugin.name")
-                        + ": " + plugin.getName());
-                CATEGORY category = Utils.getCategory(plugin);
-                Integer[] range = ImagePluginConfiguration.getRange(category);
-                String cat = range == null ? category.getName() : category.getName()
-                        + ". " + bundleHelper.getMessage("main.plugin.range.from")
-                        + " " + range[0] + " " + bundleHelper.
-                        getMessage("main.plugin.range.to") + " "
-                        + range[1] + ".";
-                log.println(bundleHelper.getMessage("main.plugin.category")
-                        + ": " + cat);
-                log.println(bundleHelper.getMessage("main.plugin.description")
-                        + ": " + plugin.getDescription());
-                PluginOption opt = plugin.getOption();
-                String desc = opt == null ? null : opt.getArgumentDescription();
-                log.println(bundleHelper.getMessage("main.plugin.argument")
-                        + ": " + (desc == null
-                                ? bundleHelper.getMessage("main.plugin.no.value")
-                                : desc));
-                if (opt != null) {
-                    log.println(bundleHelper.getMessage("main.plugin.option")
-                            + ": --" + opt.getName());
-                }
-                if (plugin.getAdditionalOptions() != null && !plugin.getAdditionalOptions().isEmpty()) {
-                    StringBuilder builder = new StringBuilder();
-                    for (PluginOption o : plugin.getAdditionalOptions()) {
-                        builder.append("--").append(o.getName()).append(" ").
-                                append(o.getArgumentDescription() != null
-                                        ? bundleHelper.getMessage("main.plugin.no.value")
-                                        : o.getArgumentDescription());
-                    }
-                    log.println(bundleHelper.getMessage("main.plugin.additional.options")
-                            + ": " + builder.toString());
-                }
-                log.println(bundleHelper.getMessage("main.plugin.state")
-                        + ": " + plugin.getStateDescription());
-            }
-        }
-
-        private void logBuilderOptions(List<PluginOption> options) {
-            if (options != null && !options.isEmpty()) {
-                for (PluginOption opt : options) {
-                    log.println(opt.getDescription());
-                    log.println(" --" + opt.getName() + " " + (opt.getArgumentDescription() == null
-                            ? bundleHelper.getMessage("main.plugin.no.value")
-                            : opt.getArgumentDescription()) + "\n");
-                }
-            }
-        }
-
-        public boolean listPlugins() {
-            return pluginOptions.listPlugins;
-        }
-
-        String[] getExpandedCommand() {
-            return expandedCommand;
-        }
-
-        String[] getInputCommand() {
-            return command;
-        }
-
-        String getDefaults() {
-            return defaults;
-        }
-
-        public Layer getPluginsLayer() {
-            return pluginOptions.pluginsLayer;
-        }
-    }
-
-    private PluginsOptions pluginOptions;
-    private PrintWriter log;
-    private final ResourceBundleHelper bundleHelper;
-
-    public TaskHelper(String path) {
-        if (!JLINK_BUNDLE.equals(path) && !JIMAGE_BUNDLE.equals(path)) {
-            throw new IllegalArgumentException("Invalid Bundle");
-        }
-        this.bundleHelper = new ResourceBundleHelper(path);
-    }
-
-    public <T> OptionsHelper<T> newOptionsHelper(Class<T> clazz,
-            Option<?>[] options) {
-        List<Option<T>> optionsList = new ArrayList<>();
-        for (Option<?> o : options) {
-            @SuppressWarnings("unchecked")
-            Option<T> opt = (Option<T>) o;
-            optionsList.add(opt);
-        }
-        return new OptionsHelper<>(optionsList);
-    }
-
-    public BadArgs newBadArgs(String key, Object... args) {
-        return new BadArgs(key, args);
-    }
-
-    public String getMessage(String key, Object... args) {
-        return bundleHelper.getMessage(key, args);
-    }
-
-    public void setLog(PrintWriter log) {
-        this.log = log;
-    }
-
-    public void reportError(String key, Object... args) {
-        log.println(bundleHelper.getMessage("error.prefix") + " "
-                + bundleHelper.getMessage(key, args));
-    }
-
-    public void reportUnknownError(String message) {
-        log.println(bundleHelper.getMessage("error.prefix") + " " + message);
-    }
-
-    public void warning(String key, Object... args) {
-        log.println(bundleHelper.getMessage("warn.prefix") + " "
-                + bundleHelper.getMessage(key, args));
-    }
-
-    public PluginsConfiguration getPluginsConfig(Path output) throws IOException {
-        return pluginOptions.getPluginsConfig(output);
-    }
-
-    public void showVersion(boolean full) {
-        log.println(version(full ? "full" : "release"));
-    }
-
-    public String version(String key) {
-        return System.getProperty("java.version");
-    }
-
-    // public for testing purpose
-    public static List<String> parseDefaults(String defaults) throws Exception {
-        List<String> arguments = new ArrayList<>();
-        while (!defaults.isEmpty()) {
-            int start = defaults.indexOf("--");
-            if (start < 0) {
-                throw new Exception("Invalid defaults " + defaults);
-            }
-            defaults = defaults.substring(start);
-            start = 0;
-            int end = defaults.indexOf(" ");
-            String remaining;
-
-            if (end < 0) {
-                arguments.add(defaults);
-                remaining = "";
-            } else {
-                String option = defaults.substring(start, end);
-                arguments.add(option);
-                defaults = defaults.substring(end);
-                int nextOption = defaults.indexOf("--");
-                int argEnd = nextOption < 0 ? defaults.length() : nextOption;
-                String arg = defaults.substring(0, argEnd);
-                arg = arg.replaceAll(" ", "");
-                if (!arg.isEmpty()) {
-                    arguments.add(arg);
-                }
-                remaining = defaults.substring(argEnd);
-            }
-
-            defaults = remaining;
-        }
-        return arguments;
-    }
-
-    static Layer createPluginsLayer(List<Path> paths) {
-        Path[] arr = new Path[paths.size()];
-        paths.toArray(arr);
-        ModuleFinder finder = ModuleFinder.of(arr);
-
-        // jmods are located at link-time
-        if (finder instanceof ConfigurableModuleFinder) {
-            ((ConfigurableModuleFinder) finder).configurePhase(Phase.LINK_TIME);
-        }
-
-        Configuration bootConfiguration = Layer.boot().configuration();
-
-        Configuration cf
-                = Configuration.resolve(ModuleFinder.empty(), bootConfiguration, finder);
-
-        cf = cf.bind();
-
-        ClassLoader scl = ClassLoader.getSystemClassLoader();
-        return Layer.createWithOneLoader(cf, Layer.boot(), scl);
-    }
-
-    // Display all plugins or resource only.
-    private static boolean showsPlugin(Plugin plugin, boolean showsImageBuilder) {
-        if (Utils.isEnabled(plugin) && plugin.getOption() != null && showsImageBuilder) {
-            return true;
-        }
-        return false;
-    }
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/api/Jlink.java	Mon Dec 21 14:42:17 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,352 +0,0 @@
-/*
- * Copyright (c) 2015, 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 jdk.tools.jlink.api;
-
-import java.nio.ByteOrder;
-import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import jdk.tools.jlink.JlinkTask;
-import jdk.tools.jlink.api.plugin.Plugin;
-import jdk.tools.jlink.api.plugin.PluginException;
-import jdk.tools.jlink.api.plugin.postprocessor.ExecutableImage;
-import jdk.tools.jlink.api.plugin.builder.ImageBuilder;
-
-/**
- * API for jlink tool.
- */
-public final class Jlink {
-
-    /**
-     * A plugin located inside a stack of plugins. Ordered plugin has an index
-     * in the stack.
-     */
-    public static final class OrderedPlugin {
-
-        private final int index;
-        private final boolean absIndex;
-        private final Plugin plugin;
-        private final Plugin.CATEGORY category;
-        /**
-         * A plugin inside the stack configuration.
-         *
-         * @param plugin Plugin.
-         * @param index index in the plugin stack. Must be > 0.
-         * @param absIndex true, the index is absolute otherwise index is within
-         * the plugin category.
-         * @param category The plugin category.
-         */
-        public OrderedPlugin(Plugin plugin, int index, boolean absIndex, Plugin.CATEGORY category) {
-            Objects.requireNonNull(plugin);
-            if (index < 0) {
-                throw new IllegalArgumentException("negative index");
-            }
-            this.plugin = plugin;
-            this.index = index;
-            this.absIndex = absIndex;
-            this.category = category;
-        }
-
-        /**
-         * Get the plugin index.
-         *
-         * @return the index
-         */
-        public int getIndex() {
-            return index;
-        }
-
-        public Plugin.CATEGORY getCategory() {
-            return category;
-        }
-
-        @Override
-        public String toString() {
-            return plugin.getName() + "[" + index + "]";
-        }
-
-        public Plugin getPlugin() {
-            return plugin;
-        }
-
-        /**
-         * The plugin index.
-         *
-         * @return the absolute index.
-         */
-        public boolean isAbsoluteIndex() {
-            return absIndex;
-        }
-    }
-
-    /**
-     * A complete plugin configuration. Instances of this class are used to
-     * configure jlink.
-     */
-    public static final class PluginsConfiguration {
-
-        private final List<OrderedPlugin> plugins;
-        private final ImageBuilder imageBuilder;
-        private final String lastSorterPluginName;
-
-        /**
-         * Empty plugins configuration.
-         */
-        public PluginsConfiguration() {
-            this(Collections.emptyList());
-        }
-
-        /**
-         * Plugins configuration.
-         *
-         * @param plugins List of  plugins.
-         */
-        public PluginsConfiguration(List<OrderedPlugin> plugins) {
-            this(plugins, null, null);
-        }
-
-        /**
-         * Plugins configuration with a last sorter. No sorting can occur after
-         * the last sorter plugin.
-         *
-         * @param plugins List of transformer plugins.
-         * @param imageBuilder Image builder (null default builder).
-         * @param lastSorterPluginName Name of last sorter plugin, no sorting
-         * can occur after it.
-         */
-        public PluginsConfiguration(List<OrderedPlugin> plugins,
-                ImageBuilder imageBuilder, String lastSorterPluginName) {
-            this.plugins = plugins == null ? Collections.emptyList()
-                    : plugins;
-            this.imageBuilder = imageBuilder;
-            this.lastSorterPluginName = lastSorterPluginName;
-        }
-
-        /**
-         * @return the plugins
-         */
-        public List<OrderedPlugin> getPlugins() {
-            return plugins;
-        }
-
-        /**
-         * @return the imageBuilder
-         */
-        public ImageBuilder getImageBuilder() {
-            return imageBuilder;
-        }
-
-        /**
-         * @return the lastSorterPluginName
-         */
-        public String getLastSorterPluginName() {
-            return lastSorterPluginName;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder builder = new StringBuilder();
-            builder.append("imagebuilder=").append(imageBuilder).append("\n");
-            StringBuilder pluginsBuilder = new StringBuilder();
-            for (OrderedPlugin p : plugins) {
-                pluginsBuilder.append(p).append(",");
-            }
-            builder.append("plugins=").append(pluginsBuilder).append("\n");
-            builder.append("lastsorter=").append(lastSorterPluginName).append("\n");
-
-            return builder.toString();
-        }
-    }
-
-    /**
-     * Jlink configuration. Instances of this class are used to configure jlink.
-     */
-    public static final class JlinkConfiguration {
-
-        private final List<Path> modulepaths;
-        private final Path output;
-        private final Set<String> modules;
-        private final Set<String> limitmods;
-
-        private final ByteOrder endian;
-
-        /**
-         * jlink configuration,
-         *
-         * @param output Output directory, must not exist.
-         * @param modulepaths Modules paths
-         * @param modules Root modules to resolve
-         * @param limitmods Limit the universe of observable modules
-         * @param pluginpaths Custom plugins module path
-         * @param endian Jimage byte order. Native order by default
-         */
-        public JlinkConfiguration(Path output,
-                List<Path> modulepaths,
-                Set<String> modules,
-                Set<String> limitmods,
-                List<Path> pluginpaths,
-                ByteOrder endian) {
-            this.output = output;
-            this.modulepaths = modulepaths == null ? Collections.emptyList() : modulepaths;
-            this.modules = modules == null ? Collections.emptySet() : modules;
-            this.limitmods = limitmods == null ? Collections.emptySet() : limitmods;
-            this.endian = endian == null ? ByteOrder.nativeOrder() : endian;
-        }
-
-        /**
-         * jlink configuration,
-         *
-         * @param output Output directory, must not exist.
-         * @param modulepaths Modules paths
-         * @param modules Root modules to resolve
-         * @param limitmods Limit the universe of observable modules
-         * @param pluginpaths Custom plugins module path
-         */
-        public JlinkConfiguration(Path output,
-                List<Path> modulepaths,
-                Set<String> modules,
-                Set<String> limitmods,
-                List<Path> pluginpaths) {
-            this(output, modulepaths, modules, limitmods, pluginpaths,
-                    ByteOrder.nativeOrder());
-        }
-
-        /**
-         * @return the modulepaths
-         */
-        public List<Path> getModulepaths() {
-            return modulepaths;
-        }
-
-        /**
-         * @return the byte ordering
-         */
-        public ByteOrder getByteOrder() {
-            return endian;
-        }
-
-        /**
-         * @return the output
-         */
-        public Path getOutput() {
-            return output;
-        }
-
-        /**
-         * @return the modules
-         */
-        public Set<String> getModules() {
-            return modules;
-        }
-
-        /**
-         * @return the limitmods
-         */
-        public Set<String> getLimitmods() {
-            return limitmods;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder builder = new StringBuilder();
-
-            builder.append("output=").append(output).append("\n");
-            StringBuilder pathsBuilder = new StringBuilder();
-            for (Path p : modulepaths) {
-                pathsBuilder.append(p).append(",");
-            }
-            builder.append("modulepaths=").append(pathsBuilder).append("\n");
-
-            StringBuilder modsBuilder = new StringBuilder();
-            for (String p : modules) {
-                modsBuilder.append(p).append(",");
-            }
-            builder.append("modules=").append(modsBuilder).append("\n");
-
-            StringBuilder limitsBuilder = new StringBuilder();
-            for (String p : limitmods) {
-                limitsBuilder.append(p).append(",");
-            }
-            builder.append("limitmodules=").append(limitsBuilder).append("\n");
-            builder.append("endian=").append(endian).append("\n");
-            return builder.toString();
-        }
-    }
-
-    /**
-     * Jlink instance constructor, if a security manager is set, the jlink
-     * permission is checked.
-     */
-    public Jlink() {
-        if (System.getSecurityManager() != null) {
-            System.getSecurityManager().
-                    checkPermission(new JlinkPermission("jlink"));
-        }
-    }
-
-    /**
-     * Build the image.
-     *
-     * @param config Jlink config, must not be null.
-     * @throws PluginException
-     */
-    public void build(JlinkConfiguration config) {
-        build(config, null);
-    }
-
-    /**
-     * Build the image with a plugin configuration.
-     *
-     * @param config Jlink config, must not be null.
-     * @param pluginsConfig Plugins config, can be null
-     * @throws PluginException
-     */
-    public void build(JlinkConfiguration config, PluginsConfiguration pluginsConfig) {
-        Objects.requireNonNull(config);
-        try {
-            JlinkTask.createImage(config, pluginsConfig);
-        } catch (Exception ex) {
-            throw new PluginException(ex);
-        }
-    }
-
-    /**
-     * Post process the image with a plugin configuration.
-     *
-     * @param image Existing image.
-     * @param transformerPlugins Plugins config, cannot be null
-     */
-    public void postProcess(ExecutableImage image, List<OrderedPlugin> transformerPlugins) {
-        Objects.requireNonNull(image);
-        Objects.requireNonNull(transformerPlugins);
-        try {
-            JlinkTask.postProcessImage(image, transformerPlugins);
-        } catch (Exception ex) {
-            throw new PluginException(ex);
-        }
-    }
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/api/JlinkPermission.java	Mon Dec 21 14:42:17 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2015, 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 jdk.tools.jlink.api;
-
-import java.security.BasicPermission;
-
-/**
- * The permission required to use jlink API. The permission target_name is
- * "jlink". e.g.: permission jdk.tools.jlink.plugins.JlinkPermission "jlink";
- *
- */
-public final class JlinkPermission extends BasicPermission {
-
-    private static final long serialVersionUID = -3687912306077727801L;
-
-    public JlinkPermission(String name) {
-        super(name);
-    }
-
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/Plugin.java	Mon Dec 21 14:42:17 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,246 +0,0 @@
-/*
- * Copyright (c) 2015, 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 jdk.tools.jlink.api.plugin;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
-
-/**
- * Implement this interface to develop your own plugin.
- */
-public interface Plugin {
-
-    public static final class PluginOption {
-
-        private final String name;
-        private final String description;
-        private final String argumentDescription;
-        private final boolean hasOnOffArgument;
-        private final boolean isEnabled;
-
-        PluginOption(String name,
-                String description,
-                String argumentDescription,
-                boolean hasOnOffArgument,
-                boolean isEnabled) {
-            Objects.requireNonNull(name);
-            this.name = name;
-            this.description = description;
-            this.argumentDescription = argumentDescription;
-            this.hasOnOffArgument = hasOnOffArgument;
-            this.isEnabled = isEnabled;
-        }
-
-        public String getName() {
-            return name;
-        }
-
-        public String getDescription() {
-            return description;
-        }
-
-        public String getArgumentDescription() {
-            return argumentDescription;
-        }
-
-        public boolean hasOnOffArgument() {
-            return hasOnOffArgument;
-        }
-
-        public boolean isEnabled() {
-            return isEnabled;
-        }
-
-        @Override
-        public String toString() {
-            return name + ":" + description;
-        }
-
-        @Override
-        public int hashCode() {
-            int hash = 5;
-            hash = 37 * hash + Objects.hashCode(this.name);
-            return hash;
-        }
-
-        @Override
-        public boolean equals(Object other) {
-            if (!(other instanceof PluginOption)) {
-                return false;
-            }
-            PluginOption po = (PluginOption) other;
-            return name.equals(po.name);
-        }
-
-        public static final class Builder {
-
-            public static final String ON_ARGUMENT = "on";
-            public static final String OFF_ARGUMENT = "off";
-
-            private final String name;
-            private String description;
-            private String argumentDescription;
-            private boolean hasOnOffArgument;
-            private boolean isEnabled;
-
-            public Builder(String name) {
-                Objects.requireNonNull(name);
-                this.name = name;
-            }
-
-            public Builder description(String description) {
-                this.description = description;
-                return this;
-            }
-
-            public Builder argumentDescription(String argumentDescription) {
-                this.argumentDescription = argumentDescription;
-                return this;
-            }
-
-            public Builder hasOnOffArgument() {
-                this.hasOnOffArgument = true;
-                this.argumentDescription = PluginsResourceBundle.getMessage("onoff.argument");
-                return this;
-            }
-
-            public Builder isEnabled() {
-                hasOnOffArgument();
-                isEnabled = true;
-                return this;
-            }
-
-            public PluginOption build() {
-                return new PluginOption(name, description, argumentDescription,
-                        hasOnOffArgument, isEnabled);
-            }
-        }
-
-    }
-
-    public interface PluginType {
-
-        public String getName();
-    }
-
-    public enum ORDER implements PluginType {
-        FIRST("FIRST"),
-        LAST("LAST"),
-        ANY("ANY");
-
-        private final String name;
-
-        ORDER(String name) {
-            this.name = name;
-        }
-
-        @Override
-        public String getName() {
-            return name;
-        }
-    }
-
-    /**
-     * Order of categories:
-     * <ol>
-     * <li>FILTER: Filter in/out resources or files.</li>
-     * <li>TRANSFORMER: Transform resources or files(eg: refactoring, bytecode
-     * manipulation).</li>
-     * <li>MODULEINFO_TRANSFORMER: Transform only module-info.class</li>
-     * <li>SORTER: Sort resources within the resource container.</li>
-     * <li>COMPRESSOR: Compress resource within the resouce containers.</li>
-     * <li>BUILDER: Layout image on disk.</li>
-     * <li>VERIFIER: Does some image verification.</li>
-     * <li>PROCESSOR: Does some post processing on image.</li>
-     * <li>PACKAGER: Final processing</li>
-     * </ol>
-     */
-    public enum CATEGORY implements PluginType {
-        FILTER("FILTER"),
-        TRANSFORMER("TRANSFORMER"),
-        MODULEINFO_TRANSFORMER("MODULEINFO_TRANSFORMER"),
-        SORTER("SORTER"),
-        COMPRESSOR("COMPRESSOR"),
-        BUILDER("BUILDER"),
-        VERIFIER("VERIFIER"),
-        PROCESSOR("PROCESSOR"),
-        PACKAGER("PACKAGER");
-
-        private final String name;
-
-        CATEGORY(String name) {
-            this.name = name;
-        }
-
-        @Override
-        public String getName() {
-            return name;
-        }
-    }
-
-    public enum STATE {
-        ENABLED,
-        FUNCTIONAL
-    }
-
-    public abstract Set<PluginType> getType();
-
-    public default Set<STATE> getState() {
-        return EnumSet.of(STATE.ENABLED, STATE.FUNCTIONAL);
-    }
-
-    public String getName();
-
-    public String getDescription();
-
-    public default List<PluginOption> getAdditionalOptions() {
-        return Collections.emptyList();
-    }
-
-    public PluginOption getOption();
-
-    /**
-     * Return a message indicating the status of the provider.
-     *
-     * @return A status description.
-     */
-    public default String getStateDescription() {
-        return getState().contains(STATE.FUNCTIONAL)
-                ? PluginsResourceBundle.getMessage("main.status.ok")
-                : PluginsResourceBundle.getMessage("main.status.not.ok");
-    }
-
-    /**
-     * Configure the plugin based on the passed configuration.
-     *
-     * @param config The plugin configuration.
-     */
-    public void configure(Map<PluginOption, String> config);
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/PluginException.java	Mon Dec 21 14:42:17 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2015, 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 jdk.tools.jlink.api.plugin;
-
-/**
- * An unchecked exception thrown by jlink plugin API for unrecoverable
- * conditions.
- */
-public final class PluginException extends RuntimeException {
-
-    private static final long serialVersionUID = 7117982019443100395L;
-
-    public PluginException() {
-
-    }
-
-    public PluginException(Throwable ex) {
-        super(ex);
-    }
-
-    public PluginException(String msg) {
-        super(msg);
-    }
-
-    public PluginException(String msg, Throwable thr) {
-        super(msg, thr);
-    }
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/builder/DefaultImageBuilder.java	Mon Dec 21 14:42:17 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,402 +0,0 @@
-/*
- * Copyright (c) 2015, 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 jdk.tools.jlink.api.plugin.builder;
-
-import jdk.tools.jlink.api.plugin.postprocessor.ExecutableImage;
-import jdk.tools.jlink.api.plugin.PluginException;
-import java.io.BufferedOutputStream;
-import java.io.BufferedWriter;
-import java.io.ByteArrayInputStream;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.UncheckedIOException;
-import java.io.Writer;
-import java.lang.module.ModuleDescriptor;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
-import java.nio.file.attribute.PosixFileAttributeView;
-import java.nio.file.attribute.PosixFilePermission;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Properties;
-import java.util.Set;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption;
-import jdk.tools.jlink.internal.BasicImageWriter;
-import jdk.tools.jlink.internal.JvmHandler;
-import jdk.tools.jlink.internal.plugins.FileCopierPlugin.SymImageFile;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.transformer.Pool.Module;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleData;
-
-/**
- *
- * Default Image Builder. This builder create the default Java image layout.
- */
-public class DefaultImageBuilder implements ImageBuilder {
-    /**
-     * The default java executable Image.
-     */
-    static class DefaultExecutableImage extends ExecutableImage {
-
-        public DefaultExecutableImage(Path home, Set<String> modules) {
-            super(home, modules, createArgs(home));
-        }
-
-        private static List<String> createArgs(Path home) {
-            Objects.requireNonNull(home);
-            List<String> javaArgs = new ArrayList<>();
-            javaArgs.add(home.resolve("bin").
-                    resolve(DefaultImageBuilderProvider.
-                            getJavaProcessName()).toString());
-            return javaArgs;
-        }
-
-        @Override
-        public void storeLaunchArgs(List<String> args) {
-            try {
-                patchScripts(this, args);
-            } catch (IOException ex) {
-                throw new UncheckedIOException(ex);
-            }
-        }
-    }
-
-    private final Path root;
-    private final Path mdir;
-    private final boolean genBom;
-    private final Set<String> modules = new HashSet<>();
-
-    /**
-     * Default image builder constructor.
-     *
-     * @param properties Properties to configure the builder.
-     * @param root The image directory.
-     * @throws IOException
-     */
-    public DefaultImageBuilder(Map<PluginOption, String> properties, Path root) throws IOException {
-        Objects.requireNonNull(root);
-
-        genBom = properties.containsKey(DefaultImageBuilderProvider.GEN_BOM_OPTION);
-
-        this.root = root;
-        this.mdir = root.resolve("lib").resolve("modules");
-        Files.createDirectories(mdir);
-    }
-
-    private void storeFiles(Set<String> modules, String bom) throws IOException {
-        // Retrieve release file from JDK home dir.
-        String path = System.getProperty("java.home");
-        File f = new File(path, "release");
-        Properties release = null;
-        if (!f.exists()) {
-            // XXX When jlink is exposed to user.
-            //System.err.println("WARNING, no release file found in " + path +
-            //     ". release file not added to generated image");
-        } else {
-            release = new Properties();
-            try (FileInputStream fi = new FileInputStream(f)) {
-                release.load(fi);
-            }
-            addModules(release, modules);
-        }
-
-        if (release != null) {
-            File r = new File(root.toFile(), "release");
-            try (FileOutputStream fo = new FileOutputStream(r)) {
-                release.store(fo, null);
-            }
-        }
-        // Generate bom
-        if (genBom) {
-            File bomFile = new File(root.toFile(), "bom");
-            createUtf8File(bomFile, bom);
-        }
-    }
-
-    private void addModules(Properties release, Set<String> modules) throws IOException {
-        if (release != null) {
-            StringBuilder builder = new StringBuilder();
-            int i = 0;
-            for (String m : modules) {
-                builder.append(m);
-                if (i < modules.size() - 1) {
-                    builder.append(",");
-                }
-                i++;
-            }
-            release.setProperty("MODULES", builder.toString());
-        }
-    }
-
-    @Override
-     public void storeFiles(Pool allContent, List<ModuleData> removedFiles,
-            String bom) {
-        try {
-            Pool files = new JvmHandler().handlePlatforms(allContent, removedFiles);
-
-            for (ModuleData f : files.getContent()) {
-               if (!f.getType().equals(Pool.ModuleDataType.CLASS_OR_RESOURCE)) {
-                    accept(f);
-                }
-            }
-            for (Module m : allContent.getModules()) {
-                // Only add modules that contain packages
-                if (!m.getAllPackages().isEmpty()) {
-                    if(m.getName().equals("$jlink-file-copier")) {
-                        System.err.println("MODULE " + m + " has packages " + m.getAllPackages());
-                    }
-                    modules.add(m.getName());
-                }
-            }
-            storeFiles(modules, bom);
-
-            if (Files.getFileStore(root).supportsFileAttributeView(PosixFileAttributeView.class)) {
-                // launchers in the bin directory need execute permission
-                Path bin = root.resolve("bin");
-                if (Files.isDirectory(bin)) {
-                    Files.list(bin)
-                            .filter(f -> !f.toString().endsWith(".diz"))
-                            .filter(f -> Files.isRegularFile(f))
-                            .forEach(this::setExecutable);
-                }
-
-                // jspawnhelper is in lib or lib/<arch>
-                Path lib = root.resolve("lib");
-                if (Files.isDirectory(lib)) {
-                    Files.find(lib, 2, (path, attrs) -> {
-                        return path.getFileName().toString().equals("jspawnhelper");
-                    }).forEach(this::setExecutable);
-                }
-            }
-
-            prepareApplicationFiles(allContent, modules);
-        } catch (IOException ex) {
-            throw new PluginException(ex);
-        }
-    }
-
-    protected void prepareApplicationFiles(Pool resources, Set<String> modules) throws IOException {
-        // generate launch scripts for the modules with a main class
-        for (String module : modules) {
-            String path = "/" + module + "/module-info.class";
-            ModuleData res = resources.get(path);
-            if (res == null) {
-                throw new IOException("module-info not found for " + module);
-            }
-            Optional<String> mainClass;
-            ByteArrayInputStream stream = new ByteArrayInputStream(res.getBytes());
-            mainClass = ModuleDescriptor.read(stream).mainClass();
-            if (mainClass.isPresent()) {
-                Path cmd = root.resolve("bin").resolve(module);
-                if (!Files.exists(cmd)) {
-                    StringBuilder sb = new StringBuilder();
-                    sb.append("#!/bin/sh")
-                            .append("\n");
-                    sb.append("JLINK_VM_OPTIONS=")
-                            .append("\n");
-                    sb.append("DIR=`dirname $0`")
-                            .append("\n");
-                    sb.append("$DIR/java $JLINK_VM_OPTIONS -m ")
-                            .append(module).append('/')
-                            .append(mainClass.get())
-                            .append(" $@\n");
-
-                    try (BufferedWriter writer = Files.newBufferedWriter(cmd,
-                            StandardCharsets.ISO_8859_1,
-                            StandardOpenOption.CREATE_NEW)) {
-                        writer.write(sb.toString());
-                    }
-                    if (Files.getFileStore(root.resolve("bin"))
-                            .supportsFileAttributeView(PosixFileAttributeView.class)) {
-                        setExecutable(cmd);
-                    }
-                }
-            }
-        }
-    }
-
-    @Override
-    public DataOutputStream getJImageOutputStream() {
-        try {
-            Path jimageFile = mdir.resolve(BasicImageWriter.BOOT_IMAGE_NAME);
-            OutputStream fos = Files.newOutputStream(jimageFile);
-            BufferedOutputStream bos = new BufferedOutputStream(fos);
-            return new DataOutputStream(bos);
-        } catch (IOException ex) {
-            throw new UncheckedIOException(ex);
-        }
-    }
-
-    private void accept(ModuleData file) throws IOException {
-        String name = file.getPath();
-        String filename = name.substring(name.indexOf('/') + 1);
-        try (InputStream in = file.stream()) {
-            switch (file.getType()) {
-                case NATIVE_LIB:
-                    writeEntry(in, destFile(nativeDir(filename), filename));
-                    break;
-                case NATIVE_CMD:
-                    Path path = destFile("bin", filename);
-                    writeEntry(in, path);
-                    path.toFile().setExecutable(true);
-                    break;
-                case CONFIG:
-                    writeEntry(in, destFile("conf", filename));
-                    break;
-                case OTHER:
-                    int i = name.indexOf('/');
-                    String dir = i < 0 ? "" : name.substring(0, i);
-                    if (file instanceof SymImageFile) {
-                        SymImageFile sym = (SymImageFile) file;
-                        Path target = root.resolve(sym.getTargetPath());
-                        if (!Files.exists(target)) {
-                            throw new IOException("Sym link target " + target
-                                    + " doesn't exist");
-                        }
-                        writeSymEntry(destFile(dir, filename), target);
-                    } else {
-                        writeEntry(in, destFile(dir, filename));
-                    }
-                    break;
-                default:
-                    //throw new InternalError("unexpected entry: " + name + " " + zipfile.toString()); //TODO
-                    throw new InternalError("unexpected entry: " + name + " " + name);
-            }
-        }
-    }
-
-    private Path destFile(String dir, String filename) {
-        return root.resolve(dir).resolve(filename);
-    }
-
-    private void writeEntry(InputStream in, Path dstFile) throws IOException {
-        Objects.requireNonNull(in);
-        Objects.requireNonNull(dstFile);
-        Files.createDirectories(Objects.requireNonNull(dstFile.getParent()));
-        Files.copy(in, dstFile);
-    }
-
-    private void writeSymEntry(Path dstFile, Path target) throws IOException {
-        Objects.requireNonNull(dstFile);
-        Objects.requireNonNull(target);
-        Files.createDirectories(Objects.requireNonNull(dstFile.getParent()));
-        Files.createLink(dstFile, target);
-    }
-
-    private static String nativeDir(String filename) {
-        if (isWindows()) {
-            if (filename.endsWith(".dll") || filename.endsWith(".diz")
-                    || filename.endsWith(".pdb") || filename.endsWith(".map")) {
-                return "bin";
-            } else {
-                return "lib";
-            }
-        } else {
-            return "lib";
-        }
-    }
-
-    static boolean isWindows() {
-        return System.getProperty("os.name").startsWith("Windows");
-    }
-
-    static boolean isMac() {
-        return System.getProperty("os.name").startsWith("Mac OS");
-    }
-
-    /**
-     * chmod ugo+x file
-     */
-    private void setExecutable(Path file) {
-        try {
-            Set<PosixFilePermission> perms = Files.getPosixFilePermissions(file);
-            perms.add(PosixFilePermission.OWNER_EXECUTE);
-            perms.add(PosixFilePermission.GROUP_EXECUTE);
-            perms.add(PosixFilePermission.OTHERS_EXECUTE);
-            Files.setPosixFilePermissions(file, perms);
-        } catch (IOException ioe) {
-            throw new UncheckedIOException(ioe);
-        }
-    }
-
-    private static void createUtf8File(File file, String content) throws IOException {
-        try (OutputStream fout = new FileOutputStream(file);
-                Writer output = new OutputStreamWriter(fout, "UTF-8")) {
-            output.write(content);
-        }
-    }
-
-    @Override
-    public ExecutableImage getExecutableImage() {
-        return new DefaultExecutableImage(root, modules);
-    }
-
-    // This is experimental, we should get rid-off the scripts in a near future
-    private static void patchScripts(ExecutableImage img, List<String> args) throws IOException {
-        Objects.requireNonNull(args);
-        if (!args.isEmpty()) {
-            Files.find(img.getHome().resolve("bin"), 2, (path, attrs) -> {
-                return img.getModules().contains(path.getFileName().toString());
-            }).forEach((p) -> {
-                try {
-                    String pattern = "JLINK_VM_OPTIONS=";
-                    byte[] content = Files.readAllBytes(p);
-                    String str = new String(content, StandardCharsets.UTF_8);
-                    int index = str.indexOf(pattern);
-                    StringBuilder builder = new StringBuilder();
-                    if (index != -1) {
-                        builder.append(str.substring(0, index)).
-                                append(pattern);
-                        for (String s : args) {
-                            builder.append(s).append(" ");
-                        }
-                        String remain = str.substring(index + pattern.length());
-                        builder.append(remain);
-                        str = builder.toString();
-                        try (BufferedWriter writer = Files.newBufferedWriter(p,
-                                StandardCharsets.ISO_8859_1,
-                                StandardOpenOption.WRITE)) {
-                            writer.write(str);
-                        }
-                    }
-                } catch (IOException ex) {
-                    throw new RuntimeException(ex);
-                }
-            });
-        }
-    }
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/builder/DefaultImageBuilderProvider.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/builder/DefaultImageBuilderProvider.java	Mon Dec 21 14:42:19 2015 +0100
@@ -1,121 +0,0 @@
-/*
- * Copyright (c) 2015, 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 jdk.tools.jlink.api.plugin.builder;
-
-import jdk.tools.jlink.api.plugin.postprocessor.ExecutableImage;
-import java.io.FileInputStream;
-import jdk.tools.jlink.api.plugin.PluginException;
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption.Builder;
-import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
-import static jdk.tools.jlink.api.plugin.builder.DefaultImageBuilder.isWindows;
-
-/**
- * Default Image Builder provider.
- */
-public class DefaultImageBuilderProvider {
-
-    public static final PluginOption IMAGE_PATH_OPTION
-            = new Builder("jlink.image.path").build();
-
-    public static final String GEN_BOM = "genbom";
-    public static final String JIMAGE_NAME_PROPERTY = "jimage.name";
-    public static final String NAME = "default-image-builder";
-    static final List<PluginOption> OPTIONS = new ArrayList<>();
-    public static final PluginOption GEN_BOM_OPTION;
-
-    static {
-        GEN_BOM_OPTION = new Builder(GEN_BOM).description(
-                PluginsResourceBundle.getOption(NAME, GEN_BOM)).build();
-        OPTIONS.add(GEN_BOM_OPTION);
-    }
-
-    public PluginOption getOption() {
-        return new Builder(NAME).description(
-                PluginsResourceBundle.getDescription(NAME)).build();
-    }
-
-    public List<PluginOption> getAdditionalOptions() {
-        return OPTIONS;
-    }
-
-    static String getJavaProcessName() {
-        return isWindows() ? "java.exe" : "java";
-    }
-
-    public ImageBuilder newPlugin(Map<PluginOption, String> config) {
-        try {
-            Path imageOutDir = Paths.get(config.get(IMAGE_PATH_OPTION));
-            if (Files.exists(imageOutDir)) {
-                throw new PluginException(PluginsResourceBundle.
-                        getMessage("err.dir.already.exits", imageOutDir));
-            }
-            return new DefaultImageBuilder(config, imageOutDir);
-        } catch (IOException ex) {
-            throw new UncheckedIOException(ex);
-        }
-    }
-
-    public static ExecutableImage getExecutableImage(Path root) {
-        if (Files.exists(root.resolve("bin").resolve(getJavaProcessName()))) {
-            return new DefaultImageBuilder.DefaultExecutableImage(root,
-                    retrieveModules(root));
-        }
-        return null;
-    }
-
-    private static Set<String> retrieveModules(Path root) {
-        Path releaseFile = root.resolve("release");
-        Set<String> modules = new HashSet<>();
-        if (Files.exists(releaseFile)) {
-            Properties release = new Properties();
-            try (FileInputStream fi = new FileInputStream(releaseFile.toFile())) {
-                release.load(fi);
-            } catch (IOException ex) {
-                System.err.println("Can't read release file " + ex);
-            }
-            String mods = release.getProperty("MODULES");
-            if (mods != null) {
-                String[] arr = mods.split(",");
-                for (String m : arr) {
-                    modules.add(m);
-                }
-
-            }
-        }
-        return modules;
-    }
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/builder/ExecutableImage.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/builder/ExecutableImage.java	Mon Dec 21 14:42:19 2015 +0100
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2015, 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 jdk.tools.jlink.api.plugin.postprocessor;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import jdk.tools.jlink.api.plugin.transformer.Pool.Module;
-
-/**
- * An executable Image.
- */
-public abstract class ExecutableImage {
-
-    private final Path home;
-    private final List<String> args;
-    private final Set<String> modules;
-
-    protected ExecutableImage(Path home, Set<String> modules,
-            List<String> args) {
-        Objects.requireNonNull(home);
-        Objects.requireNonNull(args);
-        if (!Files.exists(home)) {
-            throw new IllegalArgumentException("Invalid image home");
-        }
-        this.home = home;
-        this.modules = Collections.unmodifiableSet(modules);
-        this.args = Collections.unmodifiableList(args);
-    }
-
-    public Path getHome() {
-        return home;
-    }
-
-    /**
-     * The names of the modules located in the image.
-     *
-     * @return The set of modules.
-     */
-    public Set<String> getModules() {
-        return modules;
-    }
-
-    public List<String> getExecutionArgs() {
-        return args;
-    }
-
-    public abstract void storeLaunchArgs(List<String> args);
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/builder/ImageBuilder.java	Mon Dec 21 14:42:17 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/*
- * Copyright (c) 2015, 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 jdk.tools.jlink.api.plugin.builder;
-
-import jdk.tools.jlink.api.plugin.postprocessor.ExecutableImage;
-import java.io.DataOutputStream;
-import java.util.List;
-import jdk.tools.jlink.api.plugin.PluginException;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleData;
-
-/**
- * Implement this interface to develop your own image layout. First the jimage
- * is written onto the output stream returned by getOutputStream then storeFiles
- * is called.
- */
-public interface ImageBuilder {
-
-    /**
-     * Store the external files.
-     *
-     * @param content Pool of module content.
-     * @param removed List of files that have been removed (if any).
-     * @param bom The options used to build the image
-     * file.
-     * @throws PluginException
-     */
-    public void storeFiles(Pool content, List<ModuleData> removed,
-            String bom);
-
-    /**
-     * The OutputStream to store the jimage file.
-     *
-     * @return The output stream
-     * @throws PluginException
-     */
-    public DataOutputStream getJImageOutputStream();
-
-    /**
-     * Gets the executable image that is generated.
-     *
-     * @return The executable image.
-     * @throws PluginException
-     */
-    public ExecutableImage getExecutableImage();
-
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/postprocessor/PostProcessorPlugin.java	Mon Dec 21 14:42:17 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2015, 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 jdk.tools.jlink.api.plugin.postprocessor;
-
-import jdk.tools.jlink.api.plugin.Plugin;
-import java.util.List;
-
-/**
- * Implement this interface to develop your own plugin.
- * PostProcessing can
- * modify the image content.
- */
-public interface PostProcessorPlugin extends Plugin {
-
-    /**
-     * Post process an image.
-     *
-     * @param image The executable image.
-     * @return The list of arguments to add to launchers if any.
-     */
-    public List<String> process(ExecutableImage image);
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/transformer/Pool.java	Mon Dec 21 14:42:17 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,372 +0,0 @@
-/*
- * Copyright (c) 2015, 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 jdk.tools.jlink.api.plugin.transformer;
-
-import jdk.tools.jlink.api.plugin.PluginException;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UncheckedIOException;
-import java.lang.module.ModuleDescriptor;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import jdk.tools.jlink.internal.ImageFileCreator;
-
-/**
- * Pool of module data.
- *
- */
-public abstract class Pool {
-
-    public interface Visitor {
-
-        /**
-         * Called for each visited file.
-         *
-         * @param content The file to deal with.
-         * @return A resource or null if the passed resource is to be removed
-         * from the image.
-         * @throws PluginException
-         */
-        public ModuleData visit(ModuleData content);
-    }
-
-    public static enum ModuleDataType {
-
-        CLASS_OR_RESOURCE,
-        CONFIG,
-        NATIVE_CMD,
-        NATIVE_LIB,
-        OTHER;
-    }
-
-    public interface Module {
-         public String getName();
-
-        public ModuleData get(String path);
-
-        public ModuleDescriptor getDescriptor();
-
-        public void add(ModuleData data);
-
-        public Set<String> getAllPackages();
-    }
-
-    private class ModuleImpl implements Module {
-        private final Map<String, ModuleData> moduleContent = new LinkedHashMap<>();
-        private ModuleDescriptor descriptor;
-        private final String name;
-        private ModuleImpl(String name) {
-            this.name = name;
-        }
-
-        @Override
-        public String getName() {
-            return name;
-        }
-
-        @Override
-        public ModuleData get(String path) {
-            if (!path.startsWith("/")) {
-                path = "/" + path;
-            }
-            if (!path.startsWith("/" + name)) {
-                path = "/" + name + path;
-            }
-            return moduleContent.get(path);
-        }
-
-        @Override
-        public ModuleDescriptor getDescriptor() {
-            if(descriptor == null) {
-                String p = "/" + name + "/module-info.class";
-                ModuleData content = moduleContent.get(p);
-                if(content == null) {
-                    throw new PluginException("No module-info for " + name +
-                            " module");
-                }
-                ByteBuffer bb = ByteBuffer.wrap(content.getBytes());
-                descriptor = ModuleDescriptor.read(bb);
-            }
-            return descriptor;
-        }
-
-        @Override
-        public void add(ModuleData data) {
-            if (isReadOnly()) {
-                throw new PluginException("pool is readonly");
-            }
-            Objects.requireNonNull(data);
-            if (!data.getModule().equals(name)) {
-                throw new PluginException("Can't add resource " + data.getPath()
-                        + " to module " + name);
-            }
-            Pool.this.add(data);
-        }
-
-        @Override
-        public Set<String> getAllPackages() {
-            Set<String> pkgs = new HashSet<>();
-            moduleContent.values().stream().filter(m -> m.getType().
-                    equals(ModuleDataType.CLASS_OR_RESOURCE)).forEach((res) -> {
-                // Module metadata only contains packages with .class files
-                if (ImageFileCreator.isClassPackage(res.getPath())) {
-                    String[] split = ImageFileCreator.splitPath(res.getPath());
-                    String pkg = split[1];
-                    if (pkg != null && !pkg.isEmpty()) {
-                        pkgs.add(pkg);
-                    }
-                }
-            });
-            return pkgs;
-        }
-
-        @Override
-        public String toString() {
-            return getName();
-        }
-    }
-
-    public static class ModuleData {
-
-        private final ModuleDataType type;
-        private final String path;
-        private final String module;
-        private final long length;
-        private final InputStream stream;
-
-        private byte[] buffer;
-        public ModuleData(String module, String path, ModuleDataType type,
-                InputStream stream, long length) {
-            Objects.requireNonNull(module);
-            Objects.requireNonNull(path);
-            Objects.requireNonNull(type);
-            Objects.requireNonNull(stream);
-            this.path = path;
-            this.type = type;
-            this.module = module;
-            this.stream = stream;
-            this.length = length;
-        }
-
-        public final String getModule() {
-            return module;
-        }
-
-        public final String getPath() {
-            return path;
-        }
-
-        public final ModuleDataType getType() {
-            return type;
-        }
-
-        public byte[] getBytes() {
-            if (buffer == null) {
-                try {
-                    buffer = stream.readAllBytes();
-                } catch (IOException ex) {
-                    throw new UncheckedIOException(ex);
-                }
-            }
-            return buffer;
-        }
-
-        public long getLength() {
-            return length;
-        }
-
-        public InputStream stream() {
-            return stream;
-        }
-
-        @Override
-        public int hashCode() {
-            int hash = 7;
-            hash = 89 * hash + Objects.hashCode(this.path);
-            return hash;
-        }
-
-        @Override
-        public boolean equals(Object other) {
-            if (!(other instanceof ModuleData)) {
-                return false;
-            }
-            ModuleData f = (ModuleData) other;
-            return f.path.equals(path);
-        }
-
-        @Override
-        public String toString() {
-            return getPath();
-        }
-    }
-
-    private final Map<String, ModuleData> resources = new LinkedHashMap<>();
-    private final Map<String, ModuleImpl> modules = new LinkedHashMap<>();
-
-    private final ByteOrder order;
-
-    protected Pool() {
-        this(ByteOrder.nativeOrder());
-    }
-
-    protected Pool(ByteOrder order) {
-        Objects.requireNonNull(order);
-        this.order = order;
-    }
-
-    /**
-     * Read only state.
-     *
-     * @return true if readonly false otherwise.
-     */
-    public abstract boolean isReadOnly();
-
-    /**
-     * Add a resource.
-     *
-     * @param resource The Resource to add.
-     */
-    public void add(ModuleData resource) {
-        if (isReadOnly()) {
-            throw new PluginException("pool is readonly");
-        }
-        Objects.requireNonNull(resource);
-        if (resources.get(resource.getPath()) != null) {
-            throw new PluginException("Resource " + resource.getPath()
-                    + " already present");
-        }
-        ModuleImpl m = modules.get(resource.getModule());
-        if(m == null) {
-            m = new ModuleImpl(resource.getModule());
-            modules.put(resource.getModule(), m);
-        }
-        resources.put(resource.getPath(), resource);
-        m.moduleContent.put(resource.getPath(), resource);
-    }
-
-    /**
-     * Retrieves the module of the provided name.
-     * @param name The module name
-     * @return the module or null if the module doesn't exist.
-     */
-    public Module getModule(String name) {
-        Objects.requireNonNull(name);
-        return modules.get(name);
-    }
-
-    /**
-     * The collection of modules contained in this pool.
-     * @return The collection of modules.
-     */
-    public Collection<Module> getModules() {
-        return Collections.unmodifiableCollection(modules.values());
-    }
-
-    /**
-     * Get all resources contained in this pool instance.
-     *
-     * @return The collection of resources;
-     */
-    public Collection<ModuleData> getContent() {
-        return Collections.unmodifiableCollection(resources.values());
-    }
-
-    /**
-     * Get the resource for the passed path.
-     *
-     * @param path A resource path
-     * @return A Resource instance or null if the resource is not found
-     */
-    public ModuleData get(String path) {
-        Objects.requireNonNull(path);
-        return resources.get(path);
-    }
-
-    public boolean contains(ModuleData res) {
-        Objects.requireNonNull(res);
-        return get(res.getPath()) != null;
-    }
-
-    public boolean isEmpty() {
-        return resources.isEmpty();
-    }
-
-    public void visit(Visitor visitor, Pool output) {
-        for (ModuleData resource : getContent()) {
-            ModuleData res = visitor.visit(resource);
-            if (res != null) {
-                output.add(res);
-            }
-        }
-    }
-
-    public ByteOrder getByteOrder() {
-        return order;
-    }
-
-    public void addTransformedResource(ModuleData original, InputStream transformed, long length) {
-        if (isReadOnly()) {
-            throw new PluginException("Pool is readonly");
-        }
-        Objects.requireNonNull(original);
-        Objects.requireNonNull(transformed);
-        if (get(original.getPath()) != null) {
-            throw new PluginException("Resource already present");
-        }
-        ModuleData res = new ModuleData(original.getModule(), original.getPath(),
-                original.getType(), transformed, length);
-        add(res);
-    }
-
-    public static ModuleData newResource(String path, InputStream content, long size) {
-        Objects.requireNonNull(path);
-        Objects.requireNonNull(content);
-        String[] split = ImageFileCreator.splitPath(path);
-        String module = split[0];
-        return new ModuleData(module, path, ModuleDataType.CLASS_OR_RESOURCE, content, size);
-    }
-
-    public static ModuleData newResource(String path, byte[] content) {
-        return newResource(path, new ByteArrayInputStream(content),
-                content.length);
-    }
-
-    public static ModuleData newImageFile(String module, String path, ModuleDataType type,
-            InputStream content, long size) {
-        Objects.requireNonNull(path);
-        Objects.requireNonNull(content);
-        return new ModuleData(module, path, type, content, size);
-    }
-
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/transformer/TransformerPlugin.java	Mon Dec 21 14:42:17 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2015, 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 jdk.tools.jlink.api.plugin.transformer;
-
-import jdk.tools.jlink.api.plugin.Plugin;
-
-
-/**
- * Implement this interface to develop your own plugin.
- * TransformerPlugin
- * instances modify the content of a jimage.
- */
-public interface TransformerPlugin extends Plugin {
-    /**
-     * Visit the content of modules.
-     *
-     * @param in Read only content.
-     * @param out The pool to fill with content. Will contain the result of the
-     * visit
-     *
-     * @throws PluginException
-     */
-    public void visit(Pool in, Pool out);
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java	Mon Dec 21 14:42:19 2015 +0100
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2015, 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 jdk.tools.jlink.builder;
+
+import jdk.tools.jlink.plugin.ExecutableImage;
+import jdk.tools.jlink.plugin.PluginException;
+import java.io.BufferedOutputStream;
+import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.lang.module.ModuleDescriptor;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.PosixFileAttributeView;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Set;
+import jdk.tools.jlink.internal.BasicImageWriter;
+import jdk.tools.jlink.internal.plugins.FileCopierPlugin;
+import jdk.tools.jlink.internal.plugins.FileCopierPlugin.SymImageFile;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.Pool.Module;
+import jdk.tools.jlink.plugin.Pool.ModuleData;
+
+/**
+ *
+ * Default Image Builder. This builder create the default Java image layout.
+ */
+public class DefaultImageBuilder implements ImageBuilder {
+
+    public static final String JIMAGE_NAME_PROPERTY = "jimage.name";
+    public static final String NAME = "default-image-builder";
+
+    /**
+     * The default java executable Image.
+     */
+    static class DefaultExecutableImage extends ExecutableImage {
+
+        public DefaultExecutableImage(Path home, Set<String> modules) {
+            super(home, modules, createArgs(home));
+        }
+
+        private static List<String> createArgs(Path home) {
+            Objects.requireNonNull(home);
+            List<String> javaArgs = new ArrayList<>();
+            javaArgs.add(home.resolve("bin").
+                    resolve(getJavaProcessName()).toString());
+            return javaArgs;
+        }
+
+        @Override
+        public void storeLaunchArgs(List<String> args) {
+            try {
+                patchScripts(this, args);
+            } catch (IOException ex) {
+                throw new UncheckedIOException(ex);
+            }
+        }
+    }
+
+    private final Path root;
+    private final Path mdir;
+    private final boolean genBom;
+    private final Set<String> modules = new HashSet<>();
+
+    /**
+     * Default image builder constructor.
+     *
+     * @param genBom
+     * @param root The image directory.
+     * @throws IOException
+     */
+    public DefaultImageBuilder(boolean genBom, Path root) throws IOException {
+        Objects.requireNonNull(root);
+
+        this.genBom = genBom;
+
+        this.root = root;
+        this.mdir = root.resolve("lib").resolve("modules");
+        Files.createDirectories(mdir);
+    }
+
+    private void storeFiles(Set<String> modules, String bom) throws IOException {
+        // Retrieve release file from JDK home dir.
+        String path = System.getProperty("java.home");
+        File f = new File(path, "release");
+        Properties release = null;
+        if (!f.exists()) {
+            // XXX When jlink is exposed to user.
+            //System.err.println("WARNING, no release file found in " + path +
+            //     ". release file not added to generated image");
+        } else {
+            release = new Properties();
+            try (FileInputStream fi = new FileInputStream(f)) {
+                release.load(fi);
+            }
+            addModules(release, modules);
+        }
+
+        if (release != null) {
+            File r = new File(root.toFile(), "release");
+            try (FileOutputStream fo = new FileOutputStream(r)) {
+                release.store(fo, null);
+            }
+        }
+        // Generate bom
+        if (genBom) {
+            File bomFile = new File(root.toFile(), "bom");
+            createUtf8File(bomFile, bom);
+        }
+    }
+
+    private void addModules(Properties release, Set<String> modules) throws IOException {
+        if (release != null) {
+            StringBuilder builder = new StringBuilder();
+            int i = 0;
+            for (String m : modules) {
+                builder.append(m);
+                if (i < modules.size() - 1) {
+                    builder.append(",");
+                }
+                i++;
+            }
+            release.setProperty("MODULES", builder.toString());
+        }
+    }
+
+    @Override
+    public void storeFiles(Pool files, String bom) {
+            String bom) {
+        try {
+            for (ModuleData f : files.getContent()) {
+               if (!f.getType().equals(Pool.ModuleDataType.CLASS_OR_RESOURCE)) {
+                    accept(f);
+                }
+            }
+            for (Module m : files.getModules()) {
+                // Only add modules that contain packages
+                if (!m.getAllPackages().isEmpty()) {
+                    // Skip the fake module used by FileCopierPlugin when copying files.
+                    if (m.getName().equals(FileCopierPlugin.FAKE_MODULE)) {
+                       continue;
+                    }
+                    modules.add(m.getName());
+                }
+            }
+            storeFiles(modules, bom);
+
+            if (Files.getFileStore(root).supportsFileAttributeView(PosixFileAttributeView.class)) {
+                // launchers in the bin directory need execute permission
+                Path bin = root.resolve("bin");
+                if (Files.isDirectory(bin)) {
+                    Files.list(bin)
+                            .filter(f -> !f.toString().endsWith(".diz"))
+                            .filter(f -> Files.isRegularFile(f))
+                            .forEach(this::setExecutable);
+                }
+
+                // jspawnhelper is in lib or lib/<arch>
+                Path lib = root.resolve("lib");
+                if (Files.isDirectory(lib)) {
+                    Files.find(lib, 2, (path, attrs) -> {
+                        return path.getFileName().toString().equals("jspawnhelper");
+                    }).forEach(this::setExecutable);
+                }
+            }
+
+            prepareApplicationFiles(files, modules);
+        } catch (IOException ex) {
+            throw new PluginException(ex);
+        }
+    }
+
+    protected void prepareApplicationFiles(Pool files, Set<String> modules) throws IOException {
+        // generate launch scripts for the modules with a main class
+        for (String module : modules) {
+            String path = "/" + module + "/module-info.class";
+            ModuleData res = files.get(path);
+            if (res == null) {
+                throw new IOException("module-info not found for " + module);
+            }
+            Optional<String> mainClass;
+            ByteArrayInputStream stream = new ByteArrayInputStream(res.getBytes());
+            mainClass = ModuleDescriptor.read(stream).mainClass();
+            if (mainClass.isPresent()) {
+                Path cmd = root.resolve("bin").resolve(module);
+                if (!Files.exists(cmd)) {
+                    StringBuilder sb = new StringBuilder();
+                    sb.append("#!/bin/sh")
+                            .append("\n");
+                    sb.append("JLINK_VM_OPTIONS=")
+                            .append("\n");
+                    sb.append("DIR=`dirname $0`")
+                            .append("\n");
+                    sb.append("$DIR/java $JLINK_VM_OPTIONS -m ")
+                            .append(module).append('/')
+                            .append(mainClass.get())
+                            .append(" $@\n");
+
+                    try (BufferedWriter writer = Files.newBufferedWriter(cmd,
+                            StandardCharsets.ISO_8859_1,
+                            StandardOpenOption.CREATE_NEW)) {
+                        writer.write(sb.toString());
+                    }
+                    if (Files.getFileStore(root.resolve("bin"))
+                            .supportsFileAttributeView(PosixFileAttributeView.class)) {
+                        setExecutable(cmd);
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public DataOutputStream getJImageOutputStream() {
+        try {
+            Path jimageFile = mdir.resolve(BasicImageWriter.BOOT_IMAGE_NAME);
+            OutputStream fos = Files.newOutputStream(jimageFile);
+            BufferedOutputStream bos = new BufferedOutputStream(fos);
+            return new DataOutputStream(bos);
+        } catch (IOException ex) {
+            throw new UncheckedIOException(ex);
+        }
+    }
+
+    private void accept(ModuleData file) throws IOException {
+        String fullPath = file.getPath();
+        String module = "/" + file.getModule()+ "/";
+        String filename = fullPath.substring(module.length());
+        // Remove radical native|config|...
+        filename = filename.substring(filename.indexOf('/') + 1);
+        try (InputStream in = file.stream()) {
+            switch (file.getType()) {
+                case NATIVE_LIB:
+                    writeEntry(in, destFile(nativeDir(filename), filename));
+                    break;
+                case NATIVE_CMD:
+                    Path path = destFile("bin", filename);
+                    writeEntry(in, path);
+                    path.toFile().setExecutable(true);
+                    break;
+                case CONFIG:
+                    writeEntry(in, destFile("conf", filename));
+                    break;
+                case OTHER:
+                    if (file instanceof SymImageFile) {
+                        SymImageFile sym = (SymImageFile) file;
+                        Path target = root.resolve(sym.getTargetPath());
+                        if (!Files.exists(target)) {
+                            throw new IOException("Sym link target " + target
+                                    + " doesn't exist");
+                        }
+                        writeSymEntry(root.resolve(filename), target);
+                    } else {
+                        writeEntry(in, root.resolve(filename));
+                    }
+                    break;
+                default:
+                    throw new InternalError("unexpected entry: " + fullPath);
+            }
+        }
+    }
+
+    private Path destFile(String dir, String filename) {
+        return root.resolve(dir).resolve(filename);
+    }
+
+    private void writeEntry(InputStream in, Path dstFile) throws IOException {
+        Objects.requireNonNull(in);
+        Objects.requireNonNull(dstFile);
+        Files.createDirectories(Objects.requireNonNull(dstFile.getParent()));
+        Files.copy(in, dstFile);
+    }
+
+    private void writeSymEntry(Path dstFile, Path target) throws IOException {
+        Objects.requireNonNull(dstFile);
+        Objects.requireNonNull(target);
+        Files.createDirectories(Objects.requireNonNull(dstFile.getParent()));
+        Files.createLink(dstFile, target);
+    }
+
+    private static String nativeDir(String filename) {
+        if (isWindows()) {
+            if (filename.endsWith(".dll") || filename.endsWith(".diz")
+                    || filename.endsWith(".pdb") || filename.endsWith(".map")) {
+                return "bin";
+            } else {
+                return "lib";
+            }
+        } else {
+            return "lib";
+        }
+    }
+
+    static boolean isWindows() {
+        return System.getProperty("os.name").startsWith("Windows");
+    }
+
+    static boolean isMac() {
+        return System.getProperty("os.name").startsWith("Mac OS");
+    }
+
+    /**
+     * chmod ugo+x file
+     */
+    private void setExecutable(Path file) {
+        try {
+            Set<PosixFilePermission> perms = Files.getPosixFilePermissions(file);
+            perms.add(PosixFilePermission.OWNER_EXECUTE);
+            perms.add(PosixFilePermission.GROUP_EXECUTE);
+            perms.add(PosixFilePermission.OTHERS_EXECUTE);
+            Files.setPosixFilePermissions(file, perms);
+        } catch (IOException ioe) {
+            throw new UncheckedIOException(ioe);
+        }
+    }
+
+    private static void createUtf8File(File file, String content) throws IOException {
+        try (OutputStream fout = new FileOutputStream(file);
+                Writer output = new OutputStreamWriter(fout, "UTF-8")) {
+            output.write(content);
+        }
+    }
+
+    @Override
+    public ExecutableImage getExecutableImage() {
+        return new DefaultExecutableImage(root, modules);
+    }
+
+    // This is experimental, we should get rid-off the scripts in a near future
+    private static void patchScripts(ExecutableImage img, List<String> args) throws IOException {
+        Objects.requireNonNull(args);
+        if (!args.isEmpty()) {
+            Files.find(img.getHome().resolve("bin"), 2, (path, attrs) -> {
+                return img.getModules().contains(path.getFileName().toString());
+            }).forEach((p) -> {
+                try {
+                    String pattern = "JLINK_VM_OPTIONS=";
+                    byte[] content = Files.readAllBytes(p);
+                    String str = new String(content, StandardCharsets.UTF_8);
+                    int index = str.indexOf(pattern);
+                    StringBuilder builder = new StringBuilder();
+                    if (index != -1) {
+                        builder.append(str.substring(0, index)).
+                                append(pattern);
+                        for (String s : args) {
+                            builder.append(s).append(" ");
+                        }
+                        String remain = str.substring(index + pattern.length());
+                        builder.append(remain);
+                        str = builder.toString();
+                        try (BufferedWriter writer = Files.newBufferedWriter(p,
+                                StandardCharsets.ISO_8859_1,
+                                StandardOpenOption.WRITE)) {
+                            writer.write(str);
+                        }
+                    }
+                } catch (IOException ex) {
+                    throw new RuntimeException(ex);
+                }
+            });
+        }
+    }
+
+    static String getJavaProcessName() {
+        return isWindows() ? "java.exe" : "java";
+    }
+
+    public static ExecutableImage getExecutableImage(Path root) {
+        if (Files.exists(root.resolve("bin").resolve(getJavaProcessName()))) {
+            return new DefaultImageBuilder.DefaultExecutableImage(root,
+                    retrieveModules(root));
+        }
+        return null;
+    }
+
+    private static Set<String> retrieveModules(Path root) {
+        Path releaseFile = root.resolve("release");
+        Set<String> modules = new HashSet<>();
+        if (Files.exists(releaseFile)) {
+            Properties release = new Properties();
+            try (FileInputStream fi = new FileInputStream(releaseFile.toFile())) {
+                release.load(fi);
+            } catch (IOException ex) {
+                System.err.println("Can't read release file " + ex);
+            }
+            String mods = release.getProperty("MODULES");
+            if (mods != null) {
+                String[] arr = mods.split(",");
+                for (String m : arr) {
+                    modules.add(m.trim());
+                }
+
+            }
+        }
+        return modules;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/ImageBuilder.java	Mon Dec 21 14:42:19 2015 +0100
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, 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 jdk.tools.jlink.builder;
+
+import jdk.tools.jlink.plugin.ExecutableImage;
+import java.io.DataOutputStream;
+import java.util.List;
+import jdk.tools.jlink.plugin.PluginException;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.Pool.ModuleData;
+
+/**
+ * Implement this interface to develop your own image layout. First the jimage
+ * is written onto the output stream returned by getOutputStream then storeFiles
+ * is called.
+ */
+public interface ImageBuilder {
+
+    /**
+     * Store the external files.
+     *
+     * @param content Pool of module content.
+     * @param bom The options used to build the image file.
+     * @throws PluginException
+     */
+    public void storeFiles(Pool content, String bom);
+
+    /**
+     * The OutputStream to store the jimage file.
+     *
+     * @return The output stream
+     * @throws PluginException
+     */
+    public DataOutputStream getJImageOutputStream();
+
+    /**
+     * Gets the executable image that is generated.
+     *
+     * @return The executable image.
+     * @throws PluginException
+     */
+    public ExecutableImage getExecutableImage();
+}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java	Mon Dec 21 14:42:19 2015 +0100
@@ -45,10 +45,10 @@
 import jdk.tools.jlink.internal.Archive.Entry;
 import jdk.tools.jlink.internal.Archive.Entry.EntryType;
 import jdk.tools.jlink.internal.PoolImpl.CompressedModuleData;
-import jdk.tools.jlink.api.plugin.postprocessor.ExecutableImage;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleData;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleDataType;
+import jdk.tools.jlink.plugin.ExecutableImage;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.Pool.ModuleData;
+import jdk.tools.jlink.plugin.Pool.ModuleDataType;
 
 /**
  * An image (native endian.)
@@ -295,9 +295,9 @@
                 } else {
                     try {
                         // Entry.path() contains the kind of file native, conf, bin, ...
-                        // It is kept in order for ImageBuilder to put files in right place
+                        // Keep it to avoid naming conflict (eg: native/jvm.cfg and config/jvm.cfg
                         resources.add(Pool.newImageFile(mn,
-                                entry.path(), mapImageFileType(entry.type()),
+                                "/" + mn + "/" + entry.path(), mapImageFileType(entry.type()),
                                 entry.stream(), entry.size()));
                     } catch (Exception ex) {
                         throw new IOException(ex);
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java	Mon Dec 21 14:42:19 2015 +0100
@@ -26,7 +26,7 @@
 
 import java.io.DataOutputStream;
 
-import jdk.tools.jlink.api.plugin.Plugin;
+import jdk.tools.jlink.plugin.Plugin;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -34,14 +34,14 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import jdk.tools.jlink.api.plugin.postprocessor.ExecutableImage;
-import jdk.tools.jlink.api.plugin.builder.ImageBuilder;
-import jdk.tools.jlink.api.Jlink;
-import jdk.tools.jlink.api.plugin.transformer.TransformerPlugin;
-import jdk.tools.jlink.api.plugin.PluginException;
-import jdk.tools.jlink.api.plugin.Plugin.CATEGORY;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.postprocessor.PostProcessorPlugin;
+import jdk.tools.jlink.plugin.ExecutableImage;
+import jdk.tools.jlink.builder.ImageBuilder;
+import jdk.tools.jlink.Jlink;
+import jdk.tools.jlink.plugin.TransformerPlugin;
+import jdk.tools.jlink.plugin.PluginException;
+import jdk.tools.jlink.plugin.Plugin.CATEGORY;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.PostProcessorPlugin;
 
 /**
  * Plugins configuration.
@@ -100,7 +100,6 @@
         CATEGORIES_ORDER.add(Plugin.CATEGORY.MODULEINFO_TRANSFORMER);
         CATEGORIES_ORDER.add(Plugin.CATEGORY.SORTER);
         CATEGORIES_ORDER.add(Plugin.CATEGORY.COMPRESSOR);
-        CATEGORIES_ORDER.add(Plugin.CATEGORY.BUILDER);
         CATEGORIES_ORDER.add(Plugin.CATEGORY.VERIFIER);
         CATEGORIES_ORDER.add(Plugin.CATEGORY.PROCESSOR);
         CATEGORIES_ORDER.add(Plugin.CATEGORY.PACKAGER);
@@ -145,13 +144,18 @@
                         + " added more than once to stack ");
             }
             seen.add(plug.getPlugin().getName());
-            List<Integer> lst = resources.get(plug.getCategory());
+            CATEGORY category = Utils.getCategory(plug.getPlugin());
+            if (category == null) {
+                throw new PluginException("Invalid category for "
+                        + plug.getPlugin().getName());
+            }
+            List<Integer> lst = resources.get(category);
             if (lst == null) {
                 lst = new ArrayList<>();
-                resources.put(plug.getCategory(), lst);
+                resources.put(category, lst);
             }
             int index = getAbsoluteIndex(plug.getIndex(),
-                    plug.getCategory(),
+                    category,
                     plug.isAbsoluteIndex(),
                     CATEGORIES_RANGES);
             if (lst.contains(index)) {
@@ -160,7 +164,7 @@
             }
             lst.add(index);
             OrderedPlugin p = new OrderedPlugin(index, plug.getPlugin());
-            if (Utils.isPostProcessor(plug.getCategory())) {
+            if (Utils.isPostProcessor(category)) {
                 postProcessingPlugins.add(p);
             } else {
                 resourcePlugins.add(p);
@@ -197,8 +201,7 @@
                 }
 
                 @Override
-                public void storeFiles(Pool files, List<Pool.ModuleData> removed,
-                        String bom) {
+                public void storeFiles(Pool files, String bom) {
                     throw new PluginException("No directory setup to store files");
                 }
             };
@@ -214,9 +217,12 @@
         if (absolute) {
             return index;
         }
-        // If non null category and not absolute, get index within category
-        if (category != null) {
-            return ranges.get(category) + index;
+        if (index == Integer.MAX_VALUE) {
+            return ranges.get(category) + RANGE_LENGTH - 1;
+        } else { // If non null category and not absolute, get index within category
+            if (category != null) {
+                return ranges.get(category) + index;
+            }
         }
 
         throw new Exception("Can't compute index, no category");
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java	Mon Dec 21 14:42:19 2015 +0100
@@ -26,7 +26,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.DataOutputStream;
-import jdk.tools.jlink.api.plugin.Plugin;
+import jdk.tools.jlink.plugin.Plugin;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.module.ModuleDescriptor;
@@ -43,14 +43,14 @@
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import jdk.internal.jimage.decompressor.Decompressor;
-import jdk.tools.jlink.api.plugin.postprocessor.ExecutableImage;
-import jdk.tools.jlink.api.plugin.builder.ImageBuilder;
-import jdk.tools.jlink.api.plugin.transformer.TransformerPlugin;
-import jdk.tools.jlink.api.plugin.PluginException;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleData;
-import jdk.tools.jlink.api.plugin.postprocessor.PostProcessorPlugin;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleDataType;
+import jdk.tools.jlink.plugin.ExecutableImage;
+import jdk.tools.jlink.builder.ImageBuilder;
+import jdk.tools.jlink.plugin.TransformerPlugin;
+import jdk.tools.jlink.plugin.PluginException;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.Pool.ModuleData;
+import jdk.tools.jlink.plugin.PostProcessorPlugin;
+import jdk.tools.jlink.plugin.Pool.ModuleDataType;
 
 /**
  * Plugins Stack. Plugins entry point to apply transformations onto resources
@@ -278,6 +278,10 @@
         return current;
     }
 
+    /**
+     * This pool wrap the original pool and automatically uncompress moduledata
+     * if needed.
+     */
     private class LastPool extends Pool {
         private class LastModule implements Module {
 
@@ -317,6 +321,15 @@
             public String toString() {
                 return getName();
             }
+
+            @Override
+            public Collection<ModuleData> getContent() {
+                List<ModuleData> lst = new ArrayList<>();
+                for(ModuleData md : module.getContent()) {
+                    lst.add(getUncompressed(md));
+                }
+                return lst;
+            }
         }
         private final PoolImpl pool;
         Decompressor decompressor = new Decompressor();
@@ -450,14 +463,7 @@
             BasicImageWriter writer)
             throws Exception {
         Objects.requireNonNull(original);
-        // Build the diff between input and output
-        List<ModuleData> removed = new ArrayList<>();
-        for (ModuleData f : transformed.getContent()) {
-            if (!f.getType().equals(ModuleDataType.CLASS_OR_RESOURCE) && !transformed.contains(f)) {
-                removed.add(f);
-            }
-        }
-        imageBuilder.storeFiles(new LastPool(transformed), removed, bom);
+        imageBuilder.storeFiles(new LastPool(transformed), bom);
     }
 
     public ExecutableImage getExecutableImage() throws IOException {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java	Mon Dec 21 14:42:19 2015 +0100
@@ -0,0 +1,560 @@
+/*
+ * Copyright (c) 2015, 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 jdk.tools.jlink.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.UncheckedIOException;
+import java.lang.module.Configuration;
+import java.lang.module.ModuleReference;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ResolutionException;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URI;
+import java.nio.ByteOrder;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Date;
+import java.util.Formatter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import jdk.internal.module.ConfigurableModuleFinder;
+import jdk.internal.module.ConfigurableModuleFinder.Phase;
+import jdk.tools.jlink.Jlink;
+import jdk.tools.jlink.internal.TaskHelper.BadArgs;
+import jdk.tools.jlink.internal.TaskHelper.HiddenOption;
+import static jdk.tools.jlink.internal.TaskHelper.JLINK_BUNDLE;
+import jdk.tools.jlink.internal.TaskHelper.Option;
+import jdk.tools.jlink.internal.TaskHelper.OptionsHelper;
+import jdk.tools.jlink.internal.ImagePluginStack.ImageProvider;
+import jdk.tools.jlink.plugin.ExecutableImage;
+import jdk.tools.jlink.Jlink.JlinkConfiguration;
+import jdk.tools.jlink.Jlink.PluginsConfiguration;
+import jdk.tools.jlink.plugin.PluginException;
+import jdk.tools.jlink.builder.DefaultImageBuilder;
+
+/**
+ * Implementation for the jlink tool.
+ *
+ * ## Should use jdk.joptsimple some day.
+ */
+public class JlinkTask {
+
+    private static <T extends Throwable> void fail(Class<T> type,
+            String format,
+            Object... args) throws T {
+        String msg = new Formatter().format(format, args).toString();
+        try {
+            T t = type.getConstructor(String.class).newInstance(msg);
+            throw t;
+        } catch (InstantiationException |
+                InvocationTargetException |
+                NoSuchMethodException |
+                IllegalAccessException e) {
+            throw new InternalError("Unable to create an instance of " + type, e);
+        }
+    }
+
+    private static final TaskHelper taskHelper
+            = new TaskHelper(JLINK_BUNDLE);
+
+    static Option<?>[] recognizedOptions = {
+        new Option<JlinkTask>(false, (task, opt, arg) -> {
+            task.options.help = true;
+        }, "--help"),
+        new Option<JlinkTask>(false, (task, opt, arg) -> {
+            task.options.xhelp = true;
+        }, "--xhelp"),
+        new Option<JlinkTask>(true, (task, opt, arg) -> {
+            String[] dirs = arg.split(File.pathSeparator);
+            task.options.modulePath = new Path[dirs.length];
+            int i = 0;
+            for (String dir : dirs) {
+                task.options.modulePath[i++] = Paths.get(dir);
+            }
+        }, "--modulepath", "--mp"),
+        new Option<JlinkTask>(true, (task, opt, arg) -> {
+            for (String mn : arg.split(",")) {
+                if (mn.isEmpty()) {
+                    throw taskHelper.newBadArgs("err.mods.must.be.specified",
+                            "--limitmods");
+                }
+                task.options.limitMods.add(mn);
+            }
+        }, "--limitmods"),
+        new Option<JlinkTask>(true, (task, opt, arg) -> {
+            for (String mn : arg.split(",")) {
+                if (mn.isEmpty()) {
+                    throw taskHelper.newBadArgs("err.mods.must.be.specified",
+                            "--addmods");
+                }
+                task.options.addMods.add(mn);
+            }
+        }, "--addmods"),
+        new Option<JlinkTask>(true, (task, opt, arg) -> {
+            Path path = Paths.get(arg);
+            task.options.output = path;
+        }, "--output"),
+        new Option<JlinkTask>(true, (task, opt, arg) -> {
+            if ("little".equals(arg)) {
+                task.options.endian = ByteOrder.LITTLE_ENDIAN;
+            } else if ("big".equals(arg)) {
+                task.options.endian = ByteOrder.BIG_ENDIAN;
+            } else {
+                throw taskHelper.newBadArgs("err.unknown.byte.order", arg);
+            }
+        }, "--endian"),
+        new Option<JlinkTask>(false, (task, opt, arg) -> {
+            task.options.version = true;
+        }, "--version"),
+        new Option<JlinkTask>(false, (task, opt, arg) -> {
+            task.options.genbom = true;
+        }, "--genbom"),
+        new HiddenOption<JlinkTask>(false, (task, opt, arg) -> {
+            task.options.fullVersion = true;
+        }, "--fullversion"),};
+
+    private static final String PROGNAME = "jlink";
+    private final OptionsValues options = new OptionsValues();
+
+    private static final OptionsHelper<JlinkTask> optionsHelper
+            = taskHelper.newOptionsHelper(JlinkTask.class, recognizedOptions);
+    private PrintWriter log;
+
+    void setLog(PrintWriter out) {
+        log = out;
+        taskHelper.setLog(log);
+    }
+
+    /**
+     * Result codes.
+     */
+    static final int EXIT_OK = 0, // Completed with no errors.
+            EXIT_ERROR = 1, // Completed but reported errors.
+            EXIT_CMDERR = 2, // Bad command-line arguments
+            EXIT_SYSERR = 3, // System error or resource exhaustion.
+            EXIT_ABNORMAL = 4;// terminated abnormally
+
+    static class OptionsValues {
+        boolean help;
+        boolean xhelp;
+        boolean genbom;
+        boolean version;
+        boolean fullVersion;
+        Path[] modulePath;
+        Set<String> limitMods = new HashSet<>();
+        Set<String> addMods = new HashSet<>();
+        Path output;
+        ByteOrder endian = ByteOrder.nativeOrder();
+    }
+
+    int run(String[] args) {
+        if (log == null) {
+            setLog(new PrintWriter(System.err));
+        }
+        try {
+            optionsHelper.handleOptions(this, args);
+            if (options.help) {
+                optionsHelper.showHelp(PROGNAME);
+                return EXIT_OK;
+            }
+            if (options.xhelp) {
+                optionsHelper.showXHelp(PROGNAME, true);
+                return EXIT_OK;
+            }
+            if (options.version || options.fullVersion) {
+                taskHelper.showVersion(options.fullVersion);
+                return EXIT_OK;
+            }
+            if (taskHelper.getExistingImage() == null) {
+                if (options.modulePath == null || options.modulePath.length == 0) {
+                    throw taskHelper.newBadArgs("err.modulepath.must.be.specified").showUsage(true);
+                }
+                createImage();
+            } else {
+                postProcessOnly(taskHelper.getExistingImage());
+            }
+
+            return EXIT_OK;
+        } catch (UncheckedIOException | PluginException | IOException | ResolutionException e) {
+            log.println(taskHelper.getMessage("error.prefix") + " " + e.getMessage());
+            log.println(taskHelper.getMessage("main.usage.summary", PROGNAME));
+            return EXIT_ERROR;
+        } catch (BadArgs e) {
+            taskHelper.reportError(e.key, e.args);
+            if (e.showUsage) {
+                log.println(taskHelper.getMessage("main.usage.summary", PROGNAME));
+            }
+            return EXIT_CMDERR;
+        } catch (Throwable x) {
+            log.println(taskHelper.getMessage("main.msg.bug"));
+            x.printStackTrace(log);
+            return EXIT_ABNORMAL;
+        } finally {
+            log.flush();
+        }
+    }
+
+    private static Map<String, Path> modulesToPath(ModuleFinder finder,
+            Set<ModuleDescriptor> modules) {
+        Map<String, Path> modPaths = new HashMap<>();
+        for (ModuleDescriptor m : modules) {
+            String name = m.name();
+
+            Optional<ModuleReference> omref = finder.find(name);
+            if (!omref.isPresent()) {
+                // this should not happen, module path bug?
+                fail(InternalError.class,
+                        "Selected module %s not on module path",
+                        name);
+            }
+
+            URI location = omref.get().location().get();
+            String scheme = location.getScheme();
+            if (!scheme.equalsIgnoreCase("jmod") && !scheme.equalsIgnoreCase("jar")
+                    && !scheme.equalsIgnoreCase("file")) {
+                fail(RuntimeException.class,
+                        "Selected module %s (%s) not in jmod, modular jar or directory format",
+                        name,
+                        location);
+            }
+
+            // convert to file URIs
+            URI fileURI;
+            if (scheme.equalsIgnoreCase("jmod")) {
+                // jmod:file:/home/duke/duke.jmod!/ -> file:/home/duke/duke.jmod
+                String s = location.toString();
+                fileURI = URI.create(s.substring(5, s.length() - 2));
+            } else if (scheme.equalsIgnoreCase("jar")) {
+                // jar:file:/home/duke/duke.jar!/ -> file:/home/duke/duke.jar
+                String s = location.toString();
+                fileURI = URI.create(s.substring(4, s.length() - 2));
+            } else {
+                fileURI = URI.create(location.toString());
+            }
+
+            modPaths.put(name, Paths.get(fileURI));
+        }
+        return modPaths;
+    }
+
+    /*
+     * Jlink API entry point.
+     */
+    public static void createImage(JlinkConfiguration config,
+            PluginsConfiguration plugins)
+            throws Exception {
+        Objects.requireNonNull(config);
+        Objects.requireNonNull(config.getOutput());
+        plugins = plugins == null ? new PluginsConfiguration() : plugins;
+
+        if (config.getModulepaths().isEmpty()) {
+            throw new Exception("Empty module paths");
+        }
+        Path[] arr = new Path[config.getModulepaths().size()];
+        arr = config.getModulepaths().toArray(arr);
+        ModuleFinder finder
+                = newModuleFinder(arr, config.getLimitmods(), config.getModules());
+
+        // First create the image provider
+        ImageProvider imageProvider
+                = createImageProvider(finder,
+                        checkAddMods(config.getModules()),
+                        config.getLimitmods(), config.getByteOrder());
+
+        // Then create the Plugin Stack
+        ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(plugins,
+                genBOMContent(config, plugins));
+
+        //Ask the stack to proceed;
+        stack.operate(imageProvider);
+    }
+
+    /*
+     * Jlink API entry point.
+     */
+    public static void postProcessImage(ExecutableImage image, List<Jlink.OrderedPlugin> postProcessorPlugins)
+            throws Exception {
+        Objects.requireNonNull(image);
+        Objects.requireNonNull(postProcessorPlugins);
+        PluginsConfiguration config = new PluginsConfiguration(postProcessorPlugins);
+        ImagePluginStack stack = ImagePluginConfiguration.
+                parseConfiguration(config);
+
+        stack.operate((ImagePluginStack stack1) -> image);
+    }
+
+    private void postProcessOnly(Path existingImage) throws Exception {
+        PluginsConfiguration config = taskHelper.getPluginsConfig(null, false);
+        ExecutableImage img = DefaultImageBuilder.getExecutableImage(existingImage);
+        if (img == null) {
+            throw taskHelper.newBadArgs("err.existing.image.invalid");
+        }
+        postProcessImage(img, config.getPlugins());
+    }
+
+    private void createImage() throws Exception {
+        if (options.output == null) {
+            throw taskHelper.newBadArgs("err.output.must.be.specified").showUsage(true);
+        }
+        ModuleFinder finder
+                = newModuleFinder(options.modulePath, options.limitMods, options.addMods);
+        try {
+            options.addMods = checkAddMods(options.addMods);
+        } catch (IllegalArgumentException ex) {
+            throw taskHelper.newBadArgs("err.mods.must.be.specified", "--addmods")
+                    .showUsage(true);
+        }
+        // First create the image provider
+        ImageProvider imageProvider
+                = createImageProvider(finder,
+                        options.addMods,
+                        options.limitMods,
+                        options.endian);
+
+        // Then create the Plugin Stack
+        ImagePluginStack stack = ImagePluginConfiguration.
+                parseConfiguration(taskHelper.getPluginsConfig(options.output, options.genbom),
+                        genBOMContent());
+
+        //Ask the stack to proceed
+        stack.operate(imageProvider);
+    }
+
+    private static Set<String> checkAddMods(Set<String> addMods) {
+        if (addMods.isEmpty()) {
+            throw new IllegalArgumentException("no modules to add");
+        }
+        return addMods;
+    }
+
+    private static ModuleFinder newModuleFinder(Path[] paths,
+            Set<String> limitMods,
+            Set<String> addMods) {
+        ModuleFinder finder = ModuleFinder.of(paths);
+
+        // jmods are located at link-time
+        if (finder instanceof ConfigurableModuleFinder) {
+            ((ConfigurableModuleFinder) finder).configurePhase(Phase.LINK_TIME);
+        }
+
+        // if limitmods is specified then limit the universe
+        if (!limitMods.isEmpty()) {
+            finder = limitFinder(finder, limitMods, addMods);
+        }
+        return finder;
+    }
+
+    private static ImageProvider createImageProvider(ModuleFinder finder,
+            Set<String> addMods,
+            Set<String> limitMods,
+            ByteOrder order)
+            throws IOException {
+        if (addMods.isEmpty()) {
+            throw new IllegalArgumentException("empty modules and limitmods");
+        }
+        Configuration cf = Configuration.resolve(finder,
+                Configuration.empty(),
+                ModuleFinder.empty(),
+                addMods);
+        Map<String, Path> mods = modulesToPath(finder, cf.descriptors());
+        return new ImageHelper(cf, mods, order);
+    }
+
+    /**
+     * Returns a ModuleFinder that limits observability to the given root
+     * modules, their transitive dependences, plus a set of other modules.
+     */
+    private static ModuleFinder limitFinder(ModuleFinder finder,
+            Set<String> roots,
+            Set<String> otherMods) {
+        // resolve all root modules
+        Configuration cf = Configuration.resolve(finder,
+                Configuration.empty(),
+                ModuleFinder.empty(),
+                roots);
+
+        // module name -> reference
+        Map<String, ModuleReference> map = new HashMap<>();
+        cf.descriptors().forEach(md -> {
+            String name = md.name();
+            map.put(name, finder.find(name).get());
+        });
+
+        // set of modules that are observable
+        Set<ModuleReference> mrefs = new HashSet<>(map.values());
+
+        // add the other modules
+        for (String mod : otherMods) {
+            Optional<ModuleReference> omref = finder.find(mod);
+            if (omref.isPresent()) {
+                ModuleReference mref = omref.get();
+                map.putIfAbsent(mod, mref);
+                mrefs.add(mref);
+            } else {
+                // no need to fail
+            }
+        }
+
+        return new ModuleFinder() {
+            @Override
+            public Optional<ModuleReference> find(String name) {
+                return Optional.ofNullable(map.get(name));
+            }
+
+            @Override
+            public Set<ModuleReference> findAll() {
+                return mrefs;
+            }
+        };
+    }
+
+    private static String getBomHeader() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("#").append(new Date()).append("\n");
+        sb.append("#Please DO NOT Modify this file").append("\n");
+        return sb.toString();
+    }
+
+    private String genBOMContent() throws IOException {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getBomHeader());
+        StringBuilder command = new StringBuilder();
+        for (String c : optionsHelper.getInputCommand()) {
+            command.append(c).append(" ");
+        }
+        sb.append("command").append(" = ").append(command);
+        sb.append("\n");
+
+        return sb.toString();
+    }
+
+    private static String genBOMContent(JlinkConfiguration config,
+            PluginsConfiguration plugins)
+            throws IOException {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getBomHeader());
+        sb.append(config);
+        sb.append(plugins);
+        return sb.toString();
+    }
+
+    private static class ImageHelper implements ImageProvider {
+
+        final Set<Archive> archives;
+        final ByteOrder order;
+
+        ImageHelper(Configuration cf,
+                Map<String, Path> modsPaths,
+                ByteOrder order)
+                throws IOException {
+            archives = modsPaths.entrySet().stream()
+                    .map(e -> newArchive(e.getKey(), e.getValue()))
+                    .collect(Collectors.toSet());
+            this.order = order;
+        }
+
+        private Archive newArchive(String module, Path path) {
+            if (path.toString().endsWith(".jmod")) {
+                return new JmodArchive(module, path);
+            } else if (path.toString().endsWith(".jar")) {
+                return new ModularJarArchive(module, path);
+            } else if (Files.isDirectory(path)) {
+                return new DirArchive(path);
+            } else {
+                fail(RuntimeException.class,
+                        "Selected module %s (%s) not in jmod or modular jar format",
+                        module,
+                        path);
+            }
+            return null;
+        }
+
+        @Override
+        public ExecutableImage retrieve(ImagePluginStack stack) throws IOException {
+            return ImageFileCreator.create(archives, order, stack);
+        }
+    }
+
+    private static enum Section {
+        NATIVE_LIBS("native", nativeDir()),
+        NATIVE_CMDS("bin", "bin"),
+        CLASSES("classes", "classes"),
+        CONFIG("conf", "conf"),
+        UNKNOWN("unknown", "unknown");
+
+        private static String nativeDir() {
+            if (System.getProperty("os.name").startsWith("Windows")) {
+                return "bin";
+            } else {
+                return "lib";
+            }
+        }
+
+        private final String jmodDir;
+        private final String imageDir;
+
+        Section(String jmodDir, String imageDir) {
+            this.jmodDir = jmodDir;
+            this.imageDir = imageDir;
+        }
+
+        String imageDir() {
+            return imageDir;
+        }
+
+        String jmodDir() {
+            return jmodDir;
+        }
+
+        boolean matches(String path) {
+            return path.startsWith(jmodDir);
+        }
+
+        static Section getSectionFromName(String dir) {
+            if (Section.NATIVE_LIBS.matches(dir)) {
+                return Section.NATIVE_LIBS;
+            } else if (Section.NATIVE_CMDS.matches(dir)) {
+                return Section.NATIVE_CMDS;
+            } else if (Section.CLASSES.matches(dir)) {
+                return Section.CLASSES;
+            } else if (Section.CONFIG.matches(dir)) {
+                return Section.CONFIG;
+            } else {
+                return Section.UNKNOWN;
+            }
+        }
+    }
+}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JvmHandler.java	Mon Dec 21 14:42:17 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,204 +0,0 @@
-/*
- * Copyright (c) 2015, 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 jdk.tools.jlink.internal;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleData;
-
-/**
- * A class to deal with JVM platforms
- */
-public final class JvmHandler {
-
-    private static final class JvmComparator implements Comparator<String> {
-
-        @Override
-        public int compare(String t, String t1) {
-            return Jvm.getIndex(t) - Jvm.getIndex(t1);
-        }
-    }
-
-    private enum Jvm {
-
-        SERVER("server", 0), CLIENT("client", 1), MINIMAL("minimal", 2);
-        private final String name;
-        private final int index;
-
-        Jvm(String name, int index) {
-            this.name = name;
-            this.index = index;
-        }
-
-        private static int getIndex(String platform) {
-            return Jvm.valueOf(platform.toUpperCase(Locale.US)).index;
-        }
-    }
-
-    private static final String JVM_CFG = "jvm.cfg";
-
-    static boolean isWindows() {
-        return System.getProperty("os.name").startsWith("Windows");
-    }
-
-    static boolean isMac() {
-        return System.getProperty("os.name").startsWith("Mac OS");
-    }
-
-    public Pool handlePlatforms(Pool files,
-            List<ModuleData> removedFiles) throws IOException {
-        Objects.requireNonNull(files);
-        Objects.requireNonNull(removedFiles);
-
-        List<String> removed = getJVM(removedFiles);
-        Pool ret = files;
-        if (!removed.isEmpty()) {
-            List<String> existing = new ArrayList<>();
-            String jvmlib = jvmlib();
-            ret = new PoolImpl();
-            List<ModuleData> origHolder = new ArrayList<>();
-            try {
-                files.visit(new Pool.Visitor() {
-                    @Override
-                    public ModuleData visit(ModuleData file) {
-                        // skip the original cfg file
-                        if (file.getPath().endsWith(JVM_CFG)) {
-                            origHolder.add(file);
-                            return null;
-                        } else {
-                            String jvm = getJVM(file, jvmlib);
-                            if (jvm != null) {
-                                existing.add(jvm);
-                            }
-                            return file;
-                        }
-                    }
-                }, ret);
-                if (existing.isEmpty()) {
-                    throw new Exception("no JVM found, image must contain at least one jvm");
-                }
-                //create the cfg file based on removed and existing
-                if (origHolder.size() == 1) {
-                    StringBuilder builder = new StringBuilder();
-                    ModuleData orig = origHolder.get(0);
-                    // Keep comments
-                    try (BufferedReader reader
-                            = new BufferedReader(new InputStreamReader(orig.stream(),
-                                            StandardCharsets.UTF_8))) {
-                        reader.lines().forEach((s) -> {
-                            if (s.startsWith("#")) {
-                                builder.append(s).append("\n");
-                            }
-                        });
-                    }
-                    Collections.sort(existing, new JvmComparator());
-                    List<String> remaining = new ArrayList<>();
-                    for (String platform : existing) {
-                        if (!removed.contains(platform)) {
-                            remaining.add(platform);
-                            builder.append("-").append(platform).append(" KNOWN\n");
-                        }
-                    }
-
-                    // removed JVM are aliased to first one.
-                    for (String platform : removed) {
-                        builder.append("-").append(platform).
-                                append(" ALIASED_TO -").
-                                append(remaining.get(0)).append("\n");
-                    }
-
-                    byte[] content = builder.toString().getBytes(StandardCharsets.UTF_8);
-                    ModuleData rewritten = Pool.newImageFile(orig.getModule(),
-                            orig.getPath(),
-                            orig.getType(),
-                            new ByteArrayInputStream(content), content.length);
-                    ret.add(rewritten);
-                } else {
-                    System.err.println("No jvm.cfg file, skipping rewriting.");
-                }
-            } catch (Exception ex) {
-                throw new IOException(ex);
-            }
-        }
-        return ret;
-    }
-
-    private static List<String> getJVM(Collection<ModuleData> removed) {
-        List<String> ret = new ArrayList<>();
-        String jvmlib = jvmlib();
-        for (ModuleData f : removed) {
-            String jvm = getJVM(f, jvmlib);
-            if (jvm != null) {
-                ret.add(jvm);
-            }
-        }
-        return ret;
-    }
-
-    private static String getJVM(ModuleData f, String jvmlib) {
-        // Path is /<native|classes|...>/actual path
-        String path = f.getPath().substring(1);
-        int nameIndex = path.indexOf("/");
-        String p = path.substring(nameIndex + 1);
-        String radical;
-        int ind = p.lastIndexOf("/");
-        if (ind != -1) {
-            radical = p.substring(0, ind);
-            p = p.substring(ind + 1);
-        } else {
-            radical = null;
-        }
-        if (p.equals(jvmlib)) {
-            if (radical == null) {
-                System.err.println("jvm lib not in a directory");
-            }
-            return radical;
-        }
-        return null;
-    }
-
-    private static String jvmlib() {
-        String lib = "libjvm.so";
-        if (isWindows()) {
-            lib = "jvm.dll";
-        } else {
-            if (isMac()) {
-                lib = "libjvm.dylib";
-            }
-        }
-        return lib;
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Main.java	Mon Dec 21 14:42:19 2015 +0100
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.tools.jlink.internal;
+
+import java.io.*;
+
+public class Main {
+    public static void main(String... args) throws Exception {
+        JlinkTask t = new JlinkTask();
+        int rc = t.run(args);
+        System.exit(rc);
+    }
+
+
+    /**
+     * Entry point that does <i>not</i> call System.exit.
+     *
+     * @param args command line arguments
+     * @param out output stream
+     * @return an exit code. 0 means success, non-zero means an error occurred.
+     */
+    public static int run(String[] args, PrintWriter out) {
+        JlinkTask t = new JlinkTask();
+        t.setLog(out);
+        return t.run(args);
+    }
+}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/PluginRepository.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/PluginRepository.java	Mon Dec 21 14:42:19 2015 +0100
@@ -32,9 +32,11 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.ServiceLoader;
-import jdk.tools.jlink.api.plugin.Plugin;
-import jdk.tools.jlink.api.plugin.PluginException;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption;
+import jdk.tools.jlink.plugin.Plugin;
+import jdk.tools.jlink.plugin.PluginException;
+import jdk.tools.jlink.plugin.PluginOption;
+import jdk.tools.jlink.plugin.PostProcessorPlugin;
+import jdk.tools.jlink.plugin.TransformerPlugin;
 
 /**
  *
@@ -58,7 +60,11 @@
      */
     public static Plugin getPlugin(String name,
             Layer pluginsLayer) {
-        return getPlugin(Plugin.class, name, pluginsLayer);
+        Plugin p = getPlugin(TransformerPlugin.class, name, pluginsLayer);
+        if (p == null) {
+            p = getPlugin(PostProcessorPlugin.class, name, pluginsLayer);
+        }
+        return p;
     }
 
     /**
@@ -67,14 +73,16 @@
      * @param config Optional config.
      * @param name Non null name.
      * @param pluginsLayer
-     * @return A plugin.
+     * @return A plugin or null if no plugin found.
      */
     public static Plugin newPlugin(Map<PluginOption, String> config, String name,
             Layer pluginsLayer) {
         Objects.requireNonNull(name);
         Objects.requireNonNull(pluginsLayer);
         Plugin plugin = getPlugin(name, pluginsLayer);
-        plugin.configure(config);
+        if (plugin != null) {
+            plugin.configure(config);
+        }
         return plugin;
     }
 
@@ -99,7 +107,10 @@
     }
 
     public static List<Plugin> getPlugins(Layer pluginsLayer) {
-        return getPlugins(Plugin.class, pluginsLayer);
+        List<Plugin> plugins = new ArrayList<>();
+        plugins.addAll(getPlugins(TransformerPlugin.class, pluginsLayer));
+        plugins.addAll(getPlugins(PostProcessorPlugin.class, pluginsLayer));
+        return plugins;
     }
 
     private static <T extends Plugin> T getPlugin(Class<T> clazz, String name,
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/PoolImpl.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/PoolImpl.java	Mon Dec 21 14:42:19 2015 +0100
@@ -30,7 +30,7 @@
 import java.nio.ByteOrder;
 import java.util.Objects;
 import jdk.internal.jimage.decompressor.CompressedResourceHeader;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
+import jdk.tools.jlink.plugin.Pool;
 
 /**
  * Pool of module data.
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePrevisitor.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePrevisitor.java	Mon Dec 21 14:42:19 2015 +0100
@@ -24,7 +24,7 @@
  */
 package jdk.tools.jlink.internal;
 
-import jdk.tools.jlink.api.plugin.transformer.Pool;
+import jdk.tools.jlink.plugin.Pool;
 
 /**
  * Plugin wishing to pre-visit the resources must implement this interface.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java	Mon Dec 21 14:42:19 2015 +0100
@@ -0,0 +1,769 @@
+/*
+ * Copyright (c) 2015, 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 jdk.tools.jlink.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.module.Configuration;
+import java.lang.module.ModuleFinder;
+import java.lang.reflect.Layer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import jdk.internal.module.ConfigurableModuleFinder;
+import jdk.internal.module.ConfigurableModuleFinder.Phase;
+import jdk.tools.jlink.Jlink;
+import jdk.tools.jlink.Jlink.OrderedPlugin;
+import jdk.tools.jlink.Jlink.PluginsConfiguration;
+import jdk.tools.jlink.plugin.Plugin;
+import jdk.tools.jlink.plugin.PluginOption;
+import jdk.tools.jlink.plugin.PluginOption.Builder;
+import jdk.tools.jlink.plugin.Plugin.CATEGORY;
+import jdk.tools.jlink.plugin.Plugin.ORDER;
+import jdk.tools.jlink.builder.DefaultImageBuilder;
+import jdk.tools.jlink.builder.ImageBuilder;
+import jdk.tools.jlink.plugin.PluginException;
+import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
+
+/**
+ *
+ * JLink and JImage tools shared helper.
+ */
+public final class TaskHelper {
+
+    public static final String JLINK_BUNDLE = "jdk.tools.jlink.resources.jlink";
+    public static final String JIMAGE_BUNDLE = "jdk.tools.jimage.resources.jimage";
+
+    private static final String DEFAULTS_PROPERTY = "jdk.jlink.defaults";
+
+    public final class BadArgs extends Exception {
+
+        static final long serialVersionUID = 8765093759964640721L;
+
+        private BadArgs(String key, Object... args) {
+            super(bundleHelper.getMessage(key, args));
+            this.key = key;
+            this.args = args;
+        }
+
+        public BadArgs showUsage(boolean b) {
+            showUsage = b;
+            return this;
+        }
+        public final String key;
+        public final Object[] args;
+        public boolean showUsage;
+    }
+
+    public static class Option<T> {
+
+        public interface Processing<T> {
+
+            void process(T task, String opt, String arg) throws BadArgs;
+        }
+
+        final boolean hasArg;
+        final String[] aliases;
+        final Processing<T> processing;
+
+        public Option(boolean hasArg, Processing<T> processing, String... aliases) {
+            this.hasArg = hasArg;
+            this.processing = processing;
+            this.aliases = aliases;
+        }
+
+        public boolean isHidden() {
+            return false;
+        }
+
+        public boolean matches(String opt) {
+            for (String a : aliases) {
+                if (a.equals(opt)) {
+                    return true;
+                } else if (opt.startsWith("--")
+                        && (hasArg && opt.startsWith(a + "="))) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public boolean ignoreRest() {
+            return false;
+        }
+
+        void process(T task, String opt, String arg) throws BadArgs {
+            processing.process(task, opt, arg);
+        }
+    }
+
+    private static class PlugOption extends Option<PluginsOptions> {
+
+        public PlugOption(boolean hasArg,
+                Processing<PluginsOptions> processing, String... aliases) {
+            super(hasArg, processing, aliases);
+        }
+
+        @Override
+        public boolean matches(String opt) {
+            return super.matches(removeIndex(opt));
+        }
+    }
+
+    private int getIndex(String opt) throws BadArgs {
+        String orig = opt;
+        int i = opt.indexOf(":");
+        int index = -1;
+        if (i != -1) {
+            opt = opt.substring(i + 1);
+            if (opt.equals(Plugin.ORDER.FIRST.getName())) {
+                index = 0;
+            } else if (opt.equals(Plugin.ORDER.LAST.getName())) {
+                index = Integer.MAX_VALUE;
+            } else {
+                try {
+                    index = Integer.parseInt(opt);
+                } catch (NumberFormatException ex) {
+                    throw newBadArgs("err.invalid.index", orig);
+                }
+            }
+        }
+        return index;
+    }
+
+    private static String removeIndex(String opt) {
+        //has index? remove it
+        int i = opt.indexOf(":");
+        if (i != -1) {
+            opt = opt.substring(0, i);
+        }
+        return opt;
+    }
+
+    private static class HiddenPluginOption extends PlugOption {
+
+        public HiddenPluginOption(boolean hasArg,
+                Processing<PluginsOptions> processing, String... aliases) {
+            super(hasArg, processing, aliases);
+        }
+
+        @Override
+        public boolean isHidden() {
+            return true;
+        }
+    }
+
+    private final class PluginsOptions {
+
+        private static final String PLUGINS_PATH = "--plugins-modulepath";
+        private static final String POST_PROCESS = "--post-process-path";
+
+        private Layer pluginsLayer = Layer.boot();
+        private String lastSorter;
+        private Path existingImage;
+        private final Map<Plugin, Map<PluginOption, String>> plugins = new HashMap<>();
+        private final List<PlugOption> pluginsOptions = new ArrayList<>();
+        private final List<PlugOption> mainOptions = new ArrayList<>();
+        // The order in which options are declared is the stack order.
+        // Order is within plugin category
+        private final Map<CATEGORY, List<Plugin>> pluginsOrder = new HashMap<>();
+        private final Map<Plugin, Integer> pluginsIndexes = new HashMap<>();
+
+        private PluginsOptions(String pp) throws BadArgs {
+
+            if (pp != null) {
+                String[] dirs = pp.split(File.pathSeparator);
+                List<Path> paths = new ArrayList<>(dirs.length);
+                for (String dir : dirs) {
+                    paths.add(Paths.get(dir));
+                }
+
+                pluginsLayer = createPluginsLayer(paths);
+            }
+
+            Map<String, List<String>> seen = new HashMap<>();
+            for (Plugin plugin : PluginRepository.
+                    getPlugins(pluginsLayer)) {
+                addOrderedPuginOptions(plugin, seen);
+            }
+            mainOptions.add(new PlugOption(false,
+                    (task, opt, arg) -> {
+                        // This option is handled prior
+                        // to have the options parsed.
+                    },
+                    "--plugins-modulepath"));
+            mainOptions.add(new PlugOption(true, (task, opt, arg) -> {
+                Path path = Paths.get(arg);
+                if (!Files.exists(path) || !Files.isDirectory(path)) {
+                    throw newBadArgs("err.existing.image.must.exist");
+                }
+                existingImage = path.toAbsolutePath();
+            }, POST_PROCESS));
+            mainOptions.add(new PlugOption(true,
+                    (task, opt, arg) -> {
+                        lastSorter = arg;
+                    },
+                    "--resources-last-sorter"));
+        }
+
+        private void addOrderedPuginOptions(Plugin plugin,
+                Map<String, List<String>> seen) throws BadArgs {
+            PluginOption option = plugin.getOption();
+            if (option != null) {
+                for (Entry<String, List<String>> entry : seen.entrySet()) {
+                    if (entry.getKey().equals(option.getName())
+                            || entry.getValue().contains(option.getName())) {
+                        throw new BadArgs("err.plugin.mutiple.options",
+                                option.getName());
+                    }
+                }
+                List<String> optional = new ArrayList<>();
+                seen.put(option.getName(), optional);
+                PlugOption plugOption
+                        = new PlugOption(option.getArgumentDescription() != null,
+                                (task, opt, arg) -> {
+                                    if (!Utils.isFunctional(plugin)) {
+                                        throw newBadArgs("err.provider.not.functional",
+                                                option.getName());
+                                    }
+                                    Map<PluginOption, String> m = plugins.get(plugin);
+                                    if (m == null) {
+                                        m = new HashMap<>();
+                                        plugins.put(plugin, m);
+                                    }
+                                    m.put(option, arg);
+                                    int index = computeIndex(plugin);
+                                    // Overriden index?
+                                    if (index == -1) {
+                                        index = getIndex(opt);
+                                    }
+                                    pluginsIndexes.put(plugin, index);
+                                },
+                                "--" + option.getName());
+                pluginsOptions.add(plugOption);
+                if (plugin.getAdditionalOptions() != null && !plugin.getAdditionalOptions().isEmpty()) {
+                    for (PluginOption other : plugin.getAdditionalOptions()) {
+                        optional.add(other.getName());
+                        PlugOption otherOption = new PlugOption(true,
+                                (task, opt, arg) -> {
+                                    Map<PluginOption, String> m = plugins.get(plugin);
+                                    if (m == null) {
+                                        m = new HashMap<>();
+                                        plugins.put(plugin, m);
+                                    }
+                                    m.put(other, arg);
+                                },
+                                "--" + other.getName());
+                        pluginsOptions.add(otherOption);
+                    }
+                }
+                // On/Off enabled by default plugin
+                // Command line option can override it
+                if (option.hasOnOffArgument()) {
+                    boolean edefault = Utils.isEnabled(plugin)
+                            && Utils.isFunctional(plugin) && option.isEnabled();
+                    if (edefault) {
+                        Map<PluginOption, String> m = new HashMap<>();
+                        m.put(option, Builder.ON_ARGUMENT);
+                        plugins.put(plugin, m);
+                    }
+                }
+            }
+        }
+
+        private int computeIndex(Plugin plugin) {
+            CATEGORY category = Utils.getCategory(plugin);
+            List<Plugin> order = pluginsOrder.get(category);
+            if (order == null) {
+                order = new ArrayList<>();
+                pluginsOrder.put(category, order);
+            }
+            order.add(plugin);
+            int index = -1;
+            ORDER defaultOrder = Utils.getOrder(plugin);
+            if (defaultOrder == ORDER.FIRST) {
+                index = 0;
+            } else if (defaultOrder == ORDER.LAST) {
+                index = Integer.MAX_VALUE;
+            }
+            return index;
+        }
+
+        private PlugOption getOption(String name) throws BadArgs {
+            for (PlugOption o : pluginsOptions) {
+                if (o.matches(name)) {
+                    return o;
+                }
+            }
+            for (PlugOption o : mainOptions) {
+                if (o.matches(name)) {
+                    return o;
+                }
+            }
+            return null;
+        }
+
+        private PluginsConfiguration getPluginsConfig(Path output,
+                boolean genbom) throws IOException {
+            if (output != null) {
+                if (Files.exists(output)) {
+                    throw new PluginException(PluginsResourceBundle.
+                            getMessage("err.dir.already.exits", output));
+                }
+            }
+
+            List<OrderedPlugin> pluginsList = new ArrayList<>();
+            for (Entry<Plugin, Map<PluginOption, String>> entry : plugins.entrySet()) {
+                Plugin plugin = entry.getKey();
+                if (plugin.getOption() != null) {
+                    PluginOption opt = plugin.getOption();
+                    if (opt.hasOnOffArgument()) {
+                        Object val = entry.getValue().get(opt);
+                        if (Builder.OFF_ARGUMENT.equals(val)) {
+                            // Disabled plugin, no need to add it.
+                            continue;
+                        }
+                    }
+                }
+                // User defined index?
+                Integer i = pluginsIndexes.get(plugin);
+                if (i == null) {
+                    // Enabled by default plugin. Find it an index.
+                    i = computeIndex(plugin);
+                }
+                boolean absolute = false;
+                if (i == -1) {
+                    CATEGORY category = Utils.getCategory(plugin);
+                    if (category == null) {
+                        absolute = true;
+                    }
+                    List<Plugin> lstPlugins
+                            = pluginsOrder.get(category);
+                    i = lstPlugins.indexOf(plugin);
+                }
+                if (i == -1) {
+                    throw new IllegalArgumentException("Invalid index " + i);
+                }
+                Map<PluginOption, String> config = new HashMap<>();
+                config.putAll(entry.getValue());
+                plugin.configure(config);
+                OrderedPlugin conf
+                        = new Jlink.OrderedPlugin(plugin,
+                                i, absolute);
+                pluginsList.add(conf);
+            }
+
+            // recreate or postprocessing don't require an output directory.
+            ImageBuilder builder = null;
+            if (output != null) {
+                builder = new DefaultImageBuilder(genbom, output);
+
+            }
+            return new Jlink.PluginsConfiguration(pluginsList,
+                    builder, lastSorter);
+        }
+    }
+
+    public static class HiddenOption<T> extends Option<T> {
+
+        public HiddenOption(boolean hasArg, Processing<T> processing,
+                String... aliases) {
+            super(hasArg, processing, aliases);
+        }
+
+        @Override
+        public boolean isHidden() {
+            return true;
+        }
+    }
+
+    private static final class ResourceBundleHelper {
+
+        private final ResourceBundle bundle;
+        private final ResourceBundle pluginBundle;
+
+        ResourceBundleHelper(String path) {
+            Locale locale = Locale.getDefault();
+            try {
+                bundle = ResourceBundle.getBundle(path, locale);
+                pluginBundle = ResourceBundle.getBundle("jdk.tools.jlink.resources.plugins", locale);
+            } catch (MissingResourceException e) {
+                throw new InternalError("Cannot find jlink resource bundle for locale " + locale);
+            }
+        }
+
+        String getMessage(String key, Object... args) {
+            String val;
+            try {
+                val = bundle.getString(key);
+            } catch (MissingResourceException e) {
+                // XXX OK, check in plugin bundle
+                val = pluginBundle.getString(key);
+            }
+            return MessageFormat.format(val, args);
+        }
+
+    }
+
+    public final class OptionsHelper<T> {
+
+        private final List<Option<T>> options;
+        private String[] command;
+        private String defaults;
+
+        OptionsHelper(List<Option<T>> options) {
+            this.options = options;
+        }
+
+        private boolean hasArgument(String optionName) throws BadArgs {
+            Option<?> opt = getOption(optionName);
+            if (opt == null) {
+                opt = pluginOptions.getOption(optionName);
+                if (opt == null) {
+                    throw new BadArgs("err.unknown.option", optionName).
+                            showUsage(true);
+                }
+            }
+            return opt.hasArg;
+        }
+
+        private String getPluginsPath(String[] args) throws BadArgs {
+            String pp = null;
+            for (int i = 0; i < args.length; i++) {
+                if (args[i].equals(PluginsOptions.PLUGINS_PATH)) {
+                    if (i == args.length - 1) {
+                        throw new BadArgs("err.no.plugins.path").showUsage(true);
+                    } else {
+                        i += 1;
+                        pp = args[i];
+                        if (!pp.isEmpty() && pp.charAt(0) == '-') {
+                            throw new BadArgs("err.no.plugins.path").showUsage(true);
+                        }
+                        break;
+                    }
+                }
+            }
+            return pp;
+        }
+
+        public List<String> handleOptions(T task, String[] args) throws BadArgs {
+            // findbugs warning, copy instead of keeping a reference.
+            command = Arrays.copyOf(args, args.length);
+
+            // Must extract it prior to do any option analysis.
+            // Required to interpret custom plugin options.
+            // Unit tests can call Task multiple time in same JVM.
+            pluginOptions = new PluginsOptions(getPluginsPath(args));
+
+            // First extract plugins path if any
+            String pp = null;
+            List<String> filteredArgs = new ArrayList<>();
+            for (int i = 0; i < args.length; i++) {
+                if (args[i].equals(PluginsOptions.PLUGINS_PATH)) {
+                    if (i == args.length - 1) {
+                        throw new BadArgs("err.no.plugins.path").showUsage(true);
+                    } else {
+                        warning("warn.thirdparty.plugins.enabled");
+                        log.println(bundleHelper.getMessage("warn.thirdparty.plugins"));
+                        i += 1;
+                        String arg = args[i];
+                        if (!arg.isEmpty() && arg.charAt(0) == '-') {
+                            throw new BadArgs("err.no.plugins.path").showUsage(true);
+                        }
+                        pp = args[i];
+                    }
+                } else {
+                    filteredArgs.add(args[i]);
+                }
+            }
+            String[] arr = new String[filteredArgs.size()];
+            args = filteredArgs.toArray(arr);
+
+            List<String> rest = new ArrayList<>();
+            // process options
+            for (int i = 0; i < args.length; i++) {
+                if (!args[i].isEmpty() && args[i].charAt(0) == '-') {
+                    String name = args[i];
+                    PlugOption pluginOption = null;
+                    Option<T> option = getOption(name);
+                    if (option == null) {
+                        pluginOption = pluginOptions.getOption(name);
+                        if (pluginOption == null) {
+
+                            throw new BadArgs("err.unknown.option", name).
+                                    showUsage(true);
+                        }
+                    }
+                    Option<?> opt = pluginOption == null ? option : pluginOption;
+                    String param = null;
+                    if (opt.hasArg) {
+                        if (name.startsWith("--") && name.indexOf('=') > 0) {
+                            param = name.substring(name.indexOf('=') + 1,
+                                    name.length());
+                        } else if (i + 1 < args.length) {
+                            param = args[++i];
+                        }
+                        if (param == null || param.isEmpty()
+                                || (param.length() >= 2 && param.charAt(0) == '-'
+                                && param.charAt(1) == '-')) {
+                            throw new BadArgs("err.missing.arg", name).
+                                    showUsage(true);
+                        }
+                    }
+                    if (pluginOption != null) {
+                        pluginOption.process(pluginOptions, name, param);
+                    } else {
+                        option.process(task, name, param);
+                    }
+                    if (opt.ignoreRest()) {
+                        i = args.length;
+                    }
+                } else {
+                    rest.add(args[i]);
+                }
+            }
+            return rest;
+        }
+
+        private Option<T> getOption(String name) {
+            for (Option<T> o : options) {
+                if (o.matches(name)) {
+                    return o;
+                }
+            }
+            return null;
+        }
+
+        public void showHelp(String progName) {
+            log.println(bundleHelper.getMessage("main.usage", progName));
+            for (Option<?> o : options) {
+                String name = o.aliases[0].substring(1); // there must always be at least one name
+                name = name.charAt(0) == '-' ? name.substring(1) : name;
+                if (o.isHidden() || name.equals("h")) {
+                    continue;
+                }
+                log.println(bundleHelper.getMessage("main.opt." + name));
+            }
+            log.println(bundleHelper.getMessage("main.command.files"));
+        }
+
+        public void showXHelp(String progName, boolean showsImageBuilder) {
+            showHelp(progName);
+            for (Option<?> o : pluginOptions.mainOptions) {
+                if (o.aliases[0].equals(PluginsOptions.POST_PROCESS)
+                        && !showsImageBuilder) {
+                    continue;
+                }
+                String name = o.aliases[0].substring(1); // there must always be at least one name
+                name = name.charAt(0) == '-' ? name.substring(1) : name;
+                if (o.isHidden()) {
+                    continue;
+                }
+                log.println(bundleHelper.getMessage("plugin.opt." + name));
+            }
+
+            log.println("\n" + bundleHelper.getMessage("main.extended.help"));
+            List<Plugin> pluginList = PluginRepository.
+                    getPlugins(pluginOptions.pluginsLayer);
+            for (Plugin plugin : Utils.
+                    getSortedPreProcessors(pluginList)) {
+                showPlugin(plugin, log, showsImageBuilder);
+            }
+
+            if (showsImageBuilder) {
+                for (Plugin plugin : Utils.
+                        getSortedPostProcessors(pluginList)) {
+                    showPlugin(plugin, log, showsImageBuilder);
+                }
+            }
+        }
+
+        private void showPlugin(Plugin plugin, PrintWriter log, boolean showsImageBuilder) {
+            if (showsPlugin(plugin, showsImageBuilder)) {
+                log.println("\n" + bundleHelper.getMessage("main.plugin.name")
+                        + ": " + plugin.getName());
+                log.println(bundleHelper.getMessage("main.plugin.class")
+                        + ": " + plugin.getClass().getName());
+                log.println(bundleHelper.getMessage("main.plugin.module")
+                        + ": " + plugin.getClass().getModule().getName());
+                PluginOption opt = plugin.getOption();
+                if (opt != null) {
+                    log.println(bundleHelper.getMessage("main.plugin.option")
+                            + ": --" + opt.getName());
+                }
+                CATEGORY category = Utils.getCategory(plugin);
+                Integer[] range = ImagePluginConfiguration.getRange(category);
+                String cat = range == null ? category.getName() : category.getName();
+//                        + ". " + bundleHelper.getMessage("main.plugin.range.from")
+//                        + " " + range[0] + " " + bundleHelper.
+//                        getMessage("main.plugin.range.to") + " "
+//                        + range[1] + ".";
+                log.println(bundleHelper.getMessage("main.plugin.category")
+                        + ": " + cat);
+                log.println(bundleHelper.getMessage("main.plugin.description")
+                        + ": " + plugin.getDescription());
+
+                String desc = opt == null ? null : opt.getArgumentDescription();
+                log.println(bundleHelper.getMessage("main.plugin.argument")
+                        + ": " + (desc == null
+                                ? bundleHelper.getMessage("main.plugin.no.value")
+                                : desc));
+                if (plugin.getAdditionalOptions() != null && !plugin.getAdditionalOptions().isEmpty()) {
+                    StringBuilder builder = new StringBuilder();
+                    for (PluginOption o : plugin.getAdditionalOptions()) {
+                        builder.append("\n--").append(o.getName()).append(" ").
+                                append(o.getArgumentDescription() == null
+                                        ? bundleHelper.getMessage("main.plugin.no.value")
+                                        : o.getArgumentDescription());
+                    }
+                    log.println(bundleHelper.getMessage("main.plugin.additional.options")
+                            + ":" + builder.toString());
+                }
+                log.println(bundleHelper.getMessage("main.plugin.state")
+                        + ": " + plugin.getStateDescription());
+            }
+        }
+
+        String[] getInputCommand() {
+            return command;
+        }
+
+        String getDefaults() {
+            return defaults;
+        }
+
+        public Layer getPluginsLayer() {
+            return pluginOptions.pluginsLayer;
+        }
+    }
+
+    private PluginsOptions pluginOptions;
+    private PrintWriter log;
+    private final ResourceBundleHelper bundleHelper;
+
+    public TaskHelper(String path) {
+        if (!JLINK_BUNDLE.equals(path) && !JIMAGE_BUNDLE.equals(path)) {
+            throw new IllegalArgumentException("Invalid Bundle");
+        }
+        this.bundleHelper = new ResourceBundleHelper(path);
+    }
+
+    public <T> OptionsHelper<T> newOptionsHelper(Class<T> clazz,
+            Option<?>[] options) {
+        List<Option<T>> optionsList = new ArrayList<>();
+        for (Option<?> o : options) {
+            @SuppressWarnings("unchecked")
+            Option<T> opt = (Option<T>) o;
+            optionsList.add(opt);
+        }
+        return new OptionsHelper<>(optionsList);
+    }
+
+    public BadArgs newBadArgs(String key, Object... args) {
+        return new BadArgs(key, args);
+    }
+
+    public String getMessage(String key, Object... args) {
+        return bundleHelper.getMessage(key, args);
+    }
+
+    public void setLog(PrintWriter log) {
+        this.log = log;
+    }
+
+    public void reportError(String key, Object... args) {
+        log.println(bundleHelper.getMessage("error.prefix") + " "
+                + bundleHelper.getMessage(key, args));
+    }
+
+    public void reportUnknownError(String message) {
+        log.println(bundleHelper.getMessage("error.prefix") + " " + message);
+    }
+
+    public void warning(String key, Object... args) {
+        log.println(bundleHelper.getMessage("warn.prefix") + " "
+                + bundleHelper.getMessage(key, args));
+    }
+
+    public PluginsConfiguration getPluginsConfig(Path output, boolean genbom) throws IOException {
+        return pluginOptions.getPluginsConfig(output, genbom);
+    }
+
+    public Path getExistingImage() {
+        return pluginOptions.existingImage;
+    }
+
+    public void showVersion(boolean full) {
+        log.println(version(full ? "full" : "release"));
+    }
+
+    public String version(String key) {
+        return System.getProperty("java.version");
+    }
+
+    static Layer createPluginsLayer(List<Path> paths) {
+        Path[] arr = new Path[paths.size()];
+        paths.toArray(arr);
+        ModuleFinder finder = ModuleFinder.of(arr);
+
+        // jmods are located at link-time
+        if (finder instanceof ConfigurableModuleFinder) {
+            ((ConfigurableModuleFinder) finder).configurePhase(Phase.LINK_TIME);
+        }
+
+        Configuration bootConfiguration = Layer.boot().configuration();
+
+        Configuration cf
+                = Configuration.resolve(ModuleFinder.empty(), bootConfiguration, finder);
+
+        cf = cf.bind();
+
+        ClassLoader scl = ClassLoader.getSystemClassLoader();
+        return Layer.createWithOneLoader(cf, Layer.boot(), scl);
+    }
+
+    // Display all plugins or pre processors only.
+    private static boolean showsPlugin(Plugin plugin, boolean showsImageBuilder) {
+        if (Utils.isEnabled(plugin) && plugin.getOption() != null) {
+            if (Utils.isPostProcessor(plugin) && !showsImageBuilder) {
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Utils.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Utils.java	Mon Dec 21 14:42:19 2015 +0100
@@ -25,10 +25,11 @@
 package jdk.tools.jlink.internal;
 
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.List;
 import java.util.function.Function;
-import jdk.tools.jlink.api.plugin.Plugin;
-import jdk.tools.jlink.api.plugin.Plugin.PluginType;
+import jdk.tools.jlink.plugin.Plugin;
+import jdk.tools.jlink.plugin.Plugin.PluginType;
 
 /**
  *
@@ -62,36 +63,44 @@
     }
 
     public static boolean isPostProcessor(Plugin prov) {
-        for(PluginType pt : prov.getType()) {
-            if(pt instanceof Plugin.CATEGORY) {
-                return isPostProcessor((Plugin.CATEGORY) pt);
+        if (prov.getType() != null) {
+            for (PluginType pt : prov.getType()) {
+                if (pt instanceof Plugin.CATEGORY) {
+                    return isPostProcessor((Plugin.CATEGORY) pt);
+                }
             }
         }
         return false;
     }
 
     public static boolean isPreProcessor(Plugin prov) {
-        for(PluginType pt : prov.getType()) {
-            if(pt instanceof Plugin.CATEGORY) {
-                return isPreProcessor((Plugin.CATEGORY) pt);
+        if (prov.getType() != null) {
+            for (PluginType pt : prov.getType()) {
+                if (pt instanceof Plugin.CATEGORY) {
+                    return isPreProcessor((Plugin.CATEGORY) pt);
+                }
             }
         }
         return false;
     }
 
     public static Plugin.ORDER getOrder(Plugin provider) {
-        for (Plugin.PluginType t : provider.getType()) {
-            if (t instanceof Plugin.ORDER) {
-                return (Plugin.ORDER) t;
+        if (provider.getType() != null) {
+            for (Plugin.PluginType t : provider.getType()) {
+                if (t instanceof Plugin.ORDER) {
+                    return (Plugin.ORDER) t;
+                }
             }
         }
         return null;
     }
 
     public static Plugin.CATEGORY getCategory(Plugin provider) {
-        for (Plugin.PluginType t : provider.getType()) {
-            if (t instanceof Plugin.CATEGORY) {
-                return (Plugin.CATEGORY) t;
+        if (provider.getType() != null) {
+            for (Plugin.PluginType t : provider.getType()) {
+                if (t instanceof Plugin.CATEGORY) {
+                    return (Plugin.CATEGORY) t;
+                }
             }
         }
         return null;
@@ -107,6 +116,28 @@
         return res;
     }
 
+    public static List<Plugin> getSortedPostProcessors(List<Plugin> plugins) {
+        List<Plugin> res = getPostProcessors(plugins);
+        res.sort(new Comparator<Plugin>() {
+            @Override
+            public int compare(Plugin o1, Plugin o2) {
+                return o1.getName().compareTo(o2.getName());
+            }
+        });
+        return res;
+    }
+
+    public static List<Plugin> getSortedPreProcessors(List<Plugin> plugins) {
+        List<Plugin> res = getPreProcessors(plugins);
+        res.sort(new Comparator<Plugin>() {
+            @Override
+            public int compare(Plugin o1, Plugin o2) {
+                return o1.getName().compareTo(o2.getName());
+            }
+        });
+        return res;
+    }
+
     public static List<Plugin> getPreProcessors(List<Plugin> plugins) {
         List<Plugin> res = new ArrayList<>();
         for (Plugin p : plugins) {
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultCompressPlugin.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultCompressPlugin.java	Mon Dec 21 14:42:19 2015 +0100
@@ -32,11 +32,11 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption.Builder;
+import jdk.tools.jlink.plugin.PluginOption;
+import jdk.tools.jlink.plugin.PluginOption.Builder;
 import jdk.tools.jlink.internal.PoolImpl;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.transformer.TransformerPlugin;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.TransformerPlugin;
 import jdk.tools.jlink.internal.ImagePluginStack;
 import jdk.tools.jlink.internal.ResourcePrevisitor;
 import jdk.tools.jlink.internal.StringTable;
@@ -51,19 +51,16 @@
     static final String NAME = "compress-resources";
     public static final PluginOption NAME_OPTION
             = new Builder(NAME).
-            description(PluginsResourceBundle.getDescription(NAME)).
-            hasOnOffArgument().build();
+            description(PluginsResourceBundle.getDescription(NAME)).build();
 
     private static final String LEVEL = "compress-resources-level";
     public static final PluginOption LEVEL_OPTION
             = new Builder(LEVEL).
-            description(PluginsResourceBundle.getOption(NAME, LEVEL)).
             argumentDescription(PluginsResourceBundle.getOption(NAME, LEVEL)).
             build();
     private static final String FILTER = "compress-resources-filter";
     public static final PluginOption FILTER_OPTION
             = new Builder(FILTER).
-            description(PluginsResourceBundle.getOption(NAME, FILTER)).
             argumentDescription(PluginsResourceBundle.getOption(NAME, FILTER)).
             build();
     public static final String LEVEL_0 = "0";
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeFilesPlugin.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeFilesPlugin.java	Mon Dec 21 14:42:19 2015 +0100
@@ -31,11 +31,11 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Predicate;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption.Builder;
-import jdk.tools.jlink.api.plugin.transformer.TransformerPlugin;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleDataType;
+import jdk.tools.jlink.plugin.PluginOption;
+import jdk.tools.jlink.plugin.PluginOption.Builder;
+import jdk.tools.jlink.plugin.TransformerPlugin;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.Pool.ModuleDataType;
 import jdk.tools.jlink.internal.Utils;
 
 /**
@@ -60,7 +60,7 @@
     public void visit(Pool in, Pool out) {
         in.visit((file) -> {
             if (!file.getType().equals(ModuleDataType.CLASS_OR_RESOURCE)) {
-                file = predicate.test("/" + file.getModule() + "/" + file.getPath()) ? file : null;
+                file = predicate.test(file.getPath()) ? file : null;
             }
             return file;
         }, out);
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludePlugin.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludePlugin.java	Mon Dec 21 14:42:19 2015 +0100
@@ -30,11 +30,11 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Predicate;
-import jdk.tools.jlink.api.plugin.PluginException;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption.Builder;
-import jdk.tools.jlink.api.plugin.transformer.TransformerPlugin;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
+import jdk.tools.jlink.plugin.PluginException;
+import jdk.tools.jlink.plugin.PluginOption;
+import jdk.tools.jlink.plugin.PluginOption.Builder;
+import jdk.tools.jlink.plugin.TransformerPlugin;
+import jdk.tools.jlink.plugin.Pool;
 import jdk.tools.jlink.internal.Utils;
 
 /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeVMPlugin.java	Mon Dec 21 14:42:19 2015 +0100
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2015, 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 jdk.tools.jlink.internal.plugins;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.function.Predicate;
+import jdk.tools.jlink.plugin.PluginOption;
+import jdk.tools.jlink.plugin.PluginOption.Builder;
+import jdk.tools.jlink.plugin.TransformerPlugin;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.Pool.ModuleDataType;
+import jdk.tools.jlink.internal.Utils;
+import jdk.tools.jlink.plugin.PluginException;
+
+/**
+ *
+ * Exclude VM plugin
+ */
+public final class ExcludeVMPlugin implements TransformerPlugin {
+
+    private static final class JvmComparator implements Comparator<Jvm> {
+
+        @Override
+        public int compare(Jvm o1, Jvm o2) {
+            return o1.getEfficience() - o2.getEfficience();
+        }
+    }
+
+    private enum Jvm {
+        // The efficience order server - client - minimal.
+        SERVER("server", 3), CLIENT("client", 2), MINIMAL("minimal", 1);
+        private final String name;
+        private final int efficience;
+
+        Jvm(String name, int efficience) {
+            this.name = name;
+            this.efficience = efficience;
+        }
+
+        private String getName() {
+            return name;
+        }
+
+        private int getEfficience() {
+            return efficience;
+        }
+    }
+
+    private static final String JVM_CFG = "jvm.cfg";
+
+    public static final String NAME = "vm";
+    private static final String ALL = "all";
+    private static final String CLIENT = "client";
+    private static final String SERVER = "server";
+    private static final String MINIMAL = "minimal";
+
+    public static final PluginOption NAME_OPTION
+            = new Builder(NAME).
+            description(PluginsResourceBundle.getDescription(NAME)).
+            argumentDescription(PluginsResourceBundle.getArgument(NAME)).build();
+    private Predicate<String> predicate;
+    private Jvm target;
+    private boolean keepAll;
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public void visit(Pool in, Pool out) {
+        String jvmlib = jvmlib();
+        TreeSet<Jvm> existing = new TreeSet<>(new JvmComparator());
+        TreeSet<Jvm> removed = new TreeSet<>(new JvmComparator());
+        if (!keepAll) {
+            // First retrieve all available VM names and removed VM
+            for (Jvm jvm : Jvm.values()) {
+                Pool.ModuleData file = in.get("/java.base/native/" + jvm.getName() + "/" + jvmlib);
+                if (file != null) {
+                    existing.add(jvm);
+                    if (isRemoved(file)) {
+                        removed.add(jvm);
+                    }
+                }
+            }
+        }
+
+        // Check that target exists
+        if (!keepAll) {
+            if (!existing.contains(target)) {
+                throw new PluginException("Selected VM " + target.getName() + " doesn't exist.");
+            }
+        }
+
+        // Rewrite the jvm.cfg file.
+        in.visit((file) -> {
+            if (!keepAll) {
+                if (file.getType().equals(ModuleDataType.NATIVE_LIB)) {
+                    if (file.getPath().endsWith(JVM_CFG)) {
+                        try {
+                            file = handleJvmCfgFile(file, existing, removed);
+                        } catch (IOException ex) {
+                            throw new UncheckedIOException(ex);
+                        }
+                    }
+                }
+                file = isRemoved(file) ? null : file;
+            }
+            return file;
+        }, out);
+
+    }
+
+    private boolean isRemoved(Pool.ModuleData file) {
+        return !predicate.test(file.getPath());
+    }
+
+    @Override
+    public PluginOption getOption() {
+        return NAME_OPTION;
+    }
+
+    @Override
+    public Set<PluginType> getType() {
+        Set<PluginType> set = new HashSet<>();
+        set.add(CATEGORY.FILTER);
+        return Collections.unmodifiableSet(set);
+    }
+
+    @Override
+    public String getDescription() {
+        return PluginsResourceBundle.getDescription(NAME);
+    }
+
+    @Override
+    public void configure(Map<PluginOption, String> config) {
+        try {
+            String value = config.get(NAME_OPTION);
+            String exclude = "";
+            switch (value) {
+                case ALL: {
+                    // no filter.
+                    keepAll = true;
+                    break;
+                }
+                case CLIENT: {
+                    target = Jvm.CLIENT;
+                    exclude = "/java.base/native/server/*,/java.base/native/minimal/*";
+                    break;
+                }
+                case SERVER: {
+                    target = Jvm.SERVER;
+                    exclude = "/java.base/native/client/*,/java.base/native/minimal/*";
+                    break;
+                }
+                case MINIMAL: {
+                    target = Jvm.MINIMAL;
+                    exclude = "/java.base/native/server/*,/java.base/native/client/*";
+                    break;
+                }
+                default: {
+                    throw new PluginException("Unknown option " + value);
+                }
+            }
+            predicate = new ResourceFilter(Utils.listParser.apply(exclude), true);
+        } catch (IOException ex) {
+            throw new UncheckedIOException(ex);
+        }
+    }
+
+    private Pool.ModuleData handleJvmCfgFile(Pool.ModuleData orig,
+            TreeSet<Jvm> existing,
+            TreeSet<Jvm> removed) throws IOException {
+        if (keepAll) {
+            return orig;
+        }
+        StringBuilder builder = new StringBuilder();
+        // Keep comments
+        try (BufferedReader reader
+                = new BufferedReader(new InputStreamReader(orig.stream(),
+                        StandardCharsets.UTF_8))) {
+            reader.lines().forEach((s) -> {
+                if (s.startsWith("#")) {
+                    builder.append(s).append("\n");
+                }
+            });
+        }
+        TreeSet<Jvm> remaining = new TreeSet<>(new JvmComparator());
+        // Add entry in jvm.cfg file from the more efficient to less efficient.
+        for (Jvm platform : existing) {
+            if (!removed.contains(platform)) {
+                remaining.add(platform);
+                builder.append("-").append(platform.getName()).append(" KNOWN\n");
+            }
+        }
+
+        // removed JVM are aliased to the most efficient remaining JVM (last one).
+        // The order in the file is from most to less efficient platform
+        for (Jvm platform : removed.descendingSet()) {
+            builder.append("-").append(platform.getName()).
+                    append(" ALIASED_TO -").
+                    append(remaining.last().getName()).append("\n");
+        }
+
+        byte[] content = builder.toString().getBytes(StandardCharsets.UTF_8);
+
+        return Pool.newImageFile(orig.getModule(),
+                orig.getPath(),
+                orig.getType(),
+                new ByteArrayInputStream(content), content.length);
+    }
+
+    private static String jvmlib() {
+        String lib = "libjvm.so";
+        if (isWindows()) {
+            lib = "jvm.dll";
+        } else if (isMac()) {
+            lib = "libjvm.dylib";
+        }
+        return lib;
+    }
+
+    private static boolean isWindows() {
+        return System.getProperty("os.name").startsWith("Windows");
+    }
+
+    private static boolean isMac() {
+        return System.getProperty("os.name").startsWith("Mac OS");
+    }
+}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/FileCopierPlugin.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/FileCopierPlugin.java	Mon Dec 21 14:42:19 2015 +0100
@@ -41,13 +41,13 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import jdk.tools.jlink.api.plugin.PluginException;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption.Builder;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleData;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleDataType;
-import jdk.tools.jlink.api.plugin.transformer.TransformerPlugin;
+import jdk.tools.jlink.plugin.PluginException;
+import jdk.tools.jlink.plugin.PluginOption;
+import jdk.tools.jlink.plugin.PluginOption.Builder;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.Pool.ModuleData;
+import jdk.tools.jlink.plugin.Pool.ModuleDataType;
+import jdk.tools.jlink.plugin.TransformerPlugin;
 import jdk.tools.jlink.internal.Utils;
 
 /**
@@ -67,7 +67,7 @@
         Path source;
         Path target;
     }
-    private static final String FAKE_MODULE = "$jlink-file-copier";
+    public static final String FAKE_MODULE = "$jlink-file-copier";
 
     private final List<CopiedFile> files = new ArrayList<>();
 
@@ -183,7 +183,8 @@
         Objects.requireNonNull(pool);
         Objects.requireNonNull(file);
         Objects.requireNonNull(path);
-        ModuleData impl = Pool.newImageFile(FAKE_MODULE, path,
+        ModuleData impl = Pool.newImageFile(FAKE_MODULE,
+                "/" + FAKE_MODULE + "/other/" + path,
                 Pool.ModuleDataType.OTHER, newStream(file), length(file));
         try {
             pool.add(impl);
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/FileReplacerPlugin.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/FileReplacerPlugin.java	Mon Dec 21 14:42:19 2015 +0100
@@ -1,119 +0,0 @@
-/*
- * Copyright (c) 2015, 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 jdk.tools.jlink.internal.plugins;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.UncheckedIOException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption.Builder;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.transformer.TransformerPlugin;
-import jdk.tools.jlink.internal.Utils;
-
-/**
- *
- * Replaces files with custom content
- */
-public final class FileReplacerPlugin implements TransformerPlugin {
-
-    private final Map<String, File> mapping = new HashMap<>();
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-    @Override
-    public void visit(Pool inFiles, Pool outFiles) {
-        inFiles.visit((file) -> {
-            if (!file.getType().equals(Pool.ModuleDataType.CLASS_OR_RESOURCE)) {
-                File replaced = mapping.get("/" + file.getModule() + "/"
-                        + file.getPath());
-                if (replaced != null) {
-                    try {
-                        file = Pool.newImageFile(file.getModule(), file.getPath(),
-                                file.getType(), new FileInputStream(replaced), replaced.length());
-                    } catch (FileNotFoundException ex) {
-                        throw new UncheckedIOException(ex);
-                    }
-                }
-            }
-            return file;
-        }, outFiles);
-    }
-
-    public static final String NAME = "replace-file";
-    public static final PluginOption NAME_OPTION =
-            new Builder(NAME).
-            description(PluginsResourceBundle.getDescription(NAME)).
-            argumentDescription(PluginsResourceBundle.getArgument(NAME))
-                    .build();
-
-    @Override
-    public PluginOption getOption() {
-        return NAME_OPTION;
-    }
-
-   @Override
-    public Set<PluginType> getType() {
-        Set<PluginType> set = new HashSet<>();
-        set.add(CATEGORY.COMPRESSOR);
-        return Collections.unmodifiableSet(set);
-    }
-
-    @Override
-    public String getDescription() {
-        return PluginsResourceBundle.getDescription(NAME);
-    }
-
-    @Override
-    public void configure(Map<PluginOption, String> config) {
-        String val = config.get(NAME_OPTION);
-        String[] arguments = Utils.listParser.apply(val);
-        for (int i = 0; i < arguments.length; i++) {
-            String path = arguments[i];
-            i++;
-            if (i < arguments.length) {
-                File replacement = new File(arguments[i]);
-                if (!replacement.exists()) {
-                    throw new RuntimeException("Replacement file " + replacement
-                            + " doesn't exist.");
-                }
-                mapping.put(path, replacement);
-            } else {
-                throw new RuntimeException("Replacing file, "
-                        + "invalid number of arguments");
-            }
-        }
-    }
-
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/InstalledModuleDescriptorPlugin.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/InstalledModuleDescriptorPlugin.java	Mon Dec 21 14:42:19 2015 +0100
@@ -42,19 +42,19 @@
 import jdk.tools.jlink.internal.plugins.asm.AsmModulePool;
 
 import static jdk.internal.org.objectweb.asm.Opcodes.*;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption;
+import jdk.tools.jlink.plugin.PluginOption;
 
 /**
- * Jlink plugin to reconstitute module descriptors for installed modules. It
- * also determines the number of packages of the boot layer at link time.
+ * Jlink plugin to reconstitute module descriptors for installed modules.
+ * It also determines the number of packages of the boot layer at link time.
  *
  * This plugin will override jdk.internal.module.InstalledModules class
  *
- * This plugin is enabled by default. This can be disabled via jlink
- * --gen-installed-modules off option.
+ * This plugin is enabled by default. This can be disabled via
+ * jlink --gen-installed-modules off option.
  *
- * TODO: module-info.class may not have the ConcealedPackages attribute. This
- * plugin or a new plugin should add to module-info.class, if not present.
+ * TODO: module-info.class may not have the ConcealedPackages attribute.
+ * This plugin or a new plugin should add to module-info.class, if not present.
  *
  * @see java.lang.module.InstalledModuleFinder
  * @see jdk.internal.module.InstalledModules
@@ -162,17 +162,17 @@
     }
 
     /**
-     * Builder of a new jdk.internal.module.InstalledModules class to
-     * reconstitute ModuleDescriptor of the installed modules.
+     * Builder of a new jdk.internal.module.InstalledModules class
+     * to reconstitute ModuleDescriptor of the installed modules.
      */
     static class Builder {
 
-        private static final String CLASSNAME
-                = "jdk/internal/module/InstalledModules";
-        private static final String MODULE_DESCRIPTOR_BUILDER
-                = "jdk/internal/module/Builder";
-        private static final String MODULES_MAP_SIGNATURE
-                = "Ljava/util/Map<Ljava/lang/String;Ljava/lang/module/ModuleDescriptor;>;";
+        private static final String CLASSNAME =
+            "jdk/internal/module/InstalledModules";
+        private static final String MODULE_DESCRIPTOR_BUILDER =
+            "jdk/internal/module/Builder";
+        private static final String MODULES_MAP_SIGNATURE =
+            "Ljava/util/Map<Ljava/lang/String;Ljava/lang/module/ModuleDescriptor;>;";
 
         // static variables in InstalledModules class
         private static final String MODULE_NAMES = "MODULE_NAMES";
@@ -197,9 +197,9 @@
         private final Map<Set<String>, StringSetBuilder> stringSets = new HashMap<>();
 
         public Builder(Set<String> moduleNames, int numPackages) {
-            this.cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
+            this.cw = new ClassWriter(ClassWriter.COMPUTE_MAXS+ClassWriter.COMPUTE_FRAMES);
             this.clinit(moduleNames, numPackages);
-            this.mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
+            this.mv = cw.visitMethod(ACC_PUBLIC+ACC_STATIC,
                     "modules", "()Ljava/util/Map;",
                     "()" + MODULES_MAP_SIGNATURE, null);
             mv.visitCode();
@@ -211,21 +211,21 @@
          * static Map<String, ModuleDescriptor> map = new HashMap<>();
          */
         private void clinit(Set<String> moduleNames, int numPackages) {
-            cw.visit(Opcodes.V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, CLASSNAME,
+            cw.visit(Opcodes.V1_8, ACC_PUBLIC+ACC_FINAL+ACC_SUPER, CLASSNAME,
                     null, "java/lang/Object", null);
 
             // public static String[] MODULE_NAMES = new String[] {....};
-            cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, MODULE_NAMES,
+            cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, MODULE_NAMES,
                     "[Ljava/lang/String;", null, null)
                     .visitEnd();
 
             // public static int PACKAGES_IN_BOOT_LAYER;
-            cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, PACKAGE_COUNT,
+            cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, PACKAGE_COUNT,
                     "I", null, numPackages)
                     .visitEnd();
 
             // static Map<String, ModuleDescriptor> map = new HashMap<>();
-            cw.visitField(ACC_FINAL + ACC_STATIC, DESCRIPTOR_MAP, MAP_TYPE,
+            cw.visitField(ACC_FINAL+ACC_STATIC, DESCRIPTOR_MAP, MAP_TYPE,
                     MODULES_MAP_SIGNATURE, null)
                     .visitEnd();
 
@@ -241,6 +241,7 @@
             mv.visitIntInsn(numModules < Byte.MAX_VALUE ? BIPUSH : SIPUSH, numModules);
             mv.visitTypeInsn(ANEWARRAY, "[Ljava/lang/String;");
 
+
             mv.visitTypeInsn(NEW, "java/util/HashMap");
             mv.visitInsn(DUP);
             mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap",
@@ -298,7 +299,7 @@
                 Set<String> names, int size) {
             mv.visitIntInsn(size < Byte.MAX_VALUE ? BIPUSH : SIPUSH, size);
             mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
-            int index = 0;
+            int index=0;
             for (String n : names) {
                 addElement(cw, mv, index++);
                 mv.visitLdcInsn(n);      // value
@@ -390,8 +391,8 @@
                             requires(req.name());
                             break;
                         case 1:
-                            ModuleDescriptor.Requires.Modifier mod
-                                    = req.modifiers().iterator().next();
+                            ModuleDescriptor.Requires.Modifier mod =
+                                req.modifiers().iterator().next();
                             requires(mod, req.name());
                             break;
                         default:
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java	Mon Dec 21 14:42:19 2015 +0100
@@ -46,8 +46,8 @@
 import jdk.internal.org.objectweb.asm.tree.ClassNode;
 import jdk.internal.org.objectweb.asm.tree.MethodNode;
 import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption.Builder;
+import jdk.tools.jlink.plugin.PluginOption;
+import jdk.tools.jlink.plugin.PluginOption.Builder;
 import jdk.tools.jlink.internal.plugins.asm.AsmModulePool;
 import jdk.tools.jlink.internal.plugins.optim.ForNameFolding;
 import jdk.tools.jlink.internal.plugins.optim.ReflectionOptimizer.TypeResolver;
@@ -70,7 +70,7 @@
             argumentDescription(PluginsResourceBundle.getArgument(NAME, ALL, FORNAME_REMOVAL)).
             build();
     public static final PluginOption LOG_OPTION = new Builder(LOG_FILE).
-            description(PluginsResourceBundle.getOption(NAME, LOG_FILE)).
+            argumentDescription(PluginsResourceBundle.getOption(NAME, LOG_FILE)).
             build();
 
     /**
@@ -263,8 +263,8 @@
 
     @Override
     public void configure(Map<PluginOption, String> config) {
-       String strategies = config.get(NAME_OPTION);
-       String[] arr = strategies.split(":");
+        String strategies = config.get(NAME_OPTION);
+        String[] arr = strategies.split(":");
         for (String s : arr) {
             if (s.equals(ALL)) {
                 optimizers.clear();
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SortResourcesPlugin.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SortResourcesPlugin.java	Mon Dec 21 14:42:19 2015 +0100
@@ -39,13 +39,13 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
-import jdk.tools.jlink.api.plugin.PluginException;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption.Builder;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleData;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleDataType;
-import jdk.tools.jlink.api.plugin.transformer.TransformerPlugin;
+import jdk.tools.jlink.plugin.PluginException;
+import jdk.tools.jlink.plugin.PluginOption;
+import jdk.tools.jlink.plugin.PluginOption.Builder;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.Pool.ModuleData;
+import jdk.tools.jlink.plugin.Pool.ModuleDataType;
+import jdk.tools.jlink.plugin.TransformerPlugin;
 import jdk.tools.jlink.internal.Utils;
 
 /**
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StringSharingPlugin.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StringSharingPlugin.java	Mon Dec 21 14:42:19 2015 +0100
@@ -57,12 +57,12 @@
 import jdk.internal.jimage.decompressor.SignatureParser;
 import jdk.internal.jimage.decompressor.StringSharingDecompressor;
 import jdk.tools.jlink.internal.PoolImpl;
-import jdk.tools.jlink.api.plugin.transformer.TransformerPlugin;
-import jdk.tools.jlink.api.plugin.PluginException;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption.Builder;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleData;
+import jdk.tools.jlink.plugin.TransformerPlugin;
+import jdk.tools.jlink.plugin.PluginException;
+import jdk.tools.jlink.plugin.PluginOption;
+import jdk.tools.jlink.plugin.PluginOption.Builder;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.Pool.ModuleData;
 import jdk.tools.jlink.internal.ResourcePrevisitor;
 import jdk.tools.jlink.internal.StringTable;
 import jdk.tools.jlink.internal.Utils;
@@ -342,7 +342,9 @@
 
     private Predicate<String> predicate;
 
-    public StringSharingPlugin() {
+    public StringSharingPlugin() throws IOException {
+        this(new String[0]);
+    }
 
     }
 
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripDebugPlugin.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripDebugPlugin.java	Mon Dec 21 14:42:19 2015 +0100
@@ -24,28 +24,43 @@
  */
 package jdk.tools.jlink.internal.plugins;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
-import jdk.tools.jlink.internal.plugins.asm.AsmPools;
-import jdk.tools.jlink.internal.plugins.asm.AsmPlugin;
+import java.util.function.Predicate;
 import jdk.internal.org.objectweb.asm.ClassReader;
 import jdk.internal.org.objectweb.asm.ClassWriter;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption.Builder;
+import jdk.tools.jlink.plugin.PluginOption;
+import jdk.tools.jlink.plugin.PluginOption.Builder;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.Pool.ModuleData;
+import jdk.tools.jlink.plugin.Pool.ModuleDataType;
+import jdk.tools.jlink.plugin.TransformerPlugin;
 
 /**
  *
  * Strip debug attributes plugin
  */
-public final class StripDebugPlugin extends AsmPlugin {
+public final class StripDebugPlugin implements TransformerPlugin {
 
-    public static final String NAME = "strip-java-debug";
+    private Predicate<String> predicate;
+    public static final String NAME = "strip-debug";
     public static final PluginOption NAME_OPTION
             = new Builder(NAME).
-            description(PluginsResourceBundle.getDescription(NAME)).
-            hasOnOffArgument().build();
+            description(PluginsResourceBundle.getDescription(NAME)).build();
+    private static final String[] PATTERNS = {"*.diz"};
+
+    public StripDebugPlugin() {
+        try {
+            predicate = new ResourceFilter(PATTERNS);
+        } catch (IOException ex) {
+            throw new UncheckedIOException(ex);
+        }
+    }
 
     @Override
     public String getName() {
@@ -65,20 +80,6 @@
     }
 
     @Override
-    public void visit(AsmPools pools) {
-        pools.getGlobalPool().visitClassReaders((reader) -> {
-            ClassWriter writer = null;
-            if (reader.getClassName().contains("module-info")) {//eg: java.base/module-info
-                // XXX. Do we have debug info? Is Asm ready for module-info?
-            } else {
-                writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
-                reader.accept(writer, ClassReader.SKIP_DEBUG);
-            }
-            return writer;
-        });
-    }
-
-    @Override
     public String getDescription() {
         return PluginsResourceBundle.getDescription(NAME);
     }
@@ -87,4 +88,29 @@
     public void configure(Map<PluginOption, String> config) {
 
     }
+
+    @Override
+    public void visit(Pool in, Pool out) {
+        //remove *.diz files as well as debug attributes.
+        in.visit((resource) -> {
+            ModuleData res = resource;
+            if (resource.getType().equals(ModuleDataType.CLASS_OR_RESOURCE)) {
+                String path = resource.getPath();
+                if (path.endsWith(".class")) {
+                    if (path.endsWith("module-info.class")) {
+                        // XXX. Do we have debug info? Is Asm ready for module-info?
+                    } else {
+                        ClassReader reader = new ClassReader(resource.getBytes());
+                        ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+                        reader.accept(writer, ClassReader.SKIP_DEBUG);
+                        byte[] content = writer.toByteArray();
+                        res = Pool.newResource(path, new ByteArrayInputStream(content), content.length);
+                    }
+                }
+            } else if (predicate.test(res.getPath())) {
+                res = null;
+            }
+            return res;
+        }, out);
+    }
 }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripNativeCommandsPlugin.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripNativeCommandsPlugin.java	Mon Dec 21 14:42:19 2015 +0100
@@ -28,10 +28,10 @@
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption.Builder;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.transformer.TransformerPlugin;
+import jdk.tools.jlink.plugin.PluginOption;
+import jdk.tools.jlink.plugin.PluginOption.Builder;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.TransformerPlugin;
 
 /**
  *
@@ -43,8 +43,7 @@
 
     private static final PluginOption NAME_OPTION
             = new Builder(NAME).
-            description(PluginsResourceBundle.getDescription(NAME)).
-            hasOnOffArgument().build();
+            description(PluginsResourceBundle.getDescription(NAME)).build();
 
     @Override
     public String getName() {
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ZipPlugin.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ZipPlugin.java	Mon Dec 21 14:42:19 2015 +0100
@@ -33,14 +33,14 @@
 import java.util.Set;
 import java.util.function.Predicate;
 import java.util.zip.Deflater;
-import jdk.tools.jlink.api.plugin.PluginException;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption;
-import jdk.tools.jlink.api.plugin.Plugin.PluginOption.Builder;
+import jdk.tools.jlink.plugin.PluginException;
+import jdk.tools.jlink.plugin.PluginOption;
+import jdk.tools.jlink.plugin.PluginOption.Builder;
 import jdk.tools.jlink.internal.PoolImpl;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleData;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleDataType;
-import jdk.tools.jlink.api.plugin.transformer.TransformerPlugin;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.Pool.ModuleData;
+import jdk.tools.jlink.plugin.Pool.ModuleDataType;
+import jdk.tools.jlink.plugin.TransformerPlugin;
 import jdk.tools.jlink.internal.Utils;
 
 /**
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPlugin.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPlugin.java	Mon Dec 21 14:42:19 2015 +0100
@@ -25,9 +25,9 @@
 package jdk.tools.jlink.internal.plugins.asm;
 
 import java.util.Objects;
-import jdk.tools.jlink.api.plugin.transformer.TransformerPlugin;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleData;
+import jdk.tools.jlink.plugin.TransformerPlugin;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.Pool.ModuleData;
 import jdk.tools.jlink.internal.PoolImpl;
 
 /**
@@ -62,7 +62,7 @@
      * apply Asm transformation to jimage contained classes.
      * @param pools The pool of Asm classes and other resource files.
      * @param strings To add a string to the jimage strings table.
-     * @throws jdk.tools.jlink.api.plugin.PluginException
+     * @throws jdk.tools.jlink.plugin.PluginException
      */
     public abstract void visit(AsmPools pools);
 }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPool.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPool.java	Mon Dec 21 14:42:19 2015 +0100
@@ -30,7 +30,7 @@
 import java.util.List;
 import jdk.internal.org.objectweb.asm.ClassReader;
 import jdk.internal.org.objectweb.asm.ClassWriter;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
+import jdk.tools.jlink.plugin.Pool;
 
 /**
  * A pool of ClassReader and other resource files.
@@ -110,7 +110,7 @@
          * Add a class to the pool, if a class already exists, it is replaced.
          *
          * @param writer The class writer.
-         * @throws jdk.tools.jlink.api.plugin.PluginException
+         * @throws jdk.tools.jlink.plugin.PluginException
          */
         public void addClass(ClassWriter writer);
 
@@ -118,7 +118,7 @@
          * The class will be not added to the jimage file.
          *
          * @param className The class name to forget.
-         * @throws jdk.tools.jlink.api.plugin.PluginException
+         * @throws jdk.tools.jlink.plugin.PluginException
          */
         public void forgetClass(String className);
 
@@ -127,7 +127,7 @@
          *
          * @param binaryName The java class binary name
          * @return The ClassReader or null if the class is not found.
-         * @throws jdk.tools.jlink.api.plugin.PluginException
+         * @throws jdk.tools.jlink.plugin.PluginException
          */
         public ClassReader getClassReader(String binaryName);
 
@@ -136,7 +136,7 @@
          *
          * @param res A class resource.
          * @return The ClassReader or null if the class is not found.
-         * @throws jdk.tools.jlink.api.plugin.PluginException
+         * @throws jdk.tools.jlink.plugin.PluginException
          */
         public ClassReader getClassReader(Pool.ModuleData res);
 
@@ -158,7 +158,7 @@
          * Add a resource, if the resource exists, it is replaced.
          *
          * @param resFile The resource file to add.
-         * @throws jdk.tools.jlink.api.plugin.PluginException
+         * @throws jdk.tools.jlink.plugin.PluginException
          */
         public void addResourceFile(ResourceFile resFile);
 
@@ -166,7 +166,7 @@
          * The resource will be not added to the jimage file.
          *
          * @param resourceName
-         * @throws jdk.tools.jlink.api.plugin.PluginException If the resource to
+         * @throws jdk.tools.jlink.plugin.PluginException If the resource to
          * forget doesn't exist or is null.
          */
         public void forgetResourceFile(String resourceName);
@@ -204,7 +204,7 @@
          * @param resources The resources will be added to the jimage following
          * the order of this ResourcePool.
          * @return The resource paths ordered in the way to use for storage in the jimage.
-         * @throws jdk.tools.jlink.api.plugin.PluginException
+         * @throws jdk.tools.jlink.plugin.PluginException
          */
         public List<String> sort(Pool resources);
     }
@@ -273,7 +273,7 @@
      *
      * @param binaryName Class binary name
      * @return A reader or null if the class is unknown
-     * @throws jdk.tools.jlink.api.plugin.PluginException
+     * @throws jdk.tools.jlink.plugin.PluginException
      */
     public ClassReader getClassReader(String binaryName);
 
@@ -282,7 +282,7 @@
      *
      * @param res A resource.
      * @return A reader or null if the class is unknown
-     * @throws jdk.tools.jlink.api.plugin.PluginException
+     * @throws jdk.tools.jlink.plugin.PluginException
      */
     public ClassReader getClassReader(Pool.ModuleData res);
 
@@ -290,7 +290,7 @@
      * To visit the set of ClassReaders.
      *
      * @param visitor The visitor.
-     * @throws jdk.tools.jlink.api.plugin.PluginException
+     * @throws jdk.tools.jlink.plugin.PluginException
      */
     public void visitClassReaders(ClassReaderVisitor visitor);
 
@@ -298,7 +298,7 @@
      * To visit the set of ClassReaders.
      *
      * @param visitor The visitor.
-     * @throws jdk.tools.jlink.api.plugin.PluginException
+     * @throws jdk.tools.jlink.plugin.PluginException
      */
     public void visitResourceFiles(ResourceFileVisitor visitor);
 
@@ -308,7 +308,7 @@
      * If a sorter has been set, it is used to sort the returned resources.
      *
      * @param output The pool used to fill the jimage.
-     * @throws jdk.tools.jlink.api.plugin.PluginException
+     * @throws jdk.tools.jlink.plugin.PluginException
      */
     public void fillOutputResources(Pool output);
 
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPoolImpl.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPoolImpl.java	Mon Dec 21 14:42:19 2015 +0100
@@ -47,9 +47,9 @@
 import jdk.internal.org.objectweb.asm.ClassWriter;
 import jdk.tools.jlink.internal.ImageFileCreator;
 import jdk.tools.jlink.internal.PoolImpl;
-import jdk.tools.jlink.api.plugin.PluginException;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleData;
+import jdk.tools.jlink.plugin.PluginException;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.Pool.ModuleData;
 
 /**
  * A pool of ClassReader and other resource files. This class allows to
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPools.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPools.java	Mon Dec 21 14:42:19 2015 +0100
@@ -43,9 +43,9 @@
 import jdk.internal.org.objectweb.asm.ClassWriter;
 import jdk.tools.jlink.internal.PoolImpl;
 import jdk.tools.jlink.internal.plugins.asm.AsmPool.Sorter;
-import jdk.tools.jlink.api.plugin.PluginException;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleData;
+import jdk.tools.jlink.plugin.PluginException;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.Pool.ModuleData;
 
 /**
  * A container for pools of ClassReader and other resource files. A pool of all
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/ExecutableImage.java	Mon Dec 21 14:42:19 2015 +0100
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015, 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 jdk.tools.jlink.plugin;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * An executable Image.
+ */
+public abstract class ExecutableImage {
+
+    private final Path home;
+    private final List<String> args;
+    private final Set<String> modules;
+
+    protected ExecutableImage(Path home, Set<String> modules,
+            List<String> args) {
+        Objects.requireNonNull(home);
+        Objects.requireNonNull(args);
+        if (!Files.exists(home)) {
+            throw new IllegalArgumentException("Invalid image home");
+        }
+        this.home = home;
+        this.modules = Collections.unmodifiableSet(modules);
+        this.args = Collections.unmodifiableList(args);
+    }
+
+    public Path getHome() {
+        return home;
+    }
+
+    /**
+     * The names of the modules located in the image.
+     *
+     * @return The set of modules.
+     */
+    public Set<String> getModules() {
+        return modules;
+    }
+
+    public List<String> getExecutionArgs() {
+        return args;
+    }
+
+    public abstract void storeLaunchArgs(List<String> args);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/Plugin.java	Mon Dec 21 14:42:19 2015 +0100
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2015, 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 jdk.tools.jlink.plugin;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
+
+/**
+ * Implement this interface to develop your own plugin.
+ */
+public interface Plugin {
+
+    public interface PluginType {
+
+        public String getName();
+    }
+
+    public enum ORDER implements PluginType {
+        FIRST("FIRST"),
+        LAST("LAST"),
+        ANY("ANY");
+
+        private final String name;
+
+        ORDER(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+    }
+
+    /**
+     * Order of categories:
+     * <ol>
+     * <li>FILTER: Filter in/out resources or files.</li>
+     * <li>TRANSFORMER: Transform resources or files(eg: refactoring, bytecode
+     * manipulation).</li>
+     * <li>MODULEINFO_TRANSFORMER: Transform only module-info.class</li>
+     * <li>SORTER: Sort resources within the resource container.</li>
+     * <li>COMPRESSOR: Compress resource within the resouce containers.</li>
+     * <li>VERIFIER: Does some image verification.</li>
+     * <li>PROCESSOR: Does some post processing on image.</li>
+     * <li>PACKAGER: Final processing</li>
+     * </ol>
+     */
+    public enum CATEGORY implements PluginType {
+        FILTER("FILTER"),
+        TRANSFORMER("TRANSFORMER"),
+        MODULEINFO_TRANSFORMER("MODULEINFO_TRANSFORMER"),
+        SORTER("SORTER"),
+        COMPRESSOR("COMPRESSOR"),
+        VERIFIER("VERIFIER"),
+        PROCESSOR("PROCESSOR"),
+        PACKAGER("PACKAGER");
+
+        private final String name;
+
+        CATEGORY(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+    }
+
+    public enum STATE {
+        ENABLED,
+        FUNCTIONAL
+    }
+
+    public abstract Set<PluginType> getType();
+
+    public default Set<STATE> getState() {
+        return EnumSet.of(STATE.ENABLED, STATE.FUNCTIONAL);
+    }
+
+    public String getName();
+
+    public String getDescription();
+
+    public default List<PluginOption> getAdditionalOptions() {
+        return Collections.emptyList();
+    }
+
+    public PluginOption getOption();
+
+    /**
+     * Return a message indicating the status of the provider.
+     *
+     * @return A status description.
+     */
+    public default String getStateDescription() {
+        return getState().contains(STATE.FUNCTIONAL)
+                ? PluginsResourceBundle.getMessage("main.status.ok")
+                : PluginsResourceBundle.getMessage("main.status.not.ok");
+    }
+
+    /**
+     * Configure the plugin based on the passed configuration.
+     *
+     * @param config The plugin configuration.
+     */
+    public void configure(Map<PluginOption, String> config);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/PluginException.java	Mon Dec 21 14:42:19 2015 +0100
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2015, 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 jdk.tools.jlink.plugin;
+
+/**
+ * An unchecked exception thrown by jlink plugin API for unrecoverable
+ * conditions.
+ */
+public final class PluginException extends RuntimeException {
+
+    private static final long serialVersionUID = 7117982019443100395L;
+
+    public PluginException() {
+
+    }
+
+    public PluginException(Throwable ex) {
+        super(ex);
+    }
+
+    public PluginException(String msg) {
+        super(msg);
+    }
+
+    public PluginException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/PluginOption.java	Mon Dec 21 14:42:19 2015 +0100
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2015, 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 jdk.tools.jlink.plugin;
+
+import java.util.Objects;
+import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
+
+public final class PluginOption {
+
+    private final String name;
+    private final String description;
+    private final String argumentDescription;
+    private final boolean hasOnOffArgument;
+    private final boolean isEnabled;
+
+    PluginOption(String name,
+            String description,
+            String argumentDescription,
+            boolean hasOnOffArgument,
+            boolean isEnabled) {
+        Objects.requireNonNull(name);
+        this.name = name;
+        this.description = description;
+        this.argumentDescription = argumentDescription;
+        this.hasOnOffArgument = hasOnOffArgument;
+        this.isEnabled = isEnabled;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public String getArgumentDescription() {
+        return argumentDescription;
+    }
+
+    public boolean hasOnOffArgument() {
+        return hasOnOffArgument;
+    }
+
+    public boolean isEnabled() {
+        return isEnabled;
+    }
+
+    @Override
+    public String toString() {
+        return name + ":" + description;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 5;
+        hash = 37 * hash + Objects.hashCode(this.name);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof PluginOption)) {
+            return false;
+        }
+        PluginOption po = (PluginOption) other;
+        return name.equals(po.name);
+    }
+
+    public static final class Builder {
+
+        public static final String ON_ARGUMENT = "on";
+        public static final String OFF_ARGUMENT = "off";
+
+        private final String name;
+        private String description;
+        private String argumentDescription;
+        private boolean hasOnOffArgument;
+        private boolean isEnabled;
+
+        public Builder(String name) {
+            Objects.requireNonNull(name);
+            this.name = name;
+        }
+
+        public Builder description(String description) {
+            this.description = description;
+            return this;
+        }
+
+        public Builder argumentDescription(String argumentDescription) {
+            this.argumentDescription = argumentDescription;
+            return this;
+        }
+
+        public Builder hasOnOffArgument() {
+            this.hasOnOffArgument = true;
+            this.argumentDescription = PluginsResourceBundle.getMessage("onoff.argument");
+            return this;
+        }
+
+        public Builder isEnabled() {
+            hasOnOffArgument();
+            isEnabled = true;
+            return this;
+        }
+
+        public PluginOption build() {
+            return new PluginOption(name, description, argumentDescription,
+                    hasOnOffArgument, isEnabled);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/Pool.java	Mon Dec 21 14:42:19 2015 +0100
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2015, 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 jdk.tools.jlink.plugin;
+
+import jdk.tools.jlink.plugin.PluginException;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.lang.module.ModuleDescriptor;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import jdk.tools.jlink.internal.ImageFileCreator;
+
+/**
+ * Pool of module data.
+ *
+ */
+public abstract class Pool {
+
+    public interface Visitor {
+
+        /**
+         * Called for each visited file.
+         *
+         * @param content The file to deal with.
+         * @return A resource or null if the passed resource is to be removed
+         * from the image.
+         * @throws PluginException
+         */
+        public ModuleData visit(ModuleData content);
+    }
+
+    public static enum ModuleDataType {
+
+        CLASS_OR_RESOURCE,
+        CONFIG,
+        NATIVE_CMD,
+        NATIVE_LIB,
+        OTHER;
+    }
+
+    public interface Module {
+         public String getName();
+
+        public ModuleData get(String path);
+
+        public ModuleDescriptor getDescriptor();
+
+        public void add(ModuleData data);
+
+        public Set<String> getAllPackages();
+
+        public Collection<ModuleData> getContent();
+
+    }
+
+    private class ModuleImpl implements Module {
+        private final Map<String, ModuleData> moduleContent = new LinkedHashMap<>();
+        private ModuleDescriptor descriptor;
+        private final String name;
+        private ModuleImpl(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public ModuleData get(String path) {
+            if (!path.startsWith("/")) {
+                path = "/" + path;
+            }
+            if (!path.startsWith("/" + name)) {
+                path = "/" + name + path;
+            }
+            return moduleContent.get(path);
+        }
+
+        @Override
+        public ModuleDescriptor getDescriptor() {
+            if(descriptor == null) {
+                String p = "/" + name + "/module-info.class";
+                ModuleData content = moduleContent.get(p);
+                if(content == null) {
+                    throw new PluginException("No module-info for " + name +
+                            " module");
+                }
+                ByteBuffer bb = ByteBuffer.wrap(content.getBytes());
+                descriptor = ModuleDescriptor.read(bb);
+            }
+            return descriptor;
+        }
+
+        @Override
+        public void add(ModuleData data) {
+            if (isReadOnly()) {
+                throw new PluginException("pool is readonly");
+            }
+            Objects.requireNonNull(data);
+            if (!data.getModule().equals(name)) {
+                throw new PluginException("Can't add resource " + data.getPath()
+                        + " to module " + name);
+            }
+            Pool.this.add(data);
+        }
+
+        @Override
+        public Set<String> getAllPackages() {
+            Set<String> pkgs = new HashSet<>();
+            moduleContent.values().stream().filter(m -> m.getType().
+                    equals(ModuleDataType.CLASS_OR_RESOURCE)).forEach((res) -> {
+                // Module metadata only contains packages with .class files
+                if (ImageFileCreator.isClassPackage(res.getPath())) {
+                    String[] split = ImageFileCreator.splitPath(res.getPath());
+                    String pkg = split[1];
+                    if (pkg != null && !pkg.isEmpty()) {
+                        pkgs.add(pkg);
+                    }
+                }
+            });
+            return pkgs;
+        }
+
+        @Override
+        public String toString() {
+            return getName();
+        }
+
+        @Override
+        public Collection<ModuleData> getContent() {
+            return Collections.unmodifiableCollection(moduleContent.values());
+        }
+    }
+
+    public static class ModuleData {
+
+        private final ModuleDataType type;
+        private final String path;
+        private final String module;
+        private final long length;
+        private final InputStream stream;
+
+        private byte[] buffer;
+        public ModuleData(String module, String path, ModuleDataType type,
+                InputStream stream, long length) {
+            Objects.requireNonNull(module);
+            Objects.requireNonNull(path);
+            Objects.requireNonNull(type);
+            Objects.requireNonNull(stream);
+            this.path = path;
+            this.type = type;
+            this.module = module;
+            this.stream = stream;
+            this.length = length;
+        }
+
+        public final String getModule() {
+            return module;
+        }
+
+        public final String getPath() {
+            return path;
+        }
+
+        public final ModuleDataType getType() {
+            return type;
+        }
+
+        public byte[] getBytes() {
+            if (buffer == null) {
+                try {
+                    buffer = stream.readAllBytes();
+                } catch (IOException ex) {
+                    throw new UncheckedIOException(ex);
+                }
+            }
+            return buffer;
+        }
+
+        public long getLength() {
+            return length;
+        }
+
+        public InputStream stream() {
+            return stream;
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = 7;
+            hash = 89 * hash + Objects.hashCode(this.path);
+            return hash;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (!(other instanceof ModuleData)) {
+                return false;
+            }
+            ModuleData f = (ModuleData) other;
+            return f.path.equals(path);
+        }
+
+        @Override
+        public String toString() {
+            return getPath();
+        }
+    }
+
+    private final Map<String, ModuleData> resources = new LinkedHashMap<>();
+    private final Map<String, ModuleImpl> modules = new LinkedHashMap<>();
+
+    private final ByteOrder order;
+
+    protected Pool() {
+        this(ByteOrder.nativeOrder());
+    }
+
+    protected Pool(ByteOrder order) {
+        Objects.requireNonNull(order);
+        this.order = order;
+    }
+
+    /**
+     * Read only state.
+     *
+     * @return true if readonly false otherwise.
+     */
+    public abstract boolean isReadOnly();
+
+    /**
+     * Add a resource.
+     *
+     * @param resource The Resource to add.
+     */
+    public void add(ModuleData resource) {
+        if (isReadOnly()) {
+            throw new PluginException("pool is readonly");
+        }
+        Objects.requireNonNull(resource);
+        if (resources.get(resource.getPath()) != null) {
+            throw new PluginException("Resource " + resource.getPath()
+                    + " already present");
+        }
+        ModuleImpl m = modules.get(resource.getModule());
+        if(m == null) {
+            m = new ModuleImpl(resource.getModule());
+            modules.put(resource.getModule(), m);
+        }
+        resources.put(resource.getPath(), resource);
+        m.moduleContent.put(resource.getPath(), resource);
+    }
+
+    /**
+     * Retrieves the module of the provided name.
+     * @param name The module name
+     * @return the module or null if the module doesn't exist.
+     */
+    public Module getModule(String name) {
+        Objects.requireNonNull(name);
+        return modules.get(name);
+    }
+
+    /**
+     * The collection of modules contained in this pool.
+     * @return The collection of modules.
+     */
+    public Collection<Module> getModules() {
+        return Collections.unmodifiableCollection(modules.values());
+    }
+
+    /**
+     * Get all resources contained in this pool instance.
+     *
+     * @return The collection of resources;
+     */
+    public Collection<ModuleData> getContent() {
+        return Collections.unmodifiableCollection(resources.values());
+    }
+
+    /**
+     * Get the resource for the passed path.
+     *
+     * @param path A resource path
+     * @return A Resource instance or null if the resource is not found
+     */
+    public ModuleData get(String path) {
+        Objects.requireNonNull(path);
+        return resources.get(path);
+    }
+
+    public boolean contains(ModuleData res) {
+        Objects.requireNonNull(res);
+        return get(res.getPath()) != null;
+    }
+
+    public boolean isEmpty() {
+        return resources.isEmpty();
+    }
+
+    public void visit(Visitor visitor, Pool output) {
+        for (ModuleData resource : getContent()) {
+            ModuleData res = visitor.visit(resource);
+            if (res != null) {
+                output.add(res);
+            }
+        }
+    }
+
+    public ByteOrder getByteOrder() {
+        return order;
+    }
+
+    public void addTransformedResource(ModuleData original, InputStream transformed, long length) {
+        if (isReadOnly()) {
+            throw new PluginException("Pool is readonly");
+        }
+        Objects.requireNonNull(original);
+        Objects.requireNonNull(transformed);
+        if (get(original.getPath()) != null) {
+            throw new PluginException("Resource already present");
+        }
+        ModuleData res = new ModuleData(original.getModule(), original.getPath(),
+                original.getType(), transformed, length);
+        add(res);
+    }
+
+    public static ModuleData newResource(String path, InputStream content, long size) {
+        Objects.requireNonNull(path);
+        Objects.requireNonNull(content);
+        String[] split = ImageFileCreator.splitPath(path);
+        String module = split[0];
+        return new ModuleData(module, path, ModuleDataType.CLASS_OR_RESOURCE, content, size);
+    }
+
+    public static ModuleData newResource(String path, byte[] content) {
+        return newResource(path, new ByteArrayInputStream(content),
+                content.length);
+    }
+
+    public static ModuleData newImageFile(String module, String path, ModuleDataType type,
+            InputStream content, long size) {
+        Objects.requireNonNull(path);
+        Objects.requireNonNull(content);
+        return new ModuleData(module, path, type, content, size);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/PostProcessorPlugin.java	Mon Dec 21 14:42:19 2015 +0100
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015, 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 jdk.tools.jlink.plugin;
+
+import java.util.List;
+
+/**
+ * Implement this interface to develop your own plugin.
+ * PostProcessing can
+ * modify the image content.
+ */
+public interface PostProcessorPlugin extends Plugin {
+
+    /**
+     * Post process an image.
+     *
+     * @param image The executable image.
+     * @return The list of arguments to add to launchers if any.
+     */
+    public List<String> process(ExecutableImage image);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/TransformerPlugin.java	Mon Dec 21 14:42:19 2015 +0100
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015, 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 jdk.tools.jlink.plugin;
+
+import jdk.tools.jlink.plugin.Plugin;
+
+
+/**
+ * Implement this interface to develop your own plugin.
+ * TransformerPlugin
+ * instances modify the content of a jimage.
+ */
+public interface TransformerPlugin extends Plugin {
+    /**
+     * Visit the content of modules.
+     *
+     * @param in Read only content.
+     * @param out The pool to fill with content. Will contain the result of the
+     * visit
+     *
+     * @throws PluginException
+     */
+    public void visit(Pool in, Pool out);
+}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties	Mon Dec 21 14:42:19 2015 +0100
@@ -12,6 +12,9 @@
 main.opt.help=\
 \  --help                               Print this usage message
 
+main.opt.xhelp=\
+\  --xhelp                              Print advanced options
+
 main.opt.version=\
 \  --version                            Version information
 
@@ -27,9 +30,6 @@
 main.opt.output=\
 \  --output <path>                      Location of output path
 
-main.opt.configuration=\
-\  --configuration <path>               Path to properties file with defaults options
-
 main.command.files=\
 \  @<filename>                          Read options from file
 
@@ -37,8 +37,9 @@
 \  --endian <little|big>                Byte order of the generated jimage. \
 By default native order is used.
 
-main.opt.post-process-path=\
-\  --post-process-path <path to image>  Apply post processing to an existing image.
+main.opt.genbom=\
+\  --genbom                             To generate a bom file containing information \
+on jlink.
 
 main.msg.bug=\
 An exception has occurred in jlink. \
@@ -46,6 +47,10 @@
 after checking the database for duplicates. \
 Include your program and the following diagnostic in your report.  Thank you.
 
+main.extended.help=\
+jlink is extensible by the means of plugins. Following plugins have been discovered \
+thanks to ServiceLoader.
+
 err.unknown.byte.order:unknown byte order {0}
 err.output.must.be.specified:--output must be specified
 err.modulepath.must.be.specified:--modulepath must be specified
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties	Mon Dec 21 14:42:19 2015 +0100
@@ -27,12 +27,6 @@
 copy-files.description=Copy files. If files to copy are not absolute path, use JDK home dir.\n\
 eg: jrt-fs.jar,LICENSE,/home/me/myfile.txt=somewehere/conf.txt
 
-default-image-builder.description=\
-Default image layout.
-
-default-image-builder.genbom=\
-To generate a file containing information on jlink.
-
 exclude-files.argument=<list of files to escape | path to a file containing the list of files to escape>
 
 exclude-files.description=Exclude files from the image. eg: --{0} *.diz, /java.base/native/client/*
@@ -54,10 +48,14 @@
 
 sort-resources.description=Order resources should appear in jimage file. eg: --{0} */modules-info.class, /java-base/java/lang/*
 
-strip-java-debug.description=Strip debug information from class files.
+strip-debug.description=Strip debug information from runtime image.
 
 strip-native-commands.description=Exclude native commands (such as java/java.exe) from the image.
 
+vm.argument=<client|server|minimal|all>
+
+vm.description=Creates a runtime image that has a particular Java Virtual Machine (JVM) or all JVMs
+ 
 zip.argument=[comma separated list of resource paths]
 
 zip.description=ZIP Compression
@@ -66,18 +64,29 @@
 
 main.status.not.ok= Not functional.
 
-main.opt.list-plugins=\
-\  --list-plugins                       Available plugins information
+plugin.plugins.header=\
+List of available plugin options:
 
-main.opt.resources-last-sorter=\
-\  --resources-last-sorter              Name of the last plugin allowed to sort resources within a jimage.
+plugin.opt.post-process-path=\
+\  --post-process-path <path to image>  Apply post processing to an existing image.
 
-main.plugins-modulepath=\
-\  --plugins-modulepath                 Custom plugins module path
+plugin.opt.resources-last-sorter=\
+\  --resources-last-sorter              Name of the last plugin allowed to sort \
+resources within a jimage.
+
+plugin.opt.plugins-modulepath=\
+\  --plugins-modulepath                 Custom plugins module path. Warning: \
+Enabling third party plugins can lead to unusable generated image.
 
 main.plugin.name=\
 \Plugin Name
 
+main.plugin.class=\
+\Plugin Class
+
+main.plugin.module=\
+\Plugin Module
+
 main.plugin.category=\
 \Category
 
@@ -100,7 +109,7 @@
 to
 
 main.plugin.option=\
-\Command Line Option
+\Option
 
 main.plugin.no.value=\
 \<empty>
--- a/src/jdk.jlink/share/classes/module-info.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/src/jdk.jlink/share/classes/module-info.java	Mon Dec 21 14:42:19 2015 +0100
@@ -24,28 +24,25 @@
  */
 
 module jdk.jlink {
-    exports jdk.tools.jlink.api;
-    exports jdk.tools.jlink.api.plugin;
-    exports jdk.tools.jlink.api.plugin.builder;
-    exports jdk.tools.jlink.api.plugin.postprocessor;
-    exports jdk.tools.jlink.api.plugin.transformer;
+    exports jdk.tools.jlink;
+    exports jdk.tools.jlink.plugin;
+    exports jdk.tools.jlink.builder;
 
     requires jdk.compiler;
     requires jdk.internal.opt;
     requires jdk.jdeps;
 
-    uses jdk.tools.jlink.api.plugin.Plugin;
+    uses jdk.tools.jlink.plugin.TransformerPlugin;
+    uses jdk.tools.jlink.plugin.PostProcessorPlugin;
 
-    provides jdk.tools.jlink.api.plugin.Plugin with jdk.tools.jlink.internal.plugins.FileCopierPlugin;
-    provides jdk.tools.jlink.api.plugin.Plugin with jdk.tools.jlink.internal.plugins.FileReplacerPlugin;
-    provides jdk.tools.jlink.api.plugin.Plugin with jdk.tools.jlink.internal.plugins.StringSharingPlugin;
-    provides jdk.tools.jlink.api.plugin.Plugin with jdk.tools.jlink.internal.plugins.StripDebugPlugin;
-    provides jdk.tools.jlink.api.plugin.Plugin with jdk.tools.jlink.internal.plugins.ExcludePlugin;
-    provides jdk.tools.jlink.api.plugin.Plugin with jdk.tools.jlink.internal.plugins.ExcludeFilesPlugin;
-    provides jdk.tools.jlink.api.plugin.Plugin with jdk.tools.jlink.internal.plugins.InstalledModuleDescriptorPlugin;
-    provides jdk.tools.jlink.api.plugin.Plugin with jdk.tools.jlink.internal.plugins.StripNativeCommandsPlugin;
-    provides jdk.tools.jlink.api.plugin.Plugin with jdk.tools.jlink.internal.plugins.SortResourcesPlugin;
-    provides jdk.tools.jlink.api.plugin.Plugin with jdk.tools.jlink.internal.plugins.ZipPlugin;
-    provides jdk.tools.jlink.api.plugin.Plugin with jdk.tools.jlink.internal.plugins.DefaultCompressPlugin;
-    provides jdk.tools.jlink.api.plugin.Plugin with jdk.tools.jlink.internal.plugins.OptimizationPlugin;
+    provides jdk.tools.jlink.plugin.TransformerPlugin with jdk.tools.jlink.internal.plugins.FileCopierPlugin;
+    provides jdk.tools.jlink.plugin.TransformerPlugin with jdk.tools.jlink.internal.plugins.StripDebugPlugin;
+    provides jdk.tools.jlink.plugin.TransformerPlugin with jdk.tools.jlink.internal.plugins.ExcludePlugin;
+    provides jdk.tools.jlink.plugin.TransformerPlugin with jdk.tools.jlink.internal.plugins.ExcludeFilesPlugin;
+    provides jdk.tools.jlink.plugin.TransformerPlugin with jdk.tools.jlink.internal.plugins.InstalledModuleDescriptorPlugin;
+    provides jdk.tools.jlink.plugin.TransformerPlugin with jdk.tools.jlink.internal.plugins.StripNativeCommandsPlugin;
+    provides jdk.tools.jlink.plugin.TransformerPlugin with jdk.tools.jlink.internal.plugins.SortResourcesPlugin;
+    provides jdk.tools.jlink.plugin.TransformerPlugin with jdk.tools.jlink.internal.plugins.DefaultCompressPlugin;
+    provides jdk.tools.jlink.plugin.TransformerPlugin with jdk.tools.jlink.internal.plugins.OptimizationPlugin;
+    provides jdk.tools.jlink.plugin.TransformerPlugin with jdk.tools.jlink.internal.plugins.ExcludeVMPlugin;
 }
--- a/test/jdk/jigsaw/tools/jimage/JImageTest.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/test/jdk/jigsaw/tools/jimage/JImageTest.java	Mon Dec 21 14:42:19 2015 +0100
@@ -49,7 +49,6 @@
  * @modules java.base/jdk.internal.jimage
  *          jdk.jdeps/com.sun.tools.classfile
  *          jdk.jlink/jdk.tools.jmod
- *          jdk.jlink/jdk.tools.jlink
  *          jdk.jlink/jdk.tools.jimage
  *          jdk.jlink/jdk.tools.jlink.internal
  *          jdk.compiler
@@ -129,15 +128,13 @@
         Path recreatedImage2 = JImageGenerator.getJImageTask()
                 .dir(extractedDir)
                 .option("--compress-resources")
-                .option("on")
                 .image(helper.createNewRecreatedDir(extractedDir.getFileName().toString()))
                 .recreate().assertSuccess();
         JImageValidator.validate(recreatedImage2.toFile(), bootClasses, Collections.emptyList());
 
         Path recreatedImage3 = JImageGenerator.getJImageTask()
                 .dir(extractedDir)
-                .option("--strip-java-debug")
-                .option("on")
+                .option("--strip-debug")
                 .image(helper.createNewRecreatedDir(extractedDir.getFileName().toString()))
                 .recreate().assertSuccess();
         JImageValidator.validate(recreatedImage3.toFile(), bootClasses, Collections.emptyList());
@@ -156,9 +153,7 @@
         Path recreatedImage5 = JImageGenerator.getJImageTask()
                 .dir(extractedDir)
                 .option("--compress-resources")
-                .option("on")
-                .option("--strip-java-debug")
-                .option("on")
+                .option("--strip-debug")
                 .option("--exclude-resources")
                 .option("*.jcov, */META-INF/*")
                 .image(helper.createNewRecreatedDir(extractedDir.getFileName().toString()))
--- a/test/jdk/jigsaw/tools/jlink/CustomImageBuilderTest.java	Mon Dec 21 14:42:17 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,310 +0,0 @@
-/*
- * Copyright (c) 2015, 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.
- *
- * 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.
- */
-
-/*
- * @test
- * @summary Test custom image builder
- * @author Andrei Eremeev
- * @library ../lib
- * @modules java.base/jdk.internal.jimage
- *          java.base/jdk.internal.module
- *          jdk.jdeps/com.sun.tools.classfile
- *          jdk.jlink/jdk.tools.jlink
- *          jdk.jlink/jdk.tools.jlink.internal
- *          jdk.jlink/jdk.tools.jmod
- *          jdk.jlink/jdk.tools.jimage
- *          jdk.compiler
- * @build tests.*
- * @run testng/othervm CustomImageBuilderTest
- */
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.UncheckedIOException;
-import java.lang.module.ModuleDescriptor;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-
-import java.util.Set;
-
-import jdk.internal.module.ModuleInfoWriter;
-import org.testng.Assert;
-import org.testng.SkipException;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-import tests.Helper;
-import tests.JImageGenerator;
-import tests.JImageGenerator.JLinkTask;
-import tests.Result;
-
-@Test
-public class CustomImageBuilderTest {
-
-    private static Helper helper;
-    private static Path pluginModulePath;
-    private static Path customPluginJmod;
-    private static Path classes;
-    private static final List<String> options =
-            Arrays.asList("custom-image-option-1", "custom-image-option-2");
-
-    @BeforeClass
-    public static void registerServices() throws IOException {
-        helper = Helper.newHelper();
-        if (helper == null) {
-            throw new SkipException("Not run");
-        }
-        helper.generateDefaultModules();
-        String moduleName = "customplugin";
-        Path src = Paths.get(System.getProperty("test.src")).resolve(moduleName);
-        classes = helper.getJmodClassesDir().resolve(moduleName);
-        JImageGenerator.compile(src, classes, "-XaddExports:jdk.jlink/jdk.tools.jlink.internal=customplugin");
-        customPluginJmod = JImageGenerator.getJModTask()
-                .addClassPath(classes)
-                .jmod(helper.getJmodDir().resolve(moduleName + ".jmod"))
-                .create().assertSuccess();
-        pluginModulePath = customPluginJmod.getParent();
-    }
-
-    private JLinkTask getJLinkTask() {
-        return JImageGenerator.getJLinkTask()
-                .option("--image-builder")
-                .option("custom-image-builder");
-    }
-
-    public void testHelp() {
-        Result result = JImageGenerator.getJLinkTask()
-                .option("--help")
-                .pluginModulePath(pluginModulePath)
-                .call();
-        result.assertSuccess();
-        String message = result.getMessage();
-        if (!message.contains("Image Builder Name: custom-image-builder\n"
-                + "Image Builder Description: custom-image-builder-description\n"
-                + "Functional state: Functional.\n"
-                + " --custom-image-option-1 custom-image-option-description")) {
-            System.err.println(result.getMessage());
-            throw new AssertionError("Custom image builder not found");
-        }
-    }
-
-    public void testCustomImageBuilder() throws IOException {
-        Path image = getJLinkTask()
-                .modulePath(helper.defaultModulePath())
-                .output(helper.createNewImageDir("testCustomImageBuilder"))
-                .addMods("leaf1")
-                .pluginModulePath(pluginModulePath)
-                .call().assertSuccess();
-        checkImageBuilder(image);
-    }
-
-    public void testFirstOption() throws IOException {
-        Path image = getJLinkTask()
-                .modulePath(helper.defaultModulePath())
-                .output(helper.createNewImageDir("testFirstOption"))
-                .addMods("leaf1")
-                .pluginModulePath(pluginModulePath)
-                .option("--" + options.get(0))
-                .option("AAAA")
-                .call().assertSuccess();
-        checkImageBuilder(image, Collections.singletonList(option(options.get(0), "AAAA")));
-    }
-
-    public void testFirstOptionNoArgs() throws IOException {
-        getJLinkTask()
-                .modulePath(helper.defaultModulePath())
-                .output(helper.createNewImageDir("testFirstOptionNoArgs"))
-                .addMods("leaf1")
-                .pluginModulePath(pluginModulePath)
-                .option("--" + options.get(0))
-                .call().assertFailure("Error: no value given for --custom-image-option-1");
-    }
-
-    public void testSecondOption() throws IOException {
-        Path image = getJLinkTask()
-                .modulePath(helper.defaultModulePath())
-                .output(helper.createNewImageDir("testSecondOption"))
-                .addMods("leaf1")
-                .pluginModulePath(pluginModulePath)
-                .option("--" + options.get(1))
-                .call().assertSuccess();
-        checkImageBuilder(image, Collections.singletonList(option(options.get(1))));
-    }
-
-    public void testTwoImageBuildersInModule() throws IOException {
-        Result result = JImageGenerator.getJLinkTask()
-                .modulePath(helper.defaultModulePath())
-                .pluginModulePath(pluginModulePath)
-                .option("--list-plugins")
-                .call();
-        result.assertSuccess();
-        String message = result.getMessage();
-        if (!message.contains("Image Builder Name: custom-image-builder")) {
-            System.err.println(message);
-            throw new AssertionError("List-plugins does not contain custom-image-builder");
-        }
-        if (!message.contains("Image Builder Name: second-image-builder")) {
-            System.err.println(message);
-            throw new AssertionError("List-plugins does not contain second-image-builder");
-        }
-    }
-
-    @Test(enabled = false, priority = Integer.MAX_VALUE - 1)
-    public void testTwoPackages() throws IOException {
-        String moduleName = "customplugin_1";
-        Path src = helper.getJmodSrcDir().resolve(moduleName);
-        copyModule(src);
-        Path jmod = buildModule(moduleName);
-
-        try {
-            JImageGenerator.getJLinkTask()
-                    .pluginModulePath(pluginModulePath)
-                    .option("--list-plugins")
-                    .call().assertFailure("Modules customplugin_1 and customplugin both contain package plugin");
-        } finally {
-            Files.delete(jmod);
-        }
-    }
-
-    private Path buildModule(String moduleName) throws IOException {
-        Path src = helper.getJmodSrcDir().resolve(moduleName);
-        Path classes = helper.getJmodClassesDir().resolve(moduleName);
-        JImageGenerator.compile(src, classes, "-XaddExports:jdk.jlink/jdk.tools.jlink.internal=customplugin");
-
-        try (OutputStream out = Files.newOutputStream(classes.resolve("module-info.class"))) {
-            ModuleDescriptor md = new ModuleDescriptor.Builder(moduleName)
-                    .requires("java.base")
-                    .provides("jdk.tools.jlink.plugins.ImageBuilderProvider",
-                            "plugin.CustomImageBuilderProvider").build();
-            ModuleInfoWriter.write(md, out);
-        }
-        return JImageGenerator.getJModTask()
-                .addClassPath(classes)
-                .jmod(helper.getJmodDir().resolve(moduleName + ".jmod"))
-                .create().assertSuccess();
-    }
-
-    private void copyModule(Path src) throws IOException {
-        Path customplugin = Paths.get(System.getProperty("test.src")).resolve("customplugin");
-        Files.walk(customplugin).forEach(p -> {
-            try {
-                Path path = customplugin.relativize(p);
-                Files.copy(p, src.resolve(path));
-            } catch (IOException e) {
-                throw new UncheckedIOException(e);
-            }
-        });
-    }
-
-    private void testMalformedModule(String moduleName, ModuleDescriptor md, String expectedError) throws IOException {
-        try (OutputStream out = Files.newOutputStream(classes.resolve("module-info.class"))) {
-            ModuleInfoWriter.write(md, out);
-        }
-        Path jmod = JImageGenerator.getJModTask()
-                .addClassPath(classes)
-                .jmod(helper.getJmodDir().resolve(moduleName + ".jmod"))
-                .create().assertSuccess();
-        Files.move(jmod, customPluginJmod, StandardCopyOption.REPLACE_EXISTING);
-        getJLinkTask()
-                .modulePath(helper.defaultModulePath())
-                .output(helper.createNewImageDir(moduleName))
-                .addMods(moduleName)
-                .pluginModulePath(pluginModulePath)
-                .call().assertFailure(expectedError);
-    }
-
-    @Test(enabled = false, priority = Integer.MAX_VALUE) // run last
-    public void testIllegalAccessError() throws IOException {
-        String moduleName = "testIllegalAccessError";
-        ModuleDescriptor md = new ModuleDescriptor.Builder(moduleName)
-                .requires("java.base")
-                .provides("jdk.tools.jlink.plugins.ImageBuilderProvider",
-                        new HashSet<>(Arrays.asList(
-                                "plugin.SameNamedImageBuilderProvider",
-                                "plugin.CustomImageBuilderProvider"))).build();
-        testMalformedModule(moduleName, md, "Error: Multiple ImageBuilderProvider for the name custom-image-builder");
-    }
-
-    @Test(enabled = false, priority = Integer.MAX_VALUE) // run last
-    public void testTwoImageBuilderWithTheSameName() throws IOException {
-        String moduleName = "testTwoImageBuilderWithTheSameName";
-        ModuleDescriptor md = new ModuleDescriptor.Builder(moduleName)
-                .requires("java.base")
-                .requires("jdk.jlink")
-                .provides("jdk.tools.jlink.plugins.ImageBuilderProvider",
-                        new HashSet<>(Arrays.asList(
-                                "plugin.SameNamedImageBuilderProvider",
-                                "plugin.CustomImageBuilderProvider"))).build();
-        testMalformedModule(moduleName, md,
-                "cannot access class jdk.tools.jlink.plugins.ImageBuilderProvider (in module: jdk.jlink)");
-    }
-
-    private void checkImageBuilder(Path image) throws IOException {
-        checkImageBuilder(image, Collections.emptyList());
-    }
-
-    private void checkImageBuilder(Path image, List<Option> includes) throws IOException {
-        if (!Files.exists(image.resolve("image.jimage"))) {
-            throw new AssertionError("getJImageOutputStream was not called");
-        }
-        if (!Files.exists(image.resolve("files.txt"))) {
-            throw new AssertionError("storeFiles was not called");
-        }
-        Set<String> otherOptions = new HashSet<>(options);
-        for (Option o : includes) {
-            otherOptions.remove(o.option);
-
-            Path file = image.resolve(o.option);
-            Assert.assertTrue(Files.exists(file), "Option was not handled: " + o.option);
-            String content = new String(Files.readAllBytes(file));
-            Assert.assertEquals(content, o.value, "Option: " + o.option);
-        }
-        for (String o : otherOptions) {
-            Path file = image.resolve(o);
-            Assert.assertTrue(!Files.exists(file), "Option presented in config: " + o);
-        }
-    }
-
-    private static class Option {
-        public final String option;
-        public final String value;
-
-        private Option(String option, String value) {
-            this.option = option;
-            this.value = value;
-        }
-    }
-
-    private static Option option(String option) {
-        return option(option, "");
-    }
-
-    private static Option option(String option, String value) {
-        return new Option(option, value);
-    }
-}
--- a/test/jdk/jigsaw/tools/jlink/CustomPluginTest.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/test/jdk/jigsaw/tools/jlink/CustomPluginTest.java	Mon Dec 21 14:42:19 2015 +0100
@@ -41,7 +41,6 @@
  * @library ../lib
  * @modules java.base/jdk.internal.jimage
  *          jdk.jdeps/com.sun.tools.classfile
- *          jdk.jlink/jdk.tools.jlink
  *          jdk.jlink/jdk.tools.jlink.internal
  *          jdk.jlink/jdk.tools.jmod
  *          jdk.jlink/jdk.tools.jimage
@@ -68,47 +67,11 @@
 
         testHelloProvider(helper, pluginModulePath);
         testCustomPlugins(helper, pluginModulePath);
-        testHelp(pluginModulePath);
-    }
-
-    private void testHelp(Path pluginModulePath) {
-        Result result = JImageGenerator.getJLinkTask()
-                .option("--help")
-                .pluginModulePath(pluginModulePath)
-                .call();
-        result.assertSuccess();
-        String output = result.getMessage();
-        List<String> plugins = new ArrayList<>();
-        String[] lines = output.split("\n");
-        for (String s : lines) {
-            if (s.startsWith(" --custom-image-plugin") || s.startsWith(" custom-image-plugin") ||
-                    s.startsWith(" --custom-resource-plugin") || s.startsWith(" custom-resource-plugin")) {
-                plugins.add(s);
-            }
-        }
-        if (plugins.size() != 4) {
-            System.err.println(output);
-            throw new AssertionError("Expected two plugins " + plugins);
-        }
-        for (int i = 0; i < plugins.size(); i += 2) {
-            String[] ss = plugins.get(i).trim().split(" +");
-            String pluginName = ss[0].substring(2, ss[0].lastIndexOf('-'));
-            assertEquals("--" + pluginName + "-option", ss[0], output);
-            assertEquals(pluginName + "-argument", ss[1], output);
-            assertEquals(pluginName + "-description", plugins.get(i + 1).trim(), output);
-        }
-    }
-
-    private static void assertEquals(String expected, String actual, String message) {
-        if (!expected.equals(actual)) {
-            System.err.println(message);
-            throw new AssertionError("Expected: " + expected + ", got: " + actual);
-        }
     }
 
     private void testCustomPlugins(Helper helper, Path pluginModulePath) {
         Result result = JImageGenerator.getJLinkTask()
-                .option("--list-plugins")
+                .option("--xhelp")
                 .pluginModulePath(pluginModulePath)
                 .output(helper.createNewImageDir("customplugin"))
                 .call();
@@ -120,7 +83,7 @@
                 .filter(s -> s.startsWith("Plugin Name:"))
                 .filter(s -> s.contains("custom"))
                 .collect(Collectors.toList());
-        if (customPlugins.size() != 2) {
+        if (customPlugins.size() != 1) {
             System.err.println(result.getMessage());
             throw new AssertionError("Found plugins: " + customPlugins);
         }
--- a/test/jdk/jigsaw/tools/jlink/DefaultProviderTest.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/test/jdk/jigsaw/tools/jlink/DefaultProviderTest.java	Mon Dec 21 14:42:19 2015 +0100
@@ -23,15 +23,17 @@
 
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import jdk.tools.jlink.api.plugin.PluginOption;
-import jdk.tools.jlink.api.plugin.PluginOptionBuilder;
+import java.util.Set;
+import jdk.tools.jlink.plugin.PluginOption;
 import jdk.tools.jlink.internal.PluginRepository;
-import jdk.tools.jlink.api.plugin.PluginProvider;
-import jdk.tools.jlink.api.plugin.transformer.TransformerPlugin;
-import jdk.tools.jlink.api.plugin.transformer.TransformerPluginProvider;
+import jdk.tools.jlink.plugin.Plugin;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.TransformerPlugin;
 import tests.Helper;
 
 /*
@@ -41,7 +43,6 @@
  * @library ../lib
  * @modules java.base/jdk.internal.jimage
  *          jdk.jdeps/com.sun.tools.classfile
- *          jdk.jlink/jdk.tools.jlink
  *          jdk.jlink/jdk.tools.jlink.internal
  *          jdk.jlink/jdk.tools.jmod
  *          jdk.jlink/jdk.tools.jimage
@@ -54,32 +55,73 @@
     private final static List<PluginOption> options;
     private static final String NAME = "toto";
     private static final PluginOption NAME_OPTION
-            = new PluginOptionBuilder(NAME).isEnabled().build();
+            = new PluginOption.Builder(NAME).isEnabled().build();
     private final static Map<PluginOption, Object> expectedOptions = new HashMap<>();
 
     static {
         options = new ArrayList<>();
         options.add(NAME_OPTION);
-        options.add(new PluginOptionBuilder("option1").description("value1").build());
-        options.add(new PluginOptionBuilder("option2").description("value2").build());
+        options.add(new PluginOption.Builder("option1").description("value1").build());
+        options.add(new PluginOption.Builder("option2").description("value2").build());
 
         expectedOptions.put(NAME_OPTION, "on");
-        expectedOptions.put(new PluginOptionBuilder("option1").
+        expectedOptions.put(new PluginOption.Builder("option1").
                 description("value1").build(), "value1");
-        expectedOptions.put(new PluginOptionBuilder("option2").
+        expectedOptions.put(new PluginOption.Builder("option2").
                 description("value2").build(), "value2");
 
     }
 
-    private static class CustomProvider extends TransformerPluginProvider {
+    private static class Custom implements TransformerPlugin {
 
-        public CustomProvider() {
-            super(NAME, NAME);
+        @Override
+        public PluginOption getOption() {
+            return NAME_OPTION;
         }
 
         @Override
-        public String getCategory() {
-            return TRANSFORMER;
+        public List<PluginOption> getAdditionalOptions() {
+            return options;
+        }
+
+        @Override
+        public void visit(Pool in, Pool out) {
+            in.visit((Pool.ModuleData content) -> {
+                return content;
+            }, out);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        public String getDescription() {
+            return NAME;
+        }
+
+        @Override
+        public void configure(Map<PluginOption, String> config) {
+            DefaultProviderTest.isNewPluginsCalled = true;
+            DefaultProviderTest.receivedOptions = config;
+        }
+
+        @Override
+        public Set<PluginType> getType() {
+            Set<PluginType> set = new HashSet<>();
+            set.add(CATEGORY.TRANSFORMER);
+            return Collections.unmodifiableSet(set);
+        }
+    }
+
+    private static class Custom2 implements TransformerPlugin {
+
+        @Override
+        public Set<PluginType> getType() {
+            Set<PluginType> set = new HashSet<>();
+            set.add(CATEGORY.TRANSFORMER);
+            return Collections.unmodifiableSet(set);
         }
 
         @Override
@@ -93,54 +135,31 @@
         }
 
         @Override
-        public Type getType() {
-            return Type.RESOURCE_PLUGIN;
+        public void visit(Pool in, Pool out) {
+            in.visit((Pool.ModuleData content) -> {
+                return content;
+            }, out);
         }
 
         @Override
-        public TransformerPlugin newPlugin(Map<PluginOption, Object> config) {
-            DefaultProviderTest.isNewPluginsCalled = true;
-            DefaultProviderTest.receivedOptions = config;
-            return null;
-        }
-    }
-
-    private static class CustomProvider2 extends TransformerPluginProvider {
-
-        public CustomProvider2() {
-            super(NAME, NAME);
+        public String getName() {
+            return NAME;
         }
 
         @Override
-        public String getCategory() {
-            return TRANSFORMER;
+        public String getDescription() {
+            return NAME;
         }
 
         @Override
-        public PluginOption getOption() {
-            return NAME_OPTION;
-        }
-
-        @Override
-        public List<PluginOption> getAdditionalOptions() {
-            return options;
-        }
-
-        @Override
-        public Type getType() {
-            return Type.IMAGE_FILE_PLUGIN;
-        }
-
-        @Override
-        public TransformerPlugin newPlugin(Map<PluginOption, Object> config) {
+        public void configure(Map<PluginOption, String> config) {
             DefaultProviderTest.isNewPluginsCalled = true;
             DefaultProviderTest.receivedOptions = config;
-            return null;
         }
     }
 
     private static boolean isNewPluginsCalled;
-    private static Map<PluginOption, Object> receivedOptions;
+    private static Map<PluginOption, String> receivedOptions;
 
     private static void reset() {
         isNewPluginsCalled = false;
@@ -154,12 +173,12 @@
             return;
         }
         helper.generateDefaultModules();
-        test(helper, new CustomProvider());
-        test(helper, new CustomProvider2());
+        test(helper, new Custom());
+        test(helper, new Custom2());
     }
 
-    private static void test(Helper helper, PluginProvider provider) throws Exception {
-        PluginRepository.registerPluginProvider(provider);
+    private static void test(Helper helper, Plugin plugin) throws Exception {
+        PluginRepository.registerPlugin(plugin);
 
         {
             String[] userOptions = {};
--- a/test/jdk/jigsaw/tools/jlink/ImageFileCreatorTest.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/test/jdk/jigsaw/tools/jlink/ImageFileCreatorTest.java	Mon Dec 21 14:42:19 2015 +0100
@@ -36,9 +36,9 @@
 import jdk.tools.jlink.internal.Archive;
 import jdk.tools.jlink.internal.ImageFileCreator;
 import jdk.tools.jlink.internal.ImagePluginStack;
-import jdk.tools.jlink.api.plugin.postprocessor.ExecutableImage;
-import jdk.tools.jlink.api.plugin.builder.ImageBuilder;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
+import jdk.tools.jlink.plugin.ExecutableImage;
+import jdk.tools.jlink.builder.ImageBuilder;
+import jdk.tools.jlink.plugin.Pool;
 
 
 /*
@@ -208,25 +208,14 @@
             }
 
             @Override
-            public void storeJavaLauncherOptions(ExecutableImage image, List<String> args) {
+            public void storeFiles(Pool content, String bom) {
 
             }
 
-            @Override
-            public void storeFiles(Pool files, List<Pool.ModuleData> removed,
-            String bom, Pool resources) {
-
-            }
-
-            @Override
-            public String getName() {
-                return "";
-            }
-
         };
 
         ImagePluginStack stack = new ImagePluginStack(noopBuilder, Collections.emptyList(),
-                null, Collections.emptyList(), Collections.emptyList(), "");
+                null, Collections.emptyList(), "");
 
         ImageFileCreator.create(archives, ByteOrder.nativeOrder(), stack);
     }
--- a/test/jdk/jigsaw/tools/jlink/ImageFilePoolTest.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/test/jdk/jigsaw/tools/jlink/ImageFilePoolTest.java	Mon Dec 21 14:42:19 2015 +0100
@@ -32,10 +32,10 @@
 
 import java.io.ByteArrayInputStream;
 import jdk.tools.jlink.internal.PoolImpl;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleData;
-import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleDataType;
-import jdk.tools.jlink.api.plugin.transformer.Pool.Visitor;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.Pool.ModuleData;
+import jdk.tools.jlink.plugin.Pool.ModuleDataType;
+import jdk.tools.jlink.plugin.Pool.Visitor;
 
 public class ImageFilePoolTest {
     public static void main(String[] args) throws Exception {
--- a/test/jdk/jigsaw/tools/jlink/IntegrationTest.java	Mon Dec 21 14:42:17 2015 +0100
+++ b/test/jdk/jigsaw/tools/jlink/IntegrationTest.java	Mon Dec 21 14:42:19 2015 +0100
@@ -34,23 +34,18 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import jdk.tools.jlink.api.plugin.postprocessor.PostProcessorPluginProvider;
-import jdk.tools.jlink.internal.PluginRepository;
-import jdk.tools.jlink.api.Jlink;
-import jdk.tools.jlink.api.Jlink.JlinkConfiguration;
-import jdk.tools.jlink.api.Jlink.OrderedPluginConfiguration;
-import jdk.tools.jlink.api.Jlink.PluginConfiguration;
-import jdk.tools.jlink.api.Jlink.PluginsConfiguration;
-import jdk.tools.jlink.api.plugin.PluginOption;
-import jdk.tools.jlink.api.plugin.PluginOptionBuilder;
-import jdk.tools.jlink.api.plugin.builder.DefaultImageBuilderProvider;
-import jdk.tools.jlink.api.plugin.postprocessor.ExecutableImage;
-import jdk.tools.jlink.api.plugin.transformer.Pool;
-import jdk.tools.jlink.api.plugin.postprocessor.PostProcessorPlugin;
-import jdk.tools.jlink.api.plugin.transformer.TransformerPlugin;
-import jdk.tools.jlink.api.plugin.transformer.TransformerPluginProvider;
-import jdk.tools.jlink.internal.plugins.DefaultCompressProvider;
-import jdk.tools.jlink.internal.plugins.StripDebugProvider;
+import jdk.tools.jlink.Jlink;
+import jdk.tools.jlink.Jlink.JlinkConfiguration;
+import jdk.tools.jlink.Jlink.OrderedPlugin;
+import jdk.tools.jlink.Jlink.PluginsConfiguration;
+import jdk.tools.jlink.plugin.PluginOption;
+import jdk.tools.jlink.builder.DefaultImageBuilder;
+import jdk.tools.jlink.plugin.ExecutableImage;
+import jdk.tools.jlink.plugin.Pool;
+import jdk.tools.jlink.plugin.PostProcessorPlugin;
+import jdk.tools.jlink.plugin.TransformerPlugin;
+import jdk.tools.jlink.internal.plugins.DefaultCompressPlugin;
+import jdk.tools.jlink.internal.plugins.StripDebugPlugin;
 
 import tests.Helper;
 import tests.JImageGenerator;
@@ -62,7 +57,6 @@
  * @library ../lib
  * @modules java.base/jdk.internal.jimage
  *          jdk.jdeps/com.sun.tools.classfile
- *          jdk.jlink/jdk.tools.jlink
  *          jdk.jlink/jdk.tools.jlink.internal
  *          jdk.jlink/jdk.tools.jlink.internal.plugins
  *          jdk.jlink/jdk.tools.jmod
@@ -73,102 +67,95 @@
  */
 public class IntegrationTest {
 
-    static {
-        PluginRepository.registerPluginProvider(new MyProvider(MyProvider.NAME + "0"));
-        PluginRepository.registerPluginProvider(new MyProvider(MyProvider.NAME + "1"));
-        PluginRepository.registerPluginProvider(new MyProvider(MyProvider.NAME + "2"));
-        PluginRepository.registerPluginProvider(new MyProvider(MyProvider.NAME + "3"));
-        PluginRepository.registerPluginProvider(new MyProvider(MyProvider.NAME + "4"));
-        PluginRepository.registerPluginProvider(new MyPostProcessorProvider());
-    }
-
     private static final List<Integer> ordered = new ArrayList<>();
 
-    public static class MyPostProcessorProvider extends PostProcessorPluginProvider {
-
-        public class MyPostProcessor implements PostProcessorPlugin {
-
-            @Override
-            public List<String> process(ExecutableImage image) {
-                try {
-                    Files.createFile(image.getHome().resolve("toto.txt"));
-                    return null;
-                } catch (IOException ex) {
-                    throw new UncheckedIOException(ex);
-                }
-            }
-
-            @Override
-            public String getName() {
-                return NAME;
-            }
-
-        }
+    public static class MyPostProcessor implements PostProcessorPlugin {
 
         public static final String NAME = "mypostprocessor";
 
-        public MyPostProcessorProvider() {
-            super(NAME, "");
+        @Override
+        public List<String> process(ExecutableImage image) {
+            try {
+                Files.createFile(image.getHome().resolve("toto.txt"));
+                return null;
+            } catch (IOException ex) {
+                throw new UncheckedIOException(ex);
+            }
         }
 
         @Override
-        public PostProcessorPlugin newPlugin(Map<PluginOption, Object> config) {
-            return new MyPostProcessor();
+        public String getName() {
+            return NAME;
         }
 
         @Override
-        public String getCategory() {
-            return PROCESSOR;
+        public Set<PluginType> getType() {
+            Set<PluginType> set = new HashSet<>();
+            set.add(CATEGORY.PROCESSOR);
+            return Collections.unmodifiableSet(set);
         }
 
+        @Override
+        public String getDescription() {
+            return null;
+        }
+
+        @Override
+        public PluginOption getOption() {
+            return null;
+        }
+
+        @Override
+        public void configure(Map<PluginOption, String> config) {
+            throw new UnsupportedOperationException("Shouldn't be called");
+        }
     }
 
-    public static class MyProvider extends TransformerPluginProvider {
+    public static class MyPlugin1 implements TransformerPlugin {
 
-        public class MyPlugin1 implements TransformerPlugin {
+        Integer index;
 
-            Integer index;
+        private MyPlugin1(Integer index) {
+            this.index = index;
+        }
 
-            private MyPlugin1(Integer index) {
-                this.index = index;
-            }
+        @Override
+        public String getName() {
+            return NAME + index;
+        }
 
-            @Override
-            public String getName() {
-                return NAME;
-            }
+        @Override
+        public void visit(Pool in, Pool out) {
+            System.err.println(NAME + index);
+            ordered.add(index);
+            in.visit((file) -> {
+                return file;
+            }, out);
+        }
 
-            @Override
-            public void visit(Pool in, Pool out) {
-                System.err.println(NAME + index);
-                ordered.add(index);
-                in.visit((file) -> {
-                    return file;
-                }, out);
-            }
+        @Override
+        public Set<PluginType> getType() {
+            Set<PluginType> set = new HashSet<>();
+            set.add(CATEGORY.TRANSFORMER);
+            return Collections.unmodifiableSet(set);
+        }
 
+        @Override
+        public String getDescription() {
+            return null;
+        }
+
+        @Override
+        public PluginOption getOption() {
+            return null;
         }
         static final String NAME = "myprovider";
         static final String INDEX = "INDEX";
-        static final PluginOption INDEX_OPTION = new PluginOptionBuilder(INDEX).build();
-
-        public MyProvider(String name) {
-            super(name, "");
-        }
+        static final PluginOption INDEX_OPTION = new PluginOption.Builder(INDEX).build();
 
         @Override
-        public String getCategory() {
-            return TRANSFORMER;
-        }
-
-        @Ov