| /** |
| * 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.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.Reader; |
| import java.lang.annotation.Annotation; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.ParameterizedType; |
| import java.lang.reflect.Type; |
| import java.nio.charset.StandardCharsets; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashSet; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.ResourceBundle; |
| import java.util.Set; |
| import java.util.SortedMap; |
| import java.util.TreeMap; |
| import java.util.function.Supplier; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import java.util.stream.Collectors; |
| |
| import javax.ws.rs.ClientErrorException; |
| import javax.ws.rs.Consumes; |
| import javax.ws.rs.HttpMethod; |
| import javax.ws.rs.Produces; |
| import javax.ws.rs.WebApplicationException; |
| import javax.ws.rs.container.AsyncResponse; |
| import javax.ws.rs.container.ContainerRequestContext; |
| import javax.ws.rs.container.ContainerRequestFilter; |
| import javax.ws.rs.container.ContainerResponseContext; |
| import javax.ws.rs.container.ContainerResponseFilter; |
| import javax.ws.rs.container.ResourceContext; |
| import javax.ws.rs.container.ResourceInfo; |
| import javax.ws.rs.core.Application; |
| import javax.ws.rs.core.Configuration; |
| 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.PathSegment; |
| import javax.ws.rs.core.Request; |
| import javax.ws.rs.core.Response; |
| import javax.ws.rs.core.Response.ResponseBuilder; |
| import javax.ws.rs.core.SecurityContext; |
| import javax.ws.rs.core.StreamingOutput; |
| import javax.ws.rs.core.UriInfo; |
| import javax.ws.rs.ext.ContextResolver; |
| import javax.ws.rs.ext.MessageBodyReader; |
| import javax.ws.rs.ext.MessageBodyWriter; |
| import javax.ws.rs.ext.Providers; |
| import javax.ws.rs.ext.ReaderInterceptor; |
| import javax.ws.rs.ext.ReaderInterceptorContext; |
| import javax.ws.rs.ext.WriterInterceptor; |
| import javax.ws.rs.ext.WriterInterceptorContext; |
| import javax.xml.namespace.QName; |
| |
| import org.apache.cxf.common.i18n.BundleUtils; |
| import org.apache.cxf.common.logging.LogUtils; |
| import org.apache.cxf.common.util.PackageUtils; |
| import org.apache.cxf.common.util.PropertyUtils; |
| import org.apache.cxf.common.util.ReflectionUtil; |
| import org.apache.cxf.common.util.StringUtils; |
| import org.apache.cxf.helpers.DOMUtils; |
| import org.apache.cxf.helpers.IOUtils; |
| import org.apache.cxf.helpers.LoadingByteArrayOutputStream; |
| import org.apache.cxf.interceptor.Fault; |
| import org.apache.cxf.io.CacheAndWriteOutputStream; |
| import org.apache.cxf.io.ReaderInputStream; |
| import org.apache.cxf.jaxrs.JAXRSServiceImpl; |
| import org.apache.cxf.jaxrs.ext.ContextProvider; |
| import org.apache.cxf.jaxrs.ext.DefaultMethod; |
| import org.apache.cxf.jaxrs.ext.MessageContext; |
| import org.apache.cxf.jaxrs.ext.MessageContextImpl; |
| import org.apache.cxf.jaxrs.ext.ProtocolHeaders; |
| import org.apache.cxf.jaxrs.ext.ProtocolHeadersImpl; |
| import org.apache.cxf.jaxrs.ext.multipart.MultipartBody; |
| import org.apache.cxf.jaxrs.impl.AsyncResponseImpl; |
| import org.apache.cxf.jaxrs.impl.ContainerRequestContextImpl; |
| import org.apache.cxf.jaxrs.impl.ContainerResponseContextImpl; |
| import org.apache.cxf.jaxrs.impl.HttpHeadersImpl; |
| import org.apache.cxf.jaxrs.impl.MediaTypeHeaderProvider; |
| import org.apache.cxf.jaxrs.impl.MetadataMap; |
| import org.apache.cxf.jaxrs.impl.PathSegmentImpl; |
| import org.apache.cxf.jaxrs.impl.ProvidersImpl; |
| import org.apache.cxf.jaxrs.impl.ReaderInterceptorContextImpl; |
| import org.apache.cxf.jaxrs.impl.ReaderInterceptorMBR; |
| import org.apache.cxf.jaxrs.impl.RequestImpl; |
| import org.apache.cxf.jaxrs.impl.ResourceContextImpl; |
| import org.apache.cxf.jaxrs.impl.ResourceInfoImpl; |
| import org.apache.cxf.jaxrs.impl.ResponseBuilderImpl; |
| import org.apache.cxf.jaxrs.impl.ResponseImpl; |
| import org.apache.cxf.jaxrs.impl.SecurityContextImpl; |
| import org.apache.cxf.jaxrs.impl.UriInfoImpl; |
| import org.apache.cxf.jaxrs.impl.WriterInterceptorContextImpl; |
| import org.apache.cxf.jaxrs.impl.WriterInterceptorMBW; |
| import org.apache.cxf.jaxrs.model.BeanParamInfo; |
| import org.apache.cxf.jaxrs.model.BeanResourceInfo; |
| import org.apache.cxf.jaxrs.model.ClassResourceInfo; |
| import org.apache.cxf.jaxrs.model.ClassResourceInfoComparator; |
| import org.apache.cxf.jaxrs.model.MethodInvocationInfo; |
| import org.apache.cxf.jaxrs.model.OperationResourceInfo; |
| import org.apache.cxf.jaxrs.model.OperationResourceInfoComparator; |
| import org.apache.cxf.jaxrs.model.OperationResourceInfoStack; |
| import org.apache.cxf.jaxrs.model.Parameter; |
| import org.apache.cxf.jaxrs.model.ParameterType; |
| import org.apache.cxf.jaxrs.model.ProviderInfo; |
| import org.apache.cxf.jaxrs.model.URITemplate; |
| import org.apache.cxf.jaxrs.provider.AbstractConfigurableProvider; |
| import org.apache.cxf.jaxrs.provider.ProviderFactory; |
| import org.apache.cxf.jaxrs.provider.ServerProviderFactory; |
| import org.apache.cxf.jaxrs.utils.multipart.AttachmentUtils; |
| import org.apache.cxf.message.Exchange; |
| import org.apache.cxf.message.Message; |
| import org.apache.cxf.message.MessageUtils; |
| import org.apache.cxf.phase.PhaseInterceptorChain; |
| import org.apache.cxf.service.Service; |
| |
| public final class JAXRSUtils { |
| |
| public static final MediaType ALL_TYPES = new MediaType(); |
| public static final String ROOT_RESOURCE_CLASS = "root.resource.class"; |
| public static final String IGNORE_MESSAGE_WRITERS = "ignore.message.writers"; |
| public static final String ROOT_INSTANCE = "service.root.instance"; |
| public static final String ROOT_PROVIDER = "service.root.provider"; |
| public static final String EXCEPTION_FROM_MAPPER = "exception.from.mapper"; |
| public static final String SECOND_JAXRS_EXCEPTION = "second.jaxrs.exception"; |
| public static final String PARTIAL_HIERARCHICAL_MEDIA_SUBTYPE_CHECK = |
| "media.subtype.partial.check"; |
| public static final String DOC_LOCATION = "wadl.location"; |
| public static final String MEDIA_TYPE_Q_PARAM = "q"; |
| public static final String MEDIA_TYPE_QS_PARAM = "qs"; |
| private static final String MEDIA_TYPE_DISTANCE_PARAM = "d"; |
| private static final String DEFAULT_CONTENT_TYPE = "default.content.type"; |
| private static final String KEEP_SUBRESOURCE_CANDIDATES = "keep.subresource.candidates"; |
| private static final Logger LOG = LogUtils.getL7dLogger(JAXRSUtils.class); |
| 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 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)); |
| |
| private JAXRSUtils() { |
| } |
| |
| public static boolean isStreamingOutType(Class<?> type) { |
| return STREAMING_OUT_TYPES.contains(type); |
| } |
| |
| public static List<PathSegment> getPathSegments(String thePath, boolean decode) { |
| return getPathSegments(thePath, decode, true); |
| } |
| |
| public static List<PathSegment> getPathSegments(String thePath, boolean decode, |
| boolean ignoreLastSlash) { |
| List<PathSegment> theList = |
| Arrays.asList(thePath.split("/")).stream() |
| .filter(StringUtils.notEmpty()) |
| .map(p -> new PathSegmentImpl(p, decode)) |
| .collect(Collectors.toList()); |
| |
| int len = thePath.length(); |
| if (len > 0 && thePath.charAt(len - 1) == '/') { |
| String value = ignoreLastSlash ? "" : "/"; |
| theList.add(new PathSegmentImpl(value, false)); |
| } |
| return theList; |
| } |
| |
| private static String[] getUserMediaTypes(Object provider, boolean consumes) { |
| String[] values = null; |
| if (AbstractConfigurableProvider.class.isAssignableFrom(provider.getClass())) { |
| final List<String> types; |
| if (consumes) { |
| types = ((AbstractConfigurableProvider)provider).getConsumeMediaTypes(); |
| } else { |
| types = ((AbstractConfigurableProvider)provider).getProduceMediaTypes(); |
| } |
| if (types != null) { |
| values = !types.isEmpty() ? types.toArray(new String[0]) |
| : new String[]{"*/*"}; |
| } |
| } |
| return values; |
| } |
| |
| public static List<MediaType> getProviderConsumeTypes(MessageBodyReader<?> provider) { |
| String[] values = getUserMediaTypes(provider, true); |
| |
| if (values == null) { |
| return getConsumeTypes(provider.getClass().getAnnotation(Consumes.class)); |
| } |
| return JAXRSUtils.getMediaTypes(values); |
| } |
| |
| public static List<MediaType> getProviderProduceTypes(MessageBodyWriter<?> provider) { |
| String[] values = getUserMediaTypes(provider, false); |
| if (values == null) { |
| return getProduceTypes(provider.getClass().getAnnotation(Produces.class)); |
| } |
| return JAXRSUtils.getMediaTypes(values); |
| } |
| |
| public static List<MediaType> getMediaTypes(String[] values) { |
| List<MediaType> supportedMimeTypes = new ArrayList<>(values.length); |
| for (int i = 0; i < values.length; i++) { |
| supportedMimeTypes.addAll(parseMediaTypes(values[i])); |
| } |
| return supportedMimeTypes; |
| } |
| |
| public static void injectParameters(OperationResourceInfo ori, |
| Object requestObject, |
| Message message) { |
| injectParameters(ori, ori.getClassResourceInfo(), requestObject, message); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public static void injectParameters(OperationResourceInfo ori, |
| BeanResourceInfo bri, |
| Object requestObject, |
| Message message) { |
| |
| if (bri.isSingleton() |
| && (!bri.getParameterMethods().isEmpty() || !bri.getParameterFields().isEmpty())) { |
| LOG.fine("Injecting request parameters into singleton resource is not thread-safe"); |
| } |
| // Param methods |
| MultivaluedMap<String, String> values = |
| (MultivaluedMap<String, String>)message.get(URITemplate.TEMPLATE_PARAMETERS); |
| for (Method m : bri.getParameterMethods()) { |
| Parameter p = ResourceUtils.getParameter(0, m.getAnnotations(), |
| m.getParameterTypes()[0]); |
| Object o; |
| |
| if (p.getType() == ParameterType.BEAN) { |
| o = createBeanParamValue(message, m.getParameterTypes()[0], ori); |
| } else { |
| o = createHttpParameterValue(p, |
| m.getParameterTypes()[0], |
| m.getGenericParameterTypes()[0], |
| m.getParameterAnnotations()[0], |
| message, |
| values, |
| ori); |
| } |
| InjectionUtils.injectThroughMethod(requestObject, m, o, message); |
| } |
| // Param fields |
| for (Field f : bri.getParameterFields()) { |
| Parameter p = ResourceUtils.getParameter(0, f.getAnnotations(), |
| f.getType()); |
| final Object o; |
| |
| if (p.getType() == ParameterType.BEAN) { |
| o = createBeanParamValue(message, f.getType(), ori); |
| } else { |
| o = createHttpParameterValue(p, |
| f.getType(), |
| f.getGenericType(), |
| f.getAnnotations(), |
| message, |
| values, |
| ori); |
| } |
| InjectionUtils.injectFieldValue(f, requestObject, o); |
| } |
| } |
| |
| public static Map<ClassResourceInfo, MultivaluedMap<String, String>> selectResourceClass( |
| List<ClassResourceInfo> resources, String path, Message message) { |
| |
| LOG.fine(() -> new org.apache.cxf.common.i18n.Message("START_CRI_MATCH", |
| BUNDLE, |
| path).toString()); |
| if (resources.size() == 1) { |
| MultivaluedMap<String, String> values = new MetadataMap<>(); |
| return resources.get(0).getURITemplate().match(path, values) |
| ? Collections.singletonMap(resources.get(0), values) : null; |
| } |
| |
| SortedMap<ClassResourceInfo, MultivaluedMap<String, String>> candidateList = |
| new TreeMap<ClassResourceInfo, MultivaluedMap<String, String>>( |
| new ClassResourceInfoComparator(message)); |
| |
| for (ClassResourceInfo cri : resources) { |
| MultivaluedMap<String, String> map = new MetadataMap<>(); |
| if (cri.getURITemplate().match(path, map)) { |
| candidateList.put(cri, map); |
| LOG.fine(() -> new org.apache.cxf.common.i18n.Message("CRI_SELECTED_POSSIBLY", |
| BUNDLE, |
| cri.getServiceClass().getName(), |
| path, |
| cri.getURITemplate().getValue()).toString()); |
| } else { |
| LOG.fine(() -> new org.apache.cxf.common.i18n.Message("CRI_NO_MATCH", |
| BUNDLE, |
| path, |
| cri.getServiceClass().getName()).toString()); |
| } |
| } |
| |
| if (!candidateList.isEmpty()) { |
| Map<ClassResourceInfo, MultivaluedMap<String, String>> cris = |
| new LinkedHashMap<>(candidateList.size()); |
| ClassResourceInfo firstCri = null; |
| for (Map.Entry<ClassResourceInfo, MultivaluedMap<String, String>> entry : candidateList.entrySet()) { |
| ClassResourceInfo cri = entry.getKey(); |
| if (cris.isEmpty()) { |
| firstCri = cri; |
| cris.put(cri, entry.getValue()); |
| } else if (firstCri != null |
| && URITemplate.compareTemplates(firstCri.getURITemplate(), cri.getURITemplate()) == 0) { |
| cris.put(cri, entry.getValue()); |
| } else { |
| break; |
| } |
| LOG.fine(() -> new org.apache.cxf.common.i18n.Message("CRI_SELECTED", |
| BUNDLE, |
| cri.getServiceClass().getName(), |
| path, cri.getURITemplate().getValue()).toString()); |
| } |
| return cris; |
| } |
| |
| return null; |
| } |
| public static OperationResourceInfo findTargetMethod( |
| Map<ClassResourceInfo, MultivaluedMap<String, String>> matchedResources, |
| Message message, |
| String httpMethod, |
| MultivaluedMap<String, String> matchedValues, |
| String requestContentType, |
| List<MediaType> acceptContentTypes) { |
| return findTargetMethod(matchedResources, message, httpMethod, matchedValues, |
| requestContentType, acceptContentTypes, true, true); |
| } |
| //CHECKSTYLE:OFF |
| public static OperationResourceInfo findTargetMethod( |
| Map<ClassResourceInfo, MultivaluedMap<String, String>> matchedResources, |
| Message message, |
| String httpMethod, |
| MultivaluedMap<String, String> matchedValues, |
| String requestContentType, |
| List<MediaType> acceptContentTypes, |
| boolean throwException, |
| boolean recordMatchedUri) { |
| //CHECKSTYLE:ON |
| final boolean getMethod = HttpMethod.GET.equals(httpMethod); |
| |
| MediaType requestType; |
| try { |
| requestType = toMediaType(requestContentType); |
| } catch (IllegalArgumentException ex) { |
| throw ExceptionUtils.toNotSupportedException(ex, null); |
| } |
| |
| SortedMap<OperationResourceInfo, MultivaluedMap<String, String>> candidateList = |
| new TreeMap<OperationResourceInfo, MultivaluedMap<String, String>>( |
| new OperationResourceInfoComparator(message, httpMethod, |
| getMethod, requestType, acceptContentTypes)); |
| |
| int pathMatched = 0; |
| int methodMatched = 0; |
| int consumeMatched = 0; |
| |
| List<OperationResourceInfo> finalPathSubresources = null; |
| for (Map.Entry<ClassResourceInfo, MultivaluedMap<String, String>> rEntry : matchedResources.entrySet()) { |
| ClassResourceInfo resource = rEntry.getKey(); |
| MultivaluedMap<String, String> values = rEntry.getValue(); |
| |
| String path = getCurrentPath(values); |
| LOG.fine(() -> new org.apache.cxf.common.i18n.Message("START_OPER_MATCH", |
| BUNDLE, |
| resource.getServiceClass().getName()).toString()); |
| |
| for (OperationResourceInfo ori : resource.getMethodDispatcher().getOperationResourceInfos()) { |
| boolean added = false; |
| |
| URITemplate uriTemplate = ori.getURITemplate(); |
| MultivaluedMap<String, String> map = new MetadataMap<>(values); |
| if (uriTemplate != null && uriTemplate.match(path, map)) { |
| String finalGroup = map.getFirst(URITemplate.FINAL_MATCH_GROUP); |
| boolean finalPath = StringUtils.isEmpty(finalGroup) || PATH_SEGMENT_SEP.equals(finalGroup); |
| |
| if (ori.isSubResourceLocator()) { |
| candidateList.put(ori, map); |
| if (finalPath) { |
| if (finalPathSubresources == null) { |
| finalPathSubresources = new LinkedList<>(); |
| } |
| finalPathSubresources.add(ori); |
| } |
| added = true; |
| } else if (finalPath) { |
| pathMatched++; |
| if (matchHttpMethod(ori.getHttpMethod(), httpMethod)) { |
| methodMatched++; |
| //CHECKSTYLE:OFF |
| if (getMethod || matchConsumeTypes(requestType, ori)) { |
| consumeMatched++; |
| for (MediaType acceptType : acceptContentTypes) { |
| if (matchProduceTypes(acceptType, ori)) { |
| candidateList.put(ori, map); |
| added = true; |
| break; |
| } |
| } |
| } |
| //CHECKSTYLE:ON |
| } |
| } |
| } |
| LOG.fine(matchMessageLogSupplier(ori, path, httpMethod, requestType, acceptContentTypes, added)); |
| } |
| } |
| if (finalPathSubresources != null && pathMatched > 0 |
| && !MessageUtils.getContextualBoolean(message, KEEP_SUBRESOURCE_CANDIDATES, false)) { |
| for (OperationResourceInfo key : finalPathSubresources) { |
| candidateList.remove(key); |
| } |
| } |
| if (!candidateList.isEmpty()) { |
| Map.Entry<OperationResourceInfo, MultivaluedMap<String, String>> firstEntry = |
| candidateList.entrySet().iterator().next(); |
| matchedValues.clear(); |
| matchedValues.putAll(firstEntry.getValue()); |
| OperationResourceInfo ori = firstEntry.getKey(); |
| if (headMethodPossible(ori.getHttpMethod(), httpMethod)) { |
| LOG.info(new org.apache.cxf.common.i18n.Message("GET_INSTEAD_OF_HEAD", |
| BUNDLE, ori.getClassResourceInfo().getServiceClass().getName(), |
| ori.getMethodToInvoke().getName()).toString()); |
| } |
| LOG.fine(() -> new org.apache.cxf.common.i18n.Message("OPER_SELECTED", |
| BUNDLE, ori.getMethodToInvoke().getName(), |
| ori.getClassResourceInfo().getServiceClass().getName()).toString()); |
| if (!ori.isSubResourceLocator()) { |
| MediaType responseMediaType = intersectSortMediaTypes(acceptContentTypes, |
| ori.getProduceTypes(), |
| false).get(0); |
| message.getExchange().put(Message.CONTENT_TYPE, mediaTypeToString(responseMediaType, |
| MEDIA_TYPE_Q_PARAM, |
| MEDIA_TYPE_QS_PARAM)); |
| } |
| if (recordMatchedUri) { |
| pushOntoStack(ori, matchedValues, message); |
| } |
| return ori; |
| } |
| |
| if (!throwException) { |
| return null; |
| } |
| |
| int status; |
| |
| // criteria matched the least number of times will determine the error code; |
| // priority : path, method, consumes, produces; |
| if (pathMatched == 0) { |
| status = 404; |
| } else if (methodMatched == 0) { |
| status = 405; |
| } else if (consumeMatched == 0) { |
| status = 415; |
| } else { |
| // Not a single Produces match |
| status = 406; |
| } |
| Map.Entry<ClassResourceInfo, MultivaluedMap<String, String>> firstCri = |
| matchedResources.entrySet().iterator().next(); |
| String name = firstCri.getKey().isRoot() ? "NO_OP_EXC" : "NO_SUBRESOURCE_METHOD_FOUND"; |
| org.apache.cxf.common.i18n.Message errorMsg = |
| new org.apache.cxf.common.i18n.Message(name, |
| BUNDLE, |
| message.get(Message.REQUEST_URI), |
| getCurrentPath(firstCri.getValue()), |
| httpMethod, |
| mediaTypeToString(requestType), |
| convertTypesToString(acceptContentTypes)); |
| if (!"OPTIONS".equalsIgnoreCase(httpMethod)) { |
| Level logLevel = getExceptionLogLevel(message, ClientErrorException.class); |
| LOG.log(logLevel == null ? Level.FINE : logLevel, () -> errorMsg.toString()); |
| } |
| Response response = |
| createResponse(getRootResources(message), message, errorMsg.toString(), status, methodMatched == 0); |
| throw ExceptionUtils.toHttpException(null, response); |
| |
| } |
| |
| |
| |
| public static Level getExceptionLogLevel(Message message, Class<? extends WebApplicationException> exClass) { |
| Level logLevel = null; |
| Object logLevelProp = message.get(exClass.getName() + ".log.level"); |
| if (logLevelProp != null) { |
| if (logLevelProp instanceof Level) { |
| logLevel = (Level)logLevelProp; |
| } else { |
| try { |
| logLevel = Level.parse(logLevelProp.toString()); |
| } catch (Exception ex) { |
| // ignore |
| } |
| } |
| } |
| return logLevel; |
| } |
| |
| private static List<MediaType> intersectSortMediaTypes(List<MediaType> acceptTypes, |
| List<MediaType> producesTypes, |
| final boolean checkDistance) { |
| List<MediaType> all = intersectMimeTypes(acceptTypes, producesTypes, true, checkDistance); |
| if (all.size() > 1) { |
| all.sort(new Comparator<MediaType>() { |
| |
| public int compare(MediaType mt1, MediaType mt2) { |
| int result = compareMediaTypes(mt1, mt2, null); |
| if (result == 0) { |
| result = compareQualityAndDistance(mt1, mt2, checkDistance); |
| } |
| return result; |
| } |
| |
| }); |
| } |
| return all; |
| } |
| |
| private static int compareQualityAndDistance(MediaType mt1, MediaType mt2, boolean checkDistance) { |
| int result = compareMediaTypesQualityFactors(mt1, mt2, MEDIA_TYPE_Q_PARAM); |
| if (result == 0) { |
| result = compareMediaTypesQualityFactors(mt1, mt2, MEDIA_TYPE_QS_PARAM); |
| } |
| if (result == 0 && checkDistance) { |
| Integer dist1 = Integer.valueOf(mt1.getParameters().get(MEDIA_TYPE_DISTANCE_PARAM)); |
| Integer dist2 = Integer.valueOf(mt2.getParameters().get(MEDIA_TYPE_DISTANCE_PARAM)); |
| result = dist1.compareTo(dist2); |
| } |
| return result; |
| } |
| |
| private static String getCurrentPath(MultivaluedMap<String, String> values) { |
| String path = values.getFirst(URITemplate.FINAL_MATCH_GROUP); |
| return path == null ? "/" : path; |
| } |
| |
| public static List<ClassResourceInfo> getRootResources(Message message) { |
| Service service = message.getExchange().getService(); |
| return ((JAXRSServiceImpl)service).getClassResourceInfos(); |
| } |
| |
| public static boolean noResourceMethodForOptions(Response exResponse, String httpMethod) { |
| return exResponse != null && exResponse.getStatus() == 405 |
| && "OPTIONS".equalsIgnoreCase(httpMethod); |
| } |
| |
| private static Supplier<String> matchMessageLogSupplier(OperationResourceInfo ori, |
| String path, String httpMethod, MediaType requestType, List<MediaType> acceptContentTypes, |
| boolean added) { |
| org.apache.cxf.common.i18n.Message errorMsg = added |
| ? new org.apache.cxf.common.i18n.Message("OPER_SELECTED_POSSIBLY", |
| BUNDLE, ori.getMethodToInvoke().getName()) |
| : new org.apache.cxf.common.i18n.Message("OPER_NO_MATCH", |
| BUNDLE, |
| ori.getMethodToInvoke().getName(), |
| path, |
| ori.getURITemplate().getValue(), |
| httpMethod, |
| ori.getHttpMethod(), |
| requestType.toString(), |
| convertTypesToString(ori.getConsumeTypes()), |
| convertTypesToString(acceptContentTypes), |
| convertTypesToString(ori.getProduceTypes())); |
| return () -> errorMsg.toString(); |
| } |
| |
| public static Response createResponse(List<ClassResourceInfo> cris, Message msg, |
| String responseMessage, int status, boolean addAllow) { |
| ResponseBuilder rb = toResponseBuilder(status); |
| if (addAllow) { |
| Set<String> allowedMethods = new HashSet<>(); |
| for (ClassResourceInfo cri : cris) { |
| allowedMethods.addAll(cri.getAllowedMethods()); |
| } |
| |
| for (String m : allowedMethods) { |
| rb.header("Allow", m); |
| } |
| // "OPTIONS" are supported all the time really |
| if (!allowedMethods.contains("OPTIONS")) { |
| rb.header("Allow", "OPTIONS"); |
| } |
| if (!allowedMethods.contains("HEAD") && allowedMethods.contains("GET")) { |
| rb.header("Allow", "HEAD"); |
| } |
| } |
| if (msg != null && MessageUtils.getContextualBoolean(msg, REPORT_FAULT_MESSAGE_PROPERTY)) { |
| rb.type(MediaType.TEXT_PLAIN_TYPE).entity(responseMessage); |
| } |
| return rb.build(); |
| } |
| |
| private static boolean matchHttpMethod(String expectedMethod, String httpMethod) { |
| return expectedMethod.equalsIgnoreCase(httpMethod) |
| || headMethodPossible(expectedMethod, httpMethod) |
| || expectedMethod.equals(DefaultMethod.class.getSimpleName()); |
| } |
| |
| public static boolean headMethodPossible(String expectedMethod, String httpMethod) { |
| return HttpMethod.HEAD.equalsIgnoreCase(httpMethod) && HttpMethod.GET.equals(expectedMethod); |
| } |
| |
| private static String convertTypesToString(List<MediaType> types) { |
| StringBuilder sb = new StringBuilder(); |
| for (MediaType type : types) { |
| sb.append(mediaTypeToString(type)).append(','); |
| } |
| return sb.toString(); |
| } |
| |
| public static List<MediaType> getConsumeTypes(Consumes cm) { |
| return getConsumeTypes(cm, Collections.singletonList(ALL_TYPES)); |
| } |
| |
| public static List<MediaType> getConsumeTypes(Consumes cm, List<MediaType> defaultTypes) { |
| return cm == null ? defaultTypes |
| : getMediaTypes(cm.value()); |
| } |
| |
| public static List<MediaType> getProduceTypes(Produces pm) { |
| return getProduceTypes(pm, Collections.singletonList(ALL_TYPES)); |
| } |
| |
| public static List<MediaType> getProduceTypes(Produces pm, List<MediaType> defaultTypes) { |
| return pm == null ? defaultTypes |
| : getMediaTypes(pm.value()); |
| } |
| |
| public static int compareSortedConsumesMediaTypes(List<MediaType> mts1, List<MediaType> mts2, MediaType ct) { |
| List<MediaType> actualMts1 = getCompatibleMediaTypes(mts1, ct); |
| List<MediaType> actualMts2 = getCompatibleMediaTypes(mts2, ct); |
| return compareSortedMediaTypes(actualMts1, actualMts2, null); |
| } |
| |
| public static int compareSortedAcceptMediaTypes(List<MediaType> mts1, List<MediaType> mts2, |
| List<MediaType> acceptTypes) { |
| List<MediaType> actualMts1 = intersectSortMediaTypes(mts1, acceptTypes, true); |
| List<MediaType> actualMts2 = intersectSortMediaTypes(mts2, acceptTypes, true); |
| int size1 = actualMts1.size(); |
| int size2 = actualMts2.size(); |
| for (int i = 0; i < size1 && i < size2; i++) { |
| int result = compareMediaTypes(actualMts1.get(i), actualMts2.get(i), null); |
| if (result == 0) { |
| result = compareQualityAndDistance(actualMts1.get(i), actualMts2.get(i), true); |
| } |
| if (result != 0) { |
| return result; |
| } |
| } |
| return size1 == size2 ? 0 : size1 < size2 ? -1 : 1; |
| } |
| |
| private static List<MediaType> getCompatibleMediaTypes(List<MediaType> mts, MediaType ct) { |
| List<MediaType> actualMts; |
| if (mts.size() == 1) { |
| actualMts = mts; |
| } else { |
| actualMts = new LinkedList<>(); |
| for (MediaType mt : mts) { |
| if (isMediaTypeCompatible(mt, ct)) { |
| actualMts.add(mt); |
| } |
| } |
| } |
| return actualMts; |
| } |
| |
| public static int compareSortedMediaTypes(List<MediaType> mts1, List<MediaType> mts2, String qs) { |
| int size1 = mts1.size(); |
| int size2 = mts2.size(); |
| for (int i = 0; i < size1 && i < size2; i++) { |
| int result = compareMediaTypes(mts1.get(i), mts2.get(i), qs); |
| if (result != 0) { |
| return result; |
| } |
| } |
| return size1 == size2 ? 0 : size1 < size2 ? -1 : 1; |
| } |
| |
| public static int compareMethodParameters(Class<?>[] paraList1, Class<?>[] paraList2) { |
| int size1 = paraList1.length; |
| int size2 = paraList2.length; |
| for (int i = 0; i < size1 && i < size2; i++) { |
| if (!paraList1[i].equals(paraList2[i])) { |
| // Handling the case when bridge / synthetic methods may be taken |
| // into account (f.e. when service implements generic interfaces or |
| // extends the generic classes). |
| if (paraList1[i].isAssignableFrom(paraList2[i])) { |
| return 1; |
| } else if (paraList2[i].isAssignableFrom(paraList1[i])) { |
| return -1; |
| } else { |
| int result = paraList1[i].getName().compareTo(paraList2[i].getName()); |
| if (result != 0) { |
| return result; |
| } |
| } |
| } |
| } |
| return size1 == size2 ? 0 : size1 < size2 ? -1 : 1; |
| } |
| public static int compareMediaTypes(MediaType mt1, MediaType mt2) { |
| return compareMediaTypes(mt1, mt2, MEDIA_TYPE_Q_PARAM); |
| } |
| public static int compareMediaTypes(MediaType mt1, MediaType mt2, String qs) { |
| |
| boolean mt1TypeWildcard = mt1.isWildcardType(); |
| boolean mt2TypeWildcard = mt2.isWildcardType(); |
| if (mt1TypeWildcard && !mt2TypeWildcard) { |
| return 1; |
| } |
| if (!mt1TypeWildcard && mt2TypeWildcard) { |
| return -1; |
| } |
| |
| boolean mt1SubTypeWildcard = mt1.getSubtype().contains(MediaType.MEDIA_TYPE_WILDCARD); |
| boolean mt2SubTypeWildcard = mt2.getSubtype().contains(MediaType.MEDIA_TYPE_WILDCARD); |
| if (mt1SubTypeWildcard && !mt2SubTypeWildcard) { |
| return 1; |
| } |
| if (!mt1SubTypeWildcard && mt2SubTypeWildcard) { |
| return -1; |
| } |
| |
| return qs != null ? compareMediaTypesQualityFactors(mt1, mt2, qs) : 0; |
| } |
| |
| public static int compareMediaTypesQualityFactors(MediaType mt1, MediaType mt2) { |
| float q1 = getMediaTypeQualityFactor(mt1.getParameters().get(MEDIA_TYPE_Q_PARAM)); |
| float q2 = getMediaTypeQualityFactor(mt2.getParameters().get(MEDIA_TYPE_Q_PARAM)); |
| return Float.compare(q1, q2) * -1; |
| } |
| |
| public static int compareMediaTypesQualityFactors(MediaType mt1, MediaType mt2, String qs) { |
| float q1 = getMediaTypeQualityFactor(mt1.getParameters().get(qs)); |
| float q2 = getMediaTypeQualityFactor(mt2.getParameters().get(qs)); |
| return Float.compare(q1, q2) * -1; |
| } |
| |
| public static float getMediaTypeQualityFactor(String q) { |
| if (q == null) { |
| return 1; |
| } |
| if (q.charAt(0) == '.') { |
| q = '0' + q; |
| } |
| try { |
| return Float.parseFloat(q); |
| } catch (NumberFormatException ex) { |
| // default value will do |
| } |
| return 1; |
| } |
| |
| //Message contains following information: PATH, HTTP_REQUEST_METHOD, CONTENT_TYPE, InputStream. |
| public static List<Object> processParameters(OperationResourceInfo ori, |
| MultivaluedMap<String, String> values, |
| Message message) |
| throws IOException, WebApplicationException { |
| |
| Class<?>[] parameterTypes = ori.getInParameterTypes(); |
| List<Parameter> paramsInfo = ori.getParameters(); |
| boolean preferModelParams = paramsInfo.size() > parameterTypes.length |
| && !PropertyUtils.isTrue(message.getContextualProperty("org.apache.cxf.preferMethodParameters")); |
| |
| final int parameterTypesLength = preferModelParams ? paramsInfo.size() : parameterTypes.length; |
| if (parameterTypesLength < 1) { |
| return Collections.emptyList(); |
| } |
| |
| Type[] genericParameterTypes = ori.getInGenericParameterTypes(); |
| Annotation[][] anns = ori.getInParameterAnnotations(); |
| Object[] params = new Object[parameterTypesLength]; |
| |
| // Ensure we process all request-body parameters first, then all @*Params, etc. |
| ParamTuple[] tuple = new ParamTuple[parameterTypesLength]; |
| for (int i = 0; i < parameterTypesLength; i++) { |
| tuple[i] = new ParamTuple(); |
| if (!preferModelParams) { |
| tuple[i].param = parameterTypes[i]; |
| tuple[i].genericParam = InjectionUtils.processGenericTypeIfNeeded( |
| ori.getClassResourceInfo().getServiceClass(), tuple[i].param, genericParameterTypes[i]); |
| tuple[i].param = InjectionUtils.updateParamClassToTypeIfNeeded(tuple[i].param, |
| tuple[i].genericParam); |
| tuple[i].paramAnns = anns == null ? EMPTY_ANNOTATIONS : anns[i]; |
| } else { |
| tuple[i].param = paramsInfo.get(i).getJavaType(); |
| tuple[i].genericParam = tuple[i].param; |
| tuple[i].paramAnns = EMPTY_ANNOTATIONS; |
| } |
| if (paramsInfo.get(i).getType() == ParameterType.REQUEST_BODY) { |
| params[i] = processRequestBodyParameter(tuple[i].param, |
| tuple[i].genericParam, |
| tuple[i].paramAnns, |
| message, |
| ori); |
| } |
| } |
| for (int i = 0; i < parameterTypesLength; i++) { |
| |
| if (paramsInfo.get(i).getType() != ParameterType.REQUEST_BODY) { |
| params[i] = processParameter(tuple[i].param, |
| tuple[i].genericParam, |
| tuple[i].paramAnns, |
| paramsInfo.get(i), |
| values, |
| message, |
| ori); |
| } |
| } |
| |
| return Arrays.asList(params); |
| } |
| |
| private static class ParamTuple { |
| private Class<?> param; |
| private Type genericParam; |
| private Annotation[] paramAnns; |
| } |
| |
| private static Object processRequestBodyParameter(Class<?> parameterClass, |
| Type parameterType, |
| Annotation[] parameterAnns, |
| Message message, |
| OperationResourceInfo ori) |
| throws IOException, WebApplicationException { |
| |
| if (parameterClass == AsyncResponse.class) { |
| return new AsyncResponseImpl(message); |
| } |
| |
| String contentType = (String)message.get(Message.CONTENT_TYPE); |
| |
| if (contentType == null) { |
| String defaultCt = (String)message.getContextualProperty(DEFAULT_CONTENT_TYPE); |
| contentType = defaultCt == null ? MediaType.APPLICATION_OCTET_STREAM : defaultCt; |
| } |
| |
| final MediaType contentTypeMt = toMediaType(contentType); |
| final MessageContext mc = new MessageContextImpl(message); |
| |
| MediaType mt = mc.getHttpHeaders().getMediaType(); |
| if (mt == null) { |
| mt = contentTypeMt; |
| } |
| |
| InputStream is; |
| if (mt.isCompatible(MediaType.APPLICATION_FORM_URLENCODED_TYPE)) { |
| is = copyAndGetEntityStream(message); |
| } else { |
| is = message.getContent(InputStream.class); |
| } |
| |
| if (is == null) { |
| Reader reader = message.getContent(Reader.class); |
| if (reader != null) { |
| is = new ReaderInputStream(reader); |
| } |
| } |
| |
| return readFromMessageBody(parameterClass, |
| parameterType, |
| parameterAnns, |
| is, |
| contentTypeMt, |
| ori, |
| message); |
| } |
| |
| private static Object processParameter(Class<?> parameterClass, |
| Type parameterType, |
| Annotation[] parameterAnns, |
| Parameter parameter, |
| MultivaluedMap<String, String> values, |
| Message message, |
| OperationResourceInfo ori) |
| throws IOException, WebApplicationException { |
| |
| if (parameter.getType() == ParameterType.REQUEST_BODY) { |
| return processRequestBodyParameter(parameterClass, parameterType, parameterAnns, message, ori); |
| } else if (parameter.getType() == ParameterType.CONTEXT) { |
| return createContextValue(message, parameterType, parameterClass); |
| } else if (parameter.getType() == ParameterType.BEAN) { |
| return createBeanParamValue(message, parameterClass, ori); |
| } else { |
| |
| return createHttpParameterValue(parameter, |
| parameterClass, |
| parameterType, |
| parameterAnns, |
| message, |
| values, |
| ori); |
| } |
| } |
| |
| public static Object createHttpParameterValue(Parameter parameter, |
| Class<?> parameterClass, |
| Type genericParam, |
| Annotation[] paramAnns, |
| Message message, |
| MultivaluedMap<String, String> values, |
| OperationResourceInfo ori) { |
| |
| boolean isEncoded = parameter.isEncoded() || ori != null && ori.isEncodedEnabled(); |
| String defaultValue = parameter.getDefaultValue(); |
| if (defaultValue == null && ori != null) { |
| defaultValue = ori.getDefaultParameterValue(); |
| } |
| |
| if (parameter.getType() == ParameterType.PATH) { |
| return readFromUriParam(message, parameter.getName(), parameterClass, genericParam, |
| paramAnns, values, defaultValue, !isEncoded); |
| } |
| |
| if (parameter.getType() == ParameterType.QUERY) { |
| return readQueryString(parameter.getName(), parameterClass, genericParam, |
| paramAnns, message, defaultValue, !isEncoded); |
| } |
| |
| if (parameter.getType() == ParameterType.MATRIX) { |
| return processMatrixParam(message, parameter.getName(), parameterClass, genericParam, |
| paramAnns, defaultValue, !isEncoded); |
| } |
| |
| if (parameter.getType() == ParameterType.FORM) { |
| return processFormParam(message, parameter.getName(), parameterClass, genericParam, |
| paramAnns, defaultValue, !isEncoded); |
| } |
| |
| if (parameter.getType() == ParameterType.COOKIE) { |
| return processCookieParam(message, parameter.getName(), parameterClass, genericParam, |
| paramAnns, defaultValue); |
| } |
| |
| Object result = null; |
| if (parameter.getType() == ParameterType.HEADER) { |
| result = processHeaderParam(message, parameter.getName(), parameterClass, genericParam, |
| paramAnns, defaultValue); |
| } |
| return result; |
| } |
| |
| private static Object processMatrixParam(Message m, String key, |
| Class<?> pClass, Type genericType, |
| Annotation[] paramAnns, |
| String defaultValue, |
| boolean decode) { |
| List<PathSegment> segments = JAXRSUtils.getPathSegments( |
| (String)m.get(Message.REQUEST_URI), decode); |
| if (!segments.isEmpty()) { |
| MultivaluedMap<String, String> params = new MetadataMap<>(); |
| for (PathSegment ps : segments) { |
| MultivaluedMap<String, String> matrix = ps.getMatrixParameters(); |
| for (Map.Entry<String, List<String>> entry : matrix.entrySet()) { |
| for (String value : entry.getValue()) { |
| params.add(entry.getKey(), value); |
| } |
| } |
| } |
| |
| if ("".equals(key)) { |
| return InjectionUtils.handleBean(pClass, paramAnns, params, ParameterType.MATRIX, m, false); |
| } |
| List<String> values = params.get(key); |
| return InjectionUtils.createParameterObject(values, |
| pClass, |
| genericType, |
| paramAnns, |
| defaultValue, |
| false, |
| ParameterType.MATRIX, |
| m); |
| } |
| |
| return null; |
| } |
| |
| private static Object processFormParam(Message m, String key, |
| Class<?> pClass, Type genericType, |
| Annotation[] paramAnns, |
| String defaultValue, |
| boolean decode) { |
| |
| MessageContext mc = new MessageContextImpl(m); |
| MediaType mt = mc.getHttpHeaders().getMediaType(); |
| |
| @SuppressWarnings("unchecked") |
| MultivaluedMap<String, String> params = |
| (MultivaluedMap<String, String>)m.get(FormUtils.FORM_PARAM_MAP); |
| |
| String enc = HttpUtils.getEncoding(mt, StandardCharsets.UTF_8.name()); |
| if (params == null) { |
| params = new MetadataMap<>(); |
| m.put(FormUtils.FORM_PARAM_MAP, params); |
| |
| if (mt == null || mt.isCompatible(MediaType.APPLICATION_FORM_URLENCODED_TYPE)) { |
| InputStream entityStream = copyAndGetEntityStream(m); |
| String body = FormUtils.readBody(entityStream, enc); |
| // Do not decode unless the key is empty value, fe @FormParam("") |
| FormUtils.populateMapFromStringOrHttpRequest(params, m, body, enc, StringUtils.isEmpty(key) && decode); |
| } else { |
| if ("multipart".equalsIgnoreCase(mt.getType()) |
| && MediaType.MULTIPART_FORM_DATA_TYPE.isCompatible(mt)) { |
| MultipartBody body = AttachmentUtils.getMultipartBody(mc); |
| FormUtils.populateMapFromMultipart(params, body, m, decode); |
| } else { |
| org.apache.cxf.common.i18n.Message errorMsg = |
| new org.apache.cxf.common.i18n.Message("WRONG_FORM_MEDIA_TYPE", |
| BUNDLE, |
| mt.toString()); |
| LOG.warning(errorMsg.toString()); |
| throw ExceptionUtils.toNotSupportedException(null, null); |
| } |
| } |
| } |
| |
| if (decode) { |
| List<String> values = params.get(key); |
| if (values != null) { |
| values = values.stream().map(value -> HttpUtils.urlDecode(value, enc)).collect(Collectors.toList()); |
| params.replace(key, values); |
| } |
| } |
| |
| if ("".equals(key)) { |
| return InjectionUtils.handleBean(pClass, paramAnns, params, ParameterType.FORM, m, false); |
| } |
| List<String> results = params.get(key); |
| |
| return InjectionUtils.createParameterObject(results, |
| pClass, |
| genericType, |
| paramAnns, |
| defaultValue, |
| false, |
| ParameterType.FORM, |
| m); |
| } |
| |
| |
| public static MultivaluedMap<String, String> getMatrixParams(String path, boolean decode) { |
| int index = path.indexOf(';'); |
| return index == -1 ? new MetadataMap<String, String>() |
| : JAXRSUtils.getStructuredParams(path.substring(index + 1), ";", decode, false); |
| } |
| |
| private static Object processHeaderParam(Message m, |
| String header, |
| Class<?> pClass, |
| Type genericType, |
| Annotation[] paramAnns, |
| String defaultValue) { |
| |
| List<String> values = new HttpHeadersImpl(m).getRequestHeader(header); |
| if (values != null && values.isEmpty()) { |
| values = null; |
| } |
| return InjectionUtils.createParameterObject(values, |
| pClass, |
| genericType, |
| paramAnns, |
| defaultValue, |
| false, |
| ParameterType.HEADER, |
| m); |
| |
| |
| } |
| |
| private static Object processCookieParam(Message m, String cookieName, |
| Class<?> pClass, Type genericType, |
| Annotation[] paramAnns, String defaultValue) { |
| Cookie c = new HttpHeadersImpl(m).getCookies().get(cookieName); |
| |
| if (c == null && defaultValue != null) { |
| c = Cookie.valueOf(cookieName + '=' + defaultValue); |
| } |
| if (c == null) { |
| return null; |
| } |
| |
| if (pClass.isAssignableFrom(Cookie.class)) { |
| return c; |
| } |
| String value = InjectionUtils.isSupportedCollectionOrArray(pClass) |
| && InjectionUtils.getActualType(genericType) == Cookie.class |
| ? c.toString() : c.getValue(); |
| return InjectionUtils.createParameterObject(Collections.singletonList(value), |
| pClass, |
| genericType, |
| paramAnns, |
| null, |
| false, |
| ParameterType.COOKIE, |
| m); |
| } |
| |
| public static Object createBeanParamValue(Message m, Class<?> clazz, OperationResourceInfo ori) { |
| BeanParamInfo bmi = ServerProviderFactory.getInstance(m).getBeanParamInfo(clazz); |
| if (bmi == null) { |
| // we could've started introspecting now but the fact no bean info |
| // is available indicates that the one created at start up has been |
| // lost and hence it is 500 |
| LOG.warning("Bean parameter info is not available"); |
| throw ExceptionUtils.toInternalServerErrorException(null, null); |
| } |
| Object instance; |
| try { |
| instance = clazz.newInstance(); |
| } catch (Throwable t) { |
| throw ExceptionUtils.toInternalServerErrorException(t, null); |
| } |
| JAXRSUtils.injectParameters(ori, bmi, instance, m); |
| |
| InjectionUtils.injectContexts(instance, bmi, m); |
| |
| return instance; |
| } |
| |
| public static Message getContextMessage(Message m) { |
| |
| Message contextMessage = m.getExchange() != null ? m.getExchange().getInMessage() : m; |
| if (contextMessage == null && !PropertyUtils.isTrue(m.get(Message.INBOUND_MESSAGE))) { |
| contextMessage = m; |
| } |
| return contextMessage; |
| } |
| |
| public static <T> T createContextValue(Message m, Type genericType, Class<T> clazz) { |
| |
| Message contextMessage = getContextMessage(m); |
| Object o = null; |
| if (UriInfo.class.isAssignableFrom(clazz)) { |
| o = createUriInfo(contextMessage); |
| } else if (HttpHeaders.class.isAssignableFrom(clazz) |
| || ProtocolHeaders.class.isAssignableFrom(clazz)) { |
| o = createHttpHeaders(contextMessage, clazz); |
| } else if (SecurityContext.class.isAssignableFrom(clazz)) { |
| SecurityContext customContext = contextMessage.get(SecurityContext.class); |
| o = customContext == null ? new SecurityContextImpl(contextMessage) : customContext; |
| } else if (MessageContext.class.isAssignableFrom(clazz)) { |
| o = new MessageContextImpl(m); |
| } 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)); |
| } else if (Request.class.isAssignableFrom(clazz)) { |
| o = new RequestImpl(contextMessage); |
| } else if (Providers.class.isAssignableFrom(clazz)) { |
| o = new ProvidersImpl(contextMessage); |
| } else if (ContextResolver.class.isAssignableFrom(clazz)) { |
| o = createContextResolver(genericType, contextMessage); |
| } else if (Configuration.class.isAssignableFrom(clazz)) { |
| o = ProviderFactory.getInstance(contextMessage).getConfiguration(contextMessage); |
| } else if (Application.class.isAssignableFrom(clazz)) { |
| ProviderInfo<?> providerInfo = |
| (ProviderInfo<?>)contextMessage.getExchange().getEndpoint().get(Application.class.getName()); |
| o = providerInfo == null ? null : providerInfo.getProvider(); |
| } else if (contextMessage != null) { |
| ContextProvider<?> provider = |
| ProviderFactory.getInstance(contextMessage).createContextProvider(clazz, contextMessage); |
| if (provider != null) { |
| o = provider.createContext(contextMessage); |
| } |
| } |
| if (o == null && contextMessage != null && !MessageUtils.isRequestor(contextMessage)) { |
| o = HttpUtils.createServletResourceValue(contextMessage, clazz); |
| } |
| return clazz.cast(o); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private static UriInfo createUriInfo(Message m) { |
| if (MessageUtils.isRequestor(m)) { |
| m = m.getExchange() != null ? m.getExchange().getOutMessage() : m; |
| } |
| MultivaluedMap<String, String> templateParams = |
| (MultivaluedMap<String, String>)m.get(URITemplate.TEMPLATE_PARAMETERS); |
| return new UriInfoImpl(m, templateParams); |
| } |
| |
| private static Object createHttpHeaders(Message m, Class<?> ctxClass) { |
| if (MessageUtils.isRequestor(m)) { |
| m = m.getExchange() != null ? m.getExchange().getOutMessage() : m; |
| } |
| return HttpHeaders.class.isAssignableFrom(ctxClass) ? new HttpHeadersImpl(m) |
| : new ProtocolHeadersImpl(m); |
| } |
| |
| public static ContextResolver<?> createContextResolver(Type genericType, Message m) { |
| if (genericType instanceof ParameterizedType) { |
| return ProviderFactory.getInstance(m).createContextResolver( |
| ((ParameterizedType)genericType).getActualTypeArguments()[0], m); |
| } else if (m != null) { |
| return ProviderFactory.getInstance(m).createContextResolver(genericType, m); |
| } else { |
| return null; |
| } |
| } |
| |
| public static Object createResourceValue(Message m, Type genericType, Class<?> clazz) { |
| |
| // lets assume we're aware of servlet types only that can be @Resource-annotated |
| return createContextValue(m, genericType, clazz); |
| } |
| |
| |
| //CHECKSTYLE:OFF |
| private static Object readFromUriParam(Message m, |
| String parameterName, |
| Class<?> paramType, |
| Type genericType, |
| Annotation[] paramAnns, |
| MultivaluedMap<String, String> values, |
| String defaultValue, |
| boolean decoded) { |
| //CHECKSTYLE:ON |
| if ("".equals(parameterName)) { |
| return InjectionUtils.handleBean(paramType, paramAnns, values, ParameterType.PATH, m, decoded); |
| } |
| List<String> results = values.get(parameterName); |
| return InjectionUtils.createParameterObject(results, |
| paramType, |
| genericType, |
| paramAnns, |
| defaultValue, |
| decoded, |
| ParameterType.PATH, |
| m); |
| } |
| |
| |
| |
| //TODO : multiple query string parsing, do it once |
| private static Object readQueryString(String queryName, |
| Class<?> paramType, |
| Type genericType, |
| Annotation[] paramAnns, |
| Message m, |
| String defaultValue, |
| boolean decode) { |
| |
| MultivaluedMap<String, String> queryMap = new UriInfoImpl(m, null).getQueryParameters(decode); |
| |
| if ("".equals(queryName)) { |
| return InjectionUtils.handleBean(paramType, paramAnns, queryMap, ParameterType.QUERY, m, false); |
| } |
| return InjectionUtils.createParameterObject(queryMap.get(queryName), |
| paramType, |
| genericType, |
| paramAnns, |
| defaultValue, |
| false, |
| ParameterType.QUERY, m); |
| } |
| |
| |
| |
| /** |
| * Retrieve map of query parameters from the passed in message |
| * @return a Map of query parameters. |
| */ |
| public static MultivaluedMap<String, String> getStructuredParams(String query, |
| String sep, |
| boolean decode, |
| boolean decodePlus) { |
| MultivaluedMap<String, String> map = |
| new MetadataMap<>(new LinkedHashMap<String, List<String>>()); |
| |
| getStructuredParams(map, query, sep, decode, decodePlus); |
| |
| return map; |
| } |
| |
| public static void getStructuredParams(MultivaluedMap<String, String> queries, |
| String query, |
| String sep, |
| boolean decode, |
| boolean decodePlus) { |
| getStructuredParams(queries, query, sep, decode, decodePlus, false); |
| } |
| |
| public static void getStructuredParams(MultivaluedMap<String, String> queries, |
| String query, |
| String sep, |
| boolean decode, |
| boolean decodePlus, |
| boolean valueIsCollection) { |
| if (!StringUtils.isEmpty(query)) { |
| for (String part : query.split(sep)) { // fastpath expected |
| int index = part.indexOf('='); |
| final String name; |
| String value = null; |
| if (index == -1) { |
| name = part; |
| } else { |
| name = part.substring(0, index); |
| value = index < part.length() ? part.substring(index + 1) : ""; |
| } |
| if (valueIsCollection) { |
| if (value != null) { |
| for (String s : value.split(",")) { |
| addStructuredPartToMap(queries, sep, name, s, decode, decodePlus); |
| } |
| } |
| } else { |
| addStructuredPartToMap(queries, sep, name, value, decode, decodePlus); |
| } |
| } |
| } |
| } |
| |
| private static void addStructuredPartToMap(MultivaluedMap<String, String> queries, |
| String sep, |
| String name, |
| String value, |
| boolean decode, |
| boolean decodePlus) { |
| |
| if (value != null) { |
| if (decodePlus && value.contains("+")) { |
| value = value.replace('+', ' '); |
| } |
| if (decode) { |
| value = (";".equals(sep)) |
| ? HttpUtils.pathDecode(value) : HttpUtils.urlDecode(value); |
| } |
| } |
| |
| queries.add(HttpUtils.urlDecode(name), value); |
| } |
| |
| private static Object readFromMessageBody(Class<?> targetTypeClass, |
| Type parameterType, |
| Annotation[] parameterAnnotations, |
| InputStream is, |
| MediaType contentType, |
| OperationResourceInfo ori, |
| Message m) throws IOException, WebApplicationException { |
| |
| List<MediaType> types = JAXRSUtils.intersectMimeTypes(ori.getConsumeTypes(), contentType); |
| |
| final ProviderFactory pf = ServerProviderFactory.getInstance(m); |
| for (MediaType type : types) { |
| List<ReaderInterceptor> readers = pf.createMessageBodyReaderInterceptor( |
| targetTypeClass, |
| parameterType, |
| parameterAnnotations, |
| type, |
| m, |
| true, |
| ori.getNameBindings()); |
| if (readers != null) { |
| try { |
| return readFromMessageBodyReader(readers, |
| targetTypeClass, |
| parameterType, |
| parameterAnnotations, |
| is, |
| type, |
| m); |
| } catch (IOException e) { |
| if (e.getClass().getName().equals(NO_CONTENT_EXCEPTION)) { |
| throw ExceptionUtils.toBadRequestException(e, null); |
| } |
| throw e; |
| } catch (WebApplicationException ex) { |
| throw ex; |
| } catch (Exception ex) { |
| throw new Fault(ex); |
| } |
| } |
| } |
| |
| logMessageHandlerProblem("NO_MSG_READER", targetTypeClass, contentType); |
| throw new WebApplicationException(Response.Status.UNSUPPORTED_MEDIA_TYPE); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public static Object readFromMessageBodyReader(List<ReaderInterceptor> readers, |
| Class<?> targetTypeClass, |
| Type parameterType, |
| Annotation[] parameterAnnotations, |
| InputStream is, |
| MediaType mediaType, |
| Message m) throws IOException, WebApplicationException { |
| |
| // Verbose but avoids an extra context instantiation for the typical path |
| if (readers.size() > 1) { |
| ReaderInterceptor first = readers.remove(0); |
| ReaderInterceptorContext context = new ReaderInterceptorContextImpl(targetTypeClass, |
| parameterType, |
| parameterAnnotations, |
| is, |
| m, |
| readers); |
| |
| return first.aroundReadFrom(context); |
| } |
| MessageBodyReader<?> provider = ((ReaderInterceptorMBR)readers.get(0)).getMBR(); |
| @SuppressWarnings("rawtypes") |
| Class cls = targetTypeClass; |
| return provider.readFrom( |
| cls, parameterType, parameterAnnotations, mediaType, |
| new HttpHeadersImpl(m).getRequestHeaders(), is); |
| } |
| |
| |
| //CHECKSTYLE:OFF |
| public static void writeMessageBody(List<WriterInterceptor> writers, |
| Object entity, |
| Class<?> type, Type genericType, |
| Annotation[] annotations, |
| MediaType mediaType, |
| MultivaluedMap<String, Object> httpHeaders, |
| Message message) |
| throws WebApplicationException, IOException { |
| |
| OutputStream entityStream = message.getContent(OutputStream.class); |
| if ("org.apache.cxf.jaxrs.reactivestreams.server.StreamingAsyncSubscriber$StreamingResponseImpl".equals( |
| entity.getClass().getName())) { |
| //cache the OutputStream when it's reactive response |
| entityStream = new CacheAndWriteOutputStream(entityStream); |
| } |
| |
| if (writers.size() > 1) { |
| WriterInterceptor first = writers.remove(0); |
| WriterInterceptorContext context = new WriterInterceptorContextImpl(entity, |
| type, |
| genericType, |
| annotations, |
| entityStream, |
| message, |
| writers); |
| |
| first.aroundWriteTo(context); |
| } else { |
| MessageBodyWriter<Object> writer = ((WriterInterceptorMBW)writers.get(0)).getMBW(); |
| if (type == byte[].class) { |
| long size = writer.getSize(entity, type, genericType, annotations, mediaType); |
| if (size != -1) { |
| httpHeaders.putSingle(HttpHeaders.CONTENT_LENGTH, Long.toString(size)); |
| } |
| } |
| HttpUtils.convertHeaderValuesToString(httpHeaders, true); |
| writer.writeTo(entity, type, genericType, annotations, mediaType, |
| httpHeaders, |
| entityStream); |
| } |
| } |
| //CHECKSTYLE:ON |
| |
| |
| public static boolean matchConsumeTypes(MediaType requestContentType, |
| OperationResourceInfo ori) { |
| |
| return doMimeTypesIntersect(ori.getConsumeTypes(), requestContentType); |
| } |
| |
| public static boolean matchProduceTypes(MediaType acceptContentType, |
| OperationResourceInfo ori) { |
| |
| return doMimeTypesIntersect(ori.getProduceTypes(), acceptContentType); |
| } |
| |
| public static boolean matchMimeTypes(MediaType requestContentType, |
| MediaType acceptContentType, |
| OperationResourceInfo ori) { |
| |
| return doMimeTypesIntersect(ori.getConsumeTypes(), requestContentType) |
| && doMimeTypesIntersect(ori.getProduceTypes(), acceptContentType); |
| } |
| |
| public static List<MediaType> parseMediaTypes(String types) { |
| List<MediaType> acceptValues = new ArrayList<>(); |
| |
| if (types != null) { |
| int x = 0; |
| int y = types.indexOf(','); |
| while (y > 0) { |
| acceptValues.add(toMediaType(types.substring(x, y).trim())); |
| x = y + 1; |
| y = types.indexOf(',', x); |
| } |
| String lastMediaType = types.substring(x).trim(); |
| if (!lastMediaType.isEmpty()) { |
| acceptValues.add(toMediaType(lastMediaType)); |
| } |
| } else { |
| acceptValues.add(ALL_TYPES); |
| } |
| |
| return acceptValues; |
| } |
| |
| public static boolean doMimeTypesIntersect(List<MediaType> mimeTypesA, MediaType mimeTypeB) { |
| return doMimeTypesIntersect(mimeTypesA, Collections.singletonList(mimeTypeB)); |
| } |
| |
| public static boolean doMimeTypesIntersect(List<MediaType> requiredMediaTypes, List<MediaType> userMediaTypes) { |
| final NonAccumulatingIntersector intersector = new NonAccumulatingIntersector(); |
| intersectMimeTypes(requiredMediaTypes, userMediaTypes, intersector); |
| return intersector.doIntersect(); |
| } |
| |
| /** |
| * intersect two mime types |
| * |
| * @param requiredMediaTypes |
| * @param userMediaTypes |
| * @param addRequiredParamsIfPossible |
| * @return return a list of intersected mime types |
| */ |
| public static List<MediaType> intersectMimeTypes(List<MediaType> requiredMediaTypes, |
| List<MediaType> userMediaTypes, |
| boolean addRequiredParamsIfPossible) { |
| return intersectMimeTypes(requiredMediaTypes, userMediaTypes, addRequiredParamsIfPossible, false); |
| } |
| |
| public static List<MediaType> intersectMimeTypes(List<MediaType> requiredMediaTypes, |
| List<MediaType> userMediaTypes, |
| boolean addRequiredParamsIfPossible, |
| boolean addDistanceParameter) { |
| final AccumulatingIntersector intersector = new AccumulatingIntersector(addRequiredParamsIfPossible, |
| addDistanceParameter); |
| intersectMimeTypes(requiredMediaTypes, userMediaTypes, intersector); |
| return new ArrayList<>(intersector.getSupportedMimeTypeList()); |
| } |
| |
| private static void intersectMimeTypes(List<MediaType> requiredMediaTypes, List<MediaType> userMediaTypes, |
| MimeTypesIntersector intersector) { |
| |
| for (MediaType requiredType : requiredMediaTypes) { |
| for (MediaType userType : userMediaTypes) { |
| boolean isCompatible = isMediaTypeCompatible(requiredType, userType); |
| if (isCompatible) { |
| boolean parametersMatched = true; |
| for (Map.Entry<String, String> entry : userType.getParameters().entrySet()) { |
| String value = requiredType.getParameters().get(entry.getKey()); |
| if (value != null && entry.getValue() != null && !(stripDoubleQuotesIfNeeded(value) |
| .equals(stripDoubleQuotesIfNeeded(entry.getValue())))) { |
| |
| if (HTTP_CHARSET_PARAM.equals(entry.getKey()) && value.equalsIgnoreCase(entry.getValue())) { |
| continue; |
| } |
| parametersMatched = false; |
| break; |
| } |
| } |
| if (!parametersMatched) { |
| continue; |
| } |
| |
| if (!intersector.intersect(requiredType, userType)) { |
| return; |
| } |
| } |
| } |
| } |
| } |
| |
| private static String stripDoubleQuotesIfNeeded(String value) { |
| if (value != null && value.startsWith("\"") |
| && value.endsWith("\"") && value.length() > 1) { |
| value = value.substring(1, value.length() - 1); |
| } |
| return value; |
| } |
| |
| private static boolean isMediaTypeCompatible(MediaType requiredType, MediaType userType) { |
| boolean isCompatible = requiredType.isCompatible(userType); |
| if (!isCompatible && requiredType.getType().equalsIgnoreCase(userType.getType())) { |
| isCompatible = compareCompositeSubtypes(requiredType, userType, |
| PhaseInterceptorChain.getCurrentMessage()); |
| } |
| return isCompatible; |
| } |
| |
| static boolean compareCompositeSubtypes(String requiredType, String userType, |
| Message message) { |
| return compareCompositeSubtypes(toMediaType(requiredType), toMediaType(userType), message); |
| } |
| |
| private static boolean compareCompositeSubtypes(MediaType requiredType, MediaType userType, |
| Message message) { |
| boolean isCompatible = false; |
| // check if we have composite subtypes |
| String subType1 = requiredType.getSubtype(); |
| String subType2 = userType.getSubtype(); |
| |
| String subTypeAfterPlus1 = splitMediaSubType(subType1, true); |
| String subTypeAfterPlus2 = splitMediaSubType(subType2, true); |
| if (message != null && MessageUtils.getContextualBoolean(message, PARTIAL_HIERARCHICAL_MEDIA_SUBTYPE_CHECK)) { |
| if (subTypeAfterPlus1 != null || subTypeAfterPlus2 != null) { |
| boolean nullPossible = subTypeAfterPlus1 == null || subTypeAfterPlus2 == null; |
| isCompatible = subTypeAfterPlus1 == null && subTypeAfterPlus2.equals(subType1) |
| || subTypeAfterPlus2 == null && subTypeAfterPlus1.equals(subType2); |
| if (!isCompatible && !nullPossible) { |
| isCompatible = subTypeAfterPlus1.equalsIgnoreCase(subTypeAfterPlus2) |
| && (subType1.charAt(0) == '*' || subType2.charAt(0) == '*'); |
| } |
| |
| if (!isCompatible) { |
| String subTypeBeforePlus1 = splitMediaSubType(subType1, false); |
| String subTypeBeforePlus2 = splitMediaSubType(subType2, false); |
| nullPossible = subTypeBeforePlus1 == null || subTypeBeforePlus2 == null; |
| isCompatible = subTypeBeforePlus1 == null && subTypeBeforePlus2 != null |
| && subTypeBeforePlus2.equals(subType1) |
| || subTypeBeforePlus2 == null && subTypeBeforePlus1 != null |
| && subTypeBeforePlus1.equals(subType2); |
| if (!isCompatible && !nullPossible) { |
| isCompatible = subTypeBeforePlus1.equalsIgnoreCase(subTypeBeforePlus2) |
| && (subType1.charAt(subType1.length() - 1) == '*' |
| || subType2.charAt(subType2.length() - 1) == '*'); |
| } |
| } |
| } |
| } else { |
| if (subTypeAfterPlus1 != null && subTypeAfterPlus2 != null) { |
| |
| isCompatible = subTypeAfterPlus1.equalsIgnoreCase(subTypeAfterPlus2) |
| && (subType1.charAt(0) == '*' || subType2.charAt(0) == '*'); |
| |
| if (!isCompatible) { |
| String subTypeBeforePlus1 = splitMediaSubType(subType1, false); |
| String subTypeBeforePlus2 = splitMediaSubType(subType2, false); |
| |
| isCompatible = subTypeBeforePlus1 != null && subTypeBeforePlus1.equalsIgnoreCase(subTypeBeforePlus2) |
| && (subType1.charAt(subType1.length() - 1) == '*' |
| || subType2.charAt(subType2.length() - 1) == '*'); |
| } |
| } |
| } |
| return isCompatible; |
| } |
| |
| private static String splitMediaSubType(String type, boolean after) { |
| int index = type.indexOf('+'); |
| return index == -1 ? null : after ? type.substring(index + 1) : type.substring(0, index); |
| } |
| |
| public static List<MediaType> intersectMimeTypes(List<MediaType> mimeTypesA, |
| MediaType mimeTypeB) { |
| return intersectMimeTypes(mimeTypesA, |
| Collections.singletonList(mimeTypeB), false); |
| } |
| |
| public static List<MediaType> intersectMimeTypes(String mimeTypesA, |
| String mimeTypesB) { |
| return intersectMimeTypes(parseMediaTypes(mimeTypesA), |
| parseMediaTypes(mimeTypesB), |
| false); |
| } |
| |
| public static List<MediaType> sortMediaTypes(String mediaTypes, String qs) { |
| return sortMediaTypes(JAXRSUtils.parseMediaTypes(mediaTypes), qs); |
| } |
| public static List<MediaType> sortMediaTypes(List<MediaType> types, final String qs) { |
| if (types.size() > 1) { |
| Collections.sort(types, new Comparator<MediaType>() { |
| |
| public int compare(MediaType mt1, MediaType mt2) { |
| return JAXRSUtils.compareMediaTypes(mt1, mt2, qs); |
| } |
| |
| }); |
| } |
| return types; |
| } |
| |
| |
| public static <T extends Throwable> Response convertFaultToResponse(T ex, Message currentMessage) { |
| return ExceptionUtils.convertFaultToResponse(ex, currentMessage); |
| } |
| |
| public static void setMessageContentType(Message message, Response response) { |
| if (response != null) { |
| Object ct = response.getMetadata().getFirst(HttpHeaders.CONTENT_TYPE); |
| if (ct != null) { |
| Exchange ex = message.getExchange(); |
| if (ex.getInMessage() == message) { |
| ex.put(Message.CONTENT_TYPE, ct.toString()); |
| } else { |
| message.put(Message.CONTENT_TYPE, ct.toString()); |
| } |
| } |
| } |
| |
| } |
| |
| public static QName getClassQName(Class<?> type) { |
| String nsURI = PackageUtils.getNamespace(PackageUtils.getPackageName(type)); |
| if (nsURI.endsWith("/")) { |
| nsURI = nsURI.substring(0, nsURI.length() - 1); |
| } |
| return new QName(nsURI, type.getSimpleName(), "ns1"); |
| } |
| |
| public static QName convertStringToQName(String name) { |
| return DOMUtils.convertStringToQName(name, ""); |
| } |
| |
| public static boolean runContainerRequestFilters(ServerProviderFactory pf, |
| Message m, |
| boolean preMatch, |
| Set<String> names) throws IOException { |
| List<ProviderInfo<ContainerRequestFilter>> containerFilters = preMatch |
| ? pf.getPreMatchContainerRequestFilters() : pf.getPostMatchContainerRequestFilters(names); |
| if (!containerFilters.isEmpty()) { |
| ContainerRequestContext context = new ContainerRequestContextImpl(m, preMatch, false); |
| for (ProviderInfo<ContainerRequestFilter> filter : containerFilters) { |
| InjectionUtils.injectContexts(filter.getProvider(), filter, m); |
| filter.getProvider().filter(context); |
| Response response = m.getExchange().get(Response.class); |
| if (response != null) { |
| setMessageContentType(m, response); |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| public static void runContainerResponseFilters(ServerProviderFactory pf, |
| ResponseImpl r, |
| Message m, |
| OperationResourceInfo ori, |
| Method invoked) throws IOException, Throwable { |
| List<ProviderInfo<ContainerResponseFilter>> containerFilters = |
| pf.getContainerResponseFilters(ori == null ? null : ori.getNameBindings()); |
| if (!containerFilters.isEmpty()) { |
| ContainerRequestContext requestContext = |
| new ContainerRequestContextImpl(m.getExchange().getInMessage(), |
| false, |
| true); |
| ContainerResponseContext responseContext = |
| new ContainerResponseContextImpl(r, m, |
| ori == null ? null : ori.getClassResourceInfo().getServiceClass(), invoked); |
| for (ProviderInfo<ContainerResponseFilter> filter : containerFilters) { |
| InjectionUtils.injectContexts(filter.getProvider(), filter, m); |
| filter.getProvider().filter(requestContext, responseContext); |
| } |
| } |
| } |
| |
| public static String mediaTypeToString(MediaType mt, String... ignoreParams) { |
| List<String> list = ignoreParams == null || ignoreParams.length == 0 ? null |
| : Arrays.asList(ignoreParams); |
| |
| return MediaTypeHeaderProvider.typeToString(mt, list); |
| } |
| |
| public static MediaType toMediaType(String value) { |
| if (value == null) { |
| return ALL_TYPES; |
| } |
| return MediaTypeHeaderProvider.valueOf(value); |
| } |
| |
| public static Response toResponse(int status) { |
| return toResponseBuilder(status).build(); |
| } |
| |
| public static Response toResponse(Response.Status status) { |
| return toResponse(status.getStatusCode()); |
| } |
| |
| public static ResponseBuilder toResponseBuilder(int status) { |
| return new ResponseBuilderImpl().status(status); |
| } |
| |
| public static ResponseBuilder toResponseBuilder(Response.Status status) { |
| return toResponseBuilder(status.getStatusCode()); |
| } |
| |
| public static ResponseBuilder fromResponse(Response response) { |
| return fromResponse(response, true); |
| } |
| |
| public static ResponseBuilder fromResponse(Response response, boolean copyEntity) { |
| ResponseBuilder rb = toResponseBuilder(response.getStatus()); |
| if (copyEntity) { |
| rb.entity(response.getEntity()); |
| } |
| for (Map.Entry<String, List<Object>> entry : response.getMetadata().entrySet()) { |
| List<Object> values = entry.getValue(); |
| for (Object value : values) { |
| rb.header(entry.getKey(), value); |
| } |
| } |
| return rb; |
| } |
| |
| public static Response copyResponseIfNeeded(Response response) { |
| if (!(response instanceof ResponseImpl)) { |
| Response r = fromResponse(response).build(); |
| Field[] declaredFields = ReflectionUtil.getDeclaredFields(response.getClass()); |
| for (Field f : declaredFields) { |
| Class<?> declClass = f.getType(); |
| if (declClass == Annotation[].class) { |
| try { |
| Annotation[] fieldAnnotations = |
| ReflectionUtil.accessDeclaredField(f, response, Annotation[].class); |
| ((ResponseImpl)r).setEntityAnnotations(fieldAnnotations); |
| } catch (Throwable ex) { |
| LOG.warning("Custom annotations if any can not be copied"); |
| } |
| break; |
| } |
| } |
| return r; |
| } |
| return response; |
| } |
| |
| public static Message getCurrentMessage() { |
| return PhaseInterceptorChain.getCurrentMessage(); |
| } |
| |
| public static ClassResourceInfo getRootResource(Message m) { |
| return (ClassResourceInfo)m.getExchange().get(JAXRSUtils.ROOT_RESOURCE_CLASS); |
| } |
| |
| public static void pushOntoStack(OperationResourceInfo ori, |
| MultivaluedMap<String, String> params, |
| Message msg) { |
| OperationResourceInfoStack stack = msg.get(OperationResourceInfoStack.class); |
| if (stack == null) { |
| stack = new OperationResourceInfoStack(); |
| msg.put(OperationResourceInfoStack.class, stack); |
| } |
| |
| |
| final List<String> values; |
| if (params.size() <= 1) { |
| values = Collections.emptyList(); |
| } else { |
| values = new ArrayList<>(params.size() - 1); |
| addTemplateVarValues(values, params, ori.getClassResourceInfo().getURITemplate()); |
| addTemplateVarValues(values, params, ori.getURITemplate()); |
| } |
| Class<?> realClass = ori.getClassResourceInfo().getServiceClass(); |
| stack.push(new MethodInvocationInfo(ori, realClass, values)); |
| } |
| |
| private static void addTemplateVarValues(List<String> values, |
| MultivaluedMap<String, String> params, |
| URITemplate template) { |
| if (template != null) { |
| for (String var : template.getVariables()) { |
| List<String> paramValues = params.get(var); |
| if (paramValues != null) { |
| values.addAll(paramValues); |
| } |
| } |
| } |
| } |
| |
| public static String logMessageHandlerProblem(String name, Class<?> cls, MediaType ct) { |
| org.apache.cxf.common.i18n.Message errorMsg = |
| new org.apache.cxf.common.i18n.Message(name, BUNDLE, cls.getName(), mediaTypeToString(ct)); |
| String errorMessage = errorMsg.toString(); |
| LOG.severe(errorMessage); |
| return errorMessage; |
| } |
| |
| /** |
| * Get path URI template, combining base path, class & method & subresource templates |
| * @param message message instance |
| * @param cri class resource info |
| * @param ori operation resource info |
| * @param subOri operation subresource info |
| * @return the URI template for the method in question |
| */ |
| public static String getUriTemplate(Message message, ClassResourceInfo cri, OperationResourceInfo ori, |
| OperationResourceInfo subOri) { |
| final String template = getUriTemplate(message, cri, ori); |
| final String methodPathTemplate = getUriTemplate(subOri); |
| return combineUriTemplates(template, methodPathTemplate); |
| } |
| |
| /** |
| * Get path URI template, combining base path, class & method templates |
| * @param message message instance |
| * @param cri class resource info |
| * @param ori operation resource info |
| * @return the URI template for the method in question |
| */ |
| public static String getUriTemplate(Message message, ClassResourceInfo cri, OperationResourceInfo ori) { |
| final String basePath = (String)message.get(Message.BASE_PATH); |
| final String classPathTemplate = getUriTemplate(cri); |
| final String methodPathTemplate = getUriTemplate(ori); |
| |
| // The application path (@ApplicationPath) is incorporated into Message.BASE_PATH, |
| // since it is part of the address. |
| String template = basePath; |
| if (StringUtils.isEmpty(template)) { |
| template = "/"; |
| } else if (!template.startsWith("/")) { |
| template = "/" + template; |
| } |
| |
| template = combineUriTemplates(template, classPathTemplate); |
| return combineUriTemplates(template, methodPathTemplate); |
| } |
| |
| /** |
| * Gets the URI template of the operation from its resource info |
| * to assemble final URI template |
| * @param ori operation resource info |
| * @return URI template |
| */ |
| private static String getUriTemplate(OperationResourceInfo ori) { |
| final URITemplate template = ori.getURITemplate(); |
| if (template != null) { |
| return template.getValue(); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Goes over sub-resource class resource templates (through parent chain) if necessary |
| * to assemble final URI template |
| * @param cri root or subresource class resource info |
| * @return URI template chain |
| */ |
| private static String getUriTemplate(ClassResourceInfo cri) { |
| final URITemplate template = cri.getURITemplate(); |
| if (template != null) { |
| return template.getValue(); |
| } else if (cri.getParent() != null) { /* probably subresource */ |
| return getUriTemplate(cri.getParent()); |
| } else { |
| return null; /* should not happen */ |
| } |
| } |
| |
| /** |
| * Combines two URI templates together |
| * @param parent parent URI template |
| * @param child child URI template |
| * @return the URI template combined from the parent and child |
| */ |
| private static String combineUriTemplates(final String parent, final String child) { |
| if (StringUtils.isEmpty(child)) { |
| return parent; |
| } |
| |
| // The way URI templates are normalized in org.apache.cxf.jaxrs.model.URITemplate: |
| // - empty or null become "/" |
| // - "/" is added at the start if not present |
| if ("/".equals(parent)) { |
| return child; |
| } else if ("/".equals(child)) { |
| return parent; |
| } else if (parent.endsWith("/")) { |
| // Remove only last slash |
| return parent.replaceAll("/$", "") + child; |
| } else { |
| return parent + child; |
| } |
| } |
| |
| // copy the input stream so that it is not inadvertently closed |
| private static InputStream copyAndGetEntityStream(Message m) { |
| LoadingByteArrayOutputStream baos = new LoadingByteArrayOutputStream(); |
| try (InputStream in = m.getContent(InputStream.class)) { |
| IOUtils.copy(in, baos); |
| } catch (IOException e) { |
| throw ExceptionUtils.toInternalServerErrorException(e, null); |
| } |
| m.setContent(InputStream.class, baos.createInputStream()); |
| return baos.createInputStream(); |
| } |
| } |