How to Derive Module Descriptor for Auto Generated Module Names in Java 9

Unable to derive module descriptor for auto generated module names in Java 9?

The solution to this seems to be:-

  • A way possible to uninterruptedly using the same artifact name with a new(different) module name could be by packaging META-INF/MANIFEST.MF of the artifact with an attribute Automatic-Module-Name which governs the name of the module to be used by the module descriptor when converted as an automatic module.

OR

  • Artifact owners can add module declarations using module-info.java to their JAR. (this could result in a slow bottom-up migration)

Since the module declaration defined in the specs as:

A module declaration introduces a module name that can be used in
other module declarations to express relationships between modules. A
module name consists of one or more Java identifiers (§3.8) separated
by "." tokens.


Intersetingly the declarations suggests -

In some cases, the Internet domain name may not be a valid package
name. Here are some suggested conventions for dealing with these
situations:

  • If the domain name contains a hyphen, or any other special character
    not allowed in an identifier (§3.8), convert it into an underscore.

  • If any of the resulting package name components are keywords (§3.9),
    append an underscore to them.

  • If any of the resulting package name components start with a digit, or
    any other character that is not allowed as an initial character of an
    identifier, have an underscore prefixed to the component.

But keep in mind as you do so that Underscore is a keyword in Java9

Sample Image

int _;  // is would throw an error on javac based out of JDK9
int _native; // works fine

is there a way i can fix this invalid module name error?

If you can locate the module-info.java file, try to:

  1. Rename the jar file to something like just jlfgr.jar

  2. In the module-info.java file, add the following sentence:

    requires jlfgr;

Hope it's helpful.

Algorithm that Java tools use to automatically derive module names from jar names

A non-modular JAR file deployed on the module path is an automatic module. If the JAR file has a main attribute Automatic-Module-Name (see Main Attributes) then the attribute's value is the module name, otherwise the module name is derived from the name of the JAR file as specified in ModuleFinder.of(Path...).

If you want more information, you can always lookup the source code:

/**
* Treat the given JAR file as a module as follows:
*
* 1. The module name (and optionally the version) is derived from the file
* name of the JAR file
* 2. The packages of all .class files in the JAR file are exported
* 3. It has no module-private/concealed packages
* 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
*/
private ModuleDescriptor deriveModuleDescriptor(JarFile jf)
throws IOException
{
// Derive module name and version from JAR file name

String fn = jf.getName();
int i = fn.lastIndexOf(File.separator);
if (i != -1)
fn = fn.substring(i+1);

// drop .jar
String mn = fn.substring(0, fn.length()-4);
String vs = null;

// find first occurrence of -${NUMBER}. or -${NUMBER}$
Matcher matcher = Patterns.DASH_VERSION.matcher(mn);
if (matcher.find()) {
int start = matcher.start();

// attempt to parse the tail as a version string
try {
String tail = mn.substring(start+1);
ModuleDescriptor.Version.parse(tail);
vs = tail;
} catch (IllegalArgumentException ignore) { }

mn = mn.substring(0, start);
}

// finally clean up the module name
mn = cleanModuleName(mn);

// Builder throws IAE if module name is empty or invalid
ModuleDescriptor.Builder builder
= new ModuleDescriptor.Builder(mn)
.automatic()
.requires(Set.of(Requires.Modifier.MANDATED), "java.base");
if (vs != null)
builder.version(vs);

// scan the names of the entries in the JAR file
Map<Boolean, Set<String>> map = VersionedStream.stream(jf)
.filter(e -> !e.isDirectory())
.map(JarEntry::getName)
.collect(Collectors.partitioningBy(e -> e.startsWith(SERVICES_PREFIX),
Collectors.toSet()));

Set<String> resources = map.get(Boolean.FALSE);
Set<String> configFiles = map.get(Boolean.TRUE);

// all packages are exported
resources.stream()
.map(this::toPackageName)
.flatMap(Optional::stream)
.distinct()
.forEach(builder::exports);

// map names of service configuration files to service names
Set<String> serviceNames = configFiles.stream()
.map(this::toServiceName)
.flatMap(Optional::stream)
.collect(Collectors.toSet());

// parse each service configuration file
for (String sn : serviceNames) {
JarEntry entry = jf.getJarEntry(SERVICES_PREFIX + sn);
Set<String> providerClasses = new LinkedHashSet<>();
try (InputStream in = jf.getInputStream(entry)) {
BufferedReader reader
= new BufferedReader(new InputStreamReader(in, "UTF-8"));
String cn;
while ((cn = nextLine(reader)) != null) {
if (cn.length() > 0) {
providerClasses.add(cn);
}
}
}
if (!providerClasses.isEmpty())
builder.provides(sn, providerClasses);
}

// Main-Class attribute if it exists
Manifest man = jf.getManifest();
if (man != null) {
Attributes attrs = man.getMainAttributes();
String mainClass = attrs.getValue(Attributes.Name.MAIN_CLASS);
if (mainClass != null)
builder.mainClass(mainClass);
}

return builder.build();
}

