--- old/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java 2016-08-25 19:26:33.237687000 +0530 +++ new/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java 2016-08-25 19:26:32.427685900 +0530 @@ -32,6 +32,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import jdk.tools.jlink.builder.ImageBuilder; import jdk.tools.jlink.Jlink; import jdk.tools.jlink.plugin.Plugin; @@ -66,6 +67,15 @@ */ public static ImagePluginStack parseConfiguration(Jlink.PluginsConfiguration pluginsConfiguration) throws Exception { + return parseConfiguration(pluginsConfiguration, null); + } + + /* + * Create a stack of plugins from a a configuration. + */ + public static ImagePluginStack parseConfiguration(Jlink.PluginsConfiguration pluginsConfiguration, + Set roots) + throws Exception { if (pluginsConfiguration == null) { return new ImagePluginStack(); } @@ -128,6 +138,6 @@ }; } - return new ImagePluginStack(builder, orderedPlugins, lastSorter); + return new ImagePluginStack(builder, orderedPlugins, lastSorter, roots); } } --- old/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java 2016-08-25 19:26:37.097692400 +0530 +++ new/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java 2016-08-25 19:26:36.317691300 +0530 @@ -172,7 +172,7 @@ private final Plugin lastSorter; private final List plugins = new ArrayList<>(); private final List resourcePrevisitors = new ArrayList<>(); - + private final Set rootModules; public ImagePluginStack() { this(null, Collections.emptyList(), null); @@ -181,6 +181,13 @@ public ImagePluginStack(ImageBuilder imageBuilder, List plugins, Plugin lastSorter) { + this(imageBuilder, plugins, lastSorter, null); + } + + public ImagePluginStack(ImageBuilder imageBuilder, + List plugins, + Plugin lastSorter, + Set rootModules) { this.imageBuilder = Objects.requireNonNull(imageBuilder); this.lastSorter = lastSorter; this.plugins.addAll(Objects.requireNonNull(plugins)); @@ -190,6 +197,7 @@ resourcePrevisitors.add((ResourcePrevisitor) p); } }); + this.rootModules = rootModules; } public void operate(ImageProvider provider) throws Exception { @@ -268,6 +276,7 @@ frozenOrder = ((OrderedResourcePoolManager.OrderedResourcePool)resPool).getOrderedList(); } } + return resPool; } @@ -458,7 +467,11 @@ throws Exception { Objects.requireNonNull(original); Objects.requireNonNull(transformed); - imageBuilder.storeFiles(new LastPoolManager(transformed).resourcePool()); + ResourcePool lastPool = new LastPoolManager(transformed).resourcePool(); + if (rootModules != null) { + ResourcePoolConfiguration.validate(lastPool, rootModules); + } + imageBuilder.storeFiles(lastPool); } public ExecutableImage getExecutableImage() throws IOException { --- old/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java 2016-08-25 19:26:40.907697800 +0530 +++ new/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java 2016-08-25 19:26:40.107696600 +0530 @@ -257,7 +257,7 @@ null); // Then create the Plugin Stack - ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(plugins); + ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(plugins, config.getModules()); //Ask the stack to proceed; stack.operate(imageProvider); @@ -308,7 +308,7 @@ // Then create the Plugin Stack ImagePluginStack stack = ImagePluginConfiguration. - parseConfiguration(taskHelper.getPluginsConfig(options.output)); + parseConfiguration(taskHelper.getPluginsConfig(options.output), options.addMods); //Ask the stack to proceed stack.operate(imageProvider); --- old/test/tools/jlink/CustomPluginTest.java 2016-08-25 19:26:44.907703400 +0530 +++ new/test/tools/jlink/CustomPluginTest.java 2016-08-25 19:26:44.157702300 +0530 @@ -67,6 +67,7 @@ testHelloProvider(helper, pluginModulePath); testCustomPlugins(helper, pluginModulePath); + testModuleVerification(helper, pluginModulePath); } private void testCustomPlugins(Helper helper, Path pluginModulePath) { @@ -93,8 +94,7 @@ String name = "customplugin"; Path src = Paths.get(System.getProperty("test.src")).resolve(name); Path classes = helper.getJmodClassesDir().resolve(name); - JImageGenerator.compile(src, classes, - "--add-exports", "jdk.jlink/jdk.tools.jlink.internal=customplugin"); + JImageGenerator.compile(src, classes); return JImageGenerator.getJModTask() .addClassPath(classes) .jmod(helper.getJmodDir().resolve(name + ".jmod")) @@ -136,4 +136,44 @@ throw new AssertionError("Custom plugin not called"); } } + + private void testModuleVerification(Helper helper, Path pluginModulePath) throws IOException { + { + // dependent module missing check + String moduleName = "bar"; // 8147491 + Path jmodFoo = helper.generateDefaultJModule("foo").assertSuccess(); + Path jmodBar = helper.generateDefaultJModule(moduleName, "foo").assertSuccess(); + // rogue filter removes "foo" module resources which are + // required by "bar" module. Module checks after plugin + // application should detect and report error. + JImageGenerator.getJLinkTask() + .modulePath(helper.defaultModulePath()) + .pluginModulePath(pluginModulePath) + .output(helper.createNewImageDir(moduleName)) + .addMods(moduleName) + .option("--rogue-filter") + .option("/foo/") + .call() + .assertFailure("java.lang.module.ResolutionException"); + } + + { + // package exported by one module used as concealed package + // in another module. But, module-info.class is not updated! + String moduleName = "jdk.scripting.nashorn"; + JImageGenerator.getJLinkTask() + .modulePath(helper.defaultModulePath()) + .pluginModulePath(pluginModulePath) + .output(helper.createNewImageDir(moduleName)) + .addMods(moduleName) + // "java.logging" includes a package 'javax.script' + // which is an exported package from "java.scripting" module! + // module-info.class of java.logging left "as is". + .option("--rogue-adder") + .option("/java.logging/javax/script/Foo.class") + .call() + .assertFailure( + "Module java.logging's descriptor returns inconsistent package set"); + } + } } --- old/test/tools/jlink/ImageFileCreatorTest.java 2016-08-25 19:26:49.143858600 +0530 +++ new/test/tools/jlink/ImageFileCreatorTest.java 2016-08-25 19:26:48.349813200 +0530 @@ -220,7 +220,7 @@ }; ImagePluginStack stack = new ImagePluginStack(noopBuilder, Collections.emptyList(), - null); + null, null); ImageFileCreator.create(archives, ByteOrder.nativeOrder(), stack); } --- old/test/tools/jlink/customplugin/module-info.java 2016-08-25 19:26:53.178002900 +0530 +++ new/test/tools/jlink/customplugin/module-info.java 2016-08-25 19:26:52.408001800 +0530 @@ -25,4 +25,6 @@ requires jdk.jlink; provides jdk.tools.jlink.plugin.Plugin with plugin.HelloPlugin; provides jdk.tools.jlink.plugin.Plugin with plugin.CustomPlugin; + provides jdk.tools.jlink.plugin.Plugin with plugin.RogueAdderPlugin; + provides jdk.tools.jlink.plugin.Plugin with plugin.RogueFilterPlugin; } --- /dev/null 2016-08-25 19:26:57.000000000 +0530 +++ new/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePoolConfiguration.java 2016-08-25 19:26:56.228007100 +0530 @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2016, 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.module.Configuration; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReference; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.ByteBuffer; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import jdk.tools.jlink.plugin.PluginException; +import jdk.tools.jlink.plugin.ResourcePool; +import jdk.tools.jlink.plugin.ResourcePoolEntry; +import jdk.tools.jlink.plugin.ResourcePoolModule; + +final class ResourcePoolConfiguration { + private ResourcePoolConfiguration() {} + + private static ModuleDescriptor descriptorOf(ResourcePoolModule mod) { + ModuleDescriptor md = mod.descriptor(); + + // drop hashes + ModuleDescriptor.Builder builder = new ModuleDescriptor.Builder(md.name()); + md.requires().stream() + .forEach(builder::requires); + md.exports().stream() + .forEach(builder::exports); + md.uses().stream() + .forEach(builder::uses); + md.provides().values().stream() + .forEach(builder::provides); + + // build the proper concealed packages + Set exps = md.exports().stream() + .map(ModuleDescriptor.Exports::source) + .collect(Collectors.toSet()); + + mod.packages().stream() + .filter(pn -> !exps.contains(pn)) + .forEach(builder::conceals); + + md.version().ifPresent(builder::version); + md.mainClass().ifPresent(builder::mainClass); + md.osName().ifPresent(builder::osName); + md.osArch().ifPresent(builder::osArch); + md.osVersion().ifPresent(builder::osVersion); + + return builder.build(); + } + + private static ModuleReference moduleReference(ModuleDescriptor desc) { + return new ModuleReference(desc, null, () -> { + IOException ioe = new IOException(""); + throw new UncheckedIOException(ioe); + }); + } + + private static Map allModRefs(ResourcePool pool) { + return pool.moduleView().modules(). + collect(Collectors.toMap(ResourcePoolModule::name, + m -> moduleReference(descriptorOf(m)))); + } + + private static void checkPackages(ResourcePool pool) { + // check that each resource pool module's packages() + // returns a set that is consistent with the module + // descriptor of that module. + + pool.moduleView().modules().forEach(m -> { + ModuleDescriptor desc = m.descriptor(); + if (!desc.packages().equals(m.packages())) { + throw new RuntimeException("Module " + m.name() + + "'s descriptor returns inconsistent package set"); + } + }); + } + + static Configuration validate(ResourcePool pool, Collection roots) { + checkPackages(pool); + final Map nameToModRef = allModRefs(pool); + final Set allRefs = new HashSet<>(nameToModRef.values()); + + final ModuleFinder finder = new ModuleFinder() { + @Override + public Optional find(String name) { + return Optional.ofNullable(nameToModRef.get(name)); + } + + @Override + public Set findAll() { + return allRefs; + } + }; + + return Configuration.empty().resolveRequiresAndUses( + finder, ModuleFinder.of(), roots); + } +} --- /dev/null 2016-08-25 19:27:00.000000000 +0530 +++ new/test/tools/jlink/customplugin/plugin/RogueAdderPlugin.java 2016-08-25 19:26:59.408011600 +0530 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package plugin; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.lang.module.ModuleDescriptor; +import java.util.Collections; +import java.util.Map; +import java.util.function.Function; +import jdk.tools.jlink.plugin.ResourcePool; +import jdk.tools.jlink.plugin.ResourcePoolBuilder; +import jdk.tools.jlink.plugin.ResourcePoolEntry; +import jdk.tools.jlink.plugin.ResourcePoolModule; +import jdk.tools.jlink.plugin.Plugin; + +/** + * Rogue adder plugin + */ +public final class RogueAdderPlugin implements Plugin { + public static final String NAME = "rogue-adder"; + private String resName; + + @Override + public String getName() { + return NAME; + } + + @Override + public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { + in.transformAndCopy(Function.identity(), out); + out.add(ResourcePoolEntry.create(resName, new byte[1])); + return out.build(); + } + + @Override + public String getDescription() { + return NAME + "-description"; + } + + @Override + public Category getType() { + return Category.FILTER; + } + + @Override + public boolean hasArguments() { + return true; + } + + @Override + public void configure(Map config) { + resName = config.get(NAME); + } +} --- /dev/null 2016-08-25 19:27:03.000000000 +0530 +++ new/test/tools/jlink/customplugin/plugin/RogueFilterPlugin.java 2016-08-25 19:27:02.238015500 +0530 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package plugin; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Collections; +import java.util.Map; +import jdk.tools.jlink.plugin.ResourcePoolEntry; +import jdk.tools.jlink.plugin.ResourcePool; +import jdk.tools.jlink.plugin.ResourcePoolBuilder; +import jdk.tools.jlink.plugin.Plugin; + +/** + * Rogue filter plugin + */ +public final class RogueFilterPlugin implements Plugin { + public static final String NAME = "rogue-filter"; + private String prefix; + + @Override + public String getName() { + return NAME; + } + + @Override + public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { + in.transformAndCopy((file) -> { + return file.path().startsWith(prefix)? null : file; + }, out); + return out.build(); + } + + @Override + public String getDescription() { + return NAME + "-description"; + } + + @Override + public Category getType() { + return Category.FILTER; + } + + @Override + public boolean hasArguments() { + return true; + } + + @Override + public void configure(Map config) { + prefix = config.get(NAME); + } +}