Seth,

The reason for reporting an error is to prevent the "wrong" class being used!

-- Jon

On 09/14/2018 10:14 AM, seth lytle wrote:
this behavior of throwing an error if the class is found twice on the
classpath strikes me as unusual - afaik, in all other cases, java is fine
with finding multiple implementations on the classpath and uses the first
found (similar to the unix path, which also allows overriding by providing
an explicit path) and this is really one of the powerful features of java -
allowing you to swap out one class for another seamlessly. and for rapid
prototyping, "java -cp target/classes:$cp src/myPackage/Stuff.java" runs
much faster than "mvn package -Dexec.mainClass=myPackage.Stuff", and doubly
so for mvnDebug










On Fri, Sep 14, 2018 at 3:33 AM, Peter Levart <peter.lev...@gmail.com>
wrote:

Hi Jaikiran,

Forwarding to compiler-dev as the core of source file launcher feature is
produced there...

The check for main class is performed after compilation (which actually
produces the main class name).

I think it would be possible to check for all classes compiled from the
source file after compilation without to much complication. The compilation
produces classes and stores them into a Map<String, byte[]>. The keySet()
of that map is a Set of compiled class names. Each of them could be tested
via .getResource() invoked upon the application class loader. The error
could even point to the URL of the conflicting class file that way...

Regards, Peter


On 09/14/2018 07:36 AM, Jaikiran Pai wrote:

Please consider this trivial code C.java:

public class C {
     public static void main(String[] args) throws Exception {
         System.out.println("main() execution started");
     }
}



ls

C.java

Similar to a previous discussion[1] while doing random testing, I ended
up compiling C.java explicitly using javac:


javac C.java
ls

C.java C.class

and then at a later date tried to use the source file launcher feature
of Java 11 (without realizing C.class was already present in the dir):


java C.java

This threw the error:

error: class found on application class path: C

Although the error isn't that clear for the reason I note in [2], having
run into this before, I was aware what this meant and deleted the
C.class and moved on. The important part here is that the source
launcher noticed this condition and aborted even before it auto
compiled(?) and launched and executed the main() of the program.

Now consider a slight modification to that source file:

public class C {
     public static void main(String[] args) throws Exception {
         System.out.println("main() execution started");
         final B b = new B();
         System.out.println("Done");
     }

     private static class B {

     }
}

Again at some point I compiled this explicitly using javac, so my
directory is (among other things):


ls

C$B.class    C.class        C.java

Then ran the source file launcher feature:


java C.java

error: class found on application class path: C

As expected, ran into the same previous error. As before, in order to
move on, deleted C.class:


rm C.class

but forgot to delete the nested static class that belonged to it. So the
directory now contained:


ls

C$B.class    C.java

Now used the source launcher feature again:


java C.java

This time it failed with:

main() execution started
Exception in thread "main" java.lang.IllegalAccessError: failed to
access class C$B from class C (C$B is in unnamed module of loader 'app';
C is in unnamed module of loader
com.sun.tools.javac.launcher.Main$MemoryClassLoader @1b1473ab)
     at C.main(C.java:4)

The error message isn't clear to pinpoint the issue, but at least the
reference to MemoryClassLoader was a hint that was enough for me to
understand where to look. It did take me a few minutes to realize that
C$B.class was lying around which I needed to remove too.

However, IMO, the important part here is that unlike in the first case
where the program itself wasn't launched and instead was aborted early,
in this case the program did get executed (notice the System.out.println
"main() execution started" message that got printed) and failed at runtime.

Would it be possible to make these two behaviours consistent and detect
such cases and abort early here too? Or would that add too much
complexity to this feature?

Finally, any thoughts on the error messages for this feature to make it
a bit easier in terms of debugging (classloading) issues like these?

[1] http://mail.openjdk.java.net/pipermail/jdk-dev/2018-June/001425.html
[2] http://mail.openjdk.java.net/pipermail/jdk-dev/2018-June/001438.html

-Jaikiran





Reply via email to