changeset 14213:67a090f8d062

Rework and simplification of plugins config. Simplified ordering. Reviewed-by: jlaskey
author jfdenise
date Fri, 09 Oct 2015 18:01:08 +0200
parents 75c194907490
children 236a0b5f4df8
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/internal/ImagePluginConfiguration.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StringSharingProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ZipCompressProvider.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/OnOffPluginProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/PluginProvider.java src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties test/jdk/jigsaw/tools/jlink/CustomImageBuilderTest.java test/jdk/jigsaw/tools/jlink/JLink2Test.java test/jdk/jigsaw/tools/jlink/JLinkNegativeTest.java test/jdk/jigsaw/tools/jlink/JLinkPluginsTest.java test/jdk/jigsaw/tools/jlink/customplugin/plugin/CustomImageBuilder.java test/jdk/jigsaw/tools/jlink/plugins/LastSorterTest.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
diffstat 20 files changed, 464 insertions(+), 680 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java	Fri Oct 09 18:01:08 2015 +0200
@@ -201,7 +201,7 @@
         Path jimage = options.jimages.get(0).toPath();
 
         if (jimage.toFile().createNewFile()) {
-            ImagePluginStack pc = ImagePluginConfiguration.parseConfiguration(taskHelper.getPluginsProperties());
+            ImagePluginStack pc = ImagePluginConfiguration.parseConfiguration(taskHelper.getPluginsConfig());
             ExtractedImage img = new ExtractedImage(dirPath, pc, log, options.verbose);
             img.recreateJImage(jimage);
         } else {
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/JlinkTask.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/JlinkTask.java	Fri Oct 09 18:01:08 2015 +0200
@@ -47,7 +47,6 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.Properties;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -320,7 +319,7 @@
                                     genBOMContent(),
                                     options.endian);
 
-        imageHelper.createModularImage(taskHelper.getPluginsProperties());
+        imageHelper.createModularImage(taskHelper.getPluginsConfig());
     }
 
 
@@ -448,11 +447,6 @@
             sb.append("\n");
         }
 
-        String pluginsContent = optionsHelper.getPluginsConfig();
-        if (pluginsContent != null) {
-            sb.append("\n").append("# Plugins configuration\n");
-            sb.append(pluginsContent);
-        }
         return sb.toString();
     }
 
@@ -491,14 +485,6 @@
             this.order = order;
         }
 
