This is an automated email from the ASF dual-hosted git repository. dblevins pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/tomee-jakarta.git
commit 9a737a0e805151842209d6704d77d136970ec964 Author: David Blevins <[email protected]> AuthorDate: Sun May 2 22:01:52 2021 -0700 TCK fixes reapplied --- .../cxf/jaxrs/provider/AbstractJAXBProvider.java | 14 +++- .../apache/cxf/jaxrs/provider/ProviderFactory.java | 98 ++++++++++++++++++++-- .../org/apache/cxf/jaxrs/utils/InjectionUtils.java | 50 ++++++----- .../org/apache/cxf/jaxrs/utils/JAXRSUtils.java | 18 +++- 4 files changed, 141 insertions(+), 39 deletions(-) diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/AbstractJAXBProvider.java b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/AbstractJAXBProvider.java index f2bee17..b94760a 100644 --- a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/AbstractJAXBProvider.java +++ b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/AbstractJAXBProvider.java @@ -42,6 +42,7 @@ import java.util.concurrent.ConcurrentHashMap; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.NoContentException; import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; import javax.ws.rs.ext.ContextResolver; @@ -68,6 +69,8 @@ import org.w3c.dom.Element; import org.xml.sax.helpers.DefaultHandler; +import com.ctc.wstx.exc.WstxEOFException; + import org.apache.cxf.annotations.SchemaValidation; import org.apache.cxf.common.jaxb.JAXBUtils; import org.apache.cxf.common.util.PackageUtils; @@ -719,7 +722,12 @@ public abstract class AbstractJAXBProvider<T> extends AbstractConfigurableProvid return sb; } - protected static void handleExceptionEnd(Throwable t, String message, boolean read) { + protected static void handleExceptionEnd(Throwable t, String message, boolean read) throws NoContentException { + if (t instanceof WstxEOFException && t.getMessage().startsWith("Unexpected EOF in prolog")){ + String noContent = new org.apache.cxf.common.i18n.Message("EMPTY_BODY", BUNDLE).toString(); + LOG.warning(noContent); + throw new NoContentException(noContent); + } Response.Status status = read ? Response.Status.BAD_REQUEST : Response.Status.INTERNAL_SERVER_ERROR; Response r = JAXRSUtils.toResponseBuilder(status) @@ -728,7 +736,7 @@ public abstract class AbstractJAXBProvider<T> extends AbstractConfigurableProvid : ExceptionUtils.toInternalServerErrorException(t, r); } - protected void handleJAXBException(JAXBException e, boolean read) { + protected void handleJAXBException(JAXBException e, boolean read) throws NoContentException { StringBuilder sb = handleExceptionStart(e); Throwable linked = e.getLinkedException(); if (linked != null && linked.getMessage() != null) { @@ -753,7 +761,7 @@ public abstract class AbstractJAXBProvider<T> extends AbstractConfigurableProvid handleExceptionEnd(t, message, read); } - protected void handleXMLStreamException(XMLStreamException e, boolean read) { + protected void handleXMLStreamException(XMLStreamException e, boolean read) throws NoContentException { StringBuilder sb = handleExceptionStart(e); handleExceptionEnd(e, sb.toString(), read); } diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java index c8a0aff..65c417c 100644 --- a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java +++ b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java @@ -39,6 +39,7 @@ import java.util.Set; import java.util.TreeMap; import java.util.logging.Logger; +import javax.annotation.Priority; import javax.ws.rs.Produces; import javax.ws.rs.core.Application; import javax.ws.rs.core.Configuration; @@ -77,6 +78,8 @@ import org.apache.cxf.jaxrs.utils.ResourceUtils; import org.apache.cxf.message.Message; import org.apache.cxf.message.MessageUtils; +import static javax.ws.rs.Priorities.USER; + public abstract class ProviderFactory { public static final String DEFAULT_FILTER_NAME_BINDING = "org.apache.cxf.filter.binding"; public static final String PROVIDER_SELECTION_PROPERTY_CHANGED = "provider.selection.property.changed"; @@ -662,6 +665,7 @@ public abstract class ProviderFactory { sortReaders(); sortWriters(); sortContextResolvers(); + sortParamConverters(); mapInterceptorFilters(readerInterceptors, readInts, ReaderInterceptor.class, true); mapInterceptorFilters(writerInterceptors, writeInts, WriterInterceptor.class, true); @@ -783,7 +787,9 @@ public abstract class ProviderFactory { contextResolvers.sort(new ContextResolverComparator()); } - + private void sortParamConverters() { + paramConverters.sort(new ParamConverterComparator()); + } @@ -853,9 +859,12 @@ public abstract class ProviderFactory { setProviders(true, false, userProviders.toArray()); } - private static class MessageBodyReaderComparator + static class MessageBodyReaderComparator implements Comparator<ProviderInfo<MessageBodyReader<?>>> { + private final GenericArgumentComparator classComparator = + new GenericArgumentComparator(MessageBodyReader.class); + public int compare(ProviderInfo<MessageBodyReader<?>> p1, ProviderInfo<MessageBodyReader<?>> p2) { MessageBodyReader<?> e1 = p1.getProvider(); @@ -870,7 +879,10 @@ public abstract class ProviderFactory { if (result != 0) { return result; } - result = compareClasses(e1, e2); + + final Class<?> class1 = ClassHelper.getRealClass(e1); + final Class<?> class2 = ClassHelper.getRealClass(e2); + result = classComparator.compare(class1, class2); if (result != 0) { return result; } @@ -878,19 +890,30 @@ public abstract class ProviderFactory { if (result != 0) { return result; } - return comparePriorityStatus(p1.getProvider().getClass(), p2.getProvider().getClass()); + + result = comparePriorityStatus(p1.getProvider().getClass(), p2.getProvider().getClass()); + if (result != 0) { + return result; + } + + return p1.getProvider().getClass().getName().compareTo(p2.getProvider().getClass().getName()); } } - private static class MessageBodyWriterComparator + static class MessageBodyWriterComparator implements Comparator<ProviderInfo<MessageBodyWriter<?>>> { + private final GenericArgumentComparator classComparator = + new GenericArgumentComparator(MessageBodyWriter.class); + public int compare(ProviderInfo<MessageBodyWriter<?>> p1, ProviderInfo<MessageBodyWriter<?>> p2) { MessageBodyWriter<?> e1 = p1.getProvider(); MessageBodyWriter<?> e2 = p2.getProvider(); - int result = compareClasses(e1, e2); + final Class<?> class1 = ClassHelper.getRealClass(e1); + final Class<?> class2 = ClassHelper.getRealClass(e2); + int result = classComparator.compare(class1, class2); if (result != 0) { return result; } @@ -903,13 +926,18 @@ public abstract class ProviderFactory { if (result != 0) { return result; } - + result = compareCustomStatus(p1, p2); if (result != 0) { return result; } - return comparePriorityStatus(p1.getProvider().getClass(), p2.getProvider().getClass()); + result = comparePriorityStatus(p1.getProvider().getClass(), p2.getProvider().getClass()); + if (result != 0) { + return result; + } + + return p1.getProvider().getClass().getName().compareTo(p2.getProvider().getClass().getName()); } } @@ -1136,7 +1164,7 @@ public abstract class ProviderFactory { // superclass should go last return -1; } - + // there is no relation between the types returned by the providers return 0; } @@ -1495,4 +1523,56 @@ public abstract class ProviderFactory { writerInterceptors = sortedWriterInterceptors; } + protected static class ParamConverterComparator implements Comparator<ProviderInfo<ParamConverterProvider>> { + + @Override + public int compare(final ProviderInfo<ParamConverterProvider> a, + final ProviderInfo<ParamConverterProvider> b) { + + /* + * Primary sort. Also takes care of sorting custom + * converters from system converters due to priority + * defaults + */ + int result = sortByPriority(a, b); + + /* + * Secondary sort as this list *will* change order + * once in a while between jvm restarts, which can + * have frustrating consequences for users who are + * expecting no change in behavior as they aren't + * changing their code. + */ + if (result == 0) { + result = sortByClassName(a, b); + } + + return result; + } + + public int sortByPriority(final ProviderInfo<ParamConverterProvider> a, + final ProviderInfo<ParamConverterProvider> b) { + final int aPriority = getPriority(a); + final int bPriority = getPriority(b); + + // Sort ascending as the priority with the lowest number wins + return Integer.compare(aPriority, bPriority); + } + + public int sortByClassName(final ProviderInfo<ParamConverterProvider> a, + final ProviderInfo<ParamConverterProvider> b) { + + // Sort ascending as the priority with the lowest number wins + return a.getProvider().getClass().getName().compareTo(b.getProvider().getClass().getName()); + } + + private int getPriority(final ProviderInfo<ParamConverterProvider> providerInfo) { + final Priority priority = providerInfo.getProvider().getClass().getAnnotation(Priority.class); + if (priority!=null) { + return priority.value(); + } + return providerInfo.isCustom() ? USER : USER + 1000; + } + } + } diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java b/transform/src/patch/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java index 5543093..dca074a 100644 --- a/transform/src/patch/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java +++ b/transform/src/patch/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java @@ -53,20 +53,20 @@ import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Application; -import javax.ws.rs.core.GenericEntity; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.PathSegment; -import javax.ws.rs.core.Request; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import javax.ws.rs.core.UriInfo; -import javax.ws.rs.ext.ContextResolver; -import javax.ws.rs.ext.ParamConverter; -import javax.ws.rs.ext.Providers; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.GenericEntity; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.PathSegment; +import jakarta.ws.rs.core.Request; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.SecurityContext; +import jakarta.ws.rs.core.UriInfo; +import jakarta.ws.rs.ext.ContextResolver; +import jakarta.ws.rs.ext.ParamConverter; +import jakarta.ws.rs.ext.Providers; import org.apache.cxf.common.classloader.ClassLoaderUtils; import org.apache.cxf.common.i18n.BundleUtils; @@ -537,15 +537,19 @@ public final class InjectionUtils { } private static RuntimeException createParamConversionException(ParameterType pType, Exception ex) { - // - // For path, query & matrix parameters this is 404, - // for others 400... - // - if (pType == ParameterType.PATH || pType == ParameterType.QUERY - || pType == ParameterType.MATRIX) { - return ExceptionUtils.toNotFoundException(ex, null); - } - return ExceptionUtils.toBadRequestException(ex, null); + /* + * Loosely related to the following section of the Jakarta REST specification: + * + * At least one of the acceptable response entity body media types is a supported output data + * format (see Section 3.5). If no methods support one of the acceptable response entity body + * media types an implementation MUST generate a NotAcceptableException (406 status) + * and no entity. + * + * Tested by: + * com.sun.ts.tests.jaxrs.ee.rs.ext.paramconverter.JAXRSClient + * atomicIntegerIsLazyDeployableAndThrowsErrorTest_from_standalone + */ + return ExceptionUtils.toNotAcceptableException(ex, null); } public static <T> Optional<ParamConverter<T>> getParamConverter(Class<T> pClass, diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java b/transform/src/patch/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java index f264b48..3f0073a 100644 --- a/transform/src/patch/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java +++ b/transform/src/patch/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java @@ -47,6 +47,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import javax.activation.DataSource; import javax.ws.rs.ClientErrorException; import javax.ws.rs.Consumes; import javax.ws.rs.HttpMethod; @@ -65,6 +66,7 @@ import javax.ws.rs.core.Cookie; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.NoContentException; import javax.ws.rs.core.PathSegment; import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; @@ -164,11 +166,11 @@ public final class JAXRSUtils { private static final ResourceBundle BUNDLE = BundleUtils.getBundle(JAXRSUtils.class); private static final String PATH_SEGMENT_SEP = "/"; private static final String REPORT_FAULT_MESSAGE_PROPERTY = "org.apache.cxf.jaxrs.report-fault-message"; - private static final String NO_CONTENT_EXCEPTION = "javax.ws.rs.core.NoContentException"; + private static final String NO_CONTENT_EXCEPTION = NoContentException.class.getName(); private static final String HTTP_CHARSET_PARAM = "charset"; private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0]; private static final Set<Class<?>> STREAMING_OUT_TYPES = new HashSet<>( - Arrays.asList(InputStream.class, Reader.class, StreamingOutput.class)); + Arrays.asList(InputStream.class, Reader.class, StreamingOutput.class, DataSource.class)); private JAXRSUtils() { } @@ -394,6 +396,8 @@ public final class JAXRSUtils { int methodMatched = 0; int consumeMatched = 0; + boolean resourceMethodsAdded = false; + boolean generateOptionsResponse = false; List<OperationResourceInfo> finalPathSubresources = null; for (Map.Entry<ClassResourceInfo, MultivaluedMap<String, String>> rEntry : matchedResources.entrySet()) { ClassResourceInfo resource = rEntry.getKey(); @@ -433,18 +437,21 @@ public final class JAXRSUtils { if (matchProduceTypes(acceptType, ori)) { candidateList.put(ori, map); added = true; + resourceMethodsAdded = true; break; } } } //CHECKSTYLE:ON + } else if ("OPTIONS".equalsIgnoreCase(httpMethod)) { + generateOptionsResponse = true; } } } LOG.fine(matchMessageLogSupplier(ori, path, httpMethod, requestType, acceptContentTypes, added)); } } - if (finalPathSubresources != null && pathMatched > 0 + if (finalPathSubresources != null && (resourceMethodsAdded || generateOptionsResponse) && !MessageUtils.getContextualBoolean(message, KEEP_SUBRESOURCE_CANDIDATES, false)) { for (OperationResourceInfo key : finalPathSubresources) { candidateList.remove(key); @@ -1184,7 +1191,10 @@ public final class JAXRSUtils { } else if (ResourceInfo.class.isAssignableFrom(clazz)) { o = new ResourceInfoImpl(contextMessage); } else if (ResourceContext.class.isAssignableFrom(clazz)) { - o = new ResourceContextImpl(contextMessage, contextMessage.getExchange().get(OperationResourceInfo.class)); + OperationResourceInfo operationResourceInfo = contextMessage.getExchange().get(OperationResourceInfo.class); + if (operationResourceInfo != null) { + o = new ResourceContextImpl(contextMessage, operationResourceInfo); + } } else if (Request.class.isAssignableFrom(clazz)) { o = new RequestImpl(contextMessage); } else if (Providers.class.isAssignableFrom(clazz)) {
