This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch coheigea/CXF-7330 in repository https://gitbox.apache.org/repos/asf/cxf.git
commit e035dc1c1f2f9f2b14f0d3dda413db66d795b38d Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Wed Oct 14 09:45:43 2026 +0100 CXF-7330: Require all type variable bounds to match when selecting providers --- .../apache/cxf/jaxrs/provider/ProviderFactory.java | 7 ++--- .../cxf/jaxrs/provider/ProviderFactoryTest.java | 31 +++++++++++++++++++++- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java index 18f0a9e1098..e89b66777db 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java @@ -393,11 +393,12 @@ public abstract class ProviderFactory { if (arg instanceof TypeVariable) { TypeVariable<?> var = (TypeVariable<?>) arg; Type[] bounds = var.getBounds(); - boolean isResolved = false; + // all the bounds must match, e.g. T extends Throwable & SomeInterface + boolean isResolved = true; for (Type bound : bounds) { Class<?> cls = InjectionUtils.getRawType(bound); - if (cls != null && (cls == Object.class || cls.isAssignableFrom(expectedType))) { - isResolved = true; + if (cls == null || (cls != Object.class && !cls.isAssignableFrom(expectedType))) { + isResolved = false; break; } } diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/ProviderFactoryTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/ProviderFactoryTest.java index da351ce764d..db993f2091d 100644 --- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/ProviderFactoryTest.java +++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/ProviderFactoryTest.java @@ -831,6 +831,16 @@ public class ProviderFactoryTest { assertNull(mapper2); } + @Test + public void testExceptionMapperWithMultipleBounds() { + ServerProviderFactory pf = ServerProviderFactory.getInstance(); + FaultInfoExceptionMapper<?> faultInfoMapper = new FaultInfoExceptionMapper<>(); + pf.registerUserProvider(faultInfoMapper); + assertSame(faultInfoMapper, + pf.createExceptionMapper(FaultInfoException.class, new MessageImpl())); + assertNull(pf.createExceptionMapper(RuntimeException.class, new MessageImpl())); + } + @Test public void testRegisterCustomResolver() throws Exception { ProviderFactory pf = ServerProviderFactory.getInstance(); @@ -1616,4 +1626,23 @@ public class ProviderFactoryTest { } } -} \ No newline at end of file + public interface FaultInfo { + String getFaultInfo(); + } + + public static class FaultInfoException extends RuntimeException implements FaultInfo { + private static final long serialVersionUID = 1L; + + @Override + public String getFaultInfo() { + return "fault"; + } + } + + public static class FaultInfoExceptionMapper<T extends Throwable & FaultInfo> implements ExceptionMapper<T> { + @Override + public Response toResponse(T exception) { + return Response.serverError().entity(exception.getFaultInfo()).build(); + } + } +}