-        void createModularImage(Properties properties) throws Exception {
-            ImagePluginStack pc = ImagePluginConfiguration.
-                    parseConfiguration(output,
-                            properties, pluginsLayer,
-                            bom);
-            ImageFileCreator.create(archives, order, pc);
-        }
-
         void createModularImage(PluginsConfiguration plugins) throws Exception {
             ImagePluginStack pc = ImagePluginConfiguration.
                     parseConfiguration(output, plugins, pluginsLayer, bom);
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/TaskHelper.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/TaskHelper.java	Fri Oct 09 18:01:08 2015 +0200
@@ -26,7 +26,6 @@
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.module.Configuration;
@@ -49,12 +48,18 @@
 import jdk.internal.module.ConfigurableModuleFinder.Phase;
 import jdk.tools.jlink.internal.ImagePluginProviderRepository;
 import jdk.tools.jlink.internal.ImagePluginConfiguration;
+import jdk.tools.jlink.plugins.OnOffPluginProvider;
 import jdk.tools.jlink.plugins.CmdPluginProvider;
 import jdk.tools.jlink.plugins.CmdResourcePluginProvider;
+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.StackedPluginConfiguration;
 import jdk.tools.jlink.plugins.OnOffImageFilePluginProvider;
 import jdk.tools.jlink.plugins.OnOffResourcePluginProvider;
 import jdk.tools.jlink.plugins.PluginProvider;
+import jdk.tools.jlink.plugins.PluginProvider.ORDER;
 
 /**
  *
@@ -135,6 +140,43 @@
                 Processing<PluginsOptions> processing, String... aliases) {
             super(hasArg, processing, aliases);
         }
+
+        @Override
+        public boolean matches(String opt) {
+            return super.matches(removeIndex(opt));
+        }
+    }
+
+    private int getIndex(String opt) throws BadArgs {
+        String orig = opt;
+        int i = opt.indexOf(":");
+        int index = -1;
+        if (i != -1) {
+            opt = opt.substring(i + 1);
+            if (opt.equals(CmdPluginProvider.FIRST)) {
+                index = 0;
+            } else {
+                if (opt.equals(CmdPluginProvider.LAST)) {
+                    index = Integer.MAX_VALUE;
+                } else {
+                    try {
+                        index = Integer.valueOf(opt);
+                    } catch (NumberFormatException ex) {
+                        throw newBadArgs("err.invalid.index", orig);
+                    }
+                }
+            }
+        }
+        return index;
+    }
+
+    private static String removeIndex(String opt) {
+        //has index? remove it
+        int i = opt.indexOf(":");
+        if (i != -1) {
+            opt = opt.substring(0, i);
+        }
+        return opt;
     }
 
     private static class HiddenPluginOption extends PluginOption {
@@ -153,14 +195,20 @@
     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 pluginsProperties;
+        private String imgBuilder;
+        private String lastSorter;
         private boolean listPlugins;
         private final Map<PluginProvider, Map<String, String>> plugins = new HashMap<>();
         private final Map<ImageBuilderProvider, Map<String, String>> builders = new HashMap<>();
         private final List<PluginOption> pluginsOptions = new ArrayList<>();
 
+        // The order in which options are declared is the stack order.
+        // Order is within provider category
+        private final Map<String, List<PluginProvider>> providersOrder = new HashMap<>();
+        private final Map<PluginProvider, Integer> providersIndexes = new HashMap<>();
         private PluginsOptions(String pp) throws BadArgs {
 
             if (pp != null) {
@@ -191,12 +239,18 @@
                         PluginOption option
                                 = new PluginOption(provider.getToolArgument() != null,
                                         (task, opt, arg) -> {
-                                            Map<String, String> m = plugins.get(provider);
+                                            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);
@@ -205,7 +259,7 @@
                                 optional.add(other);
                                 PluginOption otherOption = new PluginOption(true,
                                         (task, opt, arg) -> {
-                                            Map<String, String> m = plugins.get(provider);
+                                            Map<String, String> m = plugins.get(prov);
                                             if (m == null) {
                                                 m = new HashMap<>();
                                                 plugins.put(prov, m);
@@ -229,17 +283,22 @@
                         if (edefault) {
                             Map<String, String> m = new HashMap<>();
                             m.put(CmdPluginProvider.TOOL_ARGUMENT_PROPERTY,
-                                    ImagePluginConfiguration.ON_ARGUMENT);
+                                    OnOffPluginProvider.ON_ARGUMENT);
                             plugins.put(prov, m);
                         }
                     }
                 }
             }
-            pluginsOptions.add(new HiddenPluginOption(true,
+            pluginsOptions.add(new PluginOption(true,
                     (task, opt, arg) -> {
-                        pluginsProperties = arg;
+                        imgBuilder = arg;
                     },
-                    "--plugins-configuration"));
+                    "--image-builder"));
+            pluginsOptions.add(new PluginOption(true,
+                    (task, opt, arg) -> {
+                        lastSorter = arg;
+                    },
+                    "--resources-last-sorter "));
             pluginsOptions.add(new HiddenPluginOption(false,
                     (task, opt, arg) -> {
                         listPlugins = true;
@@ -269,6 +328,26 @@
             }
         }
 
+        private int computeIndex(PluginProvider prov) {
+            String category = prov.getCategory() == null ? NO_CATEGORY : prov.getCategory();
+            List<PluginProvider> order = providersOrder.get(category);
+            if (order == null) {
+                order = new ArrayList<>();
+                providersOrder.put(category, order);
+            }
+            order.add(prov);
+            int index = -1;
+            ORDER defaultOrder = prov.getOrder();
+            if (defaultOrder == ORDER.FIRST) {
+                index = 0;
+            } else {
+                if (defaultOrder == ORDER.LAST) {
+                    index = Integer.MAX_VALUE;
+                }
+            }
+            return index;
+        }
+
         private PluginOption getOption(String name) throws BadArgs {
             for (PluginOption o : pluginsOptions) {
                 if (o.matches(name)) {
@@ -278,39 +357,46 @@
             return null;
         }
 
-        private Properties getPluginsProperties() throws IOException {
-            Properties props = new Properties();
-            if (pluginsProperties != null) {
-                try (FileInputStream stream
-                        = new FileInputStream(pluginsProperties);) {
-                    props.load(stream);
-                } catch (FileNotFoundException ex) {
-                    throw new IOException(bundleHelper.
-                            getMessage("err.path.not.valid")
-                            + " " + pluginsProperties);
+        private PluginsConfiguration getPluginsConfig() throws IOException {
+            List<StackedPluginConfiguration> lst = new ArrayList<>();
+            for (Entry<PluginProvider, Map<String, String>> entry : plugins.entrySet()) {
+                PluginProvider provider = entry.getKey();
+                // User defined index?
+                Integer i = providersIndexes.get(provider);
+                if (i == null) {
+                    // Enabled by default provider. Find it an index.
+                    i = computeIndex(provider);
+                }
+                boolean absolute = false;
+                if (i == -1) {
+                    String category = provider.getCategory();
+                    if (provider.getCategory() == null) {
+                        absolute = true;
+                        category = NO_CATEGORY;
+                    }
+                    List<PluginProvider> lstProviders
+                            = providersOrder.get(category);
+                    i = lstProviders.indexOf(provider);
+                }
+                if (i == -1) {
+                    throw new IllegalArgumentException("Invalid index " + i);
+                }
+                Map<String, Object> config = new HashMap<>();
+                config.putAll(entry.getValue());
+                lst.add(new Jlink.StackedPluginConfiguration(provider.getName(), i, absolute, config));
+            }
+
+            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);
                 }
             }
-            for (Entry<PluginProvider, Map<String, String>> entry : plugins.entrySet()) {
-                PluginProvider provider = entry.getKey();
-                ImagePluginConfiguration.addPluginProperty(props, provider);
-                if (entry.getValue() != null) {
-                    for (Entry<String, String> opts : entry.getValue().entrySet()) {
-                        if (opts.getValue() != null) {
-                            props.setProperty(provider.getName() + "."
-                                    + opts.getKey(), opts.getValue());
-                        }
-                    }
-                }
-            }
-            for (Entry<ImageBuilderProvider, Map<String, String>> provs : builders.entrySet()) {
-                ImageBuilderProvider provider = provs.getKey();
-                for (Entry<String, String> entry : provs.getValue().entrySet()) {
-                    props.setProperty(provider.getName() + "."
-                            + entry.getKey(), entry.getValue() == null ? ""
-                                    : entry.getValue());
-                }
-            }
-            return props;
+            return new Jlink.PluginsConfiguration(lst,
+                    new Jlink.PluginConfiguration(imgBuilder, builderConfig),
+                    lastSorter);
         }
     }
 
@@ -421,7 +507,17 @@
                             i++;
                             a = defArgs.get(i);
                         }
-                        int overIndex = override.indexOf(arg);
+
+                        int overIndex = -1;
+                        // Retrieve the command line option
+                        // compare by erasing possible index
+                        for (int j = 0; j < override.size(); j++) {
+                            if (removeIndex(override.get(j)).equals(removeIndex(arg))) {
+                                overIndex = j;
+                                break;
+                            }
+                        }
+
                         if (overIndex >= 0) {
                             if (hasArgument) {
                                 a = override.get(overIndex + 1);
@@ -617,7 +713,7 @@
                     if (fact.getAdditionalOptions() != null) {
                         StringBuilder builder = new StringBuilder();
                         for (Entry<String, String> entry : fact.getAdditionalOptions().entrySet()) {
-                            builder.append("--" + entry.getKey()).append(" ").
+                            builder.append("--").append(entry.getKey()).append(" ").
                                     append(entry.getValue());
                         }
                         additionalOptions = builder.toString();
@@ -667,28 +763,6 @@
             return defaults;
         }
 
-        String getPluginsConfig() throws IOException {
-            String ret = null;
-            if (pluginOptions.pluginsProperties != null) {
-                Properties props = new Properties();
-                try (FileInputStream fis
-                        = new FileInputStream(pluginOptions.pluginsProperties)) {
-                    props.load(fis);
-                } catch (FileNotFoundException ex) {
-                    throw new IOException(bundleHelper.
-                            getMessage("err.path.not.valid")
-                            + " " + pluginOptions.pluginsProperties);
-                }
-                StringBuilder sb = new StringBuilder();
-                for (String str : props.stringPropertyNames()) {
-                    sb.append(str).append(" = ").append(props.getProperty(str)).
-                            append("\n");
-                }
-                ret = sb.toString();
-            }
-            return ret;
-        }
-
         Layer getPluginsLayer() {
             return pluginOptions.pluginsLayer;
         }
@@ -742,8 +816,8 @@
                 + bundleHelper.getMessage(key, args));
     }
 
-    public Properties getPluginsProperties() throws IOException {
-        return pluginOptions.getPluginsProperties();
+    public PluginsConfiguration getPluginsConfig() throws IOException {
+        return pluginOptions.getPluginsConfig();
     }
 
     public void showVersion(boolean full) {
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java	Fri Oct 09 18:01:08 2015 +0200
@@ -37,7 +37,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.Properties;
 
 import jdk.tools.jlink.plugins.ImageBuilder;
 import jdk.tools.jlink.plugins.ImageFilePlugin;
@@ -93,31 +92,6 @@
         }
     }
 
-    public static final String ON_ARGUMENT = "on";
-    public static final String OFF_ARGUMENT = "off";
-
-    public static final String IMAGE_BUILDER_PROPERTY = "jdk.jlink.image.builder";
-
-    public static final String RESOURCES_RADICAL_PROPERTY = "jdk.jlink.plugins.resources.";
-    public static final String FILES_RADICAL_PROPERTY = "jdk.jlink.plugins.files.";
-
-    public static final String FILES_TRANSFORMER_PROPERTY = FILES_RADICAL_PROPERTY +
- PluginProvider.TRANSFORMER;
-    public static final String FILES_FILTER_PROPERTY = FILES_RADICAL_PROPERTY +
- PluginProvider.FILTER;
-
-    public static final String RESOURCES_COMPRESSOR_PROPERTY = RESOURCES_RADICAL_PROPERTY +
- PluginProvider.COMPRESSOR;
-    public static final String RESOURCES_SORTER_PROPERTY = RESOURCES_RADICAL_PROPERTY +
- PluginProvider.SORTER;
-    public static final String RESOURCES_TRANSFORMER_PROPERTY = RESOURCES_RADICAL_PROPERTY +
- PluginProvider.TRANSFORMER;
-    public static final String RESOURCES_FILTER_PROPERTY = RESOURCES_RADICAL_PROPERTY +
- PluginProvider.FILTER;
-    public static final String RESOURCES_LAST_SORTER_PROPERTY = RESOURCES_RADICAL_PROPERTY +
-            "resources.last-sorter";
-
-
     private static final Map<String, Integer> RESOURCES_RANGES = new HashMap<>();
     private static final List<String> RESOURCES_CATEGORIES = new ArrayList<>();
 
@@ -152,19 +126,13 @@
         }
     }
 
-    private ImagePluginConfiguration() {}
-
-    /**
-     * Create a stack of plugins from a configuration file.
-     * @param p Properties file.
-     * @return A stack of plugins.
-     * @throws IOException
-     */
-    public static ImagePluginStack parseConfiguration(Properties p)
-            throws Exception {
-        return parseConfiguration(null, p, Layer.boot(), null);
+    private ImagePluginConfiguration() {
     }
 
+    public static ImagePluginStack parseConfiguration(Jlink.PluginsConfiguration plugins)
+            throws Exception {
+        return parseConfiguration(null, plugins, Layer.boot(), null);
+    }
     /*
      * Create a stack of plugins from a a configuration.
      *
@@ -185,6 +153,7 @@
         // Validate stack
         Map<String, List<Integer>> resources = new HashMap<>();
         Map<String, List<Integer>> files = new HashMap<>();
+        List<String> seen = new ArrayList<>();
         for (Jlink.StackedPluginConfiguration plug : plugins.getPluginsConfig()) {
             if (plug.getIndex() < 0) {
                 throw new Exception("Invalid index " + plug.getIndex() + " for "
@@ -198,6 +167,11 @@
                 throw new Exception("Invalid provider type " + prov);
             }
 
+            if (seen.contains(prov.getName())) {
+                throw new Exception("Plugin " + prov.getName()
+                        + " added more than once to stack ");
+            }
+            seen.add(prov.getName());
             Map<String, List<Integer>> map = isResourceProvider(prov) ? resources : files;
             List<Integer> lst = map.get(prov.getCategory());
             if (lst == null) {
@@ -206,14 +180,12 @@
             }
             int index;
             if (isResourceProvider(prov)) {
-                index = getIndex(plug.getIndex(),
-                        RESOURCES_RADICAL_PROPERTY,
+                index = getAbsoluteIndex(plug.getIndex(),
                         prov.getCategory(),
                         plug.isAbsoluteIndex(),
                         RESOURCES_RANGES);
             } else {
-                index = getIndex(plug.getIndex(),
-                        FILES_RADICAL_PROPERTY,
+                index = getAbsoluteIndex(plug.getIndex(),
                         prov.getCategory(),
                         plug.isAbsoluteIndex(),
                         FILES_RANGES);
@@ -227,8 +199,7 @@
         for (Jlink.StackedPluginConfiguration prop : plugins.getPluginsConfig()) {
             PluginProvider prov = providers.get(prop.getName());
             if (isResourceProvider(prov)) {
-                int index = getIndex(prop.getIndex(),
-                        RESOURCES_RADICAL_PROPERTY,
+                int index = getAbsoluteIndex(prop.getIndex(),
                         prov.getCategory(),
                         prop.isAbsoluteIndex(),
                         RESOURCES_RANGES);
@@ -236,14 +207,12 @@
                         prop.getConfig(), pluginsLayer));
             } else {
                 if (isImageFileProvider(prov)) {
-                    int index = getIndex(prop.getIndex(),
-                            FILES_RADICAL_PROPERTY,
+                    int index = getAbsoluteIndex(prop.getIndex(),
                             prov.getCategory(),
                             prop.isAbsoluteIndex(),
                             FILES_RANGES);
                     filePlugins.addAll(createOrderedPlugins(index, prop.getName(),
                             prop.getConfig(), pluginsLayer));
-
                 }
             }
         }
@@ -301,7 +270,7 @@
         return prov instanceof ImageFilePluginProvider;
     }
 
-    private static int getIndex(int index, String radical, String category,
+    private static int getAbsoluteIndex(int index, String category,
             boolean absolute, Map<String, Integer> ranges) throws Exception {
 
         if (absolute) {
@@ -309,8 +278,7 @@
         }
         // If non null category and not absolute, get index within category
         if (category != null) {
-            String prop = radical + category + "." + index;
-            return getAbsoluteIndex(prop, radical, ranges);
+            return ranges.get(category) + index;
         }
 
         throw new Exception("Can't compute index, no category");
@@ -325,57 +293,6 @@
     }
 
     /**
-     * Create a stack of plugins from a configuration file.
-     * @param outDir The directory where to generate the image.
-     * Used to build an ImageBuilder.
-     * @param p Properties file.
-     * @param pluginsLayer Layer to retrieve plugins
-     * @param bom The tooling config data
-     * @return A stack of plugins.
-     * @throws Exception
-     */
-    public static ImagePluginStack parseConfiguration(Path outDir,
-            Properties p,
-            Layer pluginsLayer,
-            String bom)
-            throws Exception {
-        if (p == null) {
-            return parseConfiguration(outDir,
-                    (Jlink.PluginsConfiguration) null, pluginsLayer, bom);
-        }
-        String lastSorterName = (String) p.remove(RESOURCES_LAST_SORTER_PROPERTY);
-        List<Jlink.StackedPluginConfiguration> lst = new ArrayList<>();
-        for (String prop : p.stringPropertyNames()) {
-            String value = p.getProperty(prop);
-            if (prop.startsWith(RESOURCES_RADICAL_PROPERTY)) {
-                int index = getAbsoluteIndex(prop, RESOURCES_RADICAL_PROPERTY,
-                        RESOURCES_RANGES);
-                lst.add(new Jlink.StackedPluginConfiguration(value, index, true, filter(p, value)));
-            } else if (prop.startsWith(FILES_RADICAL_PROPERTY)) {
-                int index = getAbsoluteIndex(prop, FILES_RADICAL_PROPERTY, FILES_RANGES);
-                lst.add(new Jlink.StackedPluginConfiguration(value, index, true, filter(p, value)));
-            }
-        }
-        String builderName = p.getProperty(IMAGE_BUILDER_PROPERTY);
-        builderName = builderName == null ? DefaultImageBuilderProvider.NAME : builderName;
-
-        Map<String, Object> builderConfig = filter(p, builderName);
-        Jlink.PluginsConfiguration config = new Jlink.PluginsConfiguration(lst,
-                new Jlink.PluginConfiguration(builderName, builderConfig), lastSorterName);
-        return parseConfiguration(outDir, config, pluginsLayer, bom);
-    }
-
-    private static Map<String, Object> filter(Properties p, String name) {
-        Map<String, Object> filtered = new HashMap<>();
-        p.stringPropertyNames().stream().filter(
-                (n) -> (n.startsWith(name))).forEach((n) -> {
-                    String pluginProp = n.substring(name.length() + 1);
-                    filtered.put(pluginProp, p.getProperty(n));
-                });
-        return filtered;
-    }
-
-    /**
      * 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.
@@ -433,85 +350,6 @@
         return Collections.unmodifiableList(FILES_CATEGORIES);
     }
 
-    public static void addPluginProperty(Properties properties,
-            PluginProvider provider) throws IllegalArgumentException {
-
-        String radical = null;
-        Map<String, Integer> ranges = null;
-        if (isResourceProvider(provider)) {
-            ranges = RESOURCES_RANGES;
-            radical = RESOURCES_RADICAL_PROPERTY;
-        } else if (isImageFileProvider(provider)) {
-            ranges = FILES_RANGES;
-            radical = FILES_RADICAL_PROPERTY;
-        } else {
-            throw new IllegalArgumentException("Unknown provider type" + provider);
-        }
-        int index = getNextIndex(properties, provider.getCategory(), radical, ranges);
-        properties.setProperty(radical
-                + provider.getCategory() + "." + index, provider.getName());
-    }
-
-    private static int getNextIndex(Properties props,
-            String category, String radical, Map<String, Integer> ranges)
-            throws IllegalArgumentException {
-        Objects.requireNonNull(props);
-        Objects.requireNonNull(category);
-        Integer range_start = ranges.get(category);
-        if (range_start == null) {
-            throw new IllegalArgumentException("Unknown " + category);
-        }
-        int index = range_start;
-        for (String prop : props.stringPropertyNames()) {
-            if (prop.startsWith(radical)) {
-                int i = getAbsoluteIndex(prop, radical, ranges);
-                // we are in same range
-                if (i >= range_start && i < range_start + RANGE_LENGTH) {
-                    if (i > index) {
-                        index = i;
-                    }
-                }
-            }
-        }
-        index = index - range_start + 1;
-        if (index >= RANGE_LENGTH) {
-            throw new IllegalArgumentException("Can't find an available index for "
-                    + category);
-        }
-        return index;
-    }
-
-    private static int getAbsoluteIndex(String prop, String radical,
-            Map<String, Integer> ranges) {
-        String suffix = prop.substring(radical.length());
-        String[] split = suffix.split("\\.");
-        if (split.length > 2 || split.length == 0) {
-            throw new IllegalArgumentException("Invalid property " + prop);
-        }
-        int order = 0;
-        boolean label = false;
-        // radical.label[.num] or radical.num
-        for (int i = 0; i < split.length; i++) {
-            Integer val = null;
-            String s = split[i];
-            if (i == 0) {
-                val = ranges.get(s);
-                if (val == null) {
-                    val = Integer.valueOf(s);
-                } else {
-                    label = true;
-                }
-            } else {
-                if (!label) {
-                    throw new IllegalArgumentException("Invalid property " + prop);
-                }
-                val = Integer.valueOf(s);
-            }
-            order += val;
-        }
-        return order;
-    }
-
     private static List<OrderedPlugin> createOrderedPlugins(int index,
             String name, Map<String, Object> config, Layer pluginsLayer) throws IOException {
         Plugin[] plugins = ImagePluginProviderRepository.newPlugins(config,
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StringSharingProvider.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StringSharingProvider.java	Fri Oct 09 18:01:08 2015 +0200
@@ -28,7 +28,6 @@
 import java.util.Map;
 import jdk.tools.jlink.plugins.ResourcePlugin;
 import jdk.tools.jlink.plugins.CmdResourcePluginProvider;
-import jdk.tools.jlink.internal.ImagePluginConfiguration;
 import jdk.tools.jlink.plugins.PluginProvider;
 
 /**
@@ -62,7 +61,7 @@
 
     @Override
     public String getToolOption() {
-        return null;
+        return NAME;
     }
 
     @Override
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ZipCompressProvider.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ZipCompressProvider.java	Fri Oct 09 18:01:08 2015 +0200
@@ -28,7 +28,6 @@
 import java.util.Map;
 import jdk.tools.jlink.plugins.ResourcePlugin;
 import jdk.tools.jlink.plugins.CmdResourcePluginProvider;
-import jdk.tools.jlink.internal.ImagePluginConfiguration;
 import jdk.tools.jlink.plugins.PluginProvider;
 
 /**
@@ -59,7 +58,7 @@
 
     @Override
     public String getToolOption() {
-        return null;
+        return NAME;
     }
 
     @Override
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/CmdPluginProvider.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/CmdPluginProvider.java	Fri Oct 09 18:01:08 2015 +0200
@@ -28,7 +28,6 @@
 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;
 
@@ -49,6 +48,9 @@
      */
     public static final String TOOL_ARGUMENT_PROPERTY = "argument";
 
+    public static final String FIRST = "FIRST";
+    public static final String LAST = "LAST";
+
     /**
      * Returns the description
      *
@@ -112,14 +114,18 @@
     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)
-                    || !(entry.getValue() instanceof String)) {
-                throw new RuntimeException("Config should be string for "
-                        + entry.getKey());
+            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 = (String) entry.getValue();
+            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	Fri Oct 09 15:00:44 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/DefaultImageBuilder.java	Fri Oct 09 18:01:08 2015 +0200
@@ -64,17 +64,12 @@
 
     private final Path root;
     private final Path mdir;
-    private final String jimage;
     private final boolean genBom;
 
     public DefaultImageBuilder(Map<String, Object> properties, Path root) throws IOException {
         Objects.requireNonNull(root);
 
-        @SuppressWarnings("unchecked")
-        String img = (String) properties.get(DefaultImageBuilderProvider.JIMAGE_NAME_PROPERTY);
-        jimage = img == null ? BasicImageWriter.BOOT_IMAGE_NAME : img;
-
-        genBom = properties.get(DefaultImageBuilderProvider.GEN_BOM) != null;
+        genBom = properties.containsKey(DefaultImageBuilderProvider.GEN_BOM);
 
         this.root = root;
         this.mdir = root.resolve(root.getFileSystem().getPath("lib", "modules"));
@@ -200,7 +195,7 @@
 
     @Override
     public DataOutputStream getJImageOutputStream() throws IOException {
-        Path jimageFile = mdir.resolve(jimage);
+        Path jimageFile = mdir.resolve(BasicImageWriter.BOOT_IMAGE_NAME);
         OutputStream fos = Files.newOutputStream(jimageFile);
         BufferedOutputStream bos = new BufferedOutputStream(fos);
         return new DataOutputStream(bos);
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/OnOffPluginProvider.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/OnOffPluginProvider.java	Fri Oct 09 18:01:08 2015 +0200
@@ -27,7 +27,6 @@
 import java.io.IOException;
 import java.util.Map;
 import java.util.Objects;
-import jdk.tools.jlink.internal.ImagePluginConfiguration;
 
 /**
  *
@@ -36,6 +35,9 @@
  */
 public interface OnOffPluginProvider<T> extends CmdPluginProvider<T> {
 
+    public static final String ON_ARGUMENT = "on";
+    public static final String OFF_ARGUMENT = "off";
+
     @Override
     public default T[] newPlugins(String[] arguments,
             Map<String, String> otherOptions)
@@ -45,13 +47,13 @@
             throw new IOException("Invalid number of arguments expecting "
                     + getToolArgument());
         }
-        if (!ImagePluginConfiguration.OFF_ARGUMENT.equals(arguments[0])
-                && !ImagePluginConfiguration.ON_ARGUMENT.equals(arguments[0])) {
+        if (!OFF_ARGUMENT.equals(arguments[0])
+                && !ON_ARGUMENT.equals(arguments[0])) {
             throw new IOException("Invalid argument " + arguments[0]
-                    + ", expecting " + ImagePluginConfiguration.ON_ARGUMENT + " or "
-                    + ImagePluginConfiguration.OFF_ARGUMENT);
+                    + ", expecting " + ON_ARGUMENT + " or "
+                    + OFF_ARGUMENT);
         }
-        if (ImagePluginConfiguration.OFF_ARGUMENT.equals(arguments[0])) {
+        if (OFF_ARGUMENT.equals(arguments[0])) {
             return null;
         }
         return createPlugins(otherOptions);
@@ -62,8 +64,8 @@
 
     @Override
     public default String getToolArgument() {
-        return ImagePluginConfiguration.ON_ARGUMENT + "|"
-                + ImagePluginConfiguration.OFF_ARGUMENT;
+        return ON_ARGUMENT + "|"
+                + OFF_ARGUMENT;
     }
 
     public default boolean isEnabledByDefault() {
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/PluginProvider.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/plugins/PluginProvider.java	Fri Oct 09 18:01:08 2015 +0200
@@ -55,6 +55,12 @@
     public static final String FILTER = "filter";
     public static final String PACKAGER = "packager";
 
+    public static enum ORDER {
+        FIRST,
+        ANY,
+        LAST
+    }
+
     private final String name;
     private final String description;
 
@@ -71,6 +77,15 @@
 
     public abstract String getCategory();
 
+    /**
+     * Order of the plugin within its category. By default ANY.
+     *
+     * @return Expected order.
+     */
+    public ORDER getOrder() {
+        return ORDER.ANY;
+    }
+
     public String getDescription() {
         return description;
     }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties	Fri Oct 09 15:00:44 2015 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties	Fri Oct 09 18:01:08 2015 +0200
@@ -67,8 +67,11 @@
 main.opt.list-plugins=\
 \  --list-plugins                       Available plugins information
 
-main.opt.plugins-configuration=\
-\  --plugins-configuration              Path to plugins configuration properties file
+main.opt.image-builder=\
+\  --image-builder                      Name of the custom image builder
+
+main.opt.resources-last-sorter=\
+\  --resources-last-sorter              Name of the last plugin allowed to sort resources within a jimage.
 
 main.plugins-modulepath=\
 \  --plugins-modulepath                 Custom plugins module path
@@ -115,6 +118,8 @@
 
 err.dir.not.empty=not empty: {0}
 
+err.invalid.index=invalid index for option {0}
+
 warn.thirdparty.plugins=\
 Enabling third party plugins can lead to unusable generated image.
 
--- a/test/jdk/jigsaw/tools/jlink/CustomImageBuilderTest.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/test/jdk/jigsaw/tools/jlink/CustomImageBuilderTest.java	Fri Oct 09 18:01:08 2015 +0200
@@ -69,7 +69,6 @@
     private static Path pluginModulePath;
     private static Path customPluginJmod;
     private static Path classes;
-    private static final Path configFile = Paths.get("builder.cfg").toAbsolutePath();
     private static final List<String> options =
             Arrays.asList("custom-image-option-1", "custom-image-option-2");
 
@@ -89,13 +88,12 @@
                 .jmod(helper.getJmodDir().resolve(moduleName + ".jmod"))
                 .create().assertSuccess();
         pluginModulePath = customPluginJmod.getParent();
-        Files.write(configFile, "jdk.jlink.image.builder=custom-image-builder\n".getBytes());
     }
 
     private JLinkTask getJLinkTask() {
         return JImageGenerator.getJLinkTask()
-                .option("--plugins-configuration")
-                .option(configFile.toString());
+                .option("--image-builder")
+                .option("custom-image-builder");
     }
 
     public void testHelp() {
--- a/test/jdk/jigsaw/tools/jlink/JLink2Test.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/test/jdk/jigsaw/tools/jlink/JLink2Test.java	Fri Oct 09 18:01:08 2015 +0200
@@ -65,7 +65,6 @@
         helper.generateDefaultModules();
 
         testSameNames(helper);
-        testCustomization(helper);
         testBomFile(helper);
         testFileReplacement(helper);
         testModulePath(helper);
@@ -134,30 +133,12 @@
         validator.validate();
     }
 
-    private static void testCustomization(Helper helper) throws Exception {
-        File f = new File("plugins.properties");
-        String fileName = "toto.jimage";
-        Files.write(f.toPath(), (DefaultImageBuilderProvider.NAME + "."
-                + DefaultImageBuilderProvider.JIMAGE_NAME_PROPERTY + "=" + fileName).getBytes());
-        String[] userOptions = {"--plugins-configuration", f.getAbsolutePath()};
-        helper.generateDefaultJModule("totoimagemodule", "composite2");
-        File img = helper.generateDefaultImage(userOptions, "totoimagemodule").assertSuccess().toFile();
-        File imgFile = new File(img, "lib" + File.separator + "modules" + File.separator + fileName);
-        if (!imgFile.exists()) {
-            throw new RuntimeException("Expected file doesn't exist " + imgFile);
-        }
-    }
-
     private static void testBomFile(Helper helper) throws Exception {
-        File fplugins = new File("plugins.properties");
-        Files.write(fplugins.toPath(),
-                (ImagePluginConfiguration.RESOURCES_COMPRESSOR_PROPERTY + "=zip\n").getBytes());
         File defaults = new File("embedded.properties");
         Files.write(defaults.toPath(), ("jdk.jlink.defaults=--genbom --exclude-resources *.jcov,*/META-INF/*" +
                 " --addmods UNKNOWN\n").getBytes());
-        String[] userOptions = {"--plugins-configuration",
-                fplugins.getAbsolutePath(),
-                "--strip-java-debug", "on",
+        String[] userOptions = {"--zip",
+            "*",                "--strip-java-debug", "on",
                 "--configuration", defaults.getAbsolutePath()};
         String moduleName = "bomzip";
         helper.generateDefaultJModule(moduleName, "composite2");
@@ -169,11 +150,9 @@
         }
         String bomcontent = new String(Files.readAllBytes(bom.toPath()));
         if (!bomcontent.contains("--strip-java-debug")
-                || !bomcontent.contains("--plugins-configuration")
-                || !bomcontent.contains(fplugins.getAbsolutePath())
+                || !bomcontent.contains("--zip")
+                || !bomcontent.contains("*")
                 || !bomcontent.contains("--genbom")
-                || !bomcontent.contains(ImagePluginConfiguration.
-                        RESOURCES_COMPRESSOR_PROPERTY)
                 || !bomcontent.contains("zip")
                 || !bomcontent.contains("--exclude-resources *.jcov,"
                         + "*/META-INF/*")
--- a/test/jdk/jigsaw/tools/jlink/JLinkNegativeTest.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/test/jdk/jigsaw/tools/jlink/JLinkNegativeTest.java	Fri Oct 09 18:01:08 2015 +0200
@@ -352,12 +352,10 @@
     }
 
     public void testCustomImageBuilderNotFound() throws IOException {
-        Path configFile = Paths.get("builder.cfg");
         String builderName = "not-found-image-builder";
-        Files.write(configFile, ("jdk.jlink.image.builder=" + builderName).getBytes());
         JImageGenerator.getJLinkTask()
-                .option("--plugins-configuration")
-                .option(configFile.toAbsolutePath().toString())
+                .option("--image-builder")
+                .option("not-found-image-builder")
                 .modulePath(helper.defaultModulePath())
                 .output(helper.createNewImageDir("leaf1"))
                 .addMods("leaf1")
--- a/test/jdk/jigsaw/tools/jlink/JLinkPluginsTest.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/test/jdk/jigsaw/tools/jlink/JLinkPluginsTest.java	Fri Oct 09 18:01:08 2015 +0200
@@ -61,11 +61,8 @@
         }
         helper.generateDefaultModules();
         {
-            String properties = createProperties("plugins,properties",
-                    ImagePluginConfiguration.RESOURCES_COMPRESSOR_PROPERTY + "=zip\n"
-                    + "zip." + CmdPluginProvider.TOOL_ARGUMENT_PROPERTY
-                    + "=*Error.class,*Exception.class, ^/java.base/java/lang/*\n");
-            String[] userOptions = {"--plugins-configuration", properties};
+            // zip
+            String[] userOptions = {"--zip", "toto"};
             String moduleName = "zipfiltercomposite";
             helper.generateDefaultJModule(moduleName, "composite2");
             Path imageDir = helper.generateDefaultImage(userOptions, moduleName).assertSuccess();
@@ -73,36 +70,15 @@
         }
         {
             // Skip debug
-            String properties = createProperties("plugins.properties",
-                    ImagePluginConfiguration.RESOURCES_TRANSFORMER_PROPERTY + "=strip-java-debug\n"
-                    + "strip-java-debug." + CmdPluginProvider.TOOL_ARGUMENT_PROPERTY + "="
-                    + "on");
-            String[] userOptions = {"--plugins-configuration", properties};
+            String[] userOptions = {"--strip-java-debug", "on"};
             String moduleName = "skipdebugcomposite";
             helper.generateDefaultJModule(moduleName, "composite2");
             Path imageDir = helper.generateDefaultImage(userOptions, moduleName).assertSuccess();
             helper.checkImage(imageDir, moduleName, null, null);
         }
         {
-            // Skip debug + zip
-            String properties = createProperties("plugin.properties",
-                    ImagePluginConfiguration.RESOURCES_TRANSFORMER_PROPERTY + "=strip-java-debug\n"
-                    + "strip-java-debug." + CmdPluginProvider.TOOL_ARGUMENT_PROPERTY + "="
-                    + "on\n"
-                    + ImagePluginConfiguration.RESOURCES_COMPRESSOR_PROPERTY + "=zip\n");
-            String[] userOptions = {"--plugins-configuration", properties};
-            String moduleName = "zipskipdebugcomposite";
-            helper.generateDefaultJModule(moduleName, "composite2");
-            Path imageDir = helper.generateDefaultImage(userOptions, moduleName).assertSuccess();
-            helper.checkImage(imageDir, moduleName, null, null);
-        }
-        {
             // Filter out files
-            String properties = createProperties("plguins.properties",
-                    ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY + "=exclude-resources\n"
-                    + "exclude-resources." + CmdPluginProvider.TOOL_ARGUMENT_PROPERTY
-                    + "=*.jcov, */META-INF/*\n");
-            String[] userOptions = {"--plugins-configuration", properties};
+            String[] userOptions = {"--exclude-resources", "*.jcov, */META-INF/*"};
             String moduleName = "excludecomposite";
             helper.generateDefaultJModule(moduleName, "composite2");
             String[] res = {".jcov", "/META-INF/"};
@@ -111,19 +87,15 @@
         }
         {
             // Shared UTF_8 Constant Pool entries
-            String properties = createProperties("plugins.properties",
-                    ImagePluginConfiguration.RESOURCES_COMPRESSOR_PROPERTY + "=compact-cp\n");
-            String[] userOptions = {"--plugins-configuration", properties};
+            String[] userOptions = {"--compact-cp", "*"};
             String moduleName = "cpccomposite";
             helper.generateDefaultJModule(moduleName, "composite2");
             Path imageDir = helper.generateDefaultImage(userOptions, moduleName).assertSuccess();
             helper.checkImage(imageDir, moduleName, null, null);
         }
         {
-            String properties = createProperties("plugins.properties",
-                    ImagePluginConfiguration.RESOURCES_COMPRESSOR_PROPERTY + ".0=compact-cp\n"
-                    + ImagePluginConfiguration.RESOURCES_COMPRESSOR_PROPERTY + ".1=zip\n");
-            String[] userOptions = {"--plugins-configuration", properties};
+            // Ordered zip and constant cp
+            String[] userOptions = {"--zip:1", "*", "--compact-cp:0", "*"};
             String moduleName = "zipcpccomposite";
             helper.generateDefaultJModule(moduleName, "composite2");
             Path imageDir = helper.generateDefaultImage(userOptions, moduleName).assertSuccess();
--- a/test/jdk/jigsaw/tools/jlink/customplugin/plugin/CustomImageBuilder.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/test/jdk/jigsaw/tools/jlink/customplugin/plugin/CustomImageBuilder.java	Fri Oct 09 18:01:08 2015 +0200
@@ -54,7 +54,7 @@
     private void handleOption(String option) throws IOException {
         if (config.containsKey(option)) {
             String firstValue = (String) config.get(option);
-            Files.write(image.resolve(option), Objects.toString(firstValue).getBytes());
+            Files.write(image.resolve(option), Objects.toString(firstValue == null ? "" : firstValue).getBytes());
         }
     }
 
--- a/test/jdk/jigsaw/tools/jlink/plugins/LastSorterTest.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/test/jdk/jigsaw/tools/jlink/plugins/LastSorterTest.java	Fri Oct 09 18:01:08 2015 +0200
@@ -33,9 +33,9 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Properties;
 
 import jdk.tools.jlink.internal.ImagePluginConfiguration;
 import jdk.tools.jlink.internal.ImagePluginProviderRepository;
@@ -43,8 +43,9 @@
 import jdk.tools.jlink.internal.ResourcePoolImpl;
 import jdk.tools.jlink.plugins.CmdPluginProvider;
 import jdk.tools.jlink.plugins.CmdResourcePluginProvider;
-import jdk.tools.jlink.plugins.Plugin;
-import jdk.tools.jlink.plugins.PluginProvider;
+import jdk.tools.jlink.plugins.Jlink;
+import jdk.tools.jlink.plugins.Jlink.PluginsConfiguration;
+import jdk.tools.jlink.plugins.Jlink.StackedPluginConfiguration;
 import jdk.tools.jlink.plugins.ResourcePlugin;
 import jdk.tools.jlink.plugins.ResourcePool;
 import jdk.tools.jlink.plugins.StringTable;
@@ -73,12 +74,11 @@
     }
 
     private void checkTwoLastSorters() throws Exception {
-        Properties props = new Properties();
-        props.setProperty(ImagePluginConfiguration.RESOURCES_SORTER_PROPERTY, "sorterplugin6");
-        props.setProperty("sorterplugin6." + CmdPluginProvider.TOOL_ARGUMENT_PROPERTY, "/a");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_LAST_SORTER_PROPERTY, "sorterplugin6");
+        List<StackedPluginConfiguration> plugins = new ArrayList<>();
+        plugins.add(createConfig("sorterplugin6", "/a", 0));
+        PluginsConfiguration config = new Jlink.PluginsConfiguration(plugins, null, "sorterplugin6");
 
-        ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(props);
+        ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(config);
 
         // check order
         ResourcePoolImpl res = fillOutResourcePool();
@@ -117,20 +117,21 @@
         return res;
     }
 
+    private static StackedPluginConfiguration createConfig(String name, String arg, int index) {
+        Map<String, Object> conf = new HashMap<>();
+        conf.put(CmdPluginProvider.TOOL_ARGUMENT_PROPERTY, arg);
+        return new StackedPluginConfiguration(name, index, true, conf);
+    }
+
     private void checkPositiveCase() throws Exception {
-        Properties props = new Properties();
-        // plugin3 is the last one
-        props.setProperty(ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY, "sorterplugin1");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_TRANSFORMER_PROPERTY, "sorterplugin2");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_SORTER_PROPERTY, "sorterplugin3");
+        List<StackedPluginConfiguration> plugins = new ArrayList<>();
+        plugins.add(createConfig("sorterplugin1", "/c", 0));
+        plugins.add(createConfig("sorterplugin2", "/b", 1));
+        plugins.add(createConfig("sorterplugin3", "/a", 2));
 
-        props.setProperty("sorterplugin1." + CmdPluginProvider.TOOL_ARGUMENT_PROPERTY, "/c");
-        props.setProperty("sorterplugin2." + CmdPluginProvider.TOOL_ARGUMENT_PROPERTY, "/b");
-        props.setProperty("sorterplugin3." + CmdPluginProvider.TOOL_ARGUMENT_PROPERTY, "/a");
+        PluginsConfiguration config = new Jlink.PluginsConfiguration(plugins, null, "sorterplugin3");
 
-        props.setProperty(ImagePluginConfiguration.RESOURCES_LAST_SORTER_PROPERTY, "sorterplugin3");
-
-        ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(props);
+        ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(config);
 
         // check order
         ResourcePoolImpl res = fillOutResourcePool();
@@ -149,15 +150,15 @@
     }
 
     private void checkUnknownPlugin() {
-        Properties props = new Properties();
-        props.setProperty(ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY, "sorterplugin1");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_TRANSFORMER_PROPERTY, "sorterplugin2");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_SORTER_PROPERTY, "sorterplugin3");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_COMPRESSOR_PROPERTY, "sorterplugin4");
+        List<StackedPluginConfiguration> plugins = new ArrayList<>();
+        plugins.add(createConfig("sorterplugin1", "/1", 0));
+        plugins.add(createConfig("sorterplugin2", "/1", 1));
+        plugins.add(createConfig("sorterplugin3", "/1", 2));
+        plugins.add(createConfig("sorterplugin4", "/1", 3));
 
