Proposed API for JEP 259: Stack-Walking API
David Holmes
david.holmes at oracle.com
Thu Nov 19 02:32:36 UTC 2015
On 18/11/2015 10:26 PM, Peter Levart wrote:
>
>
> 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
I agree with Remi. "state" doesn't have to mean fields - there are
numerous existing examples in the JDK. Calling a method in a context
that is invalid is an illegal state to me. IllegalThreadStateException
would also work. But UnsupportedOperationException ... more of a stretch.
David
-----
> 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