andymc12 closed pull request #480: [CXF-7922] Invoke default interface methods 
on client interfaces
URL: https://github.com/apache/cxf/pull/480
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git 
a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java 
b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
index a567f83a04f..6aaee1c623d 100644
--- 
a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
+++ 
b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
@@ -21,11 +21,16 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.lang.annotation.Annotation;
+import java.lang.invoke.MethodHandles;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
 import java.net.URI;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -154,6 +159,55 @@ private void initValuesMap(Object... varValues) {
         }
     }
 
+    private static class WrappedException extends Exception {
+        final Throwable wrapped;
+        WrappedException(Throwable wrapped) {
+            this.wrapped = wrapped;
+        }
+        Throwable getWrapped() {
+            return wrapped;
+        }
+    }
+
+    private static Object invokeDefaultMethod(Class<?> declaringClass, Object 
o, Method m, Object[] params)
+        throws Throwable {
+
+        try {
+            return AccessController.doPrivileged(new 
PrivilegedExceptionAction<Object>() {
+                @Override
+                public Object run() throws Exception {
+                    try {
+                        final MethodHandles.Lookup lookup = 
MethodHandles.publicLookup()
+                                .in(declaringClass);
+
+                        // force private access so unreflectSpecial can invoke 
the interface's default method
+                        final Field f = 
MethodHandles.Lookup.class.getDeclaredField("allowedModes");
+                        final int modifiers = f.getModifiers();
+                        if (Modifier.isFinal(modifiers)) {
+                            final Field modifiersField = 
Field.class.getDeclaredField("modifiers");
+                            modifiersField.setAccessible(true);
+                            modifiersField.setInt(f, modifiers & 
~Modifier.FINAL);
+                            f.setAccessible(true);
+                            f.set(lookup, MethodHandles.Lookup.PRIVATE);
+                        }
+
+                        return lookup.unreflectSpecial(m, declaringClass)
+                                     .bindTo(o)
+                                     .invokeWithArguments(params);
+                    } catch (Throwable t) {
+                        throw new WrappedException(t);
+                    }
+                }
+            });
+        } catch (PrivilegedActionException pae) {
+            Throwable wrapped = pae.getCause();
+            if (wrapped instanceof WrappedException) {
+                throw ((WrappedException)wrapped).getWrapped();
+            }
+            throw wrapped;
+        }
+    }
+
     /**
      * Updates the current state if Client method is invoked, otherwise
      * does the remote invocation or returns a new proxy if subresource
@@ -171,6 +225,9 @@ public Object invoke(Object o, Method m, Object[] params) 
throws Throwable {
         resetResponse();
         OperationResourceInfo ori = 
cri.getMethodDispatcher().getOperationResourceInfo(m);
         if (ori == null) {
+            if (m.isDefault()) {
+                return invokeDefaultMethod(declaringClass, o, m, params);
+            }
             reportInvalidResourceMethod(m, "INVALID_RESOURCE_METHOD");
         }
 
diff --git 
a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/CxfTypeSafeClientBuilderTest.java
 
b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/CxfTypeSafeClientBuilderTest.java
index b1e88558127..54be86ecd65 100644
--- 
a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/CxfTypeSafeClientBuilderTest.java
+++ 
b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/CxfTypeSafeClientBuilderTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.cxf.microprofile.client;
 
+import java.io.IOException;
 import java.net.URI;
 import java.net.URL;
 
@@ -163,6 +164,24 @@ public void testClientPropertiesAreSet() throws Exception {
             
WebClientUtil.getClientConfigFromProxy(client).getRequestContext().get("hello"));
     }
 
+    @Test
+    public void testCanInvokeDefaultInterfaceMethods() throws Exception {
+        MyClient client = RestClientBuilder.newBuilder()
+            .register(InvokedMethodClientRequestFilter.class)
+            .baseUri(new URI("http://localhost:8080/neverUsed";))
+            .build(MyClient.class);
+        assertEquals("defaultValue", client.myDefaultMethod(false));
+    }
+
+    @Test(expected = IOException.class)
+    public void testCanInvokeDefaultInterfaceMethodsWithException() throws 
Exception {
+        MyClient client = RestClientBuilder.newBuilder()
+            .register(InvokedMethodClientRequestFilter.class)
+            .baseUri(new URI("http://localhost:8080/neverUsed";))
+            .build(MyClient.class);
+        client.myDefaultMethod(true);
+        fail("Should have thrown IOException");
+    }
     private void fail(Response r, String failureMessage) {
         System.out.println(r.getStatus());
         fail(failureMessage);
diff --git 
a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/MyClient.java
 
b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/MyClient.java
index c3808456f6e..beafc977d75 100644
--- 
a/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/MyClient.java
+++ 
b/rt/rs/microprofile-client/src/test/java/org/apache/cxf/microprofile/client/mock/MyClient.java
@@ -18,6 +18,8 @@
  */
 package org.apache.cxf.microprofile.client.mock;
 
+import java.io.IOException;
+
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.core.Response;
@@ -26,4 +28,11 @@
 public interface MyClient {
     @GET
     Response get();
+
+    default String myDefaultMethod(boolean throwException) throws IOException {
+        if (throwException) {
+            throw new IOException("expected");
+        }
+        return "defaultValue";
+    }
 }


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to