-        props.setProperty(ImagePluginConfiguration.RESOURCES_LAST_SORTER_PROPERTY, "sorterplugin5");
+        PluginsConfiguration config = new Jlink.PluginsConfiguration(plugins, null, "sorterplugin5");
         try {
-            ImagePluginConfiguration.parseConfiguration(props);
+            ImagePluginConfiguration.parseConfiguration(config);
             throw new AssertionError("Unknown plugin should have failed.");
         } catch (Exception ex) {
             // XXX OK expected
@@ -165,21 +166,15 @@
     }
 
     private void checkOrderAfterLastSorter() throws Exception {
-        // plugin4 changes the order of resources after the last sorter (plugin3), an exception should be thrown
-        Properties props = new Properties();
-        props.setProperty(ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY, "sorterplugin1");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_TRANSFORMER_PROPERTY, "sorterplugin2");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_SORTER_PROPERTY, "sorterplugin3");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_COMPRESSOR_PROPERTY, "sorterplugin4");
+        List<StackedPluginConfiguration> plugins = new ArrayList<>();
+        plugins.add(createConfig("sorterplugin1", "/c", 0));
+        plugins.add(createConfig("sorterplugin2", "/b", 1));
+        plugins.add(createConfig("sorterplugin3", "/a", 2));
+        plugins.add(createConfig("sorterplugin4", "/d", 3));
 
