JPMS Support For Module Versions — A Research Log

CodeFX Weekly #54 — 26th of January 2018

Nicolai Parlog
Jan 31, 2018 · 10 min read

Hi everyone,

it’s Friday morning and I’m looking at a full schedule (including flying to Sofia for a presentation at JUG Bulgaria) and an empty list of ideas for today’s weekly. Until just now, when I had one. I wanted to use the time on the road to experiment with the little support for module versions that the JPMS has to offer. I usually don’t write “research logs”, but I have seen it recommended a couple of times, so why not try it out?

Here’s the deal: I’m gonna write down what I do while I do it and we’ll see where it gets us. Then tonight at the hotel, I’m gonna send it out after a little polish. But no backsies, I’ll just improve phrasing and put in a few headings.

This is new for me and I guess for you, too, so I’m really interested in your opinion. Did it work out? Did you like reading along or am I rambling? Let me know under nipa@codefx.org or on Twitter.

I send this newsletter out every Friday. Yes, as an actual email. Subscribe!


Recording module versions in JPMS

Setting up the experiments

[1450, I’m at the train station in Karlsruhe and have a few minutes before my train to Frankfurt airport arrives.]

Let’s start with on and . This is what the command line help has to say:

Mmh, ok. Apparently reading time is over, time to experiment. To keep things simple and fast, I’m starting with the Hello Modules demo application. And I just now realize that there isn’t a build script for it, so let’s quickly write one:

Then, to see where the information ends up, I’ll prod - here's the demo's updated method:

[1510, the train was a little late, but it arrived eventually. This is the last time I can go online before the hotel in a few hours, so if there’s anything I need to look up, it better comes up now. Also, I just realized that I forgot my little talk tech bag with the presenter and HDMI adapter. Damn. Thankfully, a quick email exchange with Martin and Ivan from JUG Bulgaria fixes that — they have me covered. Thanks, guys!]

Building and running prints the following:

(You can see here why returns an .)

As expected. Note that I’ve so far done nothing but to set up the experiments (with the build script) and consolidate my knowledge (by looking at before adding version information). This way, experiments are as quick as I can make them and have a clear reference point to compare observed behavior against.

The option

Now it’s time to actually use the new flags. First, I compile with , then run the thing. I'm not sure what exactly to expect - you'd think the version shows up at run time, but then what's there for? The same? My gut feeling says the compiler won't embed the version.

And I was wrong. So what does do? Let's use version for and for . I assume , being called after the compiler, overrides its version.

Indeed. To verify, lets compare the two files. I don't want to (or even know how to) do something fancy, so let's see what says:

Eh… Well I can see in there. (You'll also notice that I didn't yet update to 9.0.4.) What does report?

And there’s . Clearly, rewrites , which is not at all surprising because it also has to add the main class. On a higher level of abstraction, I can use :

There it is, reports for the exploded JAR and for the packaged one. That is also as expected.

And you can see that the module path, like the class path, actually accepts exploded JARs, i.e. a folder like , which contains the raw files:

So what have we established so far?

  • embeds the specified version in , from where tools and reflection API can read it
  • does the same, possibly overriding existing version information

Recording dependency versions

The module descriptor generated by also contained the string , which is the Java version I built this on. But I suspect the concrete string comes from the base module's version because java.base is also mentioned there. Also, I thought I once saw an option to record dependency information.

Search in progress…

Nope, neither nor the same for show anything else that's related to module versions. Mmh, weird.

So let’s see whether other module versions show up. For that I switch to Monitor, the demo application for The Java Module System. It consists of a handful of modules and one of them, monitor depends on quite a few other ones, so if dependency versions show up, it should be there.

First, I sprinkle on all commands, then build and look at the module descriptor for monitor:

Mmh, no, other versions aren’t recorded. But once again is awfully close to java.base - let's throw in two other platform modules and build again.

No, still just the one (now even the went missing).

Fortunately, I’m still on the train, so I am online. Look what I found in the list of Jigsaw issues:

#VersionedDependences — Consider allowing specific version strings, or perhaps version constraints, as an optional element of requires clauses in module declarations. Failing that, consider allowing specific version strings, or perhaps version constraints, to be added to the dependences recorded in a compiled module descriptor; this would, e.g., allow a compiler or build system to record the versions of the modules against which a particular module was compiled, for use by other tools. In either case, if such version information is merely informative then it will still honor the version selection non-requirement; if such version information is interpreted by the module system then that requirement may come into question. [Cristiano Mariano, Stephane Epardaud]

