/* * 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.plugins; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import jdk.tools.jlink.internal.ModuleSorter; import jdk.tools.jlink.internal.Utils; import jdk.tools.jlink.plugin.Plugin; import jdk.tools.jlink.plugin.PluginException; import jdk.tools.jlink.plugin.ResourcePool; import jdk.tools.jlink.plugin.ResourcePoolBuilder; import jdk.tools.jlink.plugin.ResourcePoolEntry; import jdk.tools.jlink.plugin.ResourcePoolEntry.Type; import jdk.tools.jlink.plugin.ResourcePoolModule; /** * A plugin to de-duplicate the legal notices from JMOD files. * * For a de-duplicated legal notice, the actual copy will be in * the base module and with symbolic links in other modules. * On platform that does not support symbolic links, a file * will be created to contain the path to the linked target. */ public final class LegalNoticeFilePlugin implements Plugin { private static final String NAME = "dedup-legal-notices"; private static final String ERROR_IF_NOT_SAME_CONTENT = "error-if-not-same-content"; private final Map> licenseOrNotice = new HashMap<>(); private boolean errorIfNotSameContent = false; @Override public String getName() { return NAME; } @Override public Set getState() { return EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL); } @Override public void configure(Map config) { String arg = config.get(NAME); if (arg != null) { if (arg.equals(ERROR_IF_NOT_SAME_CONTENT)) { errorIfNotSameContent = true; } else { throw new IllegalArgumentException(NAME + ": " + arg); } } } @Override public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { // Sort modules in the topological order // process all legal notices/licenses entries new ModuleSorter(in.moduleView()) .sorted() .flatMap(ResourcePoolModule::entries) .filter(entry -> entry.type() == Type.LEGAL_NOTICE) .forEach(this::dedupLegalNoticeEntry); in.entries() .filter(entry -> entry.type() != Type.LEGAL_NOTICE) .forEach(out::add); licenseOrNotice.values().stream() .flatMap(List::stream) .forEach(out::add); return out.build(); } private void dedupLegalNoticeEntry(ResourcePoolEntry entry) { Path path = Utils.getJRTFSPath(entry.path()); Path filename = path.getFileName(); List entries = licenseOrNotice.computeIfAbsent(filename.toString(), _k -> new ArrayList<>()); Optional otarget = entries.stream() .filter(e -> e.linkedTarget() == null) .filter(e -> Arrays.equals(e.contentBytes(), entry.contentBytes())) .findFirst(); if (!otarget.isPresent()) { if (errorIfNotSameContent) { // all legal notices of the same file name are expected // to contain the same content Optional ores = entries.stream().filter(e -> e.linkedTarget() == null) .findAny(); if (ores.isPresent()) { throw new PluginException(ores.get().path() + " " + entry.path() + " contain different content"); } } entries.add(entry); } else { entries.add(ResourcePoolEntry.createSymLink(entry.path(), entry.type(), otarget.get())); } } @Override public Category getType() { return Category.TRANSFORMER; } @Override public String getDescription() { return PluginsResourceBundle.getDescription(NAME); } @Override public boolean hasArguments() { return true; } @Override public String getArgumentsDescription() { return PluginsResourceBundle.getArgument(NAME); } }