DEV Community

Allen D. Ball
Allen D. Ball

Posted on • Edited on • Originally published at blog.hcf.dev

2 1

Adding Support to Java InvocationHandler Implementations for Interface Default Methods

Java 8 introduced default methods to interfaces. Existing InvocationHandler implementations will not invoke default interface methods. This short article documents the necessary changes. Note: It first describes the implementation based on a reading of the documents and then provides a working implementation for Java 8.

Given the InvocationHandler implementation:

    @Override
    public Object invoke(Object proxy,
                         Method method, Object[] argv) throws Throwable {
        Object result = null;
        /*
         * Logic to calculate result.
         */
        return result;
    }
Enter fullscreen mode Exit fullscreen mode

the Java 8 solution appears to be to extend the implementation to invoke any interface default methods through a MethodHandle:

    @Override
    public Object invoke(Object proxy,
                         Method method, Object[] argv) throws Throwable {
        Object result = null;
        Class<?> declaringClass = method.getDeclaringClass();

        if (method.isDefault()) {
            result =
                MethodHandles.lookup()
                .in(declaringClass)
                .unreflectSpecial(method, declaringClass)
                .bindTo(proxy)
                .invokeWithArguments(argv);
        } else {
            /*
             * Logic to calculate result.
             */
        }

        return result;
    }
Enter fullscreen mode Exit fullscreen mode

If the Method is "default" then the target interface method is invoked. Otherwise, the InvocationHandler implementation processes as before. Any interface default method should be invoked by:

  1. Finding the MethodHandles.Lookup through MethodHandles.lookup().in(declaringClass),
  2. Get a MethodHandle bypassing any overriding methods through .unreflectSpecial(method, declaringClass), and,
  3. Invoke the method on the proxy with .bindTo(proxy).invokeWithArguments(argv)

Unfortunately, this does not work if the declaringClass is not "private-accessible" to the caller (which is most of the time) resulting in:

Caused by: java.lang.IllegalAccessException: no private access for invokespecial: interface package1.SomeInterface, from package1.SomeInterface/public
    at java.lang.invoke.MemberName.makeAccessException(MemberName.java:850)
    at java.lang.invoke.MethodHandles$Lookup.checkSpecialCaller(MethodHandles.java:1572)
    at java.lang.invoke.MethodHandles$Lookup.unreflectSpecial(MethodHandles.java:1231)
    at package2.InvocationHandlerImpl.invoke(InvocationHandlerImpl.java:59)
Enter fullscreen mode Exit fullscreen mode

The actual Java 8 solution is:

    @Override
    public Object invoke(Object proxy,
                         Method method, Object[] argv) throws Throwable {
        Object result = null;
        Class<?> declaringClass = method.getDeclaringClass();

        if (method.isDefault()) {
            Constructor<MethodHandles.Lookup> constructor =
                MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);

            constructor.setAccessible(true);

            result =
                constructor.newInstance(declaringClass)
                .in(declaringClass)
                .unreflectSpecial(method, declaringClass)
                .bindTo(proxy)
                .invokeWithArguments(argv);
        } else {
            /*
             * Logic to calculate result.
             */
        }

        return result;
    }
Enter fullscreen mode Exit fullscreen mode

This will not work in Java 9+. In Java 9 and subsequent releases, the solution should be based on MethodHandles.Lookup.findSpecial() and/or MethodHandles.privateLookupIn().

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more