# HG changeset patch # User sundar # Date 1472192436 -19800 # Node ID 27f68990ba9dc40c7f85dc27e27ba6cb0b6f070c # Parent face0a3ae86cb1fcf0b7bdacb45043c4b8132406 8147491: module graph consistency checks after jlink plugins operate on module pool Reviewed-by: jlaskey, mchung, psandoz diff -r face0a3ae86c -r 27f68990ba9d jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java Thu Aug 25 21:58:38 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java Fri Aug 26 11:50:36 2016 +0530 @@ -172,7 +172,7 @@ private final Plugin lastSorter; private final List plugins = new ArrayList<>(); private final List resourcePrevisitors = new ArrayList<>(); - + private final boolean validate; public ImagePluginStack() { this(null, Collections.emptyList(), null); @@ -181,6 +181,13 @@ public ImagePluginStack(ImageBuilder imageBuilder, List plugins, Plugin lastSorter) { + this(imageBuilder, plugins, lastSorter, true); + } + + public ImagePluginStack(ImageBuilder imageBuilder, + List plugins, + Plugin lastSorter, + boolean validate) { this.imageBuilder = Objects.requireNonNull(imageBuilder); this.lastSorter = lastSorter; this.plugins.addAll(Objects.requireNonNull(plugins)); @@ -190,6 +197,7 @@ resourcePrevisitors.add((ResourcePrevisitor) p); } }); + this.validate = validate; } 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 (validate) { + ResourcePoolConfiguration.validate(lastPool); + } + imageBuilder.storeFiles(lastPool); } public ExecutableImage getExecutableImage() throws IOException { diff -r face0a3ae86c -r 27f68990ba9d jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePoolConfiguration.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePoolConfiguration.java Fri Aug 26 11:50:36 2016 +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) { + 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().resolveRequires( + finder, ModuleFinder.of(), nameToModRef.keySet()); + } +} diff -r face0a3ae86c -r 27f68990ba9d jdk/test/tools/jlink/CustomPluginTest.java --- a/jdk/test/tools/jlink/CustomPluginTest.java Thu Aug 25 21:58:38 2016 -0700 +++ b/jdk/test/tools/jlink/CustomPluginTest.java Fri Aug 26 11:50:36 2016 +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"); + } + } } diff -r face0a3ae86c -r 27f68990ba9d jdk/test/tools/jlink/ImageFileCreatorTest.java --- a/jdk/test/tools/jlink/ImageFileCreatorTest.java Thu Aug 25 21:58:38 2016 -0700 +++ b/jdk/test/tools/jlink/ImageFileCreatorTest.java Fri Aug 26 11:50:36 2016 +0530 @@ -220,7 +220,7 @@ }; ImagePluginStack stack = new ImagePluginStack(noopBuilder, Collections.emptyList(), - null); + null, false); ImageFileCreator.create(archives, ByteOrder.nativeOrder(), stack); } diff -r face0a3ae86c -r 27f68990ba9d jdk/test/tools/jlink/customplugin/module-info.java --- a/jdk/test/tools/jlink/customplugin/module-info.java Thu Aug 25 21:58:38 2016 -0700 +++ b/jdk/test/tools/jlink/customplugin/module-info.java Fri Aug 26 11:50:36 2016 +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; } diff -r face0a3ae86c -r 27f68990ba9d jdk/test/tools/jlink/customplugin/plugin/RogueAdderPlugin.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jlink/customplugin/plugin/RogueAdderPlugin.java Fri Aug 26 11:50:36 2016 +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); + } +} diff -r face0a3ae86c -r 27f68990ba9d jdk/test/tools/jlink/customplugin/plugin/RogueFilterPlugin.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jlink/customplugin/plugin/RogueFilterPlugin.java Fri Aug 26 11:50:36 2016 +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); + } +}