• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

requires transitive and qualified exports in JPMS

 
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
TLDR: the "qualified exports" feature in JPMS is NOT overridden by requires transitive, contrary to the answer which gets marked correct on a relevant question in the 2nd Bonus Exam for the Sybex 815 book by Jeanne and Scott.  Lots of other related ideas are covered at length in this post, however.

Note: This logically belongs in both" Programmer Certification (OCPJP) Forum at JavaRanch" and in here.

Exposition:
I am complaining about what I consider a wrong answer on the second bonus (mock) exam from Jeanne and Scott's 815 prep book published by Sybex, but the behavior in question is specific to JPMS/Project Jigsaw, not their examples.

First let me say I think that JPMS is pretty neat, and gets (much) less love because it came so late.
The JPMS material seems to be #1 on the "Why is this stuff on the Certification Exam?" complaint for the Java 11 exams.
People are using Maven, Gradle, Eclipse, NetBeans or Intellij for project management, dependencies and packaging, so it seems like one more pointless thing to learn to some.

I really don't understand the people saying "You don't need to know anything about it for Real Life", this isn't making sense to me, lots and lots of stuff in the Real World is going modular in this sense, I thought.

Regardless, this whole area is in scope for the OCJP 819 exam, so even if I didn't like it, I would be trying to learn it properly.

It is definitely one of the most tedious areas to practice, because you have to build many directories, place the sources correctly, get all the command line commands typed in right, a lot of hassle.

Another poster asked two easy questions regarding this material, right after I had suspended my OCJP studies for other requirements, and didn't get answered until I came back to it six months later.

Again, I understand these questions are a big hassle to set up experiments for, with lots of manual steps and command line machinations, but I went thru all that this morning because I am either fastidious or obsessive.  I conclude the answer given on the test is wrong.

First, the question and answer that is marked as correct:

Sybex 815 Mock Test 2 wrote:Question 2
tb584704.JaSE11PrgISG.be2.02
Suppose module puppy depends on module dog and module dog depends on module animal. Which fills in the blank to allow module puppy to access the animal.behavior package in module animal?
module animal {
__________________________
}
module dog {
  requires transitive animal;
}
module puppy {
  requires dog;
}
A. export animal.behavior for dog;
B. export animal.behavior for puppy;
C. export animal.behavior to dog;
D. export animal.behavior to puppy;
E. exports animal.behavior for dog;
F. exports animal.behavior for puppy;
G. exports animal.behavior to dog;
H. exports animal.behavior to puppy;



Well, answers A thru F are obviously garbage, because they contain non-keywords just to see if we remember that the right ones are exports and to, that leaves a 50/50 chance for those who want to guess at it and move on.  I reasoned that the answer HAD to be H, because if it wasn't, it would render the whole notion of "qualified exports" effectively meaningless.  I marked H and moved on (for the moment).

Sybex 815 Mock Test 2 wrote:
You Answered Incorrectly.
Options A, B, C, and D are incorrect because the keyword is exports, not export. Options E and F are incorrect because you export a package to a module. Options G and H both are syntactically correct. However, option H would require the puppy module to require the animal module. Since it does not, option G is correct.



I wasn't buying it.  While I didn't remember seeing this interaction between requires transitive and the qualified exports <package> to <module> addressed in the sections dedicated to either, it felt like the spirit of each of them would mean "What part of  exports <package> to <module>  don't you understand?" if a simple requires transitive overrode that restriction.

Ugh!  Now to prove it!

Spoiler Alert:
The tedious experiment detailed below yielded the following results which I had expected and which disagrees with the answer marked as correct:
PS F:\Java\ModulesTests> javac --module-path mods -d puppy puppy/module-info.java puppy/puppy/adorable/CutePuppy.java
puppy\puppy\adorable\CutePuppy.java:2: error: package animal.behavior is not visible
import animal.behavior.Behave;
            ^
 (package animal.behavior is declared in module animal, which does not export it to module puppy)
1 error