Resolution When compiling a module that depends on some other modules, record the version strings of those modules, if available, in the resulting module descriptor. [Proposal, as amended per a suggestion by Rémi Forax]

From the proposal:

Extend module descriptors to allow the inclusion of version strings in the table of the attribute by introducing a field after the field of each entry of that table. The value of this new field is either zero, to indicate that no version string was recorded, or else the constant-pool index of a structure whose value is the version string.

Aha! So the versions are recorded! And they only show up once because they all reference the same constant pool index.

[1650, there aren’t many places I know at Frankfurt airport where you can get food and electricity — I’m at one of them now and just took a break, eating something while reading Persepolis Rising.]

Now I really wonder, whether there isn’t a better way to look into the module descriptor. I’m offline now, so I can’t ask the Internet, which means I have to run on what I know — that’s not a lot. I heard makes bytecode presentable (does it decompile?), so let's use it:

Wow, that was shockingly easy. But it really just gives me back the source code (almost; note that it lists java.base, which is of course not in the original declaration). Can the options help?

Imagine elevator muzak, playing in the background

Nope, nothing. I always get the same output. Now I’m out of ideas for the descriptor. But this is not a dead end yet.

Accessing dependency versions

To continue quoting the proposal:

Extend the API with a single new method:

This method will return an empty object if no version string was recorded for the corresponding dependence, or else an that contains the recorded version string.

I’ve actually seen this while working on the reflection chapter. I slip the following method into Monitor’s main module:

Output:

Neat! To summarize:

  • the compiler records the versions of dependencies in the module descriptor
  • command tools like and are hesitant to cough up the recorded versions
  • the reflection API, more precisely , make them available

So what is this good for? Good question. One immediate advantage is this:

The stack trace contains the module version! Don’t say, nobody cares about that — at least I do, so the set of caring devs is not empty.

Hey, why not analyze recorded vs actual versions? I’m possibly running into the weeds here, but what about this?

Output:

[1800, it was quite a walk from that bar to the my gate, but at least there weren’t any lines at pass control and security. Much to my chagrin I just found out that the plug at the bar that I specifically chose because it offered them was apparently not working, so now I’m almost out of battery life.]

That’s boring, but if I a couple of dependencies, it should get more interesting:

What? But I don’t want to add a manifest, define a main class (), or add files. WTF, ?! Eh... ok, maybe add an empty manifest?

That worked. After a few more edits I get:

Danger! Give it a little polish and this could make some informative log output.

With that I think that particular dead horse is sufficiently beaten. The summary of summaries:

  • embeds the specified version in , from where tools and reflection API can read it
  • does the same, possibly overriding existing version information
  • the compiler also records the versions of dependencies in the module descriptor, from where the reflection API makes them available
  • one use case would be comparison of compiled against actual versions

Oh, wait. I also checked Maven and it didn’t include version information yet. That should be straightforward — I’ll open an issue over the weekend.

[1815, and with that it’s almost time to board. I wanted to go over , too, but instead I think I'm gonna wrap this up. I'll add in some links and do a little proof reading, so I can send it out as soon as I arrive at the hotel. Also, battery is running out...]


Shots

I assume all of you know what net neutrality means and I hope you are fighting your government if it is trying to take it away from you. Part of that is explaining to non-technically inclined people why it is important and, stupid as it might be, Burger King’s video on Whopper neutrality can actually do that in a fun way. So if you don’t mind being part of a fast food empire’s marketing scheme, share it with the burger lovers you know that need to learn about net neutrality.

so long … Nicolai


PS: Don’t forget to subscribe or recommend! :)

Photo by Kari Shea on Unsplash

CodeFX Weekly

Whatever caught my interest throughout the week: libraries or tools I am using, what I’ve written or read that might interest you, fascinating questions on StackOverflow, conferences visits, odd tweets, anything really that connects to Java, software development, or writing.

Nicolai Parlog

Written by

Developer (mostly Java), blogger (http://codefx.org/), author (http://tiny.cc/jms), trainer (http://courses.codefx.org & https://www.youtube.com/c/codefx)

CodeFX Weekly

Whatever caught my interest throughout the week: libraries or tools I am using, what I’ve written or read that might interest you, fascinating questions on StackOverflow, conferences visits, odd tweets, anything really that connects to Java, software development, or writing.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade