Repository: cxf Updated Branches: refs/heads/master 26edcd457 -> 0abb19515
[CXF-6837] Prototyping MBR/MBW cache code, a patch from Neal Hu with modifications applied Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/0abb1951 Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/0abb1951 Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/0abb1951 Branch: refs/heads/master Commit: 0abb1951545fe2e271daac373669a75e501a8434 Parents: 26edcd4 Author: Sergey Beryozkin <[email protected]> Authored: Thu Apr 14 13:31:21 2016 +0100 Committer: Sergey Beryozkin <[email protected]> Committed: Thu Apr 14 13:31:21 2016 +0100 ---------------------------------------------------------------------- .../cxf/jaxrs/provider/ProviderCache.java | 106 ++++++++++++++ .../cxf/jaxrs/provider/ProviderFactory.java | 139 +++++++++++++++---- 2 files changed, 219 insertions(+), 26 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/0abb1951/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ProviderCache.java ---------------------------------------------------------------------- diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ProviderCache.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ProviderCache.java new file mode 100644 index 0000000..37aeb7b --- /dev/null +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ProviderCache.java @@ -0,0 +1,106 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.jaxrs.provider; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.MessageBodyWriter; + +import org.apache.cxf.jaxrs.model.ProviderInfo; + +public class ProviderCache { + private static final int MAX_PROVIDER_CACHE_SIZE = + Integer.getInteger("org.apache.cxf.jaxrs.max_provider_cache_size", 100); + private final Map<String, List<ProviderInfo<MessageBodyReader<?>>>> + readerProviderCache = new ConcurrentHashMap<String, List<ProviderInfo<MessageBodyReader<?>>>>(); + + private final Map<String, List<ProviderInfo<MessageBodyWriter<?>>>> + writerProviderCache = new ConcurrentHashMap<String, List<ProviderInfo<MessageBodyWriter<?>>>>(); + + private boolean checkAllCandidates; + public ProviderCache(boolean checkAllCandidates) { + this.checkAllCandidates = checkAllCandidates; + } + + public List<ProviderInfo<MessageBodyReader<?>>> getReaders(Class<?> type, MediaType mt) { + if (readerProviderCache.isEmpty()) { + return Collections.emptyList(); + } + String key = getKey(type, mt); + + List<ProviderInfo<MessageBodyReader<?>>> list = readerProviderCache.get(key); + return list != null ? list : Collections.emptyList(); + } + public List<ProviderInfo<MessageBodyWriter<?>>> getWriters(Class<?> type, MediaType mt) { + if (writerProviderCache.isEmpty()) { + return Collections.emptyList(); + } + + String key = getKey(type, mt); + + List<ProviderInfo<MessageBodyWriter<?>>> list = writerProviderCache.get(key); + return list != null ? list : Collections.emptyList(); + } + + public void putReaders(Class<?> type, MediaType mt, List<ProviderInfo<MessageBodyReader<?>>> candidates) { + if (candidates == null || candidates.isEmpty()) { + return; + } + checkCacheSize(readerProviderCache); + + String key = getKey(type, mt); + readerProviderCache.put(key, candidates); + } + + public void putWriters(Class<?> type, MediaType mt, List<ProviderInfo<MessageBodyWriter<?>>> candidates) { + if (candidates == null || candidates.isEmpty()) { + return; + } + checkCacheSize(writerProviderCache); + + String key = getKey(type, mt); + writerProviderCache.put(key, candidates); + } + + public void destroy() { + this.readerProviderCache.clear(); + this.writerProviderCache.clear(); + } + + private String getKey(Class<?> type, MediaType mt) { + return type.getName() + "-" + mt.toString(); + } + + private static void checkCacheSize(Map<?, ?> map) { + final int size = map.size(); + if (size >= MAX_PROVIDER_CACHE_SIZE) { + map.clear(); + } + } + + public boolean isCheckAllCandidates() { + return checkAllCandidates; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/0abb1951/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java ---------------------------------------------------------------------- diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java index 2754c61..41f7346 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java @@ -91,6 +91,8 @@ public abstract class ProviderFactory { private static final String JAXB_PROVIDER_NAME = "org.apache.cxf.jaxrs.provider.JAXBElementProvider"; private static final String JSON_PROVIDER_NAME = "org.apache.cxf.jaxrs.provider.json.JSONProvider"; private static final String BUS_PROVIDERS_ALL = "org.apache.cxf.jaxrs.bus.providers"; + private static final String PROVIDER_CACHE_ALLOWED = "org.apache.cxf.jaxrs.provider.cache.allowed"; + private static final String PROVIDER_CACHE_CHECK_ALL = "org.apache.cxf.jaxrs.provider.cache.checkAllCandidates"; protected Map<NameKey, ProviderInfo<ReaderInterceptor>> readerInterceptors = new NameKeyMap<ProviderInfo<ReaderInterceptor>>(true); @@ -117,14 +119,25 @@ public abstract class ProviderFactory { private Comparator<?> providerComparator; + private ProviderCache providerCache; + protected ProviderFactory(Bus bus) { this.bus = bus; + providerCache = initCache(bus); } public Bus getBus() { return bus; } - + protected static ProviderCache initCache(Bus theBus) { + Object allowProp = theBus.getProperty(PROVIDER_CACHE_ALLOWED); + boolean allowed = allowProp == null || PropertyUtils.isTrue(allowProp); + if (!allowed) { + return null; + } + boolean checkAll = PropertyUtils.isTrue(theBus.getProperty(PROVIDER_CACHE_CHECK_ALL)); + return new ProviderCache(checkAll); + } protected static void initFactory(ProviderFactory factory) { factory.setProviders(false, false, @@ -413,13 +426,45 @@ public abstract class ProviderFactory { Annotation[] annotations, MediaType mediaType, Message m) { + // Step1: check the cache + + if (providerCache != null) { + for (ProviderInfo<MessageBodyReader<?>> ep : providerCache.getReaders(type, mediaType)) { + if (isReadable(ep, type, genericType, annotations, mediaType, m)) { + return (MessageBodyReader<T>)ep.getProvider(); + } + } + } + + boolean checkAll = providerCache != null && providerCache.isCheckAllCandidates(); + List<ProviderInfo<MessageBodyReader<?>>> allCandidates = + checkAll ? new LinkedList<ProviderInfo<MessageBodyReader<?>>>() : null; + + MessageBodyReader<T> selectedReader = null; for (ProviderInfo<MessageBodyReader<?>> ep : messageReaders) { - if (matchesReaderCriterias(ep, type, genericType, annotations, mediaType, m) + if (matchesReaderMediaTypes(ep, mediaType) && handleMapper(ep, type, m, MessageBodyReader.class, false)) { - return (MessageBodyReader<T>)ep.getProvider(); + // This writer matches Media Type and Class + if (checkAll) { + allCandidates.add(ep); + } else if (providerCache != null && providerCache.getReaders(type, mediaType).isEmpty()) { + providerCache.putReaders(type, mediaType, Collections.singletonList(ep)); + } + if (selectedReader == null + && isReadable(ep, type, genericType, annotations, mediaType, m)) { + // This writer is a selected candidate + selectedReader = (MessageBodyReader<T>)ep.getProvider(); + if (!checkAll) { + return selectedReader; + } + } + } } - return null; + if (checkAll) { + providerCache.putReaders(type, mediaType, allCandidates); + } + return selectedReader; } @SuppressWarnings("unchecked") @@ -428,13 +473,49 @@ public abstract class ProviderFactory { Annotation[] annotations, MediaType mediaType, Message m) { + + // Step1: check the cache. + if (providerCache != null) { + for (ProviderInfo<MessageBodyWriter<?>> ep : providerCache.getWriters(type, mediaType)) { + if (isWriteable(ep, type, genericType, annotations, mediaType, m)) { + return (MessageBodyWriter<T>)ep.getProvider(); + } + } + } + + // Step2: check all the registered writers + + // The cache, if enabled, may have been configured to keep the top candidate only + boolean checkAll = providerCache != null && providerCache.isCheckAllCandidates(); + List<ProviderInfo<MessageBodyWriter<?>>> allCandidates = + checkAll ? new LinkedList<ProviderInfo<MessageBodyWriter<?>>>() : null; + + MessageBodyWriter<T> selectedWriter = null; for (ProviderInfo<MessageBodyWriter<?>> ep : messageWriters) { - if (matchesWriterCriterias(ep, type, genericType, annotations, mediaType, m) + if (matchesWriterMediaTypes(ep, mediaType) && handleMapper(ep, type, m, MessageBodyWriter.class, false)) { - return (MessageBodyWriter<T>)ep.getProvider(); + // This writer matches Media Type and Class + if (checkAll) { + allCandidates.add(ep); + } else if (providerCache != null && providerCache.getWriters(type, mediaType).isEmpty()) { + providerCache.putWriters(type, mediaType, Collections.singletonList(ep)); + } + if (selectedWriter == null + && isWriteable(ep, type, genericType, annotations, mediaType, m)) { + // This writer is a selected candidate + selectedWriter = (MessageBodyWriter<T>)ep.getProvider(); + if (!checkAll) { + return selectedWriter; + } + } + } - } - return null; + } + if (checkAll) { + providerCache.putWriters(type, mediaType, allCandidates); + } + return selectedWriter; + } protected void setBusProviders() { @@ -635,33 +716,32 @@ public abstract class ProviderFactory { - private <T> boolean matchesReaderCriterias(ProviderInfo<MessageBodyReader<?>> pi, - Class<T> type, - Type genericType, - Annotation[] annotations, - MediaType mediaType, - Message m) { + private <T> boolean matchesReaderMediaTypes(ProviderInfo<MessageBodyReader<?>> pi, + MediaType mediaType) { MessageBodyReader<?> ep = pi.getProvider(); List<MediaType> supportedMediaTypes = JAXRSUtils.getProviderConsumeTypes(ep); List<MediaType> availableMimeTypes = JAXRSUtils.intersectMimeTypes(Collections.singletonList(mediaType), supportedMediaTypes, false); - if (availableMimeTypes.size() == 0) { - return false; - } + return availableMimeTypes.size() != 0; + } + + private boolean isReadable(ProviderInfo<MessageBodyReader<?>> pi, + Class<?> type, + Type genericType, + Annotation[] annotations, + MediaType mediaType, + Message m) { + MessageBodyReader<?> ep = pi.getProvider(); if (m.get(ACTIVE_JAXRS_PROVIDER_KEY) != ep) { injectContextValues(pi, m); } return ep.isReadable(type, genericType, annotations, mediaType); } - private <T> boolean matchesWriterCriterias(ProviderInfo<MessageBodyWriter<?>> pi, - Class<T> type, - Type genericType, - Annotation[] annotations, - MediaType mediaType, - Message m) { + private <T> boolean matchesWriterMediaTypes(ProviderInfo<MessageBodyWriter<?>> pi, + MediaType mediaType) { MessageBodyWriter<?> ep = pi.getProvider(); List<MediaType> supportedMediaTypes = JAXRSUtils.getProviderProduceTypes(ep); @@ -669,9 +749,16 @@ public abstract class ProviderFactory { JAXRSUtils.intersectMimeTypes(Collections.singletonList(mediaType), supportedMediaTypes, false); - if (availableMimeTypes.size() == 0) { - return false; - } + return availableMimeTypes.size() != 0; + } + + private boolean isWriteable(ProviderInfo<MessageBodyWriter<?>> pi, + Class<?> type, + Type genericType, + Annotation[] annotations, + MediaType mediaType, + Message m) { + MessageBodyWriter<?> ep = pi.getProvider(); if (m.get(ACTIVE_JAXRS_PROVIDER_KEY) != ep) { injectContextValues(pi, m); }
