The mrJar Plugin v0.0.13 has been released — Modular MRJAR Files Made Easy

OK @siordache. v0.0.3 fixes that now. I won’t make any excuses this time. I will say I double-checked this time that I did actually fix it. I promise :slight_smile:

Plus, I made the setting of the environment variables the #1 step in the usage instructions.

I don’t know how I could have forgotten to document that option before. But I’ve just added this to the usage instructions:

    mrjar {

        /* (8) Optionally — or, mandatorily if you're using Gradle's Application        */
        /*     Plugin to execute a Java application bundled in your module — tell       */
        /*     mrjar the binary name of the class you want the Application Plugin to run      */
        /*     • the value of this property will automatically propagate to the               */
        /*       Application Plugin's :run task                                               */
        main = 'com.acme.main.JavaMain'

I’d bet your excellent projectand @rgoldberg’s excellent fork of Gradles excellent experiment project — never had these kinds of Gradle-rookie teething problems in their first couple of releases. That’s why your feedback is all the more valuable and sincerely appreciated. Thanks again :+1:

It works with 0.0.3, but I think you can get rid of the mrjar.main property.

tell mrjar the binary name of the class you want the Application Plugin to run.
the value of this property will automatically propagate to the Application Plugin’s :run.

Instead of propagating the mrjar.main value to the Application Plugin, the plugin should just use the value of the application.mainClassName property.

1 Like

Hey there Gents,

mrJar v0.0.6 now supports multi-module projects! I also created a repo with a demo so anybody interested can see it do its stuff.

The environment variables that were mandatory in v0.0.1 are now optional as of v0.0.6. Check out the usage page I linked to the other day for more details.

Please fire away with your feedback? TIA.

Thanks for the suggestion, @siordache. I wasn’t even aware of application.mainClassName until you brought it to my attention the other day. I’m grateful. I also coincidentally happened to notice it in the sample code on which the just-published mrJar demo is based.

I discovered main in the Gradle JavaDocs five, six months ago when I was developing my first Jar-related plugin. So you go with what you know, I guess.

If you can share what you know to be a purely technical reason why my plugin’s implementation should prefer application.mainClassName over main, I would certainly bear it in mind.

But aside from the fact that main works — and it is there to be used, after all — now that I see there is another option, I simply prefer the shorter, more elegant-looking main compared to the longer, clumsy-camel-looking alternative :slight_smile:

If it’s just one of those six of one, half-a-dozen of the other type of deals, I think I will stick with what’s working for me right now. If that’s OK :+1:

If you can share what you know to be a purely technical reason why my plugin’s implementation should prefer application.mainClassName over main , I would certainly bear it in mind.

It’s not a technical reason, it’s just what most users probably expect. The Application Plugin is a core Gradle plugin and using mainClassName is the standard way of configuring the main class.

Suppose that in the greeter.runner project of your demo there is a class example.RoadRunner beside the already existing example.Runner.

Let’s put the following code in greeter.runner/build.gradle:

application {
    mainClassName = 'example.RoadRunner'

Now let’s run:

./gradlew :greeter.runner:run

I would expect that example.RoadRunner is executed. Instead, the example.Runner is executed, because this is the class configured in the mrjar block. For me, this as a violation of the principle of least surprise (so, not a technical issue).

P.S. Don’t forget to put the plugin code on GitHub.

1 Like

I completely agree with @siordache, except that I think that this is a technical issue; change management, preventing possibilities for conflicting states, and simplifying setups are indeed technical issues.

I strongly suggest following his advice of using application.mainClassName and doing away with mrJar.main.

I really don’t see any need for mrJar {}, except for allowing users to omit from the mrjar (& possibly from compilation) source sets whose file system paths match the convention of this plugin.

1 Like

Thanks both @siordache and @rgoldberg. Your feedback is very valuable and sincerely appreciated.

Think of all the Maven „fanboys“ who hate on Gradle. They dislike Gradle for reasons other than purely technical reasons. They dislike Gradle merely because it does not do „what most Maven users probably expect“.

Do you, therefore, agree with the Maven „fanboys“ too then? :wink:

My point is: Sometimes, not doing „what most users probably expect“ turns out to be a good thing.

Right now, nobody’s using the plugin except me. If a sufficient number of people start using it, and they all clamor for certain changes, then that’s a horse of a different color.

But I totally appreciate your valuable feedback. I can’t thank you both enough :+1:

I take your point, @siordache. Thanks. And I’ve been giving it some thought. Would you be less surprised — hypothetically speaking — if that intentional design feature of mrJar was documented, in as obvious a way as possible, as something to be aware of?

I’m relieved that I finally solved #10412 with v0.0.7. But it’s „solved“ in the sense that it doesn’t happen anymore; unfortunately, not in the sense that I figured out Why it happens. Some things we can just never know I guess :slight_smile:

I’ve created another demo repo to confirm that the fix doesn’t break anything in the previously-tested functionality. This demo is mrJar’s take on the Gradle Guide’s Building Java 9 Modules storyteller application.

From the new v0.0.9 git repo:

As of v0.0.9, the mrJar Plugin Supports Multi-module Grouping, Compilation and Packaging with --module-source-path. Also, Running JavaFX Applications

This project is based on an example from @paulbakker’s and @sandermak’s Java 9 Modularity examples repository. It demonstrates the following:

  1. mrJar’s support for --module-source-path
  2. mrJar’s support for JavaFX applications
  3. mrJar’s support for running modular applications from the command line
  4. How mrJar makes it easy to migrate a modular service-based project originally built and ran with shell scripts, to instead be built more simply and ran more conveniently using a build tool
  5. How easily mrJar makes compiling and packaging projects with dozens of modules

For the release of v0.0.10, I’m sharing these 10 easy steps on how to refactor a non-module plugin, into a plugin that is both a fully-fledged JPMS module and a Multi-Release JAR File, all rolled into one:

mrJar Meets Java 9 Modularity

This project uses the code in @paulbakker’s and @sandermak’s gradle-modules-plugin repository to demonstrate the usage and functionality of the mrJar plugin. It demonstrates the following:

  1. mrJar’s support for transforming a project that is not a module, into a project that is a module
  2. mrJar’s support for creating Modular Multi-Release JAR Files
  3. mrJar’s ability to augment other plugins with functionality they don’t themselves implement; and not conflict with those plugins even though they share similar functionality that is also implemented in mrJar

These are the steps I took to turn the gradle-modules-plugin into a plugin that is a module:

Please critique it? And let me know if I’ve overlooked anything? Or if I’ve gotten anything ridiculously wrong? TIA :+1:

--release only applies to javac, @rdoldberg . And, as you know, javac is a compile-time tool. Those environment variables have nothing to do with javac . So they have nothing to do with --release.

What you don’t know, is those environment variables are an implementation detail related to runtime functionality. There is no analogous flag or -D switch to tell java: „I know you’re java 9, but I want you to run this application as if you were java N“.

If you want to run java N then you need to have java N installed on your machine. And mrJar can only run a particular java N if it’s told in advance where it is.

By the way, java N in this context is any version of java different from the one Gradle is running in already.

I’m sharing this with you because I will be focusing on that particular implementation detail for v0.0.11. So if you can share any suggestions for specifying different java runtimes in some way other than setting environment variables, then I’d jump on changing that particular implementation detail. And promptly. TIA.

Hey @tlinkowski? @siordache? A little help, please?

From the issues tab of the gradle-modules-plugin repo, this solution I offered to a guy earlier today has been deleted:

Skipped patching javax.activation-1.2.0.jar into activation
Skipped patching guice-multi-bindings-4.0.jar into
error: the unnamed module reads package javax.activation from both activation and java.activation
error: the unnamed module reads package from both and guice.multibindings

Hey @chriskessel, Interestingly I came across the exact same error when I was putting together this demonstration repo. But for different pkgs/modules.

What resolved that error for me in my demo project, was affixing an exclude on one of the two dependencies that contain the split package. I had to play around with excluding both dependencies (one at a time, of course) before I eventually hit the right one that got rid of the error.

Take a look at the dependencies{ } block of the build.gradle file in the demo project I link to above.

Please let me know how that works out for you? TIA.

What’s that about? :astonished:

Hey @lingocoder, your answer is where it was, I don’t see anything to have been deleted.

That might be the convention in Gradle World. But I move more in the wider Java World rather than exclusively in Gradle World. And I’ve actually never seen that particular directory hierarchy anywhere other than in Gradle World. And even then, only occasionally.

Better for who though, @rgoldberg? In my opinion the directory structure I’ve opted for is better. For me. Because A) Why not? And B) I’m pretty sure it’s more common in the wider Java World than most alternatives. That opinion is shared by at least

  1. Oracle devs
  2. Jersey devs
  3. JBoss devs
  4. Elasticsearch devs
  5. Game engine devs
  6. Apache devs
  7. These guys

