Loading an automatic module from an exploded directory
Eirik Bjørsnøs
eirbjo at gmail.com
Sat Apr 18 22:04:11 UTC 2020
Alan
On Sat, Apr 18, 2020 at 12:24 PM Alan Bateman <Alan.Bateman at oracle.com>
wrote:
> If MultiModuleJARs [1] is re-visited then it would need to explore this
> topic a bit more.
>
Could we update ModulePath could to detect "automatic module directories"?.
Any directory path name ending with ".jar" could be loaded as an automatic
directory module, with the same semantics as if the directory was zipped
into a JAR.
This would require a refactoring of deriveModuleDescriptor to allow it
being called both from readJar and readExplodedModule.
Then readExplodedModule would need an update to detect the lack of
module-info.class, and call deriveModuleDescriptor just like readJar does.
scan(Path) would need an update to make it skip directory scanning on
directories ending with ".jar".
As far as I can tell, this would be (or allow) an implementation of
MultiModuleJARs.
Here's a patch which implements this:
diff --git
a/src/java.base/share/classes/jdk/internal/module/ModulePath.java
b/src/java.base/share/classes/jdk/internal/module/ModulePath.java
index 9971387258..5922858faf 100644
--- a/src/java.base/share/classes/jdk/internal/module/ModulePath.java
+++ b/src/java.base/share/classes/jdk/internal/module/ModulePath.java
@@ -51,6 +51,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.function.Function;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@@ -225,7 +226,7 @@ public class ModulePath implements ModuleFinder {
try {
- if (attrs.isDirectory()) {
+ if (attrs.isDirectory() &&
!entry.getFileName().toString().endsWith(".jar")) {
Path mi = entry.resolve(MODULE_INFO);
if (!Files.exists(mi)) {
// assume a directory of modules
@@ -449,18 +450,17 @@ public class ModulePath implements ModuleFinder {
*
* 1. The value of the Automatic-Module-Name attribute is the module
name
* 2. The version, and the module name when the Automatic-Module-Name
- * attribute is not present, is derived from the file ame of the
JAR file
+ * attribute is not present, is derived from the file name of the
JAR file
* 3. All packages are derived from the .class files in the JAR file
* 4. The contents of any META-INF/services configuration files are
mapped
* to "provides" declarations
* 5. The Main-Class attribute in the main attributes of the JAR
manifest
* is mapped to the module descriptor mainClass if possible
*/
- private ModuleDescriptor deriveModuleDescriptor(JarFile jf)
+ private ModuleDescriptor deriveModuleDescriptor(Manifest man, String
fn, Set<String> entryNames, Function<String, InputStream>
serviceConfigurationFile)
throws IOException
{
// Read Automatic-Module-Name attribute if present
- Manifest man = jf.getManifest();
Attributes attrs = null;
String moduleName = null;
if (man != null) {
@@ -470,8 +470,7 @@ public class ModulePath implements ModuleFinder {
}
}
- // Derive the version, and the module name if needed, from JAR
file name
- String fn = jf.getName();
+ // Derive the version, and the module name if needed, from JAR
file (or .jar directory) name
int i = fn.lastIndexOf(File.separator);
if (i != -1)
fn = fn.substring(i + 1);
@@ -512,10 +511,8 @@ public class ModulePath implements ModuleFinder {
if (vs != null)
builder.version(vs);
- // scan the names of the entries in the JAR file
- Map<Boolean, Set<String>> map = jf.versionedStream()
- .filter(e -> !e.isDirectory())
- .map(JarEntry::getName)
+ // scan the names of the entries in the JAR file or .jar directory
+ Map<Boolean, Set<String>> map = entryNames.stream()
.filter(e -> (e.endsWith(".class") ^
e.startsWith(SERVICES_PREFIX)))
.collect(Collectors.partitioningBy(e ->
e.startsWith(SERVICES_PREFIX),
Collectors.toSet()));
@@ -541,9 +538,8 @@ public class ModulePath implements ModuleFinder {
// parse each service configuration file
for (String sn : serviceNames) {
- JarEntry entry = jf.getJarEntry(SERVICES_PREFIX + sn);
List<String> providerClasses = new ArrayList<>();
- try (InputStream in = jf.getInputStream(entry)) {
+ try (InputStream in =
serviceConfigurationFile.apply(SERVICES_PREFIX +sn)) {
BufferedReader reader
= new BufferedReader(new InputStreamReader(in,
UTF_8.INSTANCE));
String cn;
@@ -641,7 +637,18 @@ public class ModulePath implements ModuleFinder {
// no module-info.class so treat it as automatic module
try {
- ModuleDescriptor md = deriveModuleDescriptor(jf);
+ Set<String> entryNames = jf.versionedStream()
+ .filter(e -> !e.isDirectory())
+ .map(JarEntry::getName)
+ .collect(Collectors.toSet());
+
+ Function<String, InputStream> serviceCatalogProvider =
(sn) -> getInputStream(jf, sn);
+
+ ModuleDescriptor md =
deriveModuleDescriptor(jf.getManifest(),
+ jf.getName(),
+ entryNames,
+ serviceCatalogProvider);
+
attrs = new ModuleInfo.Attributes(md, null, null,
null);
} catch (RuntimeException e) {
throw new FindException("Unable to derive module
descriptor for "
@@ -659,6 +666,22 @@ public class ModulePath implements ModuleFinder {
}
}
+ private InputStream getInputStream(JarFile jf, String sn){
+ try {
+ return jf.getInputStream(jf.getJarEntry(sn));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private InputStream getInputStream(Path dir, String sn){
+ try {
+ return Files.newInputStream(dir.resolve(sn));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
// -- exploded directories --
@@ -666,7 +689,7 @@ public class ModulePath implements ModuleFinder {
try {
return Files.find(dir, Integer.MAX_VALUE,
((path, attrs) -> attrs.isRegularFile() &&
!isHidden(path)))
- .map(path -> dir.relativize(path))
+ .map(dir::relativize)
.map(this::toPackageName)
.flatMap(Optional::stream)
.collect(Collectors.toSet());
@@ -675,6 +698,18 @@ public class ModulePath implements ModuleFinder {
}
}
+ private Set<String> explodedPaths(Path dir) {
+ try {
+ return Files.find(dir, Integer.MAX_VALUE,
+ ((path, attrs) -> attrs.isRegularFile() &&
!isHidden(path)))
+ .map(dir::relativize)
+ .map(Path::toString)
+ .collect(Collectors.toSet());
+ } catch (IOException x) {
+ throw new UncheckedIOException(x);
+ }
+ }
+
/**
* Returns a {@code ModuleReference} to an exploded module on the file
* system or {@code null} if {@code module-info.class} not found.
@@ -685,12 +720,32 @@ public class ModulePath implements ModuleFinder {
private ModuleReference readExplodedModule(Path dir) throws
IOException {
Path mi = dir.resolve(MODULE_INFO);
ModuleInfo.Attributes attrs;
- try (InputStream in = Files.newInputStream(mi)) {
- attrs = ModuleInfo.read(new BufferedInputStream(in),
- () -> explodedPackages(dir));
- } catch (NoSuchFileException e) {
- // for now
- return null;
+ if(!Files.exists(mi)) {
+ Manifest man = null;
+ Path mf = dir.resolve("META-INF/MANIFEST.MF");
+ if(Files.exists(mf)) {
+ try(InputStream in = Files.newInputStream(mf)) {
+ man = new Manifest(in);
+ }
+ }
+ Set<String> entryNameSupplier = explodedPaths(dir);
+ Function<String, InputStream> serviceCatalogProvider = (sn) ->
getInputStream(dir, sn);
+
+ ModuleDescriptor md = deriveModuleDescriptor(man,
+ dir.getFileName().toString(),
+ entryNameSupplier,
+ serviceCatalogProvider);
+
+ attrs = new ModuleInfo.Attributes(md, null, null, null);
+
+ } else {
+ try (InputStream in = Files.newInputStream(mi)) {
+ attrs = ModuleInfo.read(new BufferedInputStream(in),
+ () -> explodedPackages(dir));
+ } catch (NoSuchFileException e) {
+ // for now
+ return null;
+ }
}
return ModuleReferences.newExplodedModule(attrs, patcher, dir);
}
More information about the jigsaw-dev
mailing list