You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by re...@apache.org on 2021/09/01 00:35:56 UTC
[cxf] branch 3.4.x-fixes updated: CXF-8578: Bridge methods for
covariant return types cannot be invoked on client proxies (#842)
This is an automated email from the ASF dual-hosted git repository.
reta pushed a commit to branch 3.4.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/3.4.x-fixes by this push:
new 41cba7f CXF-8578: Bridge methods for covariant return types cannot be invoked on client proxies (#842)
41cba7f is described below
commit 41cba7f66fc339107fd7544019635535fdb60417
Author: Andriy Redko <dr...@gmail.com>
AuthorDate: Tue Aug 31 17:47:09 2021 -0400
CXF-8578: Bridge methods for covariant return types cannot be invoked on client proxies (#842)
(cherry picked from commit 7dbef5304979fd2e864e0b9643d9e1c5e7da163b)
---
.../org/apache/cxf/jaxrs/utils/ResourceUtils.java | 22 ++++-
.../apache/cxf/jaxrs/utils/ResourceUtilsTest.java | 104 +++++++++++++++++++++
.../jaxrs/client/JAXRSClientFactoryBeanTest.java | 16 ++++
.../org/apache/cxf/jaxrs/resources/SuperBook.java | 44 +++++++++
.../apache/cxf/jaxrs/resources/SuperBookStore.java | 74 +++++++++++++++
5 files changed, 255 insertions(+), 5 deletions(-)
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java
index 10119d7..04e4c5c 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java
@@ -322,15 +322,15 @@ public final class ResourceUtils {
MethodDispatcher md = new MethodDispatcher();
Class<?> serviceClass = cri.getServiceClass();
- final Set<Method> annotatedMethods = new HashSet<>();
+ final Map<Method, Method> annotatedMethods = new HashMap<>();
for (Method m : serviceClass.getMethods()) {
if (!m.isBridge() && !m.isSynthetic()) {
//do real methods first
Method annotatedMethod = AnnotationUtils.getAnnotatedMethod(serviceClass, m);
- if (!annotatedMethods.contains(annotatedMethod)) {
+ if (!annotatedMethods.containsKey(annotatedMethod)) {
evaluateResourceMethod(cri, enableStatic, md, m, annotatedMethod);
- annotatedMethods.add(annotatedMethod);
+ annotatedMethods.put(annotatedMethod, m);
}
}
}
@@ -338,14 +338,26 @@ public final class ResourceUtils {
if (m.isBridge() || m.isSynthetic()) {
//if a bridge/synthetic method isn't already mapped to something, go ahead and do it
Method annotatedMethod = AnnotationUtils.getAnnotatedMethod(serviceClass, m);
- if (!annotatedMethods.contains(annotatedMethod)) {
+ if (!annotatedMethods.containsKey(annotatedMethod)) {
evaluateResourceMethod(cri, enableStatic, md, m, annotatedMethod);
- annotatedMethods.add(annotatedMethod);
+ annotatedMethods.put(annotatedMethod, m);
+ } else {
+ // Certain synthetic / bridge methods could be quite useful to handle
+ // methods with co-variant return types, especially when used with client proxies,
+ // see please: https://blogs.oracle.com/sundararajan/covariant-return-types-in-java
+ bindResourceMethod(md, m, annotatedMethods.get(annotatedMethod));
}
}
}
cri.setMethodDispatcher(md);
}
+
+ private static void bindResourceMethod(MethodDispatcher md, Method m, Method bound) {
+ final OperationResourceInfo ori = md.getOperationResourceInfo(bound);
+ if (ori != null && !ori.getMethodToInvoke().equals(m)) {
+ md.bind(ori, bound, m);
+ }
+ }
private static void evaluateResourceMethod(ClassResourceInfo cri, boolean enableStatic, MethodDispatcher md,
Method m, Method annotatedMethod) {
diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/ResourceUtilsTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/ResourceUtilsTest.java
index e470f44..f3e89b3 100644
--- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/ResourceUtilsTest.java
+++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/ResourceUtilsTest.java
@@ -21,6 +21,7 @@ package org.apache.cxf.jaxrs.utils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -48,12 +49,14 @@ import org.apache.cxf.jaxrs.model.UserResource;
import org.apache.cxf.jaxrs.resources.Book;
import org.apache.cxf.jaxrs.resources.BookInterface;
import org.apache.cxf.jaxrs.resources.Chapter;
+import org.apache.cxf.jaxrs.resources.SuperBook;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class ResourceUtilsTest {
@@ -167,6 +170,76 @@ public class ResourceUtilsTest {
assertNotNull(ori);
assertEquals("GET", ori.getHttpMethod());
}
+
+ @Test
+ public void testClassResourceInfoWithBridgeMethod() throws Exception {
+ ClassResourceInfo cri =
+ ResourceUtils.createClassResourceInfo(ExampleBridgeImpl.class, ExampleBridgeImpl.class, true, true);
+ assertNotNull(cri);
+ assertEquals(1, cri.getMethodDispatcher().getOperationResourceInfos().size());
+
+ Method m = ExampleBridgeImpl.class.getMethod("get");
+ OperationResourceInfo ori = cri.getMethodDispatcher().getOperationResourceInfo(m);
+ assertNotNull(ori);
+ assertEquals("GET", ori.getHttpMethod());
+
+ m = Arrays
+ .stream(ExampleBridgeImpl.class.getMethods())
+ .filter(method -> method.getName().equals("get"))
+ .filter(Method::isBridge)
+ .findAny()
+ .orElse(null);
+
+ ori = cri.getMethodDispatcher().getOperationResourceInfo(m);
+ assertNotNull(ori);
+ assertEquals("GET", ori.getHttpMethod());
+ }
+
+ @Test
+ public void testGenericClassResourceInfoWithBridgeMethod() throws Exception {
+ ClassResourceInfo cri = ResourceUtils.createClassResourceInfo(GenericExampleBridgeImpl.class,
+ GenericExampleBridgeImpl.class, true, true);
+ assertNotNull(cri);
+ assertEquals(1, cri.getMethodDispatcher().getOperationResourceInfos().size());
+
+ Method m = GenericExampleBridgeImpl.class.getMethod("get");
+ OperationResourceInfo ori = cri.getMethodDispatcher().getOperationResourceInfo(m);
+ assertNotNull(ori);
+ assertEquals("GET", ori.getHttpMethod());
+
+ m = Arrays
+ .stream(GenericExampleBridgeImpl.class.getMethods())
+ .filter(method -> method.getName().equals("get"))
+ .filter(Method::isBridge)
+ .findAny()
+ .orElse(null);
+
+ ori = cri.getMethodDispatcher().getOperationResourceInfo(m);
+ assertNotNull(ori);
+ assertEquals("GET", ori.getHttpMethod());
+ }
+
+ @Test
+ public void testGenericClassResourceInfo() throws Exception {
+ ClassResourceInfo cri = ResourceUtils.createClassResourceInfo(GenericExampleImpl.class,
+ GenericExampleImpl.class, true, true);
+ assertNotNull(cri);
+ assertEquals(1, cri.getMethodDispatcher().getOperationResourceInfos().size());
+
+ Method m = GenericExampleImpl.class.getMethod("get");
+ OperationResourceInfo ori = cri.getMethodDispatcher().getOperationResourceInfo(m);
+ assertNotNull(ori);
+ assertEquals("GET", ori.getHttpMethod());
+
+ m = Arrays
+ .stream(GenericExampleImpl.class.getMethods())
+ .filter(method -> method.getName().equals("get"))
+ .filter(Method::isBridge)
+ .findAny()
+ .orElse(null);
+
+ assertNull(m);
+ }
@Path("/synth-hello")
protected interface SyntheticHelloInterface<T> {
@@ -301,6 +374,13 @@ public class ResourceUtilsTest {
@GET
Book get();
}
+
+ @Path("example")
+ public interface GenericExample<T extends Book> {
+
+ @GET
+ T get();
+ }
public static class ExampleImpl implements Example {
@@ -309,6 +389,30 @@ public class ResourceUtilsTest {
return null;
}
}
+
+ public static class ExampleBridgeImpl implements Example {
+
+ @Override
+ public SuperBook get() {
+ return null;
+ }
+ }
+
+ public static class GenericExampleImpl implements GenericExample<Book> {
+
+ @Override
+ public Book get() {
+ return null;
+ }
+ }
+
+ public static class GenericExampleBridgeImpl implements GenericExample<Book> {
+
+ @Override
+ public SuperBook get() {
+ return null;
+ }
+ }
@XmlRootElement
public static class OrderItem {
diff --git a/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBeanTest.java b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBeanTest.java
index 6089d5b..c705fcb 100644
--- a/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBeanTest.java
+++ b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/JAXRSClientFactoryBeanTest.java
@@ -44,6 +44,9 @@ import org.apache.cxf.jaxrs.resources.Book;
import org.apache.cxf.jaxrs.resources.BookInterface;
import org.apache.cxf.jaxrs.resources.BookStore;
import org.apache.cxf.jaxrs.resources.BookStoreSubresourcesOnly;
+import org.apache.cxf.jaxrs.resources.BookSuperClass;
+import org.apache.cxf.jaxrs.resources.SuperBook;
+import org.apache.cxf.jaxrs.resources.SuperBookStore;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
@@ -203,6 +206,19 @@ public class JAXRSClientFactoryBeanTest {
IProductResource productResourceElement = parts.elementAt("1");
assertNotNull(productResourceElement);
}
+
+ @Test
+ public void testBookAndBridgeMethods() throws Exception {
+ SuperBookStore superBookResource = JAXRSClientFactory.create("http://localhost:9000",
+ SuperBookStore.class);
+ assertNotNull(superBookResource);
+
+ Book book = ((BookSuperClass)superBookResource).getNewBook("id4", true);
+ assertNotNull(book);
+
+ SuperBook superBook = (SuperBook)superBookResource.getNewBook("id4", true);
+ assertNotNull(superBook);
+ }
@Test
public void testVoidResponseAcceptWildcard() throws Exception {
diff --git a/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/resources/SuperBook.java b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/resources/SuperBook.java
new file mode 100644
index 0000000..79120aa
--- /dev/null
+++ b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/resources/SuperBook.java
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.jaxrs.resources;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+
+@XmlRootElement(name = "SuperBook")
+public class SuperBook extends Book {
+ private long superId;
+ public SuperBook() {
+ }
+
+ public SuperBook(String name, long id, long superId) {
+ super(name, id);
+ this.superId = superId;
+ }
+
+ public void setSuperId(long i) {
+ superId = i;
+ }
+
+ public long getSuperId() {
+ return superId;
+ }
+
+}
diff --git a/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/resources/SuperBookStore.java b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/resources/SuperBookStore.java
new file mode 100644
index 0000000..ffdd51c
--- /dev/null
+++ b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/resources/SuperBookStore.java
@@ -0,0 +1,74 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.cxf.jaxrs.resources;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+@Path("/bookstore/")
+public class SuperBookStore extends BookSuperClass implements BookInterface {
+ public SuperBookStore() {
+ }
+
+ public SuperBook getBook(String id) {
+ return null;
+ }
+
+ @Override
+ public SuperBook getNewBook(String id, Boolean isNew) {
+ return null;
+ }
+
+ @POST
+ @Path("/books")
+ @Consumes(MediaType.APPLICATION_XML)
+ public void addBook(Book book) {
+ }
+
+ @PUT
+ @Path("/books/")
+ public Response updateBook(SuperBook book) {
+ return null;
+ }
+
+ @DELETE
+ @Path("/books/{bookId}/")
+ public Response deleteBook(@PathParam("bookId") String id) {
+ return null;
+ }
+
+ @Override
+ public String getDescription() {
+ return null;
+ }
+
+ public String getAuthor() {
+ return null;
+ }
+}
+
+