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.UncheckedIOException; 31 import java.lang.module.ModuleDescriptor; 32 import java.util.EnumSet; 33 import java.util.HashMap; 34 import java.util.Map; 35 import java.util.Optional; 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.ResourcePool; 43 import jdk.tools.jlink.plugin.ResourcePoolBuilder; 44 import jdk.tools.jlink.plugin.ResourcePoolEntry; 45 import jdk.tools.jlink.plugin.ResourcePoolModule; 46 import jdk.tools.jlink.plugin.Plugin.Category; 47 import jdk.tools.jlink.plugin.Plugin.State; 48 import jdk.tools.jlink.plugin.Plugin; 49 50 /** 51 * This plugin adds/deletes information for 'release' file. 52 */ 53 public final class ReleaseInfoPlugin implements Plugin { 54 // option name 55 public static final String NAME = "release-info"; 56 public static final String KEYS = "keys"; 57 private final Map<String, String> release = new HashMap<>(); 58 59 @Override 60 public Category getType() { 61 return Category.METAINFO_ADDER; 62 } 63 64 @Override 65 public String getName() { 66 return NAME; 67 } 68 69 @Override 70 public String getDescription() { 71 return PluginsResourceBundle.getDescription(NAME); 72 } 73 74 @Override 75 public Set<State> getState() { 76 return EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL); 77 } 78 79 @Override 80 public boolean hasArguments() { 81 return true; 82 } 83 84 @Override 85 public String getArgumentsDescription() { 86 return PluginsResourceBundle.getArgument(NAME); 87 } 88 89 @Override 90 public void configure(Map<String, String> config) { 91 String operation = config.get(NAME); 92 if (operation == null) { 93 return; 94 } 95 96 switch (operation) { 97 case "add": { 98 // leave it to open-ended! source, java_version, java_full_version 99 // can be passed via this option like: 100 // 101 // --release-info add:build_type=fastdebug,source=openjdk,java_version=9 102 // and put whatever value that was passed in command line. 103 104 config.keySet().stream(). 105 filter(s -> !NAME.equals(s)). 106 forEach(s -> release.put(s, config.get(s))); 107 } 108 break; 109 110 case "del": { 111 // --release-info del:keys=openjdk,java_version 112 Utils.parseList(config.get(KEYS)).stream().forEach((k) -> { 113 release.remove(k); 114 }); 115 } 116 break; 117 118 default: { 119 // --release-info <file> 120 Properties props = new Properties(); 121 try (FileInputStream fis = new FileInputStream(operation)) { 122 props.load(fis); 123 } catch (IOException exp) { 124 throw new UncheckedIOException(exp); 125 } 126 props.forEach((k, v) -> release.put(k.toString(), v.toString())); 127 } 128 break; 129 } 130 } 131 132 @Override 133 public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { 134 in.transformAndCopy(Function.identity(), out); 135 136 Optional<ResourcePoolModule> javaBase = in.moduleView().findModule("java.base"); 137 javaBase.ifPresent(mod -> { 138 // fill release information available from transformed "java.base" module! 139 ModuleDescriptor desc = mod.descriptor(); 140 desc.osName().ifPresent(s -> { 141 release.put("OS_NAME", quote(s)); 142 }); 143 desc.osVersion().ifPresent(s -> release.put("OS_VERSION", quote(s))); 144 desc.osArch().ifPresent(s -> release.put("OS_ARCH", quote(s))); 145 desc.version().ifPresent(s -> release.put("JAVA_VERSION", 146 quote(parseVersion(s.toString())))); 147 desc.version().ifPresent(s -> release.put("JAVA_FULL_VERSION", 148 quote(s.toString()))); 149 }); 150 151 // put topological sorted module names separated by space 152 release.put("MODULES", new ModuleSorter(in.moduleView()) 153 .sorted().map(ResourcePoolModule::name) 154 .collect(Collectors.joining(" ", "\"", "\""))); 155 156 // create a TOP level ResourcePoolEntry for "release" file. 157 out.add(ResourcePoolEntry.create("/java.base/release", 158 ResourcePoolEntry.Type.TOP, 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. 164 private static String parseVersion(String str) { 165 return Runtime.Version.parse(str). 166 version(). 167 stream(). 168 map(Object::toString). 169 collect(Collectors.joining(".")); 170 } 171 172 private static String quote(String str) { 173 return "\"" + str + "\""; 174 } 175 176 private byte[] releaseFileContent() { 177 Properties props = new Properties(); 178 props.putAll(release); 179 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 180 try { 181 props.store(baos, ""); 182 return baos.toByteArray(); 183 } catch (IOException ex) { 184 throw new UncheckedIOException(ex); 185 } 186 } 187 }