Ah! You’re right @tlinkowski! My mistake. Sorry :relieved:

I have a bad habit of making that sort of hasty mistake. Which is the very reason why I’ve reached out for help from all you Gradle Gurus to critique my plugin’s functionality. I’m forever overlooking functionality that works perfectly on my machine, but that other people out there are able to spot as not functioning correctly in their environments.

I suspect peoples’ response to that would be: „You should be writing tests!“. Believe it or not, the mrJar plugin project has close to 70 tests at last count. But things sometimes slip through undetected by tests.

Exercising mrJar with some of the Java 9 Modularity example projects, however, has been a tremendous help in exposing a few things I had overlooked previously.

So please tell Messieurs Bakker and Mak I’m grateful for their book examples for being such valuable benchmarks :+1:

mrJar v0.0.11 implements per-release unit testing capability. A very valuable thing to be able to do when distributing Jigsaw modules as Multi-Release JAR Files.

In the Multi-Release JAR File, per-release unit test scenario, you write one test class to exercise the original main source set class. And then that same one test class is reused to exercise each newer release-specific class. Each class will run in its respective release-specific runtime.

To demonstrate v0.0.11’s per-release unit testing feature, I’ve added one test ( and two different release-targeted classes ( to the gradle-modules-plugin demonstration repo. When that test is ran in that project, you can observe from its stdout that each release-specific class it exercises detects and reports which release of the runtime they’re in.

The motivation for the per-release unit test use case is: You want to verify that the functionality of the newer release versions behave exactly like the more stable previous release.

You tell mrJar which runtimes to run, per-release, with environment variables. The mrJar usage page explains.

In my short time as a member of the Gradle community, I feel like I’ve gone above and beyond the call of duty to help others in the community find solutions to their problems. I assume that’s what people mean when they say: „Paying it forward“?

Up until now, I’ve hesitated playing the tit-for-tat card. But I need a little help myself right now. So I’m calling in favors from people I’ve helped here before. Including those who I’ve at least tried to help:

• … (continued below)

The specific help I’m requesting is for y’all to please review and critique the functionality of the mrJar plugin. Even more specifically:

  1. Perform the steps of the usage instructions
    • either on a brand new project (probably simpler/quicker?)
    • or on an existing project
  2. Capture all output of the build with either:
    • build scans (idealy)
    • console reports of successful builds
    • console reports of failed builds
    • stacktraces
  3. Share what you observed in 2 either:
    • here in the post
    • or PM me if you prefer

If those three easy steps seem like too much effort, then I will settle for y’all simply cloning any of these ready-made demo repos and following the steps in their README:

Convert a plugin that’s not a module, into a plugin that is an explicit module
Multi-module Grouping, Compilation and Packaging with --module-source-path. Also, Running JavaFX Applications
Build The Gradle Guide’s Fully-modular Java 9 Storyteller Application
Compile and package an example project from Bakker & Mak’s Java 9 Modularity book, into a modular MRJAR file

…then, please, just let me know what results you get from following the README steps as they’re currently written in the respective repo?

Pretty please? With sugar on top? :slight_smile: TIA :+1:

1 Like

• … (see above)
• … (continued below)

• … (see above)
TIA :+1: