changeset 14809:74110610664b

jlink API rework, some package renaming, less PluginProvider, PluginOption, no SPI for ImageBuilder
author jfdenise
date Mon, 21 Dec 2015 14:42:17 +0100
parents bb7babadf6ea
children b31d3940b546
files src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java src/jdk.jlink/share/classes/jdk/tools/jlink/JlinkTask.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/internal/ImageFileCreator.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginProviderRepository.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JvmHandler.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/ProcessingManagerImpl.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePrevisitor.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/StringTable.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/DefaultCompressProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeFilesPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeFilesProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludePlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/FileCopierPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/FileCopierProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/FileReplacerPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/FileReplacerProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/InstalledModuleDescriptorPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/InstalledModuleDescriptorProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SortResourcesPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SortResourcesProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StringSharingPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StringSharingProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripDebugPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripDebugProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripNativeCommandsPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripNativeCommandsProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ZipCompressProvider.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/plugins/CmdPluginProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/DefaultImageBuilder.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/DefaultImageBuilderProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/ExecutableImage.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/ImageBuilder.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/ImageBuilderProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/Jlink.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/JlinkPermission.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/OnOffPluginProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/OrderedPluginProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/Plugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/PluginException.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/PluginProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/Pool.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/PostProcessorCmdProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/PostProcessorOnOffProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/PostProcessorPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/PostProcessorPluginProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/ResourcePrevisitor.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/Sessions.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/StringTable.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/TransformerCmdProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/TransformerOnOffProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/TransformerPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/TransformerPluginProvider.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/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/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/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/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/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/FileCopierPluginTest.java test/jdk/jigsaw/tools/jlink/plugins/FileReplacerPluginTest.java test/jdk/jigsaw/tools/jlink/plugins/LastSorterTest.java test/jdk/jigsaw/tools/jlink/plugins/OnOffProviderTest.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
diffstat 125 files changed, 4261 insertions(+), 6146 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java	Mon Dec 21 14:42:17 2015 +0100
@@ -201,7 +201,7 @@
         Path jimage = options.jimages.get(0).toPath();
 
         if (jimage.toFile().createNewFile()) {
-            ImagePluginStack pc = ImagePluginConfiguration.parseConfiguration(taskHelper.getPluginsConfig());
+            ImagePluginStack pc = ImagePluginConfiguration.parseConfiguration(taskHelper.getPluginsConfig(null));
             ExtractedImage img = new ExtractedImage(dirPath, pc, log, options.verbose);
             img.recreateJImage(jimage);
         } else {
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/JlinkTask.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/JlinkTask.java	Mon Dec 21 14:42:17 2015 +0100
@@ -60,19 +60,19 @@
 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.ImagePluginProviderRepository;
 import jdk.tools.jlink.internal.ImagePluginStack;
 import jdk.tools.jlink.internal.ImagePluginStack.ImageProvider;
-import jdk.tools.jlink.plugins.ExecutableImage;
-import jdk.tools.jlink.plugins.ImageBuilderProvider;
-import jdk.tools.jlink.plugins.Jlink.JlinkConfiguration;
-import jdk.tools.jlink.plugins.Jlink.PluginsConfiguration;
-import jdk.tools.jlink.plugins.PluginException;
+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;
 
 
 /**
@@ -139,7 +139,7 @@
             if (!Files.exists(path) || !Files.isDirectory(path)) {
                 throw taskHelper.newBadArgs("err.existing.image.must.exist");
             }
-            task.options.existingImage = path;
+            task.options.existingImage = path.toAbsolutePath();
         }, "--post-process-path"),
         new Option<JlinkTask>(true, (task, opt, arg) -> {
             if ("little".equals(arg)) {
@@ -216,15 +216,17 @@
                 }
                 createImage();
             } else {
-                postProcessOnly();
+                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));
@@ -304,9 +306,6 @@
         ModuleFinder finder
             = newModuleFinder(arr, config.getLimitmods(), config.getModules());
 
-        Path[] pluginsPath = new Path[config.getPluginpaths().size()];
-        pluginsPath = config.getPluginpaths().toArray(pluginsPath);
-
         // First create the image provider
         ImageProvider imageProvider
                 = createImageProvider(finder,
@@ -314,51 +313,34 @@
                         config.getLimitmods(), config.getByteOrder());
 
         // Then create the Plugin Stack
-        ImagePluginStack stack = ImagePluginConfiguration.
-                parseConfiguration(config.getOutput(), plugins,
-                        TaskHelper.createPluginsLayer(pluginsPath),
+        ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(plugins,
                         genBOMContent(config, plugins));
 
         //Ask the stack to proceed;
         stack.operate(imageProvider);
     }
 
-    private class PostProcessingImageHelper implements 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);
 
-        private ImageBuilderProvider provider;
-        @Override
-        public ExecutableImage retrieve(ImagePluginStack stack) throws IOException {
-            ExecutableImage img = null;
-            for (ImageBuilderProvider provider
-                    : ImagePluginProviderRepository.
-                    getImageBuilderProviders(optionsHelper.getPluginsLayer())) {
-                img = provider.canExecute(options.existingImage);
-                if (img != null) {
-                    this.provider = provider;
-                }
-            }
-            if (img == null) {
-                throw new IOException("Image in " + options.existingImage + " is not recognized");
-            }
-            return img;
-        }
-
-        @Override
-        public void storeLauncherArgs(ImagePluginStack stack, ExecutableImage image,
-                List<String> args) throws IOException {
-            provider.storeLauncherOptions(image, args);
-        }
-
+        stack.operate((ImagePluginStack stack1) -> image);
     }
 
-    private void postProcessOnly() throws Exception {
-        // Create the Plugin Stack
-        ImagePluginStack stack = ImagePluginConfiguration.
-                parseConfiguration(null, taskHelper.getPluginsConfig(),
-                        optionsHelper.getPluginsLayer(),
-                        genBOMContent());
-
-        stack.operate(new PostProcessingImageHelper());
+    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 {
@@ -382,8 +364,7 @@
 
         // Then create the Plugin Stack
         ImagePluginStack stack = ImagePluginConfiguration.
-                parseConfiguration(options.output, taskHelper.getPluginsConfig(),
-                        optionsHelper.getPluginsLayer(),
+                parseConfiguration(taskHelper.getPluginsConfig(options.output),
                         genBOMContent());
 
         //Ask the stack to proceed
@@ -563,11 +544,6 @@
         public ExecutableImage retrieve(ImagePluginStack stack) throws IOException {
             return ImageFileCreator.create(archives, order, stack);
         }
-
-        @Override
-        public void storeLauncherArgs(ImagePluginStack stack, ExecutableImage image, List<String> args) throws IOException {
-            stack.getImageBuilder().storeJavaLauncherOptions(image, args);
-        }
     }
 
     private static enum Section {
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/TaskHelper.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/TaskHelper.java	Mon Dec 21 14:42:17 2015 +0100
@@ -47,20 +47,20 @@
 
 import jdk.internal.module.ConfigurableModuleFinder;
 import jdk.internal.module.ConfigurableModuleFinder.Phase;
-import jdk.tools.jlink.internal.ImagePluginProviderRepository;
+import jdk.tools.jlink.internal.PluginRepository;
 import jdk.tools.jlink.internal.ImagePluginConfiguration;
-import jdk.tools.jlink.plugins.OnOffPluginProvider;
-import jdk.tools.jlink.plugins.CmdPluginProvider;
-import jdk.tools.jlink.plugins.DefaultImageBuilderProvider;
-import jdk.tools.jlink.plugins.ImageBuilderProvider;
-import jdk.tools.jlink.plugins.Jlink;
-import jdk.tools.jlink.plugins.Jlink.PluginsConfiguration;
-import jdk.tools.jlink.plugins.Jlink.OrderedPluginConfiguration;
-import jdk.tools.jlink.plugins.TransformerPluginProvider;
-import jdk.tools.jlink.plugins.OrderedPluginProvider;
-import jdk.tools.jlink.plugins.OrderedPluginProvider.ORDER;
-import jdk.tools.jlink.plugins.PluginProvider;
-import jdk.tools.jlink.plugins.PostProcessorPluginProvider;
+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;
 
 /**
  *
@@ -135,9 +135,9 @@
         }
     }
 
-    private static class PluginOption extends Option<PluginsOptions> {
+    private static class PlugOption extends Option<PluginsOptions> {
 
-        public PluginOption(boolean hasArg,
+        public PlugOption(boolean hasArg,
                 Processing<PluginsOptions> processing, String... aliases) {
             super(hasArg, processing, aliases);
         }
@@ -154,17 +154,15 @@
         int index = -1;
         if (i != -1) {
             opt = opt.substring(i + 1);
-            if (opt.equals(CmdPluginProvider.FIRST)) {
+            if (opt.equals(Plugin.ORDER.FIRST.getName())) {
                 index = 0;
+            } else if (opt.equals(Plugin.ORDER.LAST.getName())) {
+                index = Integer.MAX_VALUE;
             } else {
-                if (opt.equals(CmdPluginProvider.LAST)) {
-                    index = Integer.MAX_VALUE;
-                } else {
-                    try {
-                        index = Integer.parseInt(opt);
-                    } catch (NumberFormatException ex) {
-                        throw newBadArgs("err.invalid.index", orig);
-                    }
+                try {
+                    index = Integer.parseInt(opt);
+                } catch (NumberFormatException ex) {
+                    throw newBadArgs("err.invalid.index", orig);
                 }
             }
         }
@@ -180,7 +178,7 @@
         return opt;
     }
 
-    private static class HiddenPluginOption extends PluginOption {
+    private static class HiddenPluginOption extends PlugOption {
 
         public HiddenPluginOption(boolean hasArg,
                 Processing<PluginsOptions> processing, String... aliases) {
@@ -196,48 +194,37 @@
     private final class PluginsOptions {
 
         private static final String PLUGINS_PATH = "--plugins-modulepath";
-        private static final String NO_CATEGORY = "jlink.no.category";
 
         private Layer pluginsLayer = Layer.boot();
-        private String imgBuilder;
         private String lastSorter;
         private boolean listPlugins;
-        private final Map<OrderedPluginProvider, Map<String, String>> plugins = new HashMap<>();
-        private final Map<ImageBuilderProvider, Map<String, String>> builders = new HashMap<>();
-        private final List<PluginOption> pluginsOptions = new ArrayList<>();
+        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 provider category
-        private final Map<String, List<OrderedPluginProvider>> providersOrder = new HashMap<>();
-        private final Map<OrderedPluginProvider, Integer> providersIndexes = new HashMap<>();
+        // 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);
-                Path[] paths = new Path[dirs.length];
-                int i = 0;
+                List<Path> paths = new ArrayList<>(dirs.length);
                 for (String dir : dirs) {
-                    paths[i++] = Paths.get(dir);
+                    paths.add(Paths.get(dir));
                 }
 
                 pluginsLayer = createPluginsLayer(paths);
             }
 
             Map<String, List<String>> seen = new HashMap<>();
-            for (TransformerPluginProvider prov : ImagePluginProviderRepository.
-                    getTransformerProviders(pluginsLayer)) {
-                addOrderedProviderOptions(prov, seen);
+            for (Plugin plugin : PluginRepository.
+                    getPlugins(pluginsLayer)) {
+                addOrderedPuginOptions(plugin, seen);
             }
-            for (PostProcessorPluginProvider prov : ImagePluginProviderRepository.
-                    getPostProcessingProviders(pluginsLayer)) {
-                addOrderedProviderOptions(prov, seen);
-            }
-            pluginsOptions.add(new PluginOption(true,
-                    (task, opt, arg) -> {
-                        imgBuilder = arg;
-                    },
-                    "--image-builder"));
-            pluginsOptions.add(new PluginOption(true,
+            pluginsOptions.add(new PlugOption(true,
                     (task, opt, arg) -> {
                         lastSorter = arg;
                     },
@@ -248,118 +235,106 @@
                     },
                     "--list-plugins"));
 
-            //Image Builder options
-            for (ImageBuilderProvider provider
-                    : ImagePluginProviderRepository.getImageBuilderProviders(pluginsLayer)) {
-                Map<String, String> options = provider.getOptions();
-                if (options != null && !options.isEmpty()) {
-                    for (Entry<String, String> o : options.entrySet()) {
-                        PluginOption option
-                                = new PluginOption(provider.hasArgument(o.getKey()),
-                                        (task, opt, arg) -> {
-                                            Map<String, String> m = builders.get(provider);
-                                            if (m == null) {
-                                                m = new HashMap<>();
-                                                builders.put(provider, m);
-                                            }
-                                            m.put(o.getKey(), arg);
-                                        },
-                                        "--" + o.getKey());
-                        pluginsOptions.add(option);
+            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 void addOrderedProviderOptions(OrderedPluginProvider prov,
-                Map<String, List<String>> seen) throws BadArgs {
-            if (prov instanceof CmdPluginProvider) {
-                CmdPluginProvider<?> provider = (CmdPluginProvider<?>) prov;
-                if (provider.getToolOption() != null) {
-                    for (Entry<String, List<String>> entry : seen.entrySet()) {
-                        if (entry.getKey().equals(provider.getToolOption())
-                                || entry.getValue().contains(provider.getToolOption())) {
-                            throw new BadArgs("err.plugin.mutiple.options",
-                                    provider.getToolOption());
-                        }
-                    }
-                    List<String> optional = new ArrayList<>();
-                    seen.put(provider.getToolOption(), optional);
-                    PluginOption option
-                            = new PluginOption(provider.getToolArgument() != null,
-                                    (task, opt, arg) -> {
-                                        if (!prov.isFunctional()) {
-                                            throw newBadArgs("err.provider.not.functional",
-                                                    prov.getName());
-                                        }
-                                        Map<String, String> m = plugins.get(prov);
-                                        if (m == null) {
-                                            m = new HashMap<>();
-                                            plugins.put(prov, m);
-                                        }
-                                        m.put(CmdPluginProvider.TOOL_ARGUMENT_PROPERTY, arg);
-                                        int index = computeIndex(prov);
-                                        // Overriden index?
-                                        if (index == -1) {
-                                            index = getIndex(opt);
-                                        }
-                                        providersIndexes.put(prov, index);
-                                    },
-                                    "--" + provider.getToolOption());
-                    pluginsOptions.add(option);
-                    if (provider.getAdditionalOptions() != null) {
-                        for (String other : provider.getAdditionalOptions().keySet()) {
-                            optional.add(other);
-                            PluginOption otherOption = new PluginOption(true,
-                                    (task, opt, arg) -> {
-                                        Map<String, String> m = plugins.get(prov);
-                                        if (m == null) {
-                                            m = new HashMap<>();
-                                            plugins.put(prov, m);
-                                        }
-                                        m.put(other, arg);
-                                    },
-                                    "--" + other);
-                            pluginsOptions.add(otherOption);
-                        }
-                    }
-                        // On/Off enabled by default plugin
-                    // Command line option can override it
-                    if (provider instanceof OnOffPluginProvider) {
-                        boolean edefault = prov.isFunctional() && ((OnOffPluginProvider) provider).isEnabledByDefault();
-                        if (edefault) {
-                            Map<String, String> m = new HashMap<>();
-                            m.put(CmdPluginProvider.TOOL_ARGUMENT_PROPERTY,
-                                    OnOffPluginProvider.ON_ARGUMENT);
-                            plugins.put(prov, m);
-                        }
-                    }
-                }
-            }
-        }
-
-        private int computeIndex(OrderedPluginProvider prov) {
-            String category = prov.getCategory() == null ? NO_CATEGORY : prov.getCategory();
-            List<OrderedPluginProvider> order = providersOrder.get(category);
+        private int computeIndex(Plugin plugin) {
+            CATEGORY category = Utils.getCategory(plugin);
+            List<Plugin> order = pluginsOrder.get(category);
             if (order == null) {
                 order = new ArrayList<>();
-                providersOrder.put(category, order);
+                pluginsOrder.put(category, order);
             }
-            order.add(prov);
+            order.add(plugin);
             int index = -1;
-            ORDER defaultOrder = prov.getOrder();
+            ORDER defaultOrder = Utils.getOrder(plugin);
             if (defaultOrder == ORDER.FIRST) {
                 index = 0;
-            } else {
-                if (defaultOrder == ORDER.LAST) {
-                    index = Integer.MAX_VALUE;
-                }
+            } else if (defaultOrder == ORDER.LAST) {
+                index = Integer.MAX_VALUE;
             }
             return index;
         }
 
-        private PluginOption getOption(String name) throws BadArgs {
-            for (PluginOption o : pluginsOptions) {
+        private PlugOption getOption(String name) throws BadArgs {
+            for (PlugOption o : pluginsOptions) {
                 if (o.matches(name)) {
                     return o;
                 }
@@ -367,54 +342,58 @@
             return null;
         }
 
-        private PluginsConfiguration getPluginsConfig() throws IOException {
-            List<OrderedPluginConfiguration> preProcessing = new ArrayList<>();
-            List<OrderedPluginConfiguration> postProcessing = new ArrayList<>();
-            for (Entry<OrderedPluginProvider, Map<String, String>> entry : plugins.entrySet()) {
-                OrderedPluginProvider provider = entry.getKey();
+        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 = providersIndexes.get(provider);
+                Integer i = pluginsIndexes.get(plugin);
                 if (i == null) {
-                    // Enabled by default provider. Find it an index.
-                    i = computeIndex(provider);
+                    // Enabled by default plugin. Find it an index.
+                    i = computeIndex(plugin);
                 }
                 boolean absolute = false;
                 if (i == -1) {
-                    String category = provider.getCategory();
-                    if (provider.getCategory() == null) {
+                    CATEGORY category = Utils.getCategory(plugin);
+                    if (category == null) {
                         absolute = true;
-                        category = NO_CATEGORY;
                     }
-                    List<OrderedPluginProvider> lstProviders
-                            = providersOrder.get(category);
-                    i = lstProviders.indexOf(provider);
+                    List<Plugin> lstPlugins
+                            = pluginsOrder.get(category);
+                    i = lstPlugins.indexOf(plugin);
                 }
                 if (i == -1) {
                     throw new IllegalArgumentException("Invalid index " + i);
                 }
-                Map<String, Object> config = new HashMap<>();
+                Map<PluginOption, String> config = new HashMap<>();
                 config.putAll(entry.getValue());
-                OrderedPluginConfiguration conf
-                        = new Jlink.OrderedPluginConfiguration(provider.getName(),
-                                i, absolute, config);
-                if (provider instanceof PostProcessorPluginProvider) {
-                    postProcessing.add(conf);
-                } else {
-                    preProcessing.add(conf);
-                }
+                plugin.configure(config);
+                OrderedPlugin conf
+                        = new Jlink.OrderedPlugin(plugin,
+                                i, absolute, Utils.getCategory(plugin));
+                pluginsList.add(conf);
             }
 
-            imgBuilder = imgBuilder == null ? DefaultImageBuilderProvider.NAME : imgBuilder;
-            Map<String, Object> builderConfig = new HashMap<>();
-            if (!builders.isEmpty()) {
-                Map<String, String> conf = builders.entrySet().iterator().next().getValue();
-                if (conf != null) {
-                    builderConfig.putAll(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(preProcessing, postProcessing,
-                    new Jlink.PluginConfiguration(imgBuilder, builderConfig),
-                    lastSorter);
+            return new Jlink.PluginsConfiguration(pluginsList,
+                    builder, lastSorter);
         }
     }
 
@@ -668,7 +647,7 @@
             for (int i = 0; i < args.length; i++) {
                 if (!args[i].isEmpty() && args[i].charAt(0) == '-') {
                     String name = args[i];
-                    PluginOption pluginOption = null;
+                    PlugOption pluginOption = null;
                     Option<T> option = getOption(name);
                     if (option == null) {
                         pluginOption = pluginOptions.getOption(name);
@@ -736,55 +715,45 @@
                     + bundleHelper.getMessage("warn.thirdparty.plugins"));
             log.println(bundleHelper.getMessage("main.command.files"));
 
-            log.println("\n" + pluginsHeader);
-            for (PluginProvider prov : ImagePluginProviderRepository.getTransformerProviders(pluginOptions.pluginsLayer)) {
-                printHelp(prov, showsImageBuilder);
+            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<PostProcessorPluginProvider> post = ImagePluginProviderRepository.
-                        getPostProcessingProviders(pluginOptions.pluginsLayer);
+                List<Plugin> post = Utils.getPostProcessors(PluginRepository.
+                        getPlugins(pluginOptions.pluginsLayer));
                 if (post.size() > 0) {
                     log.println(bundleHelper.getMessage("main.plugin.post.processors"));
-                    for (PluginProvider prov : post) {
-                        printHelp(prov, showsImageBuilder);
-                    }
-                }
-                log.println(bundleHelper.getMessage("main.image.builders"));
-                for (ImageBuilderProvider prov
-                        : ImagePluginProviderRepository.getImageBuilderProviders(getPluginsLayer())) {
-                    if (prov.isExposed()) {
-                        log.println("\n" + bundleHelper.getMessage("main.image.builder.name")
-                                + ": " + prov.getName());
-                        log.println(bundleHelper.getMessage("main.image.builder.description")
-                                + ": " + prov.getDescription());
-                        log.println(bundleHelper.getMessage("main.plugin.state")
-                                + ": " + prov.getFunctionalStateDescription(prov.isFunctional()));
-                        logBuilderOptions(prov.getOptions());
+                    for (Plugin plugin : post) {
+                        printHelp(plugin, showsImageBuilder);
                     }
                 }
             }
         }
 
-        private void printHelp(PluginProvider prov, boolean showsImageBuilder) {
-            if (showsPlugin(prov, showsImageBuilder)) {
-                CmdPluginProvider<?> provider = (CmdPluginProvider<?>) prov;
-
-                if (provider.getToolOption() != null) {
+        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(provider.getToolOption());
-                    if (provider.getToolArgument() != null) {
-                        line.append(" ").append(provider.getToolArgument());
+                    line.append(" --").append(opt.getName());
+                    if (opt.getArgumentDescription() != null) {
+                        line.append(" ").append(opt.getArgumentDescription());
                     }
-                    line.append("\n ").append(prov.getDescription());
-                    line.append("\n").append(bundleHelper.getMessage("main.plugin.state")).
-                            append(": ").append(prov.getFunctionalStateDescription(prov.isFunctional()));
-                    if (provider.getAdditionalOptions() != null) {
-                        line.append("\n").append(bundleHelper.
+                    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 (Entry<String, String> entry : provider.getAdditionalOptions().entrySet()) {
-                            line.append(" --").append(entry.getKey()).append(" ").
-                                    append(entry.getValue());
+                        for (PluginOption option : plugin.getAdditionalOptions()) {
+                            line.append(" --").append(option.getName()).append(" ").
+                                    append(option.getDescription());
                         }
                     }
                     log.println(line.toString() + "\n");
@@ -793,36 +762,28 @@
         }
 
         public void showPlugins(PrintWriter log, boolean showsImageBuilder) {
-            for (TransformerPluginProvider prov : ImagePluginProviderRepository.getTransformerProviders(getPluginsLayer())) {
-                showPlugin(prov, log, showsImageBuilder);
+            for (Plugin plugin : Utils.
+                    getPreProcessors(PluginRepository.
+                            getPlugins(getPluginsLayer()))) {
+                showPlugin(plugin, log, showsImageBuilder);
             }
 
             if (showsImageBuilder) {
-                for (PostProcessorPluginProvider prov : ImagePluginProviderRepository.getPostProcessingProviders(getPluginsLayer())) {
-                    showPlugin(prov, log, showsImageBuilder);
-                }
-                for (ImageBuilderProvider prov
-                        : ImagePluginProviderRepository.getImageBuilderProviders(getPluginsLayer())) {
-                    if (prov.isExposed()) {
-                        log.println("\n" + bundleHelper.getMessage("main.image.builder.name")
-                                + ": " + prov.getName());
-                        log.println(bundleHelper.getMessage("main.image.builder.description")
-                                + ": " + prov.getDescription());
-                        log.println(bundleHelper.getMessage("main.plugin.state")
-                                + ": " + prov.getFunctionalStateDescription(prov.isFunctional()));
-                        logBuilderOptions(prov.getOptions());
-                    }
+                for (Plugin plugin : Utils.
+                        getPostProcessors(PluginRepository.
+                                getPlugins(getPluginsLayer()))) {
+                    showPlugin(plugin, log, showsImageBuilder);
                 }
             }
         }
 
-        private void showPlugin(OrderedPluginProvider prov, PrintWriter log, boolean showsImageBuilder) {
-            if (showsPlugin(prov, showsImageBuilder)) {
-                CmdPluginProvider<?> fact = (CmdPluginProvider<?>) prov;
+        private void showPlugin(Plugin plugin, PrintWriter log, boolean showsImageBuilder) {
+            if (showsPlugin(plugin, showsImageBuilder)) {
                 log.println("\n" + bundleHelper.getMessage("main.plugin.name")
-                        + ": " + prov.getName());
-                Integer[] range = ImagePluginConfiguration.getRange(prov);
-                String cat = range == null ? prov.getCategory() : prov.getCategory()
+                        + ": " + 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") + " "
@@ -830,34 +791,40 @@
                 log.println(bundleHelper.getMessage("main.plugin.category")
                         + ": " + cat);
                 log.println(bundleHelper.getMessage("main.plugin.description")
-                        + ": " + prov.getDescription());
+                        + ": " + plugin.getDescription());
+                PluginOption opt = plugin.getOption();
+                String desc = opt == null ? null : opt.getArgumentDescription();
                 log.println(bundleHelper.getMessage("main.plugin.argument")
-                        + ": " + (fact.getToolArgument() == null
+                        + ": " + (desc == null
                                 ? bundleHelper.getMessage("main.plugin.no.value")
-                                : fact.getToolArgument()));
-                String additionalOptions = bundleHelper.getMessage("main.plugin.no.value");
-                if (fact.getAdditionalOptions() != null) {
+                                : 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 (Entry<String, String> entry : fact.getAdditionalOptions().entrySet()) {
-                        builder.append("--").append(entry.getKey()).append(" ").
-                                append(entry.getValue());
+                    for (PluginOption o : plugin.getAdditionalOptions()) {
+                        builder.append("--").append(o.getName()).append(" ").
+                                append(o.getArgumentDescription() != null
+                                        ? bundleHelper.getMessage("main.plugin.no.value")
+                                        : o.getArgumentDescription());
                     }
-                    additionalOptions = builder.toString();
+                    log.println(bundleHelper.getMessage("main.plugin.additional.options")
+                            + ": " + builder.toString());
                 }
-                log.println(bundleHelper.getMessage("main.plugin.additional.options")
-                        + ": " + additionalOptions);
-                String option = fact.getToolOption();
-                if (option != null) {
-                    log.println(bundleHelper.getMessage("main.plugin.option")
-                            + ": --" + option);
-                }
+                log.println(bundleHelper.getMessage("main.plugin.state")
+                        + ": " + plugin.getStateDescription());
             }
         }
 
-        private void logBuilderOptions(Map<String, String> options) {
+        private void logBuilderOptions(List<PluginOption> options) {
             if (options != null && !options.isEmpty()) {
-                for (Entry<String, String> opt : options.entrySet()) {
-                    log.println(" --" + opt.getKey() + " " + opt.getValue() + "\n");
+                for (PluginOption opt : options) {
+                    log.println(opt.getDescription());
+                    log.println(" --" + opt.getName() + " " + (opt.getArgumentDescription() == null
+                            ? bundleHelper.getMessage("main.plugin.no.value")
+                            : opt.getArgumentDescription()) + "\n");
                 }
             }
         }
@@ -878,7 +845,7 @@
             return defaults;
         }
 
-        Layer getPluginsLayer() {
+        public Layer getPluginsLayer() {
             return pluginOptions.pluginsLayer;
         }
     }
@@ -931,8 +898,8 @@
                 + bundleHelper.getMessage(key, args));
     }
 
-    public PluginsConfiguration getPluginsConfig() throws IOException {
-        return pluginOptions.getPluginsConfig();
+    public PluginsConfiguration getPluginsConfig(Path output) throws IOException {
+        return pluginOptions.getPluginsConfig(output);
     }
 
     public void showVersion(boolean full) {
@@ -978,16 +945,20 @@
         return arguments;
     }
 
-    static Layer createPluginsLayer(Path[] paths) {
-        ModuleFinder finder = ModuleFinder.of(paths);
+    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);
+        if (finder instanceof ConfigurableModuleFinder) {
+            ((ConfigurableModuleFinder) finder).configurePhase(Phase.LINK_TIME);
+        }
 
-        Configuration bootConfiguration= Layer.boot().configuration();
+        Configuration bootConfiguration = Layer.boot().configuration();
 
-        Configuration cf = Configuration.resolve(ModuleFinder.empty(), bootConfiguration, finder);
+        Configuration cf
+                = Configuration.resolve(ModuleFinder.empty(), bootConfiguration, finder);
 
         cf = cf.bind();
 
@@ -996,14 +967,10 @@
     }
 
     // Display all plugins or resource only.
-    private static boolean showsPlugin(PluginProvider prov, boolean showsImageBuilder) {
-        if (prov.isExposed() && prov instanceof CmdPluginProvider && showsImageBuilder) {
+    private static boolean showsPlugin(Plugin plugin, boolean showsImageBuilder) {
+        if (Utils.isEnabled(plugin) && plugin.getOption() != null && showsImageBuilder) {
             return true;
         }
-        if (prov.isExposed() && prov instanceof TransformerPluginProvider) {
-            TransformerPluginProvider mtp = (TransformerPluginProvider) prov;
-            return mtp.getType().equals(TransformerPluginProvider.Type.RESOURCE_PLUGIN);
-        }
         return false;
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/api/Jlink.java	Mon Dec 21 14:42:17 2015 +0100
@@ -0,0 +1,352 @@
+/*
+ * 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);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/api/JlinkPermission.java	Mon Dec 21 14:42:17 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.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);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/Plugin.java	Mon Dec 21 14:42:17 2015 +0100
@@ -0,0 +1,246 @@
+/*
+ * 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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/PluginException.java	Mon Dec 21 14:42:17 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.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);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/builder/DefaultImageBuilder.java	Mon Dec 21 14:42:17 2015 +0100
@@ -0,0 +1,402 @@
+/*
+ * 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);
+                }
+            });
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/builder/DefaultImageBuilderProvider.java	Mon Dec 21 14:42:17 2015 +0100
@@ -0,0 +1,121 @@
+/*
+ * 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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/builder/ExecutableImage.java	Mon Dec 21 14:42:17 2015 +0100
@@ -0,0 +1,75 @@
+/*
+ * 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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/builder/ImageBuilder.java	Mon Dec 21 14:42:17 2015 +0100
@@ -0,0 +1,69 @@
+/*
+ * 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();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/postprocessor/PostProcessorPlugin.java	Mon Dec 21 14:42:17 2015 +0100
@@ -0,0 +1,44 @@
+/*
+ * 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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/transformer/Pool.java	Mon Dec 21 14:42:17 2015 +0100
@@ -0,0 +1,372 @@
+/*
+ * 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);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/api/plugin/transformer/TransformerPlugin.java	Mon Dec 21 14:42:17 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.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);
+}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java	Mon Dec 21 14:42:17 2015 +0100
@@ -45,9 +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.plugins.ExecutableImage;
-import jdk.tools.jlink.plugins.Pool;
-import jdk.tools.jlink.plugins.Pool.ModuleData;
+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;
 
 /**
  * An image (native endian.)
@@ -135,11 +136,11 @@
                                     }));
             ByteOrder order = ByteOrder.nativeOrder();
             BasicImageWriter writer = new BasicImageWriter(order);
-            Pools pools = createPools(archives, entriesForModule, order, writer);
+            PoolImpl pool = createPools(archives, entriesForModule, order, writer);
             try (OutputStream fos = Files.newOutputStream(jimageFile);
                     BufferedOutputStream bos = new BufferedOutputStream(fos);
                     DataOutputStream out = new DataOutputStream(bos)) {
-                generateJImage(pools.resources, writer, pluginSupport, out);
+                generateJImage(pool, writer, pluginSupport, out);
             }
         } finally {
             //Close all archives
@@ -154,14 +155,14 @@
             throws IOException {
         try {
             BasicImageWriter writer = new BasicImageWriter(byteOrder);
-            Pools pools = createPools(archives,
+            PoolImpl allContent = createPools(archives,
                     entriesForModule, byteOrder, writer);
-            PoolImpl result = generateJImage(pools.resources,
+            PoolImpl result = generateJImage(allContent,
                  writer, plugins, plugins.getJImageFileOutputStream());
 
             //Handle files.
             try {
-                plugins.storeFiles(pools.files, result, writer);
+                plugins.storeFiles(allContent, result, writer);
             } catch (Exception ex) {
                 throw new IOException(ex);
             }
@@ -173,14 +174,14 @@
         }
     }
 
-    private static PoolImpl generateJImage(PoolImpl resources,
+    private static PoolImpl generateJImage(PoolImpl allContent,
             BasicImageWriter writer,
             ImagePluginStack pluginSupport,
             DataOutputStream out
     ) throws IOException {
         PoolImpl resultResources;
         try {
-            resultResources = pluginSupport.visitResources(resources);
+            resultResources = pluginSupport.visitResources(allContent);
         } catch (Exception ex) {
             throw new IOException(ex);
         }
@@ -192,31 +193,33 @@
                  // the order of traversing the resources and the order of
         // the module content being written must be the same
         for (ModuleData res : resultResources.getContent()) {
-            String path = res.getPath();
-            content.add(res);
-            long uncompressedSize = res.getLength();
-            long compressedSize = 0;
-            if (res instanceof CompressedModuleData) {
-                CompressedModuleData comp
-                        = (CompressedModuleData) res;
-                compressedSize = res.getLength();
-                uncompressedSize = comp.getUncompressedSize();
+            if (res.getType().equals(ModuleDataType.CLASS_OR_RESOURCE)) {
+                String path = res.getPath();
+                content.add(res);
+                long uncompressedSize = res.getLength();
+                long compressedSize = 0;
+                if (res instanceof CompressedModuleData) {
+                    CompressedModuleData comp
+                            = (CompressedModuleData) res;
+                    compressedSize = res.getLength();
+                    uncompressedSize = comp.getUncompressedSize();
+                }
+                long onFileSize = res.getLength();
+
+                if (duplicates.contains(path)) {
+                    System.err.format("duplicate resource \"%s\", skipping%n",
+                            path);
+                    // TODO Need to hang bytes on resource and write
+                    // from resource not zip.
+                    // Skipping resource throws off writing from zip.
+                    offset += onFileSize;
+                    continue;
+                }
+                duplicates.add(path);
+                writer.addLocation(path, offset, compressedSize, uncompressedSize);
+                paths.add(path);
+                offset += onFileSize;
             }
-            long onFileSize = res.getLength();
-
-            if (duplicates.contains(path)) {
-                System.err.format("duplicate resource \"%s\", skipping%n",
-                        path);
-                     // TODO Need to hang bytes on resource and write
-                // from resource not zip.
-                // Skipping resource throws off writing from zip.
-                offset += onFileSize;
-                continue;
-            }
-            duplicates.add(path);
-            writer.addLocation(path, offset, compressedSize, uncompressedSize);
-            paths.add(path);
-            offset += onFileSize;
         }
 
         ImageResourcesTree tree = new ImageResourcesTree(offset, writer, paths);
@@ -238,11 +241,6 @@
         return resultResources;
     }
 
-    private static class Pools {
-        private PoolImpl resources;
-        private PoolImpl files;
-    }
-
     private static Pool.ModuleDataType mapImageFileType(EntryType type) {
         switch(type) {
             case CONFIG: {
@@ -258,7 +256,7 @@
         return null;
     }
 
-    private static Pools createPools(Set<Archive> archives,
+    private static PoolImpl createPools(Set<Archive> archives,
             Map<String, List<Entry>> entriesForModule,
             ByteOrder byteOrder,
             BasicImageWriter writer) throws IOException {
@@ -274,7 +272,6 @@
                 return writer.getString(id);
             }
         });
-        PoolImpl files = new PoolImpl();
         for (Archive archive : archives) {
             String mn = archive.moduleName();
             for (Entry entry : entriesForModule.get(mn)) {
@@ -299,7 +296,7 @@
                     try {
                         // Entry.path() contains the kind of file native, conf, bin, ...
                         // It is kept in order for ImageBuilder to put files in right place
-                        files.add(Pool.newImageFile(mn,
+                        resources.add(Pool.newImageFile(mn,
                                 entry.path(), mapImageFileType(entry.type()),
                                 entry.stream(), entry.size()));
                     } catch (Exception ex) {
@@ -308,10 +305,7 @@
                 }
             }
         }
-        Pools pools = new Pools();
-        pools.resources = resources;
-        pools.files = files;
-        return pools;
+        return resources;
     }
 
     private static final int BUF_SIZE = 8192;
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java	Mon Dec 21 14:42:17 2015 +0100
@@ -26,35 +26,28 @@
 
 import java.io.DataOutputStream;
 
-import jdk.tools.jlink.plugins.DefaultImageBuilderProvider;
-import jdk.tools.jlink.plugins.Plugin;
+import jdk.tools.jlink.api.plugin.Plugin;
 import java.io.IOException;
-import java.lang.reflect.Layer;
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import jdk.tools.jlink.plugins.ExecutableImage;
-
-import jdk.tools.jlink.plugins.ImageBuilder;
-import jdk.tools.jlink.plugins.Jlink;
-import jdk.tools.jlink.plugins.Jlink.OrderedPluginConfiguration;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-import jdk.tools.jlink.plugins.TransformerPluginProvider;
-import jdk.tools.jlink.plugins.OrderedPluginProvider;
-import jdk.tools.jlink.plugins.PluginException;
-import jdk.tools.jlink.plugins.PluginProvider;
-import jdk.tools.jlink.plugins.Pool;
-import jdk.tools.jlink.plugins.PostProcessorPlugin;
-import jdk.tools.jlink.plugins.PostProcessorPluginProvider;
+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;
 
 /**
  * Plugins configuration.
  */
 public final class ImagePluginConfiguration {
+
     private static final class OrderedPlugin implements Comparable<OrderedPlugin> {
 
         private final int order;
@@ -96,50 +89,27 @@
         }
     }
 
-    private static final Map<String, Integer> RESOURCES_RANGES = new HashMap<>();
-    private static final List<String> RESOURCES_CATEGORIES = new ArrayList<>();
-
-    private static final List<String> FILES_CATEGORIES = new ArrayList<>();
-    private static final Map<String, Integer> FILES_RANGES = new HashMap<>();
-
-    private static final List<String> POST_PROCESSORS_CATEGORIES = new ArrayList<>();
-    private static final Map<String, Integer> POST_PROCESSORS_RANGES = new HashMap<>();
+    private static final Map<Plugin.CATEGORY, Integer> CATEGORIES_RANGES = new HashMap<>();
+    private static final List<Plugin.CATEGORY> CATEGORIES_ORDER = new ArrayList<>();
 
     private static final int RANGE_LENGTH = 5000;
 
     static {
-        RESOURCES_CATEGORIES.add(TransformerPluginProvider.FILTER);
-        RESOURCES_CATEGORIES.add(TransformerPluginProvider.TRANSFORMER);
-        RESOURCES_CATEGORIES.add(TransformerPluginProvider.SORTER);
-        RESOURCES_CATEGORIES.add(TransformerPluginProvider.COMPRESSOR);
+        CATEGORIES_ORDER.add(Plugin.CATEGORY.FILTER);
+        CATEGORIES_ORDER.add(Plugin.CATEGORY.TRANSFORMER);
+        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);
 
         int end = RANGE_LENGTH;
-        for(String category : RESOURCES_CATEGORIES) {
-            RESOURCES_RANGES.put(category, end);
-            end +=RANGE_LENGTH;
+        for (Plugin.CATEGORY category : CATEGORIES_ORDER) {
+            CATEGORIES_RANGES.put(category, end);
+            end += RANGE_LENGTH;
         }
-
-        FILES_CATEGORIES.add(TransformerPluginProvider.FILTER);
-        FILES_CATEGORIES.add(TransformerPluginProvider.TRANSFORMER);
-        FILES_CATEGORIES.add(TransformerPluginProvider.SORTER);
-        FILES_CATEGORIES.add(TransformerPluginProvider.COMPRESSOR);
-
-        int end2 = RANGE_LENGTH;
-        for(String category : FILES_CATEGORIES) {
-            FILES_RANGES.put(category, end2);
-            end2 +=RANGE_LENGTH;
-        }
-
-        POST_PROCESSORS_CATEGORIES.add(PostProcessorPluginProvider.VERIFIER);
-        POST_PROCESSORS_CATEGORIES.add(PostProcessorPluginProvider.PROCESSOR);
-        POST_PROCESSORS_CATEGORIES.add(PostProcessorPluginProvider.PACKAGER);
-
-        int end3 = RANGE_LENGTH;
-        for (String category : POST_PROCESSORS_CATEGORIES) {
-            POST_PROCESSORS_RANGES.put(category, end3);
-            end3 += RANGE_LENGTH;
-        }
-
     }
 
     private ImagePluginConfiguration() {
@@ -147,141 +117,53 @@
 
     public static ImagePluginStack parseConfiguration(Jlink.PluginsConfiguration plugins)
             throws Exception {
-        return parseConfiguration(null, plugins, Layer.boot(), null);
+        return parseConfiguration(plugins, null);
     }
+
     /*
      * Create a stack of plugins from a a configuration.
      *
      */
-    public static ImagePluginStack parseConfiguration(Path outDir,
-            Jlink.PluginsConfiguration plugins,
-            Layer pluginsLayer,
+    public static ImagePluginStack parseConfiguration(Jlink.PluginsConfiguration plugins,
             String bom)
             throws Exception {
         if (plugins == null) {
             return new ImagePluginStack(bom);
         }
-
         List<OrderedPlugin> resourcePlugins = new ArrayList<>();
-        List<OrderedPlugin> filePlugins = new ArrayList<>();
-        Map<String, OrderedPluginProvider> mtProviders
-                = toMap(ImagePluginProviderRepository.getTransformerProviders(pluginsLayer));
+        List<OrderedPlugin> postProcessingPlugins = new ArrayList<>();
         // Validate stack
-        Map<String, List<Integer>> resources = new HashMap<>();
-        Map<String, List<Integer>> files = new HashMap<>();
-        List<OrderedPlugin> postProcessingPlugins = new ArrayList<>();
-        List<OrderedPluginConfiguration> allPlugins = new ArrayList<>();
-
-        for (Jlink.OrderedPluginConfiguration plug : plugins.getTransformerPluginsConfig()) {
+        Map<Plugin.CATEGORY, List<Integer>> resources = new HashMap<>();
+        List<String> seen = new ArrayList<>();
+        for (Jlink.OrderedPlugin plug : plugins.getPlugins()) {
             if (plug.getIndex() < 0) {
                 throw new Exception("Invalid index " + plug.getIndex() + " for "
-                        + plug.getName());
+                        + plug);
             }
-            OrderedPluginProvider prov = mtProviders.get(plug.getName());
-            if (prov == null) {
-                throw new Exception("Unknown plugin " + plug.getName());
+            if (seen.contains(plug.getPlugin().getName())) {
+                throw new Exception("Plugin " + plug.getPlugin().getName()
+                        + " added more than once to stack ");
             }
-            if (!isImageFileProvider(prov) && !isResourceProvider(prov)) {
-                throw new Exception("Invalid provider type " + prov);
-            }
-            Map<String, List<Integer>> map = isResourceProvider(prov) ? resources : files;
-            List<Integer> lst = map.get(prov.getCategory());
+            seen.add(plug.getPlugin().getName());
+            List<Integer> lst = resources.get(plug.getCategory());
             if (lst == null) {
                 lst = new ArrayList<>();
-                map.put(prov.getCategory(), lst);
+                resources.put(plug.getCategory(), lst);
             }
-            int index;
-            if (isResourceProvider(prov)) {
-                index = getAbsoluteIndex(plug.getIndex(),
-                        prov.getCategory(),
-                        plug.isAbsoluteIndex(),
-                        RESOURCES_RANGES);
-            } else {
-                index = getAbsoluteIndex(plug.getIndex(),
-                        prov.getCategory(),
-                        plug.isAbsoluteIndex(),
-                        FILES_RANGES);
-            }
+            int index = getAbsoluteIndex(plug.getIndex(),
+                    plug.getCategory(),
+                    plug.isAbsoluteIndex(),
+                    CATEGORIES_RANGES);
             if (lst.contains(index)) {
-                throw new Exception(plug.getName() + ", a Plugin is already located at index " + index);
+                throw new Exception(plug.getPlugin().getName()
+                        + ", a Plugin is already located at index " + index);
             }
             lst.add(index);
-            allPlugins.add(plug);
-        }
-
-        Map<String, List<Integer>> postprocessors = new HashMap<>();
-        Map<String, OrderedPluginProvider> ppProviders
-                = toMap(ImagePluginProviderRepository.getPostProcessingProviders(pluginsLayer));
-        for (Jlink.OrderedPluginConfiguration plug : plugins.getPostProcessorPluginsConfig()) {
-            if (plug.getIndex() < 0) {
-                throw new Exception("Invalid index " + plug.getIndex() + " for "
-                        + plug.getName());
-            }
-            OrderedPluginProvider prov = ppProviders.get(plug.getName());
-            if (prov == null) {
-                throw new Exception("Unknown plugin " + plug.getName());
-            }
-
-            if (!isPostProcessingProvider(prov)) {
-                throw new Exception("Invalid provider type " + prov);
-            }
-
-            List<Integer> lst = postprocessors.get(prov.getCategory());
-            if (lst == null) {
-                lst = new ArrayList<>();
-                postprocessors.put(prov.getCategory(), lst);
-            }
-            int index = getAbsoluteIndex(plug.getIndex(),
-                    prov.getCategory(),
-                    plug.isAbsoluteIndex(),
-                    POST_PROCESSORS_RANGES);
-
-            if (lst.contains(index)) {
-                throw new Exception(plug.getName() + ", a Plugin is already located at index " + index);
-            }
-            lst.add(index);
-            allPlugins.add(plug);
-        }
-
-        List<String> seen = new ArrayList<>();
-        for (OrderedPluginConfiguration prop : allPlugins) {
-            OrderedPluginProvider prov = ppProviders.get(prop.getName());
-            if (prov == null) {
-                prov = mtProviders.get(prop.getName());
-            }
-            if (!prov.isFunctional()) {
-                throw new Exception("Provider " + prov.getName() + " is not functional");
-            }
-            if (seen.contains(prov.getName())) {
-                throw new Exception("Plugin " + prov.getName()
-                        + " added more than once to stack ");
-            }
-            seen.add(prov.getName());
-            if (isResourceProvider(prov)) {
-                int index = getAbsoluteIndex(prop.getIndex(),
-                        prov.getCategory(),
-                        prop.isAbsoluteIndex(),
-                        RESOURCES_RANGES);
-                resourcePlugins.addAll(createOrderedPlugins(index, prop.getName(),
-                        prop.getConfig(), pluginsLayer));
+            OrderedPlugin p = new OrderedPlugin(index, plug.getPlugin());
+            if (Utils.isPostProcessor(plug.getCategory())) {
+                postProcessingPlugins.add(p);
             } else {
-                if (isImageFileProvider(prov)) {
-                    int index = getAbsoluteIndex(prop.getIndex(),
-                            prov.getCategory(),
-                            prop.isAbsoluteIndex(),
-                            FILES_RANGES);
-                    filePlugins.addAll(createOrderedPlugins(index, prop.getName(),
-                            prop.getConfig(), pluginsLayer));
-                } else {
-                    if (isPostProcessingProvider(prov)) {
-                        int index = getAbsoluteIndex(prop.getIndex(),
-                                prov.getCategory(),
-                                prop.isAbsoluteIndex(),
-                                POST_PROCESSORS_RANGES);
-                        postProcessingPlugins.addAll(createOrderedPlugins(index, prop.getName(),
-                                prop.getConfig(), pluginsLayer));
-                    }
-                }
+                resourcePlugins.add(p);
             }
         }
 
@@ -297,12 +179,10 @@
             throw new IOException("Unknown last plugin "
                     + plugins.getLastSorterPluginName());
         }
-        List<TransformerPlugin> filePluginsList = toPluginsList(filePlugins);
-
         List<PostProcessorPlugin> postprocessorPluginsList = toPluginsList(postProcessingPlugins);
 
-        ImageBuilder builder;
-        if (outDir == null) {
+        ImageBuilder builder = plugins.getImageBuilder();
+        if (builder == null) {
             // This should be the case for jimage only creation or post-install.
             builder = new ImageBuilder() {
 
@@ -317,57 +197,19 @@
                 }
 
                 @Override
-                public void storeJavaLauncherOptions(ExecutableImage image, List<String> args) {
-                    throw new PluginException("No directory setup to store files");
-                }
-
-                @Override
-                public String getName() {
-                    return "";
-                }
-
-                @Override
                 public void storeFiles(Pool files, List<Pool.ModuleData> removed,
-                        String bom, Pool retriever) {
+                        String bom) {
                     throw new PluginException("No directory setup to store files");
                 }
             };
-        } else {
-            String builderName = plugins.getImageBuilder() == null
-                    ? DefaultImageBuilderProvider.NAME : plugins.getImageBuilder().getName();
-            Map<String, Object> builderConfig = plugins.getImageBuilder() == null
-                    ? Collections.emptyMap() : plugins.getImageBuilder().getConfig();
-            builder = ImagePluginProviderRepository.newImageBuilder(builderConfig,
-                    outDir,
-                    builderName,
-                    pluginsLayer);
         }
+
         return new ImagePluginStack(builder, resourcePluginsList,
-                lastSorter, filePluginsList, postprocessorPluginsList, bom);
+                lastSorter, postprocessorPluginsList, bom);
     }
 
-    private static boolean isResourceProvider(OrderedPluginProvider prov) {
-        if (prov instanceof TransformerPluginProvider) {
-            return ((TransformerPluginProvider) prov).getType().
-                    equals(TransformerPluginProvider.Type.RESOURCE_PLUGIN);
-        }
-        return false;
-    }
-
-    private static boolean isImageFileProvider(OrderedPluginProvider prov) {
-        if (prov instanceof TransformerPluginProvider) {
-            return ((TransformerPluginProvider) prov).getType().
-                    equals(TransformerPluginProvider.Type.IMAGE_FILE_PLUGIN);
-        }
-        return false;
-    }
-
-    private static boolean isPostProcessingProvider(PluginProvider prov) {
-        return prov instanceof PostProcessorPluginProvider;
-    }
-
-    private static int getAbsoluteIndex(int index, String category,
-            boolean absolute, Map<String, Integer> ranges) throws Exception {
+    private static int getAbsoluteIndex(int index, Plugin.CATEGORY category,
+            boolean absolute, Map<Plugin.CATEGORY, Integer> ranges) throws Exception {
 
         if (absolute) {
             return index;
@@ -380,38 +222,17 @@
         throw new Exception("Can't compute index, no category");
     }
 
-    private static Map<String, OrderedPluginProvider> toMap(List<? extends OrderedPluginProvider> providers) {
-        Map<String, OrderedPluginProvider> ret = new HashMap<>();
-        for (OrderedPluginProvider prov : providers) {
-            ret.put(prov.getName(), prov);
-        }
-        return ret;
-    }
-
     /**
      * Retrieve the range array (index 0 is range start, index 1 is range end)
      * associated to a category.
-     * @param provider The provider for which the range is wanted.
+     *
+     * @param category The category for which the range is wanted.
      * @return The range or null if the provider category is unknown.
      */
-    public static Integer[] getRange(OrderedPluginProvider provider) {
-        Map<String, Integer> ranges = null;
-        if (isResourceProvider(provider)) {
-            ranges = RESOURCES_RANGES;
-        } else if (isImageFileProvider(provider)) {
-            ranges = FILES_RANGES;
-        } else if (isPostProcessingProvider(provider)) {
-            ranges = POST_PROCESSORS_RANGES;
-        } else {
-            throw new IllegalArgumentException("Unknown provider type");
-        }
-        return getRange(provider.getCategory(), ranges);
-    }
-
-    private static Integer[] getRange(String category, Map<String, Integer> ranges) {
+    public static Integer[] getRange(CATEGORY category) {
         Objects.requireNonNull(category);
         Integer[] range = null;
-        Integer i = ranges.get(category);
+        Integer i = CATEGORIES_RANGES.get(category);
         if (i != null) {
             range = new Integer[2];
             range[0] = i;
@@ -420,33 +241,6 @@
         return range;
     }
 
-    private static List<OrderedPlugin> createOrderedPlugins(int index,
-            String name, Map<String, Object> config, Layer pluginsLayer) throws PluginException {
-        List<? extends Plugin> plugins = null;
-        PluginProvider prov = ImagePluginProviderRepository.getTransformerPluginProvider(name, pluginsLayer);
-        if (prov != null) {
-            plugins = prov.newPlugins(config);
-        }
-        if (plugins == null) {
-            prov = ImagePluginProviderRepository.getPostProcessingPluginProvider(name, pluginsLayer);
-            if (prov != null) {
-                plugins = prov.newPlugins(config);
-            }
-        }
-        if (prov == null) {
-            throw new PluginException("Provider not found for " + name);
-        }
-        if (plugins == null) {
-            throw new PluginException("Null plugins for " + name);
-        }
-        List<OrderedPlugin> ordered = new ArrayList<>();
-        for (Plugin plugin : plugins) {
-            ordered.add(new OrderedPlugin(index, plugin));
-            index = index + 1;
-        }
-        return ordered;
-    }
-
     private static <T> List<T> toPluginsList(List<OrderedPlugin> lst)
             throws IOException {
         Collections.sort(lst);
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginProviderRepository.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,254 +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 jdk.tools.jlink.plugins.PluginProvider;
-import java.lang.reflect.Layer;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.ServiceLoader;
-import jdk.tools.jlink.plugins.ImageBuilder;
-import jdk.tools.jlink.plugins.ImageBuilderProvider;
-import jdk.tools.jlink.plugins.PluginException;
-import jdk.tools.jlink.plugins.PostProcessorPlugin;
-import jdk.tools.jlink.plugins.PostProcessorPluginProvider;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-import jdk.tools.jlink.plugins.TransformerPluginProvider;
-
-/**
- *
- * Plugin Providers repository. Plugin Providers are
- * retrieved thanks to the ServiceLoader mechanism.
- */
-public final class ImagePluginProviderRepository {
-
-    private ImagePluginProviderRepository() {
-    }
-
-    private static final Map<String, PluginProvider> registeredProviders = new HashMap<>();
-
-    /**
-     * Retrieves the provider associated to the passed name. If multiple providers
-     * exist for the same name,
-     * then an exception is thrown.
-     * @param name The plugin provider name.
-     * @param pluginsLayer
-     * @return A provider or null if not found.
-     */
-    public static TransformerPluginProvider getTransformerPluginProvider(String name,
-            Layer pluginsLayer) {
-        return getPluginProvider(TransformerPluginProvider.class, name, pluginsLayer);
-    }
-
-    /**
-     * Retrieves the provider associated to the passed name. If multiple providers
-     * exist for the same name,
-     * then an exception is thrown.
-     * @param name The plugin provider name.
-     * @param pluginsLayer
-     * @return A provider or null if not found.
-     */
-    public static ImageBuilderProvider getImageBuilderProvider(String name,
-            Layer pluginsLayer) {
-        return getPluginProvider(ImageBuilderProvider.class, name, pluginsLayer);
-    }
-
-    /**
-     * Retrieves the provider associated to the passed name. If multiple providers
-     * exist for the same name,
-     * then an exception is thrown.
-     * @param name The plugin provider name.
-     * @param pluginsLayer
-     * @return A provider or null if not found.
-     */
-    public static PostProcessorPluginProvider getPostProcessingPluginProvider(String name,
-            Layer pluginsLayer) {
-        return getPluginProvider(PostProcessorPluginProvider.class, name, pluginsLayer);
-    }
-
-    /**
-     * Build module transformer plugins for the passed name.
-     *
-     * @param config Optional config.
-     * @param name Non null name.
-     * @param pluginsLayer
-     * @return An array of plugins.
-     */
-    public static List<? extends TransformerPlugin> newTransformerPlugins(Map<String, Object> config, String name,
-            Layer pluginsLayer) {
-        Objects.requireNonNull(name);
-        Objects.requireNonNull(pluginsLayer);
-        TransformerPluginProvider fact = getTransformerPluginProvider(name, pluginsLayer);
-        if(fact != null) {
-            return fact.newPlugins(config);
-        }
-        return null;
-    }
-
-    /**
-     * Build post processing plugins for the passed name.
-     *
-     * @param config Optional config.
-     * @param name Non null name.
-     * @param pluginsLayer
-     * @return An array of plugins.
-     */
-    public static List<? extends PostProcessorPlugin> newPostProcessingPlugins(Map<String, Object> config, String name,
-            Layer pluginsLayer) {
-        Objects.requireNonNull(name);
-        Objects.requireNonNull(pluginsLayer);
-        PostProcessorPluginProvider fact = getPostProcessingPluginProvider(name, pluginsLayer);
-        if(fact != null) {
-            return fact.newPlugins(config);
-        }
-        return null;
-    }
-
-    /**
-     * Explicit registration of a provider in the repository. Used by unit tests
-     *
-     * @param provider The provider to register.
-     */
-    public synchronized static void registerPluginProvider(PluginProvider provider) {
-        Objects.requireNonNull(provider);
-        registeredProviders.put(provider.getName(), provider);
-    }
-
-    /**
-     * Explicit unregistration of a provider in the repository. Used by unit
-     * tests
-     *
-     * @param name Provider name
-     */
-    public synchronized static void unregisterPluginProvider(String name) {
-        Objects.requireNonNull(name);
-        registeredProviders.remove(name);
-    }
-
-    public static ImageBuilder newImageBuilder(Map<String, Object> config, Path outputDir,
-            String name, Layer pluginsLayer) {
-        Objects.requireNonNull(config);
-        Objects.requireNonNull(outputDir);
-        Objects.requireNonNull(name);
-        Objects.requireNonNull(pluginsLayer);
-        List<ImageBuilderProvider> providers = getImageBuilderProviders(pluginsLayer);
-        List<? extends ImageBuilder> builder = null;
-        for (ImageBuilderProvider fact : providers) {
-            if (fact.getName().equals(name)) {
-                if(builder != null) {
-                     throw new PluginException("Multiple ImageBuilderProvider "
-                            + "for the name " + name);
-                }
-                Map<String, Object> all = new HashMap<>();
-                all.putAll(config);
-                all.put(ImageBuilderProvider.IMAGE_PATH_KEY, outputDir);
-                builder = fact.newPlugins(all);
-            }
-        }
-        if (builder == null || builder.isEmpty()) {
-            throw new PluginException("Image builder not found for " + name);
-        }
-        return builder.get(0);
-    }
-
-    /**
-     * The image builder providers accessible in the current context.
-     *
-     * @param pluginsLayer
-     * @return The list of image builder provider.
-     */
-    public static List<ImageBuilderProvider> getImageBuilderProviders(Layer pluginsLayer) {
-        return getPluginProviders(ImageBuilderProvider.class, pluginsLayer);
-    }
-
-    /**
-     * The module transformers accessible in the current context.
-     *
-     * @param pluginsLayer
-     * @return The list of module transformer.
-     */
-    public static List<TransformerPluginProvider> getTransformerProviders(Layer pluginsLayer) {
-        return getPluginProviders(TransformerPluginProvider.class, pluginsLayer);
-    }
-
-    /**
-     * The post processors accessible in the current context.
-     *
-     * @param pluginsLayer
-     * @return The list of post processors.
-     */
-    public static List<PostProcessorPluginProvider> getPostProcessingProviders(Layer pluginsLayer) {
-        return getPluginProviders(PostProcessorPluginProvider.class, pluginsLayer);
-    }
-
-    private static <T extends PluginProvider> T getPluginProvider(Class<T> clazz, String name,
-            Layer pluginsLayer) {
-        Objects.requireNonNull(name);
-        Objects.requireNonNull(pluginsLayer);
-        @SuppressWarnings("unchecked")
-        T provider = null;
-        List<T> javaProviders = getPluginProviders(clazz, pluginsLayer);
-        for(T factory : javaProviders) {
-            if (factory.getName().equals(name)) {
-                if (provider != null) {
-                    throw new PluginException("Multiple plugin "
-                            + "for the name " + name);
-                }
-                provider = factory;
-            }
-        }
-        return provider;
-    }
-
-    /**
-     * The post processors accessible in the current context.
-     *
-     * @param pluginsLayer
-     * @return The list of post processors.
-     */
-    private static <T extends PluginProvider> List<T> getPluginProviders(Class<T> clazz, Layer pluginsLayer) {
-        Objects.requireNonNull(pluginsLayer);
-        List<T> factories = new ArrayList<>();
-        Iterator<T> providers
-                = ServiceLoader.load(pluginsLayer, clazz).iterator();
-        while (providers.hasNext()) {
-            factories.add(providers.next());
-        }
-        registeredProviders.values().stream().forEach((fact) -> {
-            if (clazz.isInstance(fact)) {
-                @SuppressWarnings("unchecked")
-                T trans = (T) fact;
-                factories.add(trans);
-            }
-        });
-        return factories;
-    }
-
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java	Mon Dec 21 14:42:17 2015 +0100
@@ -26,9 +26,10 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.DataOutputStream;
-import jdk.tools.jlink.plugins.Plugin;
+import jdk.tools.jlink.api.plugin.Plugin;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.module.ModuleDescriptor;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -42,27 +43,27 @@
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import jdk.internal.jimage.decompressor.Decompressor;
-import jdk.tools.jlink.plugins.ExecutableImage;
-import jdk.tools.jlink.plugins.ImageBuilder;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-import jdk.tools.jlink.plugins.PluginException;
-import jdk.tools.jlink.plugins.Pool;
-import jdk.tools.jlink.plugins.Pool.ModuleData;
-import jdk.tools.jlink.plugins.PostProcessorPlugin;
+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;
 
 /**
- * Plugins Stack. Plugins entry point to apply transformations onto resources and files.
+ * Plugins Stack. Plugins entry point to apply transformations onto resources
+ * and files.
  */
 public final class ImagePluginStack {
 
     public interface ImageProvider {
+
         ExecutableImage retrieve(ImagePluginStack stack) throws IOException;
-
-        public void storeLauncherArgs(ImagePluginStack stack, ExecutableImage image,
-                List<String> args) throws IOException;
     }
 
-    private static final class OrderedResourcePool extends PoolImpl {
+    public static final class OrderedResourcePool extends PoolImpl {
 
         private final List<ModuleData> orderedList = new ArrayList<>();
 
@@ -74,7 +75,6 @@
          * Add a resource.
          *
          * @param resource The Resource to add.
-         * @throws java.lang.Exception If the pool is read only.
          */
         @Override
         public void add(ModuleData resource) {
@@ -160,8 +160,7 @@
     }
 
     private final Plugin lastSorter;
-    private final List<TransformerPlugin> resourcePlugins = new ArrayList<>();
-    private final List<TransformerPlugin> filePlugins = new ArrayList<>();
+    private final List<TransformerPlugin> contentPlugins = new ArrayList<>();
     private final List<PostProcessorPlugin> postProcessingPlugins = new ArrayList<>();
     private final List<ResourcePrevisitor> resourcePrevisitors = new ArrayList<>();
 
@@ -170,29 +169,23 @@
     private final String bom;
 
     public ImagePluginStack(String bom) {
-        this(null, Collections.emptyList(), null, Collections.emptyList(),
-                Collections.emptyList(), null);
+        this(null, Collections.emptyList(), null,
+                Collections.emptyList(), bom);
     }
 
     public ImagePluginStack(ImageBuilder imageBuilder,
-            List<TransformerPlugin> resourcePlugins,
+            List<TransformerPlugin> contentPlugins,
             Plugin lastSorter,
-            List<TransformerPlugin> filePlugins,
             List<PostProcessorPlugin> postprocessingPlugins,
             String bom) {
-        Objects.requireNonNull(resourcePlugins);
-        Objects.requireNonNull(filePlugins);
+        Objects.requireNonNull(contentPlugins);
         this.lastSorter = lastSorter;
-        for (TransformerPlugin p : resourcePlugins) {
+        for (TransformerPlugin p : contentPlugins) {
             Objects.requireNonNull(p);
             if (p instanceof ResourcePrevisitor) {
                 resourcePrevisitors.add((ResourcePrevisitor) p);
             }
-            this.resourcePlugins.add(p);
-        }
-        for (TransformerPlugin p : filePlugins) {
-            Objects.requireNonNull(p);
-            this.filePlugins.add(p);
+            this.contentPlugins.add(p);
         }
         for (PostProcessorPlugin p : postprocessingPlugins) {
             Objects.requireNonNull(p);
@@ -204,22 +197,14 @@
 
     public void operate(ImageProvider provider) throws Exception {
         ExecutableImage img = provider.retrieve(this);
-        List<String> arguments;
-        // Could be autocloseable but not right behavior
-        // with InterruptedException
-        ProcessingManagerImpl manager = new ProcessingManagerImpl(img);
-        arguments = new ArrayList<>();
-        try {
-            for (PostProcessorPlugin plugin : postProcessingPlugins) {
-                List<String> lst = plugin.process(manager);
-                if (lst != null) {
-                    arguments.addAll(lst);
-                }
+        List<String> arguments = new ArrayList<>();
+        for (PostProcessorPlugin plugin : postProcessingPlugins) {
+            List<String> lst = plugin.process(img);
+            if (lst != null) {
+                arguments.addAll(lst);
             }
-        } finally {
-            manager.close();
         }
-        provider.storeLauncherArgs(this, img, arguments);
+        img.storeLaunchArgs(arguments);
     }
 
     public DataOutputStream getJImageFileOutputStream() throws IOException {
@@ -231,8 +216,8 @@
     }
 
     /**
-     * Resource Plugins stack entry point. All resources are going through all the
-     * plugins.
+     * Resource Plugins stack entry point. All resources are going through all
+     * the plugins.
      *
      * @param resources The set of resources to visit
      * @return The result of the visit.
@@ -259,19 +244,18 @@
 
         PoolImpl current = resources;
         List<Pool.ModuleData> frozenOrder = null;
-        for (TransformerPlugin p : resourcePlugins) {
+        for (TransformerPlugin p : contentPlugins) {
             current.setReadOnly();
             PoolImpl output = null;
             if (p == lastSorter) {
                 if (frozenOrder != null) {
-                    throw new Exception("Oder of resources is already frozen. Plugin "
+                    throw new Exception("Order of resources is already frozen. Plugin "
                             + p.getName() + " is badly located");
                 }
                 // Create a special Resource pool to compute the indexes.
                 output = new OrderedResourcePool(current.getByteOrder(),
                         resources.getStringTable());
-            } else {
-                // If we have an order, inject it
+            } else {// If we have an order, inject it
                 if (frozenOrder != null) {
                     output = new CheckOrderResourcePool(current.getByteOrder(),
                             frozenOrder, resources.getStringTable());
@@ -295,10 +279,49 @@
     }
 
     private class LastPool extends Pool {
+        private class LastModule implements Module {
 
+            private final Module module;
+
+            LastModule(Module module) {
+                this.module = module;
+            }
+
+            @Override
+            public String getName() {
+                return module.getName();
+            }
+
+            @Override
+            public ModuleData get(String path) {
+                ModuleData d = module.get(path);
+                return getUncompressed(d);
+            }
+
+            @Override
+            public ModuleDescriptor getDescriptor() {
+                return module.getDescriptor();
+            }
+
+            @Override
+            public void add(ModuleData data) {
+                throw new PluginException("pool is readonly");
+            }
+
+            @Override
+            public Set<String> getAllPackages() {
+                return module.getAllPackages();
+            }
+
+            @Override
+            public String toString() {
+                return getName();
+            }
+        }
         private final PoolImpl pool;
         Decompressor decompressor = new Decompressor();
         Collection<ModuleData> content;
+
         LastPool(PoolImpl pool) {
             this.pool = pool;
         }
@@ -314,6 +337,35 @@
         }
 
         /**
+         * Retrieves the module of the provided name.
+         *
+         * @param name The module name
+         * @return the module or null if the module doesn't exist.
+         */
+        @Override
+        public Module getModule(String name) {
+            Module module = pool.getModule(name);
+            if (module != null) {
+                module = new LastModule(module);
+            }
+            return module;
+        }
+
+        /**
+         * The collection of modules contained in this pool.
+         *
+         * @return The collection of modules.
+         */
+        @Override
+        public Collection<Module> getModules() {
+            List<Module> modules = new ArrayList<>();
+            for (Module m : pool.getModules()) {
+                modules.add(new LastModule(m));
+            }
+            return modules;
+        }
+
+        /**
          * Get all resources contained in this pool instance.
          *
          * @return The collection of resources;
@@ -381,48 +433,31 @@
         }
 
         @Override
-        public Map<String, Set<String>> getModulePackages() {
-            return pool.getModulePackages();
-        }
-
-        @Override
         public void addTransformedResource(ModuleData original, InputStream transformed, long length) {
             pool.addTransformedResource(original, transformed, length);
         }
     }
 
     /**
-     * ImageFile Plugins stack entry point. All files are going through all the
-     * plugins.
+     * Make the imageBuilder to store files.
      *
-     * @param files
-     * @param resources
+     * @param original
+     * @param transformed
      * @param writer
-     * @throws Exception
+     * @throws java.lang.Exception
      */
-    public void storeFiles(PoolImpl files, PoolImpl resources,
+    public void storeFiles(PoolImpl original, PoolImpl transformed,
             BasicImageWriter writer)
             throws Exception {
-        Objects.requireNonNull(files);
-        PoolImpl current = files;
-        for (TransformerPlugin p : filePlugins) {
-            current.setReadOnly();
-            PoolImpl output = new PoolImpl();
-            p.visit(current, output);
-            if (output.isEmpty()) {
-                throw new Exception("Invalid files pool for plugin " + p);
-            }
-            current = output;
-        }
-        current.setReadOnly();
+        Objects.requireNonNull(original);
         // Build the diff between input and output
         List<ModuleData> removed = new ArrayList<>();
-        for (ModuleData f : files.getContent()) {
-            if (!current.contains(f)) {
+        for (ModuleData f : transformed.getContent()) {
+            if (!f.getType().equals(ModuleDataType.CLASS_OR_RESOURCE) && !transformed.contains(f)) {
                 removed.add(f);
             }
         }
-        imageBuilder.storeFiles(current, removed, bom, new LastPool(resources));
+        imageBuilder.storeFiles(new LastPool(transformed), removed, bom);
     }
 
     public ExecutableImage getExecutableImage() throws IOException {
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JvmHandler.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JvmHandler.java	Mon Dec 21 14:42:17 2015 +0100
@@ -36,8 +36,8 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
-import jdk.tools.jlink.plugins.Pool;
-import jdk.tools.jlink.plugins.Pool.ModuleData;
+import jdk.tools.jlink.api.plugin.transformer.Pool;
+import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleData;
 
 /**
  * A class to deal with JVM platforms
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/PluginRepository.java	Mon Dec 21 14:42:17 2015 +0100
@@ -0,0 +1,148 @@
+/*
+ * 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.lang.reflect.Layer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+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;
+
+/**
+ *
+ * Plugin Providers repository. Plugin Providers are
+ * retrieved thanks to the ServiceLoader mechanism.
+ */
+public final class PluginRepository {
+
+    private PluginRepository() {
+    }
+
+    private static final Map<String, Plugin> registeredPlugins = new HashMap<>();
+
+    /**
+     * Retrieves the plugin associated to the passed name. If multiple providers
+     * exist for the same name,
+     * then an exception is thrown.
+     * @param name The plugin provider name.
+     * @param pluginsLayer
+     * @return A provider or null if not found.
+     */
+    public static Plugin getPlugin(String name,
+            Layer pluginsLayer) {
+        return getPlugin(Plugin.class, name, pluginsLayer);
+    }
+
+    /**
+     * Build plugin for the passed name.
+     *
+     * @param config Optional config.
+     * @param name Non null name.
+     * @param pluginsLayer
+     * @return A plugin.
+     */
+    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);
+        return plugin;
+    }
+
+    /**
+     * Explicit registration of a plugin in the repository. Used by unit tests
+     * @param plugin The plugin to register.
+     */
+    public synchronized static void registerPlugin(Plugin plugin) {
+        Objects.requireNonNull(plugin);
+        registeredPlugins.put(plugin.getName(), plugin);
+    }
+
+    /**
+     * Explicit unregistration of a plugin in the repository. Used by unit
+     * tests
+     *
+     * @param name Plugin name
+     */
+    public synchronized static void unregisterPlugin(String name) {
+        Objects.requireNonNull(name);
+        registeredPlugins.remove(name);
+    }
+
+    public static List<Plugin> getPlugins(Layer pluginsLayer) {
+        return getPlugins(Plugin.class, pluginsLayer);
+    }
+
+    private static <T extends Plugin> T getPlugin(Class<T> clazz, String name,
+            Layer pluginsLayer) {
+        Objects.requireNonNull(name);
+        Objects.requireNonNull(pluginsLayer);
+        @SuppressWarnings("unchecked")
+        T provider = null;
+        List<T> javaProviders = getPlugins(clazz, pluginsLayer);
+        for(T factory : javaProviders) {
+            if (factory.getName().equals(name)) {
+                if (provider != null) {
+                    throw new PluginException("Multiple plugin "
+                            + "for the name " + name);
+                }
+                provider = factory;
+            }
+        }
+        return provider;
+    }
+
+    /**
+     * The post processors accessible in the current context.
+     *
+     * @param pluginsLayer
+     * @return The list of post processors.
+     */
+    private static <T extends Plugin> List<T> getPlugins(Class<T> clazz, Layer pluginsLayer) {
+        Objects.requireNonNull(pluginsLayer);
+        List<T> factories = new ArrayList<>();
+        Iterator<T> providers
+                = ServiceLoader.load(pluginsLayer, clazz).iterator();
+        while (providers.hasNext()) {
+            factories.add(providers.next());
+        }
+        registeredPlugins.values().stream().forEach((fact) -> {
+            if (clazz.isInstance(fact)) {
+                @SuppressWarnings("unchecked")
+                T trans = (T) fact;
+                factories.add(trans);
+            }
+        });
+        return factories;
+    }
+
+}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/PoolImpl.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/PoolImpl.java	Mon Dec 21 14:42:17 2015 +0100
@@ -30,7 +30,7 @@
 import java.nio.ByteOrder;
 import java.util.Objects;
 import jdk.internal.jimage.decompressor.CompressedResourceHeader;
-import jdk.tools.jlink.plugins.Pool;
+import jdk.tools.jlink.api.plugin.transformer.Pool;
 
 /**
  * Pool of module data.
@@ -87,6 +87,20 @@
         });
     }
 
+    public PoolImpl(ByteOrder order) {
+        this(order, new StringTable() {
+
+            @Override
+            public int addString(String str) {
+                return -1;
+            }
+            @Override
+            public String getString(int id) {
+                return null;
+            }
+        });
+    }
+
     public PoolImpl(ByteOrder order, StringTable table) {
         super(order);
         this.table = table;
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ProcessingManagerImpl.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,276 +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.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.FileVisitResult;
-import java.nio.file.FileVisitor;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.FutureTask;
-import jdk.tools.jlink.plugins.ExecutableImage;
-import jdk.tools.jlink.plugins.PluginException;
-import jdk.tools.jlink.plugins.Sessions;
-
-/**
- * A process manager.
- */
-class ProcessingManagerImpl implements Sessions {
-
-    private static final class StreamReader implements Runnable {
-
-        private final InputStream in;
-        private final OutputStream out;
-        private final FutureTask<Void> processingTask = new FutureTask<>(this, null);
-
-        private StreamReader(InputStream in, OutputStream out) {
-            this.in = in;
-            this.out = out;
-        }
-
-        @Override
-        public void run() {
-            try (BufferedInputStream is = new BufferedInputStream(in)) {
-                byte[] buf = new byte[1024];
-                int len = 0;
-                while ((len = is.read(buf)) > 0 && !Thread.interrupted()) {
-                    out.write(buf, 0, len);
-                }
-            } catch (IOException e) {
-                e.printStackTrace();
-            } finally {
-                try {
-                    out.flush();
-                } catch (IOException e) {
-                }
-                try {
-                    in.close();
-                } catch (IOException e) {
-                }
-            }
-        }
-
-        final public Future<Void> process() {
-            Thread t = new Thread(() -> {
-                processingTask.run();
-            });
-            t.setDaemon(true);
-            t.start();
-
-            return processingTask;
-        }
-    }
-
-    public static final class RunningProcessImpl implements RunningProcess {
-
-        private final ByteArrayOutputStream stderrBuffer = new ByteArrayOutputStream();
-        private final ByteArrayOutputStream stdoutBuffer = new ByteArrayOutputStream();
-        private final Future<Void> outTask;
-        private final Future<Void> errTask;
-        private final Process p;
-
-        @Override
-        public int getExitCode() throws InterruptedException {
-            return p.waitFor();
-        }
-
-        @Override
-        public String getStdout() throws InterruptedException, ExecutionException {
-            outTask.get();
-            return new String(stdoutBuffer.toByteArray(), StandardCharsets.UTF_8);
-
-        }
-
-        @Override
-        public String getStderr() throws InterruptedException, ExecutionException {
-            errTask.get();
-            return new String(stderrBuffer.toByteArray(), StandardCharsets.UTF_8);
-        }
-
-        @Override
-        public void kill() {
-            p.destroyForcibly();
-        }
-
-        private RunningProcessImpl(ProcessBuilder builder) throws IOException {
-            p = builder.start();
-            StreamReader outReader = new StreamReader(p.getInputStream(), stdoutBuffer);
-            StreamReader errReader = new StreamReader(p.getErrorStream(), stderrBuffer);
-            outTask = outReader.process();
-            errTask = errReader.process();
-        }
-
-        @Override
-        public Process getProcess() {
-            return p;
-        }
-
-        void close() {
-            if (p.isAlive()) {
-                kill();
-            }
-        }
-    }
-
-    private final class ProcessingSessionImpl implements Session {
-
-        private boolean closed;
-        private final Path dir;
-        private final String name;
-        private final List<RunningProcessImpl> processes = new ArrayList<>();
-
-        private ProcessingSessionImpl(String name, Path dir) {
-            this.name = name;
-            this.dir = dir;
-        }
-
-        @Override
-        public String getName() {
-            return name;
-        }
-
-        @Override
-        public Path getStorage() {
-            return dir;
-        }
-
-        @Override
-        public RunningProcess newRunningProcess(ProcessBuilder builder) {
-            try {
-                ProcessBuilder pb = builder.directory(dir.toFile());
-                RunningProcessImpl p = new RunningProcessImpl(pb);
-                processes.add(p);
-                return p;
-            } catch (IOException ex) {
-                throw new PluginException(ex);
-            }
-        }
-
-        @Override
-        public RunningProcess newImageProcess(List<String> args) {
-            try {
-                RunningProcessImpl p = new RunningProcessImpl(newJavaProcessBuilder(args));
-                processes.add(p);
-                return p;
-            } catch (IOException ex) {
-                throw new PluginException(ex);
-            }
-        }
-
-        @Override
-        public void close() throws IOException {
-            if (closed) {
-                throw new IllegalArgumentException("Session " + name
-                        + " already closed");
-            }
-            closed = true;
-            for (RunningProcessImpl r : processes) {
-                r.close();
-            }
-        }
-
-        private ProcessBuilder newJavaProcessBuilder(List<String> args) throws IOException {
-            List<String> javaArgs = new ArrayList<>();
-            javaArgs.addAll(img.getExecutionArgs());
-            javaArgs.addAll(args);
-            ProcessBuilder builder = new ProcessBuilder().command(javaArgs).
-                    directory(dir.toFile());
-            return builder;
-        }
-
-    }
-
-    private final Path tmp;
-    private final Map<String, Session> sessions = new HashMap<>();
-    private final ExecutableImage img;
-
-    ProcessingManagerImpl(ExecutableImage img) throws IOException {
-        Objects.requireNonNull(img);
-        this.img = img;
-        String dir = "jlink_tmp_" + System.currentTimeMillis();
-        Path dirPath = img.getHome().resolve(dir);
-        tmp = Files.createDirectory(dirPath);
-    }
-
-    @Override
-    public Session newSession(String name) {
-        try {
-            String id = name.replaceAll(" ", "_") + System.currentTimeMillis();
-            Path dirPath = tmp.resolve(id);
-            Files.createDirectory(dirPath);
-            return new ProcessingSessionImpl(name, dirPath);
-        } catch (IOException ex) {
-            throw new PluginException(ex);
-        }
-    }
-
-    @Override
-    public ExecutableImage getImage() {
-        return img;
-    }
-
-    public void close() throws IOException {
-        for (Session session : sessions.values()) {
-            session.close();
-        }
-        Files.walkFileTree(tmp, new FileVisitor<Path>() {
-
-            @Override
-            public FileVisitResult preVisitDirectory(Path t, BasicFileAttributes bfa) throws IOException {
-                return FileVisitResult.CONTINUE;
-            }
-
-            @Override
-            public FileVisitResult visitFile(Path t, BasicFileAttributes bfa) throws IOException {
-                Files.deleteIfExists(t);
-                return FileVisitResult.CONTINUE;
-            }
-
-            @Override
-            public FileVisitResult visitFileFailed(Path t, IOException ioe) throws IOException {
-                return FileVisitResult.CONTINUE;
-            }
-
-            @Override
-            public FileVisitResult postVisitDirectory(Path t, IOException ioe) throws IOException {
-                Files.deleteIfExists(t);
-                return FileVisitResult.CONTINUE;
-            }
-        });
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePrevisitor.java	Mon Dec 21 14:42:17 2015 +0100
@@ -0,0 +1,48 @@
+/*
+ * 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 jdk.tools.jlink.api.plugin.transformer.Pool;
+
+/**
+ * Plugin wishing to pre-visit the resources must implement this interface.
+ * Pre-visit can be useful when some activities are required prior to the actual
+ * Resource visit.
+ * The StringTable plays a special role during previsit. The passed Strings are NOT
+ * added to the jimage file. The string usage is tracked in order to build an efficient
+ * string storage.
+ */
+public interface ResourcePrevisitor {
+
+    /**
+     * Previsit the collection of resources.
+     *
+     * @param resources Read only resources.
+     * @param strings StringTable instance. Add string to the StringTable to track string
+     * usage.
+     * @throws PluginException
+     */
+    public void previsit(Pool resources, StringTable strings);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/StringTable.java	Mon Dec 21 14:42:17 2015 +0100
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+/**
+* Added strings are stored in the jimage strings table.
+*/
+public interface StringTable {
+    /**
+     * Add a string to the jimage strings table.
+     * @param str The string to add.
+     * @return a String identifier.
+     */
+    public int addString(String str);
+
+    /**
+     * Retrieve a string from the passed id.
+     * @param id The string id.
+     * @return The string referenced by the passed id.
+     */
+    public String getString(int id);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Utils.java	Mon Dec 21 14:42:17 2015 +0100
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.tools.jlink.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+import jdk.tools.jlink.api.plugin.Plugin;
+import jdk.tools.jlink.api.plugin.Plugin.PluginType;
+
+/**
+ *
+ * @author jdenise
+ */
+public class Utils {
+
+    public static final Function<String, String[]> listParser = (argument) -> {
+        String[] arguments = null;
+        if (argument != null) {
+            arguments = argument.split(",");
+            for (int i = 0; i < arguments.length; i++) {
+                arguments[i] = arguments[i].trim();
+            }
+        }
+        return arguments;
+    };
+
+    public static boolean isPostProcessor(Plugin.CATEGORY category) {
+        return category.equals(Plugin.CATEGORY.VERIFIER)
+                || category.equals(Plugin.CATEGORY.PROCESSOR)
+                || category.equals(Plugin.CATEGORY.PACKAGER);
+    }
+
+    public static boolean isPreProcessor(Plugin.CATEGORY category) {
+        return category.equals(Plugin.CATEGORY.COMPRESSOR)
+                || category.equals(Plugin.CATEGORY.FILTER)
+                || category.equals(Plugin.CATEGORY.MODULEINFO_TRANSFORMER)
+                || category.equals(Plugin.CATEGORY.SORTER)
+                || category.equals(Plugin.CATEGORY.TRANSFORMER);
+    }
+
+    public static boolean isPostProcessor(Plugin prov) {
+        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);
+            }
+        }
+        return false;
+    }
+
+    public static Plugin.ORDER getOrder(Plugin provider) {
+        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;
+            }
+        }
+        return null;
+    }
+
+    public static List<Plugin> getPostProcessors(List<Plugin> plugins) {
+        List<Plugin> res = new ArrayList<>();
+        for (Plugin p : plugins) {
+            if (isPostProcessor(p)) {
+                res.add(p);
+            }
+        }
+        return res;
+    }
+
+    public static List<Plugin> getPreProcessors(List<Plugin> plugins) {
+        List<Plugin> res = new ArrayList<>();
+        for (Plugin p : plugins) {
+            if (isPreProcessor(p)) {
+                res.add(p);
+            }
+        }
+        return res;
+    }
+
+    public static boolean isFunctional(Plugin prov) {
+        return prov.getState().contains(Plugin.STATE.FUNCTIONAL);
+    }
+
+    public static boolean isEnabled(Plugin prov) {
+        return prov.getState().contains(Plugin.STATE.ENABLED);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultCompressPlugin.java	Mon Dec 21 14:42:17 2015 +0100
@@ -0,0 +1,159 @@
+/*
+ * 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.IOException;
+import java.io.UncheckedIOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+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.internal.PoolImpl;
+import jdk.tools.jlink.api.plugin.transformer.Pool;
+import jdk.tools.jlink.api.plugin.transformer.TransformerPlugin;
+import jdk.tools.jlink.internal.ImagePluginStack;
+import jdk.tools.jlink.internal.ResourcePrevisitor;
+import jdk.tools.jlink.internal.StringTable;
+import jdk.tools.jlink.internal.Utils;
+
+/**
+ *
+ * ZIP and String Sharing compression plugin
+ */
+public final class DefaultCompressPlugin implements TransformerPlugin, ResourcePrevisitor {
+
+    static final String NAME = "compress-resources";
+    public static final PluginOption NAME_OPTION
+            = new Builder(NAME).
+            description(PluginsResourceBundle.getDescription(NAME)).
+            hasOnOffArgument().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";
+    public static final String LEVEL_1 = "1";
+    public static final String LEVEL_2 = "2";
+
+    private StringSharingPlugin ss;
+    private ZipPlugin zip;
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public void visit(Pool in, Pool out) {
+        if (ss != null && zip != null) {
+            Pool output = new ImagePluginStack.OrderedResourcePool(in.getByteOrder(),
+                    ((PoolImpl) in).getStringTable());
+            ss.visit(in, output);
+            zip.visit(output, out);
+        } else if (ss != null) {
+            ss.visit(in, out);
+        } else if (zip != null) {
+            zip.visit(in, out);
+        }
+    }
+
+    @Override
+    public void previsit(Pool resources, StringTable strings) {
+        if (ss != null) {
+            ss.previsit(resources, strings);
+        }
+    }
+
+    @Override
+    public PluginOption getOption() {
+        return NAME_OPTION;
+    }
+
+    @Override
+    public List<PluginOption> getAdditionalOptions() {
+        List<PluginOption> lst = new ArrayList<>();
+        lst.add(LEVEL_OPTION);
+        lst.add(FILTER_OPTION);
+        return lst;
+    }
+
+    @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) {
+        try {
+            String filter = config.get(FILTER_OPTION);
+            String[] patterns = filter == null ? null
+                    : Utils.listParser.apply(filter);
+            Object level = config.get(LEVEL_OPTION);
+            ResourceFilter resFilter = new ResourceFilter(patterns);
+            if (level != null) {
+                String l = config.get(LEVEL_OPTION);
+                switch (l) {
+                    case LEVEL_0:
+                        ss = new StringSharingPlugin(resFilter);
+                        break;
+                    case LEVEL_1:
+                        zip = new ZipPlugin(resFilter);
+                        break;
+                    case LEVEL_2:
+                        ss = new StringSharingPlugin(resFilter);
+                        zip = new ZipPlugin(resFilter);
+                        break;
+                    default:
+                        throw new IOException("Invalid level " + l);
+                }
+            } else {
+                ss = new StringSharingPlugin(resFilter);
+                zip = new ZipPlugin(resFilter);
+            }
+        } catch (IOException ex) {
+            throw new UncheckedIOException(ex);
+        }
+    }
+}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultCompressProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package jdk.tools.jlink.internal.plugins;
-
-import jdk.tools.jlink.plugins.TransformerOnOffProvider;
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-
-/**
- *
- * Default compression provider.
- */
-public class DefaultCompressProvider extends TransformerOnOffProvider {
-
-    public static final String NAME = "compress-resources";
-    public static final String LEVEL_OPTION = "compress-resources-level";
-    public static final String FILTER_OPTION = "compress-resources-filter";
-    public static final String LEVEL_0 = "0";
-    public static final String LEVEL_1 = "1";
-    public static final String LEVEL_2 = "2";
-
-    public DefaultCompressProvider() {
-        super(NAME, PluginsResourceBundle.getDescription(NAME));
-    }
-
-    @Override
-    public String getCategory() {
-        return TransformerOnOffProvider.COMPRESSOR;
-    }
-
-    @Override
-    public String getToolOption() {
-        return NAME;
-    }
-
-    @Override
-    public Map<String, String> getAdditionalOptions() {
-        Map<String, String> m = new HashMap<>();
-        m.put(LEVEL_OPTION, PluginsResourceBundle.getOption(NAME, LEVEL_OPTION));
-        m.put(FILTER_OPTION, PluginsResourceBundle.getOption(NAME, FILTER_OPTION));
-        return m;
-    }
-
-    @Override
-    public Type getType() {
-        return Type.RESOURCE_PLUGIN;
-    }
-
-    @Override
-    public List<TransformerPlugin> createPlugins(Map<String, String> otherOptions) {
-        try {
-            String filter = otherOptions.get(FILTER_OPTION);
-            String[] patterns = filter == null ? null : filter.split(",");
-            String level = otherOptions.get(LEVEL_OPTION);
-            List<TransformerPlugin> plugins = new ArrayList<>();
-            ResourceFilter resFilter = new ResourceFilter(patterns);
-            if (level != null) {
-                switch (level) {
-                    case LEVEL_0:
-                        plugins.add(new StringSharingPlugin(resFilter));
-                        break;
-                    case LEVEL_1:
-                        plugins.add(new ZipPlugin(resFilter));
-                        break;
-                    case LEVEL_2:
-                        plugins.add(new StringSharingPlugin(resFilter));
-                        plugins.add(new ZipPlugin(resFilter));
-                        break;
-                    default:
-                        throw new IOException("Invalid level " + level);
-                }
-            } else {
-                plugins.add(new StringSharingPlugin(resFilter));
-                plugins.add(new ZipPlugin(resFilter));
-            }
-            return plugins;
-        } catch (IOException ex) {
-            throw new UncheckedIOException(ex);
-        }
-    }
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeFilesPlugin.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeFilesPlugin.java	Mon Dec 21 14:42:17 2015 +0100
@@ -25,35 +25,71 @@
 package jdk.tools.jlink.internal.plugins;
 
 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 java.util.function.Predicate;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-import jdk.tools.jlink.plugins.Pool;
+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.internal.Utils;
 
 /**
  *
  * Exclude files plugin
  */
-final class ExcludeFilesPlugin implements TransformerPlugin {
+public final class ExcludeFilesPlugin implements TransformerPlugin {
 
-    private final Predicate<String> predicate;
-
-    ExcludeFilesPlugin(String[] patterns) throws IOException {
-        this(new ResourceFilter(patterns, true));
-    }
-
-    ExcludeFilesPlugin(Predicate<String> predicate) {
-        this.predicate = predicate;
-    }
+    public static final String NAME = "exclude-files";
+    public static final PluginOption NAME_OPTION
+            = new Builder(NAME).
+            description(PluginsResourceBundle.getDescription(NAME)).
+            argumentDescription(PluginsResourceBundle.getArgument(NAME)).build();
+    private Predicate<String> predicate;
 
     @Override
     public String getName() {
-        return ExcludeFilesProvider.NAME;
+        return NAME;
     }
 
     @Override
     public void visit(Pool in, Pool out) {
         in.visit((file) -> {
-            return predicate.test("/" + file.getModule() + "/" + file.getPath()) ? file : null;
+            if (!file.getType().equals(ModuleDataType.CLASS_OR_RESOURCE)) {
+                file = predicate.test("/" + file.getModule() + "/" + file.getPath()) ? file : null;
+            }
+            return file;
         }, out);
     }
+
+    @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);
+            predicate = new ResourceFilter(Utils.listParser.apply(value), true);
+        } catch (IOException ex) {
+            throw new UncheckedIOException(ex);
+        }
+    }
 }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeFilesProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +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.IOException;
-import java.io.UncheckedIOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import jdk.tools.jlink.plugins.Pool;
-import jdk.tools.jlink.plugins.TransformerCmdProvider;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-
-/**
- *
- * Exclude image files plugin provider
- */
-public final class ExcludeFilesProvider extends TransformerCmdProvider {
-    public static final String NAME = "exclude-files";
-    public ExcludeFilesProvider() {
-        super(NAME, PluginsResourceBundle.getDescription(NAME));
-    }
-
-    @Override
-    public String getCategory() {
-        return TransformerCmdProvider.FILTER;
-    }
-
-    @Override
-    public String getToolArgument() {
-        return PluginsResourceBundle.getArgument(NAME);
-    }
-
-    @Override
-    public String getToolOption() {
-        return NAME;
-    }
-
-    @Override
-    public Map<String, String> getAdditionalOptions() {
-        return null;
-    }
-
-    @Override
-    public List<TransformerPlugin> newPlugins(String[] arguments, Map<String, String> otherOptions) {
-        List<TransformerPlugin> ret = new ArrayList<>(1);
-        try {
-            ret.add(new ExcludeFilesPlugin(arguments));
-        } catch (IOException ex) {
-            throw new UncheckedIOException(ex);
-        }
-        return ret;
-    }
-
-    @Override
-    public Type getType() {
-        return Type.IMAGE_FILE_PLUGIN;
-    }
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludePlugin.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludePlugin.java	Mon Dec 21 14:42:17 2015 +0100
@@ -25,35 +25,71 @@
 package jdk.tools.jlink.internal.plugins;
 
 import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 import java.util.function.Predicate;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-import jdk.tools.jlink.plugins.Pool;
+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.internal.Utils;
 
 /**
  *
  * Exclude resources plugin
  */
-final class ExcludePlugin implements TransformerPlugin {
+public final class ExcludePlugin implements TransformerPlugin {
 
-    private final Predicate<String> predicate;
+    public static final String NAME = "exclude-resources";
+    public static final PluginOption NAME_OPTION
+            = new Builder(NAME).
+            description(PluginsResourceBundle.getDescription(NAME)).
+            argumentDescription(PluginsResourceBundle.getArgument(NAME)).build();
 
-    ExcludePlugin(String[] patterns) throws IOException {
-        this(new ResourceFilter(patterns, true));
-    }
-
-    ExcludePlugin(Predicate<String> predicate) {
-        this.predicate = predicate;
-    }
+    private Predicate<String> predicate;
 
     @Override
     public String getName() {
-        return ExcludeProvider.NAME;
+        return NAME;
     }
 
     @Override
     public void visit(Pool in, Pool out) {
         in.visit((resource) -> {
-            return predicate.test(resource.getPath()) ? resource : null;
+            if (resource.getType().equals(Pool.ModuleDataType.CLASS_OR_RESOURCE)) {
+                resource = predicate.test(resource.getPath()) ? resource : null;
+            }
+            return resource;
         }, out);
     }
+
+    @Override
+    public String getDescription() {
+        return PluginsResourceBundle.getDescription(NAME);
+    }
+
+    @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 void configure(Map<PluginOption, String> config) {
+        try {
+            String val = config.get(NAME_OPTION);
+            predicate = new ResourceFilter(Utils.listParser.apply(val), true);
+        } catch (IOException ex) {
+            throw new PluginException(ex);
+        }
+    }
 }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +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.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import jdk.tools.jlink.plugins.TransformerCmdProvider;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-import jdk.tools.jlink.plugins.PluginException;
-import jdk.tools.jlink.plugins.Pool;
-
-/**
- *
- * Exclude resources plugin provider
- */
-public final class ExcludeProvider extends TransformerCmdProvider {
-    public static final String NAME = "exclude-resources";
-    public ExcludeProvider() {
-        super(NAME, PluginsResourceBundle.getDescription(NAME));
-    }
-
-    @Override
-    public String getCategory() {
-        return TransformerCmdProvider.FILTER;
-    }
-
-    @Override
-    public String getToolArgument() {
-        return PluginsResourceBundle.getArgument(NAME);
-    }
-
-    @Override
-    public String getToolOption() {
-        return NAME;
-    }
-
-    @Override
-    public Map<String, String> getAdditionalOptions() {
-        return null;
-    }
-
-    @Override
-    public List<TransformerPlugin> newPlugins(String[] arguments, Map<String, String> otherOptions) {
-        try {
-            List<TransformerPlugin> ret = new ArrayList<>(1);
-            ret.add(new ExcludePlugin(arguments));
-            return ret;
-        } catch (IOException ex) {
-            throw new PluginException(ex);
-        }
-    }
-
-    @Override
-    public Type getType() {
-        return Type.RESOURCE_PLUGIN;
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/FileCopierPlugin.java	Mon Dec 21 14:42:17 2015 +0100
@@ -0,0 +1,290 @@
+/*
+ * 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.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+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.internal.Utils;
+
+/**
+ *
+ * Copy files to image from various locations.
+ */
+public class FileCopierPlugin implements TransformerPlugin {
+
+    public static final String NAME = "copy-files";
+    public static final PluginOption NAME_OPTION
+            = new Builder(NAME).
+            description(PluginsResourceBundle.getDescription(NAME)).
+            argumentDescription(PluginsResourceBundle.getArgument(NAME)).build();
+
+    private static final class CopiedFile {
+
+        Path source;
+        Path target;
+    }
+    private static final String FAKE_MODULE = "$jlink-file-copier";
+
+    private final List<CopiedFile> files = new ArrayList<>();
+
+    /**
+     * Symbolic link to another path.
+     */
+    public static abstract class SymImageFile extends Pool.ModuleData {
+
+        private final String targetPath;
+
+        public SymImageFile(String targetPath, String module, String path,
+                Pool.ModuleDataType type, InputStream stream, long size) {
+            super(module, path, type, stream, size);
+            this.targetPath = targetPath;
+        }
+
+        public String getTargetPath() {
+            return targetPath;
+        }
+    }
+
+    private static final class SymImageFileImpl extends SymImageFile {
+
+        public SymImageFileImpl(String targetPath, Path file, String module,
+                String path, ModuleDataType type) {
+            super(targetPath, module, path, type, newStream(file), length(file));
+        }
+    }
+
+    private static long length(Path file) {
+        try {
+            return Files.size(file);
+        } catch (IOException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    private static InputStream newStream(Path file) {
+        try {
+            return Files.newInputStream(file);
+        } catch (IOException ex) {
+            throw new UncheckedIOException(ex);
+        }
+    }
+
+    private static final class DirectoryCopy implements FileVisitor<Path> {
+
+        private final Path source;
+        private final Pool pool;
+        private final String targetDir;
+        private final List<SymImageFile> symlinks = new ArrayList<>();
+
+        DirectoryCopy(Path source, Pool pool, String targetDir) {
+            this.source = source;
+            this.pool = pool;
+            this.targetDir = targetDir;
+        }
+
+        @Override
+        public FileVisitResult preVisitDirectory(Path dir,
+                BasicFileAttributes attrs) throws IOException {
+            return FileVisitResult.CONTINUE;
+        }
+
+        @Override
+        public FileVisitResult visitFile(Path file,
+                BasicFileAttributes attrs) throws IOException {
+            Objects.requireNonNull(file);
+            Objects.requireNonNull(attrs);
+            String path = targetDir + "/" + source.relativize(file);
+            if (attrs.isSymbolicLink()) {
+                Path symTarget = Files.readSymbolicLink(file);
+                if (!Files.exists(symTarget)) {
+                    // relative to file parent?
+                    Path parent = file.getParent();
+                    if (parent != null) {
+                        symTarget = parent.resolve(symTarget);
+                    }
+                }
+                if (!Files.exists(symTarget)) {
+                    System.err.println("WARNING: Skipping sym link, target "
+                            + Files.readSymbolicLink(file) + "not found");
+                    return FileVisitResult.CONTINUE;
+                }
+                SymImageFileImpl impl = new SymImageFileImpl(symTarget.toString(),
+                        file, path, Objects.requireNonNull(file.getFileName()).toString(),
+                        Pool.ModuleDataType.OTHER);
+                symlinks.add(impl);
+            } else {
+                addFile(pool, file, path);
+            }
+            return FileVisitResult.CONTINUE;
+        }
+
+        @Override
+        public FileVisitResult postVisitDirectory(Path dir, IOException exc)
+                throws IOException {
+            if (exc != null) {
+                throw exc;
+            }
+            return FileVisitResult.CONTINUE;
+        }
+
+        @Override
+        public FileVisitResult visitFileFailed(Path file, IOException exc)
+                throws IOException {
+            throw exc;
+        }
+    }
+
+    private static void addFile(Pool pool, Path file, String path)
+            throws IOException {
+        Objects.requireNonNull(pool);
+        Objects.requireNonNull(file);
+        Objects.requireNonNull(path);
+        ModuleData impl = Pool.newImageFile(FAKE_MODULE, path,
+                Pool.ModuleDataType.OTHER, newStream(file), length(file));
+        try {
+            pool.add(impl);
+        } catch (Exception ex) {
+            throw new IOException(ex);
+        }
+    }
+
+    @Override
+    public Set<PluginType> getType() {
+        Set<PluginType> set = new HashSet<>();
+        set.add(CATEGORY.TRANSFORMER);
+        return Collections.unmodifiableSet(set);
+    }
+
+    @Override
+    public PluginOption getOption() {
+        return NAME_OPTION;
+    }
+
+    @Override
+    public void configure(Map<PluginOption, String> config) {
+        String val = config.get(NAME_OPTION);
+        String[] argument = Utils.listParser.apply(val);
+        if (argument == null || argument.length == 0) {
+            throw new RuntimeException("Invalid argument for " + NAME);
+        }
+
+        String javahome = System.getProperty("java.home");
+        for (String a : argument) {
+            int i = a.indexOf("=");
+            CopiedFile cf = new CopiedFile();
+            if (i == -1) {
+                Path file = Paths.get(a);
+                if (file.isAbsolute()) {
+                    cf.source = file;
+                    // The target is the image root directory.
+                    cf.target = file.getFileName();
+                } else {
+                    file = new File(javahome, a).toPath();
+                    cf.source = file;
+                    cf.target = Paths.get(a);
+                }
+            } else {
+                String target = a.substring(i + 1);
+                String f = a.substring(0, i);
+                Path file = Paths.get(f);
+                if (file.isAbsolute()) {
+                    cf.source = file;
+                } else {
+                    cf.source = new File(javahome,
+                            file.toFile().getPath()).toPath();
+                }
+                cf.target = Paths.get(target);
+            }
+            if (!Files.exists(cf.source)) {
+                System.err.println("Skipping file " + cf.source
+                        + ", it doesn't exist");
+            } else {
+                files.add(cf);
+            }
+        }
+    }
+
+    @Override
+    public void visit(Pool in, Pool out) {
+        in.visit((file) -> {
+            return file;
+        }, out);
+
+        // Add new files.
+        try {
+            for (CopiedFile file : files) {
+                if (Files.isRegularFile(file.source)) {
+                    addFile(out, file.source, file.target.toString());
+                } else if (Files.isDirectory(file.source)) {
+                    DirectoryCopy dc = new DirectoryCopy(file.source,
+                            out, file.target.toString());
+                    Files.walkFileTree(file.source, dc);
+                    // Add symlinks after actual content
+                    for (SymImageFile imf : dc.symlinks) {
+                        try {
+                            out.add(imf);
+                        } catch (Exception ex) {
+                            throw new PluginException(ex);
+                        }
+                    }
+                }
+            }
+        } catch (IOException ex) {
+            throw new UncheckedIOException(ex);
+        }
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public String getDescription() {
+        return PluginsResourceBundle.getDescription(NAME);
+    }
+}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/FileCopierProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,299 +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.IOException;
-import java.io.InputStream;
-import java.io.UncheckedIOException;
-import java.nio.file.FileVisitResult;
-import java.nio.file.FileVisitor;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import jdk.tools.jlink.plugins.PluginException;
-import jdk.tools.jlink.plugins.Pool;
-import jdk.tools.jlink.plugins.Pool.ModuleData;
-import jdk.tools.jlink.plugins.Pool.ModuleDataType;
-import jdk.tools.jlink.plugins.TransformerCmdProvider;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-import jdk.tools.jlink.plugins.TransformerPluginProvider;
-
-/**
- *
- * Copy files to image from various locations.
- */
-public class FileCopierProvider extends TransformerCmdProvider {
-
-    /**
-     * Symbolic link to another path.
-     */
-    public static abstract class SymImageFile extends Pool.ModuleData {
-
-        private final String targetPath;
-
-        public SymImageFile(String targetPath, String module, String path,
-                Pool.ModuleDataType type, InputStream stream, long size) {
-            super(module, path, type, stream, size);
-            this.targetPath = targetPath;
-        }
-
-        public String getTargetPath() {
-            return targetPath;
-        }
-    }
-
-    private static final class SymImageFileImpl extends SymImageFile {
-
-        public SymImageFileImpl(String targetPath, Path file, String module,
-                String path, ModuleDataType type) {
-            super(targetPath, module, path, type, newStream(file), length(file));
-        }
-    }
-
-    private static long length(Path file) {
-        try {
-            return Files.size(file);
-        } catch (IOException ex) {
-            throw new RuntimeException(ex);
-        }
-    }
-
-    private static InputStream newStream(Path file) {
-        try {
-            return Files.newInputStream(file);
-        } catch (IOException ex) {
-            throw new UncheckedIOException(ex);
-        }
-    }
-    private static final class DirectoryCopy implements FileVisitor<Path> {
-
-        private final Path source;
-        private final Pool pool;
-        private final String targetDir;
-        private final List<SymImageFile> symlinks = new ArrayList<>();
-
-        DirectoryCopy(Path source, Pool pool, String targetDir) {
-            this.source = source;
-            this.pool = pool;
-            this.targetDir = targetDir;
-        }
-
-        @Override
-        public FileVisitResult preVisitDirectory(Path dir,
-                BasicFileAttributes attrs) throws IOException {
-            return FileVisitResult.CONTINUE;
-        }
-
-        @Override
-        public FileVisitResult visitFile(Path file,
-                BasicFileAttributes attrs) throws IOException {
-            Objects.requireNonNull(file);
-            Objects.requireNonNull(attrs);
-            String path = targetDir + "/" + source.relativize(file);
-            if (attrs.isSymbolicLink()) {
-                Path symTarget = Files.readSymbolicLink(file);
-                if (!Files.exists(symTarget)) {
-                    // relative to file parent?
-                    Path parent = file.getParent();
-                    if (parent != null) {
-                        symTarget = parent.resolve(symTarget);
-                    }
-                }
-                if (!Files.exists(symTarget)) {
-                    System.err.println("WARNING: Skipping sym link, target "
-                            + Files.readSymbolicLink(file) + "not found");
-                    return FileVisitResult.CONTINUE;
-                }
-                SymImageFileImpl impl = new SymImageFileImpl(symTarget.toString(),
-                        file, path, Objects.requireNonNull(file.getFileName()).toString(),
-                        Pool.ModuleDataType.OTHER);
-                symlinks.add(impl);
-            } else {
-                addFile(pool, file, path);
-            }
-            return FileVisitResult.CONTINUE;
-        }
-
-        @Override
-        public FileVisitResult postVisitDirectory(Path dir, IOException exc)
-                throws IOException {
-            if (exc != null) {
-                throw exc;
-            }
-            return FileVisitResult.CONTINUE;
-        }
-
-        @Override
-        public FileVisitResult visitFileFailed(Path file, IOException exc)
-                throws IOException {
-            throw exc;
-        }
-    }
-
-    private static void addFile(Pool pool, Path file, String path)
-            throws IOException {
-        Objects.requireNonNull(pool);
-        Objects.requireNonNull(file);
-        Objects.requireNonNull(path);
-        ModuleData impl = Pool.newImageFile("$jlink-file-copier", path,
-                Pool.ModuleDataType.OTHER, newStream(file), length(file));
-        try {
-            pool.add(impl);
-        } catch (Exception ex) {
-            throw new IOException(ex);
-        }
-    }
-
-    public static final class FileCopier implements TransformerPlugin {
-
-        private static final class CopiedFile {
-            Path source;
-            Path target;
-        }
-
-        private final List<CopiedFile> files = new ArrayList<>();
-
-        public FileCopier(String[] argument) {
-            if (argument == null || argument.length == 0) {
-                throw new RuntimeException("Invalid argument for " + NAME);
-            }
-            String javahome = System.getProperty("java.home");
-            for (String a : argument) {
-                int i = a.indexOf("=");
-                CopiedFile cf = new CopiedFile();
-                if (i == -1) {
-                    Path file = Paths.get(a);
-                    if (file.isAbsolute()) {
-                        cf.source = file;
-                        // The target is the image root directory.
-                        cf.target = file.getFileName();
-                    } else {
-                        file = new File(javahome, a).toPath();
-                        cf.source = file;
-                        cf.target = Paths.get(a);
-                    }
-                } else {
-                    String target = a.substring(i + 1);
-                    String f = a.substring(0, i);
-                    Path file = Paths.get(f);
-                    if (file.isAbsolute()) {
-                        cf.source = file;
-                    } else {
-                        cf.source = new File(javahome,
-                                file.toFile().getPath()).toPath();
-                    }
-                    cf.target = Paths.get(target);
-                }
-                if (!Files.exists(cf.source)) {
-                    System.err.println("Skipping file " + cf.source
-                            + ", it doesn't exist");
-                } else {
-                    files.add(cf);
-                }
-            }
-        }
-
-        @Override
-        public void visit(Pool in, Pool out) {
-            in.visit((file) -> {
-                return file;
-            }, out);
-
-            // Add new files.
-            try {
-                for (CopiedFile file : files) {
-                    if (Files.isRegularFile(file.source)) {
-                        addFile(out, file.source, file.target.toString());
-                    } else {
-                        if (Files.isDirectory(file.source)) {
-                            DirectoryCopy dc = new DirectoryCopy(file.source,
-                                    out, file.target.toString());
-                            Files.walkFileTree(file.source, dc);
-                            // Add symlinks after actual content
-                            for (SymImageFile imf : dc.symlinks) {
-                                try {
-                                    out.add(imf);
-                                } catch (Exception ex) {
-                                    throw new PluginException(ex);
-                                }
-                            }
-                        }
-                    }
-                }
-            } catch (IOException ex) {
-                throw new UncheckedIOException(ex);
-            }
-        }
-
-        @Override
-        public String getName() {
-            return NAME;
-        }
-
-    }
-    public static final String NAME = "copy-files";
-
-    public FileCopierProvider() {
-        super(NAME, PluginsResourceBundle.getDescription(NAME));
-    }
-
-    @Override
-    public String getToolArgument() {
-        return PluginsResourceBundle.getArgument(NAME);
-    }
-
-    @Override
-    public String getCategory() {
-        return TransformerPluginProvider.TRANSFORMER;
-    }
-
-    @Override
-    public String getToolOption() {
-        return NAME;
-    }
-
-    @Override
-    public Map<String, String> getAdditionalOptions() {
-        return null;
-    }
-
-    @Override
-    public List<TransformerPlugin> newPlugins(String[] arguments, Map<String, String> otherOptions) {
-        List<TransformerPlugin> lst = new ArrayList<>();
-        lst.add(new FileCopier(arguments));
-        return lst;
-    }
-    @Override
-    public Type getType() {
-        return Type.IMAGE_FILE_PLUGIN;
-    }
-
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/FileReplacerPlugin.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/FileReplacerPlugin.java	Mon Dec 21 14:42:17 2015 +0100
@@ -28,20 +28,77 @@
 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 jdk.tools.jlink.plugins.Pool;
-import jdk.tools.jlink.plugins.TransformerPlugin;
+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
  */
-final class FileReplacerPlugin implements TransformerPlugin {
+public final class FileReplacerPlugin implements TransformerPlugin {
 
     private final Map<String, File> mapping = new HashMap<>();
 
-    FileReplacerPlugin(String[] arguments) {
+    @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++;
@@ -59,25 +116,4 @@
         }
     }
 
-    @Override
-    public String getName() {
-        return FileReplacerProvider.NAME;
-    }
-
-    @Override
-    public void visit(Pool inFiles, Pool outFiles) {
-        inFiles.visit((file) -> {
-            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);
-    }
 }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/FileReplacerProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package jdk.tools.jlink.internal.plugins;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import jdk.tools.jlink.plugins.Pool;
-import jdk.tools.jlink.plugins.TransformerCmdProvider;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-import jdk.tools.jlink.plugins.TransformerPluginProvider;
-
-/**
- *
- * Replace files by custom content
- */
-public class FileReplacerProvider extends TransformerCmdProvider {
-
-    public static final String NAME = "replace-file";
-
-    public FileReplacerProvider() {
-        super(NAME, PluginsResourceBundle.getDescription(NAME));
-    }
-
-    @Override
-    public String getToolArgument() {
-         return PluginsResourceBundle.getArgument(NAME);
-    }
-
-    @Override
-    public String getCategory() {
-        return TransformerPluginProvider.TRANSFORMER;
-    }
-
-    @Override
-    public String getToolOption() {
-        return NAME;
-    }
-
-    @Override
-    public Map<String, String> getAdditionalOptions() {
-        return null;
-    }
-
-    @Override
-    public List<TransformerPlugin> newPlugins(String[] arguments, Map<String, String> otherOptions) {
-        List<TransformerPlugin> lst = new ArrayList<>();
-        lst.add(new FileReplacerPlugin(arguments));
-        return lst;
-    }
-
-    @Override
-    public Type getType() {
-        return Type.IMAGE_FILE_PLUGIN;
-    }
-
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/InstalledModuleDescriptorPlugin.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/InstalledModuleDescriptorPlugin.java	Mon Dec 21 14:42:17 2015 +0100
@@ -27,6 +27,7 @@
 import java.lang.module.ModuleDescriptor.*;
 import java.lang.module.ModuleDescriptor;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -41,29 +42,46 @@
 import jdk.tools.jlink.internal.plugins.asm.AsmModulePool;
 
 import static jdk.internal.org.objectweb.asm.Opcodes.*;
+import jdk.tools.jlink.api.plugin.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
  */
-final class InstalledModuleDescriptorPlugin extends AsmPlugin {
-    InstalledModuleDescriptorPlugin() {
+public final class InstalledModuleDescriptorPlugin extends AsmPlugin {
+
+    static final String NAME = "gen-installed-modules";
+    private static final PluginOption NAME_OPTION
+            = new PluginOption.Builder(NAME).
+            description(PluginsResourceBundle.getDescription(NAME)).
+            isEnabled().build();
+
+    @Override
+    public PluginOption getOption() {
+        return NAME_OPTION;
+    }
+
+    @Override
+    public Set<PluginType> getType() {
+        Set<PluginType> set = new HashSet<>();
+        set.add(CATEGORY.TRANSFORMER);
+        return Collections.unmodifiableSet(set);
     }
 
     @Override
     public String getName() {
-        return InstalledModuleDescriptorProvider.NAME;
+        return NAME;
     }
 
     @Override
@@ -102,7 +120,7 @@
         for (Exports exp : md.exports()) {
             Checks.requirePackageName(exp.source());
             exp.targets()
-               .ifPresent(targets -> targets.forEach(Checks::requireModuleName));
+                    .ifPresent(targets -> targets.forEach(Checks::requireModuleName));
         }
         for (Map.Entry<String, Provides> e : md.provides().entrySet()) {
             String service = e.getKey();
@@ -133,17 +151,28 @@
         }
     }
 
+    @Override
+    public String getDescription() {
+        return PluginsResourceBundle.getDescription(NAME);
+    }
+
+    @Override
+    public void configure(Map<PluginOption, String> config) {
+        //NOOP
+    }
+
     /**
-     * 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";
@@ -168,11 +197,11 @@
         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,
-                                     "modules", "()Ljava/util/Map;",
-                                     "()" + MODULES_MAP_SIGNATURE, null);
+            this.mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
+                    "modules", "()Ljava/util/Map;",
+                    "()" + MODULES_MAP_SIGNATURE, null);
             mv.visitCode();
         }
 
@@ -182,42 +211,40 @@
          * 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,
-                     null, "java/lang/Object", null);
+            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,
-                          "[Ljava/lang/String;", null, null)
-              .visitEnd();
-
+            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,
-                          "I", null, numPackages)
-              .visitEnd();
+            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,
-                          MODULES_MAP_SIGNATURE, null)
-              .visitEnd();
+            cw.visitField(ACC_FINAL + ACC_STATIC, DESCRIPTOR_MAP, MAP_TYPE,
+                    MODULES_MAP_SIGNATURE, null)
+                    .visitEnd();
 
             MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V",
-                                              null, null);
+                    null, null);
             mv.visitCode();
 
             // create the MODULE_NAMES array
             int numModules = moduleNames.size();
             newArray(cw, mv, moduleNames, numModules);
             mv.visitFieldInsn(PUTSTATIC, CLASSNAME, MODULE_NAMES,
-                              "[Ljava/lang/String;");
+                    "[Ljava/lang/String;");
             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",
-                               "<init>", "()V", false);
+                    "<init>", "()V", false);
             mv.visitFieldInsn(PUTSTATIC, CLASSNAME, DESCRIPTOR_MAP, MAP_TYPE);
             mv.visitInsn(RETURN);
             mv.visitMaxs(0, 0);
@@ -268,10 +295,10 @@
         }
 
         private static void newArray(ClassWriter cw, MethodVisitor mv,
-                                     Set<String> names, int size) {
+                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
@@ -363,8 +390,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:
@@ -409,9 +436,9 @@
                 mv.visitLdcInsn(md.name());
                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
-                    "build", "()Ljava/lang/module/ModuleDescriptor;", false);
+                        "build", "()Ljava/lang/module/ModuleDescriptor;", false);
                 mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "put",
-                    "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true);
+                        "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true);
                 mv.visitInsn(POP);
             }
 
@@ -424,9 +451,9 @@
                 mv.visitLdcInsn(mn);
                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
-                    "build", "()Ljava/lang/module/ModuleDescriptor;", false);
+                        "build", "()Ljava/lang/module/ModuleDescriptor;", false);
                 mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map",
-                    "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true);
+                        "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true);
                 mv.visitInsn(POP);
             }
 
@@ -437,7 +464,7 @@
                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
                 mv.visitLdcInsn(name);
                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
-                                   "requires", STRING_SIG, false);
+                        "requires", STRING_SIG, false);
                 mv.visitInsn(POP);
             }
 
@@ -447,10 +474,10 @@
             void requires(ModuleDescriptor.Requires.Modifier mod, String name) {
                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
                 mv.visitFieldInsn(GETSTATIC, REQUIRES_MODIFIER_CLASSNAME, mod.name(),
-                                  REQUIRES_MODIFIER_TYPE);
+                        REQUIRES_MODIFIER_TYPE);
                 mv.visitLdcInsn(name);
                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
-                                   "requires", REQUIRES_MODIFIER_STRING_SIG, false);
+                        "requires", REQUIRES_MODIFIER_STRING_SIG, false);
                 mv.visitInsn(POP);
             }
 
@@ -465,18 +492,18 @@
                 String signature = "(";
                 for (ModuleDescriptor.Requires.Modifier m : mods) {
                     mv.visitFieldInsn(GETSTATIC, REQUIRES_MODIFIER_CLASSNAME, m.name(),
-                                      REQUIRES_MODIFIER_TYPE);
+                            REQUIRES_MODIFIER_TYPE);
                     signature += "Ljava/util/Enum;";
                 }
                 signature += ")Ljava/util/EnumSet;";
                 mv.visitMethodInsn(INVOKESTATIC, "java/util/EnumSet", "of",
-                                   signature, false);
+                        signature, false);
                 mv.visitVarInsn(ASTORE, MODS_VAR);
                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
                 mv.visitVarInsn(ALOAD, MODS_VAR);
                 mv.visitLdcInsn(name);
                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
-                                   "requires", SET_STRING_SIG, false);
+                        "requires", SET_STRING_SIG, false);
                 mv.visitInsn(POP);
             }
 
@@ -549,7 +576,7 @@
                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
                 mv.visitLdcInsn(pn);
                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
-                                   "conceals", STRING_SIG, false);
+                        "conceals", STRING_SIG, false);
                 mv.visitInsn(POP);
             }
 
@@ -560,7 +587,7 @@
                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
                 mv.visitLdcInsn(cn);
                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
-                                   "mainClass", STRING_SIG, false);
+                        "mainClass", STRING_SIG, false);
                 mv.visitInsn(POP);
             }
 
@@ -571,7 +598,7 @@
                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
                 mv.visitLdcInsn(v.toString());
                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
-                                   "version", STRING_SIG, false);
+                        "version", STRING_SIG, false);
                 mv.visitInsn(POP);
             }
 
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/InstalledModuleDescriptorProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +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.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import jdk.tools.jlink.plugins.TransformerOnOffProvider;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-
-public class InstalledModuleDescriptorProvider extends TransformerOnOffProvider {
-
-    public static final String NAME = "gen-installed-modules";
-
-    public InstalledModuleDescriptorProvider() {
-        super(NAME, PluginsResourceBundle.getDescription(NAME));
-    }
-
-    @Override
-    public String getToolOption() {
-        return NAME;
-    }
-
-    @Override
-    public Map<String, String> getAdditionalOptions() {
-        return null;
-    }
-
-    @Override
-    public String getCategory() {
-        return TRANSFORMER;
-    }
-
-    @Override
-    public boolean isEnabledByDefault() {
-        return true;
-    }
-
-    @Override
-    public Type getType() {
-        return Type.RESOURCE_PLUGIN;
-    }
-
-    @Override
-    public List<TransformerPlugin> createPlugins(Map<String, String> otherOptions) {
-        List<TransformerPlugin> lst = new ArrayList<>();
-        lst.add(new InstalledModuleDescriptorPlugin());
-        return lst;
-    }
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java	Mon Dec 21 14:42:17 2015 +0100
@@ -31,6 +31,8 @@
 import java.io.UncheckedIOException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -44,6 +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.internal.plugins.asm.AsmModulePool;
 import jdk.tools.jlink.internal.plugins.optim.ForNameFolding;
 import jdk.tools.jlink.internal.plugins.optim.ReflectionOptimizer.TypeResolver;
@@ -55,6 +59,20 @@
  */
 public final class OptimizationPlugin extends AsmPlugin {
 
+    public static final String NAME = "class-optim";
+    public static final String LOG_FILE = NAME + "-log-file";
+    public static final String ALL = "all";
+    public static final String FORNAME_REMOVAL = "forName-folding";
+
+    public static final PluginOption NAME_OPTION
+            = new Builder(NAME).
+            description(PluginsResourceBundle.getDescription(NAME)).
+            argumentDescription(PluginsResourceBundle.getArgument(NAME, ALL, FORNAME_REMOVAL)).
+            build();
+    public static final PluginOption LOG_OPTION = new Builder(LOG_FILE).
+            description(PluginsResourceBundle.getOption(NAME, LOG_FILE)).
+            build();
+
     /**
      * Default resolver. A resolver that retrieve types that are in an
      * accessible package, are public or are located in the same package as the
@@ -91,10 +109,8 @@
                         if (((r.getAccess() & Opcodes.ACC_PUBLIC)
                                 == Opcodes.ACC_PUBLIC)) {
                             reader = r;
-                        } else {
-                            if (pkg.equals(callerPkg)) {
-                                reader = r;
-                            }
+                        } else if (pkg.equals(callerPkg)) {
+                            reader = r;
                         }
                     }
                 }
@@ -127,32 +143,6 @@
     private OutputStream stream;
     private int numMethods;
 
-    OptimizationPlugin(String[] arguments, Map<String, String> options) {
-        String strategies = arguments[0];
-        String[] arr = strategies.split(":");
-        for (String s : arr) {
-            if (s.equals(OptimizationProvider.ALL)) {
-                optimizers.clear();
-                optimizers.add(new ForNameFolding());
-                break;
-            } else {
-                if (s.equals(OptimizationProvider.FORNAME_REMOVAL)) {
-                    optimizers.add(new ForNameFolding());
-                } else {
-                    throw new RuntimeException("Unknown optimization");
-                }
-            }
-        }
-        String f = options.get(OptimizationProvider.LOG_FILE);
-        if (f != null) {
-            try {
-                stream = new FileOutputStream(f);
-            } catch (FileNotFoundException ex) {
-                System.err.println(ex);
-            }
-        }
-    }
-
     private void log(String content) {
         if (stream != null) {
             try {
@@ -181,7 +171,7 @@
 
     @Override
     public String getName() {
-        return OptimizationProvider.NAME;
+        return NAME;
     }
 
     @Override
@@ -265,4 +255,53 @@
         }
         return writer;
     }
+
+    @Override
+    public String getDescription() {
+        return PluginsResourceBundle.getDescription(NAME);
+    }
+
+    @Override
+    public void configure(Map<PluginOption, String> config) {
+       String strategies = config.get(NAME_OPTION);
+       String[] arr = strategies.split(":");
+        for (String s : arr) {
+            if (s.equals(ALL)) {
+                optimizers.clear();
+                optimizers.add(new ForNameFolding());
+                break;
+            } else if (s.equals(FORNAME_REMOVAL)) {
+                optimizers.add(new ForNameFolding());
+            } else {
+                throw new RuntimeException("Unknown optimization");
+            }
+        }
+        String f = config.get(LOG_OPTION);
+        if (f != null) {
+            try {
+                stream = new FileOutputStream(f);
+            } catch (FileNotFoundException ex) {
+                System.err.println(ex);
+            }
+        }
+    }
+
+    @Override
+    public Set<PluginType> getType() {
+        Set<PluginType> set = new HashSet<>();
+        set.add(CATEGORY.TRANSFORMER);
+        return Collections.unmodifiableSet(set);
+    }
+
+    @Override
+    public PluginOption getOption() {
+        return NAME_OPTION;
+    }
+
+    @Override
+    public List<PluginOption> getAdditionalOptions() {
+        List<PluginOption> options = new ArrayList<>();
+        options.add(LOG_OPTION);
+        return options;
+    }
 }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +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.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import jdk.tools.jlink.plugins.TransformerCmdProvider;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-
-/**
- *
- */
-public final class OptimizationProvider extends TransformerCmdProvider {
-
-    public static final String NAME = "class-optim";
-    public static final String LOG_FILE = NAME + "-log-file";
-    public static final String ALL = "all";
-    public static final String FORNAME_REMOVAL = "forName-folding";
-
-    public OptimizationProvider() {
-        super(NAME, PluginsResourceBundle.getDescription(NAME));
-    }
-
-    @Override
-    public String getCategory() {
-        return TransformerCmdProvider.TRANSFORMER;
-    }
-
-    @Override
-    public String getToolOption() {
-        return NAME;
-    }
-
-    @Override
-    public Map<String, String> getAdditionalOptions() {
-        Map<String, String> map = new HashMap<>();
-        map.put(LOG_FILE, PluginsResourceBundle.getOption(NAME, LOG_FILE));
-        return map;
-    }
-
-    @Override
-    public String getToolArgument() {
-        return PluginsResourceBundle.getArgument(NAME, ALL, FORNAME_REMOVAL);
-    }
-
-    @Override
-    public List<TransformerPlugin> newPlugins(String[] arguments, Map<String, String> otherOptions) {
-        List<TransformerPlugin> lst = new ArrayList<>();
-        lst.add(new OptimizationPlugin(arguments, otherOptions));
-        return lst;
-    }
-
-    @Override
-    public Type getType() {
-        return Type.RESOURCE_PLUGIN;
-    }
-
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SortResourcesPlugin.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SortResourcesPlugin.java	Mon Dec 21 14:42:17 2015 +0100
@@ -31,58 +31,45 @@
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
-import jdk.tools.jlink.plugins.Pool;
-import jdk.tools.jlink.plugins.Pool.ModuleData;
-import jdk.tools.jlink.plugins.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.api.plugin.transformer.Pool.ModuleDataType;
+import jdk.tools.jlink.api.plugin.transformer.TransformerPlugin;
+import jdk.tools.jlink.internal.Utils;
 
 /**
  *
  * Sort Resources plugin
  */
-final class SortResourcesPlugin implements TransformerPlugin {
+public final class SortResourcesPlugin implements TransformerPlugin {
 
+    public static final String NAME = "sort-resources";
+    public static final PluginOption NAME_OPTION
+            = new Builder(NAME).
+            description(PluginsResourceBundle.getDescription(NAME)).
+            argumentDescription(PluginsResourceBundle.getArgument(NAME)).build();
     private final List<Pattern> filters = new ArrayList<>();
-    private final List<String> orderedPaths;
-    private final boolean isFile;
-    SortResourcesPlugin(String[] patterns) throws IOException {
-        boolean isf = false;
-        List<String> paths = null;
-        if (patterns != null) {
-            if (patterns.length == 1) {
-                String filePath = patterns[0];
-                File f = new File(filePath);
-                if (f.exists()) {
-                    isf = true;
-                    try (FileInputStream fis = new FileInputStream(f);
-                            InputStreamReader ins
-                            = new InputStreamReader(fis, StandardCharsets.UTF_8);
-                            BufferedReader reader = new BufferedReader(ins)) {
-                        paths = reader.lines().collect(Collectors.toList());
-                    }
-                }
-            }
-            if (!isf) {
-                for (String p : patterns) {
-                    p = p.replaceAll(" ", "");
-                    Pattern pattern = Pattern.compile(ResourceFilter.escape(p));
-                    filters.add(pattern);
-                }
-            }
-        }
-        orderedPaths = paths;
-        isFile = isf;
-    }
+    private List<String> orderedPaths;
+    private boolean isFile;
 
     @Override
     public String getName() {
-        return SortResourcesProvider.NAME;
+        return NAME;
     }
 
     static class SortWrapper {
+
         private final ModuleData resource;
         private final int ordinal;
 
@@ -123,9 +110,10 @@
     @Override
     public void visit(Pool in, Pool out) {
         in.getContent().stream()
+                .filter(w -> w.getType().equals(ModuleDataType.CLASS_OR_RESOURCE))
                 .map((r) -> new SortWrapper(r, isFile
-                                        ? getFileOrdinal(r.getPath())
-                                        : getPatternOrdinal(r.getPath())))
+                        ? getFileOrdinal(r.getPath())
+                        : getPatternOrdinal(r.getPath())))
                 .sorted((sw1, sw2) -> {
                     int ordinal1 = sw1.getOrdinal();
                     int ordinal2 = sw2.getOrdinal();
@@ -142,11 +130,73 @@
 
                     return sw1.getPath().compareTo(sw2.getPath());
                 }).forEach((sw) -> {
+            try {
+                out.add(sw.getResource());
+            } catch (Exception ex) {
+                throw new RuntimeException(ex);
+            }
+        });
+        in.getContent().stream()
+                .filter(m -> !m.getType().equals(ModuleDataType.CLASS_OR_RESOURCE))
+                .forEach((m) -> {
                     try {
-                        out.add(sw.getResource());
+                        out.add(m);
                     } catch (Exception ex) {
                         throw new RuntimeException(ex);
                     }
                 });
     }
+
+    @Override
+    public Set<PluginType> getType() {
+        Set<PluginType> set = new HashSet<>();
+        set.add(CATEGORY.SORTER);
+        return Collections.unmodifiableSet(set);
+    }
+
+    @Override
+    public PluginOption getOption() {
+        return NAME_OPTION;
+    }
+
+    @Override
+    public String getDescription() {
+        return PluginsResourceBundle.getDescription(NAME);
+    }
+
+    @Override
+    public void configure(Map<PluginOption, String> config) {
+        String val = config.get(NAME_OPTION);
+        try {
+            String[] patterns = Utils.listParser.apply(val);
+            boolean isf = false;
+            List<String> paths = null;
+            if (patterns != null) {
+                if (patterns.length == 1) {
+                    String filePath = patterns[0];
+                    File f = new File(filePath);
+                    if (f.exists()) {
+                        isf = true;
+                        try (FileInputStream fis = new FileInputStream(f);
+                                InputStreamReader ins
+                                = new InputStreamReader(fis, StandardCharsets.UTF_8);
+                                BufferedReader reader = new BufferedReader(ins)) {
+                            paths = reader.lines().collect(Collectors.toList());
+                        }
+                    }
+                }
+                if (!isf) {
+                    for (String p : patterns) {
+                        p = p.replaceAll(" ", "");
+                        Pattern pattern = Pattern.compile(ResourceFilter.escape(p));
+                        filters.add(pattern);
+                    }
+                }
+            }
+            orderedPaths = paths;
+            isFile = isf;
+        } catch (IOException ex) {
+            throw new PluginException(ex);
+        }
+    }
 }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SortResourcesProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +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.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import jdk.tools.jlink.plugins.PluginException;
-import jdk.tools.jlink.plugins.Pool;
-import jdk.tools.jlink.plugins.TransformerCmdProvider;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-import jdk.tools.jlink.plugins.TransformerPluginProvider;
-
-/**
- *
- * Sort resources plugin provider
- */
-public final class SortResourcesProvider extends TransformerCmdProvider {
-    public static final String NAME = "sort-resources";
-    public SortResourcesProvider() {
-        super(NAME, PluginsResourceBundle.getDescription(NAME));
-    }
-
-
-    @Override
-    public String getCategory() {
-        return TransformerPluginProvider.SORTER;
-    }
-
-    @Override
-    public String getToolArgument() {
-        return PluginsResourceBundle.getArgument(NAME);
-    }
-
-    @Override
-    public String getToolOption() {
-        return NAME;
-    }
-
-    @Override
-    public Map<String, String> getAdditionalOptions() {
-        return null;
-    }
-
-    @Override
-    public List<TransformerPlugin> newPlugins(String[] arguments, Map<String, String> otherOptions) {
-        try {
-            List<TransformerPlugin> lst = new ArrayList<>();
-            lst.add(new SortResourcesPlugin(arguments));
-            return lst;
-        } catch (IOException ex) {
-            throw new PluginException(ex);
-        }
-    }
-
-    @Override
-    public Type getType() {
-        return Type.RESOURCE_PLUGIN;
-    }
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StringSharingPlugin.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StringSharingPlugin.java	Mon Dec 21 14:42:17 2015 +0100
@@ -46,8 +46,10 @@
 import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
@@ -55,12 +57,15 @@
 import jdk.internal.jimage.decompressor.SignatureParser;
 import jdk.internal.jimage.decompressor.StringSharingDecompressor;
 import jdk.tools.jlink.internal.PoolImpl;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-import jdk.tools.jlink.plugins.PluginException;
-import jdk.tools.jlink.plugins.Pool;
-import jdk.tools.jlink.plugins.Pool.ModuleData;
+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.internal.ResourcePrevisitor;
 import jdk.tools.jlink.internal.StringTable;
+import jdk.tools.jlink.internal.Utils;
 
 /**
  *
@@ -69,11 +74,19 @@
  */
 public class StringSharingPlugin implements TransformerPlugin, ResourcePrevisitor {
 
+    public static final String NAME = "compact-cp";
+
+    public static final PluginOption NAME_OPTION
+            = new Builder(NAME).
+            description(PluginsResourceBundle.getDescription(NAME)).
+            argumentDescription(PluginsResourceBundle.getArgument(NAME)).
+            build();
     private static final int[] SIZES;
 
     static {
         SIZES = StringSharingDecompressor.getSizes();
     }
+
     private static final class CompactCPHelper {
 
         private static final class DescriptorsScanner {
@@ -263,8 +276,7 @@
                                         return strings.addString(type);
                                     }).collect(Collectors.toList());
                             if (!indexes.isEmpty()) {
-                                out.write(StringSharingDecompressor.
-                                        EXTERNALIZED_STRING_DESCRIPTOR);
+                                out.write(StringSharingDecompressor.EXTERNALIZED_STRING_DESCRIPTOR);
                                 int sigIndex = strings.addString(parseResult.formatted);
                                 byte[] compressed
                                         = CompressIndexes.compress(sigIndex);
@@ -328,46 +340,78 @@
         }
     }
 
-    private final Predicate<String> predicate;
+    private Predicate<String> predicate;
 
-    public StringSharingPlugin(String[] patterns) throws IOException {
+    public StringSharingPlugin() {
+
+    }
+
+    StringSharingPlugin(String[] patterns) throws IOException {
         this(new ResourceFilter(patterns));
     }
 
-    public StringSharingPlugin(Predicate<String> predicate) {
+    StringSharingPlugin(Predicate<String> predicate) {
         this.predicate = predicate;
     }
 
     @Override
+    public Set<PluginType> getType() {
+        Set<PluginType> set = new HashSet<>();
+        set.add(CATEGORY.COMPRESSOR);
+        return Collections.unmodifiableSet(set);
+    }
+
+    @Override
+    public PluginOption getOption() {
+        return NAME_OPTION;
+    }
+
+    @Override
     public void visit(Pool in, Pool result) {
         CompactCPHelper visit = new CompactCPHelper();
         in.visit((resource) -> {
             ModuleData res = resource;
-                if (predicate.test(resource.getPath()) && resource.getPath().endsWith(".class")) {
-                    byte[] compressed = null;
-                    try {
-                        compressed = visit.transform(resource, result, ((PoolImpl)in).getStringTable());
-                    } catch (Exception ex) {
-                        throw new PluginException(ex);
-                    }
-                    res = PoolImpl.newCompressedResource(resource,
-                            ByteBuffer.wrap(compressed), getName(), null,
-                            ((PoolImpl)in).getStringTable(), in.getByteOrder());
+            if (predicate.test(resource.getPath()) && resource.getPath().endsWith(".class")) {
+                byte[] compressed = null;
+                try {
+                    compressed = visit.transform(resource, result, ((PoolImpl) in).getStringTable());
+                } catch (Exception ex) {
+                    throw new PluginException(ex);
                 }
-                return res;
+                res = PoolImpl.newCompressedResource(resource,
+                        ByteBuffer.wrap(compressed), getName(), null,
+                        ((PoolImpl) in).getStringTable(), in.getByteOrder());
+            }
+            return res;
         }, result);
     }
 
     @Override
     public String getName() {
-        return StringSharingProvider.NAME;
+        return NAME;
+    }
+
+    @Override
+    public String getDescription() {
+        return PluginsResourceBundle.getDescription(NAME);
+    }
+
+    @Override
+    public void configure(Map<PluginOption, String> config) {
+        try {
+            String val = config.get(NAME_OPTION);
+            predicate = new ResourceFilter(Utils.listParser.apply(val));
+        } catch (IOException ex) {
+            throw new PluginException(ex);
+        }
     }
 
     @Override
     public void previsit(Pool resources, StringTable strings) {
         CompactCPHelper preVisit = new CompactCPHelper();
         for (ModuleData resource : resources.getContent()) {
-            if (resource.getPath().endsWith(".class") && predicate.test(resource.getPath())) {
+            if (resource.getType().equals(Pool.ModuleDataType.CLASS_OR_RESOURCE)
+                    && resource.getPath().endsWith(".class") && predicate.test(resource.getPath())) {
                 try {
                     preVisit.transform(resource, null, strings);
                 } catch (Exception ex) {
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StringSharingProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package jdk.tools.jlink.internal.plugins;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import jdk.tools.jlink.plugins.TransformerCmdProvider;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-import jdk.tools.jlink.plugins.PluginException;
-import jdk.tools.jlink.plugins.Pool;
-
-/**
- *
- * Compact CP provider.
- */
-public class StringSharingProvider extends TransformerCmdProvider {
-
-    public static final String NAME = "compact-cp";
-
-    public StringSharingProvider() {
-        super(NAME, PluginsResourceBundle.getDescription(NAME));
-    }
-
-    @Override
-    public String getCategory() {
-        return TransformerCmdProvider.COMPRESSOR;
-    }
-
-    @Override
-    public String getToolArgument() {
-        return PluginsResourceBundle.getArgument(NAME);
-    }
-
-    @Override
-    public String getToolOption() {
-        return NAME;
-    }
-
-    @Override
-    public Map<String, String> getAdditionalOptions() {
-        return null;
-    }
-
-    @Override
-    public List<TransformerPlugin> newPlugins(String[] arguments, Map<String, String> otherOptions) {
-        List<TransformerPlugin> ret = new ArrayList<>();
-        try {
-            ret.add(new StringSharingPlugin(arguments));
-        } catch (IOException ex) {
-            throw new PluginException(ex);
-        }
-        return ret;
-    }
-
-    @Override
-    public Type getType() {
-        return Type.RESOURCE_PLUGIN;
-    }
-
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripDebugPlugin.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripDebugPlugin.java	Mon Dec 21 14:42:17 2015 +0100
@@ -24,20 +24,44 @@
  */
 package jdk.tools.jlink.internal.plugins;
 
+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 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;
 
 /**
  *
  * Strip debug attributes plugin
  */
-final class StripDebugPlugin extends AsmPlugin {
+public final class StripDebugPlugin extends AsmPlugin {
+
+    public static final String NAME = "strip-java-debug";
+    public static final PluginOption NAME_OPTION
+            = new Builder(NAME).
+            description(PluginsResourceBundle.getDescription(NAME)).
+            hasOnOffArgument().build();
 
     @Override
     public String getName() {
-        return StripDebugProvider.NAME;
+        return NAME;
+    }
+
+    @Override
+    public PluginOption getOption() {
+        return NAME_OPTION;
+    }
+
+    @Override
+    public Set<PluginType> getType() {
+        Set<PluginType> set = new HashSet<>();
+        set.add(CATEGORY.TRANSFORMER);
+        return Collections.unmodifiableSet(set);
     }
 
     @Override
@@ -53,4 +77,14 @@
             return writer;
         });
     }
+
+    @Override
+    public String getDescription() {
+        return PluginsResourceBundle.getDescription(NAME);
+    }
+
+    @Override
+    public void configure(Map<PluginOption, String> config) {
+
+    }
 }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripDebugProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +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.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import jdk.tools.jlink.plugins.TransformerOnOffProvider;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-import jdk.tools.jlink.plugins.TransformerPluginProvider;
-
-/**
- *
- * Strip Debug attributes plugin
- */
-public final class StripDebugProvider extends TransformerOnOffProvider {
-
-    public static final String NAME = "strip-java-debug";
-
-    public StripDebugProvider() {
-        super(NAME, PluginsResourceBundle.getDescription(NAME));
-    }
-
-    @Override
-    public String getCategory() {
-        return TransformerPluginProvider.TRANSFORMER;
-    }
-
-    @Override
-    public String getToolOption() {
-        return NAME;
-    }
-
-    @Override
-    public Map<String, String> getAdditionalOptions() {
-        return null;
-    }
-
-    @Override
-    public Type getType() {
-        return Type.RESOURCE_PLUGIN;
-    }
-
-    @Override
-    public List<TransformerPlugin> createPlugins(Map<String, String> otherOptions) {
-        List<TransformerPlugin> lst = new ArrayList<>();
-        lst.add(new StripDebugPlugin());
-        return lst;
-    }
-
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripNativeCommandsPlugin.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripNativeCommandsPlugin.java	Mon Dec 21 14:42:17 2015 +0100
@@ -24,18 +24,43 @@
  */
 package jdk.tools.jlink.internal.plugins;
 
-import jdk.tools.jlink.plugins.Pool;
-import jdk.tools.jlink.plugins.TransformerPlugin;
+import java.util.Collections;
+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;
 
 /**
  *
  * Strip Native Commands plugin
  */
-final class StripNativeCommandsPlugin implements TransformerPlugin {
+public final class StripNativeCommandsPlugin implements TransformerPlugin {
+
+    public static final String NAME = "strip-native-commands";
+
+    private static final PluginOption NAME_OPTION
+            = new Builder(NAME).
+            description(PluginsResourceBundle.getDescription(NAME)).
+            hasOnOffArgument().build();
 
     @Override
     public String getName() {
-        return StripNativeCommandsProvider.NAME;
+        return NAME;
+    }
+
+    @Override
+    public Set<PluginType> getType() {
+        Set<PluginType> set = new HashSet<>();
+        set.add(CATEGORY.FILTER);
+        return Collections.unmodifiableSet(set);
+    }
+
+    @Override
+    public PluginOption getOption() {
+        return NAME_OPTION;
     }
 
     @Override
@@ -44,4 +69,14 @@
             return file.getType() == Pool.ModuleDataType.NATIVE_CMD ? null : file;
         }, out);
     }
+
+    @Override
+    public String getDescription() {
+        return PluginsResourceBundle.getDescription(NAME);
+    }
+
+    @Override
+    public void configure(Map<PluginOption, String> config) {
+
+    }
 }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StripNativeCommandsProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +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.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import jdk.tools.jlink.plugins.TransformerOnOffProvider;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-import jdk.tools.jlink.plugins.TransformerPluginProvider;
-
-/**
- *
- * Exclude native commands (such as java/java.exe) from the image.
- */
-public final class StripNativeCommandsProvider extends TransformerOnOffProvider {
-    public static final String NAME = "strip-native-commands";
-    public StripNativeCommandsProvider() {
-        super(NAME, PluginsResourceBundle.getDescription(NAME));
-    }
-
-
-    @Override
-    public String getCategory() {
-        return TransformerPluginProvider.FILTER;
-    }
-
-    @Override
-    public String getToolOption() {
-        return NAME;
-    }
-
-    @Override
-    public Map<String, String> getAdditionalOptions() {
-        return null;
-    }
-
-    @Override
-    public Type getType() {
-        return Type.IMAGE_FILE_PLUGIN;
-    }
-
-    @Override
-    public List<TransformerPlugin> createPlugins(Map<String, String> otherOptions) {
-        List<TransformerPlugin> lst = new ArrayList<>();
-        lst.add(new StripNativeCommandsPlugin());
-        return lst;
-    }
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ZipCompressProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +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.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import jdk.tools.jlink.plugins.PluginException;
-import jdk.tools.jlink.plugins.TransformerCmdProvider;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-import jdk.tools.jlink.plugins.TransformerPluginProvider;
-
-/**
- *
- * ZIP compression plugin provider
- */
-public class ZipCompressProvider extends TransformerCmdProvider {
-    public static final String NAME = "zip";
-    public ZipCompressProvider() {
-        super(NAME, PluginsResourceBundle.getDescription(NAME));
-    }
-
-    @Override
-    public String getCategory() {
-        return TransformerPluginProvider.COMPRESSOR;
-    }
-
-    @Override
-    public String getToolArgument() {
-        return PluginsResourceBundle.getArgument(NAME);
-    }
-
-    @Override
-    public String getToolOption() {
-        return NAME;
-    }
-
-    @Override
-    public Map<String, String> getAdditionalOptions() {
-        return null;
-    }
-
-    @Override
-    public List<TransformerPlugin> newPlugins(String[] arguments, Map<String, String> otherOptions) {
-        try {
-            List<TransformerPlugin> lst = new ArrayList<>();
-            lst.add(new ZipPlugin(arguments));
-            return lst;
-        } catch (IOException ex) {
-            throw new PluginException(ex);
-        }
-    }
-
-    @Override
-    public Type getType() {
-        return Type.RESOURCE_PLUGIN;
-    }
-
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ZipPlugin.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ZipPlugin.java	Mon Dec 21 14:42:17 2015 +0100
@@ -27,20 +27,38 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+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.internal.PoolImpl;
-import jdk.tools.jlink.plugins.Pool;
-import jdk.tools.jlink.plugins.Pool.ModuleData;
-import jdk.tools.jlink.plugins.TransformerPlugin;
+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.internal.Utils;
 
 /**
  *
  * ZIP Compression plugin
  */
-final class ZipPlugin implements TransformerPlugin {
+public final class ZipPlugin implements TransformerPlugin {
 
-    private final Predicate<String> predicate;
+    public static final String NAME = "zip";
+    public static final PluginOption NAME_OPTION
+            = new Builder(NAME).
+            description(PluginsResourceBundle.getDescription(NAME)).
+            argumentDescription(PluginsResourceBundle.getArgument(NAME)).build();
+    private Predicate<String> predicate;
+
+    public ZipPlugin() {
+
+    }
 
     ZipPlugin(String[] patterns) throws IOException {
         this(new ResourceFilter(patterns));
@@ -52,7 +70,34 @@
 
     @Override
     public String getName() {
-        return ZipCompressProvider.NAME;
+        return NAME;
+    }
+
+    @Override
+    public Set<PluginType> getType() {
+        Set<PluginType> set = new HashSet<>();
+        set.add(CATEGORY.COMPRESSOR);
+        return Collections.unmodifiableSet(set);
+    }
+
+    @Override
+    public PluginOption getOption() {
+        return NAME_OPTION;
+    }
+
+    @Override
+    public String getDescription() {
+        return PluginsResourceBundle.getDescription(NAME);
+    }
+
+    @Override
+    public void configure(Map<PluginOption, String> config) {
+        try {
+            String val = config.get(NAME_OPTION);
+            predicate = new ResourceFilter(Utils.listParser.apply(val));
+        } catch (IOException ex) {
+            throw new PluginException(ex);
+        }
     }
 
     static byte[] compress(byte[] bytesIn) {
@@ -83,12 +128,13 @@
     public void visit(Pool in, Pool out) {
         in.visit((resource) -> {
             ModuleData res = resource;
-            if (predicate.test(resource.getPath())) {
+            if (resource.getType().equals(ModuleDataType.CLASS_OR_RESOURCE)
+                    && predicate.test(resource.getPath())) {
                 byte[] compressed;
                 compressed = compress(resource.getBytes());
                 res = PoolImpl.newCompressedResource(resource,
                         ByteBuffer.wrap(compressed), getName(), null,
-                        ((PoolImpl)in).getStringTable(), in.getByteOrder());
+                        ((PoolImpl) in).getStringTable(), in.getByteOrder());
             }
             return res;
         }, out);
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPlugin.java	Mon Dec 21 14:42:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPlugin.java	Mon Dec 21 14:42:17 2015 +0100
@@ -25,8 +25,10 @@
 package jdk.tools.jlink.internal.plugins.asm;
 
 import java.util.Objects;
-import jdk.tools.jlink.plugins.TransformerPlugin;
-import jdk.tools.jlink.plugins.Pool;
+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.internal.PoolImpl;
 
 /**
  * Extend this class to develop your own plugin in order to transform jimage
@@ -39,10 +41,18 @@
     }
 
     @Override
-    public void visit(Pool inResources, Pool outResources) {
-        Objects.requireNonNull(inResources);
+    public void visit(Pool allContent, Pool outResources) {
+        Objects.requireNonNull(allContent);
         Objects.requireNonNull(outResources);
-        AsmPools pools = new AsmPools(inResources);
+        PoolImpl resources = new PoolImpl(allContent.getByteOrder());
+        for(ModuleData md : allContent.getContent()) {
+            if(md.getType().equals(Pool.ModuleDataType.CLASS_OR_RESOURCE)) {
+                resources.add(md);
+            } else {
+                outResources.add(md);
+            }
+        }
+        AsmPools pools = new AsmPools(resources);
         visit(pools);
         pools.fillOutputResources(outResources);
     }
@@ -52,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.plugins.PluginException
+     * @throws jdk.tools.jlink.api.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:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPool.java	Mon Dec 21 14:42:17 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.plugins.Pool;
+import jdk.tools.jlink.api.plugin.transformer.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.plugins.PluginException
+         * @throws jdk.tools.jlink.api.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.plugins.PluginException
+         * @throws jdk.tools.jlink.api.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.plugins.PluginException
+         * @throws jdk.tools.jlink.api.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.plugins.PluginException
+         * @throws jdk.tools.jlink.api.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.plugins.PluginException
+         * @throws jdk.tools.jlink.api.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.plugins.PluginException If the resource to
+         * @throws jdk.tools.jlink.api.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.plugins.PluginException
+         * @throws jdk.tools.jlink.api.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.plugins.PluginException
+     * @throws jdk.tools.jlink.api.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.plugins.PluginException
+     * @throws jdk.tools.jlink.api.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.plugins.PluginException
+     * @throws jdk.tools.jlink.api.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.plugins.PluginException
+     * @throws jdk.tools.jlink.api.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.plugins.PluginException
+     * @throws jdk.tools.jlink.api.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:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPoolImpl.java	Mon Dec 21 14:42:17 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.plugins.PluginException;
-import jdk.tools.jlink.plugins.Pool;
-import jdk.tools.jlink.plugins.Pool.ModuleData;
+import jdk.tools.jlink.api.plugin.PluginException;
+import jdk.tools.jlink.api.plugin.transformer.Pool;
+import jdk.tools.jlink.api.plugin.transformer.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:13 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPools.java	Mon Dec 21 14:42:17 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.plugins.PluginException;
-import jdk.tools.jlink.plugins.Pool;
-import jdk.tools.jlink.plugins.Pool.ModuleData;
+import jdk.tools.jlink.api.plugin.PluginException;
+import jdk.tools.jlink.api.plugin.transformer.Pool;
+import jdk.tools.jlink.api.plugin.transformer.Pool.ModuleData;
 
 /**
  * A container for pools of ClassReader and other resource files. A pool of all
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/CmdPluginProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +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.plugins;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-/**
- * An abstract command line plugin provider class. Such a provider has a command
- * line option, an optional argument, and optional additional options.
- * @param <T> A plugin class
- */
-public interface CmdPluginProvider<T extends Plugin> {
-
-    /**
-     * This property is the main argument (if any) passed to the plugin.
-     */
-    public static final String TOOL_ARGUMENT_PROPERTY = "argument";
-
-    public static final String FIRST = "FIRST";
-    public static final String LAST = "LAST";
-
-    /**
-     * Returns the description
-     *
-     * @return null, meaning no argument, otherwise a description of the
-     * structure of the argument.
-     */
-    public abstract String getToolArgument();
-
-    /**
-     * The command line option that identifies this provider.
-     *
-     * @return The command line option of this provider.
-     */
-    public abstract String getToolOption();
-
-    /**
-     * Additional command line options and their associated description.
-     *
-     * @return A map of the option to description mapping.
-     */
-    public abstract Map<String, String> getAdditionalOptions();
-
-    public default List<T> newPlugins(Map<String, Object> conf) {
-        Map<String, String> config = toString(conf);
-        String[] arguments = null;
-        Collection<String> options = Collections.emptyList();
-        if (getAdditionalOptions() != null) {
-            options = getAdditionalOptions().keySet();
-        }
-        Map<String, String> otherOptions = new HashMap<>();
-        for (Entry<String, String> a : config.entrySet()) {
-            if (options.contains(a.getKey())) {
-                otherOptions.put(a.getKey(), a.getValue());
-                continue;
-            }
-            switch (a.getKey()) {
-                case TOOL_ARGUMENT_PROPERTY: {
-                    arguments = a.getValue().
-                            split(",");
-                    for (int i = 0; i < arguments.length; i++) {
-                        arguments[i] = arguments[i].trim();
-                    }
-                    break;
-                }
-            }
-        }
-        return newPlugins(arguments, otherOptions);
-    }
-
-    /**
-     * Concrete sub-classes of this abstract class must implement this method.
-     *
-     * @param arguments The main option value.
-     * @param otherOptions The additional option values.
-     * @return An array of plugins.
-     */
-    public abstract List<T> newPlugins(String[] arguments,
-            Map<String, String> otherOptions);
-
-    static Map<String, String> toString(Map<String, Object> input) {
-        Map<String, String> map = new HashMap<>();
-        for (Entry<String, Object> entry : input.entrySet()) {
-            if (!(entry.getKey() instanceof String)) {
-                throw new RuntimeException("Option name should be a String");
-            }
-            if (!(entry.getValue() instanceof String)) {
-                if (entry.getValue() != null) {
-                    throw new RuntimeException("Option value should be String for "
-                            + entry.getKey());
-                }
-            }
-            String k = entry.getKey();
-            @SuppressWarnings("unchecked")
-            String v = entry.getValue() == null ? "" : (String) entry.getValue();
-            map.put(k, v);
-        }
-        return map;
-    }
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/DefaultImageBuilder.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,384 +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.plugins;
-
-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.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.internal.BasicImageWriter;
-import jdk.tools.jlink.internal.JvmHandler;
-import jdk.tools.jlink.internal.plugins.FileCopierProvider.SymImageFile;
-import jdk.tools.jlink.plugins.Pool.ModuleData;
-
-/**
- *
- * Default Image Builder.
- */
-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;
-        }
-    }
-
-    private final Path root;
-    private final Path mdir;
-    private final boolean genBom;
-    private Set<String> modules;
-
-    public DefaultImageBuilder(Map<String, Object> properties, Path root) throws IOException {
-        Objects.requireNonNull(root);
-
-        genBom = properties.containsKey(DefaultImageBuilderProvider.GEN_BOM);
-
-        this.root = root;
-        this.mdir = root.resolve(root.getFileSystem().getPath("lib", "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 pool, List<ModuleData> removedFiles,
-            String bom, Pool resources) {
-        try {
-            Pool files = new JvmHandler().handlePlatforms(pool, removedFiles);
-
-            for (ModuleData f : files.getContent()) {
-                accept(f);
-            }
-            modules = resources.getModulePackages().keySet();
-            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(resources, 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);
-    }
-
-    @Override
-    public void storeJavaLauncherOptions(ExecutableImage img, List<String> args) {
-        try {
-            patchScripts(img, args);
-        } catch (IOException ex) {
-            throw new PluginException(ex);
-        }
-    }
-
-    // This is experimental, we should get rid-off the scripts in a near future
-    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);
-                }
-            });
-        }
-    }
-
-    @Override
-    public String getName() {
-        return DefaultImageBuilderProvider.NAME;
-    }
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/DefaultImageBuilderProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,141 +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.plugins;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
-import static jdk.tools.jlink.plugins.DefaultImageBuilder.isWindows;
-
-/**
- * Default Image Builder provider.
- */
-public class DefaultImageBuilderProvider extends ImageBuilderProvider {
-
-    public static final String GEN_BOM = "genbom";
-    public static final String JIMAGE_NAME_PROPERTY = "jimage.name";
-    public static final String NAME = "default-image-builder";
-
-    private static final Map<String, String> OPTIONS = new HashMap<>();
-
-    static {
-        OPTIONS.put(GEN_BOM, PluginsResourceBundle.getOption(NAME, GEN_BOM));
-    }
-
-    public DefaultImageBuilderProvider() {
-        super(NAME, PluginsResourceBundle.getDescription(NAME));
-    }
-
-    @Override
-    public Map<String, String> getOptions() {
-        return OPTIONS;
-    }
-
-    @Override
-    public boolean hasArgument(String option) {
-        return false;
-    }
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-    @Override
-    public String getDescription() {
-        return PluginsResourceBundle.getDescription(NAME);
-    }
-
-    @Override
-    public ExecutableImage canExecute(Path root) {
-        if (Files.exists(root.resolve("bin").resolve(getJavaProcessName()))) {
-            return new DefaultImageBuilder.DefaultExecutableImage(root,
-                    retrieveModules(root));
-        }
-        return null;
-    }
-
-    static String getJavaProcessName() {
-        return isWindows() ? "java.exe" : "java";
-    }
-
-    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;
-    }
-
-    @Override
-    public void storeLauncherOptions(ExecutableImage image, List<String> arguments) {
-        try {
-            DefaultImageBuilder.patchScripts(image, arguments);
-        } catch (IOException ex) {
-            throw new PluginException(ex);
-        }
-    }
-
-    @Override
-    public List<? extends ImageBuilder> newPlugins(Map<String, Object> config) {
-        try {
-            @SuppressWarnings("unchecked")
-            Path imageOutDir = (Path) config.get(ImageBuilderProvider.IMAGE_PATH_KEY);
-            if (Files.exists(imageOutDir)) {
-                throw new PluginException(PluginsResourceBundle.
-                        getMessage("err.dir.already.exits", imageOutDir));
-            }
-            List<ImageBuilder> lst = new ArrayList<>();
-            lst.add(new DefaultImageBuilder(config, imageOutDir));
-            return lst;
-        } catch (IOException ex) {
-            throw new UncheckedIOException(ex);
-        }
-    }
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/ExecutableImage.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +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.plugins;
-
-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 class ExecutableImage {
-
-    private final Path home;
-    private final List<String> args;
-    private final Set<String> modules;
-
-    public 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;
-    }
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/ImageBuilder.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +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.plugins;
-
-import java.io.DataOutputStream;
-import java.util.List;
-import jdk.tools.jlink.plugins.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 extends Plugin {
-
-    /**
-     * Store the external files.
-     *
-     * @param files Pool of files.
-     * @param removed List of files that have been removed (if any).
-     * @param bom The options used to build the image
-     * @param resources Pool of resources that have been stored in the jimage
-     * file.
-     * @throws PluginException
-     */
-    public void storeFiles(Pool files, List<ModuleData> removed,
-            String bom, Pool resources);
-
-    /**
-     * The OutputStream to store the jimage file.
-     *
-     * @return The output stream
-     * @throws PluginException
-     */
-    public DataOutputStream getJImageOutputStream();
-
-    /**
-     * Gets executable image.
-     *
-     * @return The executable image.
-     * @throws PluginException
-     */
-    public ExecutableImage getExecutableImage();
-
-    /**
-     * Store the options that would have bee nadded by the post processing
-     * @param image
-     * @param args
-     * @throws PluginException
-     */
-    public void storeJavaLauncherOptions(ExecutableImage image, List<String> args);
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/ImageBuilderProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +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.plugins;
-
-import java.nio.file.Path;
-import java.util.List;
-import java.util.Map;
-import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
-
-/**
- * Extend this class and make your class available to the ServiceLoader in order
- * to expose your ImageBuilder.
- */
-public abstract class ImageBuilderProvider extends PluginProvider {
-
-    public static final String IMAGE_PATH_KEY = "jlink.image.path";
-
-    public ImageBuilderProvider(String name, String description) {
-        super(name, description);
-    }
-
-    @Override
-    public abstract List<? extends ImageBuilder> newPlugins(Map<String, Object> config);
-
-    /**
-     * Image builder provider can execute the image located in the passed dir
-     *
-     * @param root The image directory.
-     * @return An ExecutableImage if runnable, otherwise null.
-     */
-    public abstract ExecutableImage canExecute(Path root);
-
-    /**
-     * Ask the provider to store some options in the image launcher(s).
-     *
-     * @param image
-     * @param arguments The arguments to add to the launcher.
-     * @throws PluginException
-     */
-    public abstract void storeLauncherOptions(ExecutableImage image, List<String> arguments)
-            throws PluginException;
-
-    /**
-     * Options to configure the image builder.
-     *
-     * @return The option name / description mapping
-     */
-    public abstract Map<String, String> getOptions();
-
-    /**
-     * Check if an option expects an argument.
-     *
-     * @param option
-     * @return true if an argument is expected. False otherwise.
-     */
-    public abstract boolean hasArgument(String option);
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/Jlink.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,413 +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.plugins;
-
-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.JlinkTask;
-
-/**
- * Jlink, entry point to interact with jlink support.
- *
- */
-public final class Jlink {
-
-    /**
-     * A plugin configuration.
-     */
-    public static class PluginConfiguration {
-
-        private final String name;
-        private final Map<String, Object> config;
-
-        /**
-         * A configuration.
-         *
-         * @param name Plugin name
-         * @param config Plugin configuration. Can be null;
-         */
-        public PluginConfiguration(String name, Map<String, Object> config) {
-            Objects.requireNonNull(name);
-            this.name = name;
-            this.config = config == null ? Collections.emptyMap() : config;
-        }
-
-        /**
-         * @return the name
-         */
-        public String getName() {
-            return name;
-        }
-
-        /**
-         * @return the config
-         */
-        public Map<String, Object> getConfig() {
-            return config;
-        }
-
-        @Override
-        public boolean equals(Object other) {
-            if (!(other instanceof PluginConfiguration)) {
-                return false;
-            }
-            return name.equals(((PluginConfiguration) other).name);
-        }
-
-        @Override
-        public int hashCode() {
-            int hash = 5;
-            hash = 41 * hash + Objects.hashCode(this.name);
-            return hash;
-        }
-    }
-
-    /**
-     * A plugin located inside a stack of plugins. Such plugin has an index in
-     * the stack.
-     */
-    public static final class OrderedPluginConfiguration extends PluginConfiguration {
-
-        private final int index;
-        private final boolean absIndex;
-
-        /**
-         * A plugin inside the stack configuration.
-         *
-         * @param name Plugin name
-         * @param index index in the plugin stack. Must be > 0.
-         * @param absIndex true, the index is absolute otherwise index is within
-         * the category.
-         * @param config Plugin configuration. Can be null;
-         */
-        public OrderedPluginConfiguration(String name, int index, boolean absIndex,
-                Map<String, Object> config) {
-            super(name, config);
-            if (index < 0) {
-                throw new IllegalArgumentException("negative index");
-            }
-            this.index = index;
-            this.absIndex = absIndex;
-        }
-
-        /**
-         * @return the index
-         */
-        public int getIndex() {
-            return index;
-        }
-
-        @Override
-        public String toString() {
-            return getName() + "[" + index + "]";
-        }
-
-        /**
-         * @return the absIndex
-         */
-        public boolean isAbsoluteIndex() {
-            return absIndex;
-        }
-
-        @Override
-        public boolean equals(Object other) {
-            return super.equals(other);
-        }
-
-        @Override
-        public int hashCode() {
-            return super.hashCode();
-        }
-    }
-
-    /**
-     * A complete plugin configuration. Instances of this class are used to
-     * configure jlink.
-     */
-    public static final class PluginsConfiguration {
-
-        private final List<OrderedPluginConfiguration> transformerPluginsConfig;
-        private final List<OrderedPluginConfiguration> processorPluginsConfig;
-        private final PluginConfiguration imageBuilder;
-        private final String lastSorterPluginName;
-
-        /**
-         * Empty plugins configuration.
-         */
-        public PluginsConfiguration() {
-            this(Collections.emptyList(), Collections.emptyList(), null);
-        }
-
-        /**
-         * Plugins configuration.
-         *
-         * @param transformerPluginsConfig List of transformer plugins
-         * configuration.
-         * @param processorPluginsConfig List of processor plugins
-         * configuration.
-         * @param imageBuilder Image builder (null default builder).
-         */
-        public PluginsConfiguration(List<OrderedPluginConfiguration> transformerPluginsConfig,
-                List<OrderedPluginConfiguration> processorPluginsConfig,
-                PluginConfiguration imageBuilder) {
-            this(transformerPluginsConfig, processorPluginsConfig, imageBuilder, null);
-        }
-
-        /**
-         * Plugins configuration with a last sorter. No sorting can occur after
-         * the last sorter plugin.
-         *
-         * @param transformerPluginsConfig List of transformer plugins
-         * configuration.
-         * @param processorPluginsConfig List of processor plugins
-         * configuration.
-         * @param imageBuilder Image builder (null default builder).
-         * @param lastSorterPluginName Name of last sorter plugin, no sorting
-         * can occur after it.
-         */
-        public PluginsConfiguration(List<OrderedPluginConfiguration> transformerPluginsConfig,
-                List<OrderedPluginConfiguration> processorPluginsConfig,
-                PluginConfiguration imageBuilder, String lastSorterPluginName) {
-            this.transformerPluginsConfig = transformerPluginsConfig == null ? Collections.emptyList()
-                    : transformerPluginsConfig;
-            this.processorPluginsConfig = processorPluginsConfig == null ? Collections.emptyList()
-                    : processorPluginsConfig;
-            this.imageBuilder = imageBuilder;
-            this.lastSorterPluginName = lastSorterPluginName;
-        }
-
-        /**
-         * @return the transformer pluginsConfig
-         */
-        public List<OrderedPluginConfiguration> getTransformerPluginsConfig() {
-            return transformerPluginsConfig;
-        }
-
-        /**
-         * @return the post processors pluginsConfig
-         */
-        public List<OrderedPluginConfiguration> getPostProcessorPluginsConfig() {
-            return processorPluginsConfig;
-        }
-
-        /**
-         * @return the imageBuilder
-         */
-        public PluginConfiguration 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 (PluginConfiguration p : transformerPluginsConfig) {
-                pluginsBuilder.append(p).append(",");
-            }
-            for (PluginConfiguration p : processorPluginsConfig) {
-                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 List<Path> pluginpaths;
-        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.pluginpaths = pluginpaths == null ? Collections.emptyList() : pluginpaths;
-            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;
-        }
-
-        /**
-         * @return the pluginpaths
-         */
-        public List<Path> getPluginpaths() {
-            return pluginpaths;
-        }
-
-        @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");
-
-            StringBuilder pluginsBuilder = new StringBuilder();
-            for (Path p : pluginpaths) {
-                pluginsBuilder.append(p).append(",");
-            }
-            builder.append("pluginspaths=").append(pluginsBuilder).append("\n");
-            builder.append("endian=").append(endian).append("\n");
-            return builder.toString();
-        }
-    }
-
-    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.
-     *
-     * @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);
-        }
-    }
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/JlinkPermission.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +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.plugins;
-
-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/plugins/OnOffPluginProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +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.plugins;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- *
- * On/Off plugin provider support class.
- * @param <T>
- */
-public interface OnOffPluginProvider<T extends Plugin> extends CmdPluginProvider<T> {
-
-    public static final String ON_ARGUMENT = "on";
-    public static final String OFF_ARGUMENT = "off";
-
-    @Override
-    public default List<T> newPlugins(String[] arguments,
-            Map<String, String> otherOptions) {
-        Objects.requireNonNull(arguments);
-        if (arguments.length != 1) {
-            throw new PluginException("Invalid number of arguments expecting "
-                    + getToolArgument());
-        }
-        if (!OFF_ARGUMENT.equals(arguments[0])
-                && !ON_ARGUMENT.equals(arguments[0])) {
-            throw new PluginException("Invalid argument " + arguments[0]
-                    + ", expecting " + ON_ARGUMENT + " or "
-                    + OFF_ARGUMENT);
-        }
-        if (OFF_ARGUMENT.equals(arguments[0])) {
-            return Collections.emptyList();
-        }
-        return createPlugins(otherOptions);
-    }
-
-    public List<T> createPlugins(Map<String, String> otherOptions);
-
-    @Override
-    public default String getToolArgument() {
-        return ON_ARGUMENT + "|"
-                + OFF_ARGUMENT;
-    }
-
-    public default boolean isEnabledByDefault() {
-        return false;
-    }
-
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/OrderedPluginProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +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.plugins;
-
-/**
- * An abstract plugin provider class for plugins that are ordered.
- */
-public abstract class OrderedPluginProvider extends PluginProvider {
-
-    public static enum ORDER {
-        FIRST,
-        ANY,
-        LAST
-    }
-
-    public enum Type {
-        RESOURCE_PLUGIN,
-        IMAGE_FILE_PLUGIN
-    }
-
-    protected OrderedPluginProvider(String name, String description) {
-        super(name, description);
-    }
-
-    /**
-     * Order of the plugin within its category. By default ANY.
-     *
-     * @return Expected order.
-     */
-    public ORDER getOrder() {
-        return ORDER.ANY;
-    }
-
-    /**
-     * A category in which to understand the order.
-     *
-     * @return
-     */
-    public abstract String getCategory();
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/Plugin.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +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.plugins;
-
-/**
- * Implement this interface to develop your own plugin.
- */
-public interface Plugin {
-
-    /**
-     * Plugin unique name.
-     * @return The plugin name.
-     */
-    public String getName();
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/PluginException.java	Mon Dec 21 14:42:13 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.plugins;
-
-/**
- * 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/plugins/PluginProvider.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +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.plugins;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
-
-/**
- * An abstract plugin provider class. A provider has a name and a description.
- */
-public abstract class PluginProvider {
-
-    private final String name;
-    private final String description;
-
-    public PluginProvider(String name, String description) {
-        Objects.requireNonNull(name);
-        Objects.requireNonNull(description);
-        this.name = name;
-        this.description = description;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    /**
-     * An exposed provider wants to be advertised (e.g.: displayed in help).
-     *
-     * @return True, the provider is exposed, false the provider is hidden.
-     */
-    public boolean isExposed() {
-        return true;
-    }
-
-    /**
-     * Check if the provider can properly operate in the current context.
-     *
-     * @return true, the provider can operate
-     */
-    public boolean isFunctional() {
-        return true;
-    }
-
-    /**
-     * Return a message indicating the status of the provider.
-     *
-     * @param functional
-     * @return A status description.
-     */
-    public String getFunctionalStateDescription(boolean functional) {
-        return functional
-                ? PluginsResourceBundle.getMessage("main.status.ok")
-                : PluginsResourceBundle.getMessage("main.status.not.ok");
-    }
-
-    /**
-     * Create plugins based on passed configuration.
-     *
-     * @param config The plugins configuration.
-     * @return An array of plugins.
-     * @throws PluginException
-     */
-    public abstract List<? extends Plugin> newPlugins(Map<String, Object> config);
-}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/Pool.java	Mon Dec 21 14:42:13 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,361 +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.plugins;
-
-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;