Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/sun/launcher/LauncherHelper.java
41152 views
1
/*
2
* Copyright (c) 2007, 2020, 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
26
package sun.launcher;
27
28
/*
29
*
30
* <p><b>This is NOT part of any API supported by Sun Microsystems.
31
* If you write code that depends on this, you do so at your own
32
* risk. This code and its internal interfaces are subject to change
33
* or deletion without notice.</b>
34
*
35
*/
36
37
/**
38
* A utility package for the java(1), javaw(1) launchers.
39
* The following are helper methods that the native launcher uses
40
* to perform checks etc. using JNI, see src/share/bin/java.c
41
*/
42
import java.io.File;
43
import java.io.IOException;
44
import java.io.PrintStream;
45
import java.io.UnsupportedEncodingException;
46
import java.lang.module.Configuration;
47
import java.lang.module.ModuleDescriptor;
48
import java.lang.module.ModuleDescriptor.Exports;
49
import java.lang.module.ModuleDescriptor.Opens;
50
import java.lang.module.ModuleDescriptor.Provides;
51
import java.lang.module.ModuleDescriptor.Requires;
52
import java.lang.module.ModuleFinder;
53
import java.lang.module.ModuleReference;
54
import java.lang.module.ResolvedModule;
55
import java.lang.reflect.InvocationTargetException;
56
import java.lang.reflect.Method;
57
import java.lang.reflect.Modifier;
58
import java.math.BigDecimal;
59
import java.math.RoundingMode;
60
import java.net.URI;
61
import java.nio.charset.Charset;
62
import java.nio.file.DirectoryStream;
63
import java.nio.file.Files;
64
import java.nio.file.Path;
65
import java.text.MessageFormat;
66
import java.text.Normalizer;
67
import java.util.ArrayList;
68
import java.util.Collections;
69
import java.util.Comparator;
70
import java.util.Iterator;
71
import java.util.List;
72
import java.util.Locale;
73
import java.util.Locale.Category;
74
import java.util.Optional;
75
import java.util.Properties;
76
import java.util.ResourceBundle;
77
import java.util.Set;
78
import java.util.TreeSet;
79
import java.util.jar.Attributes;
80
import java.util.jar.JarFile;
81
import java.util.jar.Manifest;
82
import java.util.stream.Collectors;
83
import java.util.stream.Stream;
84
85
import jdk.internal.misc.VM;
86
import jdk.internal.module.ModuleBootstrap;
87
import jdk.internal.module.Modules;
88
import jdk.internal.platform.Container;
89
import jdk.internal.platform.Metrics;
90
91
92
public final class LauncherHelper {
93
94
// No instantiation
95
private LauncherHelper() {}
96
97
// used to identify JavaFX applications
98
private static final String JAVAFX_APPLICATION_MARKER =
99
"JavaFX-Application-Class";
100
private static final String JAVAFX_APPLICATION_CLASS_NAME =
101
"javafx.application.Application";
102
private static final String JAVAFX_FXHELPER_CLASS_NAME_SUFFIX =
103
"sun.launcher.LauncherHelper$FXHelper";
104
private static final String LAUNCHER_AGENT_CLASS = "Launcher-Agent-Class";
105
private static final String MAIN_CLASS = "Main-Class";
106
private static final String ADD_EXPORTS = "Add-Exports";
107
private static final String ADD_OPENS = "Add-Opens";
108
109
private static StringBuilder outBuf = new StringBuilder();
110
111
private static final String INDENT = " ";
112
private static final String VM_SETTINGS = "VM settings:";
113
private static final String PROP_SETTINGS = "Property settings:";
114
private static final String LOCALE_SETTINGS = "Locale settings:";
115
116
// sync with java.c and jdk.internal.misc.VM
117
private static final String diagprop = "sun.java.launcher.diag";
118
static final boolean trace = VM.getSavedProperty(diagprop) != null;
119
120
private static final String defaultBundleName =
121
"sun.launcher.resources.launcher";
122
123
private static class ResourceBundleHolder {
124
private static final ResourceBundle RB =
125
ResourceBundle.getBundle(defaultBundleName);
126
}
127
private static PrintStream ostream;
128
private static Class<?> appClass; // application class, for GUI/reporting purposes
129
130
/*
131
* A method called by the launcher to print out the standard settings,
132
* by default -XshowSettings is equivalent to -XshowSettings:all,
133
* Specific information may be gotten by using suboptions with possible
134
* values vm, properties and locale.
135
*
136
* printToStderr: choose between stdout and stderr
137
*
138
* optionFlag: specifies which options to print default is all other
139
* possible values are vm, properties, locale.
140
*
141
* initialHeapSize: in bytes, as set by the launcher, a zero-value indicates
142
* this code should determine this value, using a suitable method or
143
* the line could be omitted.
144
*
145
* maxHeapSize: in bytes, as set by the launcher, a zero-value indicates
146
* this code should determine this value, using a suitable method.
147
*
148
* stackSize: in bytes, as set by the launcher, a zero-value indicates
149
* this code determine this value, using a suitable method or omit the
150
* line entirely.
151
*/
152
@SuppressWarnings("fallthrough")
153
static void showSettings(boolean printToStderr, String optionFlag,
154
long initialHeapSize, long maxHeapSize, long stackSize) {
155
156
initOutput(printToStderr);
157
String opts[] = optionFlag.split(":");
158
String optStr = (opts.length > 1 && opts[1] != null)
159
? opts[1].trim()
160
: "all";
161
switch (optStr) {
162
case "vm":
163
printVmSettings(initialHeapSize, maxHeapSize, stackSize);
164
break;
165
case "properties":
166
printProperties();
167
break;
168
case "locale":
169
printLocale();
170
break;
171
case "system":
172
if (System.getProperty("os.name").contains("Linux")) {
173
printSystemMetrics();
174
break;
175
}
176
default:
177
printVmSettings(initialHeapSize, maxHeapSize, stackSize);
178
printProperties();
179
printLocale();
180
if (System.getProperty("os.name").contains("Linux")) {
181
printSystemMetrics();
182
}
183
break;
184
}
185
}
186
187
/*
188
* prints the main vm settings subopt/section
189
*/
190
private static void printVmSettings(
191
long initialHeapSize, long maxHeapSize,
192
long stackSize) {
193
194
ostream.println(VM_SETTINGS);
195
if (stackSize != 0L) {
196
ostream.println(INDENT + "Stack Size: " +
197
SizePrefix.scaleValue(stackSize));
198
}
199
if (initialHeapSize != 0L) {
200
ostream.println(INDENT + "Min. Heap Size: " +
201
SizePrefix.scaleValue(initialHeapSize));
202
}
203
if (maxHeapSize != 0L) {
204
ostream.println(INDENT + "Max. Heap Size: " +
205
SizePrefix.scaleValue(maxHeapSize));
206
} else {
207
ostream.println(INDENT + "Max. Heap Size (Estimated): "
208
+ SizePrefix.scaleValue(Runtime.getRuntime().maxMemory()));
209
}
210
ostream.println(INDENT + "Using VM: "
211
+ System.getProperty("java.vm.name"));
212
ostream.println();
213
}
214
215
/*
216
* prints the properties subopt/section
217
*/
218
private static void printProperties() {
219
Properties p = System.getProperties();
220
ostream.println(PROP_SETTINGS);
221
List<String> sortedPropertyKeys = new ArrayList<>();
222
sortedPropertyKeys.addAll(p.stringPropertyNames());
223
Collections.sort(sortedPropertyKeys);
224
for (String x : sortedPropertyKeys) {
225
printPropertyValue(x, p.getProperty(x));
226
}
227
ostream.println();
228
}
229
230
private static boolean isPath(String key) {
231
return key.endsWith(".dirs") || key.endsWith(".path");
232
}
233
234
private static void printPropertyValue(String key, String value) {
235
ostream.print(INDENT + key + " = ");
236
if (key.equals("line.separator")) {
237
for (byte b : value.getBytes()) {
238
switch (b) {
239
case 0xd:
240
ostream.print("\\r ");
241
break;
242
case 0xa:
243
ostream.print("\\n ");
244
break;
245
default:
246
// print any bizzare line separators in hex, but really
247
// shouldn't happen.
248
ostream.printf("0x%02X", b & 0xff);
249
break;
250
}
251
}
252
ostream.println();
253
return;
254
}
255
if (!isPath(key)) {
256
ostream.println(value);
257
return;
258
}
259
String[] values = value.split(System.getProperty("path.separator"));
260
boolean first = true;
261
for (String s : values) {
262
if (first) { // first line treated specially
263
ostream.println(s);
264
first = false;
265
} else { // following lines prefix with indents
266
ostream.println(INDENT + INDENT + s);
267
}
268
}
269
}
270
271
/*
272
* prints the locale subopt/section
273
*/
274
private static void printLocale() {
275
Locale locale = Locale.getDefault();
276
ostream.println(LOCALE_SETTINGS);
277
ostream.println(INDENT + "default locale = " +
278
locale.getDisplayName());
279
ostream.println(INDENT + "default display locale = " +
280
Locale.getDefault(Category.DISPLAY).getDisplayName());
281
ostream.println(INDENT + "default format locale = " +
282
Locale.getDefault(Category.FORMAT).getDisplayName());
283
printLocales();
284
ostream.println();
285
}
286
287
private static void printLocales() {
288
Locale[] tlocales = Locale.getAvailableLocales();
289
final int len = tlocales == null ? 0 : tlocales.length;
290
if (len < 1 ) {
291
return;
292
}
293
// Locale does not implement Comparable so we convert it to String
294
// and sort it for pretty printing.
295
Set<String> sortedSet = new TreeSet<>();
296
for (Locale l : tlocales) {
297
sortedSet.add(l.toString());
298
}
299
300
ostream.print(INDENT + "available locales = ");
301
Iterator<String> iter = sortedSet.iterator();
302
final int last = len - 1;
303
for (int i = 0 ; iter.hasNext() ; i++) {
304
String s = iter.next();
305
ostream.print(s);
306
if (i != last) {
307
ostream.print(", ");
308
}
309
// print columns of 8
310
if ((i + 1) % 8 == 0) {
311
ostream.println();
312
ostream.print(INDENT + INDENT);
313
}
314
}
315
}
316
317
public static void printSystemMetrics() {
318
Metrics c = Container.metrics();
319
320
ostream.println("Operating System Metrics:");
321
322
if (c == null) {
323
ostream.println(INDENT + "No metrics available for this platform");
324
return;
325
}
326
327
final long longRetvalNotSupported = -2;
328
329
ostream.println(INDENT + "Provider: " + c.getProvider());
330
ostream.println(INDENT + "Effective CPU Count: " + c.getEffectiveCpuCount());
331
ostream.println(formatCpuVal(c.getCpuPeriod(), INDENT + "CPU Period: ", longRetvalNotSupported));
332
ostream.println(formatCpuVal(c.getCpuQuota(), INDENT + "CPU Quota: ", longRetvalNotSupported));
333
ostream.println(formatCpuVal(c.getCpuShares(), INDENT + "CPU Shares: ", longRetvalNotSupported));
334
335
int cpus[] = c.getCpuSetCpus();
336
if (cpus != null) {
337
ostream.println(INDENT + "List of Processors, "
338
+ cpus.length + " total: ");
339
340
ostream.print(INDENT);
341
for (int i = 0; i < cpus.length; i++) {
342
ostream.print(cpus[i] + " ");
343
}
344
if (cpus.length > 0) {
345
ostream.println("");
346
}
347
} else {
348
ostream.println(INDENT + "List of Processors: N/A");
349
}
350
351
cpus = c.getEffectiveCpuSetCpus();
352
if (cpus != null) {
353
ostream.println(INDENT + "List of Effective Processors, "
354
+ cpus.length + " total: ");
355
356
ostream.print(INDENT);
357
for (int i = 0; i < cpus.length; i++) {
358
ostream.print(cpus[i] + " ");
359
}
360
if (cpus.length > 0) {
361
ostream.println("");
362
}
363
} else {
364
ostream.println(INDENT + "List of Effective Processors: N/A");
365
}
366
367
int mems[] = c.getCpuSetMems();
368
if (mems != null) {
369
ostream.println(INDENT + "List of Memory Nodes, "
370
+ mems.length + " total: ");
371
372
ostream.print(INDENT);
373
for (int i = 0; i < mems.length; i++) {
374
ostream.print(mems[i] + " ");
375
}
376
if (mems.length > 0) {
377
ostream.println("");
378
}
379
} else {
380
ostream.println(INDENT + "List of Memory Nodes: N/A");
381
}
382
383
mems = c.getEffectiveCpuSetMems();
384
if (mems != null) {
385
ostream.println(INDENT + "List of Available Memory Nodes, "
386
+ mems.length + " total: ");
387
388
ostream.print(INDENT);
389
for (int i = 0; i < mems.length; i++) {
390
ostream.print(mems[i] + " ");
391
}
392
if (mems.length > 0) {
393
ostream.println("");
394
}
395
} else {
396
ostream.println(INDENT + "List of Available Memory Nodes: N/A");
397
}
398
399
long limit = c.getMemoryLimit();
400
ostream.println(formatLimitString(limit, INDENT + "Memory Limit: ", longRetvalNotSupported));
401
402
limit = c.getMemorySoftLimit();
403
ostream.println(formatLimitString(limit, INDENT + "Memory Soft Limit: ", longRetvalNotSupported));
404
405
limit = c.getMemoryAndSwapLimit();
406
ostream.println(formatLimitString(limit, INDENT + "Memory & Swap Limit: ", longRetvalNotSupported));
407
408
ostream.println("");
409
}
410
411
private static String formatLimitString(long limit, String prefix, long unavailable) {
412
if (limit >= 0) {
413
return prefix + SizePrefix.scaleValue(limit);
414
} else if (limit == unavailable) {
415
return prefix + "N/A";
416
} else {
417
return prefix + "Unlimited";
418
}
419
}
420
421
private static String formatCpuVal(long cpuVal, String prefix, long unavailable) {
422
if (cpuVal >= 0) {
423
return prefix + cpuVal + "us";
424
} else if (cpuVal == unavailable) {
425
return prefix + "N/A";
426
} else {
427
return prefix + cpuVal;
428
}
429
}
430
431
private enum SizePrefix {
432
433
KILO(1024, "K"),
434
MEGA(1024 * 1024, "M"),
435
GIGA(1024 * 1024 * 1024, "G"),
436
TERA(1024L * 1024L * 1024L * 1024L, "T");
437
long size;
438
String abbrev;
439
440
SizePrefix(long size, String abbrev) {
441
this.size = size;
442
this.abbrev = abbrev;
443
}
444
445
private static String scale(long v, SizePrefix prefix) {
446
return BigDecimal.valueOf(v).divide(BigDecimal.valueOf(prefix.size),
447
2, RoundingMode.HALF_EVEN).toPlainString() + prefix.abbrev;
448
}
449
/*
450
* scale the incoming values to a human readable form, represented as
451
* K, M, G and T, see java.c parse_size for the scaled values and
452
* suffixes. The lowest possible scaled value is Kilo.
453
*/
454
static String scaleValue(long v) {
455
if (v < MEGA.size) {
456
return scale(v, KILO);
457
} else if (v < GIGA.size) {
458
return scale(v, MEGA);
459
} else if (v < TERA.size) {
460
return scale(v, GIGA);
461
} else {
462
return scale(v, TERA);
463
}
464
}
465
}
466
467
/**
468
* A private helper method to get a localized message and also
469
* apply any arguments that we might pass.
470
*/
471
private static String getLocalizedMessage(String key, Object... args) {
472
String msg = ResourceBundleHolder.RB.getString(key);
473
return (args != null) ? MessageFormat.format(msg, args) : msg;
474
}
475
476
/**
477
* The java -help message is split into 3 parts, an invariant, followed
478
* by a set of platform dependent variant messages, finally an invariant
479
* set of lines.
480
* This method initializes the help message for the first time, and also
481
* assembles the invariant header part of the message.
482
*/
483
static void initHelpMessage(String progname) {
484
outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.header",
485
(progname == null) ? "java" : progname ));
486
}
487
488
/**
489
* Appends the vm selection messages to the header, already created.
490
* initHelpSystem must already be called.
491
*/
492
static void appendVmSelectMessage(String vm1, String vm2) {
493
outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.vmselect",
494
vm1, vm2));
495
}
496
497
/**
498
* Appends the vm synoym message to the header, already created.
499
* initHelpSystem must be called before using this method.
500
*/
501
static void appendVmSynonymMessage(String vm1, String vm2) {
502
outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.hotspot",
503
vm1, vm2));
504
}
505
506
/**
507
* Appends the last invariant part to the previously created messages,
508
* and finishes up the printing to the desired output stream.
509
* initHelpSystem must be called before using this method.
510
*/
511
static void printHelpMessage(boolean printToStderr) {
512
initOutput(printToStderr);
513
outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.footer",
514
File.pathSeparator));
515
ostream.println(outBuf.toString());
516
}
517
518
/**
519
* Prints the Xusage text to the desired output stream.
520
*/
521
static void printXUsageMessage(boolean printToStderr) {
522
initOutput(printToStderr);
523
ostream.println(getLocalizedMessage("java.launcher.X.usage",
524
File.pathSeparator));
525
if (System.getProperty("os.name").contains("OS X")) {
526
ostream.println(getLocalizedMessage("java.launcher.X.macosx.usage",
527
File.pathSeparator));
528
}
529
}
530
531
static void initOutput(boolean printToStderr) {
532
ostream = (printToStderr) ? System.err : System.out;
533
}
534
535
static void initOutput(PrintStream ps) {
536
ostream = ps;
537
}
538
539
static String getMainClassFromJar(String jarname) {
540
String mainValue;
541
try (JarFile jarFile = new JarFile(jarname)) {
542
Manifest manifest = jarFile.getManifest();
543
if (manifest == null) {
544
abort(null, "java.launcher.jar.error2", jarname);
545
}
546
Attributes mainAttrs = manifest.getMainAttributes();
547
if (mainAttrs == null) {
548
abort(null, "java.launcher.jar.error3", jarname);
549
}
550
551
// Main-Class
552
mainValue = mainAttrs.getValue(MAIN_CLASS);
553
if (mainValue == null) {
554
abort(null, "java.launcher.jar.error3", jarname);
555
}
556
557
// Launcher-Agent-Class (only check for this when Main-Class present)
558
String agentClass = mainAttrs.getValue(LAUNCHER_AGENT_CLASS);
559
if (agentClass != null) {
560
ModuleLayer.boot().findModule("java.instrument").ifPresent(m -> {
561
try {
562
String cn = "sun.instrument.InstrumentationImpl";
563
Class<?> clazz = Class.forName(cn, false, null);
564
Method loadAgent = clazz.getMethod("loadAgent", String.class);
565
loadAgent.invoke(null, jarname);
566
} catch (Throwable e) {
567
if (e instanceof InvocationTargetException) e = e.getCause();
568
abort(e, "java.launcher.jar.error4", jarname);
569
}
570
});
571
}
572
573
// Add-Exports and Add-Opens
574
String exports = mainAttrs.getValue(ADD_EXPORTS);
575
if (exports != null) {
576
addExportsOrOpens(exports, false);
577
}
578
String opens = mainAttrs.getValue(ADD_OPENS);
579
if (opens != null) {
580
addExportsOrOpens(opens, true);
581
}
582
583
/*
584
* Hand off to FXHelper if it detects a JavaFX application
585
* This must be done after ensuring a Main-Class entry
586
* exists to enforce compliance with the jar specification
587
*/
588
if (mainAttrs.containsKey(
589
new Attributes.Name(JAVAFX_APPLICATION_MARKER))) {
590
FXHelper.setFXLaunchParameters(jarname, LM_JAR);
591
return FXHelper.class.getName();
592
}
593
594
return mainValue.trim();
595
} catch (IOException ioe) {
596
abort(ioe, "java.launcher.jar.error1", jarname);
597
}
598
return null;
599
}
600
601
/**
602
* Process the Add-Exports or Add-Opens value. The value is
603
* {@code <module>/<package> ( <module>/<package>)*}.
604
*/
605
static void addExportsOrOpens(String value, boolean open) {
606
for (String moduleAndPackage : value.split(" ")) {
607
String[] s = moduleAndPackage.trim().split("/");
608
if (s.length == 2) {
609
String mn = s[0];
610
String pn = s[1];
611
ModuleLayer.boot()
612
.findModule(mn)
613
.filter(m -> m.getDescriptor().packages().contains(pn))
614
.ifPresent(m -> {
615
if (open) {
616
Modules.addOpensToAllUnnamed(m, pn);
617
} else {
618
Modules.addExportsToAllUnnamed(m, pn);
619
}
620
});
621
}
622
}
623
}
624
625
// From src/share/bin/java.c:
626
// enum LaunchMode { LM_UNKNOWN = 0, LM_CLASS, LM_JAR, LM_MODULE, LM_SOURCE }
627
628
private static final int LM_UNKNOWN = 0;
629
private static final int LM_CLASS = 1;
630
private static final int LM_JAR = 2;
631
private static final int LM_MODULE = 3;
632
private static final int LM_SOURCE = 4;
633
634
static void abort(Throwable t, String msgKey, Object... args) {
635
if (msgKey != null) {
636
ostream.println(getLocalizedMessage(msgKey, args));
637
}
638
if (trace) {
639
if (t != null) {
640
t.printStackTrace();
641
} else {
642
Thread.dumpStack();
643
}
644
}
645
System.exit(1);
646
}
647
648
/**
649
* This method:
650
* 1. Loads the main class from the module or class path
651
* 2. Checks the public static void main method.
652
* 3. If the main class extends FX Application then call on FXHelper to
653
* perform the launch.
654
*
655
* @param printToStderr if set, all output will be routed to stderr
656
* @param mode LaunchMode as determined by the arguments passed on the
657
* command line
658
* @param what the module name[/class], JAR file, or the main class
659
* depending on the mode
660
*
661
* @return the application's main class
662
*/
663
@SuppressWarnings("fallthrough")
664
public static Class<?> checkAndLoadMain(boolean printToStderr,
665
int mode,
666
String what) {
667
initOutput(printToStderr);
668
669
Class<?> mainClass = null;
670
switch (mode) {
671
case LM_MODULE: case LM_SOURCE:
672
mainClass = loadModuleMainClass(what);
673
break;
674
default:
675
mainClass = loadMainClass(mode, what);
676
break;
677
}
678
679
// record the real main class for UI purposes
680
// neither method above can return null, they will abort()
681
appClass = mainClass;
682
683
/*
684
* Check if FXHelper can launch it using the FX launcher. In an FX app,
685
* the main class may or may not have a main method, so do this before
686
* validating the main class.
687
*/
688
if (JAVAFX_FXHELPER_CLASS_NAME_SUFFIX.equals(mainClass.getName()) ||
689
doesExtendFXApplication(mainClass)) {
690
// Will abort() if there are problems with FX runtime
691
FXHelper.setFXLaunchParameters(what, mode);
692
mainClass = FXHelper.class;
693
}
694
695
validateMainClass(mainClass);
696
return mainClass;
697
}
698
699
/**
700
* Returns the main class for a module. The query is either a module name
701
* or module-name/main-class. For the former then the module's main class
702
* is obtained from the module descriptor (MainClass attribute).
703
*/
704
private static Class<?> loadModuleMainClass(String what) {
705
int i = what.indexOf('/');
706
String mainModule;
707
String mainClass;
708
if (i == -1) {
709
mainModule = what;
710
mainClass = null;
711
} else {
712
mainModule = what.substring(0, i);
713
mainClass = what.substring(i+1);
714
}
715
716
// main module is in the boot layer
717
ModuleLayer layer = ModuleLayer.boot();
718
Optional<Module> om = layer.findModule(mainModule);
719
if (!om.isPresent()) {
720
// should not happen
721
throw new InternalError("Module " + mainModule + " not in boot Layer");
722
}
723
Module m = om.get();
724
725
// get main class
726
if (mainClass == null) {
727
Optional<String> omc = m.getDescriptor().mainClass();
728
if (!omc.isPresent()) {
729
abort(null, "java.launcher.module.error1", mainModule);
730
}
731
mainClass = omc.get();
732
}
733
734
// load the class from the module
735
Class<?> c = null;
736
try {
737
c = Class.forName(m, mainClass);
738
if (c == null && System.getProperty("os.name", "").contains("OS X")
739
&& Normalizer.isNormalized(mainClass, Normalizer.Form.NFD)) {
740
741
String cn = Normalizer.normalize(mainClass, Normalizer.Form.NFC);
742
c = Class.forName(m, cn);
743
}
744
} catch (LinkageError le) {
745
abort(null, "java.launcher.module.error3", mainClass, m.getName(),
746
le.getClass().getName() + ": " + le.getLocalizedMessage());
747
}
748
if (c == null) {
749
abort(null, "java.launcher.module.error2", mainClass, mainModule);
750
}
751
752
System.setProperty("jdk.module.main.class", c.getName());
753
return c;
754
}
755
756
/**
757
* Loads the main class from the class path (LM_CLASS or LM_JAR).
758
*/
759
private static Class<?> loadMainClass(int mode, String what) {
760
// get the class name
761
String cn;
762
switch (mode) {
763
case LM_CLASS:
764
cn = what;
765
break;
766
case LM_JAR:
767
cn = getMainClassFromJar(what);
768
break;
769
default:
770
// should never happen
771
throw new InternalError("" + mode + ": Unknown launch mode");
772
}
773
774
// load the main class
775
cn = cn.replace('/', '.');
776
Class<?> mainClass = null;
777
ClassLoader scl = ClassLoader.getSystemClassLoader();
778
try {
779
try {
780
mainClass = Class.forName(cn, false, scl);
781
} catch (NoClassDefFoundError | ClassNotFoundException cnfe) {
782
if (System.getProperty("os.name", "").contains("OS X")
783
&& Normalizer.isNormalized(cn, Normalizer.Form.NFD)) {
784
try {
785
// On Mac OS X since all names with diacritical marks are
786
// given as decomposed it is possible that main class name
787
// comes incorrectly from the command line and we have
788
// to re-compose it
789
String ncn = Normalizer.normalize(cn, Normalizer.Form.NFC);
790
mainClass = Class.forName(ncn, false, scl);
791
} catch (NoClassDefFoundError | ClassNotFoundException cnfe1) {
792
abort(cnfe1, "java.launcher.cls.error1", cn,
793
cnfe1.getClass().getCanonicalName(), cnfe1.getMessage());
794
}
795
} else {
796
abort(cnfe, "java.launcher.cls.error1", cn,
797
cnfe.getClass().getCanonicalName(), cnfe.getMessage());
798
}
799
}
800
} catch (LinkageError le) {
801
abort(le, "java.launcher.cls.error6", cn,
802
le.getClass().getName() + ": " + le.getLocalizedMessage());
803
}
804
return mainClass;
805
}
806
807
/*
808
* Accessor method called by the launcher after getting the main class via
809
* checkAndLoadMain(). The "application class" is the class that is finally
810
* executed to start the application and in this case is used to report
811
* the correct application name, typically for UI purposes.
812
*/
813
public static Class<?> getApplicationClass() {
814
return appClass;
815
}
816
817
/*
818
* Check if the given class is a JavaFX Application class. This is done
819
* in a way that does not cause the Application class to load or throw
820
* ClassNotFoundException if the JavaFX runtime is not available.
821
*/
822
private static boolean doesExtendFXApplication(Class<?> mainClass) {
823
for (Class<?> sc = mainClass.getSuperclass(); sc != null;
824
sc = sc.getSuperclass()) {
825
if (sc.getName().equals(JAVAFX_APPLICATION_CLASS_NAME)) {
826
return true;
827
}
828
}
829
return false;
830
}
831
832
// Check the existence and signature of main and abort if incorrect
833
static void validateMainClass(Class<?> mainClass) {
834
Method mainMethod = null;
835
try {
836
mainMethod = mainClass.getMethod("main", String[].class);
837
} catch (NoSuchMethodException nsme) {
838
// invalid main or not FX application, abort with an error
839
abort(null, "java.launcher.cls.error4", mainClass.getName(),
840
JAVAFX_APPLICATION_CLASS_NAME);
841
} catch (Throwable e) {
842
if (mainClass.getModule().isNamed()) {
843
abort(e, "java.launcher.module.error5",
844
mainClass.getName(), mainClass.getModule().getName(),
845
e.getClass().getName(), e.getLocalizedMessage());
846
} else {
847
abort(e, "java.launcher.cls.error7", mainClass.getName(),
848
e.getClass().getName(), e.getLocalizedMessage());
849
}
850
}
851
852
/*
853
* getMethod (above) will choose the correct method, based
854
* on its name and parameter type, however, we still have to
855
* ensure that the method is static and returns a void.
856
*/
857
int mod = mainMethod.getModifiers();
858
if (!Modifier.isStatic(mod)) {
859
abort(null, "java.launcher.cls.error2", "static",
860
mainMethod.getDeclaringClass().getName());
861
}
862
if (mainMethod.getReturnType() != java.lang.Void.TYPE) {
863
abort(null, "java.launcher.cls.error3",
864
mainMethod.getDeclaringClass().getName());
865
}
866
}
867
868
private static final String encprop = "sun.jnu.encoding";
869
private static String encoding = null;
870
private static boolean isCharsetSupported = false;
871
872
/*
873
* converts a c or a byte array to a platform specific string,
874
* previously implemented as a native method in the launcher.
875
*/
876
static String makePlatformString(boolean printToStderr, byte[] inArray) {
877
initOutput(printToStderr);
878
if (encoding == null) {
879
encoding = System.getProperty(encprop);
880
isCharsetSupported = Charset.isSupported(encoding);
881
}
882
try {
883
String out = isCharsetSupported
884
? new String(inArray, encoding)
885
: new String(inArray);
886
return out;
887
} catch (UnsupportedEncodingException uee) {
888
abort(uee, null);
889
}
890
return null; // keep the compiler happy
891
}
892
893
static String[] expandArgs(String[] argArray) {
894
List<StdArg> aList = new ArrayList<>();
895
for (String x : argArray) {
896
aList.add(new StdArg(x));
897
}
898
return expandArgs(aList);
899
}
900
901
static String[] expandArgs(List<StdArg> argList) {
902
ArrayList<String> out = new ArrayList<>();
903
if (trace) {
904
System.err.println("Incoming arguments:");
905
}
906
for (StdArg a : argList) {
907
if (trace) {
908
System.err.println(a);
909
}
910
if (a.needsExpansion) {
911
File x = new File(a.arg);
912
File parent = x.getParentFile();
913
String glob = x.getName();
914
if (parent == null) {
915
parent = new File(".");
916
}
917
try (DirectoryStream<Path> dstream =
918
Files.newDirectoryStream(parent.toPath(), glob)) {
919
int entries = 0;
920
for (Path p : dstream) {
921
out.add(p.normalize().toString());
922
entries++;
923
}
924
if (entries == 0) {
925
out.add(a.arg);
926
}
927
} catch (Exception e) {
928
out.add(a.arg);
929
if (trace) {
930
System.err.println("Warning: passing argument as-is " + a);
931
System.err.print(e);
932
}
933
}
934
} else {
935
out.add(a.arg);
936
}
937
}
938
String[] oarray = new String[out.size()];
939
out.toArray(oarray);
940
941
if (trace) {
942
System.err.println("Expanded arguments:");
943
for (String x : oarray) {
944
System.err.println(x);
945
}
946
}
947
return oarray;
948
}
949
950
/* duplicate of the native StdArg struct */
951
private static class StdArg {
952
final String arg;
953
final boolean needsExpansion;
954
StdArg(String arg, boolean expand) {
955
this.arg = arg;
956
this.needsExpansion = expand;
957
}
958
// protocol: first char indicates whether expansion is required
959
// 'T' = true ; needs expansion
960
// 'F' = false; needs no expansion
961
StdArg(String in) {
962
this.arg = in.substring(1);
963
needsExpansion = in.charAt(0) == 'T';
964
}
965
public String toString() {
966
return "StdArg{" + "arg=" + arg + ", needsExpansion=" + needsExpansion + '}';
967
}
968
}
969
970
static final class FXHelper {
971
972
private static final String JAVAFX_GRAPHICS_MODULE_NAME =
973
"javafx.graphics";
974
975
private static final String JAVAFX_LAUNCHER_CLASS_NAME =
976
"com.sun.javafx.application.LauncherImpl";
977
978
/*
979
* The launch method used to invoke the JavaFX launcher. These must
980
* match the strings used in the launchApplication method.
981
*
982
* Command line JavaFX-App-Class Launch mode FX Launch mode
983
* java -cp fxapp.jar FXClass N/A LM_CLASS "LM_CLASS"
984
* java -cp somedir FXClass N/A LM_CLASS "LM_CLASS"
985
* java -jar fxapp.jar Present LM_JAR "LM_JAR"
986
* java -jar fxapp.jar Not Present LM_JAR "LM_JAR"
987
* java -m module/class [1] N/A LM_MODULE "LM_MODULE"
988
* java -m module N/A LM_MODULE "LM_MODULE"
989
*
990
* [1] - JavaFX-Application-Class is ignored when modular args are used, even
991
* if present in a modular jar
992
*/
993
private static final String JAVAFX_LAUNCH_MODE_CLASS = "LM_CLASS";
994
private static final String JAVAFX_LAUNCH_MODE_JAR = "LM_JAR";
995
private static final String JAVAFX_LAUNCH_MODE_MODULE = "LM_MODULE";
996
997
/*
998
* FX application launcher and launch method, so we can launch
999
* applications with no main method.
1000
*/
1001
private static String fxLaunchName = null;
1002
private static String fxLaunchMode = null;
1003
1004
private static Class<?> fxLauncherClass = null;
1005
private static Method fxLauncherMethod = null;
1006
1007
/*
1008
* Set the launch params according to what was passed to LauncherHelper
1009
* so we can use the same launch mode for FX. Abort if there is any
1010
* issue with loading the FX runtime or with the launcher method.
1011
*/
1012
private static void setFXLaunchParameters(String what, int mode) {
1013
1014
// find the module with the FX launcher
1015
Optional<Module> om = ModuleLayer.boot().findModule(JAVAFX_GRAPHICS_MODULE_NAME);
1016
if (!om.isPresent()) {
1017
abort(null, "java.launcher.cls.error5");
1018
}
1019
1020
// load the FX launcher class
1021
fxLauncherClass = Class.forName(om.get(), JAVAFX_LAUNCHER_CLASS_NAME);
1022
if (fxLauncherClass == null) {
1023
abort(null, "java.launcher.cls.error5");
1024
}
1025
1026
try {
1027
/*
1028
* signature must be:
1029
* public static void launchApplication(String launchName,
1030
* String launchMode, String[] args);
1031
*/
1032
fxLauncherMethod = fxLauncherClass.getMethod("launchApplication",
1033
String.class, String.class, String[].class);
1034
1035
// verify launcher signature as we do when validating the main method
1036
int mod = fxLauncherMethod.getModifiers();
1037
if (!Modifier.isStatic(mod)) {
1038
abort(null, "java.launcher.javafx.error1");
1039
}
1040
if (fxLauncherMethod.getReturnType() != java.lang.Void.TYPE) {
1041
abort(null, "java.launcher.javafx.error1");
1042
}
1043
} catch (NoSuchMethodException ex) {
1044
abort(ex, "java.launcher.cls.error5", ex);
1045
}
1046
1047
fxLaunchName = what;
1048
switch (mode) {
1049
case LM_CLASS:
1050
fxLaunchMode = JAVAFX_LAUNCH_MODE_CLASS;
1051
break;
1052
case LM_JAR:
1053
fxLaunchMode = JAVAFX_LAUNCH_MODE_JAR;
1054
break;
1055
case LM_MODULE:
1056
fxLaunchMode = JAVAFX_LAUNCH_MODE_MODULE;
1057
break;
1058
default:
1059
// should not have gotten this far...
1060
throw new InternalError(mode + ": Unknown launch mode");
1061
}
1062
}
1063
1064
public static void main(String... args) throws Exception {
1065
if (fxLauncherMethod == null
1066
|| fxLaunchMode == null
1067
|| fxLaunchName == null) {
1068
throw new RuntimeException("Invalid JavaFX launch parameters");
1069
}
1070
// launch appClass via fxLauncherMethod
1071
fxLauncherMethod.invoke(null,
1072
new Object[] {fxLaunchName, fxLaunchMode, args});
1073
}
1074
}
1075
1076
/**
1077
* Called by the launcher to list the observable modules.
1078
*/
1079
static void listModules() {
1080
initOutput(System.out);
1081
1082
ModuleBootstrap.limitedFinder().findAll().stream()
1083
.sorted(new JrtFirstComparator())
1084
.forEach(LauncherHelper::showModule);
1085
}
1086
1087
/**
1088
* Called by the launcher to show the resolved modules
1089
*/
1090
static void showResolvedModules() {
1091
initOutput(System.out);
1092
1093
ModuleLayer bootLayer = ModuleLayer.boot();
1094
Configuration cf = bootLayer.configuration();
1095
1096
cf.modules().stream()
1097
.map(ResolvedModule::reference)
1098
.sorted(new JrtFirstComparator())
1099
.forEach(LauncherHelper::showModule);
1100
}
1101
1102
/**
1103
* Called by the launcher to describe a module
1104
*/
1105
static void describeModule(String moduleName) {
1106
initOutput(System.out);
1107
1108
ModuleFinder finder = ModuleBootstrap.limitedFinder();
1109
ModuleReference mref = finder.find(moduleName).orElse(null);
1110
if (mref == null) {
1111
abort(null, "java.launcher.module.error4", moduleName);
1112
}
1113
ModuleDescriptor md = mref.descriptor();
1114
1115
// one-line summary
1116
showModule(mref);
1117
1118
// unqualified exports (sorted by package)
1119
md.exports().stream()
1120
.filter(e -> !e.isQualified())
1121
.sorted(Comparator.comparing(Exports::source))
1122
.map(e -> Stream.concat(Stream.of(e.source()),
1123
toStringStream(e.modifiers()))
1124
.collect(Collectors.joining(" ")))
1125
.forEach(sourceAndMods -> ostream.format("exports %s%n", sourceAndMods));
1126
1127
// dependences
1128
for (Requires r : md.requires()) {
1129
String nameAndMods = Stream.concat(Stream.of(r.name()),
1130
toStringStream(r.modifiers()))
1131
.collect(Collectors.joining(" "));
1132
ostream.format("requires %s", nameAndMods);
1133
finder.find(r.name())
1134
.map(ModuleReference::descriptor)
1135
.filter(ModuleDescriptor::isAutomatic)
1136
.ifPresent(any -> ostream.print(" automatic"));
1137
ostream.println();
1138
}
1139
1140
// service use and provides
1141
for (String s : md.uses()) {
1142
ostream.format("uses %s%n", s);
1143
}
1144
for (Provides ps : md.provides()) {
1145
String names = ps.providers().stream().collect(Collectors.joining(" "));
1146
ostream.format("provides %s with %s%n", ps.service(), names);
1147
1148
}
1149
1150
// qualified exports
1151
for (Exports e : md.exports()) {
1152
if (e.isQualified()) {
1153
String who = e.targets().stream().collect(Collectors.joining(" "));
1154
ostream.format("qualified exports %s to %s%n", e.source(), who);
1155
}
1156
}
1157
1158
// open packages
1159
for (Opens opens: md.opens()) {
1160
if (opens.isQualified())
1161
ostream.print("qualified ");
1162
String sourceAndMods = Stream.concat(Stream.of(opens.source()),
1163
toStringStream(opens.modifiers()))
1164
.collect(Collectors.joining(" "));
1165
ostream.format("opens %s", sourceAndMods);
1166
if (opens.isQualified()) {
1167
String who = opens.targets().stream().collect(Collectors.joining(" "));
1168
ostream.format(" to %s", who);
1169
}
1170
ostream.println();
1171
}
1172
1173
// non-exported/non-open packages
1174
Set<String> concealed = new TreeSet<>(md.packages());
1175
md.exports().stream().map(Exports::source).forEach(concealed::remove);
1176
md.opens().stream().map(Opens::source).forEach(concealed::remove);
1177
concealed.forEach(p -> ostream.format("contains %s%n", p));
1178
}
1179
1180
/**
1181
* Prints a single line with the module name, version and modifiers
1182
*/
1183
private static void showModule(ModuleReference mref) {
1184
ModuleDescriptor md = mref.descriptor();
1185
ostream.print(md.toNameAndVersion());
1186
mref.location()
1187
.filter(uri -> !isJrt(uri))
1188
.ifPresent(uri -> ostream.format(" %s", uri));
1189
if (md.isOpen())
1190
ostream.print(" open");
1191
if (md.isAutomatic())
1192
ostream.print(" automatic");
1193
ostream.println();
1194
}
1195
1196
/**
1197
* A ModuleReference comparator that considers modules in the run-time
1198
* image to be less than modules than not in the run-time image.
1199
*/
1200
private static class JrtFirstComparator implements Comparator<ModuleReference> {
1201
private final Comparator<ModuleReference> real;
1202
1203
JrtFirstComparator() {
1204
this.real = Comparator.comparing(ModuleReference::descriptor);
1205
}
1206
1207
@Override
1208
public int compare(ModuleReference a, ModuleReference b) {
1209
if (isJrt(a)) {
1210
return isJrt(b) ? real.compare(a, b) : -1;
1211
} else {
1212
return isJrt(b) ? 1 : real.compare(a, b);
1213
}
1214
}
1215
}
1216
1217
private static <T> Stream<String> toStringStream(Set<T> s) {
1218
return s.stream().map(e -> e.toString().toLowerCase());
1219
}
1220
1221
private static boolean isJrt(ModuleReference mref) {
1222
return isJrt(mref.location().orElse(null));
1223
}
1224
1225
private static boolean isJrt(URI uri) {
1226
return (uri != null && uri.getScheme().equalsIgnoreCase("jrt"));
1227
}
1228
1229
}
1230
1231