RFR: jsr166 jdk9 integration wave 2

Timo Kinnunen timo.kinnunen at gmail.com
Thu Dec 10 10:58:03 UTC 2015


This seems problematic to me, because it breaks the expectation that the most important part of the stack trace can be found at the bottom of the it. In this case, if someone subsequently wraps the original exception in their own Exception the causal chain then gets jumbled. I actually wish printStackTrace processed the exceptions in the main trace  in reverse compared to how it is now. If, rather than finding this at the end of a stack trace:

Caused by: org.osgi.framework.BundleException: Error starting module.
	at org.eclipse.osgi.container.Module.doStart(Module.java:580)
	at org.eclipse.osgi.container.Module.start(Module.java:439)
	at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:454)
	at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:107)
	... 57 more
Caused by: java.lang.NoClassDefFoundError: org/eclipse/jdt/debug/core/IJavaHotCodeReplaceListener
	at java.lang.Class.getDeclaredConstructors0(Native Method)
	at java.lang.Class.privateGetDeclaredConstructors(Unknown Source)
	[snip]
	at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:318)
	at org.eclipse.osgi.container.Module.doStart(Module.java:571)
	... 60 more
Caused by: java.lang.ClassNotFoundException: org.eclipse.jdt.debug.core.IJavaHotCodeReplaceListener cannot be found by org.eclipse.jdt.debug.ui_3.6.400.qualifier
	at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:432)
	at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:345)
	at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:337)
	at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:160)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	... 69 more

You would instead see it like this at the beginning of the stack trace:

java.lang.ClassNotFoundException: org.eclipse.jdt.debug.core.IJavaHotCodeReplaceListener cannot be found by org.eclipse.jdt.debug.ui_3.6.400.qualifier
	at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:432)
	at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:345)
	at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:337)
	at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:160)
	at java.lang.ClassLoader.loadClass(Unknown Source)
Causing: java.lang.NoClassDefFoundError: org/eclipse/jdt/debug/core/IJavaHotCodeReplaceListener
	at java.lang.Class.getDeclaredConstructors0(Native Method)
	at java.lang.Class.privateGetDeclaredConstructors(Unknown Source)
	[snip]
	at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:318)
	at org.eclipse.osgi.container.Module.doStart(Module.java:571)
Causing: org.osgi.framework.BundleException: Error starting module.
	at org.eclipse.osgi.container.Module.doStart(Module.java:580)
	at org.eclipse.osgi.container.Module.start(Module.java:439)
	at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:454)
	at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:107)
Causing: java.lang.ClassNotFoundException: An error occurred while automatically activating bundle org.eclipse.jdt.debug.ui (186).
	at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:116)
	at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:531)

See how even the handoff from one handler to another is now readily apparent? The right place where the next part of the trace should appear is quite obvious. 





-- 
Have a nice day, 
Timo

Sent from Mail for Windows 10



From: Peter Levart
Sent: Thursday, December 10, 2015 09:04
To: Martin Buchholz;Jason Mehrens;Doug Lea
Cc: core-libs-dev
Subject: Re: RFR: jsr166 jdk9 integration wave 2


Hi Martin,

On 12/10/2015 12:18 AM, Martin Buchholz wrote:
> I finally studied ForkJoinTask.getThrowableException - I was not aware
> that we were doing reflection based exception cloning.  In practice it
> probably muddles through, but it just seems too hacky to use in a java
> core library.  We're relying on common conventions, but in principle
> we are invoking random code with an unknown spec.  The stack traces
> created are fake in the sense that they are not created naturally, and
> the thrown exception may differ from the original in significant ways,
> most notably missing the message string.  With heroic efforts we can
> make it safer, e.g. examine the bytecode for the constructors we are
> invoking, and check whether they call the super constructors in the
> right way, but that will be a major engineering project by itself.

I assume the aim of exception cloning in 
ForkJoinTask.getThrowableException is to provide the user with a 
stack-trace that originates in its own thread. This helps trace the 
origin of the exception in case it's handled way up in the call stack. 
In addition, the original stack trace is also preserved (original 
exception is added as a cause).

