Hi peter, Given this is a stack walker, considering the stack as a state seems natural to me.
Rémi ----- Mail original ----- > De: "Peter Levart" <peter.lev...@gmail.com> > À: "Remi Forax" <fo...@univ-mlv.fr>, "David Holmes" <david.hol...@oracle.com> > Cc: "Mandy Chung" <mandy.ch...@oracle.com>, "OpenJDK Dev list" > <core-libs-dev@openjdk.java.net> > Envoyé: Mercredi 18 Novembre 2015 13:26:32 > Objet: Re: Proposed API for JEP 259: Stack-Walking API > > > > On 11/18/2015 12:22 PM, Remi Forax wrote: > > ----- Mail original ----- > >> De: "David Holmes" <david.hol...@oracle.com> > >> À: "Mandy Chung" <mandy.ch...@oracle.com>, "Peter Levart" > >> <peter.lev...@gmail.com> > >> Cc: "OpenJDK Dev list" <core-libs-dev@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.lev...@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 > >>> > >