Source file launcher - Handling of pre-compiled classes of the source file

Jonathan Gibbons jonathan.gibbons at oracle.com
Fri Sep 14 17:49:24 UTC 2018


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.levart at 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
>>
>>
>>
>>



More information about the core-libs-dev mailing list