The annoying-to-set-up directory structure looks like this, where animal dog and puppy are the root directories for each of the three modules and mods holds all the module artifacts.
Note that the classes are Behave in package animal.behavior in module animal, Cute in package dog.cute in module dog, and CutePuppy in package puppy.adorable in module puppy:
PS F:\Java\ModulesTests> dir -s animal

   Directory: F:\Java\ModulesTests\animal

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----           9/30/2021 11:14 AM                animal
-a---           9/30/2021 11:34 AM            188 module-info.class
-a---           9/30/2021  1:32 PM             52 module-info.java

   Directory: F:\Java\ModulesTests\animal\animal\behavior

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           9/30/2021 11:34 AM            416 Behave.class
-a---           9/30/2021 11:33 AM            136 Behave.java

PS F:\Java\ModulesTests> dir -s dog

   Directory: F:\Java\ModulesTests\dog

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----           9/30/2021 11:26 AM                dog
-a---           9/30/2021  1:27 PM            185 module-info.class
-a---           9/30/2021 11:23 AM             65 module-info.java

   Directory: F:\Java\ModulesTests\dog\dog\cute

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           9/30/2021  1:27 PM            402 Cute.class
-a---           9/30/2021  1:31 PM            119 Cute.java

PS F:\Java\ModulesTests> dir -s puppy

   Directory: F:\Java\ModulesTests\puppy

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----           9/30/2021 11:28 AM                puppy
-a---           9/30/2021 11:31 AM            164 module-info.class
-a---           9/30/2021 11:03 AM             33 module-info.java

   Directory: F:\Java\ModulesTests\puppy\puppy\adorable

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           9/30/2021 11:31 AM            444 CutePuppy.class
-a---           9/30/2021 11:36 AM            199 CutePuppy.java


I am surprised that javac didn't fail with a cuteness overload error, but I didn't write the question, let's push on:
The sources in question be looking like this:
PS F:\Java\ModulesTests> get-content animal/animal/behavior/Behave.java


PS F:\Java\ModulesTests> get-content animal/module-info.java


And our lovely dog module:
PS F:\Java\ModulesTests> get-content dog/dog/cute/Cute.java


and its module-info:
PS F:\Java\ModulesTests> get-content dog/module-info.java


Lastly, our adorable puppy module in its puppy.adorable package:
PS F:\Java\ModulesTests> get-content puppy/puppy/adorable/CutePuppy.java


PS F:\Java\ModulesTests> get-content puppy/module-info.java


The annoying-to-type commands to build all this stuff...

animal first:
PS F:\Java\ModulesTests> javac --module-path mods -d animal animal/module-info.java animal/animal/behavior/Behave.java

Jar 'er up:
PS F:\Java\ModulesTests> jar -cfv mods/animal.jar -C animal .

Now Do Dog:
PS F:\Java\ModulesTests> javac --module-path mods -d dog dog/module-info.java dog/dog/cute/Cute.java

Jar the Dog:
PS F:\Java\ModulesTests> jar -cfv mods/dog.jar -C dog .

All this so we can check the Puppy Answer!  KerBlam!!

PS F:\Java\ModulesTests> javac --module-path mods -d puppy puppy/module-info.java puppy/puppy/adorable/CutePuppy.java
puppy\puppy\adorable\CutePuppy.java:2: error: package animal.behavior is not visible
import animal.behavior.Behave;
            ^
 (package animal.behavior is declared in module animal, which does not export it to module puppy)
1 error



Okay, there is still a chance that the exports I added to Dog is the source of this problem.  Dog isn't required to export any package, so maybe somehow that is the cause of our woes.

Edit dog module's module-info.java to:
PS F:\Java\ModulesTests> get-content dog/module-info.java

rebuild the module:
PS F:\Java\ModulesTests> javac --module-path mods -d dog dog/module-info.java dog/dog/cute/Cute.java
PS F:\Java\ModulesTests> jar -cfv mods/dog.jar -C dog .


Still failing?  Yup!!
PS F:\Java\ModulesTests> javac --module-path mods -d puppy puppy/module-info.java puppy/puppy/adorable/CutePuppy.java
puppy\puppy\adorable\CutePuppy.java:2: error: package animal.behavior is not visible
import animal.behavior.Behave;
            ^
 (package animal.behavior is declared in module animal, which does not export it to module puppy)