-        props.setProperty("sorterplugin1." + CmdPluginProvider.TOOL_ARGUMENT_PROPERTY, "/c");
-        props.setProperty("sorterplugin2." + CmdPluginProvider.TOOL_ARGUMENT_PROPERTY, "/b");
-        props.setProperty("sorterplugin3." + CmdPluginProvider.TOOL_ARGUMENT_PROPERTY, "/a");
-        props.setProperty("sorterplugin4." + CmdPluginProvider.TOOL_ARGUMENT_PROPERTY, "/d");
+        PluginsConfiguration config = new Jlink.PluginsConfiguration(plugins, null, "sorterplugin3");
 
-        props.setProperty(ImagePluginConfiguration.RESOURCES_LAST_SORTER_PROPERTY, "sorterplugin3");
-
-        ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(props);
+        ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(config);
 
         // check order
         ResourcePoolImpl res = fillOutResourcePool();
--- a/test/jdk/jigsaw/tools/jlink/plugins/PluginOrderTest.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/test/jdk/jigsaw/tools/jlink/plugins/PluginOrderTest.java	Fri Oct 09 18:01:08 2015 +0200
@@ -25,29 +25,41 @@
  * @test
  * @summary Test order of plugins
  * @author Jean-Francois Denise
- * @modules jdk.jlink/jdk.tools.jlink.internal
+ * @library ../../lib
+ * @modules java.base/jdk.internal.jimage
+ *          jdk.jdeps/com.sun.tools.classfile
+ *          jdk.jlink/jdk.tools.jlink
+ *          jdk.jlink/jdk.tools.jlink.internal
+ *          jdk.jlink/jdk.tools.jmod
+ *          jdk.jlink/jdk.tools.jimage
+ * @build tests.*
  * @run main/othervm PluginOrderTest
  */
-
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Properties;
 
 import jdk.tools.jlink.internal.ImagePluginConfiguration;
 import jdk.tools.jlink.internal.ImagePluginProviderRepository;
 import jdk.tools.jlink.internal.ImagePluginStack;
 import jdk.tools.jlink.internal.ResourcePoolImpl;
 import jdk.tools.jlink.plugins.CmdResourcePluginProvider;
+import jdk.tools.jlink.plugins.Jlink.PluginsConfiguration;
+import jdk.tools.jlink.plugins.Jlink.StackedPluginConfiguration;
 import jdk.tools.jlink.plugins.PluginProvider;
 import jdk.tools.jlink.plugins.ResourcePlugin;
 import jdk.tools.jlink.plugins.ResourcePool;
+import jdk.tools.jlink.plugins.ResourcePool.Visitor;
 import jdk.tools.jlink.plugins.StringTable;
 
+import tests.Helper;
+import tests.Result;
+
 public class PluginOrderTest {
 
     public static void main(String[] args) throws Exception {
@@ -55,17 +67,53 @@
     }
 
     public void test() throws Exception {
-        test1();
+        List<String> order = new ArrayList<>();
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin1_F",
+                PluginProvider.FILTER, order));
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin2_F",
+                PluginProvider.FILTER, order));
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin3_F",
+                PluginProvider.FILTER, order));
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin4_F",
+                PluginProvider.FILTER, order));
 
