You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by db...@apache.org on 2021/05/03 05:28:58 UTC

[tomee-jakarta] 06/06: TCK fixes reapplied

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 <da...@gmail.com>
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)) {