1 /*
2 * Copyright (c) 2015, 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.BufferedReader;
28 import java.io.IOException;
29 import java.io.InputStreamReader;
30 import java.io.UncheckedIOException;
31 import java.nio.charset.StandardCharsets;
32 import java.util.Comparator;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.TreeSet;
36 import java.util.function.Predicate;
37 import java.util.stream.Collectors;
38 import jdk.tools.jlink.plugin.Plugin;
39 import jdk.tools.jlink.plugin.ResourcePool;
40 import jdk.tools.jlink.plugin.ResourcePoolBuilder;
41 import jdk.tools.jlink.plugin.ResourcePoolModule;
42 import jdk.tools.jlink.plugin.ResourcePoolEntry;
43 import jdk.tools.jlink.plugin.PluginException;
44
45 /**
46 *
47 * Exclude VM plugin
48 */
49 public final class ExcludeVMPlugin implements Plugin {
50
51 private static final class JvmComparator implements Comparator<Jvm> {
52
53 @Override
54 public int compare(Jvm o1, Jvm o2) {
55 return o1.getEfficience() - o2.getEfficience();
56 }
57 }
58
59 private enum Jvm {
60 // The efficience order server - client - minimal.
61 SERVER("server", 3), CLIENT("client", 2), MINIMAL("minimal", 1);
62 private final String name;
63 private final int efficience;
64
65 Jvm(String name, int efficience) {
66 this.name = name;
67 this.efficience = efficience;
68 }
69
70 private String getName() {
71 return name;
72 }
73
74 private int getEfficience() {
75 return efficience;
76 }
77 }
78
79 private static final String JVM_CFG = "jvm.cfg";
80
81 public static final String NAME = "vm";
82 private static final String ALL = "all";
83 private static final String CLIENT = "client";
84 private static final String SERVER = "server";
85 private static final String MINIMAL = "minimal";
86
87 private Predicate<String> predicate;
88 private Jvm target;
89 private boolean keepAll;
90
91 @Override
92 public String getName() {
93 return NAME;
94 }
95
96 /**
97 * VM paths:
98 * /java.base/native/{architecture}/{server|client|minimal}/{shared lib}
99 * e.g.: /java.base/native/amd64/server/libjvm.so
100 * /java.base/native/server/libjvm.dylib
101 */
102 private List<ResourcePoolEntry> getVMs(ResourcePoolModule javaBase, String jvmlib) {
103 List<ResourcePoolEntry> ret = javaBase.entries().filter((t) -> {
104 return t.path().endsWith("/" + jvmlib);
105 }).collect(Collectors.toList());
106 return ret;
107 }
108
109 @Override
110 public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
111 ResourcePoolModule javaBase = in.moduleView().findModule("java.base").get();
112 String jvmlib = jvmlib(javaBase.descriptor().osName().get());
113 TreeSet<Jvm> existing = new TreeSet<>(new JvmComparator());
114 TreeSet<Jvm> removed = new TreeSet<>(new JvmComparator());
115 if (!keepAll) {
116 // First retrieve all available VM names and removed VM
117 List<ResourcePoolEntry> jvms = getVMs(javaBase, jvmlib);
118 for (Jvm jvm : Jvm.values()) {
119 for (ResourcePoolEntry md : jvms) {
120 if (md.path().endsWith("/" + jvm.getName() + "/" + jvmlib)) {
121 existing.add(jvm);
122 if (isRemoved(md)) {
123 removed.add(jvm);
124 }
125 }
126 }
127 }
128 }
129 // Check that target exists
130 if (!keepAll) {
131 if (!existing.contains(target)) {
132 throw new PluginException("Selected VM " + target.getName() + " doesn't exist.");
133 }
134 }
135
136 // Rewrite the jvm.cfg file.
137 in.transformAndCopy((file) -> {
138 if (!keepAll) {
139 if (file.type().equals(ResourcePoolEntry.Type.NATIVE_LIB)) {
140 if (file.path().endsWith(JVM_CFG)) {
141 try {
142 file = handleJvmCfgFile(file, existing, removed);
143 } catch (IOException ex) {
144 throw new UncheckedIOException(ex);
145 }
146 }
147 }
148 file = isRemoved(file) ? null : file;
149 }
150 return file;
151 }, out);
152
153 return out.build();
154 }
155
156 private boolean isRemoved(ResourcePoolEntry file) {
157 return !predicate.test(file.path());
158 }
159
160 @Override
161 public Category getType() {
162 return Category.FILTER;
163 }
164
165 @Override
166 public String getDescription() {
167 return PluginsResourceBundle.getDescription(NAME);
168 }
169
170 @Override
171 public boolean hasArguments() {
172 return true;
173 }
174
175 @Override
176 public String getArgumentsDescription() {
177 return PluginsResourceBundle.getArgument(NAME);
178 }
179
180 @Override
181 public void configure(Map<String, String> config) {
182 String value = config.get(NAME);
183 String exclude = "";
184 switch (value) {
185 case ALL: {
186 // no filter.
187 keepAll = true;
188 break;
189 }
190 case CLIENT: {
191 target = Jvm.CLIENT;
192 exclude = "/java.base/native**server/**,/java.base/native**minimal/**";
193 break;
194 }
195 case SERVER: {
196 target = Jvm.SERVER;
197 exclude = "/java.base/native**client/**,/java.base/native**minimal/**";
198 break;
199 }
200 case MINIMAL: {
201 target = Jvm.MINIMAL;
202 exclude = "/java.base/native**server/**,/java.base/native**client/**";
203 break;
204 }
205 default: {
206 throw new IllegalArgumentException("Unknown exclude VM option: " + value);
207 }
208 }
209 predicate = ResourceFilter.excludeFilter(exclude);
210 }
211
212 private ResourcePoolEntry handleJvmCfgFile(ResourcePoolEntry orig,
213 TreeSet<Jvm> existing,
214 TreeSet<Jvm> removed) throws IOException {
215 if (keepAll) {
216 return orig;
217 }
218 StringBuilder builder = new StringBuilder();
219 // Keep comments
220 try (BufferedReader reader
221 = new BufferedReader(new InputStreamReader(orig.content(),
222 StandardCharsets.UTF_8))) {
223 reader.lines().forEach((s) -> {
224 if (s.startsWith("#")) {
225 builder.append(s).append("\n");
226 }
227 });
228 }
229 TreeSet<Jvm> remaining = new TreeSet<>(new JvmComparator());
230 // Add entry in jvm.cfg file from the more efficient to less efficient.
231 for (Jvm platform : existing) {
232 if (!removed.contains(platform)) {
233 remaining.add(platform);
234 builder.append("-").append(platform.getName()).append(" KNOWN\n");
235 }
236 }
237
238 // removed JVM are aliased to the most efficient remaining JVM (last one).
239 // The order in the file is from most to less efficient platform
240 for (Jvm platform : removed.descendingSet()) {
241 builder.append("-").append(platform.getName()).
242 append(" ALIASED_TO -").
243 append(remaining.last().getName()).append("\n");
244 }
245
246 byte[] content = builder.toString().getBytes(StandardCharsets.UTF_8);
247
248 return orig.copyWithContent(content);
249 }
250
251 private static String jvmlib(String osName) {
252 String lib = "libjvm.so";
253 if (isWindows(osName)) {
254 lib = "jvm.dll";
255 } else if (isMac(osName)) {
256 lib = "libjvm.dylib";
257 }
258 return lib;
259 }
260
261 private static boolean isWindows(String osName) {
262 return osName.startsWith("Windows");
263 }
264
265 private static boolean isMac(String osName) {
266 return osName.startsWith("Mac OS");
267 }
268 }
--- EOF ---