-        List<String> order = new ArrayList<>();
-        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin1", order));
-        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin2", order));
-        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin3", order));
-        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin4", order));
-        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin5", order));
-        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin6", order));
-        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin7", order));
-        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin8", order));
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin1_T",
+                PluginProvider.TRANSFORMER, order));
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin2_T",
+                PluginProvider.TRANSFORMER, order));
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin3_T",
+                PluginProvider.TRANSFORMER, order));
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin4_T",
+                PluginProvider.TRANSFORMER, order));
+
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin1_S",
+                PluginProvider.SORTER, order));
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin2_S",
+                PluginProvider.SORTER, order));
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin3_S",
+                PluginProvider.SORTER, order));
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin4_S",
+                PluginProvider.SORTER, order));
+
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin1_C",
+                PluginProvider.COMPRESSOR, order));
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin2_C",
+                PluginProvider.COMPRESSOR, order));
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin3_C",
+                PluginProvider.COMPRESSOR, order));
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin4_C",
+                PluginProvider.COMPRESSOR, order));
+
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin1_A",
+                null, order));
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin2_A",
+                null, order));
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin3_A",
+                null, order));
+        ImagePluginProviderRepository.registerPluginProvider(new PProvider("plugin4_A",
+                null, order));
+
+        test1(order);
 
         test2(order);
 
@@ -79,17 +127,17 @@
 
         test7(order);
 
+        testAbs(order);
+
         test8(order);
 
         test9(order);
-
-        testInvalidProperties();
     }
 
-    private void check(Properties props, List<String> expected, List<String> order)
+    private void check(PluginsConfiguration config, List<String> expected, List<String> order)
             throws Exception {
         order.clear();
-        ImagePluginStack plugins = ImagePluginConfiguration.parseConfiguration(props);
+        ImagePluginStack plugins = ImagePluginConfiguration.parseConfiguration(config);
         ResourcePoolImpl pool = new ResourcePoolImpl(ByteOrder.nativeOrder());
         pool.addResource(new ResourcePool.Resource("/mod/com/foo/bar/A.somthing",
                 ByteBuffer.allocate(0)));
@@ -112,295 +160,159 @@
         System.err.println("Gathered plugins: " + order);
     }
 