/**
* Patterns used to derive the module name from a JAR file name.
*/
private static class Patterns {
static final Pattern DASH_VERSION = Pattern.compile("-(\\d+(\\.|$))");
static final Pattern NON_ALPHANUM = Pattern.compile("[^A-Za-z0-9]");
static final Pattern REPEATING_DOTS = Pattern.compile("(\\.)(\\1)+");
static final Pattern LEADING_DOTS = Pattern.compile("^\\.");
static final Pattern TRAILING_DOTS = Pattern.compile("\\.$");
}

Automatic module name containing number

The problem is that the module system does not identify 2_0 as a version number and hence doesn't strip it when determining the automatic module name. Unfortunately, 2_0 is not a valid Java identifier and can hence not be used as a segment in a module name.

The solution is to either rename the JAR or add the Automatic-Module-Name entry to the JAR's manifest:

  1. Create a file manifest.txt with the following content:

    Automatic-Module-Name: sdk.http.ahc
  2. Then use jar to append that line to the existing manifest:

    jar --update --file sdk-http-ahc-2_0.jar --manifest=manifest.txt

Note that locally modifying existing JARs (name or manifest) can cause problems down the road. Consider changing the Maven version of the file to something like 2.0-patched-auto-name (or similar) and add it to your local Maven repository. If the project is shared with other developers and you have a local Nexus, you can put it in there. Otherwise send a mail to everyone with the steps to add it to their local repo. /p>

Unable to derive module descriptor for auto generated module names in Java 9?

The solution to this seems to be:-

  • A way possible to uninterruptedly using the same artifact name with a new(different) module name could be by packaging META-INF/MANIFEST.MF of the artifact with an attribute Automatic-Module-Name which governs the name of the module to be used by the module descriptor when converted as an automatic module.

OR

  • Artifact owners can add module declarations using module-info.java to their JAR. (this could result in a slow bottom-up migration)

Since the module declaration defined in the specs as:

A module declaration introduces a module name that can be used in
other module declarations to express relationships between modules. A
module name consists of one or more Java identifiers (§3.8) separated
by "." tokens.


Intersetingly the declarations suggests -

In some cases, the Internet domain name may not be a valid package
name. Here are some suggested conventions for dealing with these
situations:

  • If the domain name contains a hyphen, or any other special character
    not allowed in an identifier (§3.8), convert it into an underscore.

  • If any of the resulting package name components are keywords (§3.9),
    append an underscore to them.

  • If any of the resulting package name components start with a digit, or
    any other character that is not allowed as an initial character of an
    identifier, have an underscore prefixed to the component.

But keep in mind as you do so that Underscore is a keyword in Java9

Sample Image

int _;  // is would throw an error on javac based out of JDK9
int _native; // works fine


Related Topics



Leave a reply



Submit