1/* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25package jdk.tools.jlink.internal.plugins; 26 27import java.io.ByteArrayOutputStream; 28import java.io.FileInputStream; 29import java.io.IOException; 30import java.io.PrintWriter; 31import java.io.UncheckedIOException; 32import java.lang.module.ModuleDescriptor; 33import java.util.EnumSet; 34import java.util.HashMap; 35import java.util.Map; 36import java.util.Properties; 37import java.util.Set; 38import java.util.function.Function; 39import java.util.stream.Collectors; 40import jdk.tools.jlink.internal.ModuleSorter; 41import jdk.tools.jlink.internal.Utils; 42import jdk.tools.jlink.plugin.PluginException; 43import jdk.tools.jlink.plugin.ResourcePool; 44import jdk.tools.jlink.plugin.ResourcePoolBuilder; 45import jdk.tools.jlink.plugin.ResourcePoolEntry; 46import jdk.tools.jlink.plugin.ResourcePoolModule; 47import jdk.tools.jlink.plugin.Plugin; 48 49/** 50 * This plugin adds/deletes information for 'release' file. 51 */ 52public final class ReleaseInfoPlugin implements Plugin { 53 // option name 54 public static final String NAME = "release-info"; 55 public static final String KEYS = "keys"; 56 private final Map<String, String> release = new HashMap<>(); 57 58 @Override 59 public Category getType() { 60 return Category.METAINFO_ADDER; 61 } 62 63 @Override 64 public String getName() { 65 return NAME; 66 } 67 68 @Override 69 public String getDescription() { 70 return PluginsResourceBundle.getDescription(NAME); 71 } 72 73 @Override 74 public Set<State> getState() { 75 return EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL); 76 } 77 78 @Override 79 public boolean hasArguments() { 80 return true; 81 } 82 83 @Override 84 public String getArgumentsDescription() { 85 return PluginsResourceBundle.getArgument(NAME); 86 } 87 88 @Override 89 public void configure(Map<String, String> config) { 90 String operation = config.get(NAME); 91 if (operation == null) { 92 return; 93 } 94 95 switch (operation) { 96 case "add": { 97 // leave it to open-ended! source, java_version, java_full_version 98 // can be passed via this option like: 99 // 100 // --release-info add:build_type=fastdebug,source=openjdk,java_version=9 101 // and put whatever value that was passed in command line. 102 103 config.keySet().stream() 104 .filter(s -> !NAME.equals(s)) 105 .forEach(s -> release.put(s, config.get(s))); 106 } 107 break; 108 109 case "del": { 110 // --release-info del:keys=openjdk,java_version 111 Utils.parseList(config.get(KEYS)).stream().forEach((k) -> { 112 release.remove(k); 113 }); 114 } 115 break; 116 117 default: { 118 // --release-info <file> 119 Properties props = new Properties(); 120 try (FileInputStream fis = new FileInputStream(operation)) { 121 props.load(fis); 122 } catch (IOException exp) { 123 throw new UncheckedIOException(exp); 124 } 125 props.forEach((k, v) -> release.put(k.toString(), v.toString())); 126 } 127 break; 128 } 129 } 130 131 @Override 132 public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { 133 in.transformAndCopy(Function.identity(), out); 134 135 ResourcePoolModule javaBase = in.moduleView().findModule("java.base") 136 .orElse(null); 137 if (javaBase == null || javaBase.targetPlatform() == null) { 138 throw new PluginException("ModuleTarget attribute is missing for java.base module"); 139 } 140 141 // fill release information available from transformed "java.base" module! 142 ModuleDescriptor desc = javaBase.descriptor(); 143 desc.version().ifPresent(v -> release.put("JAVA_VERSION", 144 quote(parseVersion(v)))); 145 146 // put topological sorted module names separated by space 147 release.put("MODULES", new ModuleSorter(in.moduleView()) 148 .sorted().map(ResourcePoolModule::name) 149 .collect(Collectors.joining(" ", "\"", "\""))); 150 151 // create a TOP level ResourcePoolEntry for "release" file. 152 out.add(ResourcePoolEntry.create("/java.base/release", 153 ResourcePoolEntry.Type.TOP, 154 releaseFileContent())); 155 return out.build(); 156 } 157 158 // Parse version string and return a string that includes only version part 159 // leaving "pre", "build" information. See also: java.lang.Runtime.Version. 160 private static String parseVersion(ModuleDescriptor.Version v) { 161 return Runtime.Version.parse(v.toString()) 162 .version() 163 .stream() 164 .map(Object::toString) 165 .collect(Collectors.joining(".")); 166 } 167 168 private static String quote(String str) { 169 return "\"" + str + "\""; 170 } 171 172 private byte[] releaseFileContent() { 173 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 174 try (PrintWriter pw = new PrintWriter(baos)) { 175 release.entrySet().stream() 176 .sorted(Map.Entry.comparingByKey()) 177 .forEach(e -> pw.format("%s=%s%n", e.getKey(), e.getValue())); 178 } 179 return baos.toByteArray(); 180 } 181} 182