Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CXF-7996 Jakarta EE TCKs and compatibility #783

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -405,28 +405,23 @@ public <T> T readEntity(GenericType<T> genType) throws ProcessingException, Ille

@Override
public <T> T readEntity(Class<T> cls, Annotation[] anns) throws ProcessingException, IllegalStateException {
return doReadEntity(cls, cls, anns, true);
return doReadEntity(cls, cls, anns);
}

@Override
@SuppressWarnings("unchecked")
public <T> T readEntity(GenericType<T> genType, Annotation[] anns)
throws ProcessingException, IllegalStateException {
return doReadEntity((Class<T>) genType.getRawType(),
genType.getType(), anns, true);
genType.getType(), anns);
}

public <T> T doReadEntity(Class<T> cls, Type t, Annotation[] anns)
throws ProcessingException, IllegalStateException {
return doReadEntity(cls, t, anns, false);
}

public <T> T doReadEntity(Class<T> cls, Type t, Annotation[] anns, boolean closeAfterRead)
throws ProcessingException, IllegalStateException {

checkEntityIsClosed();
//according to javadoc, should close when is not buffered.
boolean shouldClose = !this.entityBufferred;
boolean shouldClose = !entityBufferred && !JAXRSUtils.isStreamingOutType(cls);

if (lastEntity != null && cls.isAssignableFrom(lastEntity.getClass())
&& !(lastEntity instanceof InputStream)) {
Expand Down Expand Up @@ -478,7 +473,7 @@ public <T> T doReadEntity(Class<T> cls, Type t, Annotation[] anns, boolean close
T tCastLastEntity = castLastEntity();
shouldClose = shouldClose && !(tCastLastEntity instanceof AutoCloseable)
&& !(tCastLastEntity instanceof Source);
if (closeAfterRead && shouldClose) {
if (shouldClose) {
close();
}
return tCastLastEntity;
Expand All @@ -489,7 +484,7 @@ public <T> T doReadEntity(Class<T> cls, Type t, Annotation[] anns, boolean close
autoClose(cls, true);
reportMessageHandlerProblem("MSG_READER_PROBLEM", cls, mediaType, ex);
} else {
if (closeAfterRead && shouldClose) {
if (shouldClose) {
close();
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
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;
Expand All @@ -68,6 +69,8 @@

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;
Expand Down Expand Up @@ -719,7 +722,12 @@ protected static StringBuilder handleExceptionStart(Exception e) {
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)
Expand All @@ -728,7 +736,7 @@ protected static void handleExceptionEnd(Throwable t, String message, boolean re
: 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) {
Expand All @@ -753,7 +761,7 @@ protected void handleJAXBException(JAXBException e, boolean read) {
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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/**
* 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.provider;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Comparator;

import org.apache.cxf.jaxrs.utils.GenericsUtils;

public class GenericArgumentComparator implements Comparator<Class<?>> {

private final Class<?> genericInterface;

public GenericArgumentComparator(final Class<?> genericInterface) {
if (genericInterface == null) {
throw new IllegalArgumentException("Generic Interface cannot be null");
}
if (genericInterface.getTypeParameters().length == 0) {
throw new IllegalArgumentException("Interface has no generic type parameters: " + genericInterface);
}
if (genericInterface.getTypeParameters().length > 1) {
throw new IllegalArgumentException("Interface must have only 1 generic type parameter: "
+ genericInterface);
}
this.genericInterface = genericInterface;
}

/**
* This comparator sorts the most specific type to the top of the list.
*
* Effectively, this sorts classes in descending order with java.lang.Object
* always as the last element if present.
*/
@Override
public int compare(final Class<?> a, final Class<?> b) {
/*
* To keep things from being too abstract and confusing, this javadoc refers
* MessageBodyReader as the value of genericInterface.
*
* It could be any similar interface with just one generic type parameter,
* such as MessageBodyWriter, ContextResolver, Consumer, etc.
*/

/*
* Get the actual type each class specified as its MessageBodyReader generic
* parameter. An array of one arg will be returned or null if the class does
* not implement MessageBodyReader.
*/
final Type[] aTypes = GenericsUtils.getTypeArgumentsFor(genericInterface, a);
final Type[] bTypes = GenericsUtils.getTypeArgumentsFor(genericInterface, b);

/*
* If either class does not implement the MessageBodyReader interface and
* therefore returned a null Types array, that class should have the lower
* priority.
*/
if (aTypes == bTypes) {
return 0;
}
if (aTypes == null) {
return 1;
}
if (bTypes == null) {
return -1;
}

/*
* We only support interfaces like MessageBodyReader that have
* just one type parameter. Neither class returned a null Type
* array so we know each properly implements the interface and
* therefore we don't need to check array lengths.
*/
final Type aType = aTypes[0];
final Type bType = bTypes[0];

return compare(aType, bType);
}

public int compare(final Type aType, final Type bType) {
if (aType == bType) {
return 0;
}

/*
* At this point we're now dealing with actual the value each
* class specified for their MessageBodyReader implementations.
*
* Types like String, Boolean and URI will appear as a Class.
* Types like JAXBElement which themselves have a parameter will
* appear as a ParameterizedType.
*
* Let's first evaluate them as basic classes. Only if they're
* the same class do we need to look at their parameters.
*/
final Class<?> aClass = asClass(aType);
final Class<?> bClass = asClass(bType);

/*
* If they aren't the same class we only need to look at the
* classes themselves and can ignore any parameters they have
*/
if (!aClass.equals(bClass)) {
/*
* For those who can't remember this cryptic method:
*
* Red.class.isAssignableFrom(Color.class) == false
* Color.class.isAssignableFrom(Red.class) == true
*/

// bClass is a more generic version of aClass
if (bClass.isAssignableFrom(aClass)) {
return -1;
}

// aClass is a more generic version of bClass
if (aClass.isAssignableFrom(bClass)) {
return 1;
}

// These classes are unrelated
return 0;
}

/*
* They are the same class, so let's look at their parameters
* and try to sort based on those.
*/
final Type aParam = getFirstParameterOrObject(aType);
final Type bParam = getFirstParameterOrObject(bType);

return compare(aParam, bParam);
}

private Type getFirstParameterOrObject(final Type type) {
if (!(type instanceof ParameterizedType)) {
return Object.class;
}

final ParameterizedType parameterizedType = (ParameterizedType) type;
final Type[] types = parameterizedType.getActualTypeArguments();

if (types.length == 0) {
return Object.class;
}

/*
* This parameterized type may have more than one
* generic argument (like Map or Function do). If
* so, too bad, we're ignoring it out of laziness.
*
* Feel free to come here and implement what makes
* sense to you if you need this feature. Maybe
* you have a Map<String,Object> and Map<String,URL>
* situation you want to support.
*/
return types[0];
}

private Class<?> asClass(final Type aType) {
if (aType instanceof Class) {
return (Class<?>) aType;
}

if (aType instanceof ParameterizedType) {
final ParameterizedType parameterizedType = (ParameterizedType) aType;
return asClass(parameterizedType.getRawType());
}

if (aType instanceof TypeVariable) {
final TypeVariable typeVariable = (TypeVariable) aType;
final Type[] bounds = typeVariable.getBounds();

if (bounds == null || bounds.length == 0) {
return Object.class;
} else {
return asClass(bounds[0]);
}
}

if (aType instanceof WildcardType) {
// todo
return Object.class;
}

return Object.class;
}
}
Loading