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 */ 25 package jdk.tools.jlink.internal.plugins; 26 27 import java.io.ByteArrayOutputStream; 28 import java.io.FileInputStream; 29 import java.io.IOException; 30 import java.io.PrintWriter; 31 import java.io.UncheckedIOException; 32 import java.lang.module.ModuleDescriptor; 33 import java.util.EnumSet; 34 import java.util.HashMap; 35 import java.util.Map; 36 import java.util.Properties; 37 import java.util.Set; 38 import java.util.function.Function; 39 import java.util.stream.Collectors; 40 import jdk.tools.jlink.internal.ModuleSorter; 41 import jdk.tools.jlink.internal.Utils; 42 import jdk.tools.jlink.plugin.PluginException; 43 import jdk.tools.jlink.plugin.ResourcePool; 44 import jdk.tools.jlink.plugin.ResourcePoolBuilder; 45 import jdk.tools.jlink.plugin.ResourcePoolEntry; 46 import jdk.tools.jlink.plugin.ResourcePoolModule; 47 import jdk.tools.jlink.plugin.Plugin; 48 49 /** 50 * This plugin adds/deletes information for 'release' file. 51 */ 52 public 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 getType()59 public Category getType() { 60 return Category.METAINFO_ADDER; 61 } 62 63 @Override getName()64 public String getName() { 65 return NAME; 66 } 67 68 @Override getDescription()69 public String getDescription() { 70 return PluginsResourceBundle.getDescription(NAME); 71 } 72 73 @Override getState()74 public Set<State> getState() { 75 return EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL); 76 } 77 78 @Override hasArguments()79 public boolean hasArguments() { 80 return true; 81 } 82 83 @Override getArgumentsDescription()84 public String getArgumentsDescription() { 85 return PluginsResourceBundle.getArgument(NAME); 86 } 87 88 @Override configure(Map<String, String> config)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 String keys = config.get(KEYS); 112 if (keys == null || keys.isEmpty()) { 113 throw new IllegalArgumentException("No key specified for delete"); 114 } 115 Utils.parseList(keys).stream().forEach((k) -> { 116 release.remove(k); 117 }); 118 } 119 break; 120 121 default: { 122 // --release-info <file> 123 Properties props = new Properties(); 124 try (FileInputStream fis = new FileInputStream(operation)) { 125 props.load(fis); 126 } catch (IOException exp) { 127 throw new UncheckedIOException(exp); 128 } 129 props.forEach((k, v) -> release.put(k.toString(), v.toString())); 130 } 131 break; 132 } 133 } 134 135 @Override transform(ResourcePool in, ResourcePoolBuilder out)136 public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { 137 in.transformAndCopy(Function.identity(), out); 138 139 ResourcePoolModule javaBase = in.moduleView().findModule("java.base") 140 .orElse(null); 141 if (javaBase == null || javaBase.targetPlatform() == null) { 142 throw new PluginException("ModuleTarget attribute is missing for java.base module"); 143 } 144 145 // fill release information available from transformed "java.base" module! 146 ModuleDescriptor desc = javaBase.descriptor(); 147 desc.version().ifPresent(v -> release.put("JAVA_VERSION", 148 quote(parseVersion(v)))); 149 150 // put topological sorted module names separated by space 151 release.put("MODULES", new ModuleSorter(in.moduleView()) 152 .sorted().map(ResourcePoolModule::name) 153 .collect(Collectors.joining(" ", "\"", "\""))); 154 155 // create a TOP level ResourcePoolEntry for "release" file. 156 out.add(ResourcePoolEntry.create("/java.base/release", 157 ResourcePoolEntry.Type.TOP, 158 releaseFileContent())); 159 return out.build(); 160 } 161 162 // Parse version string and return a string that includes only version part 163 // leaving "pre", "build" information. See also: java.lang.Runtime.Version. parseVersion(ModuleDescriptor.Version v)164 private static String parseVersion(ModuleDescriptor.Version v) { 165 return Runtime.Version.parse(v.toString()) 166 .version() 167 .stream() 168 .map(Object::toString) 169 .collect(Collectors.joining(".")); 170 } 171 quote(String str)172 private static String quote(String str) { 173 return "\"" + str + "\""; 174 } 175 releaseFileContent()176 private byte[] releaseFileContent() { 177 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 178 try (PrintWriter pw = new PrintWriter(baos)) { 179 release.entrySet().stream() 180 .sorted(Map.Entry.comparingByKey()) 181 .forEach(e -> pw.format("%s=%s%n", e.getKey(), e.getValue())); 182 } 183 return baos.toByteArray(); 184 } 185 } 186