-    private void test1() throws Exception {
-        Properties props = new Properties();
-        props.setProperty(ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY, "plugin1");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_TRANSFORMER_PROPERTY, "plugin2");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_SORTER_PROPERTY, "plugin3");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_COMPRESSOR_PROPERTY, "plugin4");
-        int i = getNextIndex(props, PluginProvider.FILTER);
-        if (i != 1) {
-            throw new Exception("Unexpected index " + i);
+    private PluginsConfiguration createConfig(String... nameIndexAbs) {
+        List<StackedPluginConfiguration> lst = new ArrayList<>();
+        for (String s : nameIndexAbs) {
+            String name = s.substring(0, s.indexOf(":"));
+            int sep = s.indexOf("/");
+            int index = Integer.valueOf(s.substring(s.indexOf(":") + 1, sep));
+            boolean absolute = Boolean.valueOf(s.substring(sep + 1));
+            lst.add(new StackedPluginConfiguration(name, index,
+                    absolute, Collections.emptyMap()));
         }
-        i = getNextIndex(props, PluginProvider.TRANSFORMER);
-        if (i != 1) {
-            throw new Exception("Unexpected index " + i);
-        }
-        i = getNextIndex(props, PluginProvider.SORTER);
-        if (i != 1) {
-            throw new Exception("Unexpected index " + i);
-        }
-        i = getNextIndex(props, PluginProvider.COMPRESSOR);
-        if (i != 1) {
-            throw new Exception("Unexpected index " + i);
-        }
-
-        i = getNextIndex(props, PluginProvider.TRANSFORMER);
-        if (i != 2) {
-            throw new Exception("Unexpected index " + i);
-        }
-        i = getNextIndex(props, PluginProvider.SORTER);
-        if (i != 2) {
-            throw new Exception("Unexpected index " + i);
-        }
-        i = getNextIndex(props, PluginProvider.COMPRESSOR);
-        if (i != 2) {
-            throw new Exception("Unexpected index " + i);
-        }
-        props = new Properties();
-        props.setProperty(ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY + ".500", "plugin1");
-        i = getNextIndex(props, PluginProvider.FILTER);
-        if (i != 501) {
-            throw new Exception("Unexpected index " + i);
-        }
+        return new PluginsConfiguration(lst, null);
     }
 
-    private int getNextIndex(Properties props, String category) {
-        ImagePluginConfiguration.addPluginProperty(props, new CategoryProvider(category));
-        int max = 0;
-        for (String prop : props.stringPropertyNames()) {
-            if (prop.startsWith(ImagePluginConfiguration.RESOURCES_RADICAL_PROPERTY + category)) {
-                int i = prop.lastIndexOf(".");
-                String v = prop.substring(i + 1);
-                try {
-                    int index = Integer.valueOf(v);
-                    if (index > max) {
-                        max = index;
-                    }
-                } catch (NumberFormatException ex) {
-                    // XXX OK, not a number
-                }
-            }
+    private void test1(List<String> order) throws Exception {
+
+        Helper helper = Helper.newHelper();
+        if (helper == null) {
+            System.err.println("test1 not run");
+            return;
         }
-        if (max == 0) {
-            throw new RuntimeException("Next index not found");
-        }
-        return max;
-    }
+        helper.generateDefaultModules();
 
-    private void test2(List<String> order) throws Exception {
-        Properties props = new Properties();
-        props.setProperty(ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY, "plugin1");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_TRANSFORMER_PROPERTY, "plugin2");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_SORTER_PROPERTY, "plugin3");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_COMPRESSOR_PROPERTY, "plugin4");
-
-        check(props, Arrays.asList("plugin1", "plugin2", "plugin3", "plugin4"), order);
-    }
-
-    private void test3(List<String> order) throws Exception {
-        Properties props = new Properties();
-        props.setProperty(ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY + ".250", "plugin4");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_TRANSFORMER_PROPERTY + ".100", "plugin3");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_SORTER_PROPERTY + ".50", "plugin2");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_COMPRESSOR_PROPERTY + ".10", "plugin1");
-
-        check(props, Arrays.asList("plugin4", "plugin3", "plugin2", "plugin1"), order);
-    }
-
-    private void test4(List<String> order) throws Exception {
-        Properties props = new Properties();
-        props.setProperty(ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY + ".0",
-                "plugin2");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY + ".1",
-                "plugin3");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY + ".2",
-                "plugin4");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY + ".3",
-                "plugin1");
-
-        check(props, Arrays.asList("plugin2", "plugin3", "plugin4", "plugin1"), order);
-    }
-
-    private void test5(List<String> order) throws Exception {
-        Properties props = new Properties();
-        props.setProperty(ImagePluginConfiguration.RESOURCES_TRANSFORMER_PROPERTY + ".0",
-                "plugin2");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_TRANSFORMER_PROPERTY + ".1",
-                "plugin3");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_TRANSFORMER_PROPERTY + ".2",
-                "plugin4");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_TRANSFORMER_PROPERTY + ".3",
-                "plugin1");
-
-        check(props, Arrays.asList("plugin2", "plugin3", "plugin4", "plugin1"), order);
-    }
-
-    private void test6(List<String> order) throws Exception {
-        Properties props = new Properties();
-        props.setProperty(ImagePluginConfiguration.RESOURCES_SORTER_PROPERTY + ".0",
-                "plugin2");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_SORTER_PROPERTY + ".1",
-                "plugin3");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_SORTER_PROPERTY + ".2",
-                "plugin4");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_SORTER_PROPERTY + ".3",
-                "plugin1");
-
-        check(props, Arrays.asList("plugin2", "plugin3", "plugin4", "plugin1"), order);
-    }
-
-    private void test7(List<String> order) throws Exception {
-        Properties props = new Properties();
-        props.setProperty(ImagePluginConfiguration.RESOURCES_COMPRESSOR_PROPERTY + ".0",
-                "plugin2");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_COMPRESSOR_PROPERTY + ".1",
-                "plugin3");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_COMPRESSOR_PROPERTY + ".2",
-                "plugin4");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_COMPRESSOR_PROPERTY + ".3",
-                "plugin1");
-
-        check(props, Arrays.asList("plugin2", "plugin3", "plugin4", "plugin1"), order);
-    }
-
-    private void test8(List<String> order) throws Exception {
-        Properties props = new Properties();
-        props.setProperty(ImagePluginConfiguration.RESOURCES_RADICAL_PROPERTY
-                        + ImagePluginConfiguration.
-                getRange(new CategoryProvider(PluginProvider.FILTER))[0],
-                "plugin1");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_RADICAL_PROPERTY
-                        + ImagePluginConfiguration.
-                getRange(new CategoryProvider(PluginProvider.TRANSFORMER))[0],
-                "plugin3");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_RADICAL_PROPERTY
-                        + ImagePluginConfiguration.
-                getRange(new CategoryProvider(PluginProvider.SORTER))[0],
-                "plugin2");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_RADICAL_PROPERTY
-                        + ImagePluginConfiguration.
-                getRange(new CategoryProvider(PluginProvider.COMPRESSOR))[0],
-                "plugin4");
-
-        check(props, Arrays.asList("plugin1", "plugin3", "plugin2", "plugin4"), order);
-    }
-
-    private void test9(List<String> order) throws Exception {
-        Properties props = new Properties();
-        props.setProperty(ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY, "plugin1");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_TRANSFORMER_PROPERTY, "plugin2");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_SORTER_PROPERTY, "plugin3");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_COMPRESSOR_PROPERTY, "plugin4");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_RADICAL_PROPERTY
-                        + (ImagePluginConfiguration.
-                getRange(new CategoryProvider(PluginProvider.FILTER))[0] + 1),
-                "plugin5");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_RADICAL_PROPERTY
-                        + (ImagePluginConfiguration.
-                getRange(new CategoryProvider(PluginProvider.TRANSFORMER))[0] + 1),
-                "plugin6");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_RADICAL_PROPERTY
-                        + (ImagePluginConfiguration.
-                getRange(new CategoryProvider(PluginProvider.SORTER))[0] + 1),
-                "plugin7");
-        props.setProperty(ImagePluginConfiguration.RESOURCES_RADICAL_PROPERTY
-                        + (ImagePluginConfiguration.
-                getRange(new CategoryProvider(PluginProvider.COMPRESSOR))[0] + 1),
-                "plugin8");
-
-        List<String> expected = new ArrayList<>();
-        expected.add("plugin1");
-        expected.add("plugin5");
-        expected.add("plugin2");
-        expected.add("plugin6");
-        expected.add("plugin3");
-        expected.add("plugin7");
-        expected.add("plugin4");
-        expected.add("plugin8");
-
-        check(props, expected, order);
-    }
-
-    private void testInvalidProperties() throws Exception {
-        // Now invalid properties
         {
-            boolean failed = false;
-            try {
-                Properties props = new Properties();
-                props.setProperty(ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY + ".0.90",
-                        "plugin1");
-                ImagePluginConfiguration.parseConfiguration(props);
-            } catch (Exception ex) {
-                failed = true;
-            }
-            if (!failed) {
-                throw new Exception("Test case should have failed");
+            order.clear();
+            String[] userOptions = {"--plugin1_C", "--plugin2_C", "--plugin1_S", "--plugin2_S", "--plugin1_T", "--plugin2_T", "--plugin1_F", "--plugin2_F"};
+            String moduleName = "order1";
+            helper.generateDefaultJModule(moduleName, "composite2");
+            Result res = helper.generateDefaultImage(userOptions, moduleName);
+            res.assertSuccess();
+            if (!order.equals(Arrays.asList("plugin1_F", "plugin2_F", "plugin1_T", "plugin2_T", "plugin1_S", "plugin2_S", "plugin1_C", "plugin2_C"))) {
+                throw new Exception("plugins not called in right order. " + order);
             }
         }
 
         {
-            boolean failed = false;
-            try {
-                Properties props = new Properties();
-                props.setProperty(ImagePluginConfiguration.RESOURCES_RADICAL_PROPERTY + "90.23",
-                        "plugin1");
-                ImagePluginConfiguration.parseConfiguration(props);
-            } catch (Exception ex) {
-                failed = true;
-            }
-            if (!failed) {
-                throw new Exception("Test case should have failed");
+            order.clear();
+            String[] userOptions = {"--plugin1_F:2", "--plugin2_F:1"};
+            String moduleName = "order2";
+            helper.generateDefaultJModule(moduleName, "composite2");
+            Result res = helper.generateDefaultImage(userOptions, moduleName);
+            res.assertSuccess();
+            if (!order.equals(Arrays.asList("plugin2_F", "plugin1_F"))) {
+                throw new Exception("plugins not called in right order. " + order);
             }
         }
 
         {
-            boolean failed = false;
-            try {
-                Properties props = new Properties();
-                props.setProperty(ImagePluginConfiguration.RESOURCES_RADICAL_PROPERTY,
-                        "plugin1");
-                ImagePluginConfiguration.parseConfiguration(props);
-            } catch (Exception ex) {
-                failed = true;
-            }
-            if (!failed) {
-                throw new Exception("Test case should have failed");
+            order.clear();
+            String[] userOptions = {"--plugin1_C:LAST", "--plugin2_C:FIRST"};
+            String moduleName = "order3";
+            helper.generateDefaultJModule(moduleName, "composite2");
+            Result res = helper.generateDefaultImage(userOptions, moduleName);
+            res.assertSuccess();
+            if (!order.equals(Arrays.asList("plugin2_C", "plugin1_C"))) {
+                throw new Exception("plugins not called in right order. " + order);
             }
         }
+    }
 
-        {
-            boolean failed = false;
-            try {
-                Properties props = new Properties();
-                props.setProperty(ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY, "plugin1");
-                props.setProperty(ImagePluginConfiguration.RESOURCES_RADICAL_PROPERTY
-                                + ImagePluginConfiguration.getRange(
-                                new CategoryProvider(PluginProvider.FILTER))[0],
-                        "plugin5");
-                ImagePluginConfiguration.parseConfiguration(props);
-            } catch (Exception ex) {
-                failed = true;
-            }
-            if (!failed) {
-                throw new Exception("Test case should have failed");
-            }
-        }
+    private void test2(List<String> order) throws Exception {
+        check(createConfig("plugin1_F:0/false", "plugin1_T:0/false", "plugin1_S:0/false",
+                "plugin1_C:0/false", "plugin1_A:900000/true"),
+                Arrays.asList("plugin1_F", "plugin1_T", "plugin1_S", "plugin1_C", "plugin1_A"), order);
+    }
+
+    private void test3(List<String> order) throws Exception {
+        check(createConfig("plugin1_F:250/false", "plugin1_T:100/false", "plugin1_S:50/false",
+                "plugin1_C:10/false", "plugin1_A:0/true"),
+                Arrays.asList("plugin1_A", "plugin1_F", "plugin1_T", "plugin1_S", "plugin1_C"), order);
+    }
+
+    private void test4(List<String> order) throws Exception {
+        check(createConfig("plugin2_F:0/false", "plugin3_F:1/false", "plugin4_F:2/false",
+                "plugin1_F:3/false"),
+                Arrays.asList("plugin2_F", "plugin3_F", "plugin4_F", "plugin1_F"), order);
+    }
+
+    private void test5(List<String> order) throws Exception {
+        check(createConfig("plugin2_T:0/false", "plugin3_T:1/false", "plugin4_T:2/false",
+                "plugin1_T:3/false"),
+                Arrays.asList("plugin2_T", "plugin3_T", "plugin4_T", "plugin1_T"), order);
+    }
+
+    private void test6(List<String> order) throws Exception {
+        check(createConfig("plugin2_S:0/false", "plugin3_S:1/false", "plugin4_S:2/false",
+                "plugin1_S:3/false"),
+                Arrays.asList("plugin2_S", "plugin3_S", "plugin4_S", "plugin1_S"), order);
+    }
+
+    private void test7(List<String> order) throws Exception {
+        check(createConfig("plugin2_C:0/false", "plugin3_C:1/false", "plugin4_C:2/false",
+                "plugin1_C:3/false"),
+                Arrays.asList("plugin2_C", "plugin3_C", "plugin4_C", "plugin1_C"), order);
+    }
+
+    private void testAbs(List<String> order) throws Exception {
+        check(createConfig("plugin2_A:0/true", "plugin3_A:1/true", "plugin4_A:2/true",
+                "plugin1_A:3/true"),
+                Arrays.asList("plugin2_A", "plugin3_A", "plugin4_A", "plugin1_A"), order);
+    }
+
+    private void test8(List<String> order) throws Exception {
+        check(createConfig("plugin1_F:" + ImagePluginConfiguration.getRange(new CategoryProvider(PluginProvider.FILTER))[0] + "/true",
+                "plugin1_T:" + ImagePluginConfiguration.getRange(new CategoryProvider(PluginProvider.TRANSFORMER))[0] + "/true",
+                "plugin1_S:" + ImagePluginConfiguration.getRange(new CategoryProvider(PluginProvider.SORTER))[0] + "/true",
+                "plugin1_C:" + ImagePluginConfiguration.getRange(new CategoryProvider(PluginProvider.COMPRESSOR))[0] + "/true"),
+                Arrays.asList("plugin1_F", "plugin1_T", "plugin1_S", "plugin1_C"), order);
+    }
+
+    private void test9(List<String> order) throws Exception {
+        List<String> expected = new ArrayList<>();
+        expected.add("plugin1_F");
+        expected.add("plugin2_F");
+        expected.add("plugin1_T");
+        expected.add("plugin2_T");
+        expected.add("plugin1_S");
+        expected.add("plugin2_S");
+        expected.add("plugin1_C");
+        expected.add("plugin2_C");
+
+        check(createConfig("plugin1_F:0/false", "plugin1_T:0/false", "plugin1_S:0/false",
+                "plugin1_C:0/false", "plugin2_F:" + (ImagePluginConfiguration.getRange(new CategoryProvider(PluginProvider.FILTER))[0] + 1) + "/true",
+                "plugin2_T:" + (ImagePluginConfiguration.getRange(new CategoryProvider(PluginProvider.TRANSFORMER))[0] + 1) + "/true",
+                "plugin2_S:" + (ImagePluginConfiguration.getRange(new CategoryProvider(PluginProvider.SORTER))[0] + 1) + "/true",
+                "plugin2_C:" + (ImagePluginConfiguration.getRange(new CategoryProvider(PluginProvider.COMPRESSOR))[0] + 1) + "/true"), expected, order);
     }
 
     public static class PProvider extends CmdResourcePluginProvider {
 
         private final List<String> order;
-
-        PProvider(String name, List<String> order) {
+        private final String category;
+        private final String name;
+        PProvider(String name, String category, List<String> order) {
             super(name, "");
+            this.name = name;
             this.order = order;
+            this.category = category;
         }
 
         @Override
         public ResourcePlugin[] newPlugins(String[] argument, Map<String, String> options) throws IOException {
-            return new ResourcePlugin[]{                    new PluginTrap(getName(), order)
+            return new ResourcePlugin[]{new PluginTrap(getName(), order)
             };
         }
 
         @Override
         public String getCategory() {
-            return null;
+            return category;
         }
 
         @Override
         public String getToolOption() {
-            return null;
+            return name;
         }
 
         @Override
@@ -428,7 +340,9 @@
         public void visit(ResourcePool resources, ResourcePool output, StringTable strings)
                 throws Exception {
             order.add(name);
-            output.addResource(new ResourcePool.Resource("/module/com/foo/bar/X.st", ByteBuffer.allocate(0)));
+            resources.visit((resource, order, str) -> {
+                return resource;
+            }, output, strings);
         }
 
         @Override
@@ -448,7 +362,7 @@
 
         @Override
         public ResourcePlugin[] newPlugins(String[] argument,
-                                           Map<String, String> options) throws IOException {
+                Map<String, String> options) throws IOException {
             throw new IOException("Shouldn't be called");
         }
 
--- a/test/jdk/jigsaw/tools/jlink/plugins/PluginsNegativeTest.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/test/jdk/jigsaw/tools/jlink/plugins/PluginsNegativeTest.java	Fri Oct 09 18:01:08 2015 +0200
@@ -33,15 +33,18 @@
 import java.lang.reflect.Layer;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Properties;
 
 import jdk.tools.jlink.internal.ImagePluginConfiguration;
 import jdk.tools.jlink.internal.ImagePluginProviderRepository;
 import jdk.tools.jlink.internal.ImagePluginStack;
 import jdk.tools.jlink.internal.ResourcePoolImpl;
 import jdk.tools.jlink.plugins.CmdResourcePluginProvider;
+import jdk.tools.jlink.plugins.Jlink;
+import jdk.tools.jlink.plugins.Jlink.PluginsConfiguration;
 import jdk.tools.jlink.plugins.PluginProvider;
 import jdk.tools.jlink.plugins.ResourcePlugin;
 import jdk.tools.jlink.plugins.ResourcePool;
@@ -88,10 +91,14 @@
         }
     }
 
+    private static Jlink.StackedPluginConfiguration createConfig(String name, int index) {
+        return new Jlink.StackedPluginConfiguration(name, index, true, Collections.emptyMap());
+    }
+
     private void testEmptyOutputResource() throws Exception {
-        Properties properties = new Properties();
-        properties.setProperty(ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY, "plugin");
-        ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(properties);
+        List<Jlink.StackedPluginConfiguration> plugins = new ArrayList<>();
+        plugins.add(createConfig("plugin", 0));
+        ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(new PluginsConfiguration(plugins, null));
         ResourcePoolImpl inResources = new ResourcePoolImpl(ByteOrder.nativeOrder());
         inResources.addResource(new ResourcePool.Resource("/aaa/bbb/A", ByteBuffer.allocate(10)));
         try {
@@ -112,9 +119,9 @@
     }
 
     private void testEmptyInputResource() throws Exception {
-        Properties properties = new Properties();
-        properties.setProperty(ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY, "plugin");
-        ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(properties);
+        List<Jlink.StackedPluginConfiguration> plugins = new ArrayList<>();
+        plugins.add(createConfig("plugin", 0));
+        ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(new PluginsConfiguration(plugins, null));
         ResourcePoolImpl inResources = new ResourcePoolImpl(ByteOrder.nativeOrder());
         ResourcePoolImpl outResources = (ResourcePoolImpl) stack.visitResources(inResources, new StringTable() {
             @Override
--- a/test/jdk/jigsaw/tools/jlink/plugins/PrevisitorTest.java	Fri Oct 09 15:00:44 2015 +0100
+++ b/test/jdk/jigsaw/tools/jlink/plugins/PrevisitorTest.java	Fri Oct 09 18:01:08 2015 +0200
@@ -34,20 +34,18 @@
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Properties;
 import java.util.stream.Collectors;
 
 import jdk.tools.jlink.internal.ImagePluginConfiguration;
 import jdk.tools.jlink.internal.ImagePluginProviderRepository;
 import jdk.tools.jlink.internal.ImagePluginStack;
 import jdk.tools.jlink.internal.ResourcePoolImpl;
-import jdk.tools.jlink.plugins.CmdPluginProvider;
 import jdk.tools.jlink.plugins.CmdResourcePluginProvider;
-import jdk.tools.jlink.plugins.Plugin;
-import jdk.tools.jlink.plugins.PluginProvider;
+import jdk.tools.jlink.plugins.Jlink;
 import jdk.tools.jlink.plugins.ResourcePlugin;
 import jdk.tools.jlink.plugins.ResourcePool;
 import jdk.tools.jlink.plugins.ResourcePrevisitor;
@@ -59,12 +57,16 @@
         new PrevisitorTest().test();
     }
 
+    private static Jlink.StackedPluginConfiguration createConfig(String name, int index) {
+        return new Jlink.StackedPluginConfiguration(name, index, true, Collections.emptyMap());
+    }
+
     public void test() throws Exception {
         CustomProvider provider = new CustomProvider("plugin");
         ImagePluginProviderRepository.registerPluginProvider(provider);
-        Properties properties = new Properties();
-        properties.setProperty(ImagePluginConfiguration.RESOURCES_FILTER_PROPERTY, "plugin");
-        ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(properties);
+        List<Jlink.StackedPluginConfiguration> plugins = new ArrayList<>();
+        plugins.add(createConfig("plugin", 0));
+        ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(new Jlink.PluginsConfiguration(plugins, null));
         ResourcePoolImpl inResources = new ResourcePoolImpl(ByteOrder.nativeOrder());
         inResources.addResource(new ResourcePool.Resource("/aaa/bbb/res1.class", ByteBuffer.allocate(90)));
         inResources.addResource(new ResourcePool.Resource("/aaa/bbb/res2.class", ByteBuffer.allocate(90)));