This case does not suffer from the visibility of original exception 
before ForkJoinTask.getThrowableException is called, so perhaps the aim 
could be achieved in a slightly different manner: just return the 
original exception, but add a newly constructed 
CrossThreadPropagationException as a suppressed exception to it. 
Printing such augmented original exception would then reveal both stack 
traces. What do you think?

>
> For CompletableFuture.whenComplete: I also keep moving in the
> direction of less magic.  It is perfectly possible for a well-written
> whenComplete action to catch all Throwables, add the source exception
> as a suppressed exception, and rethrow.  In fact, perhaps people have
> already tried this and discovered we incorrectly throw the source
> exception.  I think we should really accept our mistake and switch to
> throwing the action exception rather than the source exception.
> Perhaps we should only call addSuppressed if the source exception is
> not already in the list of suppressed exceptions.

Less is more. I would even not bother with the additional logic of 
conditionally adding source exception to the list of suppressed 
exceptions. I doubt many codes out there do that in the whenComplete 
handler since such exception was ignored up until now. And if anybody 
does that, she will get the source exception listed twice - not a big 
deal. If javadoc describes the behavior, this will not happen frequently.

Regards, Peter

>
>
> On Thu, Dec 3, 2015 at 9:54 AM, Jason Mehrens <jason_mehrens at hotmail.com> wrote:
>> Hi Peter,
>>
>> I've done this trick before to perform Throwable cloning.  You have to hunt for the constructors in this order:
>> 1. Walk the type of the cause and super types by calling getConstructor(String, type of cause). (java.io.WriteAbortedException and javax.mail.MessagingException)
>> 2. Walk the type of the cause and super types by calling getConstructor(type of cause, String)  (I've seen this but, I can't seem to find an example)
>> 3. getConstructor(String) + initCause (java.io.InvalidObjectException)
>> 4. Walk the type of the cause and super types by calling getConstructor(type of cause). (java.awt.print.PrinterIOException)
>> 5. getConstructor() + initCause.  (java.nio.BufferOverflowException)
>> 6. Look at the return type of methods declared in the cause type and assume there is a constructor matching the type or no repeating types. (java.nio.charset.MalformedInputException, java.lang.EnumConstantNotPresentException, java.util.regex.PatternSyntaxException)
>>
>> In the end all of that can still fail.  Exceptions can be private subclasses of public classes so that gets interesting with reflection too.
>>
>> Jason
>>
>>
>> ===========
>> What about the 4th option (keep current behavior, but try the
>> best-effort with reflection to make new exception of the same type):
>>
>> ...
>>               } catch (Throwable ex) {
>>                   if (x == null) {
>>                       x = ex;
>>                   } else {
>>                       // try to create new exception with same:
>>                       // type, cause, suppressed exceptions and
>> stack-trace...
>>                       Throwable nx;
>>                       Class<?> xClass = x.getClass();
>>                       try {
>>                           Constructor<?> xConstr =
>> xClass.getConstructor(Throwable.class);
>>                           nx = (Throwable) xConstr.newInstance(x.getCause());
>>                       } catch (Exception e1) {
>>                           try {
>>                               nx = (Throwable) xClass.newInstance();
>>                               nx.initCause(x.getCause());
>>                           } catch (Exception e2) {
>>                               // no luck
>>                               nx = null;
>>                           }
>>                       }
>>                       if (nx != null) {
>>                           // inherit stack-trace of original exception
>>                           nx.setStackTrace(x.getStackTrace());
>>                           // inherit suppressed exceptions
>>                           for (Throwable sx : x.getSuppressed()) {
>>                               nx.addSuppressed(sx);
>>                           }
>>                           // add 'ex' as a final suppressed exception
>>                           nx.addSuppressed(ex);
>>                           x = nx;
>>                       }
>>                   }
>>               }
>>               completeThrowable(x, r);
>>
>> ...
>>
>>
>>
>> Note that such code and similar code in
>> ForkJoinTask.getThrowableException will probably have to be modified for
>> jigsaw to include dynamic addition of read-edge to the module of
>> exception class...
>>
>> Regards, Peter
>>






More information about the core-libs-dev mailing list