Proposed API for JEP 259: Stack-Walking API

Peter Levart peter.levart at gmail.com
Wed Nov 18 12:26:32 UTC 2015



On 11/18/2015 12:22 PM, Remi Forax wrote:
> ----- Mail original -----
>> De: "David Holmes" <david.holmes at oracle.com>
>> À: "Mandy Chung" <mandy.chung at oracle.com>, "Peter Levart" <peter.levart at gmail.com>
>> Cc: "OpenJDK Dev list" <core-libs-dev at openjdk.java.net>
>> Envoyé: Mercredi 18 Novembre 2015 08:58:56
>> Objet: Re: Proposed API for JEP 259: Stack-Walking API
>>
>> On 18/11/2015 8:42 AM, Mandy Chung wrote:
>>>> On Nov 17, 2015, at 2:09 PM, Peter Levart <peter.levart at gmail.com> wrote:
>>>>
>>>> I think that calling getCallerClass() from implementation of Runnable::run
>>>> should expect it to return a system class. It may be Thread.class or
>>>> ThreadPoolExecutor$Worker.class or anything actually.
>>>>
>>> I’m now convinced that it’s not a good idea to special case it.
>>> getCallerClass will simply return the caller frame (i.e. top-2) on the
>>> stack and throw UOE if there is no caller frame.  The user should call
>>> StackWalker::walk instead if this special case matters.
>> That sounds good to me too.
>>
>> David
> Looks good to me too if IllegalStateException is used instead of UnsupportedOperationException.
> UnsuppportedOperationException is used when the operation is not available, here, the same code can work or not depending how it is called.

But IllegalStateException is dependent on some state. There's no state 
involved here (in the sense "state" is characterized in Java). My 1st 
thought was an IllegalArgumentException. This requires some imagination 
to view the caller passed to the method as an implicit argument.

There's an obscure java.util.EmptyStackException but that is reserved 
for java.util.Stack operations.

If we consider the call stack to be part of the Thread state, then maybe 
java.lang.IllegalThreadStateException (a subclass of 
IllegalArgumentException) could be used...

Regards, Peter

> Runnable r = () -> System.out.println(stackWalker.getCallerClass());
> new Thread(r).start() // throw ISE
> r.run();  // prints main class
>
> Rémi
>
>>> How does this look?
>>>
>>> /**
>>>    * Gets the {@code Class} object of the caller invoking the method
>>>    * that calls this {@code getCallerClass} method.
>>>    *
>>>    * <p> Reflection frames, {@link java.lang.invoke.MethodHandle} and
>>>    * hidden frames are filtered regardless of the
>>>    * {@link Option#SHOW_REFLECT_FRAMES SHOW_REFLECT_FRAMES}
>>>    * and {@link Option#SHOW_HIDDEN_FRAMES SHOW_HIDDEN_FRAMES} options
>>>    * if this {@code StackWalker} has been configured.
>>>    *
>>>    * <p> This method throws {@code UnsupportedOperationException} if
>>>    * this {@code StackWalker} is not configured with
>>>    * {@link Option#RETAIN_CLASS_REFERENCE RETAIN_CLASS_REFERENCE} option
>>>    * or this method is called from the last frame on the stack,
>>>    * i.e. invoked from a JNI attached thread (
>>>    * for example, {@code static public void main} method launched by the
>>>    * {@code java} launcher).
>>>    *
>>>    * @apiNote
>>>    * For example, {@code Util::getResourceBundle} loads a resource bundle
>>>    * on behalf of the caller.  It calls this {@code getCallerClass} method
>>>    * to find the method calling {@code Util::getResourceBundle} and use the
>>>    caller's
>>>    * class loader to load the resource bundle. The caller class in this
>>>    example
>>>    * is the {@code MyTool} class.
>>>    *
>>>    * <pre>{@code
>>>    *     class Util {
>>>    *         private final StackWalker walker =
>>>    StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);
>>>    *         public ResourceBundle getResourceBundle(String bundleName) {
>>>    *             Class<?> caller = walker.getCallerClass();
>>>    *             return ResourceBundle.getBundle(bundleName,
>>>    caller.getClassLoader());
>>>    *         }
>>>    *     }
>>>    *
>>>    *     class MyTool {
>>>    *         private void init() {
>>>    *             ResourceBundle rb = Util.getResourceBundle("mybundle");
>>>    *         }
>>>    *     }
>>>    * }</pre>
>>>    *
>>>    * An equivalent way to find the caller class using the
>>>    * {@link StackWalker#walk walk} method is as follows
>>>    * (filtering the reflection frames, {@code MethodHandle} and hidden
>>>    frames
>>>    * not shown below):
>>>    * <pre>{@code
>>>    *     Optional<Class<?>> caller = walker.walk(s ->
>>>    *         s.map(StackFrame::getDeclaringClass)
>>>    *          .skip(2)
>>>    *          .findFirst());
>>>    * }</pre>
>>>    *
>>>    * When the {@code getCallerClass} method is called from a method that
>>>    * is the last frame on the stack, i.e. invoked from a JNI attached
>>>    thread,
>>>    * for example, {@code static public void main} method launched by the
>>>    * {@code java} launcher,
>>>    *
>>>    * @return {@code Class} object of the caller's caller invoking this
>>>    method.
>>>    *
>>>    * @throws UnsupportedOperationException if this {@code StackWalker}
>>>    *         is not configured with {@link Option#RETAIN_CLASS_REFERENCE
>>>    *         Option.RETAIN_CLASS_REFERENCE}.
>>>    * @throws UnsupportedOperationException if there is no caller frame, i.e.
>>>    *         when this {@code getCallerClass} method is called from a method
>>>    *         which is the last frame on the stack.
>>>    */
>>>
>>> Mandy
>>>




More information about the core-libs-dev mailing list