| /* |
| * 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<String, List<ResourcePoolEntry>> licenseOrNotice = |
| new HashMap<>(); |
| |
| private boolean errorIfNotSameContent = false; |
| |
| @Override |
| public String getName() { |
| return NAME; |
| } |
| |
| @Override |
| public Set<State> getState() { |
| return EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL); |
| } |
| |
| @Override |
| public void configure(Map<String, String> 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<ResourcePoolEntry> entries = |
| licenseOrNotice.computeIfAbsent(filename.toString(), _k -> new ArrayList<>()); |
| |
| Optional<ResourcePoolEntry> 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<ResourcePoolEntry> 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); |
| } |
| } |