blob: 55430931f71151d97549da4f9169acac45393821 [file] [log] [blame]
/**
* 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.utils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.SortedSet;
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 org.apache.cxf.common.classloader.ClassLoaderUtils;
import org.apache.cxf.common.i18n.BundleUtils;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.common.util.ClassHelper;
import org.apache.cxf.common.util.PrimitiveUtils;
import org.apache.cxf.common.util.ProxyClassLoaderCache;
import org.apache.cxf.common.util.ReflectionUtil;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.jaxrs.ext.ContextProvider;
import org.apache.cxf.jaxrs.ext.MessageContext;
import org.apache.cxf.jaxrs.ext.ProtocolHeaders;
import org.apache.cxf.jaxrs.impl.MetadataMap;
import org.apache.cxf.jaxrs.impl.PathSegmentImpl;
import org.apache.cxf.jaxrs.impl.tl.ThreadLocalContextResolver;
import org.apache.cxf.jaxrs.impl.tl.ThreadLocalHttpHeaders;
import org.apache.cxf.jaxrs.impl.tl.ThreadLocalInvocationHandler;
import org.apache.cxf.jaxrs.impl.tl.ThreadLocalMessageContext;
import org.apache.cxf.jaxrs.impl.tl.ThreadLocalProtocolHeaders;
import org.apache.cxf.jaxrs.impl.tl.ThreadLocalProviders;
import org.apache.cxf.jaxrs.impl.tl.ThreadLocalProxy;
import org.apache.cxf.jaxrs.impl.tl.ThreadLocalRequest;
import org.apache.cxf.jaxrs.impl.tl.ThreadLocalSecurityContext;
import org.apache.cxf.jaxrs.impl.tl.ThreadLocalUriInfo;
import org.apache.cxf.jaxrs.model.AbstractResourceInfo;
import org.apache.cxf.jaxrs.model.Parameter;
import org.apache.cxf.jaxrs.model.ParameterType;
import org.apache.cxf.jaxrs.provider.ProviderFactory;
import org.apache.cxf.jaxrs.provider.ServerProviderFactory;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageUtils;
public final class InjectionUtils {
public static final Set<String> STANDARD_CONTEXT_CLASSES = new HashSet<>();
public static final Set<String> VALUE_CONTEXTS = new HashSet<>();
private static final boolean USE_JAXB;
static {
// JAX-RS 1.0-1.1
STANDARD_CONTEXT_CLASSES.add(Application.class.getName());
STANDARD_CONTEXT_CLASSES.add(UriInfo.class.getName());
STANDARD_CONTEXT_CLASSES.add(HttpHeaders.class.getName());
STANDARD_CONTEXT_CLASSES.add(Request.class.getName());
STANDARD_CONTEXT_CLASSES.add(SecurityContext.class.getName());
STANDARD_CONTEXT_CLASSES.add(Providers.class.getName());
STANDARD_CONTEXT_CLASSES.add(ContextResolver.class.getName());
STANDARD_CONTEXT_CLASSES.add("javax.servlet.http.HttpServletRequest");
STANDARD_CONTEXT_CLASSES.add("javax.servlet.http.HttpServletResponse");
STANDARD_CONTEXT_CLASSES.add("javax.servlet.ServletContext");
// JAX-RS 2.0
STANDARD_CONTEXT_CLASSES.add("javax.ws.rs.container.ResourceContext");
STANDARD_CONTEXT_CLASSES.add("javax.ws.rs.container.ResourceInfo");
STANDARD_CONTEXT_CLASSES.add("javax.ws.rs.core.Configuration");
// JAX-RS 2.1
STANDARD_CONTEXT_CLASSES.add("javax.ws.rs.sse.Sse");
STANDARD_CONTEXT_CLASSES.add("javax.ws.rs.sse.SseEventSink");
VALUE_CONTEXTS.add(Application.class.getName());
VALUE_CONTEXTS.add("javax.ws.rs.sse.Sse");
boolean useJaxb;
try {
ClassLoaderUtils.loadClass(
"javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter",
InjectionUtils.class);
useJaxb = true;
} catch (final ClassNotFoundException cnfe) {
useJaxb = false;
}
USE_JAXB = useJaxb;
}
private static final Logger LOG = LogUtils.getL7dLogger(InjectionUtils.class);
private static final ResourceBundle BUNDLE = BundleUtils.getBundle(InjectionUtils.class);
private static final String SERVLET_CONFIG_CLASS_NAME = "javax.servlet.ServletConfig";
private static final String SERVLET_CONTEXT_CLASS_NAME = "javax.servlet.ServletContext";
private static final String HTTP_SERVLET_REQUEST_CLASS_NAME = "javax.servlet.http.HttpServletRequest";
private static final String HTTP_SERVLET_RESPONSE_CLASS_NAME = "javax.servlet.http.HttpServletResponse";
private static final String ENUM_CONVERSION_CASE_SENSITIVE = "enum.conversion.case.sensitive";
private static final String IGNORE_MATRIX_PARAMETERS = "ignore.matrix.parameters";
private static ProxyClassLoaderCache proxyClassLoaderCache =
new ProxyClassLoaderCache();
private InjectionUtils() {
}
public static Field getDeclaredField(Class<?> cls, String fieldName) {
if (cls == null || cls == Object.class) {
return null;
}
Field f = ReflectionUtil.getDeclaredField(cls, fieldName);
if (f != null) {
return f;
}
return getDeclaredField(cls.getSuperclass(), fieldName);
}
public static boolean isConcreteClass(Class<?> cls) {
return !cls.isInterface() && !Modifier.isAbstract(cls.getModifiers());
}
private static ParameterizedType findGenericDeclaration(GenericDeclaration declaration, Type scope) {
if (scope instanceof ParameterizedType) {
ParameterizedType type = (ParameterizedType) scope;
if (type.getRawType() == declaration) {
return type;
}
scope = type.getRawType();
}
if (scope instanceof Class) {
Class<?> classScope = (Class<?>)scope;
ParameterizedType result = findGenericDeclaration(declaration, classScope.getGenericSuperclass());
if (result == null) {
for (Type type : classScope.getGenericInterfaces()) {
result = findGenericDeclaration(declaration, type);
if (result != null) {
break;
}
}
}
return result;
}
return null;
}
public static Type getSuperType(Class<?> serviceClass, TypeVariable<?> var) {
int pos = 0;
GenericDeclaration genericDeclaration = var.getGenericDeclaration();
TypeVariable<?>[] vars = genericDeclaration.getTypeParameters();
for (; pos < vars.length; pos++) {
if (vars[pos].getName().equals(var.getName())) {
break;
}
}
ParameterizedType genericSubtype = findGenericDeclaration(genericDeclaration, serviceClass);
Type result = null;
if (genericSubtype != null) {
result = genericSubtype.getActualTypeArguments()[pos];
}
if (result instanceof TypeVariable) {
result = getSuperType(serviceClass, (TypeVariable<?>) result);
}
if (result == null || result == Object.class) {
for (Type bound : var.getBounds()) {
if (bound != Object.class) {
result = bound;
break;
}
}
}
return result;
}
public static Method checkProxy(Method methodToInvoke, Object resourceObject) {
if (Proxy.class.isInstance(resourceObject)) {
String methodToInvokeName = methodToInvoke.getName();
Class<?>[] methodToInvokeTypes = methodToInvoke.getParameterTypes();
for (Class<?> c : resourceObject.getClass().getInterfaces()) {
try {
return c.getMethod(methodToInvokeName, methodToInvokeTypes);
} catch (NoSuchMethodException ex) {
//ignore
}
if (methodToInvokeTypes.length > 0) {
for (Method m : c.getMethods()) {
if (m.getName().equals(methodToInvokeName)
&& m.getParameterTypes().length == methodToInvokeTypes.length) {
Class<?>[] methodTypes = m.getParameterTypes();
for (int i = 0; i < methodTypes.length; i++) {
if (!methodTypes[i].isAssignableFrom(methodToInvokeTypes[i])) {
break;
}
}
return m;
}
}
}
}
}
return methodToInvoke;
}
public static void injectFieldValue(final Field f,
final Object o,
final Object v) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
try {
f.setAccessible(true);
f.set(o, v);
} catch (IllegalAccessException ex) {
reportServerError("FIELD_ACCESS_FAILURE",
f.getType().getName());
}
return null;
}
});
}
public static Object extractFieldValue(final Field f,
final Object o) {
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
try {
f.setAccessible(true);
return f.get(o);
} catch (IllegalAccessException ex) {
reportServerError("FIELD_ACCESS_FAILURE",
f.getType().getName());
}
return null;
}
});
}
public static Class<?> getActualType(Type genericType) {
return getActualType(genericType, 0);
}
public static Class<?> getActualType(Type genericType, int pos) {
if (genericType == null) {
return null;
}
if (genericType == Object.class) {
return (Class<?>)genericType;
}
if (!ParameterizedType.class.isAssignableFrom(genericType.getClass())) {
if (genericType instanceof TypeVariable) {
genericType = getType(((TypeVariable<?>)genericType).getBounds(), pos);
} else if (genericType instanceof WildcardType) {
WildcardType wildcardType = (WildcardType)genericType;
Type[] bounds = wildcardType.getLowerBounds();
if (bounds.length == 0) {
bounds = wildcardType.getUpperBounds();
}
genericType = getType(bounds, pos);
} else if (genericType instanceof GenericArrayType) {
genericType = ((GenericArrayType)genericType).getGenericComponentType();
}
final Class<?> cls;
if (!(genericType instanceof ParameterizedType)) {
cls = (Class<?>)genericType;
} else {
cls = (Class<?>)((ParameterizedType)genericType).getRawType();
}
return cls.isArray() ? cls.getComponentType() : cls;
}
ParameterizedType paramType = (ParameterizedType)genericType;
Type t = getType(paramType.getActualTypeArguments(), pos);
return t instanceof Class ? (Class<?>)t : getActualType(t, 0);
}
public static Type getType(Type[] types, int pos) {
if (pos >= types.length) {
throw new RuntimeException("No type can be found at position " + pos);
}
return types[pos];
}
public static Class<?> getRawType(Type genericType) {
if (genericType instanceof Class) {
return (Class<?>) genericType;
} else if (genericType instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType)genericType;
Type t = paramType.getRawType();
if (t instanceof Class) {
return (Class<?>)t;
}
} else if (genericType instanceof GenericArrayType) {
return getRawType(((GenericArrayType)genericType).getGenericComponentType());
}
return null;
}
public static Type[] getActualTypes(Type genericType) {
if (genericType == null
|| !ParameterizedType.class.isAssignableFrom(genericType.getClass())) {
return null;
}
ParameterizedType paramType = (ParameterizedType)genericType;
return paramType.getActualTypeArguments();
}
public static void injectThroughMethod(Object requestObject,
Method method,
Object parameterValue) {
injectThroughMethod(requestObject, method, parameterValue, null);
}
public static void injectThroughMethod(Object requestObject,
Method method,
Object parameterValue,
Message inMessage) {
try {
Method methodToInvoke = checkProxy(method, requestObject);
methodToInvoke.invoke(requestObject, new Object[]{parameterValue});
} catch (IllegalAccessException ex) {
reportServerError("METHOD_ACCESS_FAILURE", method.getName());
} catch (InvocationTargetException ex) {
LOG.log(Level.SEVERE, ex.getCause().getMessage(), ex);
Response r = JAXRSUtils.convertFaultToResponse(ex.getCause(), inMessage);
if (r != null) {
inMessage.getExchange().put(Response.class, r);
throw new WebApplicationException();
}
reportServerError("METHOD_ACCESS_FAILURE", method.getName());
} catch (Exception ex) {
reportServerError("METHOD_INJECTION_FAILURE", method.getName());
}
}
public static Object extractFromMethod(Object requestObject, Method method) {
return extractFromMethod(requestObject, method, true);
}
public static Object extractFromMethod(Object requestObject,
Method method,
boolean logError) {
try {
Method methodToInvoke = checkProxy(method, requestObject);
return methodToInvoke.invoke(requestObject);
} catch (IllegalAccessException ex) {
reportServerError("METHOD_ACCESS_FAILURE", method.getName(), logError);
} catch (Exception ex) {
reportServerError("METHOD_INJECTION_FAILURE", method.getName(), logError);
}
return null;
}
@SuppressWarnings("unchecked")
public static <T> T handleParameter(String value,
boolean decoded,
Class<T> pClass,
Type genericType,
Annotation[] paramAnns,
ParameterType pType,
Message message) {
if (value == null) {
return null;
}
if (pType == ParameterType.PATH) {
if (PathSegment.class.isAssignableFrom(pClass)) {
return pClass.cast(new PathSegmentImpl(value, decoded));
} else if (!MessageUtils.getContextualBoolean(message, IGNORE_MATRIX_PARAMETERS)) {
value = new PathSegmentImpl(value, false).getPath();
}
}
value = decodeValue(value, decoded, pType);
final Optional<ParamConverter<T>> converter = getParamConverter(pClass, genericType, paramAnns, message);
Object result = null;
try {
if (converter.isPresent()) {
result = converter.get().fromString(value);
}
} catch (IllegalArgumentException nfe) {
throw createParamConversionException(pType, nfe);
}
if (result != null) {
final T theResult;
if (pClass.isPrimitive()) {
theResult = (T)result;
} else {
theResult = pClass.cast(result);
}
return theResult;
} else if (converter.isPresent() && !pClass.isPrimitive()) {
// The converter was applied and returned null value, acceptable
// outcome for non-primitive type.
return pClass.cast(result);
}
if (Number.class.isAssignableFrom(pClass) && "".equals(value)) {
//pass empty string to boxed number type will result in 404
return null;
}
if (Boolean.class == pClass) {
// allow == checks for Boolean object
pClass = (Class<T>) Boolean.TYPE;
}
if (pClass.isPrimitive()) {
try {
// cannot us pClass.cast as the pClass is something like
// Boolean.TYPE (representing the boolean primitive) and
// the object is a Boolean object
return (T)PrimitiveUtils.read(value, pClass);
} catch (NumberFormatException nfe) {
throw createParamConversionException(pType, nfe);
}
}
boolean adapterHasToBeUsed = false;
Class<?> cls = pClass;
Class<?> valueType = !USE_JAXB
? cls
: JAXBUtils.getValueTypeFromAdapter(pClass, pClass, paramAnns);
if (valueType != cls) {
cls = valueType;
adapterHasToBeUsed = true;
}
if (pClass == String.class && !adapterHasToBeUsed) {
return pClass.cast(value);
}
// check constructors accepting a single String value
try {
Constructor<?> c = cls.getConstructor(new Class<?>[]{String.class});
result = c.newInstance(new Object[]{value});
} catch (NoSuchMethodException ex) {
// try valueOf
} catch (WebApplicationException ex) {
throw ex;
} catch (Exception ex) {
Throwable t = getOrThrowActualException(ex);
LOG.warning(new org.apache.cxf.common.i18n.Message("CLASS_CONSTRUCTOR_FAILURE",
BUNDLE,
pClass.getName()).toString());
Response r = JAXRSUtils.toResponse(HttpUtils.getParameterFailureStatus(pType));
throw ExceptionUtils.toHttpException(t, r);
}
if (result == null) {
// check for valueOf(String) static methods
String[] methodNames = cls.isEnum()
? new String[] {"fromString", "fromValue", "valueOf"}
: new String[] {"valueOf", "fromString"};
result = evaluateFactoryMethods(value, pType, result, cls, methodNames);
}
if (adapterHasToBeUsed) {
// as the last resort, try XmlJavaTypeAdapters
Object valueToReplace = result != null ? result : value;
try {
result = JAXBUtils.convertWithAdapter(valueToReplace, pClass, paramAnns);
} catch (Throwable ex) {
result = null;
}
}
if (result == null) {
reportServerError("WRONG_PARAMETER_TYPE", pClass.getName());
}
try {
return pClass.cast(result);
} catch (ClassCastException ex) {
reportServerError("WRONG_PARAMETER_TYPE", pClass.getName());
return null;
}
}
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);
}
public static <T> Optional<ParamConverter<T>> getParamConverter(Class<T> pClass,
Type genericType, Annotation[] anns, Message message) {
if (message != null) {
ServerProviderFactory pf = ServerProviderFactory.getInstance(message);
ParamConverter<T> pm = pf.createParameterHandler(pClass, genericType, anns, message);
return Optional.ofNullable(pm);
}
return Optional.empty();
}
public static <T> T createFromParameterHandler(String value,
Class<T> pClass,
Type genericType,
Annotation[] anns,
Message message) {
return getParamConverter(pClass, genericType, anns, message)
.map(pm -> pm.fromString(value))
.orElse(null);
}
public static void reportServerError(String messageName, String parameter) {
reportServerError(messageName, parameter, true);
}
public static void reportServerError(String messageName, String parameter, boolean logError) {
org.apache.cxf.common.i18n.Message errorMessage =
new org.apache.cxf.common.i18n.Message(messageName,
BUNDLE,
parameter);
if (logError) {
LOG.severe(errorMessage.toString());
}
Response r = JAXRSUtils.toResponseBuilder(Response.Status.INTERNAL_SERVER_ERROR)
.type(MediaType.TEXT_PLAIN_TYPE)
.entity(errorMessage.toString()).build();
throw ExceptionUtils.toInternalServerErrorException(null, r);
}
private static Object evaluateFactoryMethods(String value, ParameterType pType, Object result,
Class<?> cls, String[] methodNames) {
Exception factoryMethodEx = null;
for (String mName : methodNames) {
try {
result = evaluateFactoryMethod(value, cls, mName);
if (result != null) {
factoryMethodEx = null;
break;
}
} catch (Exception ex) {
// If it is enum and the method name is "fromValue" then don't throw
// the exception immediately but try the next factory method
factoryMethodEx = ex;
if (!cls.isEnum() || !"fromValue".equals(mName)) {
break;
}
}
}
if (factoryMethodEx != null) {
Throwable t = getOrThrowActualException(factoryMethodEx);
LOG.warning(new org.apache.cxf.common.i18n.Message("CLASS_VALUE_OF_FAILURE",
BUNDLE,
cls.getName()).toString());
throw new WebApplicationException(t, HttpUtils.getParameterFailureStatus(pType));
}
return result;
}
private static <T> T evaluateFactoryMethod(String value,
Class<T> pClass,
String methodName)
throws InvocationTargetException {
try {
Method m = pClass.getMethod(methodName, new Class<?>[]{String.class});
if (Modifier.isStatic(m.getModifiers())) {
return pClass.cast(m.invoke(null, new Object[]{value}));
}
} catch (NoSuchMethodException ex) {
// no luck: try another factory methods
} catch (IllegalAccessException ex) {
// factory method is not accessible: try another
}
return null;
}
private static Throwable getOrThrowActualException(Throwable ex) {
Throwable t = ex instanceof InvocationTargetException ? ((InvocationTargetException)ex).getCause() : ex;
if (t instanceof WebApplicationException) {
throw (WebApplicationException)t;
}
return t;
}
public static Object handleBean(Class<?> paramType, Annotation[] paramAnns,
MultivaluedMap<String, String> values,
ParameterType pType, Message message, boolean decoded) {
Object bean = null;
try {
if (paramType.isInterface()) {
paramType = org.apache.cxf.jaxrs.utils.JAXBUtils.getValueTypeFromAdapter(paramType,
paramType,
paramAnns);
}
bean = paramType.newInstance();
} catch (IllegalAccessException ex) {
reportServerError("CLASS_ACCESS_FAILURE", paramType.getName());
} catch (Exception ex) {
reportServerError("CLASS_INSTANTIATION_FAILURE", paramType.getName());
}
Map<String, MultivaluedMap<String, String>> parsedValues =
new HashMap<>();
for (Map.Entry<String, List<String>> entry : values.entrySet()) {
String memberKey = entry.getKey();
final String beanKey;
int idx = memberKey.indexOf('.');
if (idx == -1) {
beanKey = '.' + memberKey;
} else {
beanKey = memberKey.substring(0, idx);
memberKey = memberKey.substring(idx + 1);
}
MultivaluedMap<String, String> value = parsedValues.get(beanKey);
if (value == null) {
value = new MetadataMap<>();
parsedValues.put(beanKey, value);
}
value.put(memberKey, entry.getValue());
}
if (!parsedValues.isEmpty()) {
for (Map.Entry<String, MultivaluedMap<String, String>> entry : parsedValues.entrySet()) {
String memberKey = entry.getKey();
boolean isbean = !memberKey.startsWith(".");
if (!isbean) {
memberKey = memberKey.substring(1);
}
Object setter = null;
Object getter = null;
for (Method m : paramType.getMethods()) {
if (m.getName().equalsIgnoreCase("set" + memberKey)
&& m.getParameterTypes().length == 1) {
setter = m;
} else if (m.getName().equalsIgnoreCase("get" + memberKey)
|| isBooleanType(m.getReturnType())
&& m.getName().equalsIgnoreCase("is" + memberKey)) {
getter = m;
}
if (setter != null && getter != null) {
break;
}
}
if (setter == null) {
for (Field f : paramType.getFields()) {
if (f.getName().equalsIgnoreCase(memberKey)) {
setter = f;
getter = f;
break;
}
}
}
if (setter != null && getter != null) {
final Class<?> type;
final Type genericType;
Object paramValue;
if (setter instanceof Method) {
type = Method.class.cast(setter).getParameterTypes()[0];
genericType = Method.class.cast(setter).getGenericParameterTypes()[0];
paramValue = InjectionUtils.extractFromMethod(bean, (Method) getter);
} else {
type = Field.class.cast(setter).getType();
genericType = Field.class.cast(setter).getGenericType();
paramValue = InjectionUtils.extractFieldValue((Field) getter, bean);
}
List<MultivaluedMap<String, String>> processedValuesList =
processValues(type, genericType, entry.getValue(), isbean);
for (MultivaluedMap<String, String> processedValues : processedValuesList) {
if (InjectionUtils.isSupportedCollectionOrArray(type)) {
Object appendValue = InjectionUtils.injectIntoCollectionOrArray(type,
genericType, paramAnns, processedValues,
isbean, true,
pType, message);
paramValue = InjectionUtils.mergeCollectionsOrArrays(paramValue, appendValue,
genericType);
} else if (isSupportedMap(genericType)) {
Object appendValue = injectIntoMap(
genericType, paramAnns, processedValues, true, pType, message);
paramValue = mergeMap(paramValue, appendValue);
} else if (isbean) {
paramValue = InjectionUtils.handleBean(type, paramAnns, processedValues,
pType, message, decoded);
} else {
paramValue = InjectionUtils.handleParameter(
processedValues.values().iterator().next().get(0),
decoded, type, type, paramAnns, pType, message);
}
if (paramValue != null) {
if (setter instanceof Method) {
InjectionUtils.injectThroughMethod(bean, (Method) setter, paramValue);
} else {
InjectionUtils.injectFieldValue((Field) setter, bean, paramValue);
}
}
}
}
}
}
return bean;
}
@SuppressWarnings("unchecked")
private static Object mergeMap(Object first, Object second) {
if (first == null) {
return second;
} else if (first instanceof Map) {
Map.class.cast(first).putAll((Map<?, ?>) second);
return first;
}
return null;
}
private static Object injectIntoMap(Type genericType,
Annotation[] paramAnns,
MultivaluedMap<String, String> processedValues,
boolean decoded,
ParameterType pathParam, Message message) {
ParameterizedType paramType = (ParameterizedType) genericType;
Class<?> keyType = (Class<?>)paramType.getActualTypeArguments()[0];
Type secondType = InjectionUtils.getType(paramType.getActualTypeArguments(), 1);
if (secondType instanceof ParameterizedType) {
MultivaluedMap<Object, Object> theValues = new MetadataMap<>();
ParameterizedType valueParamType = (ParameterizedType) secondType;
Class<?> valueType = (Class<?>) InjectionUtils.getType(valueParamType
.getActualTypeArguments(), 0);
for (Map.Entry<String, List<String>> processedValuesEntry : processedValues.entrySet()) {
List<String> valuesList = processedValuesEntry.getValue();
for (String value : valuesList) {
Object o = InjectionUtils.handleParameter(value,
decoded, valueType, valueType, paramAnns, pathParam, message);
theValues.add(convertStringToPrimitive(processedValuesEntry.getKey(), keyType), o);
}
}
return theValues;
}
Map<Object, Object> theValues = new HashMap<>();
Class<?> valueType =
(Class<?>) InjectionUtils.getType(paramType.getActualTypeArguments(), 1);
for (Map.Entry<String, List<String>> processedValuesEntry : processedValues.entrySet()) {
List<String> valuesList = processedValuesEntry.getValue();
for (String value : valuesList) {
Object o = InjectionUtils.handleParameter(value,
decoded, valueType, valueType, paramAnns, pathParam, message);
theValues.put(
convertStringToPrimitive(processedValuesEntry.getKey(), keyType),
o);
}
}
return theValues;
}
private static boolean isSupportedMap(Type genericType) {
Class<?> rawType = getRawType(genericType);
if (Map.class.isAssignableFrom(rawType) && genericType instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) genericType;
if (paramType.getActualTypeArguments().length == 2) {
Class<?> firstType = getRawType(getType(paramType.getActualTypeArguments(), 0));
Type secondType = getType(paramType.getActualTypeArguments(), 1);
Class<?> secondRawType = getRawType(secondType);
return InjectionUtils.isPrimitive(firstType)
&& (InjectionUtils.isPrimitive(secondRawType)
|| allowedMapListValue(secondRawType, secondType));
}
}
return false;
}
private static boolean allowedMapListValue(Class<?> cls, Type type) {
if (List.class.isAssignableFrom(cls)) {
Class<?> listtype = getRawType(
getType(((ParameterizedType)type).getActualTypeArguments(), 0));
return InjectionUtils.isPrimitive(listtype);
}
return false;
}
private static List<MultivaluedMap<String, String>> processValues(Class<?> type, Type genericType,
MultivaluedMap<String, String> values,
boolean isbean) {
final List<MultivaluedMap<String, String>> valuesList;
if (isbean && InjectionUtils.isSupportedCollectionOrArray(type)) {
valuesList = new ArrayList<>();
Class<?> realType = InjectionUtils.getActualType(genericType);
for (Map.Entry<String, List<String>> entry : values.entrySet()) {
String memberKey = entry.getKey();
Class<?> memberType = null;
for (Method m : realType.getMethods()) {
if (m.getName().equalsIgnoreCase("set" + memberKey)
&& m.getParameterTypes().length == 1) {
memberType = m.getParameterTypes()[0];
break;
}
}
if (memberType == null) {
for (Field f : realType.getFields()) {
if (f.getName().equalsIgnoreCase(memberKey)) {
memberType = f.getType();
break;
}
}
}
// Strip values tied to collection/array fields from beans that are within
// collection/array themselves, the only way to support this would be to have
// an indexing syntax for nested beans, perhaps like this:
// a(0).b=1&a(0).b=2&a(1).b=3&a(1).b=4
// For now though we simply don't support this capability. To illustrate, the 'c'
// param is dropped from this multivaluedmap example since it is a list:
// {c=[71, 81, 91, 72, 82, 92], a=[C1, C2], b=[790, 791]}
if (memberType != null && InjectionUtils.isSupportedCollectionOrArray(memberType)) {
continue;
}
// Split multivaluedmap value list contents into separate multivaluedmap instances
// whose list contents are only 1 level deep, for example:
// {a=[C1, C2], b=[790, 791]}
// becomes these 2 separate multivaluedmap instances:
// {a=[C1], b=[790]} and {a=[C2], b=[791]}
int idx = 0;
for (String value : entry.getValue()) {
MultivaluedMap<String, String> splitValues =
(idx < valuesList.size()) ? valuesList.get(idx) : null;
if (splitValues == null) {
splitValues = new MetadataMap<>();
valuesList.add(splitValues);
}
splitValues.add(memberKey, value);
idx++;
}
}
} else {
valuesList = Collections.singletonList(values);
}
return valuesList;
}
public static boolean isSupportedCollectionOrArray(Class<?> type) {
return Collection.class.isAssignableFrom(type) || type.isArray();
}
@SuppressWarnings("unchecked")
private static Object mergeCollectionsOrArrays(Object first, Object second, Type genericType) {
if (first == null) {
return second;
} else if (first instanceof Collection) {
Collection.class.cast(first).addAll((Collection<?>) second);
return first;
} else {
int firstLen = Array.getLength(first);
int secondLen = Array.getLength(second);
Object mergedArray = Array.newInstance(InjectionUtils.getActualType(genericType),
firstLen + secondLen);
System.arraycopy(first, 0, mergedArray, 0, firstLen);
System.arraycopy(second, 0, mergedArray, firstLen, secondLen);
return mergedArray;
}
}
static Class<?> getCollectionType(Class<?> rawType) {
Class<?> type = null;
if (SortedSet.class.isAssignableFrom(rawType)) {
type = TreeSet.class; //NOPMD
} else if (Set.class.isAssignableFrom(rawType)) {
type = HashSet.class; //NOPMD
} else if (Collection.class.isAssignableFrom(rawType)) {
type = ArrayList.class; //NOPMD
}
return type;
}
//CHECKSTYLE:OFF
private static Object injectIntoCollectionOrArray(Class<?> rawType,
Type genericType,
Annotation[] paramAnns,
MultivaluedMap<String, String> values,
boolean isbean, boolean decoded,
ParameterType pathParam, Message message) {
//CHECKSTYLE:ON
Class<?> type = getCollectionType(rawType);
final Class<?> realType;
final Type realGenericType;
if (rawType.isArray()) {
realType = rawType.getComponentType();
realGenericType = realType;
} else {
Type[] types = getActualTypes(genericType);
if (types == null || types.length == 0 || !(types[0] instanceof ParameterizedType)) {
realType = getActualType(genericType);
realGenericType = realType;
} else {
realType = getRawType(types[0]);
realGenericType = types[0];
}
}
Object theValues = null;
if (type != null) {
try {
theValues = type.newInstance();
} catch (IllegalAccessException ex) {
reportServerError("CLASS_ACCESS_FAILURE", type.getName());
} catch (Exception ex) {
reportServerError("CLASS_INSTANTIATION_FAILURE", type.getName());
}
} else {
theValues = Array.newInstance(realType, isbean ? 1 : values.values().iterator().next().size());
}
if (isbean) {
Object o = InjectionUtils.handleBean(realType, paramAnns, values, pathParam, message, decoded);
addToCollectionValues(theValues, o, 0);
} else {
List<String> valuesList = values.values().iterator().next();
valuesList = checkPathSegment(valuesList, realType, pathParam);
for (int ind = 0; ind < valuesList.size(); ind++) {
Object o = InjectionUtils.handleParameter(valuesList.get(ind), decoded,
realType, realGenericType, paramAnns, pathParam, message);
addToCollectionValues(theValues, o, ind);
}
}
return theValues;
}
@SuppressWarnings("unchecked")
private static void addToCollectionValues(Object theValues, Object o, int index) {
if (o != null) {
if (theValues instanceof Collection) {
Collection.class.cast(theValues).add(o);
} else if (theValues.getClass().getComponentType().isPrimitive()) {
Array.set(theValues, index, o);
} else {
((Object[]) theValues)[index] = o;
}
}
}
private static List<String> checkPathSegment(List<String> values, Class<?> type,
ParameterType pathParam) {
if (pathParam != ParameterType.PATH || !PathSegment.class.isAssignableFrom(type)) {
return values;
}
List<String> newValues = new ArrayList<>();
for (String v : values) {
String[] segments = v.split("/");
for (String s : segments) {
if (!s.isEmpty()) {
newValues.add(s);
}
}
if (v.endsWith("/")) {
newValues.add("");
}
}
return newValues;
}
//
//CHECKSTYLE:OFF
public static Object createParameterObject(List<String> paramValues,
Class<?> paramType,
Type genericType,
Annotation[] paramAnns,
String defaultValue,
boolean decoded,
ParameterType pathParam,
Message message) {
//CHECKSTYLE:ON
if (paramValues == null || paramValues.size() == 1 && paramValues.get(0) == null) {
if (defaultValue != null) {
paramValues = Collections.singletonList(defaultValue);
} else {
if (paramType.isPrimitive()) {
paramValues = Collections.singletonList(
boolean.class == paramType ? "false"
: char.class == paramType ? Character.toString('\u0000') : "0");
} else if (InjectionUtils.isSupportedCollectionOrArray(paramType)) {
paramValues = Collections.emptyList();
} else {
return null;
}
}
}
Object value = null;
if (InjectionUtils.isSupportedCollectionOrArray(paramType)) {
MultivaluedMap<String, String> paramValuesMap = new MetadataMap<>();
paramValuesMap.put("", paramValues);
value = InjectionUtils.injectIntoCollectionOrArray(paramType, genericType, paramAnns,
paramValuesMap, false, decoded, pathParam, message);
} else {
String result = null;
if (!paramValues.isEmpty()) {
boolean isLast = pathParam == ParameterType.PATH ? true : false;
result = isLast ? paramValues.get(paramValues.size() - 1)
: paramValues.get(0);
}
if (result != null) {
value = InjectionUtils.handleParameter(result, decoded, paramType, genericType,
paramAnns, pathParam, message);
}
}
return value;
}
// TODO : investigate the possibility of using generic proxies only
@SuppressWarnings("unchecked")
public static <T> ThreadLocalProxy<T> createThreadLocalProxy(Class<T> type) {
ThreadLocalProxy<?> proxy = null;
if (UriInfo.class.isAssignableFrom(type)) {
proxy = new ThreadLocalUriInfo();
} else if (HttpHeaders.class.isAssignableFrom(type)) {
proxy = new ThreadLocalHttpHeaders();
} else if (ProtocolHeaders.class.isAssignableFrom(type)) {
proxy = new ThreadLocalProtocolHeaders();
} else if (SecurityContext.class.isAssignableFrom(type)) {
proxy = new ThreadLocalSecurityContext();
} else if (ContextResolver.class.isAssignableFrom(type)) {
proxy = new ThreadLocalContextResolver<>();
} else if (Request.class.isAssignableFrom(type)) {
proxy = new ThreadLocalRequest();
} else if (Providers.class.isAssignableFrom(type)) {
proxy = new ThreadLocalProviders();
} else if (MessageContext.class.isAssignableFrom(type)) {
proxy = new ThreadLocalMessageContext();
}
if (proxy == null && isServletApiContext(type.getName())) {
proxy = createThreadLocalServletApiContext(type.getName());
}
if (proxy == null) {
ClassLoader loader
= proxyClassLoaderCache.getProxyClassLoader(Proxy.class.getClassLoader(),
new Class<?>[]{Proxy.class, ThreadLocalProxy.class, type});
if (!canSeeAllClasses(loader, new Class<?>[]{Proxy.class, ThreadLocalProxy.class, type})) {
LOG.log(Level.FINE, "find a loader from ProxyClassLoader cache,"
+ " but can't see all interfaces");
LOG.log(Level.FINE, "create a new one with parent " + Proxy.class.getClassLoader());
proxyClassLoaderCache.removeStaleProxyClassLoader(type);
proxyClassLoaderCache.getProxyClassLoader(Proxy.class.getClassLoader(),
new Class<?>[]{Proxy.class, ThreadLocalProxy.class, type});
}
return (ThreadLocalProxy<T>)Proxy.newProxyInstance(loader,
new Class[] {type, ThreadLocalProxy.class },
new ThreadLocalInvocationHandler<T>());
}
return (ThreadLocalProxy<T>)proxy;
}
private static boolean canSeeAllClasses(ClassLoader loader, Class<?>[] interfaces) {
for (Class<?> currentInterface : interfaces) {
String ifName = currentInterface.getName();
try {
Class<?> ifClass = Class.forName(ifName, true, loader);
if (ifClass != currentInterface) {
return false;
}
} catch (NoClassDefFoundError | ClassNotFoundException e) {
return false;
}
}
return true;
}
private static boolean isServletApiContext(String name) {
return name.startsWith("javax.servlet.");
}
private static ThreadLocalProxy<?> createThreadLocalServletApiContext(String name) {
String proxyClassName = null;
if (HTTP_SERVLET_REQUEST_CLASS_NAME.equals(name)) {
proxyClassName = "org.apache.cxf.jaxrs.impl.tl.ThreadLocalHttpServletRequest";
} else if (HTTP_SERVLET_RESPONSE_CLASS_NAME.equals(name)) {
proxyClassName = "org.apache.cxf.jaxrs.impl.tl.ThreadLocalHttpServletResponse";
} else if (SERVLET_CONTEXT_CLASS_NAME.equals(name)) {
proxyClassName = "org.apache.cxf.jaxrs.impl.tl.ThreadLocalServletContext";
} else if (SERVLET_CONFIG_CLASS_NAME.equals(name)) {
proxyClassName = "org.apache.cxf.jaxrs.impl.tl.ThreadLocalServletConfig";
}
if (proxyClassName != null) {
try {
return (ThreadLocalProxy<?>)ClassLoaderUtils.loadClass(proxyClassName, InjectionUtils.class)
.newInstance();
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
return null;
}
public static Method getGetterFromSetter(Method setter) throws Exception {
return setter.getDeclaringClass().getMethod("get" + setter.getName().substring(3));
}
public static void injectContextProxiesAndApplication(AbstractResourceInfo cri,
Object instance,
Application app,
ProviderFactory factory) {
if (!cri.contextsAvailable() || !cri.isSingleton()) {
return;
}
synchronized (instance) {
for (Map.Entry<Class<?>, Method> entry : cri.getContextMethods().entrySet()) {
Method method = entry.getValue();
Object value = null;
Class<?> cls = method.getParameterTypes()[0];
if (cls == Application.class) {
value = app;
} else if (VALUE_CONTEXTS.contains(cls.getName()) && factory != null) {
ContextProvider<?> p = factory.createContextProvider(cls, null);
if (p != null) {
value = p.createContext(null);
}
} else {
value = cri.getContextSetterProxy(method);
}
try {
if (value == InjectionUtils.extractFromMethod(instance,
getGetterFromSetter(method),
false)) {
continue;
}
} catch (Throwable t) {
// continue
}
InjectionUtils.injectThroughMethod(instance, method, value);
}
for (Field f : cri.getContextFields()) {
Object value = null;
Class<?> cls = f.getType();
if (cls == Application.class) {
value = app;
} else if (VALUE_CONTEXTS.contains(cls.getName()) && factory != null) {
ContextProvider<?> p = factory.createContextProvider(cls, null);
if (p != null) {
value = p.createContext(null);
}
} else {
value = cri.getContextFieldProxy(f);
}
try {
if (value == InjectionUtils.extractFieldValue(f, instance)) {
continue;
}
} catch (Throwable t) {
// continue
}
InjectionUtils.injectFieldValue(f, instance, value);
}
}
}
public static void injectContextProxies(AbstractResourceInfo cri, Object instance) {
injectContextProxiesAndApplication(cri, instance, null, null);
}
@SuppressWarnings("unchecked")
public static void injectContextField(AbstractResourceInfo cri,
Field f, Object o, Object value) {
if (!cri.isSingleton()) {
InjectionUtils.injectFieldValue(f, o, value);
} else {
ThreadLocalProxy<Object> proxy = (ThreadLocalProxy<Object>)cri.getContextFieldProxy(f);
if (proxy != null) {
proxy.set(value);
}
}
}
public static void injectContexts(Object requestObject,
AbstractResourceInfo resource,
Message message) {
if (resource.contextsAvailable()) {
injectContextMethods(requestObject, resource, message);
injectContextFields(requestObject, resource, message);
injectConstructorProxies(requestObject, resource, message);
}
}
@SuppressWarnings("unchecked")
public static void injectContextMethods(Object requestObject,
AbstractResourceInfo cri,
Message message) {
for (Map.Entry<Class<?>, Method> entry : cri.getContextMethods().entrySet()) {
Method method = entry.getValue();
if (VALUE_CONTEXTS.contains(method.getParameterTypes()[0].getName()) && cri.isSingleton()) {
continue;
}
Object o = JAXRSUtils.createContextValue(message,
method.getGenericParameterTypes()[0],
entry.getKey());
if (o != null) {
if (!cri.isSingleton()) {
InjectionUtils.injectThroughMethod(requestObject, method, o, message);
} else {
ThreadLocalProxy<Object> proxy
= (ThreadLocalProxy<Object>)cri.getContextSetterProxy(method);
if (proxy != null) {
proxy.set(o);
}
}
}
}
}
public static void injectContextFields(Object o,
AbstractResourceInfo cri,
Message m) {
for (Field f : cri.getContextFields()) {
if (VALUE_CONTEXTS.contains(f.getType().getName()) && cri.isSingleton()) {
continue;
}
Object value = JAXRSUtils.createContextValue(m, f.getGenericType(), f.getType());
InjectionUtils.injectContextField(cri, f, o, value);
}
}
@SuppressWarnings("unchecked")
public static void injectConstructorProxies(Object o,
AbstractResourceInfo cri,
Message m) {
Map<Class<?>, ThreadLocalProxy<?>> proxies = cri.getConstructorProxies();
if (proxies != null) {
for (Map.Entry<Class<?>, ThreadLocalProxy<?>> entry : proxies.entrySet()) {
Object value = JAXRSUtils.createContextValue(m, entry.getKey(), entry.getKey());
((ThreadLocalProxy<Object>)entry.getValue()).set(value);
}
}
}
public static MultivaluedMap<String, Object> extractValuesFromBean(Object bean, String baseName) {
MultivaluedMap<String, Object> values = new MetadataMap<>();
fillInValuesFromBean(bean, baseName, values);
return values;
}
private static boolean isBooleanType(Class<?> cls) {
return boolean.class == cls || Boolean.class == cls;
}
public static void fillInValuesFromBean(Object bean, String baseName,
MultivaluedMap<String, Object> values) {
for (Method m : bean.getClass().getMethods()) {
String methodName = m.getName();
boolean startsFromGet = methodName.startsWith("get");
if ((startsFromGet
|| isBooleanType(m.getReturnType()) && methodName.startsWith("is"))
&& m.getParameterTypes().length == 0) {
int minLen = startsFromGet ? 3 : 2;
if (methodName.length() <= minLen) {
continue;
}
String propertyName = StringUtils.uncapitalize(methodName.substring(minLen));
if (baseName.contains(propertyName)
|| "class".equals(propertyName)
|| "declaringClass".equals(propertyName)) {
continue;
}
if (!"".equals(baseName)) {
propertyName = baseName + '.' + propertyName;
}
Object value = extractFromMethod(bean, m);
if (value == null) {
continue;
}
if (isPrimitive(value.getClass()) || Date.class.isAssignableFrom(value.getClass())) {
values.putSingle(propertyName, value);
} else if (value.getClass().isEnum()) {
values.putSingle(propertyName, value.toString());
} else if (isSupportedCollectionOrArray(value.getClass())) {
final List<Object> theValues;
if (value.getClass().isArray()) {
theValues = Arrays.asList((Object[])value);
} else if (value instanceof Set) {
theValues = new ArrayList<>((Set<?>)value);
} else {
theValues = CastUtils.cast((List<?>)value);
}
values.put(propertyName, theValues);
} else if (Map.class.isAssignableFrom(value.getClass())) {
if (isSupportedMap(m.getGenericReturnType())) {
Map<Object, Object> map = CastUtils.cast((Map<?, ?>)value);
for (Map.Entry<Object, Object> entry : map.entrySet()) {
values.add(propertyName + '.' + entry.getKey().toString(),
entry.getValue().toString());
}
}
} else {
fillInValuesFromBean(value, propertyName, values);
}
}
}
}
public static Map<Parameter, Class<?>> getParametersFromBeanClass(Class<?> beanClass,
ParameterType type,
boolean checkIgnorable) {
Map<Parameter, Class<?>> params = new LinkedHashMap<>();
for (Method m : beanClass.getMethods()) {
String methodName = m.getName();
boolean startsFromGet = methodName.startsWith("get");
if ((startsFromGet
|| isBooleanType(m.getReturnType()) && methodName.startsWith("is"))
&& m.getParameterTypes().length == 0) {
int minLen = startsFromGet ? 3 : 2;
if (methodName.length() <= minLen) {
continue;
}
String propertyName = StringUtils.uncapitalize(methodName.substring(minLen));
if (m.getReturnType() == Class.class
|| checkIgnorable && canPropertyBeIgnored(m, propertyName)) {
continue;
}
params.put(new Parameter(type, propertyName), m.getReturnType());
}
}
return params;
}
private static boolean canPropertyBeIgnored(Method m, String propertyName) {
for (Annotation ann : m.getAnnotations()) {
String annType = ann.annotationType().getName();
if ("org.apache.cxf.aegis.type.java5.IgnoreProperty".equals(annType)
|| "javax.xml.bind.annotation.XmlTransient".equals(annType)) {
return true;
}
}
return false;
}
public static boolean isPrimitive(Class<?> type) {
return String.class == type
|| isPrimitiveOnly(type);
}
public static boolean isPrimitiveOnly(Class<?> type) {
return type.isPrimitive()
|| Number.class.isAssignableFrom(type)
|| Boolean.class == type
|| Character.class == type;
}
public static String decodeValue(String value, boolean decode, ParameterType param) {
if (!decode) {
return value;
}
if (param == ParameterType.PATH || param == ParameterType.MATRIX) {
return HttpUtils.pathDecode(value);
}
return HttpUtils.urlDecode(value);
}
public static void invokeLifeCycleMethod(Object instance, Method method) {
if (method != null) {
method = InjectionUtils.checkProxy(method, instance);
try {
method.invoke(instance, new Object[]{});
} catch (InvocationTargetException ex) {
String msg = "Method " + method.getName() + " can not be invoked"
+ " due to InvocationTargetException";
throw new WebApplicationException(JAXRSUtils.toResponseBuilder(500).entity(msg).build());
} catch (IllegalAccessException ex) {
String msg = "Method " + method.getName() + " can not be invoked"
+ " due to IllegalAccessException";
throw ExceptionUtils.toInternalServerErrorException(ex,
JAXRSUtils.toResponseBuilder(500).entity(msg).build());
}
}
}
public static Object convertStringToPrimitive(String value, Class<?> cls) {
return convertStringToPrimitive(value, cls, new Annotation[]{});
}
public static Object convertStringToPrimitive(String value, Class<?> cls, Annotation[] anns) {
Message m = JAXRSUtils.getCurrentMessage();
Object obj = createFromParameterHandler(value, cls, cls, anns, m);
if (obj != null) {
return obj;
}
if (String.class == cls) {
return value;
} else if (cls.isPrimitive()) {
return PrimitiveUtils.read(value, cls);
} else if (cls.isEnum()) {
if (m != null && !MessageUtils.getContextualBoolean(m, ENUM_CONVERSION_CASE_SENSITIVE, false)) {
obj = invokeValueOf(value.toUpperCase(), cls);
}
if (obj == null) {
try {
obj = invokeValueOf(value, cls);
} catch (RuntimeException ex) {
if (m == null) {
obj = invokeValueOf(value.toUpperCase(), cls);
} else {
throw ex;
}
}
}
return obj;
} else {
try {
Constructor<?> c = cls.getConstructor(new Class<?>[]{String.class});
return c.newInstance(new Object[]{value});
} catch (Throwable ex) {
// try valueOf
}
return invokeValueOf(value, cls);
}
}
private static Object invokeValueOf(String value, Class<?> cls) {
try {
Method m = cls.getMethod("valueOf", new Class[]{String.class});
return m.invoke(null, value);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static Class<?> getRawResponseClass(Object targetObject) {
if (targetObject != null) {
Class<?> targetClass = targetObject.getClass();
return ClassHelper.getRealClassFromClass(targetClass);
}
return null;
}
public static Type getGenericResponseType(Method invoked,
Class<?> serviceCls,
Object targetObject,
Class<?> targetType,
Exchange exchange) {
if (targetObject == null) {
return null;
}
final Type type;
if (GenericEntity.class.isAssignableFrom(targetObject.getClass())) {
type = processGenericTypeIfNeeded(serviceCls, targetType, ((GenericEntity<?>)targetObject).getType());
} else if (invoked == null
|| !invoked.getReturnType().isAssignableFrom(targetType)) {
// when a method has been invoked it is still possible that either an ExceptionMapper
// or a ResponseHandler filter overrides a response entity; if it happens then
// the Type is the class of the response object, unless this new entity is assignable
// to invoked.getReturnType(); same applies to the case when a method returns Response
type = targetObject.getClass();
} else {
type = processGenericTypeIfNeeded(serviceCls, targetType, invoked.getGenericReturnType());
}
return type;
}
public static Class<?> updateParamClassToTypeIfNeeded(Class<?> paramCls, Type type) {
if (paramCls != type && type instanceof Class) {
Class<?> clsType = (Class<?>)type;
if (paramCls.isAssignableFrom(clsType)
|| clsType != Object.class && !clsType.isInterface() && clsType.isAssignableFrom(paramCls)) {
paramCls = clsType;
}
}
return paramCls;
}
public static Type processGenericTypeIfNeeded(Class<?> serviceCls, Class<?> paramCls, Type type) {
if (type instanceof TypeVariable) {
type = InjectionUtils.getSuperType(serviceCls, (TypeVariable<?>)type);
} else if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType)type;
if (pt.getActualTypeArguments()[0] instanceof TypeVariable
&& isSupportedCollectionOrArray(getRawType(pt))) {
TypeVariable<?> typeVar = (TypeVariable<?>)pt.getActualTypeArguments()[0];
Type theType = InjectionUtils.getSuperType(serviceCls, typeVar);
if (theType instanceof Class) {
type = new ParameterizedCollectionType((Class<?>)theType);
} else {
type = processGenericTypeIfNeeded(serviceCls, paramCls, theType);
type = new ParameterizedCollectionType(type);
}
}
}
if (type == null || type == Object.class) {
type = paramCls;
}
return type;
}
public static Object getEntity(Object o) {
return o instanceof GenericEntity ? ((GenericEntity<?>)o).getEntity() : o;
}
}