8156497: Add jar tool support for Multi-Release Modular JARs
Reviewed-by: alanb
This commit is contained in:
parent
d1d3cba9db
commit
cbd92ed3f5
@ -38,6 +38,7 @@ import java.lang.module.ModuleReference;
|
||||
import java.lang.module.ResolutionException;
|
||||
import java.lang.module.ResolvedModule;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
@ -62,6 +63,7 @@ import jdk.internal.util.jar.JarIndex;
|
||||
import static jdk.internal.util.jar.JarIndex.INDEX_NAME;
|
||||
import static java.util.jar.JarFile.MANIFEST_NAME;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||
|
||||
/**
|
||||
@ -129,10 +131,8 @@ class Main {
|
||||
|
||||
private static final String MODULE_INFO = "module-info.class";
|
||||
|
||||
Path moduleInfo;
|
||||
private boolean isModularJar() { return moduleInfo != null; }
|
||||
|
||||
static final String MANIFEST_DIR = "META-INF/";
|
||||
static final String VERSIONS_DIR = MANIFEST_DIR + "versions/";
|
||||
static final String VERSION = "1.0";
|
||||
|
||||
private static ResourceBundle rsrc;
|
||||
@ -242,12 +242,27 @@ class Main {
|
||||
addMainClass(manifest, ename);
|
||||
}
|
||||
}
|
||||
expand(null, files, false);
|
||||
Map<String,Path> moduleInfoPaths = new HashMap<>();
|
||||
expand(null, files, false, moduleInfoPaths);
|
||||
|
||||
Map<String,byte[]> moduleInfos = new LinkedHashMap<>();
|
||||
if (!moduleInfoPaths.isEmpty()) {
|
||||
if (!checkModuleInfos(moduleInfoPaths))
|
||||
return false;
|
||||
|
||||
// root module-info first
|
||||
byte[] b = readModuleInfo(moduleInfoPaths.get(MODULE_INFO));
|
||||
moduleInfos.put(MODULE_INFO, b);
|
||||
for (Map.Entry<String,Path> e : moduleInfoPaths.entrySet())
|
||||
moduleInfos.putIfAbsent(e.getKey(), readModuleInfo(e.getValue()));
|
||||
|
||||
if (!addExtendedModuleAttributes(moduleInfos))
|
||||
return false;
|
||||
|
||||
// Basic consistency checks for modular jars.
|
||||
if (!checkServices(moduleInfos.get(MODULE_INFO)))
|
||||
return false;
|
||||
|
||||
byte[] moduleInfoBytes = null;
|
||||
if (isModularJar()) {
|
||||
moduleInfoBytes = addExtendedModuleAttributes(
|
||||
readModuleInfo(moduleInfo));
|
||||
} else if (moduleVersion != null || modulesToHash != null) {
|
||||
error(getMsg("error.module.options.without.info"));
|
||||
return false;
|
||||
@ -274,13 +289,7 @@ class Main {
|
||||
tmpfile = createTemporaryFile(tmpbase, ".jar");
|
||||
out = new FileOutputStream(tmpfile);
|
||||
}
|
||||
create(new BufferedOutputStream(out, 4096), manifest, moduleInfoBytes);
|
||||
|
||||
// Consistency checks for modular jars.
|
||||
if (isModularJar()) {
|
||||
if (!checkServices(moduleInfoBytes))
|
||||
return false;
|
||||
}
|
||||
create(new BufferedOutputStream(out, 4096), manifest, moduleInfos);
|
||||
|
||||
if (in != null) {
|
||||
in.close();
|
||||
@ -337,19 +346,20 @@ class Main {
|
||||
}
|
||||
InputStream manifest = (!Mflag && (mname != null)) ?
|
||||
(new FileInputStream(mname)) : null;
|
||||
expand(null, files, true);
|
||||
|
||||
byte[] moduleInfoBytes = null;
|
||||
if (isModularJar()) {
|
||||
moduleInfoBytes = readModuleInfo(moduleInfo);
|
||||
}
|
||||
Map<String,Path> moduleInfoPaths = new HashMap<>();
|
||||
expand(null, files, true, moduleInfoPaths);
|
||||
|
||||
Map<String,byte[]> moduleInfos = new HashMap<>();
|
||||
for (Map.Entry<String,Path> e : moduleInfoPaths.entrySet())
|
||||
moduleInfos.put(e.getKey(), readModuleInfo(e.getValue()));
|
||||
|
||||
boolean updateOk = update(in, new BufferedOutputStream(out),
|
||||
manifest, moduleInfoBytes, null);
|
||||
manifest, moduleInfos, null);
|
||||
|
||||
// Consistency checks for modular jars.
|
||||
if (isModularJar()) {
|
||||
if(!checkServices(moduleInfoBytes))
|
||||
if (!moduleInfos.isEmpty()) {
|
||||
if(!checkServices(moduleInfos.get(MODULE_INFO)))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -638,7 +648,12 @@ class Main {
|
||||
* Expands list of files to process into full list of all files that
|
||||
* can be found by recursively descending directories.
|
||||
*/
|
||||
void expand(File dir, String[] files, boolean isUpdate) throws IOException {
|
||||
void expand(File dir,
|
||||
String[] files,
|
||||
boolean isUpdate,
|
||||
Map<String,Path> moduleInfoPaths)
|
||||
throws IOException
|
||||
{
|
||||
if (files == null)
|
||||
return;
|
||||
|
||||
@ -651,18 +666,17 @@ class Main {
|
||||
|
||||
if (f.isFile()) {
|
||||
String path = f.getPath();
|
||||
if (entryName(path).equals(MODULE_INFO)) {
|
||||
if (moduleInfo != null && vflag)
|
||||
output(formatMsg("error.unexpected.module-info", path));
|
||||
moduleInfo = f.toPath();
|
||||
String entryName = entryName(path);
|
||||
if (entryName.endsWith(MODULE_INFO)) {
|
||||
moduleInfoPaths.put(entryName, f.toPath());
|
||||
if (isUpdate)
|
||||
entryMap.put(entryName(path), f);
|
||||
entryMap.put(entryName, f);
|
||||
} else if (entries.add(f)) {
|
||||
jarEntries.add(entryName(path));
|
||||
if (path.endsWith(".class"))
|
||||
packages.add(toPackageName(entryName(path)));
|
||||
jarEntries.add(entryName);
|
||||
if (path.endsWith(".class") && !entryName.startsWith(VERSIONS_DIR))
|
||||
packages.add(toPackageName(entryName));
|
||||
if (isUpdate)
|
||||
entryMap.put(entryName(f.getPath()), f);
|
||||
entryMap.put(entryName, f);
|
||||
}
|
||||
} else if (f.isDirectory()) {
|
||||
if (entries.add(f)) {
|
||||
@ -672,7 +686,7 @@ class Main {
|
||||
(dirPath + File.separator);
|
||||
entryMap.put(entryName(dirPath), f);
|
||||
}
|
||||
expand(f, f.list(), isUpdate);
|
||||
expand(f, f.list(), isUpdate, moduleInfoPaths);
|
||||
}
|
||||
} else {
|
||||
error(formatMsg("error.nosuch.fileordir", String.valueOf(f)));
|
||||
@ -684,7 +698,7 @@ class Main {
|
||||
/**
|
||||
* Creates a new JAR file.
|
||||
*/
|
||||
void create(OutputStream out, Manifest manifest, byte[] moduleInfoBytes)
|
||||
void create(OutputStream out, Manifest manifest, Map<String,byte[]> moduleInfos)
|
||||
throws IOException
|
||||
{
|
||||
ZipOutputStream zos = new JarOutputStream(out);
|
||||
@ -710,17 +724,19 @@ class Main {
|
||||
manifest.write(zos);
|
||||
zos.closeEntry();
|
||||
}
|
||||
if (moduleInfoBytes != null) {
|
||||
for (Map.Entry<String,byte[]> mi : moduleInfos.entrySet()) {
|
||||
String entryName = mi.getKey();
|
||||
byte[] miBytes = mi.getValue();
|
||||
if (vflag) {
|
||||
output(getMsg("out.added.module-info"));
|
||||
output(formatMsg("out.added.module-info", entryName));
|
||||
}
|
||||
ZipEntry e = new ZipEntry(MODULE_INFO);
|
||||
ZipEntry e = new ZipEntry(mi.getKey());
|
||||
e.setTime(System.currentTimeMillis());
|
||||
if (flag0) {
|
||||
crc32ModuleInfo(e, moduleInfoBytes);
|
||||
crc32ModuleInfo(e, miBytes);
|
||||
}
|
||||
zos.putNextEntry(e);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(moduleInfoBytes);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(miBytes);
|
||||
in.transferTo(zos);
|
||||
zos.closeEntry();
|
||||
}
|
||||
@ -754,19 +770,42 @@ class Main {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true of the given module-info's are located in acceptable
|
||||
* locations. Otherwise, outputs an appropriate message and returns false.
|
||||
*/
|
||||
private boolean checkModuleInfos(Map<String,?> moduleInfos) {
|
||||
// there must always be, at least, a root module-info
|
||||
if (!moduleInfos.containsKey(MODULE_INFO)) {
|
||||
error(getMsg("error.versioned.info.without.root"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// module-info can only appear in the root, or a versioned section
|
||||
Optional<String> other = moduleInfos.keySet().stream()
|
||||
.filter(x -> !x.equals(MODULE_INFO))
|
||||
.filter(x -> !x.startsWith(VERSIONS_DIR))
|
||||
.findFirst();
|
||||
|
||||
if (other.isPresent()) {
|
||||
error(formatMsg("error.unexpected.module-info", other.get()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing jar file.
|
||||
*/
|
||||
boolean update(InputStream in, OutputStream out,
|
||||
InputStream newManifest,
|
||||
byte[] newModuleInfoBytes,
|
||||
Map<String,byte[]> moduleInfos,
|
||||
JarIndex jarIndex) throws IOException
|
||||
{
|
||||
ZipInputStream zis = new ZipInputStream(in);
|
||||
ZipOutputStream zos = new JarOutputStream(out);
|
||||
ZipEntry e = null;
|
||||
boolean foundManifest = false;
|
||||
boolean foundModuleInfo = false;
|
||||
boolean updateOk = true;
|
||||
|
||||
if (jarIndex != null) {
|
||||
@ -778,7 +817,7 @@ class Main {
|
||||
String name = e.getName();
|
||||
|
||||
boolean isManifestEntry = equalsIgnoreCase(name, MANIFEST_NAME);
|
||||
boolean isModuleInfoEntry = name.equals(MODULE_INFO);
|
||||
boolean isModuleInfoEntry = name.endsWith(MODULE_INFO);
|
||||
|
||||
if ((jarIndex != null && equalsIgnoreCase(name, INDEX_NAME))
|
||||
|| (Mflag && isManifestEntry)) {
|
||||
@ -806,13 +845,8 @@ class Main {
|
||||
if (!updateManifest(old, zos)) {
|
||||
return false;
|
||||
}
|
||||
} else if (isModuleInfoEntry
|
||||
&& ((newModuleInfoBytes != null) || (ename != null)
|
||||
|| moduleVersion != null || modulesToHash != null)) {
|
||||
if (newModuleInfoBytes == null) {
|
||||
// Update existing module-info.class
|
||||
newModuleInfoBytes = readModuleInfo(zis);
|
||||
}
|
||||
} else if (moduleInfos != null && isModuleInfoEntry) {
|
||||
moduleInfos.putIfAbsent(name, readModuleInfo(zis));
|
||||
} else {
|
||||
if (!entryMap.containsKey(name)) { // copy the old stuff
|
||||
// do our own compression
|
||||
@ -860,13 +894,22 @@ class Main {
|
||||
}
|
||||
}
|
||||
|
||||
// write the module-info.class
|
||||
if (newModuleInfoBytes != null) {
|
||||
newModuleInfoBytes = addExtendedModuleAttributes(newModuleInfoBytes);
|
||||
if (moduleInfos != null && !moduleInfos.isEmpty()) {
|
||||
if (!checkModuleInfos(moduleInfos))
|
||||
updateOk = false;
|
||||
|
||||
if (updateOk) {
|
||||
if (!addExtendedModuleAttributes(moduleInfos))
|
||||
updateOk = false;
|
||||
}
|
||||
|
||||
// TODO: check manifest main classes, etc
|
||||
if (!updateModuleInfo(newModuleInfoBytes, zos)) {
|
||||
updateOk = false;
|
||||
|
||||
if (updateOk) {
|
||||
for (Map.Entry<String,byte[]> mi : moduleInfos.entrySet()) {
|
||||
if (!updateModuleInfo(mi.getValue(), zos, mi.getKey()))
|
||||
updateOk = false;
|
||||
}
|
||||
}
|
||||
} else if (moduleVersion != null || modulesToHash != null) {
|
||||
error(getMsg("error.module.options.without.info"));
|
||||
@ -894,10 +937,10 @@ class Main {
|
||||
zos.closeEntry();
|
||||
}
|
||||
|
||||
private boolean updateModuleInfo(byte[] moduleInfoBytes, ZipOutputStream zos)
|
||||
private boolean updateModuleInfo(byte[] moduleInfoBytes, ZipOutputStream zos, String entryName)
|
||||
throws IOException
|
||||
{
|
||||
ZipEntry e = new ZipEntry(MODULE_INFO);
|
||||
ZipEntry e = new ZipEntry(entryName);
|
||||
e.setTime(System.currentTimeMillis());
|
||||
if (flag0) {
|
||||
crc32ModuleInfo(e, moduleInfoBytes);
|
||||
@ -905,7 +948,7 @@ class Main {
|
||||
zos.putNextEntry(e);
|
||||
zos.write(moduleInfoBytes);
|
||||
if (vflag) {
|
||||
output(getMsg("out.update.module-info"));
|
||||
output(formatMsg("out.update.module-info", entryName));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1710,13 +1753,11 @@ class Main {
|
||||
return (classname.replace('.', '/')) + ".class";
|
||||
}
|
||||
|
||||
/* A module must have the implementation class of the services it 'provides'. */
|
||||
private boolean checkServices(byte[] moduleInfoBytes)
|
||||
throws IOException
|
||||
{
|
||||
ModuleDescriptor md;
|
||||
try (InputStream in = new ByteArrayInputStream(moduleInfoBytes)) {
|
||||
md = ModuleDescriptor.read(in);
|
||||
}
|
||||
ModuleDescriptor md = ModuleDescriptor.read(ByteBuffer.wrap(moduleInfoBytes));
|
||||
Set<String> missing = md.provides()
|
||||
.values()
|
||||
.stream()
|
||||
@ -1732,63 +1773,140 @@ class Main {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a byte array containing the module-info.class.
|
||||
* Adds extended modules attributes to the given module-info's. The given
|
||||
* Map values are updated in-place. Returns false if an error occurs.
|
||||
*/
|
||||
private boolean addExtendedModuleAttributes(Map<String,byte[]> moduleInfos)
|
||||
throws IOException
|
||||
{
|
||||
assert !moduleInfos.isEmpty() && moduleInfos.get(MODULE_INFO) != null;
|
||||
|
||||
ByteBuffer bb = ByteBuffer.wrap(moduleInfos.get(MODULE_INFO));
|
||||
ModuleDescriptor rd = ModuleDescriptor.read(bb);
|
||||
|
||||
Set<String> exports = rd.exports()
|
||||
.stream()
|
||||
.map(Exports::source)
|
||||
.collect(toSet());
|
||||
|
||||
Set<String> conceals = packages.stream()
|
||||
.filter(p -> !exports.contains(p))
|
||||
.collect(toSet());
|
||||
|
||||
for (Map.Entry<String,byte[]> e: moduleInfos.entrySet()) {
|
||||
ModuleDescriptor vd = ModuleDescriptor.read(ByteBuffer.wrap(e.getValue()));
|
||||
if (!(isValidVersionedDescriptor(vd, rd)))
|
||||
return false;
|
||||
e.setValue(extendedInfoBytes(rd, vd, e.getValue(), conceals));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isPlatformModule(String name) {
|
||||
return name.startsWith("java.") || name.startsWith("jdk.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether or not the given versioned module descriptor's attributes
|
||||
* are valid when compared against the given root module descriptor.
|
||||
*
|
||||
* A versioned module descriptor must be identical to the root module
|
||||
* descriptor, with two exceptions:
|
||||
* - A versioned descriptor can have different non-public `requires`
|
||||
* clauses of platform ( `java.*` and `jdk.*` ) modules, and
|
||||
* - A versioned descriptor can have different `uses` clauses, even of
|
||||
* service types defined outside of the platform modules.
|
||||
*/
|
||||
private boolean isValidVersionedDescriptor(ModuleDescriptor vd,
|
||||
ModuleDescriptor rd)
|
||||
throws IOException
|
||||
{
|
||||
if (!rd.name().equals(vd.name())) {
|
||||
fatalError(getMsg("error.versioned.info.name.notequal"));
|
||||
return false;
|
||||
}
|
||||
if (!rd.requires().equals(vd.requires())) {
|
||||
Set<Requires> rootRequires = rd.requires();
|
||||
for (Requires r : vd.requires()) {
|
||||
if (rootRequires.contains(r)) {
|
||||
continue;
|
||||
} else if (r.modifiers().contains(Requires.Modifier.PUBLIC)) {
|
||||
fatalError(getMsg("error.versioned.info.requires.public"));
|
||||
return false;
|
||||
} else if (!isPlatformModule(r.name())) {
|
||||
fatalError(getMsg("error.versioned.info.requires.added"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (Requires r : rootRequires) {
|
||||
Set<Requires> mdRequires = vd.requires();
|
||||
if (mdRequires.contains(r)) {
|
||||
continue;
|
||||
} else if (!isPlatformModule(r.name())) {
|
||||
fatalError(getMsg("error.versioned.info.requires.dropped"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!rd.exports().equals(vd.exports())) {
|
||||
fatalError(getMsg("error.versioned.info.exports.notequal"));
|
||||
return false;
|
||||
}
|
||||
if (!rd.provides().equals(vd.provides())) {
|
||||
fatalError(getMsg("error.versioned.info.provides.notequal"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a byte array containing the given module-info.class plus any
|
||||
* extended attributes.
|
||||
*
|
||||
* If --module-version, --main-class, or other options were provided
|
||||
* then the corresponding class file attributes are added to the
|
||||
* module-info here.
|
||||
*/
|
||||
private byte[] addExtendedModuleAttributes(byte[] moduleInfoBytes)
|
||||
private byte[] extendedInfoBytes(ModuleDescriptor rootDescriptor,
|
||||
ModuleDescriptor md,
|
||||
byte[] miBytes,
|
||||
Set<String> conceals)
|
||||
throws IOException
|
||||
{
|
||||
assert isModularJar();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
InputStream is = new ByteArrayInputStream(miBytes);
|
||||
ModuleInfoExtender extender = ModuleInfoExtender.newExtender(is);
|
||||
|
||||
ModuleDescriptor md;
|
||||
try (InputStream in = new ByteArrayInputStream(moduleInfoBytes)) {
|
||||
md = ModuleDescriptor.read(in);
|
||||
}
|
||||
String name = md.name();
|
||||
Set<String> exported = md.exports()
|
||||
.stream()
|
||||
.map(ModuleDescriptor.Exports::source)
|
||||
.collect(Collectors.toSet());
|
||||
// Add (or replace) the ConcealedPackages attribute
|
||||
extender.conceals(conceals);
|
||||
|
||||
// copy the module-info.class into the jmod with the additional
|
||||
// attributes for the version, main class and other meta data
|
||||
try (InputStream in = new ByteArrayInputStream(moduleInfoBytes);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||
ModuleInfoExtender extender = ModuleInfoExtender.newExtender(in);
|
||||
// --main-class
|
||||
if (ename != null)
|
||||
extender.mainClass(ename);
|
||||
else if (rootDescriptor.mainClass().isPresent())
|
||||
extender.mainClass(rootDescriptor.mainClass().get());
|
||||
|
||||
// Add (or replace) the ConcealedPackages attribute
|
||||
Set<String> conceals = packages.stream()
|
||||
.filter(p -> !exported.contains(p))
|
||||
.collect(Collectors.toSet());
|
||||
// --module-version
|
||||
if (moduleVersion != null)
|
||||
extender.version(moduleVersion);
|
||||
else if (rootDescriptor.version().isPresent())
|
||||
extender.version(rootDescriptor.version().get());
|
||||
|
||||
extender.conceals(conceals);
|
||||
|
||||
// --main-class
|
||||
if (ename != null)
|
||||
extender.mainClass(ename);
|
||||
|
||||
// --module-version
|
||||
if (moduleVersion != null)
|
||||
extender.version(moduleVersion);
|
||||
|
||||
// --hash-modules
|
||||
if (modulesToHash != null) {
|
||||
Hasher hasher = new Hasher(md, fname);
|
||||
ModuleHashes moduleHashes = hasher.computeHashes(name);
|
||||
if (moduleHashes != null) {
|
||||
extender.hashes(moduleHashes);
|
||||
} else {
|
||||
// should it issue warning or silent?
|
||||
System.out.println("warning: no module is recorded in hash in " + name);
|
||||
}
|
||||
// --hash-modules
|
||||
if (modulesToHash != null) {
|
||||
String mn = md.name();
|
||||
Hasher hasher = new Hasher(md, fname);
|
||||
ModuleHashes moduleHashes = hasher.computeHashes(mn);
|
||||
if (moduleHashes != null) {
|
||||
extender.hashes(moduleHashes);
|
||||
} else {
|
||||
// should it issue warning or silent?
|
||||
System.out.println("warning: no module is recorded in hash in " + mn);
|
||||
}
|
||||
|
||||
extender.write(baos);
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
extender.write(baos);
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1865,8 +1983,8 @@ class Main {
|
||||
deque.add(name);
|
||||
Set<String> mods = visitNodes(graph, deque);
|
||||
|
||||
// filter modules matching the pattern specified --hash-modules
|
||||
// as well as itself as the jmod file is being generated
|
||||
// filter modules matching the pattern specified in --hash-modules,
|
||||
// as well as the modular jar file that is being created / updated
|
||||
Map<String, Path> modulesForHash = mods.stream()
|
||||
.filter(mn -> !mn.equals(name) && modules.contains(mn))
|
||||
.collect(Collectors.toMap(Function.identity(), moduleNameToPath::get));
|
||||
|
@ -62,16 +62,33 @@ error.unexpected.module-info=\
|
||||
Unexpected module descriptor {0}
|
||||
error.module.descriptor.not.found=\
|
||||
Module descriptor not found
|
||||
error.versioned.info.without.root=\
|
||||
module-info.class found in versioned section without module-info.class \
|
||||
in the root
|
||||
error.versioned.info.name.notequal=\
|
||||
module-info.class in versioned section contains incorrect name
|
||||
error.versioned.info.requires.public=\
|
||||
module-info.class in versioned section contains additional requires public
|
||||
error.versioned.info.requires.added=\
|
||||
module-info.class in versioned section contains additional requires
|
||||
error.versioned.info.requires.dropped=\
|
||||
module-info.class in versioned section contains missing requires
|
||||
error.versioned.info.exports.notequal=\
|
||||
module-info.class in versioned section contains different exports
|
||||
error.versioned.info.provides.notequal=\
|
||||
module-info.class in versioned section contains different provides
|
||||
error.invalid.versioned.module.attribute=\
|
||||
Invalid module descriptor attribute {0}
|
||||
error.missing.provider=\
|
||||
Service provider not found: {0}
|
||||
out.added.manifest=\
|
||||
added manifest
|
||||
out.added.module-info=\
|
||||
added module-info.class
|
||||
added module-info: {0}
|
||||
out.update.manifest=\
|
||||
updated manifest
|
||||
out.update.module-info=\
|
||||
updated module-info.class
|
||||
updated module-info: {0}
|
||||
out.ignore.entry=\
|
||||
ignoring entry {0}
|
||||
out.adding=\
|
||||
|
@ -33,6 +33,7 @@ import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarInputStream;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
import javax.tools.JavaCompiler;
|
||||
@ -52,45 +53,64 @@ import static java.lang.System.out;
|
||||
/*
|
||||
* @test
|
||||
* @library /lib/testlibrary
|
||||
* @modules jdk.compiler
|
||||
* jdk.jartool
|
||||
* @build jdk.testlibrary.FileUtils jdk.testlibrary.JDKToolFinder
|
||||
* @compile Basic.java
|
||||
* @run testng Basic
|
||||
* @summary Basic test for Modular jars
|
||||
* @summary Tests for plain Modular jars & Multi-Release Modular jars
|
||||
*/
|
||||
|
||||
public class Basic {
|
||||
static final Path TEST_SRC = Paths.get(System.getProperty("test.src", "."));
|
||||
static final Path TEST_CLASSES = Paths.get(System.getProperty("test.classes", "."));
|
||||
static final Path MODULE_CLASSES = TEST_CLASSES.resolve("build");
|
||||
static final Path MRJAR_DIR = MODULE_CLASSES.resolve("mrjar");
|
||||
|
||||
// Details based on the checked in module source
|
||||
static TestModuleData FOO = new TestModuleData("foo",
|
||||
"1.123",
|
||||
"jdk.test.foo.Foo",
|
||||
"Hello World!!!", null,
|
||||
"jdk.test.foo.internal");
|
||||
"Hello World!!!",
|
||||
null, // no hashes
|
||||
Set.of("java.base"),
|
||||
Set.of("jdk.test.foo"),
|
||||
null, // no uses
|
||||
null, // no provides
|
||||
Set.of("jdk.test.foo.internal"));
|
||||
static TestModuleData BAR = new TestModuleData("bar",
|
||||
"4.5.6.7",
|
||||
"jdk.test.bar.Bar",
|
||||
"Hello from Bar!", null,
|
||||
"jdk.test.bar",
|
||||
"jdk.test.bar.internal");
|
||||
"Hello from Bar!",
|
||||
null, // no hashes
|
||||
Set.of("java.base", "foo"),
|
||||
null, // no exports
|
||||
null, // no uses
|
||||
null, // no provides
|
||||
Set.of("jdk.test.bar",
|
||||
"jdk.test.bar.internal"));
|
||||
|
||||
static class TestModuleData {
|
||||
final String moduleName;
|
||||
final Set<String> requires;
|
||||
final Set<String> exports;
|
||||
final Set<String> uses;
|
||||
final Set<String> provides;
|
||||
final String mainClass;
|
||||
final String version;
|
||||
final String message;
|
||||
final String hashes;
|
||||
final Set<String> conceals;
|
||||
TestModuleData(String mn, String v, String mc, String m, String h, String... pkgs) {
|
||||
|
||||
TestModuleData(String mn, String v, String mc, String m, String h,
|
||||
Set<String> requires, Set<String> exports, Set<String> uses,
|
||||
Set<String> provides, Set<String> conceals) {
|
||||
moduleName = mn; mainClass = mc; version = v; message = m; hashes = h;
|
||||
conceals = new HashSet<>();
|
||||
Stream.of(pkgs).forEach(conceals::add);
|
||||
}
|
||||
TestModuleData(String mn, String v, String mc, String m, String h, Set<String> pkgs) {
|
||||
moduleName = mn; mainClass = mc; version = v; message = m; hashes = h;
|
||||
conceals = pkgs;
|
||||
this.requires = requires;
|
||||
this.exports = exports;
|
||||
this.uses = uses;
|
||||
this.provides = provides;
|
||||
this.conceals = conceals;
|
||||
}
|
||||
static TestModuleData from(String s) {
|
||||
try {
|
||||
@ -99,7 +119,8 @@ public class Basic {
|
||||
String message = null;
|
||||
String name = null, version = null, mainClass = null;
|
||||
String hashes = null;
|
||||
Set<String> conceals = null;
|
||||
Set<String> requires, exports, uses, provides, conceals;
|
||||
requires = exports = uses = provides = conceals = null;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.startsWith("message:")) {
|
||||
message = line.substring("message:".length());
|
||||
@ -114,28 +135,46 @@ public class Basic {
|
||||
}
|
||||
} else if (line.startsWith("mainClass:")) {
|
||||
mainClass = line.substring("mainClass:".length());
|
||||
} else if (line.startsWith("requires:")) {
|
||||
line = line.substring("requires:".length());
|
||||
requires = stringToSet(line);
|
||||
} else if (line.startsWith("exports:")) {
|
||||
line = line.substring("exports:".length());
|
||||
exports = stringToSet(line);
|
||||
} else if (line.startsWith("uses:")) {
|
||||
line = line.substring("uses:".length());
|
||||
uses = stringToSet(line);
|
||||
} else if (line.startsWith("provides:")) {
|
||||
line = line.substring("provides:".length());
|
||||
provides = stringToSet(line);
|
||||
} else if (line.startsWith("hashes:")) {
|
||||
hashes = line.substring("hashes:".length());
|
||||
} else if (line.startsWith("conceals:")) {
|
||||
} else if (line.startsWith("conceals:")) {
|
||||
line = line.substring("conceals:".length());
|
||||
conceals = new HashSet<>();
|
||||
int i = line.indexOf(',');
|
||||
if (i != -1) {
|
||||
String[] p = line.split(",");
|
||||
Stream.of(p).forEach(conceals::add);
|
||||
} else {
|
||||
conceals.add(line);
|
||||
}
|
||||
conceals = stringToSet(line);
|
||||
} else {
|
||||
throw new AssertionError("Unknown value " + line);
|
||||
}
|
||||
}
|
||||
|
||||
return new TestModuleData(name, version, mainClass, message, hashes, conceals);
|
||||
return new TestModuleData(name, version, mainClass, message,
|
||||
hashes, requires, exports, uses,
|
||||
provides, conceals);
|
||||
} catch (IOException x) {
|
||||
throw new UncheckedIOException(x);
|
||||
}
|
||||
}
|
||||
static Set<String> stringToSet(String commaList) {
|
||||
Set<String> s = new HashSet<>();
|
||||
int i = commaList.indexOf(',');
|
||||
if (i != -1) {
|
||||
String[] p = commaList.split(",");
|
||||
Stream.of(p).forEach(s::add);
|
||||
} else {
|
||||
s.add(commaList);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
static void assertModuleData(Result r, TestModuleData expected) {
|
||||
@ -150,10 +189,19 @@ public class Basic {
|
||||
"Expected version: ", expected.version, ", got:", received.version);
|
||||
assertTrue(expected.mainClass.equals(received.mainClass),
|
||||
"Expected mainClass: ", expected.mainClass, ", got:", received.mainClass);
|
||||
expected.conceals.forEach(p -> assertTrue(received.conceals.contains(p),
|
||||
"Expected ", p, ", in ", received.conceals));
|
||||
received.conceals.forEach(p -> assertTrue(expected.conceals.contains(p),
|
||||
"Expected ", p, ", in ", expected.conceals));
|
||||
assertSetsEqual(expected.requires, received.requires);
|
||||
assertSetsEqual(expected.exports, received.exports);
|
||||
assertSetsEqual(expected.uses, received.uses);
|
||||
assertSetsEqual(expected.provides, received.provides);
|
||||
assertSetsEqual(expected.conceals, received.conceals);
|
||||
}
|
||||
|
||||
static void assertSetsEqual(Set<String> s1, Set<String> s2) {
|
||||
if (s1 == null && s2 == null) // none expected, or received
|
||||
return;
|
||||
assertTrue(s1.size() == s2.size(),
|
||||
"Unexpected set size difference: ", s1.size(), ", ", s2.size());
|
||||
s1.forEach(p -> assertTrue(s2.contains(p), "Expected ", p, ", in ", s2));
|
||||
}
|
||||
|
||||
@BeforeTest
|
||||
@ -161,6 +209,10 @@ public class Basic {
|
||||
compileModule(FOO.moduleName);
|
||||
compileModule(BAR.moduleName, MODULE_CLASSES);
|
||||
compileModule("baz"); // for service provider consistency checking
|
||||
|
||||
setupMRJARModuleInfo(FOO.moduleName);
|
||||
setupMRJARModuleInfo(BAR.moduleName);
|
||||
setupMRJARModuleInfo("baz");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -180,7 +232,6 @@ public class Basic {
|
||||
java(mp, FOO.moduleName + "/" + FOO.mainClass)
|
||||
.assertSuccess()
|
||||
.resultChecker(r -> assertModuleData(r, FOO));
|
||||
|
||||
try (InputStream fis = Files.newInputStream(modularJar);
|
||||
JarInputStream jis = new JarInputStream(fis)) {
|
||||
assertTrue(!jarContains(jis, "./"),
|
||||
@ -188,6 +239,30 @@ public class Basic {
|
||||
}
|
||||
}
|
||||
|
||||
/** Similar to createFoo, but with a Multi-Release Modular jar. */
|
||||
@Test
|
||||
public void createMRMJarFoo() throws IOException {
|
||||
Path mp = Paths.get("createMRMJarFoo");
|
||||
createTestDir(mp);
|
||||
Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName);
|
||||
Path mrjarDir = MRJAR_DIR.resolve(FOO.moduleName);
|
||||
Path modularJar = mp.resolve(FOO.moduleName + ".jar");
|
||||
|
||||
// Positive test, create
|
||||
jar("--create",
|
||||
"--file=" + modularJar.toString(),
|
||||
"--main-class=" + FOO.mainClass,
|
||||
"--module-version=" + FOO.version,
|
||||
"-m", mrjarDir.resolve("META-INF/MANIFEST.MF").toRealPath().toString(),
|
||||
"-C", mrjarDir.toString(), "META-INF/versions/9/module-info.class",
|
||||
"-C", modClasses.toString(), ".")
|
||||
.assertSuccess();
|
||||
java(mp, FOO.moduleName + "/" + FOO.mainClass)
|
||||
.assertSuccess()
|
||||
.resultChecker(r -> assertModuleData(r, FOO));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void updateFoo() throws IOException {
|
||||
Path mp = Paths.get("updateFoo");
|
||||
@ -212,6 +287,32 @@ public class Basic {
|
||||
.resultChecker(r -> assertModuleData(r, FOO));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateMRMJarFoo() throws IOException {
|
||||
Path mp = Paths.get("updateMRMJarFoo");
|
||||
createTestDir(mp);
|
||||
Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName);
|
||||
Path mrjarDir = MRJAR_DIR.resolve(FOO.moduleName);
|
||||
Path modularJar = mp.resolve(FOO.moduleName + ".jar");
|
||||
|
||||
jar("--create",
|
||||
"--file=" + modularJar.toString(),
|
||||
"--no-manifest",
|
||||
"-C", modClasses.toString(), "jdk")
|
||||
.assertSuccess();
|
||||
jar("--update",
|
||||
"--file=" + modularJar.toString(),
|
||||
"--main-class=" + FOO.mainClass,
|
||||
"--module-version=" + FOO.version,
|
||||
"-m", mrjarDir.resolve("META-INF/MANIFEST.MF").toRealPath().toString(),
|
||||
"-C", mrjarDir.toString(), "META-INF/versions/9/module-info.class",
|
||||
"-C", modClasses.toString(), "module-info.class")
|
||||
.assertSuccess();
|
||||
java(mp, FOO.moduleName + "/" + FOO.mainClass)
|
||||
.assertSuccess()
|
||||
.resultChecker(r -> assertModuleData(r, FOO));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void partialUpdateFooMainClass() throws IOException {
|
||||
Path mp = Paths.get("partialUpdateFooMainClass");
|
||||
@ -289,6 +390,30 @@ public class Basic {
|
||||
.resultChecker(r -> assertModuleData(r, FOO));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void partialUpdateMRMJarFooNotAllFiles() throws IOException {
|
||||
Path mp = Paths.get("partialUpdateMRMJarFooNotAllFiles");
|
||||
createTestDir(mp);
|
||||
Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName);
|
||||
Path mrjarDir = MRJAR_DIR.resolve(FOO.moduleName);
|
||||
Path modularJar = mp.resolve(FOO.moduleName + ".jar");
|
||||
|
||||
jar("--create",
|
||||
"--file=" + modularJar.toString(),
|
||||
"--module-version=" + FOO.version,
|
||||
"-C", modClasses.toString(), ".")
|
||||
.assertSuccess();
|
||||
jar("--update",
|
||||
"--file=" + modularJar.toString(),
|
||||
"--main-class=" + FOO.mainClass,
|
||||
"-m", mrjarDir.resolve("META-INF/MANIFEST.MF").toRealPath().toString(),
|
||||
"-C", mrjarDir.toString(), "META-INF/versions/9/module-info.class")
|
||||
.assertSuccess();
|
||||
java(mp, FOO.moduleName + "/" + FOO.mainClass)
|
||||
.assertSuccess()
|
||||
.resultChecker(r -> assertModuleData(r, FOO));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void partialUpdateFooAllFilesAndAttributes() throws IOException {
|
||||
Path mp = Paths.get("partialUpdateFooAllFilesAndAttributes");
|
||||
@ -527,6 +652,24 @@ public class Basic {
|
||||
.assertFailure();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void servicesCreateWithoutFailureMRMJAR() throws IOException {
|
||||
Path mp = Paths.get("servicesCreateWithoutFailureMRMJAR");
|
||||
createTestDir(mp);
|
||||
Path modClasses = MODULE_CLASSES.resolve("baz");
|
||||
Path mrjarDir = MRJAR_DIR.resolve("baz");
|
||||
Path modularJar = mp.resolve("baz" + ".jar");
|
||||
|
||||
jar("--create",
|
||||
"--file=" + modularJar.toString(),
|
||||
"-m", mrjarDir.resolve("META-INF/MANIFEST.MF").toRealPath().toString(),
|
||||
"-C", modClasses.toString(), "module-info.class",
|
||||
"-C", mrjarDir.toString(), "META-INF/versions/9/module-info.class",
|
||||
"-C", modClasses.toString(), "jdk/test/baz/BazService.class",
|
||||
"-C", modClasses.toString(), "jdk/test/baz/internal/BazServiceImpl.class")
|
||||
.assertSuccess();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printModuleDescriptorFoo() throws IOException {
|
||||
Path mp = Paths.get("printModuleDescriptorFoo");
|
||||
@ -611,6 +754,24 @@ public class Basic {
|
||||
return build;
|
||||
}
|
||||
|
||||
static void setupMRJARModuleInfo(String moduleName) throws IOException {
|
||||
Path modClasses = MODULE_CLASSES.resolve(moduleName);
|
||||
Path metaInfDir = MRJAR_DIR.resolve(moduleName).resolve("META-INF");
|
||||
Path versionSection = metaInfDir.resolve("versions").resolve("9");
|
||||
createTestDir(versionSection);
|
||||
|
||||
Path versionModuleInfo = versionSection.resolve("module-info.class");
|
||||
System.out.println("copying " + modClasses.resolve("module-info.class") + " to " + versionModuleInfo);
|
||||
Files.copy(modClasses.resolve("module-info.class"), versionModuleInfo);
|
||||
|
||||
Manifest manifest = new Manifest();
|
||||
manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
|
||||
manifest.getMainAttributes().putValue("Multi-Release", "true");
|
||||
try (OutputStream os = Files.newOutputStream(metaInfDir.resolve("MANIFEST.MF"))) {
|
||||
manifest.write(os);
|
||||
}
|
||||
}
|
||||
|
||||
// Re-enable when there is support in javax.tools for module path
|
||||
// static void javac(Path dest, Path... sourceFiles) throws IOException {
|
||||
// out.printf("Compiling %d source files %s%n", sourceFiles.length,
|
||||
@ -690,7 +851,7 @@ public class Basic {
|
||||
static void createTestDir(Path p) throws IOException{
|
||||
if (Files.exists(p))
|
||||
FileUtils.deleteFileTreeWithRetry(p);
|
||||
Files.createDirectory(p);
|
||||
Files.createDirectories(p);
|
||||
}
|
||||
|
||||
static boolean jarContains(JarInputStream jis, String entryName)
|
||||
|
@ -1,12 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
@ -26,6 +24,8 @@
|
||||
package jdk.test.bar;
|
||||
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.Exports;
|
||||
import java.lang.module.ModuleDescriptor.Requires;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
@ -39,18 +39,36 @@ public class Bar {
|
||||
|
||||
ModuleDescriptor md = Bar.class.getModule().getDescriptor();
|
||||
System.out.println("nameAndVersion:" + md.toNameAndVersion());
|
||||
System.out.println("mainClass:" + md.mainClass().get());
|
||||
md.mainClass().ifPresent(mc -> System.out.println("mainClass:" + mc));
|
||||
|
||||
StringJoiner sj = new StringJoiner(",");
|
||||
md.requires().stream().map(ModuleDescriptor.Requires::name).sorted().forEach(sj::add);
|
||||
System.out.println("requires:" + sj.toString());
|
||||
|
||||
sj = new StringJoiner(",");
|
||||
md.exports().stream().map(ModuleDescriptor.Exports::source).sorted().forEach(sj::add);
|
||||
if (!sj.toString().equals(""))
|
||||
System.out.println("exports:" + sj.toString());
|
||||
|
||||
sj = new StringJoiner(",");
|
||||
md.uses().stream().sorted().forEach(sj::add);
|
||||
if (!sj.toString().equals(""))
|
||||
System.out.println("uses:" + sj.toString());
|
||||
|
||||
sj = new StringJoiner(",");
|
||||
md.provides().keySet().stream().sorted().forEach(sj::add);
|
||||
if (!sj.toString().equals(""))
|
||||
System.out.println("provides:" + sj.toString());
|
||||
|
||||
sj = new StringJoiner(",");
|
||||
md.conceals().forEach(sj::add);
|
||||
if (!sj.toString().equals(""))
|
||||
System.out.println("conceals:" + sj.toString());
|
||||
|
||||
Method m = ModuleDescriptor.class.getDeclaredMethod("hashes");
|
||||
m.setAccessible(true);
|
||||
ModuleDescriptor foo = jdk.test.foo.Foo.class.getModule().getDescriptor();
|
||||
Optional<ModuleHashes> oHashes =
|
||||
(Optional<ModuleHashes>) m.invoke(foo);
|
||||
|
||||
Optional<ModuleHashes> oHashes = (Optional<ModuleHashes>) m.invoke(foo);
|
||||
System.out.println("hashes:" + oHashes.get().hashFor("bar"));
|
||||
|
||||
StringJoiner sj = new StringJoiner(",");
|
||||
md.conceals().forEach(sj::add);
|
||||
System.out.println("conceals:" + sj.toString());
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
|
@ -1,12 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
|
@ -1,12 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
|
@ -1,12 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
|
@ -1,12 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
|
@ -1,12 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
@ -26,6 +24,8 @@
|
||||
package jdk.test.foo;
|
||||
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.Exports;
|
||||
import java.lang.module.ModuleDescriptor.Requires;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import jdk.test.foo.internal.Message;
|
||||
@ -36,10 +36,30 @@ public class Foo {
|
||||
|
||||
ModuleDescriptor md = Foo.class.getModule().getDescriptor();
|
||||
System.out.println("nameAndVersion:" + md.toNameAndVersion());
|
||||
System.out.println("mainClass:" + md.mainClass().get());
|
||||
md.mainClass().ifPresent(mc -> System.out.println("mainClass:" + mc));
|
||||
|
||||
StringJoiner sj = new StringJoiner(",");
|
||||
md.requires().stream().map(Requires::name).sorted().forEach(sj::add);
|
||||
System.out.println("requires:" + sj.toString());
|
||||
|
||||
sj = new StringJoiner(",");
|
||||
md.exports().stream().map(Exports::source).sorted().forEach(sj::add);
|
||||
if (!sj.toString().equals(""))
|
||||
System.out.println("exports:" + sj.toString());
|
||||
|
||||
sj = new StringJoiner(",");
|
||||
md.uses().stream().sorted().forEach(sj::add);
|
||||
if (!sj.toString().equals(""))
|
||||
System.out.println("uses:" + sj.toString());
|
||||
|
||||
sj = new StringJoiner(",");
|
||||
md.provides().keySet().stream().sorted().forEach(sj::add);
|
||||
if (!sj.toString().equals(""))
|
||||
System.out.println("provides:" + sj.toString());
|
||||
|
||||
sj = new StringJoiner(",");
|
||||
md.conceals().forEach(sj::add);
|
||||
System.out.println("conceals:" + sj.toString());
|
||||
if (!sj.toString().equals(""))
|
||||
System.out.println("conceals:" + sj.toString());
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
|
@ -1,12 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
|
Loading…
x
Reference in New Issue
Block a user