1 error


Tediously prove that answer H is correct:
PS F:\Java\ModulesTests> get-content animal/module-info.java


Rebuild animal module again:
PS F:\Java\ModulesTests> javac --module-path mods animal/module-info.java animal/animal/behavior/Behave.java
PS F:\Java\ModulesTests> jar -cfv --module-path mods -C animal .


All this to prove I was right?  No, it is to help others.  That's the ticket:
Don't take a chance, rebuild and re-jar dog module (I don't think that is needed, but...)
PS F:\Java\ModulesTests> javac --module-path mods -d dog dog/module-info.java dog/dog/cute/Cute.java
PS F:\Java\ModulesTests> jar -cfv --module-path mods -C dog .


PS F:\Java\ModulesTests> javac --module-path mods -d puppy puppy/module-info.java puppy/puppy/adorable/CutePuppy.java
puppy\puppy\adorable\CutePuppy.java:2: error: package animal.behavior is not visible
import animal.behavior.Behave;
            ^


Hey, that didn't not work neither!!   ARRRRGGGGHHHH!!
Let's try this:
PS F:\Java\ModulesTests> get-content animal/module-info.java


Re-build, re-jar, rinse, repeat:
PS F:\Java\ModulesTests> javac --module-path mods -d puppy puppy/module-info.java puppy/puppy/adorable/CutePuppy.java
puppy\puppy\adorable\CutePuppy.java:2: error: package animal.behavior is not visible
import animal.behavior.Behave;
            ^
 (package animal.behavior is declared in module animal, which does not export it to module puppy)
1 error

NOOOOOOOOOOOOOOOOOOOO!!!  THAT DIDN'T WORK EITHER??!!  I'm losing it, man.

Let's get rid of the qualified export, I consider it over-qualified for this position:

Still no dice!
PS F:\Java\ModulesTests> javac --module-path mods -d puppy puppy/module-info.java puppy/puppy/adorable/CutePuppy.java
puppy\puppy\adorable\CutePuppy.java:2: error: package animal.behavior is not visible
import animal.behavior.Behave;
            ^
 (package animal.behavior is declared in module animal, which does not export it to module puppy)
1 error

Now I am doing something wrong, I can't get this to build at all, not with the exports in animal module saying:
exports animal.behavior to dog;
nor
exports animal.behavior to puppy;
nor
exports animal.behavior to dog, puppy;
nor
exports animal.behavior;


I give up for a while, I am burned here...how do we get this puppy to compile?
 
Jesse Silverman
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Okay, I was too burned before to see what I did wrong, but I have it working now, with the qualified exports to puppy and requires animal for puppy as well...

PS F:\Java\ModulesTests> jar -cfv mods/animal.jar -C animal/ .
added manifest
added module-info: module-info.class
adding: animal/(in = 0) (out= 0)(stored 0%)
adding: animal/behavior/(in = 0) (out= 0)(stored 0%)
adding: animal/behavior/Behave.class(in = 416) (out= 294)(deflated 29%)
adding: animal/behavior/Behave.java(in = 136) (out= 113)(deflated 16%)
adding: module-info.java(in = 54) (out= 51)(deflated 5%)
PS F:\Java\ModulesTests> jar --describe-module -f mods/animal.jar
animal jar:file:///F:/Java/ModulesTests/mods/animal.jar!/module-info.class
requires java.base mandated
qualified exports animal.behavior to puppy

PS F:\Java\ModulesTests> javac --module-path mods -d puppy puppy/module-info.java puppy/puppy/adorable/CutePuppy.java


It is also working with the qualified exports to puppy and NO requires animal in puppy's module-info.java!

I conclude that my answer, whatever it was, my frazzled brain can't recall it now, was correct, and the suggested answer was wrong.

Let's finish proving that again:
put animal's module-info.java back to:


Rebuild correctly (not enough energy to see what I did wrong in the earlier post) and...
PS F:\Java\ModulesTests> javac --module-path mods animal/module-info.java animal/animal/behavior/Behave.java
PS F:\Java\ModulesTests> jar -cfv mods/animal.jar -C animal/ .
added manifest
added module-info: module-info.class
adding: animal/(in = 0) (out= 0)(stored 0%)
adding: animal/behavior/(in = 0) (out= 0)(stored 0%)
adding: animal/behavior/Behave.class(in = 416) (out= 294)(deflated 29%)
adding: animal/behavior/Behave.java(in = 136) (out= 113)(deflated 16%)
adding: module-info.java(in = 52) (out= 49)(deflated 5%)
PS F:\Java\ModulesTests> jar --describe-module -f mods/animal.jar
animal jar:file:///F:/Java/ModulesTests/mods/animal.jar!/module-info.class
requires java.base mandated
qualified exports animal.behavior to dog


Okay, so that matches with their suggested answer, so now we try again and...
PS F:\Java\ModulesTests> javac --module-path mods -d puppy puppy/module-info.java puppy/puppy/adorable/CutePuppy.java
puppy\puppy\adorable\CutePuppy.java:2: error: package animal.behavior is not visible
import animal.behavior.Behave;
            ^
 (package animal.behavior is declared in module animal, which does not export it to module puppy)
1 error


Okay, okay, I see why some people hate this part of the OCJP preparation, it is all a bit tedious.
Not more tedious, or even equally tedious to stuff I did daily for decades, under pressure at work with managers asking "Is it in yet??" but people are spoiled now.

When stuff looks like this (what I thought the answer was):
PS F:\Java\ModulesTests> get-content animal/module-info.java
module animal {
       exports animal.behavior to puppy;
}
PS F:\Java\ModulesTests> get-content dog/module-info.java
module dog {
       requires transitive animal;
}
PS F:\Java\ModulesTests> notepad puppy/module-info.java
PS F:\Java\ModulesTests> get-content puppy/module-info.java
module puppy {
       requires dog;
}


This works!
PS F:\Java\ModulesTests> javac --module-path mods -d puppy puppy/module-info.java puppy/puppy/adorable/CutePuppy.java
PS F:\Java\ModulesTests> jar -cfv mods/puppy.jar -C puppy/ .
PS F:\Java\ModulesTests> java -p mods -m puppy/puppy.adorable.CutePuppy
Puppy being cute!
Oh, Behave!
Puppy being bad!
Oh, Behave!


I guess few people are willing to really work thru all the examples, or at least took the accepted answer in the Bonus Test at face value.

One thing I can say for myself, good or bad, if something looks wrong to me, I don't take it at face value, even if it is annoying to investigate!

Proof above.
 
Jesse Silverman
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Currently studying the 6th Chapter (Advanced JPMS) in the 816 book, I am going to make the following bold statement.

Under NO CIRCUMSTANCES can a MODULE that is NOT LISTED as one of the modules in the QUALIFIED EXPORT targets for a given PACKAGE, then access that package, unless it is an UNNAMED MODULE running from the CLASSPATH, which exhibits legacy behavior of everyone can access everything marked public.  In my mind, an UNNAMED MODULE isn't really a module at all.

If the MODULE trying to make use of this package is an AUTOMATIC MODULE, and itself has no module-info.java file, but is a MODULE nonetheless because it is being accessed from --module-path rather than --class-path the rule as listed in Table 6-3 of the Sybex 816 book says that the AUTOMATIC MODULE will not be able to access the package.

If it is a NAMED MODULE that is trying to access the package in question, also no go.

Only, an UNNAMED MODULE which is only living on the CLASSPATH, and exhibits legacy behavior because it really isn't a module at all (despite us calling it one) could access the package.

However, it is clear from the question that puppy is not intended to be considered as an UNNAMED MODULE.
 
author & internet detective
Posts: 41878
909
Eclipse IDE VI Editor Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Well yuck. I'm leaving it off the errata list because H is the best answer. (luckily we didn't put "none of the above" on this question!) I made a note though.
 
Jesse Silverman
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
H is the best answer, but G, which is not, is the one that gets marked correct!  That's what sent me off on the whole goose puppy chase!

This set a new record for how much I learned from disagreeing with a single mock test question, but I may have broke that record more recently.
 
Consider Paul's rocket mass heater.
reply
    Bookmark Topic Watch Topic
  • New Topic