Path: blob/master/test/jdk/tools/jlink/plugins/CompressorPluginTest.java
41152 views
/*1* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @summary Test zip compressor26* @author Jean-Francois Denise27* @modules java.base/jdk.internal.jimage.decompressor28* jdk.jlink/jdk.tools.jlink.internal29* jdk.jlink/jdk.tools.jlink.internal.plugins30* jdk.jlink/jdk.tools.jlink.plugin31* @run main CompressorPluginTest32*/33import java.net.URI;34import java.nio.ByteOrder;35import java.nio.file.FileSystem;36import java.nio.file.FileSystemNotFoundException;37import java.nio.file.FileSystems;38import java.nio.file.Files;39import java.nio.file.Path;40import java.nio.file.ProviderNotFoundException;41import java.util.Collections;42import java.util.HashMap;43import java.util.Iterator;44import java.util.List;45import java.util.Map;46import java.util.Properties;47import java.util.regex.Pattern;48import java.util.stream.Collectors;49import java.util.stream.Stream;5051import jdk.internal.jimage.decompressor.CompressedResourceHeader;52import jdk.internal.jimage.decompressor.ResourceDecompressor;53import jdk.internal.jimage.decompressor.ResourceDecompressorFactory;54import jdk.internal.jimage.decompressor.StringSharingDecompressorFactory;55import jdk.internal.jimage.decompressor.ZipDecompressorFactory;56import jdk.tools.jlink.internal.ResourcePoolManager;57import jdk.tools.jlink.internal.StringTable;58import jdk.tools.jlink.internal.plugins.DefaultCompressPlugin;59import jdk.tools.jlink.internal.plugins.StringSharingPlugin;60import jdk.tools.jlink.internal.plugins.ZipPlugin;61import jdk.tools.jlink.plugin.Plugin;62import jdk.tools.jlink.plugin.ResourcePool;63import jdk.tools.jlink.plugin.ResourcePoolBuilder;64import jdk.tools.jlink.plugin.ResourcePoolEntry;6566public class CompressorPluginTest {6768private static int strID = 1;6970public static void main(String[] args) throws Exception {71new CompressorPluginTest().test();72}7374public void test() throws Exception {75FileSystem fs;76try {77fs = FileSystems.getFileSystem(URI.create("jrt:/"));78} catch (ProviderNotFoundException | FileSystemNotFoundException e) {79System.err.println("Not an image build, test skipped.");80return;81}82Path javabase = fs.getPath("/modules/java.base");8384checkCompress(gatherResources(javabase), new ZipPlugin(), null,85new ResourceDecompressorFactory[]{86new ZipDecompressorFactory()87});8889ResourcePool classes = gatherClasses(javabase);90// compress = String sharing91checkCompress(classes, new StringSharingPlugin(), null,92new ResourceDecompressorFactory[]{93new StringSharingDecompressorFactory()});9495// compress level 0 == no compression96Properties options0 = new Properties();97DefaultCompressPlugin compressPlugin = new DefaultCompressPlugin();98options0.setProperty(compressPlugin.getName(),99DefaultCompressPlugin.LEVEL_0);100checkCompress(classes, compressPlugin,101options0,102new ResourceDecompressorFactory[]{103});104105// compress level 1 == String sharing106Properties options1 = new Properties();107compressPlugin = new DefaultCompressPlugin();108options1.setProperty(compressPlugin.getName(), DefaultCompressPlugin.LEVEL_1);109checkCompress(classes, compressPlugin,110options1,111new ResourceDecompressorFactory[]{112new StringSharingDecompressorFactory()113});114115// compress level 1 == String sharing + filter116options1.setProperty(DefaultCompressPlugin.FILTER,117"**Exception.class");118compressPlugin = new DefaultCompressPlugin();119options1.setProperty(compressPlugin.getName(), DefaultCompressPlugin.LEVEL_1);120checkCompress(classes, compressPlugin,121options1,122new ResourceDecompressorFactory[]{123new StringSharingDecompressorFactory()124}, Collections.singletonList(".*Exception.class"));125126// compress level 2 == ZIP127Properties options2 = new Properties();128options2.setProperty(DefaultCompressPlugin.FILTER,129"**Exception.class");130compressPlugin = new DefaultCompressPlugin();131options2.setProperty(compressPlugin.getName(), DefaultCompressPlugin.LEVEL_2);132checkCompress(classes, compressPlugin,133options2,134new ResourceDecompressorFactory[]{135new ZipDecompressorFactory()136}, Collections.singletonList(".*Exception.class"));137138// compress level 2 == ZIP + filter139options2.setProperty(DefaultCompressPlugin.FILTER,140"**Exception.class");141compressPlugin = new DefaultCompressPlugin();142options2.setProperty(compressPlugin.getName(), DefaultCompressPlugin.LEVEL_2);143checkCompress(classes, compressPlugin,144options2,145new ResourceDecompressorFactory[]{146new ZipDecompressorFactory(),147}, Collections.singletonList(".*Exception.class"));148}149150private ResourcePool gatherResources(Path module) throws Exception {151ResourcePoolManager poolMgr = new ResourcePoolManager(ByteOrder.nativeOrder(), new StringTable() {152153@Override154public int addString(String str) {155return -1;156}157158@Override159public String getString(int id) {160return null;161}162});163164ResourcePoolBuilder poolBuilder = poolMgr.resourcePoolBuilder();165try (Stream<Path> stream = Files.walk(module)) {166for (Iterator<Path> iterator = stream.iterator(); iterator.hasNext();) {167Path p = iterator.next();168if (Files.isRegularFile(p)) {169byte[] content = Files.readAllBytes(p);170poolBuilder.add(ResourcePoolEntry.create(p.toString(), content));171}172}173}174return poolBuilder.build();175}176177private ResourcePool gatherClasses(Path module) throws Exception {178ResourcePoolManager poolMgr = new ResourcePoolManager(ByteOrder.nativeOrder(), new StringTable() {179180@Override181public int addString(String str) {182return -1;183}184185@Override186public String getString(int id) {187return null;188}189});190191ResourcePoolBuilder poolBuilder = poolMgr.resourcePoolBuilder();192try (Stream<Path> stream = Files.walk(module)) {193for (Iterator<Path> iterator = stream.iterator(); iterator.hasNext();) {194Path p = iterator.next();195if (Files.isRegularFile(p) && p.toString().endsWith(".class")) {196byte[] content = Files.readAllBytes(p);197poolBuilder.add(ResourcePoolEntry.create(p.toString(), content));198}199}200}201return poolBuilder.build();202}203204private void checkCompress(ResourcePool resources, Plugin prov,205Properties config,206ResourceDecompressorFactory[] factories) throws Exception {207checkCompress(resources, prov, config, factories, Collections.emptyList());208}209210private void checkCompress(ResourcePool resources, Plugin prov,211Properties config,212ResourceDecompressorFactory[] factories,213List<String> includes) throws Exception {214if (factories.length == 0) {215// no compression, nothing to check!216return;217}218219long[] original = new long[1];220long[] compressed = new long[1];221resources.entries().forEach(resource -> {222List<Pattern> includesPatterns = includes.stream()223.map(Pattern::compile)224.collect(Collectors.toList());225226Map<String, String> props = new HashMap<>();227if (config != null) {228for (String p : config.stringPropertyNames()) {229props.put(p, config.getProperty(p));230}231}232prov.configure(props);233final Map<Integer, String> strings = new HashMap<>();234ResourcePoolManager inputResourcesMgr = new ResourcePoolManager(ByteOrder.nativeOrder(), new StringTable() {235@Override236public int addString(String str) {237int id = strID;238strID += 1;239strings.put(id, str);240return id;241}242243@Override244public String getString(int id) {245return strings.get(id);246}247});248inputResourcesMgr.add(resource);249ResourcePool compressedResources = applyCompressor(prov, inputResourcesMgr, resource, includesPatterns);250original[0] += resource.contentLength();251compressed[0] += compressedResources.findEntry(resource.path()).get().contentLength();252applyDecompressors(factories, inputResourcesMgr.resourcePool(), compressedResources, strings, includesPatterns);253});254String compressors = Stream.of(factories)255.map(Object::getClass)256.map(Class::getSimpleName)257.collect(Collectors.joining(", "));258String size = "Compressed size: " + compressed[0] + ", original size: " + original[0];259System.out.println("Used " + compressors + ". " + size);260if (original[0] <= compressed[0]) {261throw new AssertionError("java.base not compressed.");262}263}264265private ResourcePool applyCompressor(Plugin plugin,266ResourcePoolManager inputResources,267ResourcePoolEntry res,268List<Pattern> includesPatterns) {269ResourcePoolManager resMgr = new ResourcePoolManager(ByteOrder.nativeOrder(),270inputResources.getStringTable());271ResourcePool compressedResourcePool = plugin.transform(inputResources.resourcePool(),272resMgr.resourcePoolBuilder());273String path = res.path();274ResourcePoolEntry compressed = compressedResourcePool.findEntry(path).get();275CompressedResourceHeader header276= CompressedResourceHeader.readFromResource(ByteOrder.nativeOrder(), compressed.contentBytes());277if (isIncluded(includesPatterns, path)) {278if (header == null) {279throw new AssertionError("Path should be compressed: " + path);280}281if (header.getDecompressorNameOffset() == 0) {282throw new AssertionError("Invalid plugin offset "283+ header.getDecompressorNameOffset());284}285if (header.getResourceSize() <= 0) {286throw new AssertionError("Invalid compressed size "287+ header.getResourceSize());288}289} else if (header != null) {290throw new AssertionError("Path should not be compressed: " + path);291}292return compressedResourcePool;293}294295private void applyDecompressors(ResourceDecompressorFactory[] decompressors,296ResourcePool inputResources,297ResourcePool compressedResources,298Map<Integer, String> strings,299List<Pattern> includesPatterns) {300compressedResources.entries().forEach(compressed -> {301CompressedResourceHeader header = CompressedResourceHeader.readFromResource(302ByteOrder.nativeOrder(), compressed.contentBytes());303String path = compressed.path();304ResourcePoolEntry orig = inputResources.findEntry(path).get();305if (!isIncluded(includesPatterns, path)) {306return;307}308byte[] decompressed = compressed.contentBytes();309for (ResourceDecompressorFactory factory : decompressors) {310try {311ResourceDecompressor decompressor = factory.newDecompressor(new Properties());312decompressed = decompressor.decompress(313strings::get, decompressed,314CompressedResourceHeader.getSize(), header.getUncompressedSize());315} catch (Exception exp) {316throw new RuntimeException(exp);317}318}319320if (decompressed.length != orig.contentLength()) {321throw new AssertionError("Invalid uncompressed size "322+ header.getUncompressedSize());323}324byte[] origContent = orig.contentBytes();325for (int i = 0; i < decompressed.length; i++) {326if (decompressed[i] != origContent[i]) {327throw new AssertionError("Decompressed and original differ at index " + i);328}329}330});331}332333private boolean isIncluded(List<Pattern> includesPatterns, String path) {334return includesPatterns.isEmpty() ||335includesPatterns.stream().anyMatch((pattern) -> pattern.matcher(path).matches());336}337}338339340