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

[tomee-jakarta] branch master updated (4be503d -> 9a737a0)

This is an automated email from the ASF dual-hosted git repository.

dblevins pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/tomee-jakarta.git.


    from 4be503d  Add AnnotationUtils from 3.5.0-SNAPSHOT
     new e56ec49  cxf-rt-frontend-jaxrs changes since 3.4.3
     new 924dff9  cxf-core and cxf-rt-frontend-jaxrs changes since 3.4.3
     new 4aefc31  cxf-rt-transports-http changes since 3.4.3
     new b6b3737  cxf-rt-rs-client changes since 3.4.3
     new cafba39  cxf-rt-databinding-jaxb changes since 3.4.3
     new 9a737a0  TCK fixes reapplied

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 tomee/tomee-plume-webapp/pom.xml                   |   14 +
 .../java/org/apache/cxf/annotations/Policy.java    |   80 +
 .../apache/cxf/annotations/WSDLDocumentation.java  |   88 +
 .../cxf/attachment/AttachmentDataSource.java       |  107 +
 .../cxf/attachment/AttachmentDeserializer.java     |  462 ++++
 .../cxf/attachment/AttachmentSerializer.java       |  345 +++
 .../org/apache/cxf/attachment/AttachmentUtil.java  |  572 +++++
 .../apache/cxf/attachment/Base64DecoderStream.java |  196 ++
 .../apache/cxf/attachment/ContentDisposition.java  |  144 ++
 .../cxf/attachment/LazyAttachmentCollection.java   |  362 ++++
 .../cxf/attachment/MimeBodyPartInputStream.java    |  275 +++
 .../java/org/apache/cxf/bus/CXFBusFactory.java     |   47 +
 .../bus/blueprint/BundleDelegatingClassLoader.java |  134 ++
 .../cxf/bus/blueprint/BusDefinitionParser.java     |   72 +
 .../apache/cxf/bus/blueprint/ConfigurerImpl.java   |  180 ++
 .../org/apache/cxf/bus/extension/Extension.java    |  291 +++
 .../cxf/bus/extension/ExtensionManagerImpl.java    |  381 ++++
 .../ServiceContractResolverRegistryImpl.java       |  113 +
 .../java/org/apache/cxf/bus/osgi/CXFActivator.java |  134 ++
 .../cxf/bus/osgi/CXFExtensionBundleListener.java   |  181 ++
 .../org/apache/cxf/bus/osgi/OSGIBusListener.java   |  224 ++
 .../cxf/bus/resource/ResourceManagerImpl.java      |   85 +
 .../apache/cxf/bus/spring/BusDefinitionParser.java |  310 +++
 .../apache/cxf/bus/spring/BusEntityResolver.java   |   94 +
 .../cxf/bus/spring/BusExtensionPostProcessor.java  |   71 +
 .../cxf/bus/spring/Jsr250BeanPostProcessor.java    |  164 ++
 .../apache/cxf/bus/spring/NamespaceHandler.java    |   57 +
 .../java/org/apache/cxf/bus/spring/SpringBus.java  |  142 ++
 .../cxf/catalog/CatalogXmlSchemaURIResolver.java   |  102 +
 .../java/org/apache/cxf/common/i18n/Exception.java |   58 +
 .../java/org/apache/cxf/common/i18n/Message.java   |   91 +
 .../apache/cxf/common/i18n/UncheckedException.java |   84 +
 .../cxf/common/injection/ResourceInjector.java     |  446 ++++
 .../java/org/apache/cxf/common/jaxb/JAXBUtils.java | 1180 +++++++++++
 .../common/logging/AbstractDelegatingLogger.java   |  457 ++++
 .../org/apache/cxf/common/logging/LogUtils.java    |  485 +++++
 .../cxf/common/logging/RegexLoggingFilter.java     |  117 ++
 .../cxf/common/spi/ClassGeneratorClassLoader.java  |  153 ++
 .../cxf/common/spi/NamespaceClassGenerator.java    |  450 ++++
 .../org/apache/cxf/common/util/ASMHelperImpl.java  |  273 +++
 .../org/apache/cxf/common/util/Base64Utility.java  |  474 +++++
 .../org/apache/cxf/common/util/CachedClass.java    |   37 +
 .../org/apache/cxf/common/util/ClassHelper.java    |  140 ++
 .../apache/cxf/common/util/CollectionUtils.java    |  126 ++
 .../java/org/apache/cxf/common/util/Compiler.java  |  382 ++++
 .../common/util/ModCountCopyOnWriteArrayList.java  |  156 ++
 .../org/apache/cxf/common/util/PackageUtils.java   |  181 ++
 .../apache/cxf/common/util/ProxyClassLoader.java   |   89 +
 .../org/apache/cxf/common/util/ProxyHelper.java    |  140 ++
 .../common/util/ReflectionInvokationHandler.java   |  199 ++
 .../org/apache/cxf/common/util/SortedArraySet.java |  266 +++
 .../cxf/common/util/SpringClassUnwrapper.java      |  111 +
 .../cxf/common/util/SpringClasspathScanner.java    |  202 ++
 .../org/apache/cxf/common/util/StreamPrinter.java  |   63 +
 .../org/apache/cxf/common/util/URIParserUtil.java  |  209 ++
 .../cxf/common/xmlschema/SchemaCollection.java     |  389 ++++
 .../jsse/MultiKeyPasswordKeyManager.java           |   84 +
 .../configuration/jsse/TLSClientParameters.java    |  258 +++
 .../configuration/jsse/TLSParameterJaxBUtils.java  |  420 ++++
 .../cxf/configuration/spring/ConfigurerImpl.java   |  288 +++
 .../cxf/databinding/source/SourceDataBinding.java  |  104 +
 .../cxf/databinding/stax/StaxDataBinding.java      |  187 ++
 .../cxf/endpoint/AbstractConduitSelector.java      |  308 +++
 .../org/apache/cxf/endpoint/ClientCallback.java    |  166 ++
 .../java/org/apache/cxf/endpoint/ClientImpl.java   | 1192 +++++++++++
 .../java/org/apache/cxf/endpoint/EndpointImpl.java |  220 ++
 .../java/org/apache/cxf/endpoint/ServerImpl.java   |  221 ++
 .../org/apache/cxf/feature/FastInfosetFeature.java |  110 +
 .../org/apache/cxf/feature/WrappedFeature.java     |   61 +
 .../cxf/feature/transform/XSLTOutInterceptor.java  |  210 ++
 .../patch/java/org/apache/cxf/headers/Header.java  |   78 +
 .../java/org/apache/cxf/helpers/DOMUtils.java      |  895 ++++++++
 .../patch/java/org/apache/cxf/helpers/IOUtils.java |  428 ++++
 .../java/org/apache/cxf/helpers/ServiceUtils.java  |  214 ++
 .../AbstractFaultChainInitiatorObserver.java       |  142 ++
 .../interceptor/AbstractLoggingInterceptor.java    |  298 +++
 .../cxf/interceptor/AnnotationInterceptors.java    |  151 ++
 .../cxf/interceptor/AttachmentInInterceptor.java   |   72 +
 .../cxf/interceptor/ClientOutFaultObserver.java    |   68 +
 .../interceptor/InFaultChainInitiatorObserver.java |   84 +
 .../apache/cxf/interceptor/InterceptorChain.java   |  109 +
 .../org/apache/cxf/interceptor/LoggingMessage.java |  133 ++
 .../interceptor/OneWayProcessorInterceptor.java    |  177 ++
 .../OutFaultChainInitiatorObserver.java            |   84 +
 .../security/DefaultSecurityContext.java           |  188 ++
 .../interceptor/security/JAASLoginInterceptor.java |  233 ++
 .../cxf/internal/CXFAPINamespaceHandler.java       |  127 ++
 .../apache/cxf/io/CacheAndWriteOutputStream.java   |   96 +
 .../patch/java/org/apache/cxf/io/CachedWriter.java |  665 ++++++
 .../java/org/apache/cxf/io/ReaderInputStream.java  |  294 +++
 .../apache/cxf/io/WriteOnCloseOutputStream.java    |   46 +
 .../org/apache/cxf/jaxb/FactoryClassGenerator.java |   86 +
 .../java/org/apache/cxf/jaxb/JAXBDataBase.java     |  192 ++
 .../java/org/apache/cxf/jaxb/JAXBDataBinding.java  |  873 ++++++++
 .../org/apache/cxf/jaxb/JAXBEncoderDecoder.java    | 1119 ++++++++++
 .../org/apache/cxf/jaxb/JAXBSchemaInitializer.java |  823 ++++++++
 .../org/apache/cxf/jaxb/io/DataReaderImpl.java     |  207 ++
 .../org/apache/cxf/jaxb/io/DataWriterImpl.java     |  321 +++
 .../java/org/apache/cxf/jaxrs/JAXRSInvoker.java    |  460 ++++
 .../apache/cxf/jaxrs/JAXRSServerFactoryBean.java   |  467 ++++
 .../apache/cxf/jaxrs/client/ClientProxyImpl.java   | 1149 ++++++++++
 .../cxf/jaxrs/client/JAXRSClientFactory.java       |  414 ++++
 .../apache/cxf/jaxrs/client/LocalClientState.java  |  185 ++
 .../org/apache/cxf/jaxrs/client/WebClient.java     | 1349 ++++++++++++
 .../client/spec/ClientRequestContextImpl.java      |  186 ++
 .../JAXRSClientFactoryBeanDefinitionParser.java    |  187 ++
 .../cxf/jaxrs/impl/EntityTagHeaderProvider.java    |   77 +
 .../cxf/jaxrs/impl/MediaTypeHeaderProvider.java    |  221 ++
 .../org/apache/cxf/jaxrs/impl/RequestImpl.java     |  388 ++++
 .../apache/cxf/jaxrs/impl/ResourceContextImpl.java |   67 +
 .../impl/tl/ThreadLocalInvocationHandler.java      |   53 +
 .../cxf/jaxrs/interceptor/JAXRSInInterceptor.java  |  274 +++
 .../cxf/jaxrs/interceptor/JAXRSOutInterceptor.java |  498 +++++
 .../cxf/jaxrs/model/AbstractResourceInfo.java      |  389 ++++
 .../apache/cxf/jaxrs/model/ClassResourceInfo.java  |  366 ++++
 .../org/apache/cxf/jaxrs/model/URITemplate.java    |  627 ++++++
 .../jaxrs/provider/CachingMessageBodyReader.java   |  100 +
 .../jaxrs/provider/CachingMessageBodyWriter.java   |  102 +
 .../cxf/jaxrs/provider/DataSourceProvider.java     |  110 +
 .../cxf/jaxrs/provider/FormEncodingProvider.java   |  228 ++
 .../cxf/jaxrs/provider/JAXBElementProvider.java    |  635 ++++++
 .../cxf/jaxrs/provider/MultipartProvider.java      |  474 +++++
 .../apache/cxf/jaxrs/provider/SourceProvider.java  |  236 +++
 .../cxf/jaxrs/provider/XSLTJaxbProvider.java       |  588 ++++++
 .../jaxrs/security/JAASAuthenticationFilter.java   |  170 ++
 .../security/KerberosAuthenticationFilter.java     |  251 +++
 .../jaxrs/servlet/CXFNonSpringJaxrsServlet.java    |  640 ++++++
 .../JAXRSServerFactoryBeanDefinitionParser.java    |  286 +++
 .../java/org/apache/cxf/jaxrs/utils/HttpUtils.java |  704 +++++++
 .../org/apache/cxf/jaxrs/utils/ResourceUtils.java  | 1044 +++++++++
 .../java/org/apache/cxf/message/MessageUtils.java  |  261 +++
 .../java/org/apache/cxf/phase/PhaseChainCache.java |  137 ++
 .../apache/cxf/phase/PhaseInterceptorChain.java    |  857 ++++++++
 .../cxf/service/factory/FactoryBeanListener.java   |  145 ++
 .../apache/cxf/service/invoker/FactoryInvoker.java |   70 +
 .../service/model/AbstractPropertiesHolder.java    |  271 +++
 .../org/apache/cxf/service/model/FaultInfo.java    |   61 +
 .../apache/cxf/service/model/InterfaceInfo.java    |  124 ++
 .../apache/cxf/service/model/OperationInfo.java    |  242 +++
 .../cxf/service/model/UnwrappedOperationInfo.java  |   72 +
 .../java/org/apache/cxf/staxutils/StaxUtils.java   | 2222 ++++++++++++++++++++
 .../apache/cxf/staxutils/W3CDOMStreamReader.java   |  429 ++++
 .../cxf/transport/ChainInitiationObserver.java     |  197 ++
 .../transport/http/AbstractHTTPDestination.java    |  956 +++++++++
 .../cxf/transport/http/CXFAuthenticator.java       |  177 ++
 .../org/apache/cxf/transport/http/HTTPConduit.java | 1952 +++++++++++++++++
 .../cxf/transport/http/HTTPTransportFactory.java   |  297 +++
 .../org/apache/cxf/transport/http/Headers.java     |  583 +++++
 .../transport/http/HttpServletRequestSnapshot.java |  277 +++
 .../transport/http/ReferencingAuthenticator.java   |  234 +++
 .../http/Servlet3ContinuationProvider.java         |  281 +++
 .../transport/http/URLConnectionHTTPConduit.java   |  439 ++++
 .../cxf/transport/http/auth/HttpAuthHeader.java    |  154 ++
 .../transport/https/HttpsURLConnectionFactory.java |  247 +++
 .../transport/https/HttpsURLConnectionInfo.java    |  168 ++
 .../servicelist/ServiceListJAASAuthenticator.java  |  160 ++
 .../AbstractBeanValidationInterceptor.java         |   65 +
 .../cxf/workqueue/AutomaticWorkQueueImpl.java      |  619 ++++++
 .../apache/cxf/ws/addressing/MAPAggregator.java    |  224 ++
 159 files changed, 49263 insertions(+)
 create mode 100644 transform/src/patch/java/org/apache/cxf/annotations/Policy.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/annotations/WSDLDocumentation.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/attachment/AttachmentDataSource.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/attachment/AttachmentDeserializer.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/attachment/AttachmentSerializer.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/attachment/AttachmentUtil.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/attachment/Base64DecoderStream.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/attachment/ContentDisposition.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/attachment/LazyAttachmentCollection.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/attachment/MimeBodyPartInputStream.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/bus/CXFBusFactory.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/bus/blueprint/BundleDelegatingClassLoader.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/bus/blueprint/BusDefinitionParser.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/bus/blueprint/ConfigurerImpl.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/bus/extension/Extension.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/bus/extension/ExtensionManagerImpl.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/bus/managers/ServiceContractResolverRegistryImpl.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/bus/osgi/CXFActivator.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/bus/osgi/CXFExtensionBundleListener.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/bus/osgi/OSGIBusListener.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/bus/resource/ResourceManagerImpl.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/bus/spring/BusDefinitionParser.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/bus/spring/BusEntityResolver.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/bus/spring/BusExtensionPostProcessor.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/bus/spring/Jsr250BeanPostProcessor.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/bus/spring/NamespaceHandler.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/bus/spring/SpringBus.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/catalog/CatalogXmlSchemaURIResolver.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/i18n/Exception.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/i18n/Message.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/i18n/UncheckedException.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/injection/ResourceInjector.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/jaxb/JAXBUtils.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/logging/AbstractDelegatingLogger.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/logging/LogUtils.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/logging/RegexLoggingFilter.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/spi/ClassGeneratorClassLoader.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/spi/NamespaceClassGenerator.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/util/ASMHelperImpl.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/util/Base64Utility.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/util/CachedClass.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/util/ClassHelper.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/util/CollectionUtils.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/util/Compiler.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/util/ModCountCopyOnWriteArrayList.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/util/PackageUtils.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/util/ProxyClassLoader.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/util/ProxyHelper.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/util/ReflectionInvokationHandler.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/util/SortedArraySet.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/util/SpringClassUnwrapper.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/util/SpringClasspathScanner.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/util/StreamPrinter.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/util/URIParserUtil.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/common/xmlschema/SchemaCollection.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/configuration/jsse/MultiKeyPasswordKeyManager.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/configuration/jsse/TLSClientParameters.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/configuration/jsse/TLSParameterJaxBUtils.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/configuration/spring/ConfigurerImpl.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/databinding/source/SourceDataBinding.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/databinding/stax/StaxDataBinding.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/endpoint/AbstractConduitSelector.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/endpoint/ClientCallback.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/endpoint/ClientImpl.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/endpoint/EndpointImpl.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/endpoint/ServerImpl.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/feature/FastInfosetFeature.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/feature/WrappedFeature.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/feature/transform/XSLTOutInterceptor.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/headers/Header.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/helpers/DOMUtils.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/helpers/IOUtils.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/helpers/ServiceUtils.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/interceptor/AbstractFaultChainInitiatorObserver.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/interceptor/AbstractLoggingInterceptor.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/interceptor/AnnotationInterceptors.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/interceptor/AttachmentInInterceptor.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/interceptor/ClientOutFaultObserver.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/interceptor/InFaultChainInitiatorObserver.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/interceptor/InterceptorChain.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/interceptor/LoggingMessage.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/interceptor/OneWayProcessorInterceptor.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/interceptor/OutFaultChainInitiatorObserver.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/interceptor/security/DefaultSecurityContext.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/interceptor/security/JAASLoginInterceptor.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/internal/CXFAPINamespaceHandler.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/io/CacheAndWriteOutputStream.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/io/CachedWriter.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/io/ReaderInputStream.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/io/WriteOnCloseOutputStream.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxb/FactoryClassGenerator.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxb/JAXBDataBase.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxb/JAXBDataBinding.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxb/JAXBEncoderDecoder.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxb/JAXBSchemaInitializer.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxb/io/DataReaderImpl.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxb/io/DataWriterImpl.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/JAXRSInvoker.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/JAXRSServerFactoryBean.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/client/JAXRSClientFactory.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/client/LocalClientState.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/client/WebClient.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/client/spec/ClientRequestContextImpl.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/client/spring/JAXRSClientFactoryBeanDefinitionParser.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/impl/EntityTagHeaderProvider.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/impl/MediaTypeHeaderProvider.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/impl/RequestImpl.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/impl/ResourceContextImpl.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/impl/tl/ThreadLocalInvocationHandler.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/interceptor/JAXRSInInterceptor.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/interceptor/JAXRSOutInterceptor.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/model/AbstractResourceInfo.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/model/ClassResourceInfo.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/model/URITemplate.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/provider/CachingMessageBodyReader.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/provider/CachingMessageBodyWriter.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/provider/DataSourceProvider.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/provider/FormEncodingProvider.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/provider/JAXBElementProvider.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/provider/SourceProvider.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/provider/XSLTJaxbProvider.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/security/JAASAuthenticationFilter.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/security/KerberosAuthenticationFilter.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/spring/JAXRSServerFactoryBeanDefinitionParser.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/utils/HttpUtils.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/message/MessageUtils.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/phase/PhaseChainCache.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/phase/PhaseInterceptorChain.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/service/factory/FactoryBeanListener.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/service/invoker/FactoryInvoker.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/service/model/AbstractPropertiesHolder.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/service/model/FaultInfo.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/service/model/InterfaceInfo.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/service/model/OperationInfo.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/service/model/UnwrappedOperationInfo.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/staxutils/StaxUtils.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/staxutils/W3CDOMStreamReader.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/transport/ChainInitiationObserver.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/transport/http/AbstractHTTPDestination.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/transport/http/CXFAuthenticator.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/transport/http/HTTPConduit.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/transport/http/HTTPTransportFactory.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/transport/http/Headers.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/transport/http/HttpServletRequestSnapshot.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/transport/http/ReferencingAuthenticator.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/transport/http/Servlet3ContinuationProvider.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/transport/http/URLConnectionHTTPConduit.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/transport/http/auth/HttpAuthHeader.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/transport/https/HttpsURLConnectionFactory.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/transport/https/HttpsURLConnectionInfo.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/transport/servlet/servicelist/ServiceListJAASAuthenticator.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/validation/AbstractBeanValidationInterceptor.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/workqueue/AutomaticWorkQueueImpl.java
 create mode 100644 transform/src/patch/java/org/apache/cxf/ws/addressing/MAPAggregator.java

[tomee-jakarta] 04/06: cxf-rt-rs-client changes since 3.4.3

Posted by db...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dblevins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomee-jakarta.git

commit b6b3737f17f9ec65bdcd70cfe63e534fa70b24d3
Author: David Blevins <da...@gmail.com>
AuthorDate: Sun May 2 21:54:51 2021 -0700

    cxf-rt-rs-client changes since 3.4.3
---
 .../apache/cxf/jaxrs/client/ClientProxyImpl.java   | 1149 +++++++++++++++++
 .../cxf/jaxrs/client/JAXRSClientFactory.java       |  414 ++++++
 .../apache/cxf/jaxrs/client/LocalClientState.java  |  185 +++
 .../org/apache/cxf/jaxrs/client/WebClient.java     | 1349 ++++++++++++++++++++
 .../client/spec/ClientRequestContextImpl.java      |  186 +++
 .../JAXRSClientFactoryBeanDefinitionParser.java    |  187 +++
 6 files changed, 3470 insertions(+)

diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java b/transform/src/patch/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
new file mode 100644
index 0000000..8937909
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
@@ -0,0 +1,1149 @@
+/**
+ * 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.client;
+
+import java.io.Closeable;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.function.Predicate;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.MatrixParam;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.classloader.ClassLoaderUtils.ClassLoaderHolder;
+import org.apache.cxf.common.i18n.BundleUtils;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.PrimitiveUtils;
+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.endpoint.Endpoint;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.interceptor.InterceptorProvider;
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.apache.cxf.jaxrs.ext.multipart.Multipart;
+import org.apache.cxf.jaxrs.impl.MetadataMap;
+import org.apache.cxf.jaxrs.impl.ResponseImpl;
+import org.apache.cxf.jaxrs.model.ClassResourceInfo;
+import org.apache.cxf.jaxrs.model.OperationResourceInfo;
+import org.apache.cxf.jaxrs.model.Parameter;
+import org.apache.cxf.jaxrs.model.ParameterType;
+import org.apache.cxf.jaxrs.utils.AnnotationUtils;
+import org.apache.cxf.jaxrs.utils.FormUtils;
+import org.apache.cxf.jaxrs.utils.InjectionUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.Message;
+
+/**
+ * Proxy-based client implementation
+ *
+ */
+public class ClientProxyImpl extends AbstractClient implements
+    InvocationHandlerAware, InvocationHandler, Closeable {
+
+    protected static final Logger LOG = LogUtils.getL7dLogger(ClientProxyImpl.class);
+    protected static final ResourceBundle BUNDLE = BundleUtils.getBundle(ClientProxyImpl.class);
+    protected static final String SLASH = "/";
+    protected static final String BUFFER_PROXY_RESPONSE = "buffer.proxy.response";
+    protected static final String PROXY_METHOD_PARAM_BODY_INDEX = "proxy.method.parameter.body.index";
+
+    protected ClassResourceInfo cri;
+    protected ClassLoader proxyLoader;
+    protected boolean inheritHeaders;
+    protected boolean isRoot;
+    protected Map<String, Object> valuesMap = Collections.emptyMap();
+    protected BodyWriter bodyWriter = new BodyWriter();
+    protected Client proxy;
+    public ClientProxyImpl(URI baseURI,
+                           ClassLoader loader,
+                           ClassResourceInfo cri,
+                           boolean isRoot,
+                           boolean inheritHeaders,
+                           Object... varValues) {
+        this(baseURI, loader, cri, isRoot, inheritHeaders, Collections.emptyMap(), varValues);
+    }
+
+    public ClientProxyImpl(URI baseURI,
+            ClassLoader loader,
+            ClassResourceInfo cri,
+            boolean isRoot,
+            boolean inheritHeaders,
+            Map<String, Object> properties,
+            Object... varValues) {
+        this(new LocalClientState(baseURI, properties), loader, cri, isRoot, inheritHeaders, varValues);
+    }
+
+    public ClientProxyImpl(ClientState initialState,
+                           ClassLoader loader,
+                           ClassResourceInfo cri,
+                           boolean isRoot,
+                           boolean inheritHeaders,
+                           Object... varValues) {
+        super(initialState);
+        this.proxyLoader = loader;
+        this.cri = cri;
+        this.isRoot = isRoot;
+        this.inheritHeaders = inheritHeaders;
+        initValuesMap(varValues);
+        cfg.getInInterceptors().add(new ClientAsyncResponseInterceptor());
+    }
+
+    void setProxyClient(Client client) {
+        this.proxy = client;
+    }
+
+    private void initValuesMap(Object... varValues) {
+        if (isRoot) {
+            List<String> vars = cri.getURITemplate().getVariables();
+            valuesMap = new LinkedHashMap<>();
+            for (int i = 0; i < vars.size(); i++) {
+                if (varValues.length > 0) {
+                    if (i < varValues.length) {
+                        valuesMap.put(vars.get(i), varValues[i]);
+                    } else {
+                        org.apache.cxf.common.i18n.Message msg = new org.apache.cxf.common.i18n.Message(
+                             "ROOT_VARS_MISMATCH", BUNDLE, vars.size(), varValues.length);
+                        LOG.info(msg.toString());
+                        break;
+                    }
+                } else {
+                    valuesMap.put(vars.get(i), "");
+                }
+            }
+        }
+    }
+
+    private static class WrappedException extends Exception {
+        private static final long serialVersionUID = 1183890106889852917L;
+
+        final Throwable wrapped;
+        WrappedException(Throwable wrapped) {
+            this.wrapped = wrapped;
+        }
+        Throwable getWrapped() {
+            return wrapped;
+        }
+    }
+
+    private static Object invokeDefaultMethod(Class<?> declaringClass, Object o, Method m, Object[] params)
+        throws Throwable {
+
+        try {
+            return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+                @Override
+                public Object run() throws Exception {
+                    try {
+                        final MethodHandles.Lookup lookup = MethodHandles
+                                .publicLookup()
+                                .in(declaringClass);
+                        // force private access so unreflectSpecial can invoke the interface's default method
+                        Field f;
+                        try { 
+                            f = MethodHandles.Lookup.class.getDeclaredField("allowedModes");
+                        } catch (NoSuchFieldException nsfe) {
+                            // IBM and OpenJ9 JDKs use a different field name
+                            f = MethodHandles.Lookup.class.getDeclaredField("accessMode");
+                            m.setAccessible(true);
+                        }
+                        final int modifiers = f.getModifiers();
+                        if (Modifier.isFinal(modifiers)) {
+                            final Field modifiersField = Field.class.getDeclaredField("modifiers");
+                            modifiersField.setAccessible(true);
+                            modifiersField.setInt(f, modifiers & ~Modifier.FINAL);
+                            f.setAccessible(true);
+                            f.set(lookup, MethodHandles.Lookup.PRIVATE);
+                        }
+                        MethodHandle mh = lookup.unreflectSpecial(m, declaringClass).bindTo(o);
+                        return params != null && params.length > 0 ? mh.invokeWithArguments(params) : mh.invoke();
+                    } catch (Throwable t) {
+                        try { // try using built-in JDK 9+ API for invoking default method
+                            return invokeDefaultMethodUsingPrivateLookup(declaringClass, o, m, params);
+                        } catch (final NoSuchMethodException ex) {
+                            throw new WrappedException(t);
+                        }
+                    }
+                }
+            });
+        } catch (PrivilegedActionException pae) {
+            Throwable wrapped = pae.getCause();
+            if (wrapped instanceof WrappedException) {
+                throw ((WrappedException)wrapped).getWrapped();
+            }
+            throw wrapped;
+        }
+    }
+
+    /**
+     * For JDK 9+, we could use MethodHandles.privateLookupIn, which is not 
+     * available in JDK 8.
+     */
+    private static Object invokeDefaultMethodUsingPrivateLookup(Class<?> declaringClass, Object o, Method m, 
+            Object[] params) throws WrappedException, NoSuchMethodException {
+        try {
+            final Method privateLookup = MethodHandles
+                .class
+                .getDeclaredMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
+            
+            return ((MethodHandles.Lookup)privateLookup
+                .invoke(null, declaringClass, MethodHandles.lookup()))
+                .unreflectSpecial(m, declaringClass)
+                .bindTo(o)
+                .invokeWithArguments(params);
+        } catch (NoSuchMethodException t) {
+            throw t;
+        } catch (Throwable t) {
+            throw new WrappedException(t);
+        }
+    }
+
+    /**
+     * Updates the current state if Client method is invoked, otherwise
+     * does the remote invocation or returns a new proxy if subresource
+     * method is invoked. Can throw an expected exception if ResponseExceptionMapper
+     * is registered
+     */
+    @Override
+    public Object invoke(Object o, Method m, Object[] params) throws Throwable {
+        checkClosed();
+        Class<?> declaringClass = m.getDeclaringClass();
+        if (Client.class == declaringClass || InvocationHandlerAware.class == declaringClass
+            || Object.class == declaringClass || Closeable.class == declaringClass
+            || AutoCloseable.class == declaringClass) {
+            return m.invoke(this, params);
+        }
+        resetResponse();
+        OperationResourceInfo ori = cri.getMethodDispatcher().getOperationResourceInfo(m);
+        if (ori == null) {
+            if (m.isDefault()) {
+                return invokeDefaultMethod(declaringClass, o, m, params);
+            }
+            reportInvalidResourceMethod(m, "INVALID_RESOURCE_METHOD");
+        }
+
+        MultivaluedMap<ParameterType, Parameter> types = getParametersInfo(m, params, ori);
+        List<Parameter> beanParamsList = getParameters(types, ParameterType.BEAN);
+
+        int bodyIndex = getBodyIndex(types, ori);
+
+        List<Object> pathParams = getPathParamValues(m, params, types, beanParamsList, ori, bodyIndex);
+
+        UriBuilder builder = getCurrentBuilder().clone();
+        if (isRoot) {
+            addNonEmptyPath(builder, ori.getClassResourceInfo().getURITemplate().getValue());
+        }
+        addNonEmptyPath(builder, ori.getURITemplate().getValue());
+
+        handleMatrixes(m, params, types, beanParamsList, builder);
+        handleQueries(m, params, types, beanParamsList, builder);
+
+        URI uri = builder.buildFromEncoded(pathParams.toArray()).normalize();
+
+        MultivaluedMap<String, String> headers = getHeaders();
+        MultivaluedMap<String, String> paramHeaders = new MetadataMap<>();
+        handleHeaders(m, params, paramHeaders, beanParamsList, types);
+        handleCookies(m, params, paramHeaders, beanParamsList, types);
+
+        if (ori.isSubResourceLocator()) {
+            ClassResourceInfo subCri = cri.getSubResource(m.getReturnType(), m.getReturnType());
+            if (subCri == null) {
+                reportInvalidResourceMethod(m, "INVALID_SUBRESOURCE");
+            }
+
+            MultivaluedMap<String, String> subHeaders = paramHeaders;
+            if (inheritHeaders) {
+                subHeaders.putAll(headers);
+            }
+
+            ClientState newState = getState().newState(uri, subHeaders,
+                 getTemplateParametersMap(ori.getURITemplate(), pathParams));
+            ClientProxyImpl proxyImpl =
+                new ClientProxyImpl(newState, proxyLoader, subCri, false, inheritHeaders);
+            proxyImpl.setConfiguration(getConfiguration());
+            return JAXRSClientFactory.createProxy(m.getReturnType(), proxyLoader, proxyImpl);
+        }
+        headers.putAll(paramHeaders);
+
+        getState().setTemplates(getTemplateParametersMap(ori.getURITemplate(), pathParams));
+
+        Object body = null;
+        if (bodyIndex != -1) {
+            body = params[bodyIndex];
+            if (body == null) {
+                bodyIndex = -1;
+            }
+        } else if (types.containsKey(ParameterType.FORM))  {
+            body = handleForm(m, params, types, beanParamsList);
+        } else if (types.containsKey(ParameterType.REQUEST_BODY))  {
+            body = handleMultipart(types, ori, params);
+        } else if (hasFormParams(params, beanParamsList)) {
+            body = handleForm(m, params, types, beanParamsList);
+        }
+        
+        setRequestHeaders(headers, ori, types.containsKey(ParameterType.FORM),
+            body == null ? null : body.getClass(), m.getReturnType());
+
+        try {
+            return doChainedInvocation(uri, headers, ori, params, body, bodyIndex, null, null);
+        } finally {
+            resetResponseStateImmediatelyIfNeeded();
+        }
+
+    }
+
+    protected void addNonEmptyPath(UriBuilder builder, String pathValue) {
+        if (!SLASH.equals(pathValue)) {
+            builder.path(pathValue);
+        }
+    }
+
+    protected MultivaluedMap<ParameterType, Parameter> getParametersInfo(Method m,
+        Object[] params, OperationResourceInfo ori) {
+        MultivaluedMap<ParameterType, Parameter> map = new MetadataMap<>();
+
+        List<Parameter> parameters = ori.getParameters();
+        if (parameters.isEmpty()) {
+            return map;
+        }
+        int requestBodyParam = 0;
+        int multipartParam = 0;
+        for (Parameter p : parameters) {
+            if (isIgnorableParameter(m, p)) {
+                continue;
+            }
+            if (p.getType() == ParameterType.REQUEST_BODY) {
+                requestBodyParam++;
+                if (getMultipart(ori, p.getIndex()) != null) {
+                    multipartParam++;
+                }
+            }
+            map.add(p.getType(), p);
+        }
+
+        if (map.containsKey(ParameterType.REQUEST_BODY)) {
+            if (requestBodyParam > 1 && requestBodyParam != multipartParam) {
+                reportInvalidResourceMethod(ori.getMethodToInvoke(), "SINGLE_BODY_ONLY");
+            }
+            if (map.containsKey(ParameterType.FORM)) {
+                reportInvalidResourceMethod(ori.getMethodToInvoke(), "ONLY_FORM_ALLOWED");
+            }
+        }
+        return map;
+    }
+
+    protected boolean isIgnorableParameter(Method m, Parameter p) {
+        if (p.getType() == ParameterType.CONTEXT) {
+            return true;
+        }
+        return p.getType() == ParameterType.REQUEST_BODY
+            && m.getParameterTypes()[p.getIndex()] == AsyncResponse.class;
+    }
+
+    protected static int getBodyIndex(MultivaluedMap<ParameterType, Parameter> map,
+                                    OperationResourceInfo ori) {
+        List<Parameter> list = map.get(ParameterType.REQUEST_BODY);
+        int index = list == null || list.size() > 1 ? -1 : list.get(0).getIndex();
+        if (ori.isSubResourceLocator() && index != -1) {
+            reportInvalidResourceMethod(ori.getMethodToInvoke(), "NO_BODY_IN_SUBRESOURCE");
+        }
+        return index;
+    }
+
+    protected void checkResponse(Method m, Response r, Message inMessage) throws Throwable {
+        Throwable t = null;
+        int status = r.getStatus();
+
+        if (status >= 300) {
+            Class<?>[] exTypes = m.getExceptionTypes();
+            if (exTypes.length == 0) {
+                exTypes = new Class<?>[]{WebApplicationException.class};
+            }
+            for (Class<?> exType : exTypes) {
+                ResponseExceptionMapper<?> mapper = findExceptionMapper(inMessage, exType);
+                if (mapper != null) {
+                    t = mapper.fromResponse(r);
+                    if (t != null) {
+                        throw t;
+                    }
+                }
+            }
+
+            if ((t == null) && (m.getReturnType() == Response.class) && (m.getExceptionTypes().length == 0)) {
+                return;
+            }
+
+            t = convertToWebApplicationException(r);
+
+            if (inMessage.getExchange().get(Message.RESPONSE_CODE) == null) {
+                throw t;
+            }
+
+            Endpoint ep = inMessage.getExchange().getEndpoint();
+            inMessage.getExchange().put(InterceptorProvider.class, getConfiguration());
+            inMessage.setContent(Exception.class, new Fault(t));
+            inMessage.getInterceptorChain().abort();
+            if (ep.getInFaultObserver() != null) {
+                ep.getInFaultObserver().onMessage(inMessage);
+            }
+
+            throw t;
+
+        }
+    }
+
+    protected static ResponseExceptionMapper<?> findExceptionMapper(Message message, Class<?> exType) {
+        ClientProviderFactory pf = ClientProviderFactory.getInstance(message);
+        return pf.createResponseExceptionMapper(message, exType);
+    }
+
+    protected MultivaluedMap<String, String> setRequestHeaders(MultivaluedMap<String, String> headers,
+                                                             OperationResourceInfo ori,
+                                                             boolean formParams,
+                                                             Class<?> bodyClass,
+                                                             Class<?> responseClass) {
+        if (headers.getFirst(HttpHeaders.CONTENT_TYPE) == null) {
+            if (formParams || bodyClass != null && MultivaluedMap.class.isAssignableFrom(bodyClass)) {
+                headers.putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED);
+            } else {
+                String ctType = null;
+                List<MediaType> consumeTypes = ori.getConsumeTypes();
+                if (!consumeTypes.isEmpty() && !consumeTypes.get(0).equals(MediaType.WILDCARD_TYPE)) {
+                    ctType = JAXRSUtils.mediaTypeToString(ori.getConsumeTypes().get(0));
+                }
+                if (ctType != null) {
+                    headers.putSingle(HttpHeaders.CONTENT_TYPE, ctType);
+                }
+            }
+        }
+
+        List<MediaType> accepts = getAccept(headers);
+        if (accepts == null) {
+            if (responseClass == Void.class || responseClass == Void.TYPE) {
+                accepts = Collections.singletonList(MediaType.WILDCARD_TYPE);
+            } else {
+                List<MediaType> produceTypes = ori.getProduceTypes();
+                boolean produceWildcard = produceTypes.isEmpty()
+                    || produceTypes.get(0).equals(MediaType.WILDCARD_TYPE);
+                if (produceWildcard) {
+                    accepts = InjectionUtils.isPrimitive(responseClass)
+                        ? Collections.singletonList(MediaType.TEXT_PLAIN_TYPE)
+                        : Collections.singletonList(MediaType.APPLICATION_XML_TYPE);
+                } else {
+                    accepts = produceTypes;
+                }
+            }
+
+            for (MediaType mt : accepts) {
+                headers.add(HttpHeaders.ACCEPT, JAXRSUtils.mediaTypeToString(mt));
+            }
+        }
+
+        return headers;
+    }
+
+    protected List<MediaType> getAccept(MultivaluedMap<String, String> allHeaders) {
+        List<String> headers = allHeaders.get(HttpHeaders.ACCEPT);
+        if (headers == null || headers.isEmpty()) {
+            return null;
+        }
+        return headers.stream().
+                flatMap(header -> JAXRSUtils.parseMediaTypes(header).stream()).collect(Collectors.toList());
+    }
+
+    protected List<Object> getPathParamValues(Method m,
+                                            Object[] params,
+                                            MultivaluedMap<ParameterType, Parameter> map,
+                                            List<Parameter> beanParams,
+                                            OperationResourceInfo ori,
+                                            int bodyIndex) {
+        List<Object> list = new ArrayList<>();
+
+        List<String> methodVars = ori.getURITemplate().getVariables();
+        List<Parameter> paramsList = getParameters(map, ParameterType.PATH);
+        Map<String, BeanPair> beanParamValues = new HashMap<>(beanParams.size());
+        beanParams.forEach(p -> {
+            beanParamValues.putAll(getValuesFromBeanParam(params[p.getIndex()], PathParam.class));
+        });
+        if (!beanParamValues.isEmpty() && !methodVars.containsAll(beanParamValues.keySet())) {
+            List<String> classVars = ori.getClassResourceInfo().getURITemplate().getVariables();
+            classVars.forEach(classVar -> {
+                BeanPair pair = beanParamValues.get(classVar);
+                if (pair != null) {
+                    Object paramValue = convertParamValue(pair.getValue(), pair.getAnns());
+                    if (isRoot) {
+                        valuesMap.put(classVar, paramValue);
+                    } else {
+                        list.add(paramValue);
+                    }
+                }
+            });
+        }
+        if (isRoot) {
+            list.addAll(valuesMap.values());
+        }
+
+
+        Map<String, Parameter> paramsMap = new LinkedHashMap<>();
+        paramsList.forEach(p -> {
+            if (p.getName().isEmpty()) {
+                MultivaluedMap<String, Object> values = InjectionUtils.extractValuesFromBean(params[p.getIndex()], "");
+                methodVars.forEach(var -> {
+                    list.addAll(values.get(var));
+                });
+            } else {
+                paramsMap.put(p.getName(), p);
+            }
+        });
+
+        Object requestBody = bodyIndex == -1 ? null : params[bodyIndex];
+        methodVars.forEach(varName -> {
+            Parameter p = paramsMap.remove(varName);
+            if (p != null) {
+                list.add(convertParamValue(params[p.getIndex()],
+                        m.getParameterTypes()[p.getIndex()],
+                        getParamAnnotations(m, p)));
+            } else if (beanParamValues.containsKey(varName)) {
+                BeanPair pair = beanParamValues.get(varName);
+                list.add(convertParamValue(pair.getValue(), pair.getAnns()));
+            } else if (requestBody != null) {
+                try {
+                    Method getter = requestBody.getClass().
+                            getMethod("get" + StringUtils.capitalize(varName), new Class<?>[]{});
+                    list.add(getter.invoke(requestBody, new Object[]{}));
+                } catch (Exception ex) {
+                    // continue
+                }
+            }
+        });
+
+        for (Parameter p : paramsMap.values()) {
+            if (valuesMap.containsKey(p.getName())) {
+                int index = 0;
+                for (Iterator<String> it = valuesMap.keySet().iterator(); it.hasNext(); index++) {
+                    if (it.next().equals(p.getName()) && index < list.size()) {
+                        list.set(index, convertParamValue(params[p.getIndex()], null));
+                        break;
+                    }
+                }
+            }
+        }
+
+
+        return list;
+    }
+
+    protected static Annotation[] getParamAnnotations(Method m, Parameter p) {
+        return m.getParameterAnnotations()[p.getIndex()];
+    }
+
+    protected static List<Parameter> getParameters(MultivaluedMap<ParameterType, Parameter> map,
+                                           ParameterType key) {
+        return map.get(key) == null ? Collections.emptyList() : map.get(key);
+    }
+
+    protected void handleQueries(Method m,
+                               Object[] params,
+                               MultivaluedMap<ParameterType, Parameter> map,
+                               List<Parameter> beanParams,
+                               UriBuilder ub) {
+        List<Parameter> qs = getParameters(map, ParameterType.QUERY);
+        qs.stream().
+                filter(p -> params[p.getIndex()] != null).
+                forEachOrdered(p -> {
+                    addMatrixQueryParamsToBuilder(ub, p.getName(), ParameterType.QUERY,
+                            getParamAnnotations(m, p), params[p.getIndex()]);
+                });
+        beanParams.stream().
+                map(p -> getValuesFromBeanParam(params[p.getIndex()], QueryParam.class)).
+                forEachOrdered(values -> {
+                    values.forEach((key, value) -> {
+                        if (value != null) {
+                            addMatrixQueryParamsToBuilder(ub, key, ParameterType.QUERY,
+                                    value.getAnns(), value.getValue());
+                        }
+                    });
+                });
+    }
+
+    protected Map<String, BeanPair> getValuesFromBeanParam(Object bean, Class<? extends Annotation> annClass) {
+        Map<String, BeanPair> values = new HashMap<>();
+        getValuesFromBeanParam(bean, annClass, values);
+        return values;
+    }
+
+    protected Map<String, BeanPair> getValuesFromBeanParam(Object bean,
+                                                         Class<? extends Annotation> annClass,
+                                                         Map<String, BeanPair> values) {
+        boolean completeFieldIntrospectionNeeded = false;
+        for (Method m : bean.getClass().getMethods()) {
+            if (m.getName().startsWith("set")) {
+                try {
+                    String propertyName = m.getName().substring(3);
+                    Annotation methodAnnotation = m.getAnnotation(annClass);
+                    boolean beanParam = m.getAnnotation(BeanParam.class) != null;
+                    if (methodAnnotation != null || beanParam) {
+                        Method getter = bean.getClass().getMethod("get" + propertyName, new Class<?>[]{});
+                        Object value = getter.invoke(bean, new Object[]{});
+                        if (value != null) {
+                            if (methodAnnotation != null) {
+                                String annotationValue = AnnotationUtils.getAnnotationValue(methodAnnotation);
+                                values.put(annotationValue, new BeanPair(value, m.getParameterAnnotations()[0]));
+                            } else {
+                                getValuesFromBeanParam(value, annClass, values);
+                            }
+                        }
+                    } else {
+                        String fieldName = StringUtils.uncapitalize(propertyName);
+                        Field f = InjectionUtils.getDeclaredField(bean.getClass(), fieldName);
+                        if (f == null) {
+                            completeFieldIntrospectionNeeded = true;
+                            continue;
+                        }
+                        boolean jaxrsParamAnnAvailable = getValuesFromBeanParamField(bean, f, annClass, values);
+                        if (!jaxrsParamAnnAvailable && f.getAnnotation(BeanParam.class) != null) {
+                            Object value = ReflectionUtil.accessDeclaredField(f, bean, Object.class);
+                            if (value != null) {
+                                getValuesFromBeanParam(value, annClass, values);
+                            }
+                        }
+                    }
+                } catch (Throwable t) {
+                    // ignore
+                }
+            }
+            if (completeFieldIntrospectionNeeded) {
+                for (Field f : bean.getClass().getDeclaredFields()) {
+                    boolean jaxrsParamAnnAvailable = getValuesFromBeanParamField(bean, f, annClass, values);
+                    if (!jaxrsParamAnnAvailable && f.getAnnotation(BeanParam.class) != null) {
+                        Object value = ReflectionUtil.accessDeclaredField(f, bean, Object.class);
+                        if (value != null) {
+                            getValuesFromBeanParam(value, annClass, values);
+                        }
+                    }
+                }
+            }
+        }
+        return values;
+    }
+
+    protected boolean getValuesFromBeanParamField(Object bean,
+                                                Field f,
+                                                Class<? extends Annotation> annClass,
+                                                Map<String, BeanPair> values) {
+        boolean jaxrsParamAnnAvailable = false;
+        Annotation fieldAnnotation = f.getAnnotation(annClass);
+        if (fieldAnnotation != null) {
+            jaxrsParamAnnAvailable = true;
+            Object value = ReflectionUtil.accessDeclaredField(f, bean, Object.class);
+            if (value != null) {
+                String annotationValue = AnnotationUtils.getAnnotationValue(fieldAnnotation);
+                values.put(annotationValue, new BeanPair(value, f.getAnnotations()));
+            }
+        }
+        return jaxrsParamAnnAvailable;
+    }
+
+    protected void handleMatrixes(Method m,
+                                Object[] params,
+                                MultivaluedMap<ParameterType, Parameter> map,
+                                List<Parameter> beanParams,
+                                UriBuilder ub) {
+        List<Parameter> mx = getParameters(map, ParameterType.MATRIX);
+        mx.stream().
+                filter(p -> params[p.getIndex()] != null).
+                forEachOrdered(p -> {
+                    addMatrixQueryParamsToBuilder(ub, p.getName(), ParameterType.MATRIX,
+                            getParamAnnotations(m, p), params[p.getIndex()]);
+                });
+        beanParams.stream().
+                map(p -> getValuesFromBeanParam(params[p.getIndex()], MatrixParam.class)).
+                forEachOrdered(values -> {
+                    values.forEach((key, value) -> {
+                        if (value != null) {
+                            addMatrixQueryParamsToBuilder(ub, key, ParameterType.MATRIX,
+                                    value.getAnns(), value.getValue());
+                        }
+                    });
+                });
+    }
+
+    protected MultivaluedMap<String, String> handleForm(Method m,
+                                                      Object[] params,
+                                                      MultivaluedMap<ParameterType, Parameter> map,
+                                                      List<Parameter> beanParams) {
+
+        MultivaluedMap<String, String> form = new MetadataMap<>();
+
+        List<Parameter> fm = getParameters(map, ParameterType.FORM);
+        fm.forEach(p -> {
+            addFormValue(form, p.getName(), params[p.getIndex()], getParamAnnotations(m, p));
+        });
+        beanParams.stream().
+                map(p -> getValuesFromBeanParam(params[p.getIndex()], FormParam.class)).
+                forEachOrdered(values -> {
+                    values.forEach((key, value) -> {
+                        addFormValue(form, key, value.getValue(), value.getAnns());
+                    });
+                });
+
+        return form;
+    }
+
+    protected void addFormValue(MultivaluedMap<String, String> form, String name, Object pValue, Annotation[] anns) {
+        if (pValue != null) {
+            if (InjectionUtils.isSupportedCollectionOrArray(pValue.getClass())) {
+                Collection<?> c = pValue.getClass().isArray()
+                    ? Arrays.asList((Object[]) pValue) : (Collection<?>) pValue;
+                for (Iterator<?> it = c.iterator(); it.hasNext();) {
+                    FormUtils.addPropertyToForm(form, name, convertParamValue(it.next(), anns));
+                }
+            } else {
+                FormUtils.addPropertyToForm(form, name, name.isEmpty()
+                                            ? pValue : convertParamValue(pValue, anns));
+            }
+
+        }
+
+    }
+
+    protected List<Attachment> handleMultipart(MultivaluedMap<ParameterType, Parameter> map,
+                                             OperationResourceInfo ori,
+                                             Object[] params) {
+        List<Parameter> fm = getParameters(map, ParameterType.REQUEST_BODY);
+        List<Attachment> atts = new ArrayList<>(fm.size());
+        fm.forEach(p -> {
+            Multipart part = getMultipart(ori, p.getIndex());
+            if (part != null) {
+                Object partObject = params[p.getIndex()];
+                if (partObject != null) {
+                    atts.add(new Attachment(part.value(), part.type(), partObject));
+                }
+            }
+        });
+        return atts;
+    }
+
+    protected void handleHeaders(Method m,
+                               Object[] params,
+                               MultivaluedMap<String, String> headers,
+                               List<Parameter> beanParams,
+                               MultivaluedMap<ParameterType, Parameter> map) {
+        List<Parameter> hs = getParameters(map, ParameterType.HEADER);
+        hs.stream().
+                filter(p -> params[p.getIndex()] != null).
+                forEachOrdered(p -> {
+                    headers.add(p.getName(), convertParamValue(params[p.getIndex()], getParamAnnotations(m, p)));
+                });
+        beanParams.stream().
+                map(p -> getValuesFromBeanParam(params[p.getIndex()], HeaderParam.class)).
+                forEachOrdered(values -> {
+                    values.forEach((key, value) -> {
+                        if (value != null) {
+                            headers.add(key, convertParamValue(value.getValue(), value.getAnns()));
+                        }
+                    });
+                });
+    }
+
+    protected static Multipart getMultipart(OperationResourceInfo ori, int index) {
+        Method aMethod = ori.getAnnotatedMethod();
+        return aMethod != null ? AnnotationUtils.getAnnotation(
+            aMethod.getParameterAnnotations()[index], Multipart.class) : null;
+    }
+
+    protected void handleCookies(Method m,
+                               Object[] params,
+                               MultivaluedMap<String, String> headers,
+                               List<Parameter> beanParams,
+                               MultivaluedMap<ParameterType, Parameter> map) {
+        List<Parameter> cs = getParameters(map, ParameterType.COOKIE);
+        cs.stream().
+                filter(p -> params[p.getIndex()] != null).
+                forEachOrdered(p -> {
+                    headers.add(HttpHeaders.COOKIE,
+                            p.getName() + '='
+                            + convertParamValue(params[p.getIndex()].toString(), getParamAnnotations(m, p)));
+                });
+        beanParams.stream().
+                map(p -> getValuesFromBeanParam(params[p.getIndex()], CookieParam.class)).
+                forEachOrdered(values -> {
+                    values.forEach((key, value) -> {
+                        if (value != null) {
+                            headers.add(HttpHeaders.COOKIE,
+                                    key + "=" + convertParamValue(value.getValue(), value.getAnns()));
+                        }
+                    });
+                });
+    }
+
+    protected Message createMessage(Object body,
+                                    OperationResourceInfo ori,
+                                    MultivaluedMap<String, String> headers,
+                                    URI currentURI,
+                                    Exchange exchange,
+                                    Map<String, Object> invocationContext,
+                                    boolean isProxy) {
+        return createMessage(body, ori.getHttpMethod(), headers, currentURI,
+                             exchange, invocationContext, isProxy);
+    }
+
+    //CHECKSTYLE:OFF
+    protected Object doChainedInvocation(URI uri,
+                                       MultivaluedMap<String, String> headers,
+                                       OperationResourceInfo ori,
+                                       Object[] methodParams,
+                                       Object body,
+                                       int bodyIndex,
+                                       Exchange exchange,
+                                       Map<String, Object> invocationContext) throws Throwable {
+    //CHECKSTYLE:ON
+        Bus configuredBus = getConfiguration().getBus();
+        Bus origBus = BusFactory.getAndSetThreadDefaultBus(configuredBus);
+        ClassLoaderHolder origLoader = null;
+        try {
+            ClassLoader loader = configuredBus.getExtension(ClassLoader.class);
+            if (loader != null) {
+                origLoader = ClassLoaderUtils.setThreadContextClassloader(loader);
+            }
+            Message outMessage = createMessage(body, ori, headers, uri, exchange, invocationContext, true);
+            if (bodyIndex != -1) {
+                outMessage.put(Type.class, ori.getMethodToInvoke().getGenericParameterTypes()[bodyIndex]);
+            }
+            outMessage.getExchange().setOneWay(ori.isOneway());
+            setSupportOnewayResponseProperty(outMessage);
+            outMessage.setContent(OperationResourceInfo.class, ori);
+            setPlainOperationNameProperty(outMessage, ori.getMethodToInvoke().getName());
+            outMessage.getExchange().put(Method.class, ori.getMethodToInvoke());
+
+            outMessage.put(Annotation.class.getName(),
+                           getMethodAnnotations(ori.getAnnotatedMethod(), bodyIndex));
+
+            outMessage.getExchange().put(Message.SERVICE_OBJECT, proxy);
+            if (methodParams != null) {
+                outMessage.put(List.class, Arrays.asList(methodParams));
+            }
+            if (body != null) {
+                outMessage.put(PROXY_METHOD_PARAM_BODY_INDEX, bodyIndex);
+            }
+            outMessage.getInterceptorChain().add(bodyWriter);
+
+            Map<String, Object> reqContext = getRequestContext(outMessage);
+            reqContext.put(OperationResourceInfo.class.getName(), ori);
+            reqContext.put(PROXY_METHOD_PARAM_BODY_INDEX, bodyIndex);
+
+            // execute chain
+            InvocationCallback<Object> asyncCallback = checkAsyncCallback(ori, reqContext, outMessage);
+            if (asyncCallback != null) {
+                return doInvokeAsync(ori, outMessage, asyncCallback);
+            }
+            doRunInterceptorChain(outMessage);
+
+            Object[] results = preProcessResult(outMessage);
+            if (results != null && results.length == 1) {
+                return results[0];
+            }
+
+            try {
+                return handleResponse(outMessage, ori.getClassResourceInfo().getServiceClass());
+            } finally {
+                completeExchange(outMessage.getExchange(), true);
+            }
+
+        } finally {
+            if (origLoader != null) {
+                origLoader.reset();
+            }
+            if (origBus != configuredBus) {
+                BusFactory.setThreadDefaultBus(origBus);
+            }
+        }
+
+    }
+
+    protected InvocationCallback<Object> checkAsyncCallback(OperationResourceInfo ori,
+                                                            Map<String, Object> reqContext,
+                                                            Message outMessage) {
+        Object callbackProp = reqContext.get(InvocationCallback.class.getName());
+        if (callbackProp != null) {
+            if (callbackProp instanceof Collection) {
+                @SuppressWarnings("unchecked")
+                Collection<InvocationCallback<Object>> callbacks = (Collection<InvocationCallback<Object>>)callbackProp;
+                for (InvocationCallback<Object> callback : callbacks) {
+                    if (doCheckAsyncCallback(ori, callback) != null) {
+                        return callback;
+                    }
+                }
+            } else {
+                @SuppressWarnings("unchecked")
+                InvocationCallback<Object> callback = (InvocationCallback<Object>)callbackProp;
+                return doCheckAsyncCallback(ori, callback);
+            }
+        }
+        return null;
+    }
+
+    protected InvocationCallback<Object> doCheckAsyncCallback(OperationResourceInfo ori,
+                                                            InvocationCallback<Object> callback) {
+        Type callbackOutType = getCallbackType(callback);
+        Class<?> callbackRespClass = getCallbackClass(callbackOutType);
+
+        Class<?> methodReturnType = ori.getMethodToInvoke().getReturnType();
+        if (Object.class == callbackRespClass
+            || callbackRespClass.isAssignableFrom(methodReturnType)
+            || PrimitiveUtils.canPrimitiveTypeBeAutoboxed(methodReturnType, callbackRespClass)) {
+            return callback;
+        }
+        return null;
+    }
+
+    protected Object doInvokeAsync(OperationResourceInfo ori, 
+                                   Message outMessage,
+                                   InvocationCallback<Object> asyncCallback) {
+        outMessage.getExchange().setSynchronous(false);
+        setAsyncMessageObserverIfNeeded(outMessage.getExchange());
+        JaxrsClientCallback<?> cb = newJaxrsClientCallback(asyncCallback, outMessage,
+            ori.getMethodToInvoke().getReturnType(), ori.getMethodToInvoke().getGenericReturnType());
+        outMessage.getExchange().put(JaxrsClientCallback.class, cb);
+        doRunInterceptorChain(outMessage);
+
+        return null;
+    }
+
+    protected JaxrsClientCallback<?> newJaxrsClientCallback(InvocationCallback<Object> asyncCallback,
+                                                            Message outMessage,
+                                                            Class<?> responseClass,
+                                                            Type outGenericType) {
+        return new JaxrsClientCallback<>(asyncCallback, responseClass, outGenericType);
+    }
+
+    @Override
+    protected Object retryInvoke(URI newRequestURI,
+                                 MultivaluedMap<String, String> headers,
+                                 Object body,
+                                 Exchange exchange,
+                                 Map<String, Object> invContext) throws Throwable {
+
+        Map<String, Object> reqContext = CastUtils.cast((Map<?, ?>)invContext.get(REQUEST_CONTEXT));
+        int bodyIndex = body != null ? (Integer)reqContext.get(PROXY_METHOD_PARAM_BODY_INDEX) : -1;
+        OperationResourceInfo ori =
+            (OperationResourceInfo)reqContext.get(OperationResourceInfo.class.getName());
+        return doChainedInvocation(newRequestURI, headers, ori, null,
+                                   body, bodyIndex, exchange, invContext);
+    }
+
+    protected Object handleResponse(Message outMessage, Class<?> serviceCls)
+        throws Throwable {
+        try {
+            Response r = setResponseBuilder(outMessage, outMessage.getExchange()).build();
+            ((ResponseImpl)r).setOutMessage(outMessage);
+            getState().setResponse(r);
+
+            Method method = outMessage.getExchange().get(Method.class);
+            checkResponse(method, r, outMessage);
+            if (method.getReturnType() == Void.class || method.getReturnType() == Void.TYPE) {
+                return null;
+            }
+            if (method.getReturnType() == Response.class
+                && (r.getEntity() == null || InputStream.class.isAssignableFrom(r.getEntity().getClass())
+                    && ((InputStream)r.getEntity()).available() == 0)) {
+                return r;
+            }
+            if (PropertyUtils.isTrue(super.getConfiguration().getResponseContext().get(BUFFER_PROXY_RESPONSE))) {
+                r.bufferEntity();
+            }
+
+            Class<?> returnType = getReturnType(method, outMessage);
+            Type genericType = getGenericReturnType(serviceCls, method, returnType);
+            
+            returnType = InjectionUtils.updateParamClassToTypeIfNeeded(returnType, genericType);
+            return readBody(r,
+                            outMessage,
+                            returnType,
+                            genericType,
+                            method.getDeclaredAnnotations());
+        } finally {
+            ClientProviderFactory.getInstance(outMessage).clearThreadLocalProxies();
+        }
+    }
+
+    protected Type getGenericReturnType(Class<?> serviceCls, Method method, Class<?> returnType) {
+        return InjectionUtils.processGenericTypeIfNeeded(serviceCls, returnType, method.getGenericReturnType());
+    }
+
+    protected Class<?> getReturnType(Method method, Message outMessage) {
+        return method.getReturnType();
+    }
+
+    public Object getInvocationHandler() {
+        return this;
+    }
+
+    protected static void reportInvalidResourceMethod(Method m, String name) {
+        org.apache.cxf.common.i18n.Message errorMsg =
+            new org.apache.cxf.common.i18n.Message(name,
+                                                   BUNDLE,
+                                                   m.getDeclaringClass().getName(),
+                                                   m.getName());
+        LOG.severe(errorMsg.toString());
+        throw new ProcessingException(errorMsg.toString());
+    }
+
+    protected static Annotation[] getMethodAnnotations(Method aMethod, int bodyIndex) {
+        return aMethod == null || bodyIndex == -1 ? new Annotation[0]
+            : aMethod.getParameterAnnotations()[bodyIndex];
+    }
+    
+    /**
+     * Checks if @BeanParam object has at least one @FormParam declaration.
+     * @param params parameter values
+     * @param beanParams bean parameters
+     * @return "true" @BeanParam object has at least one @FormParam, "false" otherwise
+     */
+    private boolean hasFormParams(Object[] params, List<Parameter> beanParams) {
+        return beanParams
+            .stream()
+            .map(p -> getValuesFromBeanParam(params[p.getIndex()], FormParam.class))
+            .anyMatch(((Predicate<Map<String, BeanPair>>) Map::isEmpty).negate());
+    }
+
+    protected class BodyWriter extends AbstractBodyWriter {
+
+        @Override
+        protected void doWriteBody(Message outMessage,
+                                   Object body,
+                                   Type bodyType,
+                                   Annotation[] customAnns,
+                                   OutputStream os) throws Fault {
+
+
+            OperationResourceInfo ori = outMessage.getContent(OperationResourceInfo.class);
+            if (ori == null) {
+                return;
+            }
+
+            Method method = ori.getMethodToInvoke();
+            int bodyIndex = (Integer)outMessage.get(PROXY_METHOD_PARAM_BODY_INDEX);
+
+            Annotation[] anns = customAnns != null ? customAnns
+                : getMethodAnnotations(ori.getAnnotatedMethod(), bodyIndex);
+            try {
+                Class<?>[] parameterTypes = method.getParameterTypes();
+                if (bodyIndex >= 0 && bodyIndex < parameterTypes.length) {
+                    Class<?> paramClass = parameterTypes[bodyIndex];
+                    Class<?> bodyClass =
+                        paramClass.isAssignableFrom(body.getClass()) ? paramClass : body.getClass();
+                    Type genericType = bodyType;
+                    if (genericType == null) {
+                        Type[] genericParameterTypes = method.getGenericParameterTypes();
+                        if (bodyIndex < genericParameterTypes.length) {
+                            genericType = genericParameterTypes[bodyIndex];
+                        }
+                    }
+                    genericType = InjectionUtils.processGenericTypeIfNeeded(
+                        ori.getClassResourceInfo().getServiceClass(), bodyClass, genericType);
+                    bodyClass = InjectionUtils.updateParamClassToTypeIfNeeded(bodyClass, genericType);
+                    writeBody(body, outMessage, bodyClass, genericType, anns, os);
+                } else {
+                    Type paramType = body.getClass();
+                    if (bodyType != null) {
+                        paramType = bodyType;
+                    }
+                    writeBody(body, outMessage, body.getClass(), paramType,
+                              anns, os);
+                }
+            } catch (Exception ex) {
+                throw new Fault(ex);
+            }
+
+        }
+
+    }
+
+    protected static class BeanPair {
+        protected Object value;
+        protected Annotation[] anns;
+        BeanPair(Object value, Annotation[] anns) {
+            this.value = value;
+            this.anns = anns;
+        }
+        public Object getValue() {
+            return value;
+        }
+        public Annotation[] getAnns() {
+            return anns;
+        }
+    }
+    class ClientAsyncResponseInterceptor extends AbstractClientAsyncResponseInterceptor {
+        @Override
+        protected void doHandleAsyncResponse(Message message, Response r, JaxrsClientCallback<?> cb) {
+            try {
+                Object entity = handleResponse(message.getExchange().getOutMessage(),
+                                               cb.getResponseClass());
+                cb.handleResponse(message, new Object[] {entity});
+            } catch (Throwable t) {
+                cb.handleException(message, t);
+            } finally {
+                completeExchange(message.getExchange(), false);
+                closeAsyncResponseIfPossible(r, message, cb);
+            }
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/client/JAXRSClientFactory.java b/transform/src/patch/java/org/apache/cxf/jaxrs/client/JAXRSClientFactory.java
new file mode 100644
index 0000000..1e487ab
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/client/JAXRSClientFactory.java
@@ -0,0 +1,414 @@
+/**
+ * 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.client;
+
+import java.lang.reflect.InvocationHandler;
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.core.MultivaluedMap;
+
+import org.apache.cxf.common.util.ProxyHelper;
+import org.apache.cxf.feature.Feature;
+import org.apache.cxf.jaxrs.model.UserResource;
+
+/**
+ * Factory for creating proxy clients.
+ *
+ */
+public final class JAXRSClientFactory {
+
+    private JAXRSClientFactory() {
+
+    }
+
+    /**
+     * Creates a proxy
+     * @param baseAddress baseAddress
+     * @param cls resource class, if not interface then a CGLIB proxy will be created
+     * @return typed proxy
+     */
+    public static <T> T create(String baseAddress, Class<T> cls) {
+        return create(URI.create(baseAddress), cls);
+    }
+
+    /**
+     * Creates a proxy using a custom class loader
+     * @param baseAddress baseAddress
+     * @param loader class loader
+     * @param cls resource class, if not interface then a CGLIB proxy will be created
+     * @return typed proxy
+     */
+    public static <T> T create(String baseAddress, Class<T> cls, ClassLoader loader) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, cls, null);
+        bean.setClassLoader(loader);
+        return bean.create(cls);
+    }
+
+    /**
+     * Creates a proxy
+     * @param baseURI baseURI
+     * @param cls resource class, if not interface then a CGLIB proxy will be created
+     * @return typed proxy
+     */
+    public static <T> T create(URI baseURI, Class<T> cls) {
+        return create(baseURI, cls, false);
+    }
+
+    /**
+     * Creates a proxy
+     * @param baseURI baseURI
+     * @param cls resource class, if not interface then a CGLIB proxy will be created
+     * @param inheritHeaders if true then existing proxy headers will be inherited by
+     *        subresource proxies if any
+     * @return typed proxy
+     */
+    public static <T> T create(URI baseURI, Class<T> cls, boolean inheritHeaders) {
+        JAXRSClientFactoryBean bean = getBean(baseURI.toString(), cls, null);
+        bean.setInheritHeaders(inheritHeaders);
+        return bean.create(cls);
+    }
+
+    /**
+     * Creates a proxy
+     * @param baseAddress baseAddres
+     * @param cls resource class, if not interface then a CGLIB proxy will be created
+     * @param properties additional properties
+     * @return typed proxy
+     */
+    public static <T> T create(String baseAddress, Class<T> cls, Map<String, Object> properties) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, cls, null);
+        bean.setProperties(properties);
+        return bean.create(cls);
+    }
+
+    /**
+     * Creates a proxy
+     * @param baseAddress baseAddress
+     * @param cls resource class, if not interface then a CGLIB proxy will be created
+     * @param configLocation classpath location of the configuration resource
+     * @return typed proxy
+     */
+    public static <T> T create(String baseAddress, Class<T> cls, String configLocation) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, cls, configLocation);
+        return bean.create(cls);
+    }
+
+    /**
+     * Creates a proxy
+     * @param baseAddress baseAddress
+     * @param cls resource class, if not interface then a CGLIB proxy will be created
+     *        This class is expected to have a root JAXRS Path annotation containing
+     *        template variables, for ex, "/path/{id1}/{id2}"
+     * @param configLocation classpath location of the configuration resource
+     * @param varValues values to replace root Path template variables
+     * @return typed proxy
+     */
+    public static <T> T create(String baseAddress, Class<T> cls, String configLocation,
+                               Object... varValues) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, cls, configLocation);
+        return bean.create(cls, varValues);
+    }
+
+
+    /**
+     * Creates a proxy
+     * @param baseAddress baseAddress
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @param providers list of providers
+     * @return typed proxy
+     */
+    public static <T> T create(String baseAddress, Class<T> cls, List<?> providers) {
+        return create(baseAddress, cls, providers, null);
+    }
+
+    /**
+     * Creates a thread safe proxy
+     * @param baseAddress baseAddress
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @param providers list of providers
+     * @param threadSafe if true then a thread-safe proxy will be created
+     * @return typed proxy
+     */
+    public static <T> T create(String baseAddress, Class<T> cls, List<?> providers, boolean threadSafe) {
+        return create(baseAddress, cls, providers, Collections.emptyMap(), threadSafe);
+    }
+    /**
+     * Creates a thread safe proxy
+     * @param baseAddress baseAddress
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @param providers list of providers
+     * @param threadSafe if true then a thread-safe proxy will be created
+     * @param properties additional properties
+     * @return typed proxy
+     */
+    public static <T> T create(String baseAddress, Class<T> cls, List<?> providers, 
+            Map<String, Object> properties, boolean threadSafe) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, cls, null);
+        bean.setProviders(providers);
+        bean.setProperties(properties);
+        if (threadSafe) {
+            bean.setInitialState(new ThreadLocalClientState(baseAddress, properties));
+        }
+        return bean.create(cls);
+    }
+
+    /**
+     * Creates a thread safe proxy and allows to specify time to keep state.
+     * @param baseAddress baseAddress
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @param providers list of providers
+     * @param timeToKeepState how long to keep this state
+     * @return typed proxy
+     */
+    public static <T> T create(String baseAddress, Class<T> cls, List<?> providers, long timeToKeepState) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, cls, null);
+        bean.setProviders(providers);
+        bean.setInitialState(new ThreadLocalClientState(baseAddress, timeToKeepState));
+        return bean.create(cls);
+    }
+
+    /**
+     * Creates a proxy
+     * @param baseAddress baseAddress
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @param providers list of providers
+     * @param configLocation classpath location of the configuration resource
+     * @return typed proxy
+     */
+    public static <T> T create(String baseAddress, Class<T> cls, List<?> providers, String configLocation) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, cls, configLocation);
+        bean.setProviders(providers);
+        return bean.create(cls);
+    }
+
+    /**
+     * Creates a proxy
+     * @param baseAddress baseAddress
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @param providers list of providers
+     * @param features the features which will be applied to the client
+     * @param configLocation classpath location of the configuration resource
+     * @return typed proxy
+     */
+    public static <T> T create(String baseAddress, Class<T> cls, List<?> providers,
+                               List<Feature> features,
+                               String configLocation) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, cls, configLocation);
+        bean.setProviders(providers);
+        bean.setFeatures(features);
+        return bean.create(cls);
+    }
+
+    /**
+     * Creates a proxy which will do basic authentication
+     * @param baseAddress baseAddress
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @param username username
+     * @param password password
+     * @param configLocation classpath location of the configuration resource
+     * @return typed proxy
+     */
+    public static <T> T create(String baseAddress, Class<T> cls, String username,
+                               String password, String configLocation) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, cls, configLocation);
+        bean.setUsername(username);
+        bean.setPassword(password);
+        return bean.create(cls);
+    }
+
+    /**
+     * Creates a proxy which will do basic authentication
+     * @param baseAddress baseAddress
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @param providers list of providers
+     * @param username username
+     * @param password password
+     * @param configLocation classpath location of the configuration resource
+     * @return typed proxy
+     */
+    public static <T> T create(String baseAddress, Class<T> cls, List<?> providers,
+                               String username, String password, String configLocation) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, cls, configLocation);
+        bean.setUsername(username);
+        bean.setPassword(password);
+        bean.setProviders(providers);
+        return bean.create(cls);
+    }
+
+    /**
+     * Creates a proxy using user resource model
+     * @param baseAddress baseAddress
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @param modelRef model location
+     * @return typed proxy
+     */
+    public static <T> T createFromModel(String baseAddress, Class<T> cls, String modelRef,
+                                        String configLocation) {
+        return createFromModel(baseAddress, cls, modelRef, Collections.emptyList(), configLocation);
+    }
+
+    /**
+     * Creates a proxy using user resource model
+     * @param baseAddress baseAddress
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @param modelRef model location
+     * @param providers list of providers
+     * @param configLocation classpath location of the configuration resource
+     * @return typed proxy
+     */
+    public static <T> T createFromModel(String baseAddress, Class<T> cls, String modelRef,
+                               List<?> providers, String configLocation) {
+        JAXRSClientFactoryBean bean = WebClient.getBean(baseAddress, configLocation);
+        bean.setProviders(providers);
+        bean.setModelRef(modelRef);
+        bean.setServiceClass(cls);
+        return bean.create(cls);
+    }
+
+    /**
+     * Creates a thread safe proxy using user resource model
+     * @param baseAddress baseAddress
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @param modelRef model location
+     * @param providers list of providers
+     * @param threadSafe if true then thread-safe proxy will be created
+     * @return typed proxy
+     */
+    public static <T> T createFromModel(String baseAddress, Class<T> cls, String modelRef,
+                                        List<?> providers, boolean threadSafe) {
+        JAXRSClientFactoryBean bean = WebClient.getBean(baseAddress, null);
+        bean.setProviders(providers);
+        bean.setModelRef(modelRef);
+        bean.setServiceClass(cls);
+        if (threadSafe) {
+            bean.setInitialState(new ThreadLocalClientState(baseAddress));
+        }
+        return bean.create(cls);
+    }
+
+    /**
+     * Creates a thread safe proxy using user resource model and allows to
+     * specify time to keep state.
+     * @param baseAddress baseAddress
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @param modelRef model location
+     * @param providers list of providers
+     * @param timeToKeepState how long to keep this state
+     * @return typed proxy
+     */
+    public static <T> T createFromModel(String baseAddress, Class<T> cls, String modelRef,
+                                        List<?> providers, long timeToKeepState) {
+        JAXRSClientFactoryBean bean = WebClient.getBean(baseAddress, null);
+        bean.setProviders(providers);
+        bean.setModelRef(modelRef);
+        bean.setServiceClass(cls);
+        bean.setInitialState(new ThreadLocalClientState(baseAddress, timeToKeepState));
+        return bean.create(cls);
+    }
+
+    /**
+     * Creates a proxy using user resource model
+     * @param baseAddress baseAddress
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @param modelBeans model beans
+     * @param configLocation classpath location of the configuration resource
+     * @return typed proxy
+     */
+    public static <T> T createFromModel(String baseAddress, Class<T> cls, List<UserResource> modelBeans,
+                               String configLocation) {
+        return createFromModel(baseAddress, cls, modelBeans, Collections.emptyList(), configLocation);
+    }
+
+    /**
+     * Creates a proxy using user resource model
+     * @param baseAddress baseAddress
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @param modelBeans model beans
+     * @param providers list of providers
+     * @param configLocation classpath location of the configuration resource
+     * @return typed proxy
+     */
+    public static <T> T createFromModel(String baseAddress, Class<T> cls, List<UserResource> modelBeans,
+                               List<?> providers, String configLocation) {
+        JAXRSClientFactoryBean bean = WebClient.getBean(baseAddress, configLocation);
+
+        bean.setProviders(providers);
+        bean.setModelBeans(modelBeans);
+        bean.setServiceClass(cls);
+        return bean.create(cls);
+    }
+
+    /**
+     * Creates a proxy, baseURI will be set to Client currentURI
+     *
+     * @param client Client instance
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @return typed proxy
+     */
+    public static <T> T fromClient(Client client, Class<T> cls) {
+        return fromClient(client, cls, false);
+    }
+
+    /**
+     * Creates a proxy, baseURI will be set to Client currentURI
+     * @param client Client instance
+     * @param cls proxy class, if not interface then a CGLIB proxy will be created
+     * @param inheritHeaders if true then existing Client headers will be inherited by new proxy
+     *        and subresource proxies if any
+     * @return typed proxy
+     */
+    public static <T> T fromClient(Client client, Class<T> cls, boolean inheritHeaders) {
+        JAXRSClientFactoryBean bean = getBean(client.getCurrentURI().toString(), cls, null);
+        bean.setInheritHeaders(inheritHeaders);
+
+        ClientState clientState = WebClient.getClientState(client);
+
+        final T proxy;
+        if (clientState == null) {
+            proxy = bean.create(cls);
+            if (inheritHeaders) {
+                WebClient.client(proxy).headers(client.getHeaders());
+            }
+        } else {
+            MultivaluedMap<String, String> headers = inheritHeaders ? client.getHeaders() : null;
+            bean.setInitialState(clientState.newState(client.getCurrentURI(), headers, null, bean.getProperties()));
+            proxy = bean.create(cls);
+        }
+        WebClient.copyProperties(WebClient.client(proxy), client);
+        return proxy;
+    }
+
+    static <T> T createProxy(Class<T> cls, ClassLoader loader, InvocationHandler handler) {
+
+        return cls.cast(ProxyHelper.getProxy(loader == null ? cls.getClassLoader() : loader,
+                                             new Class[]{Client.class, InvocationHandlerAware.class, cls},
+                                             handler));
+    }
+
+    private static JAXRSClientFactoryBean getBean(String baseAddress, Class<?> cls, String configLocation) {
+        JAXRSClientFactoryBean bean = WebClient.getBean(baseAddress, configLocation);
+        bean.setServiceClass(cls);
+        return bean;
+    }
+
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/client/LocalClientState.java b/transform/src/patch/java/org/apache/cxf/jaxrs/client/LocalClientState.java
new file mode 100644
index 0000000..477e5d3
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/client/LocalClientState.java
@@ -0,0 +1,185 @@
+/**
+ * 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.client;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.jaxrs.impl.MetadataMap;
+import org.apache.cxf.jaxrs.impl.UriBuilderImpl;
+
+/**
+ * Keeps the client state such as the baseURI, currentURI, requestHeaders, current response
+ *
+ */
+public class LocalClientState implements ClientState {
+    private static final String HTTP_SCHEME = "http";
+    private static final String WS_SCHEME = "ws";
+
+    private MultivaluedMap<String, String> requestHeaders = new MetadataMap<>(false, true);
+    private MultivaluedMap<String, String> templates;
+    private Response response;
+    private URI baseURI;
+    private UriBuilder currentBuilder;
+    private Map<String, Object> properties;
+
+    public LocalClientState() {
+
+    }
+
+    public LocalClientState(URI baseURI) {
+        this(baseURI, Collections.emptyMap());
+    }
+    
+    public LocalClientState(URI baseURI, Map<String, Object> properties) {
+        this.baseURI = baseURI;
+        
+        if (properties != null) {
+            this.properties = new HashMap<>(properties);
+        }
+        
+        resetCurrentUri(properties);
+    }
+
+    public LocalClientState(URI baseURI, URI currentURI) {
+        this(baseURI, currentURI, Collections.emptyMap()); 
+    }
+
+    public LocalClientState(URI baseURI, URI currentURI, Map<String, Object> properties) {
+        this.baseURI = baseURI;
+        
+        if (properties != null) {
+            this.properties = new HashMap<>(properties);
+        }
+        
+        this.currentBuilder = new UriBuilderImpl(properties).uri(currentURI);
+    }
+
+    public LocalClientState(LocalClientState cs) {
+        this.requestHeaders = new MetadataMap<>(cs.requestHeaders);
+        this.templates = cs.templates == null ? null : new MetadataMap<String, String>(cs.templates);
+        this.response = cs.response;
+
+        this.baseURI = cs.baseURI;
+        this.currentBuilder = cs.currentBuilder != null ? cs.currentBuilder.clone() : null;
+        this.properties = cs.properties;
+    }
+
+    private void resetCurrentUri(Map<String, Object> props) {
+        if (isSupportedScheme(baseURI)) {
+            this.currentBuilder = new UriBuilderImpl(props).uri(baseURI);
+        } else {
+            this.currentBuilder = new UriBuilderImpl(props).uri("/");
+        }
+    }
+
+    public void setCurrentBuilder(UriBuilder currentBuilder) {
+        this.currentBuilder = currentBuilder;
+    }
+
+    public UriBuilder getCurrentBuilder() {
+        return currentBuilder;
+    }
+
+    public void setBaseURI(URI baseURI) {
+        this.baseURI = baseURI;
+        resetCurrentUri(Collections.emptyMap());
+    }
+
+    public URI getBaseURI() {
+        return baseURI;
+    }
+
+    public void setResponse(Response r) {
+        this.response = r;
+    }
+
+    public Response getResponse() {
+        return response;
+    }
+
+    public void setRequestHeaders(MultivaluedMap<String, String> requestHeaders) {
+        this.requestHeaders = requestHeaders;
+    }
+
+    public MultivaluedMap<String, String> getRequestHeaders() {
+        return requestHeaders;
+    }
+
+    public MultivaluedMap<String, String> getTemplates() {
+        return templates;
+    }
+
+    public void setTemplates(MultivaluedMap<String, String> map) {
+        if (templates == null) {
+            this.templates = map;
+        } else if (map != null) {
+            templates.putAll(map);
+        } else {
+            templates = null;
+        }
+    }
+
+    public void reset() {
+        requestHeaders.clear();
+        response = null;
+        currentBuilder = new UriBuilderImpl(properties).uri(baseURI);
+        templates = null;
+    }
+    
+    public ClientState newState(URI currentURI, MultivaluedMap<String, String> headers,
+            MultivaluedMap<String, String> templatesMap, Map<String, Object> props) {
+        final ClientState state;
+        if (isSupportedScheme(currentURI)) {
+            state = new LocalClientState(currentURI, props);
+        } else {
+            state = new LocalClientState(baseURI, currentURI, props);
+        }
+        if (headers != null) {
+            state.setRequestHeaders(headers);
+        }
+        // we need to carry the template parameters forward
+        MultivaluedMap<String, String> newTemplateParams = templates;
+        if (newTemplateParams != null && templatesMap != null) {
+            newTemplateParams.putAll(templatesMap);
+        } else {
+            newTemplateParams = templatesMap;
+        }
+        state.setTemplates(newTemplateParams);
+        return state;
+    }
+
+    public ClientState newState(URI currentURI,
+                                MultivaluedMap<String, String> headers,
+                                MultivaluedMap<String, String> templatesMap) {
+        return newState(currentURI, headers, templatesMap, properties);
+    }
+
+    private static boolean isSupportedScheme(URI uri) {
+        return !StringUtils.isEmpty(uri.getScheme())
+            && (uri.getScheme().startsWith(HTTP_SCHEME) || uri.getScheme().startsWith(WS_SCHEME));
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/client/WebClient.java b/transform/src/patch/java/org/apache/cxf/jaxrs/client/WebClient.java
new file mode 100644
index 0000000..eaedd4b
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/client/WebClient.java
@@ -0,0 +1,1349 @@
+/**
+ * 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.client;
+
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.AsyncInvoker;
+import javax.ws.rs.client.CompletionStageRxInvoker;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.client.RxInvoker;
+import javax.ws.rs.client.RxInvokerProvider;
+import javax.ws.rs.client.SyncInvoker;
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.GenericType;
+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.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.UriBuilder;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.bus.spring.SpringBusFactory;
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.classloader.ClassLoaderUtils.ClassLoaderHolder;
+import org.apache.cxf.common.util.ClassHelper;
+import org.apache.cxf.common.util.PropertyUtils;
+import org.apache.cxf.feature.Feature;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.jaxrs.client.spec.ClientImpl.WebTargetImpl;
+import org.apache.cxf.jaxrs.client.spec.InvocationBuilderImpl;
+import org.apache.cxf.jaxrs.impl.ResponseImpl;
+import org.apache.cxf.jaxrs.impl.UriBuilderImpl;
+import org.apache.cxf.jaxrs.model.ParameterType;
+import org.apache.cxf.jaxrs.model.URITemplate;
+import org.apache.cxf.jaxrs.utils.HttpUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.jaxrs.utils.ParameterizedCollectionType;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.Message;
+
+
+/**
+ * Http-centric web client
+ *
+ */
+public class WebClient extends AbstractClient {
+    private static final String REQUEST_CLASS = "request.class";
+    private static final String REQUEST_TYPE = "request.type";
+    private static final String REQUEST_ANNS = "request.annotations";
+    private static final String RESPONSE_CLASS = "response.class";
+    private static final String RESPONSE_TYPE = "response.type";
+    private static final String WEB_CLIENT_OPERATION_REPORTING = "enable.webclient.operation.reporting";
+    private BodyWriter bodyWriter = new BodyWriter();
+    protected WebClient(String baseAddress) {
+        this(convertStringToURI(baseAddress), Collections.emptyMap());
+    }
+    
+    protected WebClient(String baseAddress, Map<String, Object> properties) {
+        this(convertStringToURI(baseAddress), properties);
+    }
+
+    protected WebClient(URI baseURI) {
+        this(baseURI, Collections.emptyMap());
+    }
+
+    protected WebClient(URI baseURI, Map<String, Object> properties) {
+        this(new LocalClientState(baseURI, properties));
+    }
+
+    protected WebClient(ClientState state) {
+        super(state);
+        cfg.getInInterceptors().add(new ClientAsyncResponseInterceptor());
+    }
+
+
+
+
+    /**
+     * Creates WebClient
+     * @param baseAddress baseAddress
+     */
+    public static WebClient create(String baseAddress) {
+        return create(baseAddress, Collections.emptyMap());
+    }
+
+    /**
+     * Creates WebClient
+     * @param baseAddress baseAddress
+     */
+    public static WebClient create(String baseAddress, Map<String, Object> properties) {
+        JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
+        bean.setAddress(baseAddress);
+        bean.setProperties(properties);
+        return bean.createWebClient();
+    }
+
+    /**
+     * Creates WebClient
+     * @param baseURI baseURI
+     */
+    public static WebClient create(URI baseURI) {
+        return create(baseURI.toString());
+    }
+
+    /**
+     * Creates WebClient
+     * @param baseURI baseURI
+     */
+    public static WebClient create(String baseURI, boolean threadSafe) {
+        return create(baseURI, Collections.emptyList(), threadSafe);
+    }
+
+    /**
+     * Creates WebClient
+     * @param baseAddress baseURI
+     * @param providers list of providers
+     */
+    public static WebClient create(String baseAddress, List<?> providers) {
+        return create(baseAddress, providers, null);
+    }
+
+    /**
+     * Creates WebClient
+     * @param baseAddress baseURI
+     * @param providers list of providers
+     * @param threadSafe if true ThreadLocalClientState is used
+     */
+    public static WebClient create(String baseAddress, List<?> providers, boolean threadSafe) {
+        return create(baseAddress, providers, Collections.emptyMap(), threadSafe);
+    }
+    
+    /**
+     * Creates WebClient
+     * @param baseAddress baseURI
+     * @param providers list of providers
+     * @param threadSafe if true ThreadLocalClientState is used
+     * @param properties additional properties
+     */
+    public static WebClient create(String baseAddress, List<?> providers, 
+            Map<String, Object> properties, boolean threadSafe) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, null);
+        bean.setProviders(providers);
+        bean.setProperties(properties);
+        if (threadSafe) {
+            bean.setInitialState(new ThreadLocalClientState(baseAddress, properties));
+        }
+        return bean.createWebClient();
+    }
+
+    /**
+     * Creates a thread safe WebClient
+     * @param baseAddress baseURI
+     * @param providers list of providers
+     * @param timeToKeepState time to keep this thread safe state.
+     */
+    public static WebClient create(String baseAddress, List<?> providers, long timeToKeepState) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, null);
+        bean.setProviders(providers);
+        bean.setInitialState(new ThreadLocalClientState(baseAddress, timeToKeepState));
+        return bean.createWebClient();
+    }
+
+    /**
+     * Creates WebClient
+     * @param baseAddress baseAddress
+     * @param providers list of providers
+     * @param configLocation classpath location of the configuration resource, can be null
+     * @return WebClient instance
+     */
+    public static WebClient create(String baseAddress, List<?> providers, String configLocation) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, configLocation);
+        bean.setProviders(providers);
+        return bean.createWebClient();
+    }
+
+    /**
+     * Creates WebClient with a list of custom features
+     * @param baseAddress baseAddress
+     * @param providers list of providers
+     * @param features the features which will be applied to the client
+     * @param configLocation classpath location of the configuration resource, can be null
+     * @return WebClient instance
+     */
+    public static WebClient create(String baseAddress,
+                                   List<?> providers,
+                                   List<? extends Feature> features,
+                                   String configLocation) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, configLocation);
+        bean.setProviders(providers);
+        bean.setFeatures(features);
+        return bean.createWebClient();
+    }
+
+    /**
+     * Creates WebClient
+     * @param baseAddress baseAddress
+     * @param configLocation classpath location of the configuration resource, can be null
+     * @return WebClient instance
+     */
+    public static WebClient create(String baseAddress, String configLocation) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, configLocation);
+
+        return bean.createWebClient();
+    }
+
+    /**
+     * Creates WebClient which will do basic authentication
+     * @param baseAddress baseAddress
+     * @param username username
+     * @param password password
+     * @param configLocation classpath location of the configuration resource, can be null
+     * @return WebClient instance
+     */
+    public static WebClient create(String baseAddress, String username, String password,
+                                         String configLocation) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, configLocation);
+
+        bean.setUsername(username);
+        bean.setPassword(password);
+
+        return bean.createWebClient();
+    }
+
+    /**
+     * Creates WebClient which will do basic authentication
+     * @param baseAddress baseAddress
+     * @param providers list of providers
+     * @param username username
+     * @param password password
+     * @param configLocation classpath location of the configuration resource, can be null
+     * @return WebClient instance
+     */
+    public static WebClient create(String baseAddress, List<?> providers,
+                                   String username, String password, String configLocation) {
+        JAXRSClientFactoryBean bean = getBean(baseAddress, configLocation);
+
+        bean.setUsername(username);
+        bean.setPassword(password);
+        bean.setProviders(providers);
+        return bean.createWebClient();
+    }
+
+    /**
+     * Creates WebClient, baseURI will be set to Client currentURI
+     * @param object existing client object
+     */
+    public static WebClient fromClientObject(Object object) {
+        Client client = client(object);
+        return client == null ? null : fromClient(client, false);
+    }
+
+    /**
+     * Creates WebClient, baseURI will be set to Client currentURI
+     * @param client existing client
+     */
+    public static WebClient fromClient(Client client) {
+        return fromClient(client, false);
+    }
+
+    /**
+     * Creates WebClient, baseURI will be set to Client currentURI
+     * @param client existing client
+     * @param inheritHeaders  if existing Client headers can be inherited by new client
+     */
+    public static WebClient fromClient(Client client, boolean inheritHeaders) {
+
+        final WebClient webClient;
+
+        ClientState clientState = getClientState(client);
+        if (clientState == null) {
+            webClient = create(client.getCurrentURI());
+            if (inheritHeaders) {
+                webClient.headers(client.getHeaders());
+            }
+        } else {
+            MultivaluedMap<String, String> headers = inheritHeaders ? client.getHeaders() : null;
+            webClient = new WebClient(clientState.newState(client.getCurrentURI(), headers, null));
+        }
+        copyProperties(webClient, client);
+        return webClient;
+    }
+
+    /**
+     * Converts object to Client
+     * @param object the object
+     * @return Client object converted to Client
+     */
+    public static Client client(Object object) {
+        if (object instanceof Client) {
+            return (Client)object;
+        }
+        return null;
+    }
+
+    /**
+     * Retrieves ClientConfiguration
+     * @param client proxy or http-centric Client
+     * @return underlying ClientConfiguration instance
+     */
+    public static ClientConfiguration getConfig(Object client) {
+        if (client instanceof WebTargetImpl) {
+            client = ((WebTargetImpl)client).getWebClient();
+        } else if (client instanceof InvocationBuilderImpl) {
+            client = ((InvocationBuilderImpl)client).getWebClient();
+        }
+
+        if (client instanceof Client) {
+            if (client instanceof WebClient) {
+                return ((AbstractClient)client).getConfiguration();
+            } else if (client instanceof InvocationHandlerAware) {
+                Object handler = ((InvocationHandlerAware)client).getInvocationHandler();
+                return ((AbstractClient)handler).getConfiguration();
+            }
+        }
+        throw new IllegalArgumentException("Not a valid Client");
+    }
+
+    /**
+     * Does HTTP invocation
+     * @param httpMethod HTTP method
+     * @param body request body, can be null
+     * @return JAXRS Response, entity may hold a string representaion of
+     *         error message if client or server error occured
+     */
+    public Response invoke(String httpMethod, Object body) {
+        return doInvoke(httpMethod, body, null, Response.class, Response.class);
+    }
+
+    /**
+     * Does HTTP POST invocation
+     * @param body request body, can be null
+     * @return JAXRS Response
+     */
+    public Response post(Object body) {
+        return invoke(HttpMethod.POST, body);
+    }
+
+    /**
+     * Does HTTP PUT invocation
+     * @param body request body, can be null
+     * @return JAXRS Response
+     */
+    public Response put(Object body) {
+        return invoke(HttpMethod.PUT, body);
+    }
+
+    /**
+     * Does HTTP GET invocation
+     * @return JAXRS Response
+     */
+    public Response get() {
+        return invoke(HttpMethod.GET, null);
+    }
+
+    /**
+     * Does HTTP HEAD invocation
+     * @return JAXRS Response
+     */
+    public Response head() {
+        return invoke(HttpMethod.HEAD, null);
+    }
+
+    /**
+     * Does HTTP OPTIONS invocation
+     * @return JAXRS Response
+     */
+    public Response options() {
+        return invoke(HttpMethod.OPTIONS, null);
+    }
+
+    /**
+     * Does HTTP DELETE invocation
+     * @return JAXRS Response
+     */
+    public Response delete() {
+        return invoke(HttpMethod.DELETE, null);
+    }
+
+    /**
+     * Posts form data
+     * @param values form values
+     * @return JAXRS Response
+     */
+    public Response form(Map<String, List<Object>> values) {
+        type(MediaType.APPLICATION_FORM_URLENCODED);
+        return doInvoke(HttpMethod.POST, values, null, Response.class, Response.class);
+    }
+
+    /**
+     * Posts form data
+     * @param form form values
+     * @return JAXRS Response
+     */
+    public Response form(Form form) {
+        type(MediaType.APPLICATION_FORM_URLENCODED);
+        return doInvoke(HttpMethod.POST, form.asMap(), null, Response.class, Response.class);
+    }
+
+    /**
+     * Does HTTP invocation and returns types response object
+     * @param httpMethod HTTP method
+     * @param body request body, can be null
+     * @param responseType generic response type
+     * @return typed object, can be null. Response status code and headers
+     *         can be obtained too, see Client.getResponse()
+     */
+    public <T> T invoke(String httpMethod, Object body, GenericType<T> responseType) {
+        @SuppressWarnings("unchecked")
+        Class<T> responseClass = (Class<T>)responseType.getRawType();
+        Response r = doInvoke(httpMethod, body, null, responseClass, responseType.getType());
+        return castResponse(r, responseClass);
+    }
+
+    /**
+     * Does HTTP invocation and returns types response object
+     * @param httpMethod HTTP method
+     * @param body request body, can be null
+     * @param responseClass expected type of response object
+     * @return typed object, can be null. Response status code and headers
+     *         can be obtained too, see Client.getResponse()
+     */
+    public <T> T invoke(String httpMethod, Object body, Class<T> responseClass) {
+        Response r = doInvoke(httpMethod, body, null, responseClass, responseClass);
+        return castResponse(r, responseClass);
+    }
+
+    /**
+     * Does HTTP invocation and returns types response object
+     * @param httpMethod HTTP method
+     * @param body request body, can be null
+     * @param requestClass request body class
+     * @param responseClass expected type of response object
+     * @return typed object, can be null. Response status code and headers
+     *         can be obtained too, see Client.getResponse()
+     */
+    public <T> T invoke(String httpMethod, Object body, Class<?> requestClass, Class<T> responseClass) {
+        Response r = doInvoke(httpMethod, body, requestClass, null, responseClass, responseClass);
+        return castResponse(r, responseClass);
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T> T castResponse(Response r, Class<T> responseClass) {
+        return (T)(responseClass == Response.class ? r : r.getEntity());
+    }
+    /**
+     * Does HTTP POST invocation and returns typed response object
+     * @param body request body, can be null
+     * @param responseClass expected type of response object
+     * @return typed object, can be null. Response status code and headers
+     *         can be obtained too, see Client.getResponse()
+     */
+    public <T> T post(Object body, Class<T> responseClass) {
+        return invoke(HttpMethod.POST, body, responseClass);
+    }
+
+    /**
+     * Does HTTP POST invocation and returns typed response object
+     * @param body request body, can be null
+     * @param responseType generic response type
+     * @return typed object, can be null. Response status code and headers
+     *         can be obtained too, see Client.getResponse()
+     */
+    public <T> T post(Object body, GenericType<T> responseType) {
+        return invoke(HttpMethod.POST, body, responseType);
+    }
+
+    /**
+     * Does HTTP Async POST invocation and returns Future.
+     * Shortcut for async().post(Entity, InvocationCallback)
+     * @param callback invocation callback
+     * @return the future
+     */
+    public <T> Future<T> post(Object body, InvocationCallback<T> callback) {
+        return doInvokeAsyncCallback(HttpMethod.POST, body, body.getClass(), null, callback);
+    }
+
+    /**
+     * Does HTTP PUT invocation and returns typed response object
+     * @param body request body, can be null
+     * @param responseClass expected type of response object
+     * @return typed object, can be null. Response status code and headers
+     *         can be obtained too, see Client.getResponse()
+     */
+    public <T> T put(Object body, Class<T> responseClass) {
+        return invoke(HttpMethod.PUT, body, responseClass);
+    }
+
+
+    /**
+     * Does HTTP PUT invocation and returns typed response object
+     * @param body request body, can be null
+     * @param responseType generic response type
+     * @return typed object, can be null. Response status code and headers
+     *         can be obtained too, see Client.getResponse()
+     */
+    public <T> T put(Object body, GenericType<T> responseType) {
+        return invoke(HttpMethod.PUT, body, responseType);
+    }
+
+    /**
+     * Does HTTP Async PUT invocation and returns Future.
+     * Shortcut for async().put(Entity, InvocationCallback)
+     * @param callback invocation callback
+     * @return the future
+     */
+    public <T> Future<T> put(Object body, InvocationCallback<T> callback) {
+        return doInvokeAsyncCallback(HttpMethod.PUT, body, body.getClass(), null, callback);
+    }
+
+    /**
+     * Does HTTP invocation and returns a collection of typed objects
+     * @param httpMethod HTTP method
+     * @param body request body, can be null
+     * @param memberClass expected type of collection member class
+     * @return typed collection
+     */
+    public <T> Collection<? extends T> invokeAndGetCollection(String httpMethod, Object body,
+                                                    Class<T> memberClass) {
+        Response r = doInvoke(httpMethod, body, null,
+                              Collection.class, new ParameterizedCollectionType(memberClass));
+        return CastUtils.cast((Collection<?>)r.getEntity(), memberClass);
+    }
+
+    /**
+     * Posts a collection of typed objects
+     * @param collection request body
+     * @param memberClass type of collection member class
+     * @return JAX-RS Response
+     */
+    public <T> Response postCollection(Object collection, Class<T> memberClass) {
+        return doInvoke(HttpMethod.POST, collection, new ParameterizedCollectionType(memberClass),
+                        Response.class, Response.class);
+    }
+
+    /**
+     * Posts a collection of typed objects
+     * @param collection request body
+     * @param memberClass type of collection member class
+     * @param responseClass expected type of response object
+     * @return JAX-RS Response
+     */
+    public <T1, T2> T2 postCollection(Object collection, Class<T1> memberClass,
+                                            Class<T2> responseClass) {
+        Response r = doInvoke(HttpMethod.POST, collection, new ParameterizedCollectionType(memberClass),
+                              responseClass, responseClass);
+        return responseClass.cast(responseClass == Response.class ? r : r.getEntity());
+    }
+
+    /**
+     * Posts collection of typed objects and returns a collection of typed objects
+     * @param collection request body
+     * @param memberClass type of collection member class
+     * @param responseClass expected type of response object
+     * @return JAX-RS Response
+     */
+    public <T1, T2> Collection<? extends T2> postAndGetCollection(Object collection,
+                                                                  Class<T1> memberClass,
+                                                                  Class<T2> responseClass) {
+        Response r = doInvoke(HttpMethod.POST, collection, new ParameterizedCollectionType(memberClass),
+                              Collection.class, new ParameterizedCollectionType(responseClass));
+        return CastUtils.cast((Collection<?>)r.getEntity(), responseClass);
+    }
+
+    /**
+     * Posts the object and returns a collection of typed objects
+     * @param body request body
+     * @param responseClass expected type of response object
+     * @return JAX-RS Response
+     */
+    public <T> Collection<? extends T> postObjectGetCollection(Object body,
+                                                                  Class<T> responseClass) {
+        Response r = doInvoke(HttpMethod.POST, body, null, Collection.class,
+                              new ParameterizedCollectionType(responseClass));
+        return CastUtils.cast((Collection<?>)r.getEntity(), responseClass);
+    }
+
+    /**
+     * Posts request body and returns a collection of typed objects
+     * @param body request body, can be null
+     * @param memberClass expected type of collection member class
+     * @return typed collection
+     */
+    public <T> Collection<? extends T> postAndGetCollection(Object body, Class<T> memberClass) {
+        return invokeAndGetCollection(HttpMethod.POST, body, memberClass);
+    }
+
+    /**
+     * Does HTTP GET invocation and returns a collection of typed objects
+     * @param memberClass expected type of collection member class
+     * @return typed collection
+     */
+    public <T> Collection<? extends T> getCollection(Class<T> memberClass) {
+        return invokeAndGetCollection(HttpMethod.GET, null, memberClass);
+    }
+
+    /**
+     * Does HTTP GET invocation and returns typed response object
+     * @param responseClass expected type of response object
+     * @return typed object, can be null. Response status code and headers
+     *         can be obtained too, see Client.getResponse()
+     */
+    public <T> T get(Class<T> responseClass) {
+        return invoke(HttpMethod.GET, null, responseClass);
+    }
+
+
+    /**
+     * Does HTTP GET invocation and returns typed response object
+     * @param responseType generic response type
+     * @return typed object, can be null. Response status code and headers
+     *         can be obtained too, see Client.getResponse()
+     */
+    public <T> T get(GenericType<T> responseType) {
+        return invoke(HttpMethod.GET, null, responseType);
+    }
+
+    /**
+     * Does HTTP Async GET invocation and returns Future.
+     * Shortcut for async().get(InvocationCallback)
+     * @param callback invocation callback
+     * @return the future
+     */
+    public <T> Future<T> get(InvocationCallback<T> callback) {
+        return doInvokeAsyncCallback(HttpMethod.GET, null, null, null, callback);
+    }
+
+    /**
+     * Updates the current URI path
+     * @param path new relative path segment
+     * @return updated WebClient
+     */
+    public WebClient path(Object path) {
+        getCurrentBuilder().path(convertParamValue(path, null));
+
+        return this;
+    }
+
+    /**
+     * Updates the current URI path with path segment which may contain template variables
+     * @param path new relative path segment
+     * @param values template variable values
+     * @return updated WebClient
+     */
+    public WebClient path(String path, Object... values) {
+        URI u = new UriBuilderImpl().uri(URI.create("http://tempuri")).path(path).buildFromEncoded(values);
+        getState().setTemplates(getTemplateParametersMap(new URITemplate(path), Arrays.asList(values)));
+        return path(u.getRawPath());
+    }
+
+    @Override
+    public WebClient query(String name, Object ...values) {
+        return (WebClient)super.query(name, values);
+    }
+
+    /**
+     * Updates the current URI matrix parameters
+     * @param name matrix name
+     * @param values matrix values
+     * @return updated WebClient
+     */
+    public WebClient matrix(String name, Object ...values) {
+        addMatrixQueryParamsToBuilder(getCurrentBuilder(), name, ParameterType.MATRIX, null, values);
+        return this;
+    }
+
+    /**
+     * Updates the current URI fragment
+     * @param name fragment name
+     * @return updated WebClient
+     */
+    public WebClient fragment(String name) {
+        getCurrentBuilder().fragment(name);
+        return this;
+    }
+
+    /**
+     * Moves WebClient to a new baseURI or forwards to new currentURI
+     * @param newAddress new URI
+     * @param forward if true then currentURI will be based on baseURI
+     * @return updated WebClient
+     */
+    public WebClient to(String newAddress, boolean forward) {
+        getState().setTemplates(null);
+        if (forward) {
+            if (!newAddress.startsWith("/")
+                && !newAddress.startsWith(getBaseURI().toString())) {
+                throw new IllegalArgumentException("Base address can not be preserved");
+            }
+            resetCurrentBuilder(URI.create(newAddress));
+        } else {
+            resetBaseAddress(URI.create(newAddress));
+        }
+        return this;
+    }
+
+    /**
+     * Goes back
+     * @param fast if true then goes back to baseURI otherwise to a previous path segment
+     * @return updated WebClient
+     */
+    public WebClient back(boolean fast) {
+        getState().setTemplates(null);
+        if (fast) {
+            getCurrentBuilder().replacePath(getBaseURI().getPath());
+        } else {
+            URI uri = getCurrentURI();
+            if (uri == getBaseURI()) {
+                return this;
+            }
+            List<PathSegment> segments = JAXRSUtils.getPathSegments(uri.getPath(), false);
+            getCurrentBuilder().replacePath(null);
+            for (int i = 0; i < segments.size() - 1; i++) {
+                getCurrentBuilder().path(HttpUtils.fromPathSegment(segments.get(i)));
+            }
+
+        }
+        return this;
+    }
+
+    /**
+     * Replaces the current path with the new value.
+     * @param path new path value. If it starts from "/" then all the current
+     * path starting from the base URI will be replaced, otherwise only the
+     * last path segment will be replaced. Providing a null value is equivalent
+     * to calling back(true)
+     * @return updated WebClient
+     */
+    public WebClient replacePath(String path) {
+        if (path == null) {
+            return back(true);
+        }
+        back(path.startsWith("/") ? true : false);
+        return path(path);
+    }
+
+    /**
+     * Resets the current query
+     * @return updated WebClient
+     */
+    public WebClient resetQuery() {
+        return replaceQuery(null);
+    }
+
+    /**
+     * Replaces the current query with the new value.
+     * @param queryString the new value, providing a null is
+     *        equivalent to calling resetQuery().
+     * @return updated WebClient
+     */
+    public WebClient replaceQuery(String queryString) {
+        getCurrentBuilder().replaceQuery(queryString);
+        return this;
+    }
+
+    /**
+     * Replaces the header value with the new values.
+     * @param headerName headerValues
+     * @param value new values, null is equivalent to removing the header
+     * @return updated WebClient
+     */
+    public WebClient replaceHeader(String headerName, Object value) {
+        MultivaluedMap<String, String> headers = getState().getRequestHeaders();
+        headers.remove(headerName);
+        if (value != null) {
+            super.header(headerName, value);
+        }
+        return this;
+    }
+
+    /**
+     * Replaces the current query with the new value.
+     * @param queryParam query param name
+     * @param value the new value, providing a null is
+     *        equivalent to calling resetQuery().
+     * @return updated WebClient
+     */
+    public WebClient replaceQueryParam(String queryParam, Object... value) {
+        getCurrentBuilder().replaceQueryParam(queryParam, value);
+        return this;
+    }
+
+    @Override
+    public WebClient type(MediaType ct) {
+        return (WebClient)super.type(ct);
+    }
+
+    @Override
+    public WebClient type(String type) {
+        return (WebClient)super.type(type);
+    }
+
+    @Override
+    public WebClient accept(MediaType... types) {
+        return (WebClient)super.accept(types);
+    }
+
+    @Override
+    public WebClient accept(String... types) {
+        return (WebClient)super.accept(types);
+    }
+
+    @Override
+    public WebClient language(String language) {
+        return (WebClient)super.language(language);
+    }
+
+    @Override
+    public WebClient acceptLanguage(String ...languages) {
+        return (WebClient)super.acceptLanguage(languages);
+    }
+
+    @Override
+    public WebClient encoding(String encoding) {
+        return (WebClient)super.encoding(encoding);
+    }
+
+    @Override
+    public WebClient acceptEncoding(String ...encodings) {
+        return (WebClient)super.acceptEncoding(encodings);
+    }
+
+    @Override
+    public WebClient match(EntityTag tag, boolean ifNot) {
+        return (WebClient)super.match(tag, ifNot);
+    }
+
+    @Override
+    public WebClient modified(Date date, boolean ifNot) {
+        return (WebClient)super.modified(date, ifNot);
+    }
+
+    @Override
+    public WebClient cookie(Cookie cookie) {
+        return (WebClient)super.cookie(cookie);
+    }
+
+    @Override
+    public WebClient authorization(Object auth) {
+        return (WebClient)super.authorization(auth);
+    }
+
+    @Override
+    public WebClient header(String name, Object... values) {
+        return (WebClient)super.header(name, values);
+    }
+
+    @Override
+    public WebClient headers(MultivaluedMap<String, String> map) {
+        return (WebClient)super.headers(map);
+    }
+
+    @Override
+    public WebClient reset() {
+        //clearTemplates();
+        return (WebClient)super.reset();
+    }
+
+    protected Response doInvoke(String httpMethod,
+                                Object body,
+                                Type inGenericType,
+                                Class<?> responseClass,
+                                Type outGenericType) {
+        return doInvoke(httpMethod, body, body == null ? null : body.getClass(), inGenericType,
+            responseClass, outGenericType);
+    }
+
+
+
+    protected Response doInvoke(String httpMethod,
+                                Object body,
+                                Class<?> requestClass,
+                                Type inGenericType,
+                                Class<?> responseClass,
+                                Type outGenericType) {
+        Annotation[] inAnns = null;
+        if (body instanceof Entity) {
+            Entity<?> entity = (Entity<?>)body;
+            setEntityHeaders(entity);
+            body = entity.getEntity();
+            requestClass = body.getClass();
+            inGenericType = body.getClass();
+            inAnns = entity.getAnnotations();
+        }
+        if (body instanceof GenericEntity) {
+            GenericEntity<?> genericEntity = (GenericEntity<?>)body;
+            body = genericEntity.getEntity();
+            requestClass = genericEntity.getRawType();
+            inGenericType = genericEntity.getType();
+        }
+        MultivaluedMap<String, String> headers = prepareHeaders(responseClass, body);
+        resetResponse();
+        final Response r;
+        try {
+            r = doChainedInvocation(httpMethod, headers, body, requestClass, inGenericType,
+                                             inAnns, responseClass, outGenericType, null, null);
+        } finally {
+            resetResponseStateImmediatelyIfNeeded();
+        }
+
+        int status = r.getStatus();
+        if (status != 304 && status >= 300 && responseClass != Response.class) {
+            throw convertToWebApplicationException(r);
+        }
+        return r;
+    }
+
+    protected <T> Future<T> doInvokeAsyncCallback(String httpMethod,
+                                                  Object body,
+                                                  Class<?> requestClass,
+                                                  Type inType,
+                                                  InvocationCallback<T> callback) {
+
+        Type outType = getCallbackType(callback);
+        Class<?> respClass = getCallbackClass(outType);
+        return doInvokeAsync(httpMethod, body, requestClass, inType, respClass, outType, callback);
+    }
+
+    protected <T> Future<T> doInvokeAsync(String httpMethod,
+                                          Object body,
+                                          Class<?> requestClass,
+                                          Type inType,
+                                          Class<?> respClass,
+                                          Type outType,
+                                          InvocationCallback<T> callback) {
+        JaxrsClientCallback<T> cb = new JaxrsClientCallback<>(callback, respClass, outType);
+        prepareAsyncClient(httpMethod, body, requestClass, inType, respClass, outType, cb);
+        return cb.createFuture();
+    }
+
+    protected void prepareAsyncClient(String httpMethod,
+                                   Object body,
+                                   Class<?> requestClass,
+                                   Type inType,
+                                   Class<?> respClass,
+                                   Type outType,
+                                   JaxrsClientCallback<?> cb) {
+        Annotation[] inAnns = null;
+        if (body instanceof Entity) {
+            Entity<?> entity = (Entity<?>)body;
+            setEntityHeaders(entity);
+            body = entity.getEntity();
+            requestClass = body.getClass();
+            inType = body.getClass();
+            inAnns = entity.getAnnotations();
+        }
+        if (body instanceof GenericEntity) {
+            GenericEntity<?> genericEntity = (GenericEntity<?>)body;
+            body = genericEntity.getEntity();
+            requestClass = genericEntity.getRawType();
+            inType = genericEntity.getType();
+        }
+
+        MultivaluedMap<String, String> headers = prepareHeaders(respClass, body);
+        resetResponse();
+
+        Message m = finalizeMessage(httpMethod, headers, body, requestClass, inType,
+                                  inAnns, respClass, outType, null, null);
+
+        m.getExchange().setSynchronous(false);
+        setAsyncMessageObserverIfNeeded(m.getExchange());
+        m.getExchange().put(JaxrsClientCallback.class, cb);
+
+        doRunInterceptorChain(m);
+    }
+
+
+    private MultivaluedMap<String, String> prepareHeaders(Class<?> responseClass, Object body) {
+        MultivaluedMap<String, String> headers = getHeaders();
+        if (headers.getFirst(HttpHeaders.CONTENT_TYPE) == null && body instanceof Form) {
+            headers.putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED);
+        }
+
+        if (responseClass != null && responseClass != Response.class
+            && headers.getFirst(HttpHeaders.ACCEPT) == null) {
+            headers.putSingle(HttpHeaders.ACCEPT, MediaType.WILDCARD);
+        }
+        return headers;
+    }
+
+    class ClientAsyncResponseInterceptor extends AbstractClientAsyncResponseInterceptor {
+        @Override
+        protected void doHandleAsyncResponse(Message message, Response r, JaxrsClientCallback<?> cb) {
+            if (r == null) {
+                try {
+                    r = handleResponse(message.getExchange().getOutMessage(),
+                                       cb.getResponseClass(),
+                                       cb.getOutGenericType());
+                } catch (Throwable t) {
+                    cb.handleException(message, t);
+                    return;
+                } finally {
+                    completeExchange(message.getExchange(), false);
+                }
+            }
+            if (cb.getResponseClass() == null || Response.class.equals(cb.getResponseClass())) {
+                cb.handleResponse(message, new Object[] {r});
+            } else if (r.getStatus() >= 300) {
+                cb.handleException(message, convertToWebApplicationException(r));
+            } else {
+                cb.handleResponse(message, new Object[] {r.getEntity()});
+                closeAsyncResponseIfPossible(r, message, cb);
+            }
+        }
+    }
+
+
+
+    //TODO: retry invocation will not work in case of async request failures for the moment
+    @Override
+    protected Object retryInvoke(URI newRequestURI,
+                                 MultivaluedMap<String, String> headers,
+                                 Object body,
+                                 Exchange exchange,
+                                 Map<String, Object> invContext) throws Throwable {
+
+        Map<String, Object> reqContext = CastUtils.cast((Map<?, ?>)invContext.get(REQUEST_CONTEXT));
+        String httpMethod = (String)reqContext.get(Message.HTTP_REQUEST_METHOD);
+        Class<?> requestClass = (Class<?>)reqContext.get(REQUEST_CLASS);
+        Type inType = (Type)reqContext.get(REQUEST_TYPE);
+        Annotation[] inAnns = (Annotation[])reqContext.get(REQUEST_ANNS);
+        Class<?> respClass = (Class<?>)reqContext.get(RESPONSE_CLASS);
+        Type outType = (Type)reqContext.get(RESPONSE_TYPE);
+        return doChainedInvocation(httpMethod, headers, body, requestClass, inType,
+                                   inAnns, respClass, outType, exchange, invContext);
+    }
+    //CHECKSTYLE:OFF
+    protected Response doChainedInvocation(String httpMethod, //NOPMD
+                                           MultivaluedMap<String, String> headers,
+                                           Object body,
+                                           Class<?> requestClass,
+                                           Type inType,
+                                           Annotation[] inAnns,
+                                           Class<?> respClass,
+                                           Type outType,
+                                           Exchange exchange,
+                                           Map<String, Object> invContext) {
+    //CHECKSTYLE:ON
+        Bus configuredBus = getConfiguration().getBus();
+        Bus origBus = BusFactory.getAndSetThreadDefaultBus(configuredBus);
+        ClassLoaderHolder origLoader = null;
+        try {
+            ClassLoader loader = configuredBus.getExtension(ClassLoader.class);
+            if (loader != null) {
+                origLoader = ClassLoaderUtils.setThreadContextClassloader(loader);
+            }
+            Message m = finalizeMessage(httpMethod, headers, body, requestClass, inType,
+                                        inAnns, respClass, outType, exchange, invContext);
+            doRunInterceptorChain(m);
+            return doResponse(m, respClass, outType);
+        } finally {
+            if (origLoader != null) {
+                origLoader.reset();
+            }
+            if (origBus != configuredBus) {
+                BusFactory.setThreadDefaultBus(origBus);
+            }
+        }
+    }
+
+    //CHECKSTYLE:OFF
+    private Message finalizeMessage(String httpMethod, //NOPMD
+                                   MultivaluedMap<String, String> headers,
+                                   Object body,
+                                   Class<?> requestClass,
+                                   Type inGenericType,
+                                   Annotation[] inAnns,
+                                   Class<?> responseClass,
+                                   Type outGenericType,
+                                   Exchange exchange,
+                                   Map<String, Object> invContext) {
+   //CHECKSTYLE:ON
+        URI uri = getCurrentURI();
+        Message m = createMessage(body, httpMethod, headers, uri, exchange,
+                invContext, false);
+        setSupportOnewayResponseProperty(m);
+        if (inAnns != null) {
+            m.put(Annotation.class.getName(), inAnns);
+        }
+        Map<String, Object> reqContext = getRequestContext(m);
+        reqContext.put(Message.HTTP_REQUEST_METHOD, httpMethod);
+        reqContext.put(REQUEST_CLASS, requestClass);
+        reqContext.put(REQUEST_TYPE, inGenericType);
+        reqContext.put(REQUEST_ANNS, inAnns);
+        reqContext.put(RESPONSE_CLASS, responseClass);
+        reqContext.put(RESPONSE_TYPE, outGenericType);
+
+        if (body != null) {
+            m.put(Type.class, inGenericType);
+        }
+        m.getInterceptorChain().add(bodyWriter);
+
+        setWebClientOperationProperty(m, httpMethod);
+
+        return m;
+    }
+
+    private void setWebClientOperationProperty(Message m, String httpMethod) {
+        Object prop = m.getContextualProperty(WEB_CLIENT_OPERATION_REPORTING);
+        // Enable the operation reporting by default
+        if (prop == null || PropertyUtils.isTrue(prop)) {
+            UriBuilder absPathUri = super.getCurrentBuilder().clone();
+            absPathUri.replaceQuery(null);
+            setPlainOperationNameProperty(m, httpMethod + ":" + absPathUri.build().toString());
+        }
+
+    }
+
+    protected Response doResponse(Message m,
+                                  Class<?> responseClass,
+                                  Type outGenericType) {
+        try {
+            Object[] results = preProcessResult(m);
+            if (results != null && results.length == 1) {
+                return (Response)results[0];
+            }
+        } catch (WebApplicationException | ProcessingException ex) {
+            throw ex;
+        } catch (Exception ex) {
+            throw new ProcessingException(ex);
+        }
+
+        try {
+            return handleResponse(m, responseClass, outGenericType);
+        } finally {
+            completeExchange(m.getExchange(), false);
+        }
+    }
+
+    protected Response handleResponse(Message outMessage, Class<?> responseClass, Type genericType) {
+        try {
+            ResponseBuilder rb = setResponseBuilder(outMessage, outMessage.getExchange());
+            Response currentResponse = rb.clone().build();
+            ((ResponseImpl)currentResponse).setOutMessage(outMessage);
+
+            Object entity = readBody(currentResponse, outMessage, responseClass, genericType,
+                                     new Annotation[]{});
+
+            if (entity == null) {
+                int status = currentResponse.getStatus();
+                if (status >= 400) {
+                    entity = currentResponse.getEntity();
+                }
+            }
+            rb = JAXRSUtils.fromResponse(currentResponse, false);
+
+            rb.entity(entity instanceof Response
+                      ? ((Response)entity).getEntity() : entity);
+
+            Response r = rb.build();
+            getState().setResponse(r);
+            ((ResponseImpl)r).setOutMessage(outMessage);
+            return r;
+        } catch (ProcessingException ex) {
+            throw ex;
+        } catch (Throwable ex) {
+            throw new ProcessingException(ex);
+        } finally {
+            ClientProviderFactory.getInstance(outMessage).clearThreadLocalProxies();
+        }
+    }
+
+
+    private class BodyWriter extends AbstractBodyWriter {
+
+        protected void doWriteBody(Message outMessage,
+                                   Object body,
+                                   Type bodyType,
+                                   Annotation[] customAnns,
+                                   OutputStream os) throws Fault {
+
+            Map<String, Object> requestContext = WebClient.this.getRequestContext(outMessage);
+            Class<?> requestClass = null;
+            Type requestType = null;
+            if (requestContext != null) {
+                requestClass = (Class<?>)requestContext.get(REQUEST_CLASS);
+                requestType = (Type)requestContext.get(REQUEST_TYPE);
+            }
+            if (bodyType != null) {
+                requestType = bodyType;
+            }
+
+            Annotation[] anns = customAnns != null ? customAnns : new Annotation[]{};
+            boolean isAssignable = requestClass != null && requestClass.isAssignableFrom(body.getClass());
+            try {
+                writeBody(body, outMessage,
+                          requestClass == null || !isAssignable ? body.getClass() : requestClass,
+                          requestType == null || !isAssignable ? body.getClass() : requestType,
+                          anns, os);
+            } catch (Exception ex) {
+                throw new Fault(ex);
+            }
+        }
+    }
+
+    static void copyProperties(Client toClient, Client fromClient) {
+        AbstractClient newClient = toAbstractClient(toClient);
+        AbstractClient oldClient = toAbstractClient(fromClient);
+        newClient.setConfiguration(oldClient.getConfiguration());
+    }
+
+    private static AbstractClient toAbstractClient(Object client) {
+
+        if (client instanceof AbstractClient) {
+            return (AbstractClient)client;
+        } else if (client instanceof InvocationHandlerAware) {
+            return (AbstractClient)((InvocationHandlerAware)client).getInvocationHandler();
+        } else {
+            Object realObject = ClassHelper.getRealObject(client);
+            if (realObject instanceof AbstractClient) {
+                return (AbstractClient)realObject;
+            }
+        }
+        return null;
+    }
+
+    static JAXRSClientFactoryBean getBean(String baseAddress, String configLocation) {
+        JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
+
+        if (configLocation != null) {
+            SpringBusFactory bf = new SpringBusFactory();
+            Bus bus = bf.createBus(configLocation);
+            bean.setBus(bus);
+        }
+        bean.setAddress(baseAddress);
+        return bean;
+    }
+
+    static ClientState getClientState(Client client) {
+        AbstractClient newClient = toAbstractClient(client);
+        if (newClient == null) {
+            return null;
+        }
+        return newClient.getState();
+    }
+
+    static URI convertStringToURI(String baseAddress) {
+        try {
+            return URI.create(baseAddress);
+        } catch (RuntimeException ex) {
+            // no need to check "https" scheme or indeed ':'
+            // as the relative address will not work as the base address
+            if (baseAddress.startsWith(HTTP_SCHEME)) {
+                return new UriBuilderImpl().uriAsTemplate(baseAddress).build();
+            }
+            throw ex;
+        }
+    }
+
+    // Link to JAX-RS 2.0 AsyncInvoker
+    public AsyncInvoker async() {
+        return new AsyncInvokerImpl(this);
+    }
+
+    // Link to JAX-RS 2.0 SyncInvoker
+    public SyncInvoker sync() {
+        return new SyncInvokerImpl(this);
+    }
+
+    // Link to JAX-RS 2.1 CompletionStageRxInvoker
+    public CompletionStageRxInvoker rx() {
+        return rx(lookUpExecutorService());
+    }
+
+    public CompletionStageRxInvoker rx(ExecutorService ex) {
+        return new CompletionStageRxInvokerImpl(this, ex);
+    }
+    // Link to JAX-RS 2.1 RxInvoker extensions
+    @SuppressWarnings("rawtypes")
+    public <T extends RxInvoker> T rx(Class<T> rxCls) {
+        return rx(rxCls, (ExecutorService)null);
+    }
+
+    @SuppressWarnings({
+        "rawtypes", "unchecked"
+    })
+    public <T extends RxInvoker> T rx(Class<T> rxCls, ExecutorService executorService) {
+        if (CompletionStageRxInvoker.class.isAssignableFrom(rxCls)) {
+            return (T)rx(executorService);
+        }
+        ClientProviderFactory pf =
+            ClientProviderFactory.getInstance(WebClient.getConfig(this).getEndpoint());
+        RxInvokerProvider rxProvider = pf.getRxInvokerProvider();
+        if (rxProvider != null && rxProvider.isProviderFor(rxCls)) {
+            return (T)rxProvider.getRxInvoker(sync(), executorService);
+        }
+        throw new IllegalStateException("Provider for " + rxCls.getName() + " is not available");
+    }
+
+    private void setEntityHeaders(Entity<?> entity) {
+        type(entity.getMediaType());
+        if (entity.getLanguage() != null) {
+            language(entity.getLanguage().toString());
+        }
+        if (entity.getEncoding() != null) {
+            encoding(entity.getEncoding());
+        }
+    }
+
+    private ExecutorService lookUpExecutorService() {
+        try {
+            javax.naming.InitialContext ic = new javax.naming.InitialContext();
+            Object execService = ic.lookup("java:comp/DefaultManagedExecutorService");
+            if (execService != null) {
+                return (ExecutorService)execService;
+            }
+        } catch (Throwable ex) {
+            // ignore
+        }
+        return null;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/client/spec/ClientRequestContextImpl.java b/transform/src/patch/java/org/apache/cxf/jaxrs/client/spec/ClientRequestContextImpl.java
new file mode 100644
index 0000000..cddf5c6
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/client/spec/ClientRequestContextImpl.java
@@ -0,0 +1,186 @@
+/**
+ * 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.client.spec;
+
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.core.Configuration;
+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 org.apache.cxf.jaxrs.client.ClientProviderFactory;
+import org.apache.cxf.jaxrs.impl.AbstractRequestContextImpl;
+import org.apache.cxf.jaxrs.utils.HttpUtils;
+import org.apache.cxf.jaxrs.utils.InjectionUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageContentsList;
+
+
+public class ClientRequestContextImpl extends AbstractRequestContextImpl
+    implements ClientRequestContext {
+
+    public ClientRequestContextImpl(Message m,
+                                    boolean responseContext) {
+        super(m, responseContext);
+    }
+
+    @Override
+    public MediaType getMediaType() {
+        if (!hasEntity()) {
+            return null;
+        }
+        Object mt = HttpUtils.getModifiableHeaders(m).getFirst(HttpHeaders.CONTENT_TYPE);
+        return mt instanceof MediaType ? (MediaType)mt : JAXRSUtils.toMediaType(mt.toString());
+    }
+
+    @Override
+    public Client getClient() {
+        return (Client)m.getContextualProperty(Client.class.getName());
+    }
+
+    @Override
+    public Configuration getConfiguration() {
+        ClientProviderFactory cpf = ClientProviderFactory.getInstance(m);
+        return cpf.getConfiguration(m);
+    }
+
+    private Object getMessageContent() {
+        MessageContentsList objs = MessageContentsList.getContentsList(m);
+        if (objs == null || objs.isEmpty()) {
+            return null;
+        }
+        return objs.get(0);
+    }
+
+    @Override
+    public Object getEntity() {
+        return getMessageContent();
+    }
+
+    @Override
+    public Annotation[] getEntityAnnotations() {
+        Annotation[] anns = (Annotation[])m.get(Annotation.class.getName());
+        return anns == null ? new Annotation[] {} : anns;
+    }
+
+    @Override
+    public Class<?> getEntityClass() {
+        Object entity = getEntity();
+        return entity == null ? null : entity.getClass();
+    }
+
+    @Override
+    public Type getEntityType() {
+        Type t = m.get(Type.class);
+        return t != null ? t : getEntityClass();
+    }
+
+    @Override
+    public OutputStream getEntityStream() {
+        return m.getContent(OutputStream.class);
+    }
+
+    @Override
+    public boolean hasEntity() {
+        return getEntity() != null;
+    }
+
+    @Override
+    public void setEntity(Object entity, Annotation[] anns, MediaType mt) {
+        if (mt != null) {
+            MultivaluedMap<String, Object> headers = getHeaders();
+            headers.putSingle(HttpHeaders.CONTENT_TYPE, mt);
+            m.put(Message.CONTENT_TYPE, mt.toString());
+        }
+        if (anns != null) {
+            m.put(Annotation.class.getName(), anns);
+        }
+        doSetEntity(entity);
+    }
+
+    @Override
+    public void setEntity(Object entity) {
+        doSetEntity(entity);
+    }
+
+    private void doSetEntity(Object entity) {
+        Object actualEntity = InjectionUtils.getEntity(entity);
+        m.setContent(List.class, actualEntity == null ? new MessageContentsList()
+            : new MessageContentsList(actualEntity));
+        if (entity != null) {
+            final Type type;
+            if (GenericEntity.class.isAssignableFrom(entity.getClass())) {
+                type = ((GenericEntity<?>)entity).getType();
+            } else {
+                type = entity.getClass();
+            }
+            m.put(Type.class, type);
+            m.remove("org.apache.cxf.empty.request");
+        }
+
+    }
+
+    @Override
+    public URI getUri() {
+        String requestURI = (String)m.get(Message.REQUEST_URI);
+        if (requestURI  == null) {
+            return null;
+        }
+        if (requestURI.startsWith("/")) {
+            String endpointAddress = (String)m.get(Message.ENDPOINT_ADDRESS);
+            requestURI = requestURI.length() == 1 ? endpointAddress : endpointAddress + requestURI;
+        }
+        return URI.create(requestURI);
+    }
+
+    @Override
+    public void setEntityStream(OutputStream os) {
+        m.setContent(OutputStream.class, os);
+
+    }
+
+    @Override
+    public void setUri(URI requestURI) {
+        m.put(Message.ENDPOINT_ADDRESS, requestURI.toString());
+        m.put(Message.REQUEST_URI, requestURI.toString());
+
+    }
+
+    @Override
+    public MultivaluedMap<String, Object> getHeaders() {
+        h = null;
+        return HttpUtils.getModifiableHeaders(m);
+    }
+
+    @Override
+    public MultivaluedMap<String, String> getStringHeaders() {
+        h = null;
+        return HttpUtils.getModifiableStringHeaders(m);
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/client/spring/JAXRSClientFactoryBeanDefinitionParser.java b/transform/src/patch/java/org/apache/cxf/jaxrs/client/spring/JAXRSClientFactoryBeanDefinitionParser.java
new file mode 100644
index 0000000..6543ddc
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/client/spring/JAXRSClientFactoryBeanDefinitionParser.java
@@ -0,0 +1,187 @@
+/**
+ * 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.client.spring;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.Path;
+import javax.ws.rs.ext.Provider;
+import javax.xml.namespace.QName;
+
+import org.w3c.dom.Element;
+
+import org.apache.cxf.bus.spring.BusWiringBeanFactoryPostProcessor;
+import org.apache.cxf.common.util.ClasspathScanner;
+import org.apache.cxf.configuration.spring.AbstractFactoryBeanDefinitionParser;
+import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
+import org.apache.cxf.jaxrs.model.UserResource;
+import org.apache.cxf.jaxrs.utils.ResourceUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+
+
+
+
+public class JAXRSClientFactoryBeanDefinitionParser extends AbstractFactoryBeanDefinitionParser {
+
+    public JAXRSClientFactoryBeanDefinitionParser() {
+        super();
+        setBeanClass(Object.class);
+    }
+
+    @Override
+    protected Class<?> getFactoryClass() {
+        return JAXRSSpringClientFactoryBean.class;
+    }
+
+    @Override
+    protected String getFactoryIdSuffix() {
+        return ".proxyFactory";
+    }
+
+    @Override
+    protected String getSuffix() {
+        return ".jaxrs-client";
+    }
+
+    @Override
+    protected void mapAttribute(BeanDefinitionBuilder bean, Element e, String name, String val) {
+        if ("serviceName".equals(name)) {
+            QName q = parseQName(e, val);
+            bean.addPropertyValue(name, q);
+        } else if ("basePackages".equals(name)) {
+            bean.addPropertyValue("basePackages", ClasspathScanner.parsePackages(val));
+        } else {
+            mapToProperty(bean, name, val);
+        }
+    }
+
+    @Override
+    protected void mapElement(ParserContext ctx, BeanDefinitionBuilder bean, Element el, String name) {
+        if ("properties".equals(name) || "headers".equals(name)) {
+            Map<?, ?> map = ctx.getDelegate().parseMapElement(el, bean.getBeanDefinition());
+            bean.addPropertyValue(name, map);
+        } else if ("executor".equals(name)) {
+            setFirstChildAsProperty(el, ctx, bean, "serviceFactory.executor");
+        } else if ("binding".equals(name)) {
+            setFirstChildAsProperty(el, ctx, bean, "bindingConfig");
+        } else if ("inInterceptors".equals(name) || "inFaultInterceptors".equals(name)
+            || "outInterceptors".equals(name) || "outFaultInterceptors".equals(name)) {
+            List<?> list = ctx.getDelegate().parseListElement(el, bean.getBeanDefinition());
+            bean.addPropertyValue(name, list);
+        } else if ("features".equals(name) || "providers".equals(name)
+                   || "schemaLocations".equals(name) || "modelBeans".equals(name)) {
+            List<?> list = ctx.getDelegate().parseListElement(el, bean.getBeanDefinition());
+            bean.addPropertyValue(name, list);
+        } else if ("model".equals(name)) {
+            List<UserResource> resources = ResourceUtils.getResourcesFromElement(el);
+            bean.addPropertyValue("modelBeans", resources);
+        } else {
+            setFirstChildAsProperty(el, ctx, bean, name);
+        }
+    }
+
+    public static class JAXRSSpringClientFactoryBean extends JAXRSClientFactoryBean
+        implements ApplicationContextAware {
+
+        private List<String> basePackages;
+
+        public JAXRSSpringClientFactoryBean() {
+            super();
+        }
+
+        public void setBasePackages(List<String> basePackages) {
+            this.basePackages = basePackages;
+        }
+
+        public void setApplicationContext(ApplicationContext ctx) throws BeansException {
+            try {
+                if (basePackages != null) {
+                    final Map< Class< ? extends Annotation >, Collection< Class< ? > > > classes =
+                        ClasspathScanner.findClasses(basePackages, Path.class, Provider.class);
+
+                    if (classes.get(Path.class).size() > 1) {
+                        throw new NoUniqueBeanDefinitionException(Path.class, classes.get(Path.class).size(),
+                            "More than one service class (@Path) has been discovered");
+                    }
+                    AutowireCapableBeanFactory beanFactory = ctx.getAutowireCapableBeanFactory();
+                    for (final Class< ? > providerClass: classes.get(Provider.class)) {
+                        Object bean;
+                        try {
+                            bean = beanFactory.createBean(providerClass,
+                                                   AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
+                        } catch (Exception ex) {
+                            bean = beanFactory.createBean(providerClass);
+                        }
+                        setProvider(bean);
+                    }
+
+                    for (final Class< ? > serviceClass: classes.get(Path.class)) {
+                        setServiceClass(serviceClass);
+                    }
+                }
+            } catch (IOException ex) {
+                throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
+            } catch (ClassNotFoundException ex) {
+                throw new BeanCreationException("Failed to create bean from classfile", ex);
+            }
+
+            if (bus == null) {
+                setBus(BusWiringBeanFactoryPostProcessor.addDefaultBus(ctx));
+            }
+        }
+    }
+
+    static Class<?> getServiceClass(Collection<Class<?>> rootClasses) {
+        for (Class<?> cls : rootClasses) {
+            if (cls.isInterface()) {
+                return cls;
+            }
+        }
+        return rootClasses.iterator().next();
+    }
+    static List<Object> getProviders(ApplicationContext context, Collection<Class<?>> providerClasses) {
+        List<Object> providers = new LinkedList<>();
+        AutowireCapableBeanFactory beanFactory = context.getAutowireCapableBeanFactory();
+        for (final Class< ? > providerClass: providerClasses) {
+            Object bean;
+            try {
+                bean = beanFactory.createBean(providerClass,
+                                       AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
+            } catch (Exception ex) {
+                bean = beanFactory.createBean(providerClass);
+            }
+            providers.add(bean);
+        }
+        return providers;
+    }
+}

[tomee-jakarta] 05/06: cxf-rt-databinding-jaxb changes since 3.4.3

Posted by db...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dblevins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomee-jakarta.git

commit cafba39b3194571baba9e0c00632c73b1cff3ac8
Author: David Blevins <da...@gmail.com>
AuthorDate: Sun May 2 21:57:24 2021 -0700

    cxf-rt-databinding-jaxb changes since 3.4.3
---
 .../org/apache/cxf/jaxb/FactoryClassGenerator.java |   86 ++
 .../java/org/apache/cxf/jaxb/JAXBDataBase.java     |  192 ++++
 .../java/org/apache/cxf/jaxb/JAXBDataBinding.java  |  873 +++++++++++++++
 .../org/apache/cxf/jaxb/JAXBEncoderDecoder.java    | 1119 ++++++++++++++++++++
 .../org/apache/cxf/jaxb/JAXBSchemaInitializer.java |  823 ++++++++++++++
 .../org/apache/cxf/jaxb/io/DataReaderImpl.java     |  207 ++++
 .../org/apache/cxf/jaxb/io/DataWriterImpl.java     |  321 ++++++
 7 files changed, 3621 insertions(+)

diff --git a/transform/src/patch/java/org/apache/cxf/jaxb/FactoryClassGenerator.java b/transform/src/patch/java/org/apache/cxf/jaxb/FactoryClassGenerator.java
new file mode 100644
index 0000000..fe4c08b
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxb/FactoryClassGenerator.java
@@ -0,0 +1,86 @@
+/**
+ * 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.jaxb;
+
+import java.lang.reflect.Constructor;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.spi.ClassGeneratorClassLoader;
+import org.apache.cxf.common.util.ASMHelper;
+import org.apache.cxf.common.util.OpcodesProxy;
+import org.apache.cxf.common.util.ReflectionUtil;
+import org.apache.cxf.common.util.StringUtils;
+
+
+public class FactoryClassGenerator extends ClassGeneratorClassLoader implements FactoryClassCreator {
+    private final ASMHelper helper;
+    FactoryClassGenerator(Bus bus) {
+        super(bus);
+        helper = bus.getExtension(ASMHelper.class);
+    }
+    @SuppressWarnings("unused")
+    public Class<?> createFactory(Class<?> cls) {
+        String newClassName = cls.getName() + "Factory";
+        Class<?> factoryClass = findClass(newClassName, cls);
+        if (factoryClass != null) {
+            return factoryClass;
+        }
+        Constructor<?> contructor = ReflectionUtil.getDeclaredConstructors(cls)[0];
+        OpcodesProxy opcodes = helper.getOpCodes();
+        ASMHelper.ClassWriter cw = helper.createClassWriter();
+        ASMHelper.MethodVisitor mv;
+
+        cw.visit(opcodes.V1_6, opcodes.ACC_PUBLIC + opcodes.ACC_SUPER,
+                StringUtils.periodToSlashes(newClassName), null, "java/lang/Object", null);
+
+        cw.visitSource(cls.getSimpleName() + "Factory" + ".java", null);
+
+        mv = cw.visitMethod(opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
+        mv.visitCode();
+        mv.visitVarInsn(opcodes.ALOAD, 0);
+        mv.visitMethodInsn(opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+        mv.visitInsn(opcodes.RETURN);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+        mv = cw.visitMethod(opcodes.ACC_PUBLIC, "create" + cls.getSimpleName(),
+                "()L" + StringUtils.periodToSlashes(cls.getName()) + ";", null, null);
+        mv.visitCode();
+        String name = cls.getName().replace('.', '/');
+        mv.visitTypeInsn(opcodes.NEW, name);
+        mv.visitInsn(opcodes.DUP);
+        StringBuilder paraString = new StringBuilder(32).append('(');
+
+        for (Class<?> paraClass : contructor.getParameterTypes()) {
+            mv.visitInsn(opcodes.ACONST_NULL);
+            paraString.append("Ljava/lang/Object;");
+        }
+        paraString.append(")V");
+
+        mv.visitMethodInsn(opcodes.INVOKESPECIAL, name, "<init>", paraString.toString(), false);
+
+        mv.visitInsn(opcodes.ARETURN);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+        cw.visitEnd();
+        return loadClass(newClassName, cls, cw.toByteArray());
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxb/JAXBDataBase.java b/transform/src/patch/java/org/apache/cxf/jaxb/JAXBDataBase.java
new file mode 100644
index 0000000..55ac0cf
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxb/JAXBDataBase.java
@@ -0,0 +1,192 @@
+/**
+ * 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.jaxb;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.ValidationEventHandler;
+import javax.xml.bind.annotation.XmlAttachmentRef;
+import javax.xml.bind.annotation.XmlList;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import javax.xml.bind.attachment.AttachmentMarshaller;
+import javax.xml.bind.attachment.AttachmentUnmarshaller;
+import javax.xml.validation.Schema;
+
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.jaxb.attachment.JAXBAttachmentMarshaller;
+import org.apache.cxf.jaxb.attachment.JAXBAttachmentUnmarshaller;
+import org.apache.cxf.message.Attachment;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.service.model.AbstractMessageContainer;
+import org.apache.cxf.service.model.MessageInfo;
+import org.apache.cxf.service.model.MessagePartInfo;
+import org.apache.cxf.service.model.OperationInfo;
+
+/**
+ *
+ */
+public abstract class JAXBDataBase {
+    static final Logger LOG = LogUtils.getL7dLogger(JAXBDataBase.class);
+
+    protected JAXBContext context;
+    protected Schema schema;
+    protected Collection<Attachment> attachments;
+    protected Integer mtomThreshold; // null if we should default.
+
+    protected JAXBDataBase(JAXBContext ctx) {
+        context = ctx;
+    }
+
+    public void setSchema(Schema s) {
+        this.schema = s;
+    }
+
+    public void setJAXBContext(JAXBContext jc) {
+        this.context = jc;
+    }
+
+    public Schema getSchema() {
+        return schema;
+    }
+    public JAXBContext getJAXBContext() {
+        return context;
+    }
+
+    public Collection<Attachment> getAttachments() {
+        return attachments;
+    }
+
+    public void setAttachments(Collection<Attachment> attachments) {
+        this.attachments = attachments;
+    }
+
+    protected AttachmentUnmarshaller getAttachmentUnmarshaller() {
+        return new JAXBAttachmentUnmarshaller(attachments);
+    }
+
+    protected AttachmentMarshaller getAttachmentMarshaller() {
+        return new JAXBAttachmentMarshaller(attachments, mtomThreshold);
+    }
+
+    public void setProperty(String prop, Object value) {
+    }
+
+    protected Annotation[] getJAXBAnnotation(MessagePartInfo mpi) {
+        List<Annotation> annoList = null;
+        if (mpi != null) {
+            annoList = extractJAXBAnnotations((Annotation[])mpi.getProperty("parameter.annotations"));
+            if (annoList == null) {
+                annoList = extractJAXBAnnotations(getReturnMethodAnnotations(mpi));
+            }
+        }
+        return annoList == null ? new Annotation[0] : annoList.toArray(new Annotation[0]);
+    }
+
+    private List<Annotation> extractJAXBAnnotations(Annotation[] anns) {
+        List<Annotation> annoList = null;
+        if (anns != null) {
+            for (Annotation ann : anns) {
+                if (ann instanceof XmlList || ann instanceof XmlAttachmentRef
+                    || ann instanceof XmlJavaTypeAdapter) {
+                    if (annoList == null) {
+                        annoList = new ArrayList<>();
+                    }
+                    annoList.add(ann);
+                }
+            }
+        }
+        return annoList;
+    }
+
+    private Annotation[] getReturnMethodAnnotations(MessagePartInfo mpi) {
+        AbstractMessageContainer mi = mpi.getMessageInfo();
+        if (mi == null || !isOutputMessage(mi)) {
+            return null;
+        }
+        OperationInfo oi = mi.getOperation();
+        return oi != null ? (Annotation[])oi.getProperty("method.return.annotations") : null;
+    }
+
+    protected boolean isOutputMessage(AbstractMessageContainer messageContainer) {
+        if (messageContainer instanceof MessageInfo) {
+            return MessageInfo.Type.OUTPUT.equals(((MessageInfo)messageContainer).getType());
+        }
+        return false;
+    }
+
+    public Integer getMtomThreshold() {
+        return mtomThreshold;
+    }
+
+    public void setMtomThreshold(Integer threshold) {
+        this.mtomThreshold = threshold;
+    }
+
+    protected final boolean honorJAXBAnnotations(MessagePartInfo part) {
+        if (part == null) {
+            return false;
+        }
+        if (!part.isElement()) {
+            //RPC-Lit always needs to look for these
+            return true;
+        }
+        //certain cases that use XmlJavaTypeAdapters will require this and the
+        //JAXBSchemaInitializer will set this.
+        Boolean b = (Boolean)part.getProperty("honor.jaxb.annotations");
+        return b != null && b;
+    }
+
+    protected ValidationEventHandler getValidationEventHandler(String cn) {
+        try {
+            return (ValidationEventHandler)ClassLoaderUtils.loadClass(cn, getClass()).newInstance();
+        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
+            LOG.log(Level.INFO, "Could not create validation event handler", e);
+        }
+        return null;
+    }
+
+    protected ValidationEventHandler getValidationEventHandler(Message m, String property) {
+        Object value = m.getContextualProperty(property);
+        ValidationEventHandler veventHandler;
+        if (value instanceof String) {
+            veventHandler = getValidationEventHandler((String)value);
+        } else {
+            veventHandler = (ValidationEventHandler)value;
+        }
+        if (veventHandler == null) {
+            value = m.getContextualProperty(JAXBDataBinding.VALIDATION_EVENT_HANDLER);
+            if (value instanceof String) {
+                veventHandler = getValidationEventHandler((String)value);
+            } else {
+                veventHandler = (ValidationEventHandler)value;
+            }
+        }
+        return veventHandler;
+    }
+
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxb/JAXBDataBinding.java b/transform/src/patch/java/org/apache/cxf/jaxb/JAXBDataBinding.java
new file mode 100644
index 0000000..a66e9bf
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxb/JAXBDataBinding.java
@@ -0,0 +1,873 @@
+/**
+ * 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.jaxb;
+
+import java.io.IOException;
+import java.io.OutputStream;
+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.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.ValidationEventHandler;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementRef;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLEventWriter;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import org.xml.sax.InputSource;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.injection.NoJSR250Annotations;
+import org.apache.cxf.common.jaxb.JAXBBeanInfo;
+import org.apache.cxf.common.jaxb.JAXBContextCache;
+import org.apache.cxf.common.jaxb.JAXBContextCache.CachedContextAndSchemas;
+import org.apache.cxf.common.jaxb.JAXBContextProxy;
+import org.apache.cxf.common.jaxb.JAXBUtils;
+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.xmlschema.SchemaCollection;
+import org.apache.cxf.databinding.AbstractInterceptorProvidingDataBinding;
+import org.apache.cxf.databinding.AbstractWrapperHelper;
+import org.apache.cxf.databinding.DataReader;
+import org.apache.cxf.databinding.DataWriter;
+import org.apache.cxf.databinding.WrapperCapableDatabinding;
+import org.apache.cxf.databinding.WrapperHelper;
+import org.apache.cxf.interceptor.InterceptorProvider;
+import org.apache.cxf.jaxb.attachment.JAXBAttachmentSchemaValidationHack;
+import org.apache.cxf.jaxb.io.DataReaderImpl;
+import org.apache.cxf.jaxb.io.DataWriterImpl;
+import org.apache.cxf.resource.URIResolver;
+import org.apache.cxf.service.Service;
+import org.apache.cxf.service.factory.ServiceConstructionException;
+import org.apache.cxf.service.model.MessageInfo;
+import org.apache.cxf.service.model.MessagePartInfo;
+import org.apache.cxf.service.model.ServiceInfo;
+import org.apache.cxf.staxutils.StaxUtils;
+import org.apache.cxf.ws.addressing.ObjectFactory;
+
+@NoJSR250Annotations
+public class JAXBDataBinding extends AbstractInterceptorProvidingDataBinding
+    implements WrapperCapableDatabinding, InterceptorProvider {
+
+    public static final String READER_VALIDATION_EVENT_HANDLER = "jaxb-reader-validation-event-handler";
+    public static final String VALIDATION_EVENT_HANDLER = "jaxb-validation-event-handler";
+    public static final String SET_VALIDATION_EVENT_HANDLER = "set-jaxb-validation-event-handler";
+    public static final String WRITER_VALIDATION_EVENT_HANDLER = "jaxb-writer-validation-event-handler";
+
+    public static final String SCHEMA_RESOURCE = "SCHEMRESOURCE";
+    public static final String MTOM_THRESHOLD = "org.apache.cxf.jaxb.mtomThreshold";
+
+    public static final String UNWRAP_JAXB_ELEMENT = "unwrap.jaxb.element";
+
+    public static final String USE_JAXB_BRIDGE = "use.jaxb.bridge";
+
+    public static final String JAXB_SCAN_PACKAGES = "jaxb.scanPackages";
+
+    private static final Logger LOG = LogUtils.getLogger(JAXBDataBinding.class);
+
+    private static final Class<?>[] SUPPORTED_READER_FORMATS = new Class<?>[] {Node.class,
+                                                                               XMLEventReader.class,
+                                                                               XMLStreamReader.class};
+    private static final Class<?>[] SUPPORTED_WRITER_FORMATS = new Class<?>[] {OutputStream.class,
+                                                                               Node.class,
+                                                                               XMLEventWriter.class,
+                                                                               XMLStreamWriter.class};
+
+    private static class DelayedDOMResult extends DOMResult {
+        private final URL resource;
+        private final String publicId;
+        DelayedDOMResult(URL url, String sysId, String pId) {
+            super(null, sysId);
+            resource = url;
+            publicId = pId;
+        }
+        public synchronized Node getNode() {
+            Node nd = super.getNode();
+            if (nd == null) {
+                try {
+                    InputSource src = new InputSource(resource.openStream());
+                    src.setSystemId(this.getSystemId());
+                    src.setPublicId(publicId);
+                    Document doc = StaxUtils.read(src);
+                    setNode(doc);
+                    nd = super.getNode();
+                } catch (Exception ex) {
+                    throw new RuntimeException(ex);
+                }
+            }
+            return nd;
+        }
+    }
+    private static final Map<String, DOMResult> BUILT_IN_SCHEMAS = new HashMap<>();
+    static {
+        try (URIResolver resolver = new URIResolver()) {
+            try {
+                resolver.resolve("", "classpath:/schemas/wsdl/ws-addr-wsdl.xsd", JAXBDataBinding.class);
+                if (resolver.isResolved()) {
+                    resolver.getInputStream().close();
+                    DOMResult dr = new DelayedDOMResult(resolver.getURL(),
+                                                        "classpath:/schemas/wsdl/ws-addr-wsdl.xsd",
+                                                        "http://www.w3.org/2005/02/addressing/wsdl");
+                    BUILT_IN_SCHEMAS.put("http://www.w3.org/2005/02/addressing/wsdl", dr);
+                    resolver.unresolve();
+                }
+            } catch (Exception e) {
+                //IGNORE
+            }
+            try {
+                resolver.resolve("", "classpath:/schemas/wsdl/ws-addr.xsd", JAXBDataBinding.class);
+                if (resolver.isResolved()) {
+                    resolver.getInputStream().close();
+                    DOMResult dr = new DelayedDOMResult(resolver.getURL(),
+                                                        "classpath:/schemas/wsdl/ws-addr.xsd",
+                                                        "http://www.w3.org/2005/08/addressing");
+                    BUILT_IN_SCHEMAS.put("http://www.w3.org/2005/08/addressing", dr);
+                    resolver.unresolve();
+                }
+            } catch (Exception e) {
+                //IGNORE
+            }
+            try {
+                resolver.resolve("", "classpath:/schemas/wsdl/wsrm.xsd", JAXBDataBinding.class);
+                if (resolver.isResolved()) {
+                    resolver.getInputStream().close();
+                    DOMResult dr = new DelayedDOMResult(resolver.getURL(),
+                                                        "classpath:/schemas/wsdl/wsrm.xsd",
+                                                        "http://schemas.xmlsoap.org/ws/2005/02/rm");
+                    BUILT_IN_SCHEMAS.put("http://schemas.xmlsoap.org/ws/2005/02/rm", dr);
+                    resolver.unresolve();
+                }
+            } catch (Exception e) {
+                //IGNORE
+            }
+        } catch (Exception e) {
+            //IGNORE
+        }
+    }
+
+    Class<?>[] extraClass;
+
+    JAXBContext context;
+    Set<Class<?>> contextClasses;
+    Collection<Object> typeRefs = new ArrayList<>();
+
+    Class<?> cls;
+
+    private Map<String, Object> contextProperties = new HashMap<>();
+    private List<XmlAdapter<?, ?>> adapters = new ArrayList<>();
+    private Map<String, Object> marshallerProperties = new HashMap<>();
+    private Map<String, Object> unmarshallerProperties = new HashMap<>();
+    private Unmarshaller.Listener unmarshallerListener;
+    private Marshaller.Listener marshallerListener;
+    private ValidationEventHandler validationEventHandler;
+    private Object escapeHandler;
+    private Object noEscapeHandler;
+
+    private boolean unwrapJAXBElement = true;
+    private boolean scanPackages = true;
+    private boolean qualifiedSchemas;
+
+    public JAXBDataBinding() {
+    }
+
+    public JAXBDataBinding(boolean q) {
+        this.qualifiedSchemas = q;
+    }
+
+    public JAXBDataBinding(Class<?>... classes) throws JAXBException {
+        contextClasses = new LinkedHashSet<>(Arrays.asList(classes));
+        setContext(createJAXBContext(contextClasses)); //NOPMD - specifically allow this
+    }
+    public JAXBDataBinding(boolean qualified, Map<String, Object> props) throws JAXBException {
+        this(qualified);
+        if (props != null && props.get("jaxb.additionalContextClasses") != null) {
+            Object o = props.get("jaxb.additionalContextClasses");
+            if (o instanceof Class) {
+                o = new Class[] {(Class<?>)o};
+            }
+            extraClass = (Class[])o;
+        }
+
+        // the default for scan packages is true, so the jaxb scan packages
+        // property must be explicitly set to false to disable it
+        if (PropertyUtils.isFalse(props, JAXB_SCAN_PACKAGES)) {
+            scanPackages = false;
+        }
+    }
+
+    public JAXBDataBinding(JAXBContext context) {
+        this();
+        setContext(context);
+    }
+
+    protected boolean getQualifiedSchemas() {
+        return qualifiedSchemas;
+    }
+
+    public JAXBContext getContext() {
+        return context;
+    }
+
+    public final void setContext(JAXBContext ctx) {
+        context = ctx;
+        //create default MininumEscapeHandler
+        escapeHandler = JAXBUtils.createMininumEscapeHandler(ctx.getClass());
+        noEscapeHandler = JAXBUtils.createNoEscapeHandler(ctx.getClass());
+    }
+
+    public Object getEscapeHandler() {
+        return escapeHandler;
+    }
+    
+    public void setEscapeHandler(Object handler) {
+        escapeHandler = handler;
+    }
+    
+    public void applyEscapeHandler(boolean escape, Consumer<Object> consumer) {
+        if (escape) {
+            consumer.accept(escapeHandler);
+        } else if (noEscapeHandler != null) {
+            consumer.accept(noEscapeHandler);
+        }
+    }
+    
+    
+    @SuppressWarnings("unchecked")
+    public <T> DataWriter<T> createWriter(Class<T> c) {
+
+        Integer mtomThresholdInt = Integer.valueOf(getMtomThreshold());
+        if (c == XMLStreamWriter.class) {
+            DataWriterImpl<XMLStreamWriter> r
+                = new DataWriterImpl<>(getBus(), this, true);
+            r.setMtomThreshold(mtomThresholdInt);
+            return (DataWriter<T>)r;
+        } else if (c == OutputStream.class) {
+            DataWriterImpl<OutputStream> r = new DataWriterImpl<>(getBus(), this, false);
+            r.setMtomThreshold(mtomThresholdInt);
+            return (DataWriter<T>)r;
+        } else if (c == XMLEventWriter.class) {
+            DataWriterImpl<XMLEventWriter> r = new DataWriterImpl<>(getBus(), this, true);
+            r.setMtomThreshold(mtomThresholdInt);
+            return (DataWriter<T>)r;
+        } else if (c == Node.class) {
+            DataWriterImpl<Node> r = new DataWriterImpl<>(getBus(), this, false);
+            r.setMtomThreshold(mtomThresholdInt);
+            return (DataWriter<T>)r;
+        }
+        return null;
+    }
+
+    public Class<?>[] getSupportedWriterFormats() {
+        return SUPPORTED_WRITER_FORMATS;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> DataReader<T> createReader(Class<T> c) {
+        DataReader<T> dr = null;
+        if (c == XMLStreamReader.class) {
+            dr = (DataReader<T>)new DataReaderImpl<XMLStreamReader>(this, unwrapJAXBElement);
+        } else if (c == XMLEventReader.class) {
+            dr = (DataReader<T>)new DataReaderImpl<XMLEventReader>(this, unwrapJAXBElement);
+        } else if (c == Node.class) {
+            dr = (DataReader<T>)new DataReaderImpl<Node>(this, unwrapJAXBElement);
+        }
+
+        return dr;
+    }
+
+    public Class<?>[] getSupportedReaderFormats() {
+        return SUPPORTED_READER_FORMATS;
+    }
+
+    @SuppressWarnings("unchecked")
+    public synchronized void initialize(Service service) {
+
+        inInterceptors.addIfAbsent(JAXBAttachmentSchemaValidationHack.INSTANCE);
+        inFaultInterceptors.addIfAbsent(JAXBAttachmentSchemaValidationHack.INSTANCE);
+
+        // context is already set, don't redo it
+        if (context != null) {
+            return;
+        }
+
+        contextClasses = new LinkedHashSet<>();
+
+        for (ServiceInfo serviceInfo : service.getServiceInfos()) {
+            JAXBContextInitializer initializer = new JAXBContextInitializer(getBus(), serviceInfo, contextClasses,
+                    typeRefs, this.getUnmarshallerProperties());
+            initializer.walk();
+            if (serviceInfo.getProperty("extra.class") != null) {
+                Set<Class<?>> exClasses = serviceInfo.getProperty("extra.class", Set.class);
+                contextClasses.addAll(exClasses);
+            }
+
+        }
+
+        String tns = getNamespaceToUse(service);
+        final CachedContextAndSchemas cachedContextAndSchemas;
+        try {
+            cachedContextAndSchemas = createJAXBContextAndSchemas(contextClasses, tns);
+        } catch (JAXBException e1) {
+            throw new ServiceConstructionException(e1);
+        }
+        final JAXBContext ctx = cachedContextAndSchemas.getContext();
+        if (LOG.isLoggable(Level.FINE)) {
+            LOG.log(Level.FINE, "CREATED_JAXB_CONTEXT", new Object[] {ctx, contextClasses});
+        }
+        setContext(ctx);
+
+        for (ServiceInfo serviceInfo : service.getServiceInfos()) {
+            SchemaCollection col = serviceInfo.getXmlSchemaCollection();
+
+            if (col.getXmlSchemas().length > 1) {
+                // someone has already filled in the types
+                justCheckForJAXBAnnotations(serviceInfo);
+                continue;
+            }
+
+            boolean schemasFromCache = false;
+            Collection<DOMSource> schemas = getSchemas();
+            if (schemas == null || schemas.isEmpty()) {
+                schemas = cachedContextAndSchemas.getSchemas();
+                if (schemas != null) {
+                    schemasFromCache = true;
+                }
+            } else {
+                schemasFromCache = true;
+            }
+            Set<DOMSource> bi = new LinkedHashSet<>();
+            if (schemas == null) {
+                schemas = new LinkedHashSet<>();
+                try {
+                    for (DOMResult r : generateJaxbSchemas()) {
+                        DOMSource src = new DOMSource(r.getNode(), r.getSystemId());
+                        if (isInBuiltInSchemas(r)) {
+                            bi.add(src);
+                        } else {
+                            schemas.add(src);
+                        }
+                    }
+                    //put any builtins at the end.   Anything that DOES import them
+                    //will cause it to load automatically and we'll skip them later
+                    schemas.addAll(bi);
+                } catch (IOException e) {
+                    throw new ServiceConstructionException("SCHEMA_GEN_EXC", LOG, e);
+                }
+            }
+            for (DOMSource r : schemas) {
+                if (bi.contains(r)) {
+                    String ns = ((Document)r.getNode()).getDocumentElement().getAttribute("targetNamespace");
+                    if (serviceInfo.getSchema(ns) != null) {
+                        continue;
+                    }
+                }
+                //StaxUtils.print(r.getNode());
+                //System.out.println();
+                addSchemaDocument(serviceInfo,
+                                  col,
+                                 (Document)r.getNode(),
+                                  r.getSystemId());
+            }
+
+            JAXBSchemaInitializer schemaInit = new JAXBSchemaInitializer(serviceInfo, col, context,
+                                                                         this.qualifiedSchemas, tns);
+            schemaInit.walk();
+            if (cachedContextAndSchemas != null && !schemasFromCache) {
+                cachedContextAndSchemas.setSchemas(schemas);
+            }
+        }
+    }
+
+    protected void justCheckForJAXBAnnotations(ServiceInfo serviceInfo) {
+        for (MessageInfo mi: serviceInfo.getMessages().values()) {
+            for (MessagePartInfo mpi : mi.getMessageParts()) {
+                checkForJAXBAnnotations(mpi, serviceInfo.getXmlSchemaCollection(), serviceInfo.getTargetNamespace());
+            }
+        }
+    }
+    private void checkForJAXBAnnotations(MessagePartInfo mpi, SchemaCollection schemaCollection, String ns) {
+        Annotation[] anns = (Annotation[])mpi.getProperty("parameter.annotations");
+        JAXBContextProxy ctx = JAXBUtils.createJAXBContextProxy(context, schemaCollection, ns);
+        XmlJavaTypeAdapter jta = JAXBSchemaInitializer.findFromTypeAdapter(ctx, mpi.getTypeClass(), anns);
+        if (jta != null) {
+            JAXBBeanInfo jtaBeanInfo = JAXBSchemaInitializer.findFromTypeAdapter(ctx, jta.value());
+            JAXBBeanInfo beanInfo = JAXBSchemaInitializer.getBeanInfo(ctx, mpi.getTypeClass());
+            if (jtaBeanInfo != beanInfo) {
+                mpi.setProperty("parameter.annotations", anns);
+                mpi.setProperty("honor.jaxb.annotations", Boolean.TRUE);
+            }
+        }
+    }
+
+    protected String getNamespaceToUse(Service service) {
+        if ("true".equals(service.get("org.apache.cxf.databinding.namespace"))) {
+            return null;
+        }
+        final String tns;
+        if (!service.getServiceInfos().isEmpty()) {
+            tns = service.getServiceInfos().get(0).getInterface().getName().getNamespaceURI();
+        } else {
+            tns = service.getName().getNamespaceURI();
+        }
+        return tns;
+    }
+
+    public void setExtraClass(Class<?>[] userExtraClass) {
+        extraClass = userExtraClass;
+    }
+
+    public Class<?>[] getExtraClass() {
+        return extraClass;
+    }
+
+    // default access for tests.
+    List<DOMResult> generateJaxbSchemas() throws IOException {
+        return JAXBUtils.generateJaxbSchemas(context, BUILT_IN_SCHEMAS);
+    }
+
+    public JAXBContext createJAXBContext(Set<Class<?>> classes) throws JAXBException {
+        return createJAXBContext(classes, null);
+    }
+
+    public JAXBContext createJAXBContext(Set<Class<?>> classes, String defaultNs) throws JAXBException {
+        return createJAXBContextAndSchemas(classes, defaultNs).getContext();
+    }
+
+    public CachedContextAndSchemas createJAXBContextAndSchemas(Set<Class<?>> classes,
+                                                               String defaultNs)
+        throws JAXBException {
+        //add user extra class into jaxb context
+        if (extraClass != null && extraClass.length > 0) {
+            for (Class<?> clz : extraClass) {
+                classes.add(clz);
+            }
+        }
+        if (scanPackages) {
+            JAXBContextCache.scanPackages(classes);
+        }
+        addWsAddressingTypes(classes);
+
+        return JAXBContextCache.getCachedContextAndSchemas(classes, defaultNs,
+                                                          contextProperties,
+                                                          typeRefs, true);
+    }
+
+
+    private void addWsAddressingTypes(Set<Class<?>> classes) {
+        if (classes.contains(ObjectFactory.class)) {
+            // ws-addressing is used, lets add the specific types
+            try {
+                classes.add(Class.forName("org.apache.cxf.ws.addressing.wsdl.ObjectFactory"));
+                classes.add(Class.forName("org.apache.cxf.ws.addressing.wsdl.AttributedQNameType"));
+                classes.add(Class.forName("org.apache.cxf.ws.addressing.wsdl.ServiceNameType"));
+            } catch (ClassNotFoundException unused) {
+                // REVISIT - ignorable if WS-ADDRESSING not available?
+                // maybe add a way to allow interceptors to add stuff to the
+                // context?
+            }
+        }
+    }
+
+    public Set<Class<?>> getContextClasses() {
+        return Collections.unmodifiableSet(this.contextClasses);
+    }
+
+    /**
+     * Return a map of properties. These properties are passed to
+     * JAXBContext.newInstance when this object creates a context.
+     *
+     * @return the map of JAXB context properties.
+     */
+    public Map<String, Object> getContextProperties() {
+        return contextProperties;
+    }
+
+    /**
+     * Set a map of JAXB context properties. These properties are passed to
+     * JAXBContext.newInstance when this object creates a context. Note that if
+     * you create a JAXB context elsewhere, you will not respect these
+     * properties unless you handle it manually.
+     *
+     * @param contextProperties map of properties.
+     */
+    public void setContextProperties(Map<String, Object> contextProperties) {
+        this.contextProperties = contextProperties;
+    }
+
+    public List<XmlAdapter<?, ?>> getConfiguredXmlAdapters() {
+        return adapters;
+    }
+
+    public void setConfiguredXmlAdapters(List<XmlAdapter<?, ?>> adpters) {
+        this.adapters = adpters;
+    }
+
+    /**
+     * Return a map of properties. These properties are set into the JAXB
+     * Marshaller (via Marshaller.setProperty(...) when the marshaller is
+     * created.
+     *
+     * @return the map of JAXB marshaller properties.
+     */
+    public Map<String, Object> getMarshallerProperties() {
+        return marshallerProperties;
+    }
+
+    /**
+     * Set a map of JAXB marshaller properties. These properties are set into
+     * the JAXB Marshaller (via Marshaller.setProperty(...) when the marshaller
+     * is created.
+     *
+     * @param marshallerProperties map of properties.
+     */
+    public void setMarshallerProperties(Map<String, Object> marshallerProperties) {
+        this.marshallerProperties = marshallerProperties;
+    }
+
+
+    /**
+     * Return a map of properties. These properties are set into the JAXB
+     * Unmarshaller (via Unmarshaller.setProperty(...) when the unmarshaller is
+     * created.
+     *
+     * @return the map of JAXB unmarshaller properties.
+     */
+    public Map<String, Object> getUnmarshallerProperties() {
+        return unmarshallerProperties;
+    }
+
+    /**
+     * Set a map of JAXB unmarshaller properties. These properties are set into
+     * the JAXB Unmarshaller (via Unmarshaller.setProperty(...) when the unmarshaller
+     * is created.
+     *
+     * @param unmarshallerProperties map of properties.
+     */
+    public void setUnmarshallerProperties(Map<String, Object> unmarshallerProperties) {
+        this.unmarshallerProperties = unmarshallerProperties;
+    }
+
+    /**
+     * Returns the Unmarshaller.Listener that will be registered on the Unmarshallers
+     * @return
+     */
+    public Unmarshaller.Listener getUnmarshallerListener() {
+        return unmarshallerListener;
+    }
+
+    /**
+     * Sets the Unmarshaller.Listener that will be registered on the Unmarshallers
+     * @param unmarshallerListener
+     */
+    public void setUnmarshallerListener(Unmarshaller.Listener unmarshallerListener) {
+        this.unmarshallerListener = unmarshallerListener;
+    }
+    /**
+     * Returns the Marshaller.Listener that will be registered on the Marshallers
+     * @return
+     */
+    public Marshaller.Listener getMarshallerListener() {
+        return marshallerListener;
+    }
+
+    /**
+     * Sets the Marshaller.Listener that will be registered on the Marshallers
+     * @param marshallerListener
+     */
+    public void setMarshallerListener(Marshaller.Listener marshallerListener) {
+        this.marshallerListener = marshallerListener;
+    }
+
+
+    public ValidationEventHandler getValidationEventHandler() {
+        return validationEventHandler;
+    }
+
+    public void setValidationEventHandler(ValidationEventHandler validationEventHandler) {
+        this.validationEventHandler = validationEventHandler;
+    }
+
+
+    public boolean isUnwrapJAXBElement() {
+        return unwrapJAXBElement;
+    }
+
+    public void setUnwrapJAXBElement(boolean unwrapJAXBElement) {
+        this.unwrapJAXBElement = unwrapJAXBElement;
+    }
+
+    public WrapperHelper createWrapperHelper(Class<?> wrapperType, QName wrapperName, List<String> partNames,
+                                             List<String> elTypeNames, List<Class<?>> partClasses) {
+        List<Method> getMethods = new ArrayList<>(partNames.size());
+        List<Method> setMethods = new ArrayList<>(partNames.size());
+        List<Method> jaxbMethods = new ArrayList<>(partNames.size());
+        List<Field> fields = new ArrayList<>(partNames.size());
+
+        Method[] allMethods = wrapperType.getMethods();
+        String packageName = PackageUtils.getPackageName(wrapperType);
+
+        //if wrappertype class is generated by ASM, getPackage() always return null
+        if (wrapperType.getPackage() != null) {
+            packageName = wrapperType.getPackage().getName();
+        }
+
+        String objectFactoryClassName = packageName + ".ObjectFactory";
+
+        Object objectFactory = null;
+        try {
+            objectFactory = wrapperType.getClassLoader().loadClass(objectFactoryClassName).newInstance();
+        } catch (Exception e) {
+            //ignore, probably won't need it
+        }
+        Method[] allOFMethods;
+        if (objectFactory != null) {
+            allOFMethods = objectFactory.getClass().getMethods();
+        } else {
+            allOFMethods = new Method[0];
+        }
+
+        for (int x = 0; x < partNames.size(); x++) {
+            String partName = partNames.get(x);
+            if (partName == null) {
+                getMethods.add(null);
+                setMethods.add(null);
+                fields.add(null);
+                jaxbMethods.add(null);
+                continue;
+            }
+
+            String elementType = elTypeNames.get(x);
+
+            String getAccessor = JAXBUtils.nameToIdentifier(partName, JAXBUtils.IdentifierType.GETTER);
+            String setAccessor = JAXBUtils.nameToIdentifier(partName, JAXBUtils.IdentifierType.SETTER);
+            Method getMethod = null;
+            Method setMethod = null;
+            Class<?> valueClass = wrapperType;
+
+            try {
+                getMethod = valueClass.getMethod(getAccessor, AbstractWrapperHelper.NO_CLASSES);
+            } catch (NoSuchMethodException ex) {
+                //ignore for now
+            }
+
+            Field elField = getElField(partName, valueClass);
+            if (getMethod == null
+                && elementType != null
+                && "boolean".equalsIgnoreCase(elementType)
+                && (elField == null
+                    || (!Collection.class.isAssignableFrom(elField.getType())
+                    && !elField.getType().isArray()))) {
+
+                try {
+                    String newAcc = getAccessor.replaceFirst("get", "is");
+                    getMethod = wrapperType.getMethod(newAcc, AbstractWrapperHelper.NO_CLASSES);
+                } catch (NoSuchMethodException ex) {
+                    //ignore for now
+                }
+            }
+            if (getMethod == null
+                && "return".equals(partName)) {
+                //RI generated code uses this
+                try {
+                    getMethod = valueClass.getMethod("get_return", AbstractWrapperHelper.NO_CLASSES);
+                } catch (NoSuchMethodException ex) {
+                    try {
+                        getMethod = valueClass.getMethod("is_return",
+                                                          new Class[0]);
+                    } catch (NoSuchMethodException ex2) {
+                        //ignore for now
+                    }
+                }
+            }
+            if (getMethod == null && elField != null) {
+                getAccessor = JAXBUtils.nameToIdentifier(elField.getName(), JAXBUtils.IdentifierType.GETTER);
+                setAccessor = JAXBUtils.nameToIdentifier(elField.getName(), JAXBUtils.IdentifierType.SETTER);
+                try {
+                    getMethod = valueClass.getMethod(getAccessor, AbstractWrapperHelper.NO_CLASSES);
+                } catch (NoSuchMethodException ex) {
+                    //ignore for now
+                }
+            }
+            String setAccessor2 = setAccessor;
+            if ("return".equals(partName)) {
+                //some versions of jaxb map "return" to "set_return" instead of "setReturn"
+                setAccessor2 = "set_return";
+            }
+
+            for (Method method : allMethods) {
+                if (method.getParameterTypes() != null && method.getParameterTypes().length == 1
+                    && (setAccessor.equals(method.getName())
+                        || setAccessor2.equals(method.getName()))) {
+                    setMethod = method;
+                    break;
+                }
+            }
+
+            getMethods.add(getMethod);
+            setMethods.add(setMethod);
+            if (setMethod != null
+                && JAXBElement.class.isAssignableFrom(setMethod.getParameterTypes()[0])) {
+
+                Type t = setMethod.getGenericParameterTypes()[0];
+                Class<?> pcls = null;
+                if (t instanceof ParameterizedType) {
+                    t = ((ParameterizedType)t).getActualTypeArguments()[0];
+                }
+                if (t instanceof Class) {
+                    pcls = (Class<?>)t;
+                }
+
+                String methodName = "create" + wrapperType.getSimpleName()
+                    + setMethod.getName().substring(3);
+
+                for (Method m : allOFMethods) {
+                    if (m.getName().equals(methodName)
+                        && m.getParameterTypes().length == 1
+                        && (pcls == null
+                            || pcls.equals(m.getParameterTypes()[0]))) {
+                        jaxbMethods.add(m);
+                    }
+                }
+            } else {
+                jaxbMethods.add(null);
+            }
+
+            if (elField != null) {
+                // JAXB Type get XmlElement Annotation
+                XmlElement el = elField.getAnnotation(XmlElement.class);
+                if (el != null
+                    && (partName.equals(el.name())
+                        || "##default".equals(el.name()))) {
+                    ReflectionUtil.setAccessible(elField);
+                    fields.add(elField);
+                } else {
+                    if (getMethod == null && setMethod == null) {
+                        if (el != null) {
+                            LOG.warning("Could not create accessor for property " + partName
+                                        + " of type " + wrapperType.getName() + " as the @XmlElement "
+                                        + "defines the name as " + el.name());
+                        } else {
+                            LOG.warning("Could not create accessor for property " + partName
+                                        + " of type " + wrapperType.getName());
+                        }
+                    }
+                    fields.add(null);
+                }
+            } else {
+                fields.add(null);
+            }
+
+        }
+
+        return createWrapperHelper(getBus(), wrapperType,
+                                 setMethods.toArray(new Method[0]),
+                                 getMethods.toArray(new Method[0]),
+                                 jaxbMethods.toArray(new Method[0]),
+                                 fields.toArray(new Field[0]),
+                                 objectFactory);
+    }
+
+    public static boolean isInBuiltInSchemas(DOMResult schema) {
+        return BUILT_IN_SCHEMAS.containsValue(schema);
+    }
+
+    private static Field getElField(String partName, final Class<?> wrapperType) {
+        String fieldName = JAXBUtils.nameToIdentifier(partName, JAXBUtils.IdentifierType.VARIABLE);
+        Field[] fields = ReflectionUtil.getDeclaredFields(wrapperType);
+        for (Field field : fields) {
+            XmlElement el = field.getAnnotation(XmlElement.class);
+            if (el != null
+                && partName.equals(el.name())) {
+                return field;
+            }
+
+            XmlElementRef xmlElementRefAnnotation = field.getAnnotation(XmlElementRef.class);
+            if (xmlElementRefAnnotation != null && partName.equals(xmlElementRefAnnotation.name())) {
+                return field;
+            }
+
+            if (field.getName().equals(fieldName)) {
+                return field;
+            }
+        }
+        return null;
+    }
+
+
+    private static WrapperHelper createWrapperHelper(Bus bus, Class<?> wrapperType, Method[] setMethods,
+                                                     Method[] getMethods, Method[] jaxbMethods,
+                                                     Field[] fields, Object objectFactory) {
+
+        WrapperHelper wh = compileWrapperHelper(bus, wrapperType, setMethods, getMethods, jaxbMethods, fields,
+                                                objectFactory);
+
+        if (wh == null) {
+            wh = new JAXBWrapperHelper(wrapperType, setMethods, getMethods, jaxbMethods, fields,
+                                       objectFactory);
+        }
+        return wh;
+    }
+
+    private static WrapperHelper compileWrapperHelper(Bus bus, Class<?> wrapperType, Method[] setMethods,
+                                                      Method[] getMethods, Method[] jaxbMethods,
+                                                      Field[] fields, Object objectFactory) {
+        try {
+            WrapperHelperCreator creator = bus.getExtension(WrapperHelperCreator.class);
+            return creator.compile(wrapperType, setMethods, getMethods,
+                    jaxbMethods, fields, objectFactory);
+        } catch (Throwable t) {
+            // Some error - probably a bad version of ASM or similar
+            return null;
+        }
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxb/JAXBEncoderDecoder.java b/transform/src/patch/java/org/apache/cxf/jaxb/JAXBEncoderDecoder.java
new file mode 100644
index 0000000..4d177e9
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxb/JAXBEncoderDecoder.java
@@ -0,0 +1,1119 @@
+/**
+ * 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.jaxb;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+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.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.logging.Logger;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlAccessOrder;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorOrder;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
+import javax.xml.bind.attachment.AttachmentMarshaller;
+import javax.xml.bind.attachment.AttachmentUnmarshaller;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+import javax.xml.stream.Location;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLEventWriter;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.stream.util.StreamReaderDelegate;
+import javax.xml.transform.stream.StreamResult;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import org.apache.cxf.common.i18n.Message;
+import org.apache.cxf.common.jaxb.JAXBUtils;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.ReflectionUtil;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.helpers.DOMUtils;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.service.model.MessagePartInfo;
+import org.apache.cxf.service.model.SchemaInfo;
+import org.apache.cxf.staxutils.DepthXMLStreamReader;
+import org.apache.cxf.staxutils.StaxUtils;
+import org.apache.cxf.staxutils.W3CDOMStreamWriter;
+import org.apache.cxf.staxutils.W3CNamespaceContext;
+import org.apache.ws.commons.schema.XmlSchemaElement;
+import org.apache.ws.commons.schema.XmlSchemaSimpleType;
+import org.apache.ws.commons.schema.XmlSchemaSimpleTypeList;
+import org.apache.ws.commons.schema.constants.Constants;
+
+/**
+ * Utility functions for JAXB.
+ */
+public final class JAXBEncoderDecoder {
+    private static final class AddXSITypeStreamReader extends StreamReaderDelegate {
+        private boolean first = true;
+        private int offset = 1;
+        private final QName typeQName;
+
+        private AddXSITypeStreamReader(XMLStreamReader reader, QName typeQName) {
+            super(reader);
+            this.typeQName = typeQName;
+        }
+
+        public int getAttributeCount() {
+            return super.getAttributeCount() + offset;
+        }
+
+        public String getAttributeLocalName(int index) {
+            if (first && index == 0) {
+                return "type";
+            }
+            return super.getAttributeLocalName(index - offset);
+        }
+
+        public QName getAttributeName(int index) {
+            if (first && index == 0) {
+                return new QName(Constants.URI_2001_SCHEMA_XSI, "type");
+            }
+            return super.getAttributeName(index - offset);
+        }
+
+        public String getAttributeNamespace(int index) {
+            if (first && index == 0) {
+                return Constants.URI_2001_SCHEMA_XSI;
+            }
+            return super.getAttributeNamespace(index - offset);
+        }
+
+        public String getAttributePrefix(int index) {
+            if (first && index == 0) {
+                return "xsi";
+            }
+            return super.getAttributePrefix(index - offset);
+        }
+
+        public String getAttributeType(int index) {
+            if (first && index == 0) {
+                return "#TEXT";
+            }
+            return super.getAttributeType(index - offset);
+        }
+
+        public String getAttributeValue(int index) {
+            if (first && index == 0) {
+                String pfx = this.getNamespaceContext().getPrefix(typeQName.getNamespaceURI());
+                if (StringUtils.isEmpty(pfx)) {
+                    return typeQName.getLocalPart();
+                }
+                return pfx + ":" + typeQName.getLocalPart();
+            }
+            return super.getAttributeValue(index - offset);
+        }
+
+        public int next()  throws XMLStreamException {
+            first = false;
+            offset = 0;
+            return super.next();
+        }
+
+        public String getAttributeValue(String namespaceUri,
+                                        String localName) {
+            if (first
+                && Constants.URI_2001_SCHEMA_XSI.equals(namespaceUri)
+                && "type".equals(localName)) {
+                String pfx = this.getNamespaceContext().getPrefix(typeQName.getNamespaceURI());
+                if (StringUtils.isEmpty(pfx)) {
+                    return typeQName.getLocalPart();
+                }
+                return pfx + ":" + typeQName.getLocalPart();
+            }
+            return super.getAttributeValue(namespaceUri, localName);
+        }
+    }
+
+    private static final Logger LOG = LogUtils.getLogger(JAXBEncoderDecoder.class);
+
+    private JAXBEncoderDecoder() {
+    }
+
+    public static void marshall(Marshaller marshaller,
+                                Object elValue,
+                                MessagePartInfo part,
+                                Object source) {
+        try {
+            // The Marshaller.JAXB_FRAGMENT will tell the Marshaller not to
+            // generate the xml declaration.
+            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
+            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, false);
+        } catch (javax.xml.bind.PropertyException e) {
+            // intentionally empty.
+        }
+
+        Class<?> cls = null;
+        if (part != null) {
+            cls = part.getTypeClass();
+        }
+
+        if (cls == null) {
+            cls = null != elValue ? elValue.getClass() : null;
+        }
+
+        if (cls != null && cls.isArray() && elValue instanceof Collection) {
+            Collection<?> col = (Collection<?>)elValue;
+            elValue = col.toArray((Object[])Array.newInstance(cls.getComponentType(), col.size()));
+        }
+
+        try {
+            Object mObj = elValue;
+            QName elName = null;
+            if (part != null) {
+                elName = part.getConcreteName();
+            }
+
+            if (null != elName) {
+
+                if (part != null && part.getXmlSchema() instanceof XmlSchemaElement) {
+
+                    XmlSchemaElement el = (XmlSchemaElement)part.getXmlSchema();
+
+                    if (mObj.getClass().isArray()
+                        && el.getSchemaType() instanceof XmlSchemaSimpleType
+                        && ((XmlSchemaSimpleType)el.getSchemaType()).
+                        getContent() instanceof XmlSchemaSimpleTypeList) {
+                        mObj = Arrays.asList((Object[])mObj);
+                        writeObject(marshaller, source, newJAXBElement(elName, cls, mObj));
+                    } else if (part.getMessageInfo().getOperation().isUnwrapped()
+                               && (mObj.getClass().isArray() || mObj instanceof List)
+                               && el.getMaxOccurs() != 1) {
+                        writeArrayObject(marshaller,
+                                         source,
+                                         elName,
+                                         mObj);
+                    } else {
+                        writeObject(marshaller, source, newJAXBElement(elName, cls, mObj));
+                    }
+                } else if (byte[].class == cls && part.getTypeQName() != null
+                           && "hexBinary".equals(part.getTypeQName().getLocalPart())) {
+                    mObj = new HexBinaryAdapter().marshal((byte[])mObj);
+                    writeObject(marshaller, source, newJAXBElement(elName, String.class, mObj));
+                } else if (mObj instanceof JAXBElement) {
+                    writeObject(marshaller, source, mObj);
+                } else if (marshaller.getSchema() != null) {
+                    //force xsi:type so types can be validated instead of trying to
+                    //use the RPC/lit element names that aren't in the schema
+                    writeObject(marshaller, source, newJAXBElement(elName, Object.class, mObj));
+                } else {
+                    writeObject(marshaller, source, newJAXBElement(elName, cls, mObj));
+                }
+            } else {
+                writeObject(marshaller, source, mObj);
+            }
+        } catch (Fault ex) {
+            throw ex;
+        } catch (javax.xml.bind.MarshalException ex) {
+            Message faultMessage = new Message("MARSHAL_ERROR", LOG, ex.getLinkedException()
+                .getMessage());
+            throw new Fault(faultMessage, ex);
+        } catch (Exception ex) {
+            throw new Fault(new Message("MARSHAL_ERROR", LOG, ex.getMessage()), ex);
+        }
+    }
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private static JAXBElement<?> newJAXBElement(QName elName, Class<?> cls, Object mObj) {
+        if (mObj instanceof JAXBElement) {
+            return (JAXBElement)mObj;
+        }
+        if (cls == null && mObj != null) {
+            cls = mObj.getClass();
+        }
+        return new JAXBElement(elName, cls, mObj);
+    }
+
+    //TODO: cache the JAXBRIContext
+    public static void marshalWithBridge(QName qname,
+                                         Class<?> cls,
+                                         Annotation[] anns,
+                                         Set<Class<?>> ctxClasses,
+                                         Object elValue,
+                                         Object source, AttachmentMarshaller am) {
+        try {
+            JAXBUtils.BridgeWrapper bridge = JAXBUtils.createBridge(ctxClasses, qname, cls, anns);
+
+            if (source instanceof XMLStreamWriter) {
+                bridge.marshal(elValue, (XMLStreamWriter)source, am);
+            } else if (source instanceof OutputStream) {
+                //the namespace is missing when marshal the xsd:QName type
+                //to the OutputStream directly
+                java.io.StringWriter sw = new java.io.StringWriter();
+                StreamResult s1 = new StreamResult(sw);
+                bridge.marshal(elValue, s1);
+                ((OutputStream)source).write(sw.toString().getBytes());
+            } else if (source instanceof Node) {
+                bridge.marshal(elValue, (Node)source, am);
+            } else {
+                throw new Fault(new Message("UNKNOWN_SOURCE", LOG, source.getClass().getName()));
+            }
+        } catch (javax.xml.bind.MarshalException ex) {
+            Message faultMessage = new Message("MARSHAL_ERROR", LOG, ex.getLinkedException()
+                .getMessage());
+            throw new Fault(faultMessage, ex);
+        } catch (Exception ex) {
+            throw new Fault(new Message("MARSHAL_ERROR", LOG, ex.getMessage()), ex);
+        }
+
+    }
+
+//  TODO: cache the JAXBRIContext
+    public static Object unmarshalWithBridge(QName qname,
+                                             Class<?> cls,
+                                             Annotation[] anns,
+                                             Set<Class<?>> ctxClasses,
+                                             Object source,
+                                             AttachmentUnmarshaller am) {
+
+        try {
+            JAXBUtils.BridgeWrapper bridge = JAXBUtils.createBridge(ctxClasses, qname, cls, anns);
+
+            if (source instanceof XMLStreamReader) {
+                //DOMUtils.writeXml(StaxUtils.read((XMLStreamReader)source), System.out);
+                return bridge.unmarshal((XMLStreamReader)source, am);
+            } else if (source instanceof InputStream) {
+                return bridge.unmarshal((InputStream)source);
+            } else if (source instanceof Node) {
+                return bridge.unmarshal((Node)source, am);
+            } else {
+                throw new Fault(new Message("UNKNOWN_SOURCE", LOG, source.getClass().getName()));
+            }
+        } catch (javax.xml.bind.MarshalException ex) {
+            Message faultMessage = new Message("MARSHAL_ERROR", LOG, ex.getLinkedException()
+                .getMessage());
+            throw new Fault(faultMessage, ex);
+        } catch (Exception ex) {
+            throw new Fault(new Message("MARSHAL_ERROR", LOG, ex.getMessage()), ex);
+        }
+
+    }
+
+    public static void marshallException(Marshaller marshaller, Exception elValue,
+                                         MessagePartInfo part, Object source) {
+        XMLStreamWriter writer = getStreamWriter(source);
+        QName qn = part.getElementQName();
+        try {
+            writer.writeStartElement("ns1", qn.getLocalPart(), qn.getNamespaceURI());
+            Class<?> cls = part.getTypeClass();
+            XmlAccessType accessType = Utils.getXmlAccessType(cls);
+            String namespace = part.getElementQName().getNamespaceURI();
+            String attNs = namespace;
+
+            SchemaInfo sch = part.getMessageInfo().getOperation().getInterface()
+                .getService().getSchema(namespace);
+            if (sch == null) {
+                LOG.warning("Schema associated with " + namespace + " is null");
+                namespace = null;
+                attNs = null;
+            } else {
+                if (!sch.isElementFormQualified()) {
+                    namespace = null;
+                }
+                if (!sch.isAttributeFormQualified()) {
+                    attNs = null;
+                }
+            }
+            List<Member> combinedMembers = new ArrayList<>();
+
+            for (Field f : Utils.getFields(cls, accessType)) {
+                XmlAttribute at = f.getAnnotation(XmlAttribute.class);
+                if (at == null) {
+                    combinedMembers.add(f);
+                } else {
+                    QName fname = new QName(attNs, StringUtils.isEmpty(at.name()) ? f.getName() : at.name());
+                    ReflectionUtil.setAccessible(f);
+                    Object o = Utils.getFieldValue(f, elValue);
+                    DocumentFragment frag = DOMUtils.getEmptyDocument().createDocumentFragment();
+                    writeObject(marshaller, frag, newJAXBElement(fname, String.class, o));
+
+                    if (attNs != null) {
+                        writer.writeAttribute(attNs, fname.getLocalPart(),
+                                              DOMUtils.getAllContent(frag));
+                    } else {
+                        writer.writeAttribute(fname.getLocalPart(), DOMUtils.getAllContent(frag));
+                    }
+                }
+            }
+            for (Method m : Utils.getGetters(cls, accessType)) {
+                if (!m.isAnnotationPresent(XmlAttribute.class)) {
+                    combinedMembers.add(m);
+                } else {
+                    int idx = m.getName().startsWith("get") ? 3 : 2;
+                    String name = m.getName().substring(idx);
+                    name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
+                    XmlAttribute at = m.getAnnotation(XmlAttribute.class);
+                    QName mname = new QName(namespace, StringUtils.isEmpty(at.name()) ? name : at.name());
+                    DocumentFragment frag = DOMUtils.getEmptyDocument().createDocumentFragment();
+                    Object o = Utils.getMethodValue(m, elValue);
+                    writeObject(marshaller, frag, newJAXBElement(mname, String.class, o));
+                    if (attNs != null) {
+                        writer.writeAttribute(attNs, mname.getLocalPart(),
+                                              DOMUtils.getAllContent(frag));
+                    } else {
+                        writer.writeAttribute(mname.getLocalPart(), DOMUtils.getAllContent(frag));
+                    }
+                }
+            }
+
+            XmlAccessorOrder xmlAccessorOrder = cls.getAnnotation(XmlAccessorOrder.class);
+            if (xmlAccessorOrder != null && xmlAccessorOrder.value().equals(XmlAccessOrder.ALPHABETICAL)) {
+                Collections.sort(combinedMembers, new Comparator<Member>() {
+                    public int compare(Member m1, Member m2) {
+                        return m1.getName().compareTo(m2.getName());
+                    }
+                });
+            }
+            XmlType xmlType = cls.getAnnotation(XmlType.class);
+            if (xmlType != null && xmlType.propOrder().length > 1 && !xmlType.propOrder()[0].isEmpty()) {
+                final List<String> orderList = Arrays.asList(xmlType.propOrder());
+                Collections.sort(combinedMembers, new Comparator<Member>() {
+                    public int compare(Member m1, Member m2) {
+                        String m1Name = getName(m1);
+                        String m2Name = getName(m2);
+                        int m1Index = orderList.indexOf(m1Name);
+                        int m2Index = orderList.indexOf(m2Name);
+                        if (m1Index != -1 && m2Index != -1) {
+                            return m1Index - m2Index;
+                        }
+                        if (m1Index == -1 && m2Index != -1) {
+                            return 1;
+                        }
+                        if (m1Index != -1 && m2Index == -1) {
+                            return -1;
+                        }
+                        return 0;
+                    }
+                });
+            }
+            for (Member member : combinedMembers) {
+                if (member instanceof Field) {
+                    Field f = (Field)member;
+                    QName fname = new QName(namespace, f.getName());
+                    ReflectionUtil.setAccessible(f);
+                    if (JAXBSchemaInitializer.isArray(f.getGenericType())) {
+                        writeArrayObject(marshaller, writer, fname, f.get(elValue));
+                    } else {
+                        Object o = Utils.getFieldValue(f, elValue);
+                        writeObject(marshaller, writer, newJAXBElement(fname, String.class, o));
+                    }
+                } else { // it's a Method
+                    Method m = (Method)member;
+                    int idx = m.getName().startsWith("get") ? 3 : 2;
+                    String name = m.getName().substring(idx);
+                    name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
+                    QName mname = new QName(namespace, name);
+                    if (JAXBSchemaInitializer.isArray(m.getGenericReturnType())) {
+                        writeArrayObject(marshaller, writer, mname, m.invoke(elValue));
+                    } else {
+                        Object o = Utils.getMethodValue(m, elValue);
+                        writeObject(marshaller, writer, newJAXBElement(mname, String.class, o));
+                    }
+                }
+            }
+
+            writer.writeEndElement();
+            writer.flush();
+        } catch (Exception e) {
+            throw new Fault(new Message("MARSHAL_ERROR", LOG, e.getMessage()), e);
+        } finally {
+            StaxUtils.close(writer);
+        }
+    }
+
+    private static String getName(Member m1) {
+        final String m1Name;
+        if (m1 instanceof Field) {
+            m1Name = ((Field)m1).getName();
+        } else {
+            int idx = m1.getName().startsWith("get") ? 3 : 2;
+            String name = m1.getName().substring(idx);
+            m1Name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
+        }
+        return m1Name;
+    }
+    private static void writeArrayObject(Marshaller marshaller,
+                                         Object source,
+                                         QName mname,
+                                         Object mObj) throws Fault, JAXBException {
+        // Have to handle this ourselves.... which really
+        // sucks.... but what can we do?
+        if (mObj == null) {
+            return;
+        }
+        Object objArray;
+        final Class<?> cls;
+        if (mObj instanceof List) {
+            List<?> l = (List<?>)mObj;
+            objArray = l.toArray();
+            cls = null;
+        } else {
+            objArray = mObj;
+            cls = objArray.getClass().getComponentType();
+        }
+        int len = Array.getLength(objArray);
+        for (int x = 0; x < len; x++) {
+            Object o = Array.get(objArray, x);
+            writeObject(marshaller, source,
+                        newJAXBElement(mname, cls == null ? o.getClass() : cls, o));
+        }
+    }
+
+    public static Exception unmarshallException(Unmarshaller u,
+                                                Object source,
+                                                MessagePartInfo part) {
+        XMLStreamReader reader;
+        if (source instanceof XMLStreamReader) {
+            reader = (XMLStreamReader)source;
+        } else if (source instanceof Element) {
+            reader = StaxUtils.createXMLStreamReader((Element)source);
+            try {
+                // advance into the node
+                reader.nextTag();
+            } catch (XMLStreamException e) {
+                // ignore
+            }
+        } else {
+            throw new Fault(new Message("UNKNOWN_SOURCE", LOG, source.getClass().getName()));
+        }
+        try {
+            QName qn = part.getElementQName();
+            if (!qn.equals(reader.getName())) {
+                throw new Fault(new Message("ELEMENT_NAME_MISMATCH", LOG, qn, reader.getName()));
+            }
+
+            Class<?> cls = part.getTypeClass();
+            Object obj;
+            try {
+                Constructor<?> cons = cls.getConstructor();
+                obj = cons.newInstance();
+            } catch (NoSuchMethodException nse) {
+                Constructor<?> cons = cls.getConstructor(new Class[] {String.class});
+                obj = cons.newInstance(new Object[1]);
+            }
+
+            XmlAccessType accessType = Utils.getXmlAccessType(cls);
+            reader.nextTag();
+            while (reader.getEventType() == XMLStreamConstants.START_ELEMENT) {
+                QName q = reader.getName();
+                String fieldName = q.getLocalPart();
+                Field f = Utils.getField(cls, accessType, fieldName);
+                if (f != null) {
+                    Type type = f.getGenericType();
+                    ReflectionUtil.setAccessible(f);
+                    if (JAXBSchemaInitializer.isArray(type)) {
+                        Class<?> compType = JAXBSchemaInitializer.getArrayComponentType(type);
+                        List<Object> ret = unmarshallArray(u, reader, q, compType, createList(type));
+                        Object o = ret;
+                        if (!isList(type)) {
+                            if (compType.isPrimitive()) {
+                                o = java.lang.reflect.Array.newInstance(compType, ret.size());
+                                for (int x = 0; x < ret.size(); x++) {
+                                    Array.set(o, x, ret.get(x));
+                                }
+                            } else {
+                                o = ret.toArray((Object[]) Array.newInstance(compType, ret.size()));
+                            }
+                        }
+
+                        f.set(obj, o);
+                    } else {
+                        Object o = getElementValue(u.unmarshal(reader, Utils.getFieldType(f)));
+                        Utils.setFieldValue(f, obj, o);
+                    }
+                } else {
+                    String s = StringUtils.capitalize(q.getLocalPart());
+                    Method m = Utils.getMethod(cls, accessType, "get" + s);
+                    if (m == null) {
+                        m = Utils.getMethod(cls, accessType, "is" + s);
+                    }
+                    Type type = m.getGenericReturnType();
+                    Object o;
+                    if (JAXBSchemaInitializer.isArray(type)) {
+                        Class<?> compType = JAXBSchemaInitializer
+                            .getArrayComponentType(type);
+                        List<Object> ret = unmarshallArray(u, reader,
+                                                           q,
+                                                           compType,
+                                                           createList(type));
+                        o = ret;
+                        if (!isList(type)) {
+                            if (compType.isPrimitive()) {
+                                o = java.lang.reflect.Array.newInstance(compType, ret.size());
+                                for (int x = 0; x < ret.size(); x++) {
+                                    Array.set(o, x, ret.get(x));
+                                }
+                            } else {
+                                o = ret.toArray((Object[])Array.newInstance(compType, ret.size()));
+                            }
+                        }
+                    } else {
+                        o = getElementValue(u.unmarshal(reader, Utils.getMethodReturnType(m)));
+                    }
+                    Method m2 = Utils.getMethod(cls, accessType, "set" + s, m.getReturnType());
+                    if (m2 != null) {
+                        if (JAXBSchemaInitializer.isArray(type)) {
+                            m2.invoke(obj, o);
+                        } else {
+                            Utils.setMethodValue(m, m2, obj, o);
+                        }
+                    } else {
+                        Field fn = ReflectionUtil.getDeclaredField(cls, q.getLocalPart());
+                        if (fn != null) {
+                            ReflectionUtil.setAccessible(fn);
+                            fn.set(obj, o);
+                        }
+                    }
+                }
+                if (reader.getEventType() == XMLStreamConstants.END_ELEMENT && q.equals(reader.getName())) {
+                    reader.next();
+                }
+            }
+            return (Exception)obj;
+        } catch (Exception e) {
+            throw new Fault(new Message("MARSHAL_ERROR", LOG, e.getMessage()), e);
+        }
+    }
+
+    private static void writeObject(Marshaller u, Object source, Object mObj) throws Fault, JAXBException {
+        if (source instanceof XMLStreamWriter) {
+            // allows the XML Stream Writer to adjust it's behaviour based on the state of the unmarshaller
+            if (source instanceof MarshallerAwareXMLWriter) {
+                ((MarshallerAwareXMLWriter) source).setMarshaller(u);
+            }
+            u.marshal(mObj, (XMLStreamWriter)source);
+        } else if (source instanceof OutputStream) {
+            u.marshal(mObj, (OutputStream)source);
+        } else if (source instanceof Node) {
+            u.marshal(mObj, (Node)source);
+        } else if (source instanceof XMLEventWriter) {
+            // allows the XML Event Writer to adjust it's behaviour based on the state of the unmarshaller
+            if (source instanceof MarshallerAwareXMLWriter) {
+                ((MarshallerAwareXMLWriter) source).setMarshaller(u);
+            }
+
+            u.marshal(mObj, (XMLEventWriter)source);
+        } else {
+            throw new Fault(new Message("UNKNOWN_SOURCE", LOG, source.getClass().getName()));
+        }
+    }
+
+    private static XMLStreamWriter getStreamWriter(Object source) throws Fault {
+        if (source instanceof XMLStreamWriter) {
+            return (XMLStreamWriter)source;
+        } else if (source instanceof OutputStream) {
+            return StaxUtils.createXMLStreamWriter((OutputStream)source);
+        } else if (source instanceof Node) {
+            return new W3CDOMStreamWriter((Element)source);
+        }
+        throw new Fault(new Message("UNKNOWN_SOURCE", LOG, source.getClass().getName()));
+    }
+
+
+    public static void marshallNullElement(Marshaller marshaller,
+                                           Object source,
+                                           MessagePartInfo part) {
+        Class<?> clazz = part != null ? part.getTypeClass() : null;
+        try {
+            writeObject(marshaller, source, newJAXBElement(part.getElementQName(), clazz, null));
+        } catch (JAXBException e) {
+            throw new Fault(new Message("MARSHAL_ERROR", LOG, e.getMessage()), e);
+        }
+    }
+
+
+    public static Object unmarshall(Unmarshaller u,
+                                    Object source,
+                                    MessagePartInfo part,
+                                    boolean unwrap) {
+        Class<?> clazz = part != null ? part.getTypeClass() : null;
+        if (clazz != null && Exception.class.isAssignableFrom(clazz)
+            && Boolean.TRUE.equals(part.getProperty(JAXBDataBinding.class.getName() + ".CUSTOM_EXCEPTION"))) {
+            return unmarshallException(u, source, part);
+        }
+
+        QName elName = part != null ? part.getConcreteName() : null;
+        if (clazz != null && clazz.isArray()
+            && part.getXmlSchema() instanceof XmlSchemaElement) {
+            XmlSchemaElement el = (XmlSchemaElement)part.getXmlSchema();
+
+            if (el.getSchemaType() instanceof XmlSchemaSimpleType
+                && ((XmlSchemaSimpleType)el.getSchemaType()).getContent()
+                instanceof XmlSchemaSimpleTypeList) {
+
+                Object obj = unmarshall(u, source, elName, null, unwrap);
+                if (clazz.isArray() && obj instanceof List) {
+                    return ((List<?>)obj).toArray((Object[])Array.newInstance(clazz.getComponentType(),
+                                                                           ((List<?>)obj).size()));
+                }
+
+                return obj;
+            } else if (part.getMessageInfo().getOperation().isUnwrapped() && el.getMaxOccurs() != 1) {
+                // must read ourselves....
+                List<Object> ret = unmarshallArray(u, source, elName, clazz.getComponentType(),
+                                                   createList(part));
+                Object o = ret;
+                if (!isList(part)) {
+                    if (isSet(part)) {
+                        o = createSet(part, ret);
+                    } else if (clazz.getComponentType().isPrimitive()) {
+                        o = java.lang.reflect.Array.newInstance(clazz.getComponentType(), ret.size());
+                        for (int x = 0; x < ret.size(); x++) {
+                            Array.set(o, x, ret.get(x));
+                        }
+                    } else {
+                        o = ret.toArray((Object[])Array.newInstance(clazz.getComponentType(), ret.size()));
+                    }
+                }
+                return o;
+            }
+        } else if (byte[].class == clazz && part.getTypeQName() != null
+                   && "hexBinary".equals(part.getTypeQName().getLocalPart())) {
+
+            String obj = (String)unmarshall(u, source, elName, String.class, unwrap);
+            return new HexBinaryAdapter().unmarshal(obj);
+        } else if (part != null && u.getSchema() != null
+            && !(part.getXmlSchema() instanceof XmlSchemaElement)) {
+            //Validating RPC/Lit, make sure we don't try a root element name thing
+            source = updateSourceWithXSIType(source, part.getTypeQName());
+        }
+
+        Object o = unmarshall(u, source, elName, clazz, unwrap);
+        if (o != null && o.getClass().isArray() && isList(part)) {
+            List<Object> ret = createList(part);
+            Collections.addAll(ret, (Object[])o);
+            o = ret;
+        }
+        return o;
+    }
+
+    private static Object updateSourceWithXSIType(Object source, final QName typeQName) {
+        if (source instanceof XMLStreamReader
+            && typeQName != null) {
+            XMLStreamReader reader = (XMLStreamReader)source;
+            String type = reader.getAttributeValue(Constants.URI_2001_SCHEMA_XSI, "type");
+            if (StringUtils.isEmpty(type)) {
+                source = new AddXSITypeStreamReader(reader, typeQName);
+            }
+        }
+        return source;
+    }
+
+    private static Object createSet(MessagePartInfo part, List<Object> ret) {
+        Type genericType = (Type)part.getProperty("generic.type");
+        Class<?> tp2 = (Class<?>)((ParameterizedType)genericType).getRawType();
+        if (tp2.isInterface()) {
+            return new HashSet<>(ret);
+        }
+        Collection<Object> c;
+        try {
+            c = CastUtils.cast((Collection<?>)tp2.newInstance());
+        } catch (Exception e) {
+            c = new HashSet<>();
+        }
+
+        c.addAll(ret);
+        return c;
+    }
+
+    private static boolean isSet(MessagePartInfo part) {
+        if (part.getTypeClass().isArray() && !part.getTypeClass().getComponentType().isPrimitive()) {
+            // && Collection.class.isAssignableFrom(part.getTypeClass())) {
+            // it's List Para
+            //
+            Type genericType = (Type)part.getProperty("generic.type");
+
+            if (genericType instanceof ParameterizedType) {
+                Type tp2 = ((ParameterizedType)genericType).getRawType();
+                if (tp2 instanceof Class) {
+                    return Set.class.isAssignableFrom((Class<?>)tp2);
+                }
+            }
+        }
+        return false;
+    }
+
+    private static List<Object> createList(MessagePartInfo part) {
+        Type genericType = (Type)part.getProperty("generic.type");
+        return createList(genericType);
+    }
+    private static List<Object> createList(Type genericType) {
+        if (genericType instanceof ParameterizedType) {
+            Type tp2 = ((ParameterizedType)genericType).getRawType();
+            if (tp2 instanceof Class) {
+                Class<?> cls = (Class<?>)tp2;
+                if (!cls.isInterface() && List.class.isAssignableFrom(cls)) {
+                    try {
+                        return CastUtils.cast((List<?>)cls.newInstance());
+                    } catch (Exception e) {
+                        // ignore, just return an ArrayList
+                    }
+                }
+            }
+        }
+        return new ArrayList<>();
+    }
+
+    private static boolean isList(Type cls) {
+        return cls instanceof ParameterizedType;
+    }
+    private static boolean isList(MessagePartInfo part) {
+        if (part.getTypeClass().isArray() && !part.getTypeClass().getComponentType().isPrimitive()) {
+            // && Collection.class.isAssignableFrom(part.getTypeClass())) {
+            // it's List Para
+            //
+            Type genericType = (Type)part.getProperty("generic.type");
+
+            if (genericType instanceof ParameterizedType) {
+                Type tp2 = ((ParameterizedType)genericType).getRawType();
+                if (tp2 instanceof Class) {
+                    return List.class.isAssignableFrom((Class<?>)tp2);
+                }
+            }
+        }
+        return false;
+    }
+
+    private static Object doUnmarshal(final Unmarshaller u,
+                                      final Object source,
+                                      final QName elName,
+                                      final Class<?> clazz,
+                                      final boolean unwrap) throws Exception {
+
+        final Object obj;
+        boolean unmarshalWithClass = true;
+
+        if (clazz == null
+            || (!clazz.isPrimitive()
+                && !clazz.isArray()
+                && !clazz.isEnum()
+                && !clazz.equals(Calendar.class)
+                && (Modifier.isAbstract(clazz.getModifiers())
+                    || Modifier.isInterface(clazz.getModifiers())))) {
+            unmarshalWithClass = false;
+        }
+
+        if (clazz != null
+            && ("javax.xml.datatype.XMLGregorianCalendar".equals(clazz.getName())
+                || "javax.xml.datatype.Duration".equals(clazz.getName()))) {
+            // special treat two jaxb defined built-in abstract types
+            unmarshalWithClass = true;
+        }
+        if (source instanceof Node) {
+            obj = unmarshalWithClass ? u.unmarshal((Node)source, clazz)
+                : u.unmarshal((Node)source);
+        } else if (source instanceof DepthXMLStreamReader) {
+            // JAXB optimizes a ton of stuff depending on the StreamReader impl. Thus,
+            // we REALLY want to pass the original reader in.   This is OK with JAXB
+            // as it doesn't read beyond the end so the DepthXMLStreamReader state
+            // would be OK when it returns.   The main winner is FastInfoset where parsing
+            // a testcase I have goes from about 300/sec to well over 1000.
+
+            DepthXMLStreamReader dr = (DepthXMLStreamReader)source;
+            XMLStreamReader reader = dr.getReader();
+
+            // allows the XML Stream Reader to adjust it's behaviour based on the state of the unmarshaller
+            if (reader instanceof UnmarshallerAwareXMLReader) {
+                ((UnmarshallerAwareXMLReader) reader).setUnmarshaller(u);
+            }
+
+            if (u.getSchema() != null) {
+                //validating, but we may need more namespaces
+                reader = findExtraNamespaces(reader);
+            }
+            obj = unmarshalWithClass ? u.unmarshal(reader, clazz) : u
+                .unmarshal(dr.getReader());
+        } else if (source instanceof XMLStreamReader) {
+            XMLStreamReader reader = (XMLStreamReader)source;
+
+            // allows the XML Stream Reader to adjust it's behaviour based on the state of the unmarshaller
+            if (reader instanceof UnmarshallerAwareXMLReader) {
+                ((UnmarshallerAwareXMLReader) reader).setUnmarshaller(u);
+            }
+
+            if (u.getSchema() != null) {
+                //validating, but we may need more namespaces
+                reader = findExtraNamespaces(reader);
+            }
+            obj = unmarshalWithClass ? u.unmarshal(reader, clazz) : u
+                .unmarshal(reader);
+        } else if (source instanceof XMLEventReader) {
+            // allows the XML Event Reader to adjust it's behaviour based on the state of the unmarshaller
+            if (source instanceof UnmarshallerAwareXMLReader) {
+                ((UnmarshallerAwareXMLReader) source).setUnmarshaller(u);
+            }
+
+            obj = unmarshalWithClass ? u.unmarshal((XMLEventReader)source, clazz) : u
+                .unmarshal((XMLEventReader)source);
+        } else if (source == null) {
+            throw new Fault(new Message("UNKNOWN_SOURCE", LOG, "null"));
+        } else {
+            throw new Fault(new Message("UNKNOWN_SOURCE", LOG, source.getClass().getName()));
+        }
+        return unwrap ? getElementValue(obj) : obj;
+    }
+    public static Object unmarshall(final Unmarshaller u,
+                                    final Object source,
+                                    final QName elName,
+                                    final Class<?> clazz,
+                                    final boolean unwrap) {
+        try {
+            return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+                public Object run() throws Exception {
+                    return doUnmarshal(u, source, elName, clazz, unwrap);
+                }
+            });
+        } catch (PrivilegedActionException e) {
+            Exception ex = e.getException();
+            if (ex instanceof Fault) {
+                throw (Fault)ex;
+            }
+            if (ex instanceof javax.xml.bind.UnmarshalException) {
+                javax.xml.bind.UnmarshalException unmarshalEx = (javax.xml.bind.UnmarshalException)ex;
+                if (unmarshalEx.getLinkedException() != null) {
+                    throw new Fault(new Message("UNMARSHAL_ERROR", LOG,
+                                            unmarshalEx.getLinkedException().getMessage()), ex);
+                }
+                throw new Fault(new Message("UNMARSHAL_ERROR", LOG,
+                                            unmarshalEx.getMessage()), ex);
+            }
+            throw new Fault(new Message("UNMARSHAL_ERROR", LOG, ex.getMessage()), ex);
+        }
+    }
+
+    private static XMLStreamReader findExtraNamespaces(XMLStreamReader source) {
+        //due to a deficiency in the Stax API, there isn't a way to get all
+        //the namespace prefixes that are "valid" at this point.  Thus, JAXB
+        //cannot set all the prefixes into the validator (which also doesn't allow
+        //setting a NSContext, just allows declaring of prefixes) so resolving
+        //prefixes and such will fail if they were declared on any of the parent
+        //elements.
+        //
+        //We'll use some reflection to grab the known namespaces from woodstox
+        //or the xerces parser and fake extra namespace decls on the root elements.
+        //slight performance penalty, but there already is a penalty if you are validating
+        //anyway.
+
+        NamespaceContext c = source.getNamespaceContext();
+        final Map<String, String> nsMap = new TreeMap<>();
+        try {
+            if (c instanceof W3CNamespaceContext) {
+                Element element = ((W3CNamespaceContext)c).getElement();
+                while (element != null) {
+                    NamedNodeMap namedNodeMap = element.getAttributes();
+                    for (int i = 0; i < namedNodeMap.getLength(); i++) {
+                        Attr attr = (Attr)namedNodeMap.item(i);
+                        if (attr.getPrefix() != null && "xmlns".equals(attr.getPrefix())) {
+                            nsMap.put(attr.getLocalName(), attr.getValue());
+                        }
+                    }
+                    element = (Element)element.getParentNode();
+                }
+            } else {
+                try {
+                    //Woodstox version
+                    c = (NamespaceContext)c.getClass().getMethod("createNonTransientNsContext",
+                                                                 Location.class)
+                        .invoke(c, new Object[1]);
+                } catch (Throwable t) {
+                    //ignore
+                }
+                Field f = ReflectionUtil.getDeclaredField(c.getClass(), "mNamespaces");
+                ReflectionUtil.setAccessible(f);
+                String[] ns = (String[])f.get(c);
+                for (int x = 0; x < ns.length; x += 2) {
+                    if (ns[x] == null) {
+                        nsMap.put("", ns[x + 1]);
+                    } else {
+                        nsMap.put(ns[x], ns[x + 1]);
+                    }
+                }
+            }
+        } catch (Throwable t) {
+            //internal JDK/xerces version
+            try {
+                Field f = ReflectionUtil.getDeclaredField(c.getClass(), "fNamespaceContext");
+                ReflectionUtil.setAccessible(f);
+                Object c2 = f.get(c);
+                Enumeration<?> enm = (Enumeration<?>)c2.getClass().getMethod("getAllPrefixes").invoke(c2);
+                while (enm.hasMoreElements()) {
+                    String s = (String)enm.nextElement();
+                    if (s == null) {
+                        nsMap.put("", c.getNamespaceURI(null));
+                    } else {
+                        nsMap.put(s, c.getNamespaceURI(s));
+                    }
+                }
+            } catch (Throwable t2) {
+                //ignore
+            }
+        }
+        if (!nsMap.isEmpty()) {
+            for (int x = 0; x < source.getNamespaceCount(); x++) {
+                String pfx = source.getNamespacePrefix(x);
+                if (pfx == null) {
+                    nsMap.remove("");
+                } else {
+                    nsMap.remove(pfx);
+                }
+            }
+            if (!nsMap.isEmpty()) {
+                @SuppressWarnings("unchecked")
+                final Map.Entry<String, String>[] namespaces
+                    = nsMap.entrySet().toArray(new Map.Entry[nsMap.size()]);
+                //OK. we have extra namespaces.  We'll need to wrapper the reader
+                //with a new one that will fake extra namespace events
+                source = new DepthXMLStreamReader(source) {
+                    public int getNamespaceCount() {
+                        if (getDepth() == 0 && isStartElement()) {
+                            return super.getNamespaceCount() + nsMap.size();
+                        }
+                        return super.getNamespaceCount();
+                    }
+
+                    public String getNamespacePrefix(int arg0) {
+                        if (getDepth() == 0 && isStartElement()) {
+                            int i = super.getNamespaceCount();
+                            if (arg0 >= i) {
+                                arg0 -= i;
+                                return namespaces[arg0].getKey();
+                            }
+                        }
+                        return super.getNamespacePrefix(arg0);
+                    }
+
+                    public String getNamespaceURI(int arg0) {
+                        if (getDepth() == 0 && isStartElement()) {
+                            int i = super.getNamespaceCount();
+                            if (arg0 >= i) {
+                                arg0 -= i;
+                                return namespaces[arg0].getValue();
+                            }
+                        }
+                        return super.getNamespaceURI(arg0);
+                    }
+
+                };
+            }
+        }
+
+        return source;
+    }
+
+    public static Object getElementValue(Object obj) {
+        if (null == obj) {
+            return null;
+        }
+
+        if (obj instanceof JAXBElement) {
+            return ((JAXBElement<?>)obj).getValue();
+        }
+        return obj;
+    }
+
+    public static Class<?> getClassFromType(Type t) {
+        if (t instanceof Class) {
+            return (Class<?>)t;
+        } else if (t instanceof GenericArrayType) {
+            GenericArrayType g = (GenericArrayType)t;
+            return Array.newInstance(getClassFromType(g.getGenericComponentType()), 0).getClass();
+        } else if (t instanceof ParameterizedType) {
+            ParameterizedType p = (ParameterizedType)t;
+            return getClassFromType(p.getRawType());
+        }
+        // TypeVariable and WildCardType are not handled as it is unlikely such
+        // Types will
+        // JAXB Code Generated.
+        assert false;
+        throw new IllegalArgumentException("Cannot get Class object from unknown Type");
+    }
+
+    public static List<Object> unmarshallArray(Unmarshaller u, Object source,
+                                               QName elName, Class<?> clazz,
+                                               List<Object> ret) {
+        try {
+            XMLStreamReader reader;
+            if (source instanceof XMLStreamReader) {
+                reader = (XMLStreamReader)source;
+            } else if (source instanceof Element) {
+                reader = StaxUtils.createXMLStreamReader((Element)source);
+            } else {
+                throw new Fault(new Message("UNKNOWN_SOURCE", LOG, source.getClass().getName()));
+            }
+            while (reader.getName().equals(elName)) {
+                JAXBElement<?> type = u.unmarshal(reader, clazz);
+                if (type != null) {
+                    ret.add(type.getValue());
+                }
+                while (reader.getEventType() != XMLStreamConstants.START_ELEMENT
+                    && reader.getEventType() != XMLStreamConstants.END_ELEMENT) {
+                    reader.nextTag();
+                }
+            }
+            return ret;
+        } catch (Fault ex) {
+            throw ex;
+        } catch (javax.xml.bind.MarshalException ex) {
+            throw new Fault(new Message("UNMARSHAL_ERROR", LOG, ex.getLinkedException()
+                .getMessage()), ex);
+        } catch (Exception ex) {
+            throw new Fault(new Message("UNMARSHAL_ERROR", LOG, ex.getMessage()), ex);
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxb/JAXBSchemaInitializer.java b/transform/src/patch/java/org/apache/cxf/jaxb/JAXBSchemaInitializer.java
new file mode 100644
index 0000000..dde712f
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxb/JAXBSchemaInitializer.java
@@ -0,0 +1,823 @@
+/**
+ * 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.jaxb;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.annotation.XmlAccessOrder;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorOrder;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlList;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import javax.xml.namespace.QName;
+
+import org.apache.cxf.common.i18n.Message;
+import org.apache.cxf.common.jaxb.JAXBBeanInfo;
+import org.apache.cxf.common.jaxb.JAXBContextProxy;
+import org.apache.cxf.common.jaxb.JAXBUtils;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.common.xmlschema.SchemaCollection;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.service.ServiceModelVisitor;
+import org.apache.cxf.service.model.FaultInfo;
+import org.apache.cxf.service.model.MessagePartInfo;
+import org.apache.cxf.service.model.SchemaInfo;
+import org.apache.cxf.service.model.ServiceInfo;
+import org.apache.cxf.wsdl.WSDLConstants;
+import org.apache.ws.commons.schema.XmlSchema;
+import org.apache.ws.commons.schema.XmlSchemaComplexType;
+import org.apache.ws.commons.schema.XmlSchemaElement;
+import org.apache.ws.commons.schema.XmlSchemaForm;
+import org.apache.ws.commons.schema.XmlSchemaSequence;
+import org.apache.ws.commons.schema.XmlSchemaSequenceMember;
+import org.apache.ws.commons.schema.XmlSchemaSimpleType;
+import org.apache.ws.commons.schema.XmlSchemaSimpleTypeList;
+import org.apache.ws.commons.schema.XmlSchemaType;
+import org.apache.ws.commons.schema.utils.NamespaceMap;
+
+/**
+ * Walks the service model and sets up the element/type names.
+ */
+class JAXBSchemaInitializer extends ServiceModelVisitor {
+    private static final Logger LOG = LogUtils.getLogger(JAXBSchemaInitializer.class);
+
+    private SchemaCollection schemas;
+    private JAXBContextProxy context;
+    private final boolean qualifiedSchemas;
+
+    JAXBSchemaInitializer(ServiceInfo serviceInfo,
+                          SchemaCollection col,
+                          JAXBContext context,
+                          boolean q,
+                          String defaultNs) {
+        super(serviceInfo);
+        schemas = col;
+        this.context = JAXBUtils.createJAXBContextProxy(context, serviceInfo.getXmlSchemaCollection(), defaultNs);
+        this.qualifiedSchemas = q;
+    }
+
+    static Class<?> getArrayComponentType(Type cls) {
+        if (cls instanceof Class) {
+            if (((Class<?>)cls).isArray()) {
+                return ((Class<?>)cls).getComponentType();
+            }
+            return (Class<?>)cls;
+        } else if (cls instanceof ParameterizedType) {
+            for (Type t2 : ((ParameterizedType)cls).getActualTypeArguments()) {
+                return getArrayComponentType(t2);
+            }
+        } else if (cls instanceof GenericArrayType) {
+            GenericArrayType gt = (GenericArrayType)cls;
+            Class<?> ct = (Class<?>) gt.getGenericComponentType();
+            return Array.newInstance(ct, 0).getClass();
+        }
+        return null;
+    }
+
+    public JAXBBeanInfo getBeanInfo(Type cls) {
+        if (cls instanceof Class) {
+            if (((Class<?>)cls).isArray()) {
+                return getBeanInfo(((Class<?>)cls).getComponentType());
+            }
+            return getBeanInfo((Class<?>)cls);
+        } else if (cls instanceof ParameterizedType) {
+            for (Type t2 : ((ParameterizedType)cls).getActualTypeArguments()) {
+                return getBeanInfo(t2);
+            }
+        } else if (cls instanceof GenericArrayType) {
+            GenericArrayType gt = (GenericArrayType)cls;
+            Class<?> ct = (Class<?>) gt.getGenericComponentType();
+            ct = Array.newInstance(ct, 0).getClass();
+
+            return getBeanInfo(ct);
+        }
+
+        return null;
+    }
+
+    public JAXBBeanInfo getBeanInfo(Class<?> cls) {
+        return getBeanInfo(context, cls);
+    }
+
+    public static JAXBBeanInfo getBeanInfo(JAXBContextProxy context, Class<?> cls) {
+        return JAXBUtils.getBeanInfo(context, cls);
+    }
+
+    @Override
+    public void begin(MessagePartInfo part) {
+        // Check to see if the WSDL information has been filled in for us.
+        if (part.getTypeQName() != null || part.getElementQName() != null) {
+            checkForExistence(part);
+            return;
+        }
+
+        Class<?> clazz = part.getTypeClass();
+        if (clazz == null) {
+            return;
+        }
+
+        boolean isFromWrapper = part.getMessageInfo().getOperation().isUnwrapped();
+        boolean isList = false;
+        if (clazz.isArray()) {
+            if (isFromWrapper && !Byte.TYPE.equals(clazz.getComponentType())) {
+                clazz = clazz.getComponentType();
+            } else if (!isFromWrapper) {
+                Annotation[] anns = (Annotation[])part.getProperty("parameter.annotations");
+                for (Annotation a : anns) {
+                    if (a instanceof XmlList) {
+                        part.setProperty("honor.jaxb.annotations", Boolean.TRUE);
+                        clazz = clazz.getComponentType();
+                        isList = true;
+                    }
+                }
+            }
+        }
+
+        Annotation[] anns = (Annotation[])part.getProperty("parameter.annotations");
+        XmlJavaTypeAdapter jta = findFromTypeAdapter(context, clazz, anns);
+        JAXBBeanInfo jtaBeanInfo = null;
+        if (jta != null) {
+            jtaBeanInfo = findFromTypeAdapter(context, jta.value());
+        }
+        JAXBBeanInfo beanInfo = getBeanInfo(clazz);
+        if (jtaBeanInfo != beanInfo && jta != null) {
+            beanInfo = jtaBeanInfo;
+            if (anns == null) {
+                anns = new Annotation[] {jta};
+            } else {
+                boolean found = false;
+                for (Annotation t : anns) {
+                    if (t == jta) {
+                        found = true;
+                    }
+                }
+                if (!found) {
+                    Annotation[] tmp = new Annotation[anns.length + 1];
+                    System.arraycopy(anns, 0, tmp, 0, anns.length);
+                    tmp[anns.length] = jta;
+                    anns = tmp;
+                }
+            }
+            part.setProperty("parameter.annotations", anns);
+            part.setProperty("honor.jaxb.annotations", Boolean.TRUE);
+        }
+        if (beanInfo == null) {
+            if (Exception.class.isAssignableFrom(clazz)) {
+                QName name = (QName)part.getMessageInfo().getProperty("elementName");
+                part.setElementQName(name);
+                buildExceptionType(part, clazz);
+            }
+            return;
+        }
+        boolean isElement = beanInfo.isElement()
+            && !Boolean.TRUE.equals(part.getMessageInfo().getOperation()
+                                        .getProperty("operation.force.types"));
+        boolean hasType = !beanInfo.getTypeNames().isEmpty();
+        if (isElement && isFromWrapper && hasType) {
+            //if there is both a Global element and a global type, AND we are in a wrapper,
+            //make sure we use the type instead of a ref to the element to
+            //match the rules for wrapped/unwrapped
+            isElement = false;
+        }
+
+        part.setElement(isElement);
+
+        if (isElement) {
+            QName name = new QName(beanInfo.getElementNamespaceURI(null),
+                                   beanInfo.getElementLocalName(null));
+            XmlSchemaElement el = schemas.getElementByQName(name);
+            if (el != null && el.getRef().getTarget() != null) {
+                part.setTypeQName(el.getRef().getTargetQName());
+            } else {
+                part.setElementQName(name);
+            }
+            part.setXmlSchema(el);
+        } else  {
+            QName typeName = getTypeName(beanInfo);
+            if (typeName != null) {
+                XmlSchemaType type = schemas.getTypeByQName(typeName);
+                if  (isList && type instanceof XmlSchemaSimpleType) {
+                    XmlSchemaSimpleType simpleType = new XmlSchemaSimpleType(type.getParent(), false);
+                    XmlSchemaSimpleTypeList list = new XmlSchemaSimpleTypeList();
+                    XmlSchemaSimpleType stype = (XmlSchemaSimpleType)type;
+                    list.setItemTypeName(stype.getQName());
+                    simpleType.setContent(list);
+                    part.setXmlSchema(simpleType);
+                    if (part.getConcreteName() == null) {
+                        part.setConcreteName(new QName(null, part.getName().getLocalPart()));
+                    }
+                } else {
+                    part.setTypeQName(typeName);
+                    part.setXmlSchema(type);
+                }
+            }
+        }
+    }
+
+    static XmlJavaTypeAdapter findFromTypeAdapter(JAXBContextProxy context, Class<?> clazz, Annotation[] anns) {
+        if (anns != null) {
+            for (Annotation a : anns) {
+                if (XmlJavaTypeAdapter.class.isAssignableFrom(a.annotationType())) {
+                    JAXBBeanInfo ret = findFromTypeAdapter(context, ((XmlJavaTypeAdapter)a).value());
+                    if (ret != null) {
+                        return (XmlJavaTypeAdapter)a;
+                    }
+                }
+            }
+        }
+        if (clazz != null) {
+            XmlJavaTypeAdapter xjta = clazz.getAnnotation(XmlJavaTypeAdapter.class);
+            if (xjta != null) {
+                JAXBBeanInfo ret = findFromTypeAdapter(context, xjta.value());
+                if (ret != null) {
+                    return xjta;
+                }
+            }
+        }
+        return null;
+    }
+
+    static JAXBBeanInfo findFromTypeAdapter(JAXBContextProxy context,
+                                            @SuppressWarnings("rawtypes")
+                                             Class<? extends XmlAdapter> aclass) {
+        Class<?> c2 = aclass;
+        Type sp = c2.getGenericSuperclass();
+        while (!XmlAdapter.class.equals(c2) && c2 != null) {
+            sp = c2.getGenericSuperclass();
+            c2 = c2.getSuperclass();
+        }
+        if (sp instanceof ParameterizedType) {
+            Type tp = ((ParameterizedType)sp).getActualTypeArguments()[0];
+            if (tp instanceof Class) {
+                return getBeanInfo(context, (Class<?>)tp);
+            }
+        }
+        return null;
+    }
+
+    private QName getTypeName(JAXBBeanInfo beanInfo) {
+        Iterator<QName> itr = beanInfo.getTypeNames().iterator();
+        if (!itr.hasNext()) {
+            return null;
+        }
+
+        return itr.next();
+    }
+    public void checkForExistence(MessagePartInfo part) {
+        QName qn = part.getElementQName();
+        if (qn != null) {
+            XmlSchemaElement el = schemas.getElementByQName(qn);
+            if (el == null) {
+                Class<?> clazz = part.getTypeClass();
+                if (clazz == null) {
+                    return;
+                }
+
+                boolean isFromWrapper = part.getMessageInfo().getOperation().isUnwrapped();
+                if (isFromWrapper && clazz.isArray() && !Byte.TYPE.equals(clazz.getComponentType())) {
+                    clazz = clazz.getComponentType();
+                }
+                JAXBBeanInfo beanInfo = getBeanInfo(clazz);
+                if (beanInfo == null) {
+                    if (Exception.class.isAssignableFrom(clazz)) {
+                        QName name = (QName)part.getMessageInfo().getProperty("elementName");
+                        part.setElementQName(name);
+                        buildExceptionType(part, clazz);
+                    }
+                    return;
+                }
+
+                QName typeName = getTypeName(beanInfo);
+
+                createBridgeXsElement(part, qn, typeName);
+            } else if (part.getXmlSchema() == null) {
+                part.setXmlSchema(el);
+            }
+        }
+    }
+
+    private void createBridgeXsElement(MessagePartInfo part, QName qn, QName typeName) {
+        SchemaInfo schemaInfo = serviceInfo.getSchema(qn.getNamespaceURI());
+        if (schemaInfo != null) {
+            XmlSchemaElement el = schemaInfo.getElementByQName(qn);
+            if (el == null) {
+                createXsElement(schemaInfo.getSchema(), part, typeName, schemaInfo);
+
+            } else if (!typeName.equals(el.getSchemaTypeName())) {
+                throw new Fault(new Message("CANNOT_CREATE_ELEMENT", LOG,
+                                            qn, typeName, el.getSchemaTypeName()));
+            }
+            return;
+        }
+
+        XmlSchema schema = schemas.newXmlSchemaInCollection(qn.getNamespaceURI());
+        if (qualifiedSchemas) {
+            schema.setElementFormDefault(XmlSchemaForm.QUALIFIED);
+        }
+        schemaInfo = new SchemaInfo(qn.getNamespaceURI(), qualifiedSchemas, false);
+        schemaInfo.setSchema(schema);
+
+        createXsElement(schema, part, typeName, schemaInfo);
+
+        NamespaceMap nsMap = new NamespaceMap();
+        nsMap.add(WSDLConstants.CONVENTIONAL_TNS_PREFIX, schema.getTargetNamespace());
+        nsMap.add(WSDLConstants.NP_SCHEMA_XSD, WSDLConstants.NS_SCHEMA_XSD);
+        schema.setNamespaceContext(nsMap);
+
+        serviceInfo.addSchema(schemaInfo);
+    }
+
+    private XmlSchemaElement createXsElement(XmlSchema schema,
+                                             MessagePartInfo part,
+                                             QName typeName, SchemaInfo schemaInfo) {
+        XmlSchemaElement el = new XmlSchemaElement(schema, true);
+        el.setName(part.getElementQName().getLocalPart());
+        el.setNillable(true);
+        el.setSchemaTypeName(typeName);
+        part.setXmlSchema(el);
+        schemaInfo.setElement(null);
+        return el;
+    }
+
+    public void end(FaultInfo fault) {
+        MessagePartInfo part = fault.getFirstMessagePart();
+        Class<?> cls = part.getTypeClass();
+        Class<?> cl2 = (Class<?>)fault.getProperty(Class.class.getName());
+        if (cls != cl2) {
+            QName name = (QName)fault.getProperty("elementName");
+            part.setElementQName(name);
+            JAXBBeanInfo beanInfo = getBeanInfo(cls);
+            if (beanInfo == null) {
+                throw new Fault(new Message("NO_BEAN_INFO", LOG, cls.getName()));
+            }
+            SchemaInfo schemaInfo = serviceInfo.getSchema(part.getElementQName().getNamespaceURI());
+            if (schemaInfo != null
+                && !isExistSchemaElement(schemaInfo.getSchema(), part.getElementQName())) {
+
+                XmlSchemaElement el = new XmlSchemaElement(schemaInfo.getSchema(), true);
+                el.setName(part.getElementQName().getLocalPart());
+                el.setNillable(true);
+
+                schemaInfo.setElement(null);
+
+                Iterator<QName> itr = beanInfo.getTypeNames().iterator();
+                if (!itr.hasNext()) {
+                    return;
+                }
+                QName typeName = itr.next();
+                el.setSchemaTypeName(typeName);
+            }
+        } else if (part.getXmlSchema() == null) {
+            try {
+                cls.getConstructor(new Class[] {String.class});
+            } catch (Exception e) {
+                try {
+                    cls.getConstructor(new Class[0]);
+                } catch (Exception e2) {
+                    //no String or default constructor, we cannot use it
+                    return;
+                }
+            }
+
+            //not mappable in JAXBContext directly, we'll have to do it manually :-(
+            SchemaInfo schemaInfo = serviceInfo.getSchema(part.getElementQName().getNamespaceURI());
+            if (schemaInfo == null
+                || isExistSchemaElement(schemaInfo.getSchema(), part.getElementQName())) {
+                return;
+            }
+
+            XmlSchemaElement el = new XmlSchemaElement(schemaInfo.getSchema(), true);
+            el.setName(part.getElementQName().getLocalPart());
+
+            schemaInfo.setElement(null);
+
+            part.setXmlSchema(el);
+
+            XmlSchemaComplexType ct = new XmlSchemaComplexType(schemaInfo.getSchema(), false);
+            el.setSchemaType(ct);
+            XmlSchemaSequence seq = new XmlSchemaSequence();
+            ct.setParticle(seq);
+
+            Method[] methods = cls.getMethods();
+            for (Method m : methods) {
+                if (m.getName().startsWith("get")
+                    || m.getName().startsWith("is")) {
+                    int beginIdx = m.getName().startsWith("get") ? 3 : 2;
+                    try {
+                        m.getDeclaringClass().getMethod("set" + m.getName().substring(beginIdx),
+                                                        m.getReturnType());
+
+                        JAXBBeanInfo beanInfo = getBeanInfo(m.getReturnType());
+                        if (beanInfo != null) {
+                            el = new XmlSchemaElement(schemaInfo.getSchema(), false);
+                            el.setName(m.getName().substring(beginIdx));
+                            Iterator<QName> itr = beanInfo.getTypeNames().iterator();
+                            if (!itr.hasNext()) {
+                                return;
+                            }
+                            QName typeName = itr.next();
+                            el.setSchemaTypeName(typeName);
+                        }
+
+                        seq.getItems().add(el);
+                    } catch (Exception e) {
+                        //not mappable
+                    }
+                }
+            }
+        }
+    }
+
+
+    private void buildExceptionType(MessagePartInfo part, Class<?> cls) {
+        SchemaInfo schemaInfo = null;
+        for (SchemaInfo s : serviceInfo.getSchemas()) {
+            if (s.getNamespaceURI().equals(part.getElementQName().getNamespaceURI())) {
+                schemaInfo = s;
+                break;
+            }
+        }
+        XmlAccessorOrder xmlAccessorOrder = cls.getAnnotation(XmlAccessorOrder.class);
+        XmlType xmlTypeAnno = cls.getAnnotation(XmlType.class);
+        String[] propertyOrder = null;
+        boolean respectXmlTypeNS = false;
+        XmlSchema faultBeanSchema = null;
+        if (xmlTypeAnno != null && !StringUtils.isEmpty(xmlTypeAnno.namespace())
+            && !xmlTypeAnno.namespace().equals(part.getElementQName().getNamespaceURI())) {
+            respectXmlTypeNS = true;
+            NamespaceMap nsMap = new NamespaceMap();
+            nsMap.add(WSDLConstants.CONVENTIONAL_TNS_PREFIX, xmlTypeAnno.namespace());
+            nsMap.add(WSDLConstants.NP_SCHEMA_XSD, WSDLConstants.NS_SCHEMA_XSD);
+
+            SchemaInfo faultBeanSchemaInfo = createSchemaIfNeeded(xmlTypeAnno.namespace(), nsMap);
+            faultBeanSchema = faultBeanSchemaInfo.getSchema();
+        }
+
+        if (xmlTypeAnno != null &&  xmlTypeAnno.propOrder().length > 0) {
+            propertyOrder = xmlTypeAnno.propOrder();
+            //TODO: handle @XmlAccessOrder
+        }
+
+        if (schemaInfo == null) {
+            NamespaceMap nsMap = new NamespaceMap();
+            nsMap.add(WSDLConstants.CONVENTIONAL_TNS_PREFIX, part.getElementQName().getNamespaceURI());
+            nsMap.add(WSDLConstants.NP_SCHEMA_XSD, WSDLConstants.NS_SCHEMA_XSD);
+            schemaInfo = createSchemaIfNeeded(part.getElementQName().getNamespaceURI(), nsMap);
+
+        }
+        XmlSchema schema = schemaInfo.getSchema();
+
+
+        // Before updating everything, make sure we haven't added this
+        // type yet.  Multiple methods that throw the same exception
+        // types will cause duplicates.
+        String faultTypeName = xmlTypeAnno != null && !StringUtils.isEmpty(xmlTypeAnno.name())
+               ? xmlTypeAnno.name()  :  part.getElementQName().getLocalPart();
+        XmlSchemaType existingType = schema.getTypeByName(faultTypeName);
+        if (existingType != null) {
+            return;
+        }
+
+        XmlSchemaElement el = new XmlSchemaElement(schema, true);
+        el.setName(part.getElementQName().getLocalPart());
+        part.setXmlSchema(el);
+        schemaInfo.setElement(null);
+
+        if (respectXmlTypeNS) {
+            schema = faultBeanSchema; //create complexType in the new created schema for xmlType
+        }
+
+        XmlSchemaComplexType ct = new XmlSchemaComplexType(schema, true);
+        ct.setName(faultTypeName);
+
+        el.setSchemaTypeName(ct.getQName());
+
+        XmlSchemaSequence seq = new XmlSchemaSequence();
+        ct.setParticle(seq);
+        String namespace = part.getElementQName().getNamespaceURI();
+        XmlAccessType accessType = Utils.getXmlAccessType(cls);
+//
+        for (Field f : Utils.getFields(cls, accessType)) {
+            //map field
+            Type type = Utils.getFieldType(f);
+            //we want to return the right type for collections so if we get null
+            //from the return type we check if it's ParameterizedType and get the
+            //generic return type.
+            if ((type == null) && (f.getGenericType() instanceof ParameterizedType)) {
+                type = f.getGenericType();
+            }
+            if (generateGenericType(type)) {
+                buildGenericElements(schema, seq, f);
+            } else {
+                JAXBBeanInfo beanInfo = getBeanInfo(type);
+                if (beanInfo != null) {
+                    XmlElement xmlElementAnno = f.getAnnotation(XmlElement.class);
+                    addElement(schema, seq, beanInfo, new QName(namespace, f.getName()), isArray(type), xmlElementAnno);
+                }
+            }
+        }
+        for (Method m : Utils.getGetters(cls, accessType)) {
+            //map method
+            Type type = Utils.getMethodReturnType(m);
+            // we want to return the right type for collections so if we get null
+            // from the return type we check if it's ParameterizedType and get the
+            // generic return type.
+            if ((type == null) && (m.getGenericReturnType() instanceof ParameterizedType)) {
+                type = m.getGenericReturnType();
+            }
+
+            if (generateGenericType(type)) {
+                buildGenericElements(schema, seq, m, type);
+            } else {
+                JAXBBeanInfo beanInfo = getBeanInfo(type);
+                if (beanInfo != null) {
+                    int idx = m.getName().startsWith("get") ? 3 : 2;
+                    String name = m.getName().substring(idx);
+                    name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
+                    XmlElement xmlElementAnno = m.getAnnotation(XmlElement.class);
+                    addElement(schema, seq, beanInfo, new QName(namespace, name), isArray(type), xmlElementAnno);
+                }
+            }
+        }
+        // Create element in xsd:sequence for Exception.class
+        if (Exception.class.isAssignableFrom(cls)) {
+            addExceptionMessage(cls, schema, seq);
+        }
+
+        if (propertyOrder != null) {
+            if (propertyOrder.length == seq.getItems().size()) {
+                sortItems(seq, propertyOrder);
+            } else if (propertyOrder.length > 1
+                || (propertyOrder.length == 1 && !propertyOrder[0].isEmpty())) {
+                LOG.log(Level.WARNING, "propOrder in @XmlType doesn't define all schema elements :"
+                    + Arrays.toString(propertyOrder));
+            }
+        }
+
+        if (xmlAccessorOrder != null && xmlAccessorOrder.value().equals(XmlAccessOrder.ALPHABETICAL)
+            && propertyOrder == null) {
+            sort(seq);
+        }
+
+        schemas.addCrossImports();
+        part.setProperty(JAXBDataBinding.class.getName() + ".CUSTOM_EXCEPTION", Boolean.TRUE);
+    }
+    private void addExceptionMessage(Class<?> cls, XmlSchema schema, XmlSchemaSequence seq) {
+        try {
+            //a subclass could mark the message method as transient
+            Method m = cls.getMethod("getMessage");
+            if (!m.isAnnotationPresent(XmlTransient.class)
+                && m.getDeclaringClass().equals(Throwable.class)) {
+                JAXBBeanInfo beanInfo = getBeanInfo(java.lang.String.class);
+                XmlSchemaElement exEle = new XmlSchemaElement(schema, false);
+                exEle.setName("message");
+                exEle.setSchemaTypeName(getTypeName(beanInfo));
+                exEle.setMinOccurs(0);
+                seq.getItems().add(exEle);
+            }
+        } catch (Exception e) {
+            //ignore, just won't have the message element
+        }
+    }
+
+    private boolean generateGenericType(Type type) {
+        if (type instanceof ParameterizedType) {
+            ParameterizedType paramType = (ParameterizedType)type;
+            if (paramType.getActualTypeArguments().length > 1) {
+                return true;
+
+            }
+        }
+        return false;
+    }
+
+    private void buildGenericElements(XmlSchema schema, XmlSchemaSequence seq, Field f) {
+        XmlSchemaComplexType generics = new XmlSchemaComplexType(schema, true);
+        Type type = f.getGenericType();
+        String rawType = ((ParameterizedType)type).getRawType().toString();
+        String typeName = StringUtils.uncapitalize(rawType.substring(rawType.lastIndexOf('.') + 1));
+        generics.setName(typeName);
+
+        Class<?> genericsClass = f.getType();
+        buildGenericSeq(schema, generics, genericsClass);
+
+        String name = Character.toLowerCase(f.getName().charAt(0)) + f.getName().substring(1);
+        XmlSchemaElement newel = new XmlSchemaElement(schema, false);
+        newel.setName(name);
+        newel.setSchemaTypeName(generics.getQName());
+        newel.setMinOccurs(0);
+        if (!seq.getItems().contains(newel)) {
+            seq.getItems().add(newel);
+        }
+    }
+
+    private void buildGenericElements(XmlSchema schema, XmlSchemaSequence seq, Method m, Type type) {
+        String rawType = ((ParameterizedType)type).getRawType().toString();
+        String typeName = StringUtils.uncapitalize(rawType.substring(rawType.lastIndexOf('.') + 1));
+
+        XmlSchemaComplexType generics = (XmlSchemaComplexType)schema.getTypeByName(typeName);
+        if (generics == null) {
+            generics = new XmlSchemaComplexType(schema, true);
+            generics.setName(typeName);
+        }
+
+        Class<?> genericsClass = m.getReturnType();
+        buildGenericSeq(schema, generics, genericsClass);
+
+        int idx = m.getName().startsWith("get") ? 3 : 2;
+        String name = m.getName().substring(idx);
+        name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
+        XmlSchemaElement newel = new XmlSchemaElement(schema, false);
+        newel.setName(name);
+        newel.setSchemaTypeName(generics.getQName());
+        newel.setMinOccurs(0);
+        if (!seq.getItems().contains(newel)) {
+            seq.getItems().add(newel);
+        }
+    }
+
+    private void buildGenericSeq(XmlSchema schema, XmlSchemaComplexType generics, Class<?> genericsClass) {
+        XmlSchemaSequence genericsSeq = new XmlSchemaSequence();
+        generics.setParticle(genericsSeq);
+        XmlAccessType accessType = Utils.getXmlAccessType(genericsClass);
+
+        for (Field f : Utils.getFields(genericsClass, accessType)) {
+            if (f.getGenericType() instanceof TypeVariable) {
+                String genericName = Character.toLowerCase(f.getName().charAt(0)) + f.getName().substring(1);
+                XmlSchemaElement genericEle = new XmlSchemaElement(schema, false);
+                genericEle.setName(genericName);
+                genericEle.setMinOccurs(0);
+                JAXBBeanInfo anyBean = getBeanInfo(context, f.getType());
+                Iterator<QName> itr = anyBean.getTypeNames().iterator();
+                if (!itr.hasNext()) {
+                    return;
+                }
+                QName typeName = itr.next();
+                genericEle.setSchemaTypeName(typeName);
+                genericsSeq.getItems().add(genericEle);
+            }
+        }
+
+        for (Method genericMethod : Utils.getGetters(genericsClass, accessType)) {
+            if (genericMethod.getGenericReturnType() instanceof TypeVariable) {
+                int idx = genericMethod.getName().startsWith("get") ? 3 : 2;
+                String genericName = genericMethod.getName().substring(idx);
+                genericName = Character.toLowerCase(genericName.charAt(0)) + genericName.substring(1);
+                XmlSchemaElement genericEle = new XmlSchemaElement(schema, false);
+                genericEle.setName(genericName);
+                genericEle.setMinOccurs(0);
+                JAXBBeanInfo anyBean = getBeanInfo(context, genericMethod.getReturnType());
+                Iterator<QName> itr = anyBean.getTypeNames().iterator();
+                if (!itr.hasNext()) {
+                    return;
+                }
+                QName typeName = itr.next();
+                genericEle.setSchemaTypeName(typeName);
+                genericsSeq.getItems().add(genericEle);
+            }
+
+        }
+    }
+
+
+    static boolean isArray(Type cls) {
+        if (cls instanceof Class) {
+            return ((Class<?>)cls).isArray();
+        } else if (cls instanceof ParameterizedType) {
+            ParameterizedType pt = (ParameterizedType)cls;
+            return pt.getActualTypeArguments().length == 1
+                && pt.getRawType() instanceof Class
+                && Collection.class.isAssignableFrom((Class<?>)pt.getRawType());
+        } else if (cls instanceof GenericArrayType) {
+            return true;
+        }
+        return false;
+    }
+
+    protected void addElement(XmlSchema schema,
+                              XmlSchemaSequence seq, JAXBBeanInfo beanInfo,
+                              QName name, boolean isArray, XmlElement xmlElementAnno) {
+        XmlSchemaElement el = new XmlSchemaElement(schema, false);
+        if (isArray) {
+            el.setMinOccurs(0);
+            el.setMaxOccurs(Long.MAX_VALUE);
+        } else {
+            if (xmlElementAnno == null) {
+                el.setMinOccurs(0);
+                el.setNillable(false);
+            } else {
+                el.setNillable(xmlElementAnno.nillable());
+                int minOccurs = xmlElementAnno.required() ? 1 : 0;
+                el.setMinOccurs(minOccurs);
+            }
+        }
+
+        if (beanInfo.isElement()) {
+            QName ename = new QName(beanInfo.getElementNamespaceURI(null),
+                                   beanInfo.getElementLocalName(null));
+            XmlSchemaElement el2 = schemas.getElementByQName(ename);
+            el.setNillable(false);
+            el.getRef().setTargetQName(el2.getQName());
+        } else {
+            if (xmlElementAnno != null && !StringUtils.isEmpty(xmlElementAnno.name())) {
+                el.setName(xmlElementAnno.name());
+            } else {
+                el.setName(name.getLocalPart());
+            }
+            Iterator<QName> itr = beanInfo.getTypeNames().iterator();
+            if (!itr.hasNext()) {
+                return;
+            }
+            QName typeName = itr.next();
+            el.setSchemaTypeName(typeName);
+        }
+
+        seq.getItems().add(el);
+    }
+
+    private SchemaInfo createSchemaIfNeeded(String namespace, NamespaceMap nsMap) {
+        SchemaInfo schemaInfo = serviceInfo.getSchema(namespace);
+        if (schemaInfo == null) {
+            XmlSchema xmlSchema = schemas.newXmlSchemaInCollection(namespace);
+
+            if (qualifiedSchemas) {
+                xmlSchema.setElementFormDefault(XmlSchemaForm.QUALIFIED);
+            }
+
+            xmlSchema.setNamespaceContext(nsMap);
+
+            schemaInfo = new SchemaInfo(namespace);
+            schemaInfo.setSchema(xmlSchema);
+            serviceInfo.addSchema(schemaInfo);
+        }
+        return schemaInfo;
+    }
+
+    private boolean isExistSchemaElement(XmlSchema schema, QName qn) {
+        return schema.getElementByName(qn) != null;
+    }
+
+    private void sortItems(final XmlSchemaSequence seq, final String[] propertyOrder) {
+        final List<String> propList = Arrays.asList(propertyOrder);
+        Collections.sort(seq.getItems(), new Comparator<XmlSchemaSequenceMember>() {
+            public int compare(XmlSchemaSequenceMember o1, XmlSchemaSequenceMember o2) {
+                XmlSchemaElement element1 = (XmlSchemaElement)o1;
+                XmlSchemaElement element2 = (XmlSchemaElement)o2;
+                int index1 = propList.indexOf(element1.getName());
+                int index2 = propList.indexOf(element2.getName());
+                return index1 - index2;
+            }
+
+        });
+    }
+    //sort to Alphabetical order
+    private void sort(final XmlSchemaSequence seq) {
+        Collections.sort(seq.getItems(), new Comparator<XmlSchemaSequenceMember>() {
+            public int compare(XmlSchemaSequenceMember o1, XmlSchemaSequenceMember o2) {
+                XmlSchemaElement element1 = (XmlSchemaElement)o1;
+                XmlSchemaElement element2 = (XmlSchemaElement)o2;
+                return element1.getName().compareTo(element2.getName());
+            }
+
+        });
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxb/io/DataReaderImpl.java b/transform/src/patch/java/org/apache/cxf/jaxb/io/DataReaderImpl.java
new file mode 100644
index 0000000..ef8a15b
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxb/io/DataReaderImpl.java
@@ -0,0 +1,207 @@
+/**
+ * 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.jaxb.io;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.PropertyException;
+import javax.xml.bind.UnmarshalException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.ValidationEvent;
+import javax.xml.bind.ValidationEventHandler;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import javax.xml.namespace.QName;
+
+import org.apache.cxf.common.i18n.Message;
+import org.apache.cxf.common.jaxb.JAXBUtils;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.databinding.DataReader;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.jaxb.JAXBDataBase;
+import org.apache.cxf.jaxb.JAXBDataBinding;
+import org.apache.cxf.jaxb.JAXBEncoderDecoder;
+import org.apache.cxf.jaxb.UnmarshallerEventHandler;
+import org.apache.cxf.message.MessageUtils;
+import org.apache.cxf.service.model.MessagePartInfo;
+
+public class DataReaderImpl<T> extends JAXBDataBase implements DataReader<T> {
+    private static final Logger LOG = LogUtils.getLogger(JAXBDataBinding.class);
+    JAXBDataBinding databinding;
+    boolean unwrapJAXBElement;
+    ValidationEventHandler veventHandler;
+    boolean setEventHandler = true;
+
+    public DataReaderImpl(JAXBDataBinding binding, boolean unwrap) {
+        super(binding.getContext());
+        unwrapJAXBElement = unwrap;
+        databinding = binding;
+    }
+
+    public Object read(T input) {
+        return read(null, input);
+    }
+
+    private static class WSUIDValidationHandler implements ValidationEventHandler {
+        ValidationEventHandler origHandler;
+        WSUIDValidationHandler(ValidationEventHandler o) {
+            origHandler = o;
+        }
+
+        public boolean handleEvent(ValidationEvent event) {
+            // if the original handler has already handled the event, no need for us
+            // to do anything, otherwise if not yet handled, then do this 'hack'
+            if (origHandler != null && origHandler.handleEvent(event)) {
+                return true;
+            }
+            // hack for CXF-3453
+            String msg = event.getMessage();
+            return msg != null
+                && msg.contains(":Id")
+                && (msg.startsWith("cvc-type.3.1.1")
+                    || msg.startsWith("cvc-type.3.2.2")
+                    || msg.startsWith("cvc-complex-type.3.1.1")
+                    || msg.startsWith("cvc-complex-type.3.2.2"));
+        }
+    }
+
+    public void setProperty(String prop, Object value) {
+        if (prop.equals(JAXBDataBinding.UNWRAP_JAXB_ELEMENT)) {
+            unwrapJAXBElement = Boolean.TRUE.equals(value);
+        } else if (prop.equals(org.apache.cxf.message.Message.class.getName())) {
+            org.apache.cxf.message.Message m = (org.apache.cxf.message.Message)value;
+            veventHandler = getValidationEventHandler(m, JAXBDataBinding.READER_VALIDATION_EVENT_HANDLER);
+            if (veventHandler == null) {
+                veventHandler = databinding.getValidationEventHandler();
+            }
+            setEventHandler = MessageUtils.getContextualBoolean(m,
+                    JAXBDataBinding.SET_VALIDATION_EVENT_HANDLER, true);
+
+            Object unwrapProperty = m.get(JAXBDataBinding.UNWRAP_JAXB_ELEMENT);
+            if (unwrapProperty == null) {
+                unwrapProperty = m.getExchange().get(JAXBDataBinding.UNWRAP_JAXB_ELEMENT);
+            }
+            if (unwrapProperty != null) {
+                unwrapJAXBElement = Boolean.TRUE.equals(unwrapProperty);
+            }
+        }
+    }
+
+    private Unmarshaller createUnmarshaller() {
+        try {
+            Unmarshaller um = context.createUnmarshaller();
+            if (databinding.getUnmarshallerListener() != null) {
+                um.setListener(databinding.getUnmarshallerListener());
+            }
+            if (setEventHandler) {
+                um.setEventHandler(new WSUIDValidationHandler(veventHandler));
+            }
+            if (databinding.getUnmarshallerProperties() != null) {
+                for (Map.Entry<String, Object> propEntry
+                    : databinding.getUnmarshallerProperties().entrySet()) {
+                    try {
+                        um.setProperty(propEntry.getKey(), propEntry.getValue());
+                    } catch (PropertyException pe) {
+                        LOG.log(Level.INFO, "PropertyException setting Marshaller properties", pe);
+                    }
+                }
+            }
+            um.setSchema(schema);
+            um.setAttachmentUnmarshaller(getAttachmentUnmarshaller());
+            for (XmlAdapter<?, ?> adapter : databinding.getConfiguredXmlAdapters()) {
+                um.setAdapter(adapter);
+            }
+            return um;
+        } catch (javax.xml.bind.UnmarshalException ex) {
+            throw new Fault(new Message("UNMARSHAL_ERROR", LOG, ex.getLinkedException()
+                .getMessage()), ex);
+        } catch (JAXBException ex) {
+            throw new Fault(new Message("UNMARSHAL_ERROR", LOG, ex.getMessage()), ex);
+        }
+    }
+
+    public Object read(MessagePartInfo part, T reader) {
+        boolean honorJaxbAnnotation = honorJAXBAnnotations(part);
+        if (honorJaxbAnnotation) {
+            Annotation[] anns = getJAXBAnnotation(part);
+            if (anns.length > 0) {
+                // RpcLit will use the JAXB Bridge to unmarshall part message when it is
+                // annotated with @XmlList,@XmlAttachmentRef,@XmlJavaTypeAdapter
+                // TODO:Cache the JAXBRIContext
+                QName qname = new QName(null, part.getConcreteName().getLocalPart());
+
+                Object obj = JAXBEncoderDecoder.unmarshalWithBridge(qname,
+                                                              part.getTypeClass(),
+                                                              anns,
+                                                              databinding.getContextClasses(),
+                                                              reader,
+                                                              getAttachmentUnmarshaller());
+
+                onCompleteUnmarshalling();
+
+                return obj;
+            }
+        }
+
+        Unmarshaller um = createUnmarshaller();
+        try {
+            Object obj = JAXBEncoderDecoder.unmarshall(um, reader, part,
+                                                 unwrapJAXBElement);
+            onCompleteUnmarshalling();
+
+            return obj;
+        } finally {
+            JAXBUtils.closeUnmarshaller(um);
+        }
+    }
+
+    public Object read(QName name, T input, Class<?> type) {
+        Unmarshaller um = createUnmarshaller();
+
+        try {
+            Object obj = JAXBEncoderDecoder.unmarshall(um, input,
+                                             name, type,
+                                             unwrapJAXBElement);
+            onCompleteUnmarshalling();
+
+            return obj;
+        } finally {
+            JAXBUtils.closeUnmarshaller(um);
+        }
+
+    }
+
+    private void onCompleteUnmarshalling() {
+        if (setEventHandler && veventHandler instanceof UnmarshallerEventHandler) {
+            try {
+                ((UnmarshallerEventHandler) veventHandler).onUnmarshalComplete();
+            } catch (UnmarshalException e) {
+                if (e.getLinkedException() != null) {
+                    throw new Fault(new Message("UNMARSHAL_ERROR", LOG,
+                            e.getLinkedException().getMessage()), e);
+                }
+                throw new Fault(new Message("UNMARSHAL_ERROR", LOG, e.getMessage()), e);
+            }
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxb/io/DataWriterImpl.java b/transform/src/patch/java/org/apache/cxf/jaxb/io/DataWriterImpl.java
new file mode 100644
index 0000000..c7de57f
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxb/io/DataWriterImpl.java
@@ -0,0 +1,321 @@
+/**
+ * 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.jaxb.io;
+
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.MarshalException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.PropertyException;
+import javax.xml.bind.ValidationEvent;
+import javax.xml.bind.ValidationEventHandler;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import javax.xml.bind.attachment.AttachmentMarshaller;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.i18n.Message;
+import org.apache.cxf.common.jaxb.JAXBUtils;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.ReflectionUtil;
+import org.apache.cxf.databinding.DataWriter;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.jaxb.JAXBDataBase;
+import org.apache.cxf.jaxb.JAXBDataBinding;
+import org.apache.cxf.jaxb.JAXBEncoderDecoder;
+import org.apache.cxf.jaxb.MarshallerEventHandler;
+import org.apache.cxf.jaxb.attachment.JAXBAttachmentMarshaller;
+import org.apache.cxf.message.MessageUtils;
+import org.apache.cxf.service.model.MessagePartInfo;
+import org.apache.ws.commons.schema.XmlSchemaElement;
+
+public class DataWriterImpl<T> extends JAXBDataBase implements DataWriter<T> {
+    private static final Logger LOG = LogUtils.getLogger(JAXBDataBinding.class);
+
+    ValidationEventHandler veventHandler;
+    boolean setEventHandler = true;
+    boolean noEscape;
+    private JAXBDataBinding databinding;
+    private Bus bus;
+
+    public DataWriterImpl(Bus bus, JAXBDataBinding binding) {
+        this(bus, binding, false);
+    }
+    public DataWriterImpl(Bus bus, JAXBDataBinding binding, boolean noEsc) {
+        super(binding.getContext());
+        databinding = binding;
+        noEscape = noEsc;
+        this.bus = bus;
+    }
+
+    public void write(Object obj, T output) {
+        write(obj, null, output);
+    }
+
+    public void setProperty(String prop, Object value) {
+        if (prop.equals(org.apache.cxf.message.Message.class.getName())) {
+            org.apache.cxf.message.Message m = (org.apache.cxf.message.Message)value;
+            veventHandler = getValidationEventHandler(m, JAXBDataBinding.WRITER_VALIDATION_EVENT_HANDLER);
+            if (veventHandler == null) {
+                veventHandler = databinding.getValidationEventHandler();
+            }
+            setEventHandler = MessageUtils.getContextualBoolean(m,
+                    JAXBDataBinding.SET_VALIDATION_EVENT_HANDLER, true);
+        }
+    }
+
+    private static class MtomValidationHandler implements ValidationEventHandler {
+        ValidationEventHandler origHandler;
+        JAXBAttachmentMarshaller marshaller;
+        MtomValidationHandler(ValidationEventHandler v,
+                                     JAXBAttachmentMarshaller m) {
+            origHandler = v;
+            marshaller = m;
+        }
+
+        public boolean handleEvent(ValidationEvent event) {
+            // CXF-1194/CXF-7438 this hack is specific to MTOM, so pretty safe to leave in
+            // here before calling the origHandler.
+            String msg = event.getMessage();
+            if ((msg.startsWith("cvc-type.3.1.2") || msg.startsWith("cvc-complex-type.2.2"))
+                && msg.contains(marshaller.getLastMTOMElementName().getLocalPart())) {
+                return true;
+            }
+
+            if (origHandler != null) {
+                return origHandler.handleEvent(event);
+            }
+            return false;
+        }
+
+    }
+
+    public Marshaller createMarshaller(Object elValue, MessagePartInfo part) {
+        //Class<?> cls = null;
+        //if (part != null) {
+        //    cls = part.getTypeClass();
+        //}
+        //
+        //if (cls == null) {
+        //    cls = null != elValue ? elValue.getClass() : null;
+        //}
+        //
+        //if (cls != null && cls.isArray() && elValue instanceof Collection) {
+        //    Collection<?> col = (Collection<?>)elValue;
+        //    elValue = col.toArray((Object[])Array.newInstance(cls.getComponentType(), col.size()));
+        //}
+        Marshaller marshaller;
+        try {
+
+            marshaller = context.createMarshaller();
+            marshaller.setProperty(Marshaller.JAXB_ENCODING, StandardCharsets.UTF_8.name());
+            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
+            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.FALSE);
+            marshaller.setListener(databinding.getMarshallerListener());
+            databinding.applyEscapeHandler(!noEscape, eh -> JAXBUtils.setEscapeHandler(marshaller, eh));
+
+            if (setEventHandler) {
+                ValidationEventHandler h = veventHandler;
+                if (veventHandler == null) {
+                    h = new ValidationEventHandler() {
+                        public boolean handleEvent(ValidationEvent event) {
+                            //continue on warnings only
+                            return event.getSeverity() == ValidationEvent.WARNING;
+                        }
+                    };
+                }
+                marshaller.setEventHandler(h);
+            }
+
+            final Map<String, String> nspref = databinding.getDeclaredNamespaceMappings();
+            final Map<String, String> nsctxt = databinding.getContextualNamespaceMap();
+            // set the prefix mapper if either of the prefix map is configured
+            if (nspref != null || nsctxt != null) {
+                Object mapper = JAXBUtils.setNamespaceMapper(bus, nspref != null ? nspref : nsctxt, marshaller);
+                if (nsctxt != null) {
+                    setContextualNamespaceDecls(mapper, nsctxt);
+                }
+            }
+            if (databinding.getMarshallerProperties() != null) {
+                for (Map.Entry<String, Object> propEntry
+                    : databinding.getMarshallerProperties().entrySet()) {
+                    try {
+                        marshaller.setProperty(propEntry.getKey(), propEntry.getValue());
+                    } catch (PropertyException pe) {
+                        LOG.log(Level.INFO, "PropertyException setting Marshaller properties", pe);
+                    }
+                }
+            }
+
+            marshaller.setSchema(schema);
+            AttachmentMarshaller atmarsh = getAttachmentMarshaller();
+            marshaller.setAttachmentMarshaller(atmarsh);
+
+            if (schema != null
+                && atmarsh instanceof JAXBAttachmentMarshaller) {
+                //we need a special even handler for XOP attachments
+                marshaller.setEventHandler(new MtomValidationHandler(marshaller.getEventHandler(),
+                                                            (JAXBAttachmentMarshaller)atmarsh));
+            }
+        } catch (javax.xml.bind.MarshalException ex) {
+            Message faultMessage = new Message("MARSHAL_ERROR", LOG, ex.getLinkedException()
+                .getMessage());
+            throw new Fault(faultMessage, ex);
+        } catch (JAXBException ex) {
+            throw new Fault(new Message("MARSHAL_ERROR", LOG, ex.getMessage()), ex);
+        }
+        for (XmlAdapter<?, ?> adapter : databinding.getConfiguredXmlAdapters()) {
+            marshaller.setAdapter(adapter);
+        }
+        return marshaller;
+    }
+
+    //REVISIT should this go into JAXBUtils?
+    private static void setContextualNamespaceDecls(Object mapper, Map<String, String> nsctxt) {
+        try {
+            Method m = ReflectionUtil.getDeclaredMethod(mapper.getClass(),
+                                                        "setContextualNamespaceDecls", new Class<?>[]{String[].class});
+            String[] args = new String[nsctxt.size() * 2];
+            int ai = 0;
+            for (Entry<String, String> nsp : nsctxt.entrySet()) {
+                args[ai++] = nsp.getValue();
+                args[ai++] = nsp.getKey();
+            }
+            m.invoke(mapper, new Object[]{args});
+        } catch (Exception e) {
+            // ignore
+            LOG.log(Level.WARNING, "Failed to set the contextual namespace map", e);
+        }
+
+    }
+
+    public void write(Object obj, MessagePartInfo part, T output) {
+        boolean honorJaxbAnnotation = honorJAXBAnnotations(part);
+        if (part != null && !part.isElement() && part.getTypeClass() != null) {
+            honorJaxbAnnotation = true;
+        }
+        checkPart(part, obj);
+
+        if (obj != null
+            || !(part.getXmlSchema() instanceof XmlSchemaElement)) {
+
+            if (obj instanceof Exception
+                && part != null
+                && Boolean.TRUE.equals(part.getProperty(JAXBDataBinding.class.getName()
+                                                        + ".CUSTOM_EXCEPTION"))) {
+                JAXBEncoderDecoder.marshallException(createMarshaller(obj, part),
+                                                     (Exception)obj,
+                                                     part,
+                                                     output);
+                onCompleteMarshalling();
+            } else {
+                Annotation[] anns = getJAXBAnnotation(part);
+                if (!honorJaxbAnnotation || anns.length == 0) {
+                    JAXBEncoderDecoder.marshall(createMarshaller(obj, part), obj, part, output);
+                    onCompleteMarshalling();
+                } else if (honorJaxbAnnotation && anns.length > 0) {
+                    //RpcLit will use the JAXB Bridge to marshall part message when it is
+                    //annotated with @XmlList,@XmlAttachmentRef,@XmlJavaTypeAdapter
+                    //TODO:Cache the JAXBRIContext
+
+                    JAXBEncoderDecoder.marshalWithBridge(part.getConcreteName(),
+                                                         part.getTypeClass(),
+                                                         anns,
+                                                         databinding.getContextClasses(),
+                                                         obj,
+                                                         output,
+                                                         getAttachmentMarshaller());
+                }
+            }
+        } else if (needToRender(part)) {
+            JAXBEncoderDecoder.marshallNullElement(createMarshaller(null, part),
+                                                   output, part);
+
+            onCompleteMarshalling();
+        }
+    }
+
+    private void checkPart(MessagePartInfo part, Object object) {
+        if (part == null || part.getTypeClass() == null || object == null) {
+            return;
+        }
+        Class<?> typeClass = part.getTypeClass();
+        if (typeClass == null) {
+            return;
+        }
+        if (typeClass.isPrimitive()) {
+            if (typeClass == Long.TYPE) {
+                typeClass = Long.class;
+            } else if (typeClass == Integer.TYPE) {
+                typeClass = Integer.class;
+            } else if (typeClass == Short.TYPE) {
+                typeClass = Short.class;
+            } else if (typeClass == Byte.TYPE) {
+                typeClass = Byte.class;
+            } else if (typeClass == Character.TYPE) {
+                typeClass = Character.class;
+            } else if (typeClass == Double.TYPE) {
+                typeClass = Double.class;
+            } else if (typeClass == Float.TYPE) {
+                typeClass = Float.class;
+            } else if (typeClass == Boolean.TYPE) {
+                typeClass = Boolean.class;
+            }
+        } else if (typeClass.isArray() && object instanceof Collection) {
+            //JAXB allows a pseudo [] <--> List equivalence
+            return;
+        }
+        if (!typeClass.isInstance(object)) {
+            throw new IllegalArgumentException("Part " + part.getName() + " should be of type "
+                + typeClass.getName() + ", not "
+                + object.getClass().getName());
+        }
+    }
+
+    private boolean needToRender(MessagePartInfo part) {
+        if (part != null && part.getXmlSchema() instanceof XmlSchemaElement) {
+            XmlSchemaElement element = (XmlSchemaElement)part.getXmlSchema();
+            return element.isNillable() && element.getMinOccurs() > 0;
+        }
+        return false;
+    }
+
+    private void onCompleteMarshalling() {
+        if (setEventHandler && veventHandler instanceof MarshallerEventHandler) {
+            try {
+                ((MarshallerEventHandler) veventHandler).onMarshalComplete();
+            } catch (MarshalException e) {
+                if (e.getLinkedException() != null) {
+                    throw new Fault(new Message("MARSHAL_ERROR", LOG,
+                            e.getLinkedException().getMessage()), e);
+                }
+                throw new Fault(new Message("MARSHAL_ERROR", LOG, e.getMessage()), e);
+            }
+        }
+    }
+}

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

Posted by db...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dblevins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomee-jakarta.git

commit 9a737a0e805151842209d6704d77d136970ec964
Author: David Blevins <da...@gmail.com>
AuthorDate: Sun May 2 22:01:52 2021 -0700

    TCK fixes reapplied
---
 .../cxf/jaxrs/provider/AbstractJAXBProvider.java   | 14 +++-
 .../apache/cxf/jaxrs/provider/ProviderFactory.java | 98 ++++++++++++++++++++--
 .../org/apache/cxf/jaxrs/utils/InjectionUtils.java | 50 ++++++-----
 .../org/apache/cxf/jaxrs/utils/JAXRSUtils.java     | 18 +++-
 4 files changed, 141 insertions(+), 39 deletions(-)

diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/AbstractJAXBProvider.java b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/AbstractJAXBProvider.java
index f2bee17..b94760a 100644
--- a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/AbstractJAXBProvider.java
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/AbstractJAXBProvider.java
@@ -42,6 +42,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.NoContentException;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.StreamingOutput;
 import javax.ws.rs.ext.ContextResolver;
@@ -68,6 +69,8 @@ import org.w3c.dom.Element;
 
 import org.xml.sax.helpers.DefaultHandler;
 
+import com.ctc.wstx.exc.WstxEOFException;
+
 import org.apache.cxf.annotations.SchemaValidation;
 import org.apache.cxf.common.jaxb.JAXBUtils;
 import org.apache.cxf.common.util.PackageUtils;
@@ -719,7 +722,12 @@ public abstract class AbstractJAXBProvider<T> extends AbstractConfigurableProvid
         return sb;
     }
 
-    protected static void handleExceptionEnd(Throwable t, String message, boolean read) {
+    protected static void handleExceptionEnd(Throwable t, String message, boolean read) throws NoContentException {
+        if (t instanceof WstxEOFException && t.getMessage().startsWith("Unexpected EOF in prolog")){
+            String noContent = new org.apache.cxf.common.i18n.Message("EMPTY_BODY", BUNDLE).toString();
+            LOG.warning(noContent);
+            throw new NoContentException(noContent);
+        }
         Response.Status status = read
             ? Response.Status.BAD_REQUEST : Response.Status.INTERNAL_SERVER_ERROR;
         Response r = JAXRSUtils.toResponseBuilder(status)
@@ -728,7 +736,7 @@ public abstract class AbstractJAXBProvider<T> extends AbstractConfigurableProvid
             : ExceptionUtils.toInternalServerErrorException(t, r);
     }
 
-    protected void handleJAXBException(JAXBException e, boolean read) {
+    protected void handleJAXBException(JAXBException e, boolean read) throws NoContentException {
         StringBuilder sb = handleExceptionStart(e);
         Throwable linked = e.getLinkedException();
         if (linked != null && linked.getMessage() != null) {
@@ -753,7 +761,7 @@ public abstract class AbstractJAXBProvider<T> extends AbstractConfigurableProvid
         handleExceptionEnd(t, message, read);
     }
 
-    protected void handleXMLStreamException(XMLStreamException e, boolean read) {
+    protected void handleXMLStreamException(XMLStreamException e, boolean read) throws NoContentException {
         StringBuilder sb = handleExceptionStart(e);
         handleExceptionEnd(e, sb.toString(), read);
     }
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java
index c8a0aff..65c417c 100644
--- a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java
@@ -39,6 +39,7 @@ import java.util.Set;
 import java.util.TreeMap;
 import java.util.logging.Logger;
 
+import javax.annotation.Priority;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.Application;
 import javax.ws.rs.core.Configuration;
@@ -77,6 +78,8 @@ import org.apache.cxf.jaxrs.utils.ResourceUtils;
 import org.apache.cxf.message.Message;
 import org.apache.cxf.message.MessageUtils;
 
+import static javax.ws.rs.Priorities.USER;
+
 public abstract class ProviderFactory {
     public static final String DEFAULT_FILTER_NAME_BINDING = "org.apache.cxf.filter.binding";
     public static final String PROVIDER_SELECTION_PROPERTY_CHANGED = "provider.selection.property.changed";
@@ -662,6 +665,7 @@ public abstract class ProviderFactory {
         sortReaders();
         sortWriters();
         sortContextResolvers();
+        sortParamConverters();
 
         mapInterceptorFilters(readerInterceptors, readInts, ReaderInterceptor.class, true);
         mapInterceptorFilters(writerInterceptors, writeInts, WriterInterceptor.class, true);
@@ -783,7 +787,9 @@ public abstract class ProviderFactory {
         contextResolvers.sort(new ContextResolverComparator());
     }
 
-
+    private void sortParamConverters() {
+        paramConverters.sort(new ParamConverterComparator());
+    }
 
 
 
@@ -853,9 +859,12 @@ public abstract class ProviderFactory {
         setProviders(true, false, userProviders.toArray());
     }
 
-    private static class MessageBodyReaderComparator
+    static class MessageBodyReaderComparator
         implements Comparator<ProviderInfo<MessageBodyReader<?>>> {
 
+        private final GenericArgumentComparator classComparator =
+                new GenericArgumentComparator(MessageBodyReader.class);
+
         public int compare(ProviderInfo<MessageBodyReader<?>> p1,
                            ProviderInfo<MessageBodyReader<?>> p2) {
             MessageBodyReader<?> e1 = p1.getProvider();
@@ -870,7 +879,10 @@ public abstract class ProviderFactory {
             if (result != 0) {
                 return result;
             }
-            result = compareClasses(e1, e2);
+
+            final Class<?> class1 = ClassHelper.getRealClass(e1);
+            final Class<?> class2 = ClassHelper.getRealClass(e2);
+            result = classComparator.compare(class1, class2);
             if (result != 0) {
                 return result;
             }
@@ -878,19 +890,30 @@ public abstract class ProviderFactory {
             if (result != 0) {
                 return result;
             }
-            return comparePriorityStatus(p1.getProvider().getClass(), p2.getProvider().getClass());
+
+            result = comparePriorityStatus(p1.getProvider().getClass(), p2.getProvider().getClass());
+            if (result != 0) {
+                return result;
+            }
+
+            return p1.getProvider().getClass().getName().compareTo(p2.getProvider().getClass().getName());
         }
     }
 
-    private static class MessageBodyWriterComparator
+    static class MessageBodyWriterComparator
         implements Comparator<ProviderInfo<MessageBodyWriter<?>>> {
 
+        private final GenericArgumentComparator classComparator =
+                new GenericArgumentComparator(MessageBodyWriter.class);
+
         public int compare(ProviderInfo<MessageBodyWriter<?>> p1,
                            ProviderInfo<MessageBodyWriter<?>> p2) {
             MessageBodyWriter<?> e1 = p1.getProvider();
             MessageBodyWriter<?> e2 = p2.getProvider();
 
-            int result = compareClasses(e1, e2);
+            final Class<?> class1 = ClassHelper.getRealClass(e1);
+            final Class<?> class2 = ClassHelper.getRealClass(e2);
+            int result = classComparator.compare(class1, class2);
             if (result != 0) {
                 return result;
             }
@@ -903,13 +926,18 @@ public abstract class ProviderFactory {
             if (result != 0) {
                 return result;
             }
-            
+
             result = compareCustomStatus(p1, p2);
             if (result != 0) {
                 return result;
             }
 
-            return comparePriorityStatus(p1.getProvider().getClass(), p2.getProvider().getClass());
+            result = comparePriorityStatus(p1.getProvider().getClass(), p2.getProvider().getClass());
+            if (result != 0) {
+                return result;
+            }
+
+            return p1.getProvider().getClass().getName().compareTo(p2.getProvider().getClass().getName());
         }
     }
 
@@ -1136,7 +1164,7 @@ public abstract class ProviderFactory {
             // superclass should go last
             return -1;
         }
-        
+
         // there is no relation between the types returned by the providers
         return 0;
     }
@@ -1495,4 +1523,56 @@ public abstract class ProviderFactory {
         writerInterceptors = sortedWriterInterceptors;
     }
 
+    protected static class ParamConverterComparator implements Comparator<ProviderInfo<ParamConverterProvider>> {
+
+        @Override
+        public int compare(final ProviderInfo<ParamConverterProvider> a,
+                           final ProviderInfo<ParamConverterProvider> b) {
+
+            /*
+             * Primary sort.  Also takes care of sorting custom
+             * converters from system converters due to priority
+             * defaults
+             */
+            int result = sortByPriority(a, b);
+
+            /*
+             * Secondary sort as this list *will* change order
+             * once in a while between jvm restarts, which can
+             * have frustrating consequences for users who are
+             * expecting no change in behavior as they aren't
+             * changing their code.
+             */
+            if (result == 0) {
+                result = sortByClassName(a, b);
+            }
+
+            return result;
+        }
+
+        public int sortByPriority(final ProviderInfo<ParamConverterProvider> a,
+                           final ProviderInfo<ParamConverterProvider> b) {
+            final int aPriority = getPriority(a);
+            final int bPriority = getPriority(b);
+
+            // Sort ascending as the priority with the lowest number wins
+            return Integer.compare(aPriority, bPriority);
+        }
+
+        public int sortByClassName(final ProviderInfo<ParamConverterProvider> a,
+                           final ProviderInfo<ParamConverterProvider> b) {
+
+            // Sort ascending as the priority with the lowest number wins
+            return a.getProvider().getClass().getName().compareTo(b.getProvider().getClass().getName());
+        }
+
+        private int getPriority(final ProviderInfo<ParamConverterProvider> providerInfo) {
+            final Priority priority = providerInfo.getProvider().getClass().getAnnotation(Priority.class);
+            if (priority!=null) {
+                return priority.value();
+            }
+            return providerInfo.isCustom() ? USER : USER + 1000;
+        }
+    }
+
 }
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java b/transform/src/patch/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java
index 5543093..dca074a 100644
--- a/transform/src/patch/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java
@@ -53,20 +53,20 @@ import java.util.TreeSet;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Application;
-import javax.ws.rs.core.GenericEntity;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.PathSegment;
-import javax.ws.rs.core.Request;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.SecurityContext;
-import javax.ws.rs.core.UriInfo;
-import javax.ws.rs.ext.ContextResolver;
-import javax.ws.rs.ext.ParamConverter;
-import javax.ws.rs.ext.Providers;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.GenericEntity;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.core.PathSegment;
+import jakarta.ws.rs.core.Request;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.SecurityContext;
+import jakarta.ws.rs.core.UriInfo;
+import jakarta.ws.rs.ext.ContextResolver;
+import jakarta.ws.rs.ext.ParamConverter;
+import jakarta.ws.rs.ext.Providers;
 
 import org.apache.cxf.common.classloader.ClassLoaderUtils;
 import org.apache.cxf.common.i18n.BundleUtils;
@@ -537,15 +537,19 @@ public final class InjectionUtils {
     }
 
     private static RuntimeException createParamConversionException(ParameterType pType, Exception ex) {
-        //
-        //  For path, query & matrix parameters this is 404,
-        //  for others 400...
-        //
-        if (pType == ParameterType.PATH || pType == ParameterType.QUERY
-            || pType == ParameterType.MATRIX) {
-            return ExceptionUtils.toNotFoundException(ex, null);
-        }
-        return ExceptionUtils.toBadRequestException(ex, null);
+        /*
+         * Loosely related to the following section of the Jakarta REST specification:
+         *
+         * At least one of the acceptable response entity body media types is a supported output data
+         * format (see Section 3.5). If no methods support one of the acceptable response entity body
+         * media types an implementation MUST generate a NotAcceptableException (406 status)
+         * and no entity.
+         *
+         * Tested by:
+         * com.sun.ts.tests.jaxrs.ee.rs.ext.paramconverter.JAXRSClient
+         * atomicIntegerIsLazyDeployableAndThrowsErrorTest_from_standalone
+         */
+        return ExceptionUtils.toNotAcceptableException(ex, null);
     }
     
     public static <T> Optional<ParamConverter<T>> getParamConverter(Class<T> pClass,
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java b/transform/src/patch/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
index f264b48..3f0073a 100644
--- a/transform/src/patch/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
@@ -47,6 +47,7 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.stream.Collectors;
 
+import javax.activation.DataSource;
 import javax.ws.rs.ClientErrorException;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.HttpMethod;
@@ -65,6 +66,7 @@ import javax.ws.rs.core.Cookie;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.NoContentException;
 import javax.ws.rs.core.PathSegment;
 import javax.ws.rs.core.Request;
 import javax.ws.rs.core.Response;
@@ -164,11 +166,11 @@ public final class JAXRSUtils {
     private static final ResourceBundle BUNDLE = BundleUtils.getBundle(JAXRSUtils.class);
     private static final String PATH_SEGMENT_SEP = "/";
     private static final String REPORT_FAULT_MESSAGE_PROPERTY = "org.apache.cxf.jaxrs.report-fault-message";
-    private static final String NO_CONTENT_EXCEPTION = "javax.ws.rs.core.NoContentException";
+    private static final String NO_CONTENT_EXCEPTION = NoContentException.class.getName();
     private static final String HTTP_CHARSET_PARAM = "charset";
     private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0];
     private static final Set<Class<?>> STREAMING_OUT_TYPES = new HashSet<>(
-        Arrays.asList(InputStream.class, Reader.class, StreamingOutput.class));
+        Arrays.asList(InputStream.class, Reader.class, StreamingOutput.class, DataSource.class));
 
     private JAXRSUtils() {
     }
@@ -394,6 +396,8 @@ public final class JAXRSUtils {
         int methodMatched = 0;
         int consumeMatched = 0;
 
+        boolean resourceMethodsAdded = false;
+        boolean generateOptionsResponse = false;
         List<OperationResourceInfo> finalPathSubresources = null;
         for (Map.Entry<ClassResourceInfo, MultivaluedMap<String, String>> rEntry : matchedResources.entrySet()) {
             ClassResourceInfo resource = rEntry.getKey();
@@ -433,18 +437,21 @@ public final class JAXRSUtils {
                                     if (matchProduceTypes(acceptType, ori)) {
                                         candidateList.put(ori, map);
                                         added = true;
+                                        resourceMethodsAdded = true;
                                         break;
                                     }
                                 }
                             }
                             //CHECKSTYLE:ON
+                        } else if ("OPTIONS".equalsIgnoreCase(httpMethod)) {
+                            generateOptionsResponse = true;
                         }
                     }
                 }
                 LOG.fine(matchMessageLogSupplier(ori, path, httpMethod, requestType, acceptContentTypes, added));
             }
         }
-        if (finalPathSubresources != null && pathMatched > 0
+        if (finalPathSubresources != null && (resourceMethodsAdded || generateOptionsResponse)
             && !MessageUtils.getContextualBoolean(message, KEEP_SUBRESOURCE_CANDIDATES, false)) {
             for (OperationResourceInfo key : finalPathSubresources) {
                 candidateList.remove(key);
@@ -1184,7 +1191,10 @@ public final class JAXRSUtils {
         } else if (ResourceInfo.class.isAssignableFrom(clazz)) {
             o = new ResourceInfoImpl(contextMessage);
         } else if (ResourceContext.class.isAssignableFrom(clazz)) {
-            o = new ResourceContextImpl(contextMessage, contextMessage.getExchange().get(OperationResourceInfo.class));
+            OperationResourceInfo operationResourceInfo = contextMessage.getExchange().get(OperationResourceInfo.class);
+            if (operationResourceInfo != null) {
+                o = new ResourceContextImpl(contextMessage, operationResourceInfo);
+            }
         } else if (Request.class.isAssignableFrom(clazz)) {
             o = new RequestImpl(contextMessage);
         } else if (Providers.class.isAssignableFrom(clazz)) {

[tomee-jakarta] 01/06: cxf-rt-frontend-jaxrs changes since 3.4.3

Posted by db...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dblevins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomee-jakarta.git

commit e56ec49370e22257a8f5d8420ece459035fcbde7
Author: David Blevins <da...@gmail.com>
AuthorDate: Sun May 2 21:35:35 2021 -0700

    cxf-rt-frontend-jaxrs changes since 3.4.3
---
 tomee/tomee-plume-webapp/pom.xml                   |  7 ++
 .../cxf/jaxrs/provider/AbstractJAXBProvider.java   | 14 +---
 .../apache/cxf/jaxrs/provider/ProviderFactory.java | 98 ++--------------------
 .../org/apache/cxf/jaxrs/utils/InjectionUtils.java | 50 +++++------
 .../org/apache/cxf/jaxrs/utils/JAXRSUtils.java     | 18 +---
 5 files changed, 46 insertions(+), 141 deletions(-)

diff --git a/tomee/tomee-plume-webapp/pom.xml b/tomee/tomee-plume-webapp/pom.xml
index c62d43c..2e727e5 100644
--- a/tomee/tomee-plume-webapp/pom.xml
+++ b/tomee/tomee-plume-webapp/pom.xml
@@ -550,6 +550,13 @@
           </add>
           <source>1.8</source>
           <target>1.8</target>
+          <dependencies>
+            <dependency>org.apache.tomcat:tomcat-servlet-api:jar:10.0.4</dependency>
+            <dependency>org.springframework:spring-core:jar:5.3.6</dependency>
+            <dependency>org.springframework:spring-context:jar:5.3.6</dependency>
+            <dependency>org.springframework:spring-beans:jar:5.3.6</dependency>
+            <dependency>org.springframework:spring-webmvc:jar:5.3.6</dependency>
+          </dependencies>
         </configuration>
         <executions>
           <execution>
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/AbstractJAXBProvider.java b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/AbstractJAXBProvider.java
index b94760a..f2bee17 100644
--- a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/AbstractJAXBProvider.java
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/AbstractJAXBProvider.java
@@ -42,7 +42,6 @@ import java.util.concurrent.ConcurrentHashMap;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.NoContentException;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.StreamingOutput;
 import javax.ws.rs.ext.ContextResolver;
@@ -69,8 +68,6 @@ import org.w3c.dom.Element;
 
 import org.xml.sax.helpers.DefaultHandler;
 
-import com.ctc.wstx.exc.WstxEOFException;
-
 import org.apache.cxf.annotations.SchemaValidation;
 import org.apache.cxf.common.jaxb.JAXBUtils;
 import org.apache.cxf.common.util.PackageUtils;
@@ -722,12 +719,7 @@ public abstract class AbstractJAXBProvider<T> extends AbstractConfigurableProvid
         return sb;
     }
 
-    protected static void handleExceptionEnd(Throwable t, String message, boolean read) throws NoContentException {
-        if (t instanceof WstxEOFException && t.getMessage().startsWith("Unexpected EOF in prolog")){
-            String noContent = new org.apache.cxf.common.i18n.Message("EMPTY_BODY", BUNDLE).toString();
-            LOG.warning(noContent);
-            throw new NoContentException(noContent);
-        }
+    protected static void handleExceptionEnd(Throwable t, String message, boolean read) {
         Response.Status status = read
             ? Response.Status.BAD_REQUEST : Response.Status.INTERNAL_SERVER_ERROR;
         Response r = JAXRSUtils.toResponseBuilder(status)
@@ -736,7 +728,7 @@ public abstract class AbstractJAXBProvider<T> extends AbstractConfigurableProvid
             : ExceptionUtils.toInternalServerErrorException(t, r);
     }
 
-    protected void handleJAXBException(JAXBException e, boolean read) throws NoContentException {
+    protected void handleJAXBException(JAXBException e, boolean read) {
         StringBuilder sb = handleExceptionStart(e);
         Throwable linked = e.getLinkedException();
         if (linked != null && linked.getMessage() != null) {
@@ -761,7 +753,7 @@ public abstract class AbstractJAXBProvider<T> extends AbstractConfigurableProvid
         handleExceptionEnd(t, message, read);
     }
 
-    protected void handleXMLStreamException(XMLStreamException e, boolean read) throws NoContentException {
+    protected void handleXMLStreamException(XMLStreamException e, boolean read) {
         StringBuilder sb = handleExceptionStart(e);
         handleExceptionEnd(e, sb.toString(), read);
     }
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java
index 65c417c..c8a0aff 100644
--- a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/ProviderFactory.java
@@ -39,7 +39,6 @@ import java.util.Set;
 import java.util.TreeMap;
 import java.util.logging.Logger;
 
-import javax.annotation.Priority;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.Application;
 import javax.ws.rs.core.Configuration;
@@ -78,8 +77,6 @@ import org.apache.cxf.jaxrs.utils.ResourceUtils;
 import org.apache.cxf.message.Message;
 import org.apache.cxf.message.MessageUtils;
 
-import static javax.ws.rs.Priorities.USER;
-
 public abstract class ProviderFactory {
     public static final String DEFAULT_FILTER_NAME_BINDING = "org.apache.cxf.filter.binding";
     public static final String PROVIDER_SELECTION_PROPERTY_CHANGED = "provider.selection.property.changed";
@@ -665,7 +662,6 @@ public abstract class ProviderFactory {
         sortReaders();
         sortWriters();
         sortContextResolvers();
-        sortParamConverters();
 
         mapInterceptorFilters(readerInterceptors, readInts, ReaderInterceptor.class, true);
         mapInterceptorFilters(writerInterceptors, writeInts, WriterInterceptor.class, true);
@@ -787,9 +783,7 @@ public abstract class ProviderFactory {
         contextResolvers.sort(new ContextResolverComparator());
     }
 
-    private void sortParamConverters() {
-        paramConverters.sort(new ParamConverterComparator());
-    }
+
 
 
 
@@ -859,12 +853,9 @@ public abstract class ProviderFactory {
         setProviders(true, false, userProviders.toArray());
     }
 
-    static class MessageBodyReaderComparator
+    private static class MessageBodyReaderComparator
         implements Comparator<ProviderInfo<MessageBodyReader<?>>> {
 
-        private final GenericArgumentComparator classComparator =
-                new GenericArgumentComparator(MessageBodyReader.class);
-
         public int compare(ProviderInfo<MessageBodyReader<?>> p1,
                            ProviderInfo<MessageBodyReader<?>> p2) {
             MessageBodyReader<?> e1 = p1.getProvider();
@@ -879,10 +870,7 @@ public abstract class ProviderFactory {
             if (result != 0) {
                 return result;
             }
-
-            final Class<?> class1 = ClassHelper.getRealClass(e1);
-            final Class<?> class2 = ClassHelper.getRealClass(e2);
-            result = classComparator.compare(class1, class2);
+            result = compareClasses(e1, e2);
             if (result != 0) {
                 return result;
             }
@@ -890,30 +878,19 @@ public abstract class ProviderFactory {
             if (result != 0) {
                 return result;
             }
-
-            result = comparePriorityStatus(p1.getProvider().getClass(), p2.getProvider().getClass());
-            if (result != 0) {
-                return result;
-            }
-
-            return p1.getProvider().getClass().getName().compareTo(p2.getProvider().getClass().getName());
+            return comparePriorityStatus(p1.getProvider().getClass(), p2.getProvider().getClass());
         }
     }
 
-    static class MessageBodyWriterComparator
+    private static class MessageBodyWriterComparator
         implements Comparator<ProviderInfo<MessageBodyWriter<?>>> {
 
-        private final GenericArgumentComparator classComparator =
-                new GenericArgumentComparator(MessageBodyWriter.class);
-
         public int compare(ProviderInfo<MessageBodyWriter<?>> p1,
                            ProviderInfo<MessageBodyWriter<?>> p2) {
             MessageBodyWriter<?> e1 = p1.getProvider();
             MessageBodyWriter<?> e2 = p2.getProvider();
 
-            final Class<?> class1 = ClassHelper.getRealClass(e1);
-            final Class<?> class2 = ClassHelper.getRealClass(e2);
-            int result = classComparator.compare(class1, class2);
+            int result = compareClasses(e1, e2);
             if (result != 0) {
                 return result;
             }
@@ -926,18 +903,13 @@ public abstract class ProviderFactory {
             if (result != 0) {
                 return result;
             }
-
+            
             result = compareCustomStatus(p1, p2);
             if (result != 0) {
                 return result;
             }
 
-            result = comparePriorityStatus(p1.getProvider().getClass(), p2.getProvider().getClass());
-            if (result != 0) {
-                return result;
-            }
-
-            return p1.getProvider().getClass().getName().compareTo(p2.getProvider().getClass().getName());
+            return comparePriorityStatus(p1.getProvider().getClass(), p2.getProvider().getClass());
         }
     }
 
@@ -1164,7 +1136,7 @@ public abstract class ProviderFactory {
             // superclass should go last
             return -1;
         }
-
+        
         // there is no relation between the types returned by the providers
         return 0;
     }
@@ -1523,56 +1495,4 @@ public abstract class ProviderFactory {
         writerInterceptors = sortedWriterInterceptors;
     }
 
-    protected static class ParamConverterComparator implements Comparator<ProviderInfo<ParamConverterProvider>> {
-
-        @Override
-        public int compare(final ProviderInfo<ParamConverterProvider> a,
-                           final ProviderInfo<ParamConverterProvider> b) {
-
-            /*
-             * Primary sort.  Also takes care of sorting custom
-             * converters from system converters due to priority
-             * defaults
-             */
-            int result = sortByPriority(a, b);
-
-            /*
-             * Secondary sort as this list *will* change order
-             * once in a while between jvm restarts, which can
-             * have frustrating consequences for users who are
-             * expecting no change in behavior as they aren't
-             * changing their code.
-             */
-            if (result == 0) {
-                result = sortByClassName(a, b);
-            }
-
-            return result;
-        }
-
-        public int sortByPriority(final ProviderInfo<ParamConverterProvider> a,
-                           final ProviderInfo<ParamConverterProvider> b) {
-            final int aPriority = getPriority(a);
-            final int bPriority = getPriority(b);
-
-            // Sort ascending as the priority with the lowest number wins
-            return Integer.compare(aPriority, bPriority);
-        }
-
-        public int sortByClassName(final ProviderInfo<ParamConverterProvider> a,
-                           final ProviderInfo<ParamConverterProvider> b) {
-
-            // Sort ascending as the priority with the lowest number wins
-            return a.getProvider().getClass().getName().compareTo(b.getProvider().getClass().getName());
-        }
-
-        private int getPriority(final ProviderInfo<ParamConverterProvider> providerInfo) {
-            final Priority priority = providerInfo.getProvider().getClass().getAnnotation(Priority.class);
-            if (priority!=null) {
-                return priority.value();
-            }
-            return providerInfo.isCustom() ? USER : USER + 1000;
-        }
-    }
-
 }
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java b/transform/src/patch/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java
index dca074a..5543093 100644
--- a/transform/src/patch/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java
@@ -53,20 +53,20 @@ import java.util.TreeSet;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import jakarta.ws.rs.WebApplicationException;
-import jakarta.ws.rs.core.Application;
-import jakarta.ws.rs.core.GenericEntity;
-import jakarta.ws.rs.core.HttpHeaders;
-import jakarta.ws.rs.core.MediaType;
-import jakarta.ws.rs.core.MultivaluedMap;
-import jakarta.ws.rs.core.PathSegment;
-import jakarta.ws.rs.core.Request;
-import jakarta.ws.rs.core.Response;
-import jakarta.ws.rs.core.SecurityContext;
-import jakarta.ws.rs.core.UriInfo;
-import jakarta.ws.rs.ext.ContextResolver;
-import jakarta.ws.rs.ext.ParamConverter;
-import jakarta.ws.rs.ext.Providers;
+import 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;
@@ -537,19 +537,15 @@ public final class InjectionUtils {
     }
 
     private static RuntimeException createParamConversionException(ParameterType pType, Exception ex) {
-        /*
-         * Loosely related to the following section of the Jakarta REST specification:
-         *
-         * At least one of the acceptable response entity body media types is a supported output data
-         * format (see Section 3.5). If no methods support one of the acceptable response entity body
-         * media types an implementation MUST generate a NotAcceptableException (406 status)
-         * and no entity.
-         *
-         * Tested by:
-         * com.sun.ts.tests.jaxrs.ee.rs.ext.paramconverter.JAXRSClient
-         * atomicIntegerIsLazyDeployableAndThrowsErrorTest_from_standalone
-         */
-        return ExceptionUtils.toNotAcceptableException(ex, null);
+        //
+        //  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,
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java b/transform/src/patch/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
index 3f0073a..f264b48 100644
--- a/transform/src/patch/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
@@ -47,7 +47,6 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.stream.Collectors;
 
-import javax.activation.DataSource;
 import javax.ws.rs.ClientErrorException;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.HttpMethod;
@@ -66,7 +65,6 @@ import javax.ws.rs.core.Cookie;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.NoContentException;
 import javax.ws.rs.core.PathSegment;
 import javax.ws.rs.core.Request;
 import javax.ws.rs.core.Response;
@@ -166,11 +164,11 @@ public final class JAXRSUtils {
     private static final ResourceBundle BUNDLE = BundleUtils.getBundle(JAXRSUtils.class);
     private static final String PATH_SEGMENT_SEP = "/";
     private static final String REPORT_FAULT_MESSAGE_PROPERTY = "org.apache.cxf.jaxrs.report-fault-message";
-    private static final String NO_CONTENT_EXCEPTION = NoContentException.class.getName();
+    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, DataSource.class));
+        Arrays.asList(InputStream.class, Reader.class, StreamingOutput.class));
 
     private JAXRSUtils() {
     }
@@ -396,8 +394,6 @@ public final class JAXRSUtils {
         int methodMatched = 0;
         int consumeMatched = 0;
 
-        boolean resourceMethodsAdded = false;
-        boolean generateOptionsResponse = false;
         List<OperationResourceInfo> finalPathSubresources = null;
         for (Map.Entry<ClassResourceInfo, MultivaluedMap<String, String>> rEntry : matchedResources.entrySet()) {
             ClassResourceInfo resource = rEntry.getKey();
@@ -437,21 +433,18 @@ public final class JAXRSUtils {
                                     if (matchProduceTypes(acceptType, ori)) {
                                         candidateList.put(ori, map);
                                         added = true;
-                                        resourceMethodsAdded = true;
                                         break;
                                     }
                                 }
                             }
                             //CHECKSTYLE:ON
-                        } else if ("OPTIONS".equalsIgnoreCase(httpMethod)) {
-                            generateOptionsResponse = true;
                         }
                     }
                 }
                 LOG.fine(matchMessageLogSupplier(ori, path, httpMethod, requestType, acceptContentTypes, added));
             }
         }
-        if (finalPathSubresources != null && (resourceMethodsAdded || generateOptionsResponse)
+        if (finalPathSubresources != null && pathMatched > 0
             && !MessageUtils.getContextualBoolean(message, KEEP_SUBRESOURCE_CANDIDATES, false)) {
             for (OperationResourceInfo key : finalPathSubresources) {
                 candidateList.remove(key);
@@ -1191,10 +1184,7 @@ public final class JAXRSUtils {
         } else if (ResourceInfo.class.isAssignableFrom(clazz)) {
             o = new ResourceInfoImpl(contextMessage);
         } else if (ResourceContext.class.isAssignableFrom(clazz)) {
-            OperationResourceInfo operationResourceInfo = contextMessage.getExchange().get(OperationResourceInfo.class);
-            if (operationResourceInfo != null) {
-                o = new ResourceContextImpl(contextMessage, operationResourceInfo);
-            }
+            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)) {

[tomee-jakarta] 02/06: cxf-core and cxf-rt-frontend-jaxrs changes since 3.4.3

Posted by db...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dblevins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomee-jakarta.git

commit 924dff9807f4c7fa9c13dcaddae172087b63ee06
Author: David Blevins <da...@gmail.com>
AuthorDate: Sun May 2 21:48:40 2021 -0700

    cxf-core and cxf-rt-frontend-jaxrs changes since 3.4.3
---
 tomee/tomee-plume-webapp/pom.xml                   |   11 +-
 .../java/org/apache/cxf/annotations/Policy.java    |   80 +
 .../apache/cxf/annotations/WSDLDocumentation.java  |   88 +
 .../cxf/attachment/AttachmentDataSource.java       |  107 +
 .../cxf/attachment/AttachmentDeserializer.java     |  462 ++++
 .../cxf/attachment/AttachmentSerializer.java       |  345 +++
 .../org/apache/cxf/attachment/AttachmentUtil.java  |  572 +++++
 .../apache/cxf/attachment/Base64DecoderStream.java |  196 ++
 .../apache/cxf/attachment/ContentDisposition.java  |  144 ++
 .../cxf/attachment/LazyAttachmentCollection.java   |  362 ++++
 .../cxf/attachment/MimeBodyPartInputStream.java    |  275 +++
 .../java/org/apache/cxf/bus/CXFBusFactory.java     |   47 +
 .../bus/blueprint/BundleDelegatingClassLoader.java |  134 ++
 .../cxf/bus/blueprint/BusDefinitionParser.java     |   72 +
 .../apache/cxf/bus/blueprint/ConfigurerImpl.java   |  180 ++
 .../org/apache/cxf/bus/extension/Extension.java    |  291 +++
 .../cxf/bus/extension/ExtensionManagerImpl.java    |  381 ++++
 .../ServiceContractResolverRegistryImpl.java       |  113 +
 .../java/org/apache/cxf/bus/osgi/CXFActivator.java |  134 ++
 .../cxf/bus/osgi/CXFExtensionBundleListener.java   |  181 ++
 .../org/apache/cxf/bus/osgi/OSGIBusListener.java   |  224 ++
 .../cxf/bus/resource/ResourceManagerImpl.java      |   85 +
 .../apache/cxf/bus/spring/BusDefinitionParser.java |  310 +++
 .../apache/cxf/bus/spring/BusEntityResolver.java   |   94 +
 .../cxf/bus/spring/BusExtensionPostProcessor.java  |   71 +
 .../cxf/bus/spring/Jsr250BeanPostProcessor.java    |  164 ++
 .../apache/cxf/bus/spring/NamespaceHandler.java    |   57 +
 .../java/org/apache/cxf/bus/spring/SpringBus.java  |  142 ++
 .../cxf/catalog/CatalogXmlSchemaURIResolver.java   |  102 +
 .../java/org/apache/cxf/common/i18n/Exception.java |   58 +
 .../java/org/apache/cxf/common/i18n/Message.java   |   91 +
 .../apache/cxf/common/i18n/UncheckedException.java |   84 +
 .../cxf/common/injection/ResourceInjector.java     |  446 ++++
 .../java/org/apache/cxf/common/jaxb/JAXBUtils.java | 1180 +++++++++++
 .../common/logging/AbstractDelegatingLogger.java   |  457 ++++
 .../org/apache/cxf/common/logging/LogUtils.java    |  485 +++++
 .../cxf/common/logging/RegexLoggingFilter.java     |  117 ++
 .../cxf/common/spi/ClassGeneratorClassLoader.java  |  153 ++
 .../cxf/common/spi/NamespaceClassGenerator.java    |  450 ++++
 .../org/apache/cxf/common/util/ASMHelperImpl.java  |  273 +++
 .../org/apache/cxf/common/util/Base64Utility.java  |  474 +++++
 .../org/apache/cxf/common/util/CachedClass.java    |   37 +
 .../org/apache/cxf/common/util/ClassHelper.java    |  140 ++
 .../apache/cxf/common/util/CollectionUtils.java    |  126 ++
 .../java/org/apache/cxf/common/util/Compiler.java  |  382 ++++
 .../common/util/ModCountCopyOnWriteArrayList.java  |  156 ++
 .../org/apache/cxf/common/util/PackageUtils.java   |  181 ++
 .../apache/cxf/common/util/ProxyClassLoader.java   |   89 +
 .../org/apache/cxf/common/util/ProxyHelper.java    |  140 ++
 .../common/util/ReflectionInvokationHandler.java   |  199 ++
 .../org/apache/cxf/common/util/SortedArraySet.java |  266 +++
 .../cxf/common/util/SpringClassUnwrapper.java      |  111 +
 .../cxf/common/util/SpringClasspathScanner.java    |  202 ++
 .../org/apache/cxf/common/util/StreamPrinter.java  |   63 +
 .../org/apache/cxf/common/util/URIParserUtil.java  |  209 ++
 .../cxf/common/xmlschema/SchemaCollection.java     |  389 ++++
 .../jsse/MultiKeyPasswordKeyManager.java           |   84 +
 .../configuration/jsse/TLSClientParameters.java    |  258 +++
 .../configuration/jsse/TLSParameterJaxBUtils.java  |  420 ++++
 .../cxf/configuration/spring/ConfigurerImpl.java   |  288 +++
 .../cxf/databinding/source/SourceDataBinding.java  |  104 +
 .../cxf/databinding/stax/StaxDataBinding.java      |  187 ++
 .../cxf/endpoint/AbstractConduitSelector.java      |  308 +++
 .../org/apache/cxf/endpoint/ClientCallback.java    |  166 ++
 .../java/org/apache/cxf/endpoint/ClientImpl.java   | 1192 +++++++++++
 .../java/org/apache/cxf/endpoint/EndpointImpl.java |  220 ++
 .../java/org/apache/cxf/endpoint/ServerImpl.java   |  221 ++
 .../org/apache/cxf/feature/FastInfosetFeature.java |  110 +
 .../org/apache/cxf/feature/WrappedFeature.java     |   61 +
 .../cxf/feature/transform/XSLTOutInterceptor.java  |  210 ++
 .../patch/java/org/apache/cxf/headers/Header.java  |   78 +
 .../java/org/apache/cxf/helpers/DOMUtils.java      |  895 ++++++++
 .../patch/java/org/apache/cxf/helpers/IOUtils.java |  428 ++++
 .../java/org/apache/cxf/helpers/ServiceUtils.java  |  214 ++
 .../AbstractFaultChainInitiatorObserver.java       |  142 ++
 .../interceptor/AbstractLoggingInterceptor.java    |  298 +++
 .../cxf/interceptor/AnnotationInterceptors.java    |  151 ++
 .../cxf/interceptor/AttachmentInInterceptor.java   |   72 +
 .../cxf/interceptor/ClientOutFaultObserver.java    |   68 +
 .../interceptor/InFaultChainInitiatorObserver.java |   84 +
 .../apache/cxf/interceptor/InterceptorChain.java   |  109 +
 .../org/apache/cxf/interceptor/LoggingMessage.java |  133 ++
 .../interceptor/OneWayProcessorInterceptor.java    |  177 ++
 .../OutFaultChainInitiatorObserver.java            |   84 +
 .../security/DefaultSecurityContext.java           |  188 ++
 .../interceptor/security/JAASLoginInterceptor.java |  233 ++
 .../cxf/internal/CXFAPINamespaceHandler.java       |  127 ++
 .../apache/cxf/io/CacheAndWriteOutputStream.java   |   96 +
 .../patch/java/org/apache/cxf/io/CachedWriter.java |  665 ++++++
 .../java/org/apache/cxf/io/ReaderInputStream.java  |  294 +++
 .../apache/cxf/io/WriteOnCloseOutputStream.java    |   46 +
 .../java/org/apache/cxf/jaxrs/JAXRSInvoker.java    |  460 ++++
 .../apache/cxf/jaxrs/JAXRSServerFactoryBean.java   |  467 ++++
 .../cxf/jaxrs/impl/EntityTagHeaderProvider.java    |   77 +
 .../cxf/jaxrs/impl/MediaTypeHeaderProvider.java    |  221 ++
 .../org/apache/cxf/jaxrs/impl/RequestImpl.java     |  388 ++++
 .../apache/cxf/jaxrs/impl/ResourceContextImpl.java |   67 +
 .../impl/tl/ThreadLocalInvocationHandler.java      |   53 +
 .../cxf/jaxrs/interceptor/JAXRSInInterceptor.java  |  274 +++
 .../cxf/jaxrs/interceptor/JAXRSOutInterceptor.java |  498 +++++
 .../cxf/jaxrs/model/AbstractResourceInfo.java      |  389 ++++
 .../apache/cxf/jaxrs/model/ClassResourceInfo.java  |  366 ++++
 .../org/apache/cxf/jaxrs/model/URITemplate.java    |  627 ++++++
 .../jaxrs/provider/CachingMessageBodyReader.java   |  100 +
 .../jaxrs/provider/CachingMessageBodyWriter.java   |  102 +
 .../cxf/jaxrs/provider/DataSourceProvider.java     |  110 +
 .../cxf/jaxrs/provider/FormEncodingProvider.java   |  228 ++
 .../cxf/jaxrs/provider/JAXBElementProvider.java    |  635 ++++++
 .../cxf/jaxrs/provider/MultipartProvider.java      |  474 +++++
 .../apache/cxf/jaxrs/provider/SourceProvider.java  |  236 +++
 .../cxf/jaxrs/provider/XSLTJaxbProvider.java       |  588 ++++++
 .../jaxrs/security/JAASAuthenticationFilter.java   |  170 ++
 .../security/KerberosAuthenticationFilter.java     |  251 +++
 .../jaxrs/servlet/CXFNonSpringJaxrsServlet.java    |  640 ++++++
 .../JAXRSServerFactoryBeanDefinitionParser.java    |  286 +++
 .../java/org/apache/cxf/jaxrs/utils/HttpUtils.java |  704 +++++++
 .../org/apache/cxf/jaxrs/utils/ResourceUtils.java  | 1044 +++++++++
 .../java/org/apache/cxf/message/MessageUtils.java  |  261 +++
 .../java/org/apache/cxf/phase/PhaseChainCache.java |  137 ++
 .../apache/cxf/phase/PhaseInterceptorChain.java    |  857 ++++++++
 .../cxf/service/factory/FactoryBeanListener.java   |  145 ++
 .../apache/cxf/service/invoker/FactoryInvoker.java |   70 +
 .../service/model/AbstractPropertiesHolder.java    |  271 +++
 .../org/apache/cxf/service/model/FaultInfo.java    |   61 +
 .../apache/cxf/service/model/InterfaceInfo.java    |  124 ++
 .../apache/cxf/service/model/OperationInfo.java    |  242 +++
 .../cxf/service/model/UnwrappedOperationInfo.java  |   72 +
 .../java/org/apache/cxf/staxutils/StaxUtils.java   | 2222 ++++++++++++++++++++
 .../apache/cxf/staxutils/W3CDOMStreamReader.java   |  429 ++++
 .../cxf/transport/ChainInitiationObserver.java     |  197 ++
 .../AbstractBeanValidationInterceptor.java         |   65 +
 .../cxf/workqueue/AutomaticWorkQueueImpl.java      |  619 ++++++
 .../apache/cxf/ws/addressing/MAPAggregator.java    |  224 ++
 133 files changed, 36242 insertions(+), 2 deletions(-)

diff --git a/tomee/tomee-plume-webapp/pom.xml b/tomee/tomee-plume-webapp/pom.xml
index 2e727e5..c49a4a1 100644
--- a/tomee/tomee-plume-webapp/pom.xml
+++ b/tomee/tomee-plume-webapp/pom.xml
@@ -551,10 +551,17 @@
           <source>1.8</source>
           <target>1.8</target>
           <dependencies>
+            <dependency>org.apache.aries.blueprint:blueprint-parser:jar:1.6.0</dependency>
+            <dependency>org.apache.aries.blueprint:org.apache.aries.blueprint.api:jar:1.0.1</dependency>
+            <dependency>org.apache.aries.blueprint:org.apache.aries.blueprint.core:jar:1.10.2</dependency>
             <dependency>org.apache.tomcat:tomcat-servlet-api:jar:10.0.4</dependency>
-            <dependency>org.springframework:spring-core:jar:5.3.6</dependency>
-            <dependency>org.springframework:spring-context:jar:5.3.6</dependency>
+            <dependency>org.osgi:org.osgi.core:jar:6.0.0</dependency>
+            <dependency>org.osgi:osgi.cmpn:jar:6.0.0</dependency>
+            <dependency>org.ow2.asm:asm:jar:9.1</dependency>
+            <dependency>org.springframework:spring-aop:jar:5.3.6</dependency>
             <dependency>org.springframework:spring-beans:jar:5.3.6</dependency>
+            <dependency>org.springframework:spring-context:jar:5.3.6</dependency>
+            <dependency>org.springframework:spring-core:jar:5.3.6</dependency>
             <dependency>org.springframework:spring-webmvc:jar:5.3.6</dependency>
           </dependencies>
         </configuration>
diff --git a/transform/src/patch/java/org/apache/cxf/annotations/Policy.java b/transform/src/patch/java/org/apache/cxf/annotations/Policy.java
new file mode 100644
index 0000000..a0d4a75
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/annotations/Policy.java
@@ -0,0 +1,80 @@
+/**
+ * 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.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Attaches a Policy to a service or method
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.METHOD })
+@Inherited
+public @interface Policy {
+
+    String uri();
+
+    boolean includeInWSDL() default true;
+
+
+    /**
+     * The place to put the PolicyReference.  The Default depends on the
+     * location of the annotation.   On the method in the SEI, it would be
+     * the binding/operation, on the SEI, it would be the binding, on the
+     * service impl, the service element.
+     * @return location
+     */
+    Placement placement() default Placement.DEFAULT;
+
+    /**
+     * If Placement is PORT_TYPE_OPERATION_FAULT, or BINDING_OPERATION_FAULT,
+     * return the fault class associated with this documentation
+     * @return the fault class
+     */
+    Class<?> faultClass() default DEFAULT.class;
+
+    enum Placement {
+        DEFAULT,
+
+        PORT_TYPE,
+        PORT_TYPE_OPERATION,
+        PORT_TYPE_OPERATION_INPUT,
+        PORT_TYPE_OPERATION_OUTPUT,
+        PORT_TYPE_OPERATION_FAULT,
+
+        BINDING,
+        BINDING_OPERATION,
+        BINDING_OPERATION_INPUT,
+        BINDING_OPERATION_OUTPUT,
+        BINDING_OPERATION_FAULT,
+
+        SERVICE,
+        SERVICE_PORT,
+    }
+
+    final class DEFAULT { }
+}
+
diff --git a/transform/src/patch/java/org/apache/cxf/annotations/WSDLDocumentation.java b/transform/src/patch/java/org/apache/cxf/annotations/WSDLDocumentation.java
new file mode 100644
index 0000000..9116f1c
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/annotations/WSDLDocumentation.java
@@ -0,0 +1,88 @@
+/**
+ * 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.annotations;
+
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * Adds documentation nodes to the generated WSDL
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.METHOD })
+@Inherited
+public @interface WSDLDocumentation {
+    /**
+     * The documentation to add
+     * @return documentation string
+     */
+    String value();
+
+    /**
+     * The place to put the documentation.  The Default depends on the
+     * location of the annotation.   On the method in the SEI, it would be
+     * the portType/operation, on the SEI, it would be the portType, on the
+     * service impl, the service element.
+     * @return location
+     */
+    Placement placement() default Placement.DEFAULT;
+
+    /**
+     * If Placement is FAULT_MESSAGE, PORT_FAULT, or BINDING_FAULT,
+     * return the fault class associated with this documentation
+     * @return the fault class
+     */
+    Class<?> faultClass() default DEFAULT.class;
+
+    enum Placement {
+        DEFAULT,
+        TOP,
+
+        INPUT_MESSAGE,
+        OUTPUT_MESSAGE,
+        FAULT_MESSAGE,
+
+        PORT_TYPE,
+        PORT_TYPE_OPERATION,
+        PORT_TYPE_OPERATION_INPUT,
+        PORT_TYPE_OPERATION_OUTPUT,
+        PORT_TYPE_OPERATION_FAULT,
+
+        BINDING,
+        BINDING_OPERATION,
+        BINDING_OPERATION_INPUT,
+        BINDING_OPERATION_OUTPUT,
+        BINDING_OPERATION_FAULT,
+
+        SERVICE,
+        SERVICE_PORT,
+
+    }
+
+
+    final class DEFAULT { }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/attachment/AttachmentDataSource.java b/transform/src/patch/java/org/apache/cxf/attachment/AttachmentDataSource.java
new file mode 100644
index 0000000..6e4fa1a
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/attachment/AttachmentDataSource.java
@@ -0,0 +1,107 @@
+/**
+ * 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.attachment;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.activation.DataSource;
+
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.io.CacheSizeExceededException;
+import org.apache.cxf.io.CachedOutputStream;
+import org.apache.cxf.message.Message;
+
+public class AttachmentDataSource implements DataSource {
+
+    private final String ct;
+    private CachedOutputStream cache;
+    private InputStream ins;
+    private DelegatingInputStream delegate;
+    private String name;
+
+    public AttachmentDataSource(String ctParam, InputStream inParam) {
+        this.ct = ctParam;
+        ins = inParam;
+    }
+
+    public boolean isCached() {
+        return cache != null;
+    }
+    public void cache(Message message) throws IOException {
+        if (cache == null) {
+            cache = new CachedOutputStream();
+            AttachmentUtil.setStreamedAttachmentProperties(message, cache);
+            try {
+                IOUtils.copyAndCloseInput(ins, cache);
+                cache.lockOutputStream();
+                if (delegate != null) {
+                    delegate.setInputStream(cache.getInputStream());
+                }
+            } catch (CacheSizeExceededException | IOException cee) {
+                cache.close();
+                cache = null;
+                throw cee;
+            } finally {
+                ins = null;
+            }
+        }
+    }
+    public void hold(Message message) throws IOException {
+        cache(message);
+        cache.holdTempFile();
+    }
+    public void release() {
+        if (cache != null) {
+            cache.releaseTempFileHold();
+        }
+    }
+
+    public String getContentType() {
+        return ct;
+    }
+
+    public InputStream getInputStream() {
+        try {
+            if (cache != null) {
+                return cache.getInputStream();
+            }
+            if (delegate == null) {
+                delegate = new DelegatingInputStream(ins);
+            }
+            return delegate;
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public OutputStream getOutputStream() throws IOException {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/attachment/AttachmentDeserializer.java b/transform/src/patch/java/org/apache/cxf/attachment/AttachmentDeserializer.java
new file mode 100644
index 0000000..55a1dcc
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/attachment/AttachmentDeserializer.java
@@ -0,0 +1,462 @@
+/**
+ * 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.attachment;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.activation.DataSource;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.common.util.SystemPropertyAction;
+import org.apache.cxf.helpers.HttpHeaderHelper;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.io.CachedOutputStream;
+import org.apache.cxf.message.Attachment;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageUtils;
+
+public class AttachmentDeserializer {
+    public static final String ATTACHMENT_PART_HEADERS = AttachmentDeserializer.class.getName() + ".headers";
+
+    /**
+     * Allowed value is any instance of {@link File} or {@link String}.
+     */
+    public static final String ATTACHMENT_DIRECTORY = "attachment-directory";
+
+    /**
+     * The memory threshold of attachments. Allowed value is any instance of {@link Number} or {@link String}.
+     * The default is {@link AttachmentDeserializer#THRESHOLD}.
+     */
+    public static final String ATTACHMENT_MEMORY_THRESHOLD = "attachment-memory-threshold";
+
+    /**
+     * The maximum size of the attachment. Allowed value is any of {@link Number} or {@link String}.
+     */
+    public static final String ATTACHMENT_MAX_SIZE = "attachment-max-size";
+
+    /**
+     * The maximum number of attachments permitted in a message. The default is 50.
+     */
+    public static final String ATTACHMENT_MAX_COUNT = "attachment-max-count";
+
+    /**
+     * The maximum MIME Header Length. The default is 300.
+     */
+    public static final String ATTACHMENT_MAX_HEADER_SIZE = "attachment-max-header-size";
+    public static final int DEFAULT_MAX_HEADER_SIZE =
+        SystemPropertyAction.getInteger("org.apache.cxf.attachment-max-header-size", 300);
+
+    public static final int THRESHOLD = 1024 * 100; //100K (byte unit)
+
+    private static final Pattern CONTENT_TYPE_BOUNDARY_PATTERN = Pattern.compile("boundary=\"?([^\";]*)");
+
+    private static final Pattern INPUT_STREAM_BOUNDARY_PATTERN =
+            Pattern.compile("^--(\\S*)$", Pattern.MULTILINE);
+
+    private static final Logger LOG = LogUtils.getL7dLogger(AttachmentDeserializer.class);
+
+    private static final int PUSHBACK_AMOUNT = 2048;
+
+    private boolean lazyLoading = true;
+
+    private PushbackInputStream stream;
+    private int createCount;
+    private int closedCount;
+    private boolean closed;
+
+    private byte[] boundary;
+
+    private LazyAttachmentCollection attachments;
+
+    private Message message;
+
+    private InputStream body;
+
+    private Set<DelegatingInputStream> loaded = new HashSet<>();
+    private List<String> supportedTypes;
+
+    private int maxHeaderLength = DEFAULT_MAX_HEADER_SIZE;
+
+    public AttachmentDeserializer(Message message) {
+        this(message, Collections.singletonList("multipart/related"));
+    }
+
+    public AttachmentDeserializer(Message message, List<String> supportedTypes) {
+        this.message = message;
+        this.supportedTypes = supportedTypes;
+
+        // Get the maximum Header length from configuration
+        maxHeaderLength = MessageUtils.getContextualInteger(message, ATTACHMENT_MAX_HEADER_SIZE,
+                                                            DEFAULT_MAX_HEADER_SIZE);
+    }
+
+    public void initializeAttachments() throws IOException {
+        initializeRootMessage();
+
+        Object maxCountProperty = message.getContextualProperty(AttachmentDeserializer.ATTACHMENT_MAX_COUNT);
+        int maxCount = 50;
+        if (maxCountProperty != null) {
+            if (maxCountProperty instanceof Integer) {
+                maxCount = (Integer)maxCountProperty;
+            } else {
+                maxCount = Integer.parseInt((String)maxCountProperty);
+            }
+        }
+
+        attachments = new LazyAttachmentCollection(this, maxCount);
+        message.setAttachments(attachments);
+    }
+
+    protected void initializeRootMessage() throws IOException {
+        String contentType = (String) message.get(Message.CONTENT_TYPE);
+
+        if (contentType == null) {
+            throw new IllegalStateException("Content-Type can not be empty!");
+        }
+
+        if (message.getContent(InputStream.class) == null) {
+            throw new IllegalStateException("An InputStream must be provided!");
+        }
+
+        if (AttachmentUtil.isTypeSupported(contentType.toLowerCase(), supportedTypes)) {
+            String boundaryString = findBoundaryFromContentType(contentType);
+            if (null == boundaryString) {
+                boundaryString = findBoundaryFromInputStream();
+            }
+            // If a boundary still wasn't found, throw an exception
+            if (null == boundaryString) {
+                throw new IOException("Couldn't determine the boundary from the message!");
+            }
+            boundary = boundaryString.getBytes(StandardCharsets.UTF_8);
+
+            stream = new PushbackInputStream(message.getContent(InputStream.class), PUSHBACK_AMOUNT);
+            if (!readTillFirstBoundary(stream, boundary)) {
+                throw new IOException("Couldn't find MIME boundary: " + boundaryString);
+            }
+
+            Map<String, List<String>> ih = loadPartHeaders(stream);
+            message.put(ATTACHMENT_PART_HEADERS, ih);
+            String val = AttachmentUtil.getHeader(ih, "Content-Type", "; ");
+            if (!StringUtils.isEmpty(val)) {
+                String cs = HttpHeaderHelper.findCharset(val);
+                if (!StringUtils.isEmpty(cs)) {
+                    message.put(Message.ENCODING, HttpHeaderHelper.mapCharset(cs));
+                }
+            }
+            val = AttachmentUtil.getHeader(ih, "Content-Transfer-Encoding");
+
+            MimeBodyPartInputStream mmps = new MimeBodyPartInputStream(stream, boundary, PUSHBACK_AMOUNT);
+            InputStream ins = AttachmentUtil.decode(mmps, val);
+            if (ins != mmps) {
+                ih.remove("Content-Transfer-Encoding");
+            }
+            body = new DelegatingInputStream(ins, this);
+            createCount++;
+            message.setContent(InputStream.class, body);
+        }
+    }
+
+    private String findBoundaryFromContentType(String ct) {
+        // Use regex to get the boundary and return null if it's not found
+        Matcher m = CONTENT_TYPE_BOUNDARY_PATTERN.matcher(ct);
+        return m.find() ? "--" + m.group(1) : null;
+    }
+
+    private String findBoundaryFromInputStream() throws IOException {
+        InputStream is = message.getContent(InputStream.class);
+        //boundary should definitely be in the first 2K;
+        PushbackInputStream in = new PushbackInputStream(is, 4096);
+        byte[] buf = new byte[2048];
+        int i = in.read(buf);
+        int len = i;
+        while (i > 0 && len < buf.length) {
+            i = in.read(buf, len, buf.length - len);
+            if (i > 0) {
+                len += i;
+            }
+        }
+        String msg = IOUtils.newStringFromBytes(buf, 0, len);
+        in.unread(buf, 0, len);
+
+        // Reset the input stream since we'll need it again later
+        message.setContent(InputStream.class, in);
+
+        // Use regex to get the boundary and return null if it's not found
+        Matcher m = INPUT_STREAM_BOUNDARY_PATTERN.matcher(msg);
+        return m.find() ? "--" + m.group(1) : null;
+    }
+
+    public AttachmentImpl readNext() throws IOException {
+        // Cache any mime parts that are currently being streamed
+        cacheStreamedAttachments();
+        if (closed) {
+            return null;
+        }
+
+        int v = stream.read();
+        if (v == -1) {
+            return null;
+        }
+        stream.unread(v);
+
+        Map<String, List<String>> headers = loadPartHeaders(stream);
+        return (AttachmentImpl)createAttachment(headers);
+    }
+
+    private void cacheStreamedAttachments() throws IOException {
+        if (body instanceof DelegatingInputStream
+            && !((DelegatingInputStream) body).isClosed()) {
+
+            cache((DelegatingInputStream) body);
+        }
+
+        List<Attachment> atts = new ArrayList<>(attachments.getLoadedAttachments());
+        for (Attachment a : atts) {
+            DataSource s = a.getDataHandler().getDataSource();
+            if (s instanceof AttachmentDataSource) {
+                AttachmentDataSource ads = (AttachmentDataSource)s;
+                if (!ads.isCached()) {
+                    ads.cache(message);
+                }
+            } else if (s.getInputStream() instanceof DelegatingInputStream) {
+                cache((DelegatingInputStream) s.getInputStream());
+            } else {
+                //assume a normal stream that is already cached
+            }
+        }
+    }
+
+    private void cache(DelegatingInputStream input) throws IOException {
+        if (loaded.contains(input)) {
+            return;
+        }
+        loaded.add(input);
+        InputStream origIn = input.getInputStream();
+        try (CachedOutputStream out = new CachedOutputStream()) {
+            AttachmentUtil.setStreamedAttachmentProperties(message, out);
+            IOUtils.copy(input, out);
+            input.setInputStream(out.getInputStream());
+            origIn.close();
+        }
+    }
+
+    /**
+     * Move the read pointer to the begining of the first part read till the end
+     * of first boundary
+     *
+     * @param pushbackInStream
+     * @param boundary
+     * @throws IOException
+     */
+    private static boolean readTillFirstBoundary(PushbackInputStream pushbackInStream,
+        byte[] boundary) throws IOException {
+
+        // work around a bug in PushBackInputStream where the buffer isn't
+        // initialized
+        // and available always returns 0.
+        int value = pushbackInStream.read();
+        pushbackInStream.unread(value);
+        while (value != -1) {
+            value = pushbackInStream.read();
+            if ((byte) value == boundary[0]) {
+                int boundaryIndex = 0;
+                while (value != -1 && (boundaryIndex < boundary.length) && ((byte) value == boundary[boundaryIndex])) {
+
+                    value = pushbackInStream.read();
+                    if (value == -1) {
+                        throw new IOException("Unexpected End while searching for first Mime Boundary");
+                    }
+                    boundaryIndex++;
+                }
+                if (boundaryIndex == boundary.length) {
+                    // boundary found, read the newline
+                    if (value == 13) {
+                        pushbackInStream.read();
+                    }
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Create an Attachment from the MIME stream. If there is a previous attachment
+     * that is not read, cache that attachment.
+     *
+     * @throws IOException
+     */
+    private Attachment createAttachment(Map<String, List<String>> headers) throws IOException {
+        InputStream partStream =
+            new DelegatingInputStream(new MimeBodyPartInputStream(stream, boundary, PUSHBACK_AMOUNT),
+                                      this);
+        createCount++;
+
+        return AttachmentUtil.createAttachment(partStream, headers);
+    }
+
+    public boolean isLazyLoading() {
+        return lazyLoading;
+    }
+
+    public void setLazyLoading(boolean lazyLoading) {
+        this.lazyLoading = lazyLoading;
+    }
+
+    public void markClosed(DelegatingInputStream delegatingInputStream) throws IOException {
+        closedCount++;
+        if (closedCount == createCount && !attachments.hasNext(false)) {
+            int x = stream.read();
+            while (x != -1) {
+                x = stream.read();
+            }
+            stream.close();
+            closed = true;
+        }
+    }
+    /**
+     *  Check for more attachment.
+     *
+     * @return whether there is more attachment or not.  It will not deserialize the next attachment.
+     * @throws IOException
+     */
+    public boolean hasNext() throws IOException {
+        cacheStreamedAttachments();
+        if (closed) {
+            return false;
+        }
+
+        int v = stream.read();
+        if (v == -1) {
+            return false;
+        }
+        stream.unread(v);
+        return true;
+    }
+
+
+
+    private Map<String, List<String>> loadPartHeaders(InputStream in) throws IOException {
+        StringBuilder buffer = new StringBuilder(128);
+        StringBuilder b = new StringBuilder(128);
+        Map<String, List<String>> heads = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+        // loop until we hit the end or a null line
+        while (readLine(in, b)) {
+            // lines beginning with white space get special handling
+            char c = b.charAt(0);
+            if (c == ' ' || c == '\t') {
+                if (buffer.length() != 0) {
+                    // preserve the line break and append the continuation
+                    buffer.append("\r\n");
+                    buffer.append(b);
+                }
+            } else {
+                // if we have a line pending in the buffer, flush it
+                if (buffer.length() > 0) {
+                    addHeaderLine(heads, buffer);
+                    buffer.setLength(0);
+                }
+                // add this to the accumulator
+                buffer.append(b);
+            }
+        }
+
+        // if we have a line pending in the buffer, flush it
+        if (buffer.length() > 0) {
+            addHeaderLine(heads, buffer);
+        }
+        return heads;
+    }
+
+    private boolean readLine(InputStream in, StringBuilder buffer) throws IOException {
+        if (buffer.length() != 0) {
+            buffer.setLength(0);
+        }
+        int c;
+
+        while ((c = in.read()) != -1) {
+            // a linefeed is a terminator, always.
+            if (c == '\n') {
+                break;
+            } else if (c == '\r') {
+                //just ignore the CR.  The next character SHOULD be an NL.  If not, we're
+                //just going to discard this
+                continue;
+            } else {
+                // just add to the buffer
+                buffer.append((char)c);
+            }
+
+            if (buffer.length() > maxHeaderLength) {
+                LOG.fine("The attachment header size has exceeded the configured parameter: " + maxHeaderLength);
+                throw new HeaderSizeExceededException();
+            }
+        }
+
+        // no characters found...this was either an eof or a null line.
+        return buffer.length() != 0;
+    }
+
+    private void addHeaderLine(Map<String, List<String>> heads, StringBuilder line) {
+        // null lines are a nop
+        final int size = line.length();
+        if (size == 0) {
+            return;
+        }
+        int separator = line.indexOf(":");
+        final String name;
+        String value = "";
+        if (separator == -1) {
+            name = line.toString().trim();
+        } else {
+            name = line.substring(0, separator);
+            // step past the separator.  Now we need to remove any leading white space characters.
+            separator++;
+
+            while (separator < size) {
+                char ch = line.charAt(separator);
+                if (ch != ' ' && ch != '\t' && ch != '\r' && ch != '\n') {
+                    break;
+                }
+                separator++;
+            }
+            value = line.substring(separator);
+        }
+        List<String> v = heads.computeIfAbsent(name, k -> new ArrayList<>(1));
+        v.add(value);
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/attachment/AttachmentSerializer.java b/transform/src/patch/java/org/apache/cxf/attachment/AttachmentSerializer.java
new file mode 100644
index 0000000..3e65de3
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/attachment/AttachmentSerializer.java
@@ -0,0 +1,345 @@
+/**
+ * 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.attachment;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.activation.DataHandler;
+
+import org.apache.cxf.common.util.Base64Utility;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.message.Attachment;
+import org.apache.cxf.message.Message;
+
+
+
+
+public class AttachmentSerializer {
+    // http://tools.ietf.org/html/rfc2387
+    private static final String DEFAULT_MULTIPART_TYPE = "multipart/related";
+
+    private String contentTransferEncoding = AttachmentUtil.BINARY;
+
+    private Message message;
+    private String bodyBoundary;
+    private OutputStream out;
+    private String encoding;
+
+    private String multipartType;
+    private Map<String, List<String>> rootHeaders = Collections.emptyMap();
+    private boolean xop = true;
+    private boolean writeOptionalTypeParameters = true;
+
+
+    public AttachmentSerializer(Message messageParam) {
+        message = messageParam;
+    }
+
+    public AttachmentSerializer(Message messageParam,
+                                String multipartType,
+                                boolean writeOptionalTypeParameters,
+                                Map<String, List<String>> headers) {
+        message = messageParam;
+        this.multipartType = multipartType;
+        this.writeOptionalTypeParameters = writeOptionalTypeParameters;
+        this.rootHeaders = headers;
+    }
+
+    /**
+     * Serialize the beginning of the attachment which includes the MIME
+     * beginning and headers for the root message.
+     */
+    public void writeProlog() throws IOException {
+        // Create boundary for body
+        bodyBoundary = AttachmentUtil.getUniqueBoundaryValue();
+
+        String bodyCt = (String) message.get(Message.CONTENT_TYPE);
+        String bodyCtParams = null;
+        String bodyCtParamsEscaped = null;
+        // split the bodyCt to its head that is the type and its properties so that we
+        // can insert the values at the right places based on the soap version and the mtom option
+        // bodyCt will be of the form
+        // soap11 -> text/xml
+        // soap12 -> application/soap+xml; action="urn:ihe:iti:2007:RetrieveDocumentSet"
+        if (bodyCt.indexOf(';') != -1) {
+            int pos = bodyCt.indexOf(';');
+            // get everything from the semi-colon
+            bodyCtParams = bodyCt.substring(pos);
+            bodyCtParamsEscaped = escapeQuotes(bodyCtParams);
+            // keep the type/subtype part in bodyCt
+            bodyCt = bodyCt.substring(0, pos);
+        }
+        // Set transport mime type
+        String requestMimeType = multipartType == null ? DEFAULT_MULTIPART_TYPE : multipartType;
+
+        StringBuilder ct = new StringBuilder(32);
+        ct.append(requestMimeType);
+
+        // having xop set to true implies multipart/related, but just in case...
+        boolean xopOrMultipartRelated = xop
+            || DEFAULT_MULTIPART_TYPE.equalsIgnoreCase(requestMimeType)
+            || DEFAULT_MULTIPART_TYPE.startsWith(requestMimeType);
+
+        // type is a required parameter for multipart/related only
+        if (xopOrMultipartRelated
+            && requestMimeType.indexOf("type=") == -1) {
+            if (xop) {
+                ct.append("; type=\"application/xop+xml\"");
+            } else {
+                ct.append("; type=\"").append(bodyCt).append('"');
+            }
+        }
+
+        // boundary
+        ct.append("; boundary=\"")
+            .append(bodyBoundary)
+            .append('"');
+
+        String rootContentId = getHeaderValue("Content-ID", AttachmentUtil.BODY_ATTACHMENT_ID);
+
+        // 'start' is a required parameter for XOP/MTOM, clearly defined
+        // for simpler multipart/related payloads but is not needed for
+        // multipart/mixed, multipart/form-data
+        if (xopOrMultipartRelated) {
+            ct.append("; start=\"<")
+                .append(checkAngleBrackets(rootContentId))
+                .append(">\"");
+        }
+
+        // start-info is a required parameter for XOP/MTOM, may be needed for
+        // other WS cases but is redundant in simpler multipart/related cases
+        // the parameters need to be included within the start-info's value in the escaped form
+        if (writeOptionalTypeParameters || xop) {
+            ct.append("; start-info=\"")
+                .append(bodyCt);
+            if (bodyCtParamsEscaped != null) {
+                ct.append(bodyCtParamsEscaped);
+            }
+            ct.append('"');
+        }
+
+
+        message.put(Message.CONTENT_TYPE, ct.toString());
+
+
+        // 2. write headers
+        out = message.getContent(OutputStream.class);
+        encoding = (String) message.get(Message.ENCODING);
+        if (encoding == null) {
+            encoding = StandardCharsets.UTF_8.name();
+        }
+        StringWriter writer = new StringWriter();
+        writer.write("\r\n");
+        writer.write("--");
+        writer.write(bodyBoundary);
+
+        StringBuilder mimeBodyCt = new StringBuilder();
+        String bodyType = getHeaderValue("Content-Type", null);
+        if (bodyType == null) {
+            mimeBodyCt.append(xop ? "application/xop+xml" : bodyCt)
+                .append("; charset=").append(encoding);
+            if (xop) {
+                mimeBodyCt.append("; type=\"").append(bodyCt);
+                if (bodyCtParamsEscaped != null) {
+                    mimeBodyCt.append(bodyCtParamsEscaped);
+                }
+                mimeBodyCt.append('"');
+            } else if (bodyCtParams != null) {
+                mimeBodyCt.append(bodyCtParams);
+            }
+        } else {
+            mimeBodyCt.append(bodyType);
+        }
+
+        writeHeaders(mimeBodyCt.toString(), rootContentId, rootHeaders, writer);
+        out.write(writer.getBuffer().toString().getBytes(encoding));
+    }
+
+    private static String escapeQuotes(String s) {
+        return s.indexOf('"') != 0 ? s.replace("\"", "\\\"") : s;
+    }
+
+    public void setContentTransferEncoding(String cte) {
+        contentTransferEncoding = cte;
+    }
+
+    private String getHeaderValue(String name, String defaultValue) {
+        List<String> value = rootHeaders.get(name);
+        if (value == null || value.isEmpty()) {
+            return defaultValue;
+        }
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < value.size(); i++) {
+            sb.append(value.get(i));
+            if (i + 1 < value.size()) {
+                sb.append(',');
+            }
+        }
+        return sb.toString();
+    }
+
+    private void writeHeaders(String contentType, String attachmentId,
+                                     Map<String, List<String>> headers, Writer writer) throws IOException {
+        writer.write("\r\nContent-Type: ");
+        writer.write(contentType);
+        writer.write("\r\nContent-Transfer-Encoding: " + contentTransferEncoding + "\r\n");
+
+        if (attachmentId != null) {
+            attachmentId = checkAngleBrackets(attachmentId);
+            writer.write("Content-ID: <");
+            writer.write(URLDecoder.decode(attachmentId, StandardCharsets.UTF_8.name()));
+            writer.write(">\r\n");
+        }
+        // headers like Content-Disposition need to be serialized
+        for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
+            String name = entry.getKey();
+            if ("Content-Type".equalsIgnoreCase(name) || "Content-ID".equalsIgnoreCase(name)
+                || "Content-Transfer-Encoding".equalsIgnoreCase(name)) {
+                continue;
+            }
+            writer.write(name);
+            writer.write(": ");
+            List<String> values = entry.getValue();
+            for (int i = 0; i < values.size(); i++) {
+                writer.write(values.get(i));
+                if (i + 1 < values.size()) {
+                    writer.write(",");
+                }
+            }
+            writer.write("\r\n");
+        }
+
+        writer.write("\r\n");
+    }
+
+    private static String checkAngleBrackets(String value) {
+        if (value.charAt(0) == '<' && value.charAt(value.length() - 1) == '>') {
+            return value.substring(1, value.length() - 1);
+        }
+        return value;
+    }
+
+    /**
+     * Write the end of the body boundary and any attachments included.
+     * @throws IOException
+     */
+    public void writeAttachments() throws IOException {
+        if (message.getAttachments() != null) {
+            for (Attachment a : message.getAttachments()) {
+                StringWriter writer = new StringWriter();
+                writer.write("\r\n--");
+                writer.write(bodyBoundary);
+
+                final Map<String, List<String>> headers;
+                Iterator<String> it = a.getHeaderNames();
+                if (it.hasNext()) {
+                    headers = new LinkedHashMap<>();
+                    while (it.hasNext()) {
+                        String key = it.next();
+                        headers.put(key, Collections.singletonList(a.getHeader(key)));
+                    }
+                } else {
+                    headers = Collections.emptyMap();
+                }
+
+
+                DataHandler handler = a.getDataHandler();
+                handler.setCommandMap(AttachmentUtil.getCommandMap());
+
+                writeHeaders(handler.getContentType(), a.getId(),
+                             headers, writer);
+                out.write(writer.getBuffer().toString().getBytes(encoding));
+                if ("base64".equals(contentTransferEncoding)) {
+                    try (InputStream inputStream = handler.getInputStream()) {
+                        encodeBase64(inputStream, out, IOUtils.DEFAULT_BUFFER_SIZE);
+                    }
+                } else {
+                    handler.writeTo(out);
+                }
+            }
+        }
+        StringWriter writer = new StringWriter();
+        writer.write("\r\n--");
+        writer.write(bodyBoundary);
+        writer.write("--");
+        out.write(writer.getBuffer().toString().getBytes(encoding));
+        out.flush();
+    }
+
+    private int encodeBase64(InputStream input, OutputStream output, int bufferSize) throws IOException {
+        int avail = input.available();
+        if (avail > 262143) {
+            //must be divisible by 3
+            avail = 262143;
+        }
+        if (avail > bufferSize) {
+            bufferSize = avail;
+        }
+        final byte[] buffer = new byte[bufferSize];
+        int n = input.read(buffer);
+        int total = 0;
+        while (-1 != n) {
+            if (n == 0) {
+                throw new IOException("0 bytes read in violation of InputStream.read(byte[])");
+            }
+            //make sure n is divisible by 3
+            int left = n % 3;
+            n -= left;
+            if (n > 0) {
+                Base64Utility.encodeAndStream(buffer, 0, n, output);
+                total += n;
+            }
+            if (left != 0) {
+                for (int x = 0; x < left; ++x) {
+                    buffer[x] = buffer[n + x];
+                }
+                n = input.read(buffer, left, buffer.length - left);
+                if (n == -1) {
+                    // we've hit the end, but still have stuff left, write it out
+                    Base64Utility.encodeAndStream(buffer, 0, left, output);
+                    total += left;
+                }
+            } else {
+                n = input.read(buffer);
+            }
+        }
+        return total;
+    }
+
+    public boolean isXop() {
+        return xop;
+    }
+
+    public void setXop(boolean xop) {
+        this.xop = xop;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/attachment/AttachmentUtil.java b/transform/src/patch/java/org/apache/cxf/attachment/AttachmentUtil.java
new file mode 100644
index 0000000..7c72f00
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/attachment/AttachmentUtil.java
@@ -0,0 +1,572 @@
+/**
+ * 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.attachment;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
+
+import javax.activation.CommandInfo;
+import javax.activation.CommandMap;
+import javax.activation.DataContentHandler;
+import javax.activation.DataHandler;
+import javax.activation.DataSource;
+import javax.activation.FileDataSource;
+import javax.activation.MailcapCommandMap;
+import javax.activation.URLDataSource;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.helpers.FileUtils;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.io.CachedOutputStream;
+import org.apache.cxf.message.Attachment;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageUtils;
+
+public final class AttachmentUtil {
+    public static final String BODY_ATTACHMENT_ID = "root.message@cxf.apache.org";
+
+    static final String BINARY = "binary";
+    
+    private static final Logger LOG = LogUtils.getL7dLogger(AttachmentUtil.class);
+
+    private static final AtomicInteger COUNTER = new AtomicInteger();
+    private static final String ATT_UUID = UUID.randomUUID().toString();
+
+    private static final Random BOUND_RANDOM = new Random();
+    private static final CommandMap DEFAULT_COMMAND_MAP = CommandMap.getDefaultCommandMap();
+    private static final MailcapCommandMap COMMAND_MAP = new EnhancedMailcapCommandMap();
+    
+    
+    static final class EnhancedMailcapCommandMap extends MailcapCommandMap {
+        @Override
+        public synchronized DataContentHandler createDataContentHandler(
+                String mimeType) {
+            DataContentHandler dch = super.createDataContentHandler(mimeType);
+            if (dch == null) {
+                dch = DEFAULT_COMMAND_MAP.createDataContentHandler(mimeType);
+            }
+            return dch;
+        }
+
+        @Override
+        public DataContentHandler createDataContentHandler(String mimeType,
+                DataSource ds) {
+            DataContentHandler dch = super.createDataContentHandler(mimeType);
+            if (dch == null) {
+                dch = DEFAULT_COMMAND_MAP.createDataContentHandler(mimeType, ds);
+            }
+            return dch;
+        }
+
+        @Override
+        public synchronized CommandInfo[] getAllCommands(String mimeType) {
+            CommandInfo[] commands = super.getAllCommands(mimeType);
+            CommandInfo[] defaultCommands = DEFAULT_COMMAND_MAP.getAllCommands(mimeType);
+            List<CommandInfo> cmdList = new ArrayList<>(Arrays.asList(commands));
+
+            // Add CommandInfo which does not exist in current command map.
+            for (CommandInfo defCmdInfo : defaultCommands) {
+                String defCmdName = defCmdInfo.getCommandName();
+                boolean cmdNameExist = false;
+                for (CommandInfo cmdInfo : commands) {
+                    if (cmdInfo.getCommandName().equals(defCmdName)) {
+                        cmdNameExist = true;
+                        break;
+                    }
+                }
+                if (!cmdNameExist) {
+                    cmdList.add(defCmdInfo);
+                }
+            }
+
+            CommandInfo[] allCommandArray = new CommandInfo[0];
+            return cmdList.toArray(allCommandArray);
+        }
+
+        @Override
+        public synchronized CommandInfo getCommand(String mimeType, String cmdName) {
+            CommandInfo cmdInfo = super.getCommand(mimeType, cmdName);
+            if (cmdInfo == null) {
+                cmdInfo = DEFAULT_COMMAND_MAP.getCommand(mimeType, cmdName);
+            }
+            return cmdInfo;
+        }
+
+        /**
+         * Merge current mime types and default mime types.
+         */
+        @Override
+        public synchronized String[] getMimeTypes() {
+            String[] mimeTypes = super.getMimeTypes();
+            String[] defMimeTypes = DEFAULT_COMMAND_MAP.getMimeTypes();
+            Set<String> mimeTypeSet = new HashSet<>();
+            Collections.addAll(mimeTypeSet, mimeTypes);
+            Collections.addAll(mimeTypeSet, defMimeTypes);
+            String[] mimeArray = new String[0];
+            return mimeTypeSet.toArray(mimeArray);
+        }
+    }
+
+
+    private AttachmentUtil() {
+
+    }
+
+    static {
+        COMMAND_MAP.addMailcap("image/*;;x-java-content-handler="
+                               + ImageDataContentHandler.class.getName());
+    }
+
+    public static CommandMap getCommandMap() {
+        return COMMAND_MAP;
+    }
+
+    public static boolean isMtomEnabled(Message message) {
+        return MessageUtils.getContextualBoolean(message, Message.MTOM_ENABLED, false);
+    }
+
+    public static void setStreamedAttachmentProperties(Message message, CachedOutputStream bos)
+        throws IOException {
+        Object directory = message.getContextualProperty(AttachmentDeserializer.ATTACHMENT_DIRECTORY);
+        if (directory != null) {
+            if (directory instanceof File) {
+                bos.setOutputDir((File) directory);
+            } else if (directory instanceof String) {
+                bos.setOutputDir(new File((String) directory));
+            } else {
+                throw new IOException("The value set as " + AttachmentDeserializer.ATTACHMENT_DIRECTORY
+                        + " should be either an instance of File or String");
+            }
+        }
+
+        Object threshold = message.getContextualProperty(AttachmentDeserializer.ATTACHMENT_MEMORY_THRESHOLD);
+        if (threshold != null) {
+            if (threshold instanceof Number) {
+                long t = ((Number) threshold).longValue();
+                if (t >= 0) {
+                    bos.setThreshold(t);
+                } else {
+                    LOG.warning("Threshold value overflowed long. Setting default value!");
+                    bos.setThreshold(AttachmentDeserializer.THRESHOLD);
+                }
+            } else if (threshold instanceof String) {
+                try {
+                    bos.setThreshold(Long.parseLong((String) threshold));
+                } catch (NumberFormatException e) {
+                    throw new IOException("Provided threshold String is not a number", e);
+                }
+            } else {
+                throw new IOException("The value set as " + AttachmentDeserializer.ATTACHMENT_MEMORY_THRESHOLD
+                        + " should be either an instance of Number or String");
+            }
+        } else if (!CachedOutputStream.isThresholdSysPropSet()) {
+            // Use the default AttachmentDeserializer Threshold only if there is no system property defined
+            bos.setThreshold(AttachmentDeserializer.THRESHOLD);
+        }
+
+        Object maxSize = message.getContextualProperty(AttachmentDeserializer.ATTACHMENT_MAX_SIZE);
+        if (maxSize != null) {
+            if (maxSize instanceof Number) {
+                long size = ((Number) maxSize).longValue();
+                if (size >= 0) {
+                    bos.setMaxSize(size);
+                } else {
+                    LOG.warning("Max size value overflowed long. Do not set max size!");
+                }
+            } else if (maxSize instanceof String) {
+                try {
+                    bos.setMaxSize(Long.parseLong((String) maxSize));
+                } catch (NumberFormatException e) {
+                    throw new IOException("Provided threshold String is not a number", e);
+                }
+            } else {
+                throw new IOException("The value set as " + AttachmentDeserializer.ATTACHMENT_MAX_SIZE
+                        + " should be either an instance of Number or String");
+            }
+        }
+    }
+
+    public static String createContentID(String ns) throws UnsupportedEncodingException {
+        // tend to change
+        String cid = "cxf.apache.org";
+        if (ns != null && !ns.isEmpty()) {
+            try {
+                URI uri = new URI(ns);
+                String host = uri.getHost();
+                if (host != null) {
+                    cid = host;
+                } else {
+                    cid = ns;
+                }
+            } catch (Exception e) {
+                cid = ns;
+            }
+        }
+        return ATT_UUID + '-' + Integer.toString(COUNTER.incrementAndGet()) + '@'
+            + URLEncoder.encode(cid, StandardCharsets.UTF_8.name());
+    }
+
+    public static String getUniqueBoundaryValue() {
+        //generate a random UUID.
+        //we don't need the cryptographically secure random uuid that
+        //UUID.randomUUID() will produce.  Thus, use a faster
+        //pseudo-random thing
+        long leastSigBits;
+        long mostSigBits;
+        synchronized (BOUND_RANDOM) {
+            mostSigBits = BOUND_RANDOM.nextLong();
+            leastSigBits = BOUND_RANDOM.nextLong();
+        }
+
+        mostSigBits &= 0xFFFFFFFFFFFF0FFFL;  //clear version
+        mostSigBits |= 0x0000000000004000L;  //set version
+
+        leastSigBits &= 0x3FFFFFFFFFFFFFFFL; //clear the variant
+        leastSigBits |= 0x8000000000000000L; //set to IETF variant
+
+        UUID result = new UUID(mostSigBits, leastSigBits);
+
+        return "uuid:" + result.toString();
+    }
+
+    public static Map<String, DataHandler> getDHMap(final Collection<Attachment> attachments) {
+        Map<String, DataHandler> dataHandlers = null;
+        if (attachments != null) {
+            if (attachments instanceof LazyAttachmentCollection) {
+                dataHandlers = ((LazyAttachmentCollection)attachments).createDataHandlerMap();
+            } else {
+                dataHandlers = new DHMap(attachments);
+            }
+        }
+        return dataHandlers == null ? new LinkedHashMap<>() : dataHandlers;
+    }
+
+    static class DHMap extends AbstractMap<String, DataHandler> {
+        final Collection<Attachment> list;
+        DHMap(Collection<Attachment> l) {
+            list = l;
+        }
+        public Set<Map.Entry<String, DataHandler>> entrySet() {
+            return new AbstractSet<Map.Entry<String, DataHandler>>() {
+                @Override
+                public Iterator<Map.Entry<String, DataHandler>> iterator() {
+                    final Iterator<Attachment> it = list.iterator();
+                    return new Iterator<Map.Entry<String, DataHandler>>() {
+                        public boolean hasNext() {
+                            return it.hasNext();
+                        }
+                        public Map.Entry<String, DataHandler> next() {
+                            final Attachment a = it.next();
+                            return new Map.Entry<String, DataHandler>() {
+                                @Override
+                                public String getKey() {
+                                    return a.getId();
+                                }
+
+                                @Override
+                                public DataHandler getValue() {
+                                    return a.getDataHandler();
+                                }
+
+                                @Override
+                                public DataHandler setValue(DataHandler value) {
+                                    return null;
+                                }
+                            };
+                        }
+                        @Override
+                        public void remove() {
+                            it.remove();
+                        }
+                    };
+                }
+
+                @Override
+                public int size() {
+                    return list.size();
+                }
+            };
+        }
+        
+        @Override
+        public DataHandler put(String key, DataHandler value) {
+            Iterator<Attachment> i = list.iterator();
+            DataHandler ret = null;
+            while (i.hasNext()) {
+                Attachment a = i.next();
+                if (a.getId().equals(key)) {
+                    i.remove();
+                    ret = a.getDataHandler();
+                    break;
+                }
+            }
+            list.add(new AttachmentImpl(key, value));
+            return ret;
+        }
+    }
+
+    public static String cleanContentId(String id) {
+        if (id != null) {
+            if (id.startsWith("<")) {
+                // strip <>
+                id = id.substring(1, id.length() - 1);
+            }
+            // strip cid:
+            if (id.startsWith("cid:")) {
+                id = id.substring(4);
+            }
+            // urldecode. Is this bad even without cid:? What does decode do with malformed %-signs, anyhow?
+            try {
+                id = URLDecoder.decode(id, StandardCharsets.UTF_8.name());
+            } catch (UnsupportedEncodingException e) {
+                //ignore, keep id as is
+            }
+        }
+        if (id == null) {
+            //no Content-ID, set cxf default ID
+            id =  BODY_ATTACHMENT_ID;
+        }
+        return id;
+    }
+
+    static String getHeaderValue(List<String> v) {
+        if (v != null && !v.isEmpty()) {
+            return v.get(0);
+        }
+        return null;
+    }
+    static String getHeaderValue(List<String> v, String delim) {
+        if (v != null && !v.isEmpty()) {
+            return String.join(delim, v);
+        }
+        return null;
+    }
+    static String getHeader(Map<String, List<String>> headers, String h) {
+        return getHeaderValue(headers.get(h));
+    }
+    static String getHeader(Map<String, List<String>> headers, String h, String delim) {
+        return getHeaderValue(headers.get(h), delim);
+    }
+    public static Attachment createAttachment(InputStream stream, Map<String, List<String>> headers)
+        throws IOException {
+
+        String id = cleanContentId(getHeader(headers, "Content-ID"));
+
+        AttachmentImpl att = new AttachmentImpl(id);
+
+        final String ct = getHeader(headers, "Content-Type");
+        String cd = getHeader(headers, "Content-Disposition");
+        String fileName = getContentDispositionFileName(cd);
+
+        String encoding = null;
+
+        for (Map.Entry<String, List<String>> e : headers.entrySet()) {
+            String name = e.getKey();
+            if ("Content-Transfer-Encoding".equalsIgnoreCase(name)) {
+                encoding = getHeader(headers, name);
+                if (BINARY.equalsIgnoreCase(encoding)) {
+                    att.setXOP(true);
+                }
+            }
+            att.setHeader(name, getHeaderValue(e.getValue()));
+        }
+        if (encoding == null) {
+            encoding = BINARY;
+        }
+        InputStream ins = decode(stream, encoding);
+        if (ins != stream) {
+            headers.remove("Content-Transfer-Encoding");
+        }
+        DataSource source = new AttachmentDataSource(ct, ins);
+        if (!StringUtils.isEmpty(fileName)) {
+            ((AttachmentDataSource)source).setName(FileUtils.stripPath(fileName));
+        }
+        att.setDataHandler(new DataHandler(source));
+        return att;
+    }
+
+    static String getContentDispositionFileName(String cd) {
+        if (StringUtils.isEmpty(cd)) {
+            return null;
+        }
+        ContentDisposition c = new ContentDisposition(cd);
+        String s = c.getParameter("filename");
+        if (s == null) {
+            s = c.getParameter("name");
+        }
+        return s;
+    }
+
+    public static InputStream decode(InputStream in, String encoding) throws IOException {
+        if (encoding == null) {
+            return in;
+        }
+        encoding = encoding.toLowerCase();
+
+        // some encodings are just pass-throughs, with no real decoding.
+        if (BINARY.equals(encoding)
+            || "7bit".equals(encoding)
+            || "8bit".equals(encoding)) {
+            return in;
+        } else if ("base64".equals(encoding)) {
+            return new Base64DecoderStream(in);
+        } else if ("quoted-printable".equals(encoding)) {
+            return new QuotedPrintableDecoderStream(in);
+        } else {
+            throw new IOException("Unknown encoding " + encoding);
+        }
+    }
+    public static boolean isTypeSupported(String contentType, List<String> types) {
+        if (contentType == null) {
+            return false;
+        }
+        contentType = contentType.toLowerCase();
+        for (String s : types) {
+            if (contentType.indexOf(s) != -1) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static Attachment createMtomAttachment(boolean isXop, String mimeType, String elementNS,
+                                                 byte[] data, int offset, int length, int threshold) {
+        if (!isXop || length <= threshold) {
+            return null;
+        }
+        if (mimeType == null) {
+            mimeType = "application/octet-stream";
+        }
+
+        ByteDataSource source = new ByteDataSource(data, offset, length);
+        source.setContentType(mimeType);
+        DataHandler handler = new DataHandler(source);
+
+        String id;
+        try {
+            id = AttachmentUtil.createContentID(elementNS);
+        } catch (UnsupportedEncodingException e) {
+            throw new Fault(e);
+        }
+        AttachmentImpl att = new AttachmentImpl(id, handler);
+        att.setXOP(isXop);
+        return att;
+    }
+
+    public static Attachment createMtomAttachmentFromDH(
+        boolean isXop, DataHandler handler, String elementNS, int threshold) {
+        if (!isXop) {
+            return null;
+        }
+
+        // The following is just wrong. Even if the DataHandler has a stream, we should still
+        // apply the threshold.
+        try {
+            DataSource ds = handler.getDataSource();
+            if (ds instanceof FileDataSource) {
+                FileDataSource fds = (FileDataSource)ds;
+                File file = fds.getFile();
+                if (file.length() < threshold) {
+                    return null;
+                }
+            } else if (ds.getClass().getName().endsWith("ObjectDataSource")) {
+                Object o = handler.getContent();
+                if (o instanceof String
+                    && ((String)o).length() < threshold) {
+                    return null;
+                } else if (o instanceof byte[] && ((byte[])o).length < threshold) {
+                    return null;
+                }
+            }
+        } catch (IOException e1) {
+        //      ignore, just do the normal attachment thing
+        }
+
+        String id;
+        try {
+            id = AttachmentUtil.createContentID(elementNS);
+        } catch (UnsupportedEncodingException e) {
+            throw new Fault(e);
+        }
+        AttachmentImpl att = new AttachmentImpl(id, handler);
+        if (!StringUtils.isEmpty(handler.getName())) {
+            //set Content-Disposition attachment header if filename isn't null
+            String file = handler.getName();
+            File f = new File(file);
+            if (f.exists() && f.isFile()) {
+                file = f.getName();
+            }
+            att.setHeader("Content-Disposition", "attachment;name=\"" + file + "\"");
+        }
+        att.setXOP(isXop);
+        return att;
+    }
+
+    public static DataSource getAttachmentDataSource(String contentId, Collection<Attachment> atts) {
+        // Is this right? - DD
+        if (contentId.startsWith("cid:")) {
+            try {
+                contentId = URLDecoder.decode(contentId.substring(4), StandardCharsets.UTF_8.name());
+            } catch (UnsupportedEncodingException ue) {
+                contentId = contentId.substring(4);
+            }
+            return loadDataSource(contentId, atts);
+        } else if (contentId.indexOf("://") == -1) {
+            return loadDataSource(contentId, atts);
+        } else {
+            try {
+                return new URLDataSource(new URL(contentId));
+            } catch (MalformedURLException e) {
+                throw new Fault(e);
+            }
+        }
+
+    }
+
+    private static DataSource loadDataSource(String contentId, Collection<Attachment> atts) {
+        return new LazyDataSource(contentId, atts);
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/attachment/Base64DecoderStream.java b/transform/src/patch/java/org/apache/cxf/attachment/Base64DecoderStream.java
new file mode 100644
index 0000000..134553f
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/attachment/Base64DecoderStream.java
@@ -0,0 +1,196 @@
+/**
+ * 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.attachment;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.cxf.common.util.Base64Exception;
+import org.apache.cxf.common.util.Base64Utility;
+
+/**
+ * An implementation of a FilterInputStream that decodes the
+ * stream data in BASE64 encoding format.  This version does the
+ * decoding "on the fly" rather than decoding a single block of
+ * data.  Since this version is intended for use by the MimeUtilty class,
+ * it also handles line breaks in the encoded data.
+ */
+public class Base64DecoderStream extends FilterInputStream {
+
+    static final String MAIL_BASE64_IGNOREERRORS = "mail.mime.base64.ignoreerrors";
+
+    // number of decodeable units we'll try to process at one time.  We'll attempt to read that much
+    // data from the input stream and decode in blocks.
+    static final int BUFFERED_UNITS = 2000;
+
+    // can be overridden by a system property.
+    protected boolean ignoreErrors;
+
+    // buffer for reading in chars for decoding (which can support larger bulk reads)
+    protected char[] encodedChars = new char[BUFFERED_UNITS * 4];
+    // a buffer for one decoding unit's worth of data (3 bytes).
+    protected byte[] decodedChars;
+    // count of characters in the buffer
+    protected int decodedCount;
+    // index of the next decoded character
+    protected int decodedIndex;
+
+
+    public Base64DecoderStream(InputStream in) {
+        super(in);
+    }
+
+    /**
+     * Test for the existance of decoded characters in our buffer
+     * of decoded data.
+     *
+     * @return True if we currently have buffered characters.
+     */
+    private boolean dataAvailable() {
+        return decodedCount != 0;
+    }
+
+    /**
+     * Decode a requested number of bytes of data into a buffer.
+     *
+     * @return true if we were able to obtain more data, false otherwise.
+     */
+    private boolean decodeStreamData() throws IOException {
+        decodedIndex = 0;
+
+        // fill up a data buffer with input data
+        int readCharacters = fillEncodedBuffer();
+
+        if (readCharacters > 0) {
+            try {
+                decodedChars = Base64Utility.decodeChunk(encodedChars, 0, readCharacters);
+            } catch (Base64Exception e) {
+                throw new IOException(e);
+            }
+            decodedCount = decodedChars.length;
+            return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * Retrieve a single byte from the decoded characters buffer.
+     *
+     * @return The decoded character or -1 if there was an EOF condition.
+     */
+    private int getByte() throws IOException {
+        if (!dataAvailable() && !decodeStreamData()) {
+            return -1;
+        }
+        decodedCount--;
+        // we need to ensure this doesn't get sign extended
+        return decodedChars[decodedIndex++] & 0xff;
+    }
+
+    private int getBytes(byte[] data, int offset, int length) throws IOException {
+
+        int readCharacters = 0;
+        while (length > 0) {
+            // need data?  Try to get some
+            if (!dataAvailable() && !decodeStreamData()) {
+                // if we can't get this, return a count of how much we did get (which may be -1).
+                return readCharacters > 0 ? readCharacters : -1;
+            }
+
+            // now copy some of the data from the decoded buffer to the target buffer
+            int copyCount = Math.min(decodedCount, length);
+            System.arraycopy(decodedChars, decodedIndex, data, offset, copyCount);
+            decodedIndex += copyCount;
+            decodedCount -= copyCount;
+            offset += copyCount;
+            length -= copyCount;
+            readCharacters += copyCount;
+        }
+        return readCharacters;
+    }
+
+
+    /**
+     * Fill our buffer of input characters for decoding from the
+     * stream.  This will attempt read a full buffer, but will
+     * terminate on an EOF or read error.  This will filter out
+     * non-Base64 encoding chars and will only return a valid
+     * multiple of 4 number of bytes.
+     *
+     * @return The count of characters read.
+     */
+    private int fillEncodedBuffer() throws IOException {
+        int readCharacters = 0;
+
+        while (true) {
+            // get the next character from the stream
+            int ch = in.read();
+            // did we hit an EOF condition?
+            if (ch == -1) {
+                // now check to see if this is normal, or potentially an error
+                // if we didn't get characters as a multiple of 4, we may need to complain about this.
+                if ((readCharacters % 4) != 0) {
+                    throw new IOException("Base64 encoding error, data truncated: " + readCharacters + " "
+                                          + new String(encodedChars, 0, readCharacters));
+                }
+                // return the count.
+                return readCharacters;
+            } else if (Base64Utility.isValidBase64(ch)) {
+                // if this character is valid in a Base64 stream, copy it to the buffer.
+                encodedChars[readCharacters++] = (char)ch;
+                // if we've filled up the buffer, time to quit.
+                if (readCharacters >= encodedChars.length) {
+                    return readCharacters;
+                }
+            }
+
+            // we're filtering out whitespace and CRLF characters, so just ignore these
+        }
+    }
+
+
+    // in order to function as a filter, these streams need to override the different
+    // read() signature.
+
+    @Override
+    public int read() throws IOException {
+        return getByte();
+    }
+
+
+    @Override
+    public int read(byte [] buffer, int offset, int length) throws IOException {
+        return getBytes(buffer, offset, length);
+    }
+
+    
+    @Override
+    public boolean markSupported() {
+        return false;
+    }
+
+
+    @Override
+    public int available() throws IOException {
+        return ((in.available() / 4) * 3) + decodedCount;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/attachment/ContentDisposition.java b/transform/src/patch/java/org/apache/cxf/attachment/ContentDisposition.java
new file mode 100644
index 0000000..f9d4524
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/attachment/ContentDisposition.java
@@ -0,0 +1,144 @@
+/**
+ * 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.attachment;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ContentDisposition {
+    private static final String CD_HEADER_PARAMS_EXPRESSION =
+       "[\\w-]++( )?\\*?=( )?((\"[^\"]++\")|([^;]+))";
+    private static final Pattern CD_HEADER_PARAMS_PATTERN =
+            Pattern.compile(CD_HEADER_PARAMS_EXPRESSION);
+
+    private static final String CD_HEADER_EXT_PARAMS_EXPRESSION =
+            "(?i)(UTF-8|ISO-8859-1)''((?:%[0-9a-f]{2}|\\S)+)";
+    private static final Pattern CD_HEADER_EXT_PARAMS_PATTERN =
+            Pattern.compile(CD_HEADER_EXT_PARAMS_EXPRESSION);
+    private static final Pattern CODEPOINT_ENCODED_VALUE_PATTERN = Pattern.compile("&#[0-9]{4};|\\S");
+    
+    private static final String FILE_NAME = "filename";
+
+    private String value;
+    private String type;
+    private Map<String, String> params = new LinkedHashMap<>();
+
+    public ContentDisposition(String value) {
+        this.value = value;
+
+        String tempValue = value;
+
+        int index = tempValue.indexOf(';');
+        if (index > 0 && (tempValue.indexOf('=') >= index)) {
+            type = tempValue.substring(0, index).trim();
+            tempValue = tempValue.substring(index + 1);
+        }
+
+        String extendedFilename = null;
+        Matcher m = CD_HEADER_PARAMS_PATTERN.matcher(tempValue);
+        while (m.find()) {
+            final String paramName;
+            String paramValue = "";
+
+            String groupValue = m.group().trim();
+            int eqIndex = groupValue.indexOf('=');
+            if (eqIndex > 0) {
+                paramName = groupValue.substring(0, eqIndex).trim();
+                if (eqIndex + 1 != groupValue.length()) {
+                    paramValue = groupValue.substring(eqIndex + 1).trim().replace("\"", "");
+                }
+            } else {
+                paramName = groupValue;
+            }
+            // filename* looks like the only CD param that is human readable
+            // and worthy of the extended encoding support. Other parameters
+            // can be supported if needed, see the complete list below
+            /*
+                http://www.iana.org/assignments/cont-disp/cont-disp.xhtml#cont-disp-2
+
+                filename            name to be used when creating file [RFC2183]
+                creation-date       date when content was created [RFC2183]
+                modification-date   date when content was last modified [RFC2183]
+                read-date           date when content was last read [RFC2183]
+                size                approximate size of content in octets [RFC2183]
+                name                original field name in form [RFC2388]
+                voice               type or use of audio content [RFC2421]
+                handling            whether or not processing is required [RFC3204]
+             */
+            if ("filename*".equalsIgnoreCase(paramName)) {
+                // try to decode the value if it matches the spec
+                try {
+                    Matcher matcher = CD_HEADER_EXT_PARAMS_PATTERN.matcher(paramValue);
+                    if (matcher.matches()) {
+                        String encodingScheme = matcher.group(1);
+                        String encodedValue = matcher.group(2);
+                        paramValue = Rfc5987Util.decode(encodedValue, encodingScheme);
+                        extendedFilename = paramValue;
+                    }
+                } catch (UnsupportedEncodingException e) {
+                    // would be odd not to support UTF-8 or 8859-1
+                }
+            } else if (FILE_NAME.equalsIgnoreCase(paramName) && paramValue.contains("&#")) {
+                Matcher matcher = CODEPOINT_ENCODED_VALUE_PATTERN.matcher(paramValue);
+                StringBuilder sb = new StringBuilder();
+                while (matcher.find()) {
+                    String matched = matcher.group();
+                    if (matched.startsWith("&#")) {
+                        int codePoint = Integer.parseInt(matched.substring(2, 6));
+                        sb.append(Character.toChars(codePoint));
+                    } else {
+                        sb.append(matched.charAt(0));
+                    }
+                }
+                if (sb.length() > 0) {
+                    paramValue = sb.toString();
+                }
+            }
+            params.put(paramName.toLowerCase(), paramValue);
+        }
+        if (extendedFilename != null) {
+            params.put(FILE_NAME, extendedFilename);
+        }
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public String getFilename() {
+        return params.get(FILE_NAME);
+    }
+
+    public String getParameter(String name) {
+        return params.get(name);
+    }
+
+    public Map<String, String> getParameters() {
+        return Collections.unmodifiableMap(params);
+    }
+
+    public String toString() {
+        return value;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/attachment/LazyAttachmentCollection.java b/transform/src/patch/java/org/apache/cxf/attachment/LazyAttachmentCollection.java
new file mode 100644
index 0000000..8dd4ace
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/attachment/LazyAttachmentCollection.java
@@ -0,0 +1,362 @@
+/**
+ * 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.attachment;
+
+import java.io.IOException;
+import java.util.AbstractCollection;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.activation.DataHandler;
+
+import org.apache.cxf.message.Attachment;
+
+public class LazyAttachmentCollection
+    implements Collection<Attachment> {
+
+    private AttachmentDeserializer deserializer;
+    private final List<Attachment> attachments = new ArrayList<>();
+    private final int maxAttachmentCount;
+
+    public LazyAttachmentCollection(AttachmentDeserializer deserializer, int maxAttachmentCount) {
+        super();
+        this.deserializer = deserializer;
+        this.maxAttachmentCount = maxAttachmentCount;
+    }
+
+    public List<Attachment> getLoadedAttachments() {
+        return attachments;
+    }
+
+    private void loadAll() {
+        try {
+            Attachment a = deserializer.readNext();
+            int count = 0;
+            while (a != null) {
+                attachments.add(a);
+                count++;
+                if (count > maxAttachmentCount) {
+                    throw new IOException("The message contains more attachments than are permitted");
+                }
+                a = deserializer.readNext();
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+    /**
+     * Check for more attachments by attempting to deserialize the next attachment.
+     *
+     * @param shouldLoadNew if <i>false</i>, the "loaded attachments" List will not be changed.
+     * @return there is more attachment or not
+     * @throws IOException
+     */
+    public boolean hasNext(boolean shouldLoadNew) throws IOException {
+        if (shouldLoadNew) {
+            Attachment a = deserializer.readNext();
+            if (a != null) {
+                attachments.add(a);
+                return true;
+            }
+            return false;
+        }
+        return deserializer.hasNext();
+    }
+
+    public boolean hasNext() throws IOException {
+        return hasNext(true);
+    }
+    public Iterator<Attachment> iterator() {
+        return new Iterator<Attachment>() {
+            int current;
+            boolean removed;
+
+            public boolean hasNext() {
+                if (attachments.size() > current) {
+                    return true;
+                }
+
+                // check if there is another attachment
+                try {
+                    Attachment a = deserializer.readNext();
+                    if (a == null) {
+                        return false;
+                    }
+                    attachments.add(a);
+                    return true;
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+
+            @Override
+            public Attachment next() {
+                Attachment a = attachments.get(current);
+                current++;
+                removed = false;
+                return a;
+            }
+
+            @Override
+            public void remove() {
+                if (removed) {
+                    throw new IllegalStateException();
+                }
+                attachments.remove(--current);
+                removed = true;
+            }
+
+        };
+    }
+
+    public int size() {
+        loadAll();
+
+        return attachments.size();
+    }
+
+    public boolean add(Attachment arg0) {
+        return attachments.add(arg0);
+    }
+
+    public boolean addAll(Collection<? extends Attachment> arg0) {
+        return attachments.addAll(arg0);
+    }
+
+    public void clear() {
+        attachments.clear();
+    }
+
+    public boolean contains(Object arg0) {
+        return attachments.contains(arg0);
+    }
+
+    public boolean containsAll(Collection<?> arg0) {
+        return attachments.containsAll(arg0);
+    }
+
+    public boolean isEmpty() {
+        if (attachments.isEmpty()) {
+            return !iterator().hasNext();
+        }
+        return attachments.isEmpty();
+    }
+
+    public boolean remove(Object arg0) {
+        return attachments.remove(arg0);
+    }
+
+    public boolean removeAll(Collection<?> arg0) {
+        return attachments.removeAll(arg0);
+    }
+
+    public boolean retainAll(Collection<?> arg0) {
+        return attachments.retainAll(arg0);
+    }
+
+    public Object[] toArray() {
+        loadAll();
+
+        return attachments.toArray();
+    }
+
+    public <T> T[] toArray(T[] arg0) {
+        loadAll();
+
+        return attachments.toArray(arg0);
+    }
+
+    public Map<String, DataHandler> createDataHandlerMap() {
+        return new LazyAttachmentMap(this);
+    }
+
+    private static class LazyAttachmentMap implements Map<String, DataHandler> {
+        LazyAttachmentCollection collection;
+
+        LazyAttachmentMap(LazyAttachmentCollection c) {
+            collection = c;
+        }
+
+        public void clear() {
+            collection.clear();
+        }
+
+        public boolean containsKey(Object key) {
+            Iterator<Attachment> it = collection.iterator();
+            while (it.hasNext()) {
+                Attachment at = it.next();
+                if (key.equals(at.getId())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public boolean containsValue(Object value) {
+            Iterator<Attachment> it = collection.iterator();
+            while (it.hasNext()) {
+                Attachment at = it.next();
+                if (value.equals(at.getDataHandler())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public DataHandler get(Object key) {
+            Iterator<Attachment> it = collection.iterator();
+            while (it.hasNext()) {
+                Attachment at = it.next();
+                if (key.equals(at.getId())) {
+                    return at.getDataHandler();
+                }
+            }
+            return null;
+        }
+
+        public boolean isEmpty() {
+            return collection.isEmpty();
+        }
+        public int size() {
+            return collection.size();
+        }
+
+        public DataHandler remove(Object key) {
+            Iterator<Attachment> it = collection.iterator();
+            while (it.hasNext()) {
+                Attachment at = it.next();
+                if (key.equals(at.getId())) {
+                    collection.remove(at);
+                    return at.getDataHandler();
+                }
+            }
+            return null;
+        }
+        public DataHandler put(String key, DataHandler value) {
+            Attachment at = new AttachmentImpl(key, value);
+            collection.add(at);
+            return value;
+        }
+
+        public void putAll(Map<? extends String, ? extends DataHandler> t) {
+            for (Map.Entry<? extends String, ? extends DataHandler> ent : t.entrySet()) {
+                put(ent.getKey(), ent.getValue());
+            }
+        }
+
+
+        public Set<Map.Entry<String, DataHandler>> entrySet() {
+            return new AbstractSet<Map.Entry<String, DataHandler>>() {
+                public Iterator<Map.Entry<String, DataHandler>> iterator() {
+                    return new Iterator<Map.Entry<String, DataHandler>>() {
+                        Iterator<Attachment> it = collection.iterator();
+                        public boolean hasNext() {
+                            return it.hasNext();
+                        }
+                        public Map.Entry<String, DataHandler> next() {
+                            return new Map.Entry<String, DataHandler>() {
+                                Attachment at = it.next();
+                                public String getKey() {
+                                    return at.getId();
+                                }
+                                public DataHandler getValue() {
+                                    return at.getDataHandler();
+                                }
+                                public DataHandler setValue(DataHandler value) {
+                                    if (at instanceof AttachmentImpl) {
+                                        DataHandler h = at.getDataHandler();
+                                        ((AttachmentImpl)at).setDataHandler(value);
+                                        return h;
+                                    }
+                                    throw new UnsupportedOperationException();
+                                }
+                            };
+                        }
+                        @Override
+                        public void remove() {
+                            it.remove();
+                        }
+                    };
+                }
+                public int size() {
+                    return collection.size();
+                }
+            };
+        }
+
+        public Set<String> keySet() {
+            return new AbstractSet<String>() {
+                public Iterator<String> iterator() {
+                    return new Iterator<String>() {
+                        Iterator<Attachment> it = collection.iterator();
+                        public boolean hasNext() {
+                            return it.hasNext();
+                        }
+
+                        public String next() {
+                            return it.next().getId();
+                        }
+
+                        @Override
+                        public void remove() {
+                            it.remove();
+                        }
+                    };
+                }
+
+                public int size() {
+                    return collection.size();
+                }
+            };
+        }
+
+
+        public Collection<DataHandler> values() {
+            return new AbstractCollection<DataHandler>() {
+                public Iterator<DataHandler> iterator() {
+                    return new Iterator<DataHandler>() {
+                        Iterator<Attachment> it = collection.iterator();
+                        public boolean hasNext() {
+                            return it.hasNext();
+                        }
+                        public DataHandler next() {
+                            return it.next().getDataHandler();
+                        }
+                        @Override
+                        public void remove() {
+                            it.remove();
+                        }
+                    };
+                }
+
+                public int size() {
+                    return collection.size();
+                }
+            };
+        }
+
+    }
+
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/attachment/MimeBodyPartInputStream.java b/transform/src/patch/java/org/apache/cxf/attachment/MimeBodyPartInputStream.java
new file mode 100644
index 0000000..ab80b89
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/attachment/MimeBodyPartInputStream.java
@@ -0,0 +1,275 @@
+/**
+ * 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.attachment;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+
+public class MimeBodyPartInputStream extends InputStream {
+
+    PushbackInputStream inStream;
+
+    boolean boundaryFound;
+    int pbAmount;
+    byte[] boundary;
+    byte[] boundaryBuffer;
+
+    private boolean closed;
+
+    public MimeBodyPartInputStream(PushbackInputStream inStreamParam,
+                                   byte[] boundaryParam,
+                                   int pbsize) {
+        super();
+        this.inStream = inStreamParam;
+        this.boundary = boundaryParam;
+        this.pbAmount = pbsize;
+    }
+
+    @Override
+    public int read(byte[] buf, int origOff, int origLen) throws IOException {
+        byte[] b = buf;
+        int off = origOff;
+        int len = origLen;
+        if (boundaryFound || closed) {
+            return -1;
+        }
+        if ((off < 0) || (off > b.length) || (len < 0)
+            || ((off + len) > b.length) || ((off + len) < 0)) {
+
+            throw new IndexOutOfBoundsException();
+        }
+        if (len == 0) {
+            return 0;
+        }
+        boolean bufferCreated = false;
+        if (len < boundary.length * 2) {
+            //buffer is too short to detect boundaries with it.  We'll need to create a larger buffer
+            bufferCreated = true;
+            if (boundaryBuffer == null) {
+                boundaryBuffer = new byte[boundary.length * 2];
+            }
+            b = boundaryBuffer;
+            off = 0;
+            len = boundaryBuffer.length;
+        }
+        if (len > pbAmount) {
+            len = pbAmount;  //can only pushback that much so make sure we can
+        }
+        int read = 0;
+        int idx = 0;
+        while (read >= 0 && idx < len && idx < (boundary.length * 2)) {
+            //make sure we read enough to detect the boundary
+            read = inStream.read(b, off + idx, len - idx);
+            if (read != -1) {
+                idx += read;
+            }
+        }
+        if (read == -1 && idx == 0) {
+            return -1;
+        }
+        len = idx;
+
+        int i = processBuffer(b, off, len);
+        if (bufferCreated && i > 0) {
+            // read more than we need, push it back
+            if (origLen >= i) {
+                System.arraycopy(b, 0, buf, origOff, i);
+            } else {
+                System.arraycopy(b, 0, buf, origOff, origLen);
+                inStream.unread(b, origLen, i - origLen);
+                i = origLen;
+            }
+        } else if (i == 0 && boundaryFound) {
+            return -1;
+        }
+
+        return i;
+    }
+
+    //Has Data after encountering CRLF
+    private boolean hasData(byte[] b, int initialPointer, int pointer, int off, int len)
+        throws IOException {
+        if (pointer < (off + len)) {
+            return true;
+        } else if (pointer >= 1000000000) {
+            inStream.unread(b, initialPointer, (off + len) - initialPointer);
+            return false;
+        } else {
+            int x = inStream.read();
+            if (x != -1) {
+                inStream.unread(x);
+                inStream.unread(b, initialPointer, (off + len) - initialPointer);
+                return false;
+            }
+            return true;
+        }
+    }
+
+    protected int processBuffer(byte[] buffer, int off, int len) throws IOException {
+        for (int i = off; i < (off + len); i++) {
+            boolean needUnread0d0a = false;
+            int value = buffer[i];
+            int initialI = i;
+            if (value == 13) {
+                if (!hasData(buffer, initialI, initialI + 1, off, len)) {
+                    return initialI - off;
+                }
+                value = buffer[initialI + 1];
+                if (value != 10) {
+                    continue;
+                }
+                if (!hasData(buffer, initialI, initialI + 2, off, len)) {
+                    return initialI - off;
+                }
+                value = buffer[initialI + 2];
+                if ((byte) value != boundary[0]) {
+                    i++;
+                    continue;
+                }
+                needUnread0d0a = true;
+                i += 2; //i after this points to boundary[0] element
+            } else if (value != boundary[0]) {
+                continue;
+            }
+
+            int boundaryIndex = 0;
+            while ((boundaryIndex < boundary.length) && (value == boundary[boundaryIndex])) {
+                if (!hasData(buffer, initialI, i + 1, off, len)) {
+                    return initialI - off;
+                }
+                value = buffer[++i];
+                boundaryIndex++;
+            }
+            if (boundaryIndex == boundary.length) {
+                // read the end of line character
+                if (initialI != off) {
+                    i = 1000000000;
+                }
+                if (initialI - off != 0
+                    && !hasData(buffer, initialI, i + 1, off, len)) {
+                    return initialI - off;
+                }
+                boundaryFound = true;
+                int j = i + 1;
+                if (j < len && buffer[j] == 45 && value == 45) {
+                    // Last mime boundary should have a succeeding "--"
+                    // as we are on it, read the terminating CRLF
+                    i += 2;
+                    //last mime boundary
+                }
+
+                //boundary matched (may or may not be last mime boundary)
+                int processed = initialI - off;
+                if ((len - ((i - off) + 2)) > 0) {
+                    inStream.unread(buffer, i + 2, len - (i + 2) + off);
+                }
+                return processed;
+            }
+
+            // Boundary not found. Restoring bytes skipped.
+            // write first skipped byte, push back the rest
+            if (value != -1) { //pushing back first byte of boundary
+                // Stream might have ended
+                i--;
+            }
+            if (needUnread0d0a) { //Pushing all,  returning 13
+                i = i - boundaryIndex;
+                i--; //for 10
+//                value = 13;
+            } else {
+                i = i - boundaryIndex;
+                i++;
+//                value = boundary[0];
+            }
+        }
+        return len;
+    }
+
+    public int read() throws IOException {
+        boolean needUnread0d0a = false;
+        if (boundaryFound) {
+            return -1;
+        }
+
+        // read the next value from stream
+        int value = inStream.read();
+        // A problem occurred because all the mime parts tends to have a /r/n
+        // at the end. Making it hard to transform them to correct
+        // DataSources.
+        // This logic introduced to handle it
+        if (value == 13) {
+            value = inStream.read();
+            if (value != 10) {
+                inStream.unread(value);
+                return 13;
+            }
+            value = inStream.read();
+            if ((byte) value != boundary[0]) {
+                inStream.unread(value);
+                inStream.unread(10);
+                return 13;
+            }
+            needUnread0d0a = true;
+        } else if ((byte) value != boundary[0]) {
+            return value;
+        }
+        // read value is the first byte of the boundary. Start matching the
+        // next characters to find a boundary
+        int boundaryIndex = 0;
+        while ((boundaryIndex < boundary.length) && ((byte) value == boundary[boundaryIndex])) {
+            value = inStream.read();
+            boundaryIndex++;
+        }
+        if (boundaryIndex == boundary.length) {
+            // boundary found
+            boundaryFound = true;
+            int dashNext = inStream.read();
+            // read the end of line character
+            if (dashNext == 45 && value == 45) {
+                // Last mime boundary should have a succeeding "--"
+                // as we are on it, read the terminating CRLF
+                inStream.read();
+                inStream.read();
+            }
+            return -1;
+        }
+        // Boundary not found. Restoring bytes skipped.
+        // write first skipped byte, push back the rest
+        if (value != -1) {
+            // Stream might have ended
+            inStream.unread(value);
+        }
+        if (needUnread0d0a) {
+            inStream.unread(boundary, 0, boundaryIndex);
+            inStream.unread(10);
+            value = 13;
+        } else {
+            inStream.unread(boundary, 1, boundaryIndex - 1);
+            value = boundary[0];
+        }
+        return value;
+    }
+
+    @Override
+    public void close() throws IOException {
+        this.closed = true;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/bus/CXFBusFactory.java b/transform/src/patch/java/org/apache/cxf/bus/CXFBusFactory.java
new file mode 100644
index 0000000..fb3a833
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/bus/CXFBusFactory.java
@@ -0,0 +1,47 @@
+/**
+ * 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.bus;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.bus.extension.ExtensionManagerBus;
+
+public class CXFBusFactory extends BusFactory {
+
+    public Bus createBus() {
+        return createBus(new HashMap<>());
+    }
+
+    public Bus createBus(Map<Class<?>, Object> e) {
+        return createBus(e, new HashMap<>());
+    }
+
+    public Bus createBus(Map<Class<?>, Object> e, Map<String, Object> properties) {
+        ExtensionManagerBus bus = new ExtensionManagerBus(e, properties);
+        possiblySetDefaultBus(bus);
+        initializeBus(bus);
+        bus.initialize();
+        return bus;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/bus/blueprint/BundleDelegatingClassLoader.java b/transform/src/patch/java/org/apache/cxf/bus/blueprint/BundleDelegatingClassLoader.java
new file mode 100644
index 0000000..ff957c2
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/bus/blueprint/BundleDelegatingClassLoader.java
@@ -0,0 +1,134 @@
+/**
+ * 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.bus.blueprint;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * A ClassLoader delegating to a given OSGi bundle.
+ */
+public class BundleDelegatingClassLoader extends ClassLoader {
+
+    private final Bundle bundle;
+    private final ClassLoader classLoader;
+
+    public BundleDelegatingClassLoader(Bundle bundle) {
+        this(bundle, null);
+    }
+
+    public BundleDelegatingClassLoader(Bundle bundle, ClassLoader classLoader) {
+        this.bundle = bundle;
+        this.classLoader = classLoader;
+    }
+
+    @Override
+    protected Class<?> findClass(final String name) throws ClassNotFoundException {
+        try {
+            return AccessController.doPrivileged(new PrivilegedExceptionAction<Class<?>>() {
+                public Class<?> run() throws ClassNotFoundException {
+                    return bundle.loadClass(name);
+                }
+            });
+        } catch (PrivilegedActionException e) {
+            Exception cause = e.getException();
+
+            if (cause instanceof ClassNotFoundException) {
+                throw (ClassNotFoundException)cause;
+            }
+            throw (RuntimeException)cause;
+        }
+    }
+
+    @Override
+    protected URL findResource(final String name) {
+        URL resource = AccessController.doPrivileged(new PrivilegedAction<URL>() {
+            public URL run() {
+                return bundle.getResource(name);
+            }
+        });
+        if (classLoader != null && resource == null) {
+            resource = classLoader.getResource(name);
+        }
+        return resource;
+    }
+
+    @Override
+    protected Enumeration<URL> findResources(final String name) throws IOException {
+        Enumeration<URL> urls;
+        try {
+            urls = AccessController.doPrivileged(new PrivilegedExceptionAction<Enumeration<URL>>() {
+                public Enumeration<URL> run() throws IOException {
+                    return bundle.getResources(name);
+                }
+
+            });
+        } catch (PrivilegedActionException e) {
+            Exception cause = e.getException();
+
+            if (cause instanceof IOException) {
+                throw (IOException)cause;
+            }
+            throw (RuntimeException)cause;
+        }
+
+        if (urls == null) {
+            urls = Collections.enumeration(new ArrayList<>());
+        }
+
+        return urls;
+    }
+
+    @Override
+    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        Class<?> clazz;
+        try {
+            clazz = findClass(name);
+        } catch (ClassNotFoundException cnfe) {
+            if (classLoader != null) {
+                try {
+                    clazz = classLoader.loadClass(name);
+                } catch (ClassNotFoundException e) {
+                    throw new ClassNotFoundException(name + " from bundle " + bundle.getBundleId()
+                                                     + " (" + bundle.getSymbolicName() + ")", cnfe);
+                }
+            } else {
+                throw new ClassNotFoundException(name + " from bundle " + bundle.getBundleId()
+                                                 + " (" + bundle.getSymbolicName() + ")", cnfe);
+            }
+        }
+        if (resolve) {
+            resolveClass(clazz);
+        }
+        return clazz;
+    }
+
+    public Bundle getBundle() {
+        return bundle;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/bus/blueprint/BusDefinitionParser.java b/transform/src/patch/java/org/apache/cxf/bus/blueprint/BusDefinitionParser.java
new file mode 100644
index 0000000..baf83b7
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/bus/blueprint/BusDefinitionParser.java
@@ -0,0 +1,72 @@
+/**
+ * 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.bus.blueprint;
+
+
+import org.w3c.dom.Element;
+
+import org.apache.aries.blueprint.ParserContext;
+import org.apache.aries.blueprint.mutable.MutableBeanMetadata;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.configuration.blueprint.AbstractBPBeanDefinitionParser;
+import org.osgi.service.blueprint.reflect.Metadata;
+
+public class BusDefinitionParser
+    extends AbstractBPBeanDefinitionParser {
+
+    public BusDefinitionParser() {
+    }
+
+    public Metadata parse(Element element, ParserContext context) {
+        String bname = element.hasAttribute("bus") ? element.getAttribute("bus") : "cxf";
+        String id = element.hasAttribute("id") ? element.getAttribute("id") : null;
+        MutableBeanMetadata cxfBean = getBus(context, bname);
+        parseAttributes(element, context, cxfBean);
+        parseChildElements(element, context, cxfBean);
+        context.getComponentDefinitionRegistry().removeComponentDefinition(bname);
+        if (!StringUtils.isEmpty(id)) {
+            cxfBean.addProperty("id", createValue(context, id));
+        }
+        return cxfBean;
+    }
+    
+    @Override
+    protected void processBusAttribute(Element element, ParserContext ctx,
+                                       MutableBeanMetadata bean, String val) {
+        //nothing
+    }
+    
+    @Override
+    protected boolean hasBusProperty() {
+        return false;
+    }
+
+
+    @Override
+    protected void mapElement(ParserContext ctx, MutableBeanMetadata bean, Element el, String name) {
+        if ("inInterceptors".equals(name) || "inFaultInterceptors".equals(name)
+            || "outInterceptors".equals(name) || "outFaultInterceptors".equals(name)
+            || "features".equals(name)) {
+            bean.addProperty(name, parseListData(ctx, bean, el));
+        } else if ("properties".equals(name)) {
+            bean.addProperty(name, parseMapData(ctx, bean, el));
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/bus/blueprint/ConfigurerImpl.java b/transform/src/patch/java/org/apache/cxf/bus/blueprint/ConfigurerImpl.java
new file mode 100644
index 0000000..37f82fe
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/bus/blueprint/ConfigurerImpl.java
@@ -0,0 +1,180 @@
+/**
+ * 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.bus.blueprint;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.aries.blueprint.services.ExtendedBlueprintContainer;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.configuration.Configurable;
+import org.apache.cxf.configuration.Configurer;
+import org.osgi.service.blueprint.container.BlueprintContainer;
+import org.osgi.service.blueprint.container.NoSuchComponentException;
+import org.osgi.service.blueprint.reflect.BeanMetadata;
+import org.osgi.service.blueprint.reflect.ComponentMetadata;
+
+/**
+ *
+ */
+public class ConfigurerImpl implements Configurer {
+    private static final Logger LOG = LogUtils.getL7dLogger(ConfigurerImpl.class);
+    BlueprintContainer container;
+
+    private final Map<String, List<MatcherHolder>> wildCardBeanDefinitions
+        = new TreeMap<>();
+
+    static class MatcherHolder implements Comparable<MatcherHolder> {
+        Matcher matcher;
+        String wildCardId;
+        MatcherHolder(String orig, Matcher matcher) {
+            wildCardId = orig;
+            this.matcher = matcher;
+        }
+        @Override
+        public int compareTo(MatcherHolder mh) {
+            int literalCharsLen1 = this.wildCardId.replace("*", "").length();
+            int literalCharsLen2 = mh.wildCardId.replace("*", "").length();
+            // The expression with more literal characters should end up on the top of the list
+            return Integer.compare(literalCharsLen1, literalCharsLen2) * -1;
+        }
+    }
+
+    public ConfigurerImpl(BlueprintContainer con) {
+        container = con;
+        initializeWildcardMap();
+    }
+    private boolean isWildcardBeanName(String bn) {
+        return bn.indexOf('*') != -1 || bn.indexOf('?') != -1
+            || (bn.indexOf('(') != -1 && bn.indexOf(')') != -1);
+    }
+
+    private void initializeWildcardMap() {
+        for (String s : container.getComponentIds()) {
+            if (isWildcardBeanName(s)) {
+                ComponentMetadata cmd = container.getComponentMetadata(s);
+                Class<?> cls = BlueprintBeanLocator.getClassForMetaData(container, cmd);
+                if (cls != null) {
+                    final String cid = s.charAt(0) != '*' ? s
+                            : "." + s.replaceAll("\\.", "\\."); //old wildcard
+                    Matcher matcher = Pattern.compile(cid).matcher("");
+                    List<MatcherHolder> m = wildCardBeanDefinitions.get(cls.getName());
+                    if (m == null) {
+                        m = new ArrayList<>();
+                        wildCardBeanDefinitions.put(cls.getName(), m);
+                    }
+                    MatcherHolder holder = new MatcherHolder(s, matcher);
+                    m.add(holder);
+                }
+            }
+        }
+    }
+
+    public void configureBean(Object beanInstance) {
+        configureBean(null, beanInstance, true);
+    }
+
+    public void configureBean(String bn, Object beanInstance) {
+        configureBean(bn, beanInstance, true);
+    }
+    public synchronized void configureBean(String bn, Object beanInstance, boolean checkWildcards) {
+        if (null == bn) {
+            bn = getBeanName(beanInstance);
+        }
+
+        if (null == bn) {
+            return;
+        }
+        if (checkWildcards) {
+            configureWithWildCard(bn, beanInstance);
+        }
+
+        if (container instanceof ExtendedBlueprintContainer) {
+            try {
+                final ComponentMetadata cm = container.getComponentMetadata(bn);
+                if (cm instanceof BeanMetadata) {
+                    ((ExtendedBlueprintContainer)container).injectBeanInstance((BeanMetadata)cm, beanInstance);
+                }
+            } catch (NoSuchComponentException nsce) {
+            }
+        }
+    }
+
+    private void configureWithWildCard(String bn, Object beanInstance) {
+        if (!wildCardBeanDefinitions.isEmpty()) {
+            Class<?> clazz = beanInstance.getClass();
+            while (!Object.class.equals(clazz)) {
+                String className = clazz.getName();
+                List<MatcherHolder> matchers = wildCardBeanDefinitions.get(className);
+                if (matchers != null) {
+                    for (MatcherHolder m : matchers) {
+                        synchronized (m.matcher) {
+                            m.matcher.reset(bn);
+                            if (m.matcher.matches()) {
+                                configureBean(m.wildCardId, beanInstance, false);
+                                return;
+                            }
+                        }
+                    }
+                }
+                clazz = clazz.getSuperclass();
+            }
+        }
+    }
+
+    protected String getBeanName(Object beanInstance) {
+        if (beanInstance instanceof Configurable) {
+            return ((Configurable)beanInstance).getBeanName();
+        }
+        String beanName = null;
+        Method m = null;
+        try {
+            m = beanInstance.getClass().getDeclaredMethod("getBeanName", (Class[])null);
+        } catch (NoSuchMethodException ex) {
+            try {
+                m = beanInstance.getClass().getMethod("getBeanName", (Class[])null);
+            } catch (NoSuchMethodException e) {
+                //ignore
+            }
+        }
+        if (m != null) {
+            try {
+                beanName = (String)(m.invoke(beanInstance));
+            } catch (Exception ex) {
+                LogUtils.log(LOG, Level.WARNING, "ERROR_DETERMINING_BEAN_NAME_EXC", ex);
+            }
+        }
+
+        if (null == beanName) {
+            LogUtils.log(LOG, Level.FINE, "COULD_NOT_DETERMINE_BEAN_NAME_MSG",
+                         beanInstance.getClass().getName());
+        }
+
+        return beanName;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/bus/extension/Extension.java b/transform/src/patch/java/org/apache/cxf/bus/extension/Extension.java
new file mode 100644
index 0000000..03f0dc6
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/bus/extension/Extension.java
@@ -0,0 +1,291 @@
+/**
+ * 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.bus.extension;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.i18n.Message;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.StringUtils;
+
+public class Extension {
+    protected static final Logger LOG = LogUtils.getL7dLogger(Extension.class);
+    
+    private static final String PROBLEM_CREATING_EXTENSION_CLASS = "PROBLEM_CREATING_EXTENSION_CLASS";
+
+    protected String className;
+    protected ClassLoader classloader;
+    protected volatile Class<?> clazz;
+    protected volatile Class<?> intf;
+    protected String interfaceName;
+    protected boolean deferred;
+    protected Collection<String> namespaces = new ArrayList<>();
+    protected Object[] args;
+    protected volatile Object obj;
+    protected boolean optional;
+    protected boolean notFound;
+    
+
+    public Extension() {
+    }
+
+    public Extension(Class<?> cls, Class<?> inf) {
+        clazz = cls;
+        intf = inf;
+        interfaceName = inf.getName();
+        className = cls.getName();
+        classloader = cls.getClassLoader();
+    }
+    public Extension(Class<?> cls) {
+        clazz = cls;
+        className = cls.getName();
+        classloader = cls.getClassLoader();
+    }
+    public Extension(ClassLoader loader) {
+        classloader = loader;
+    }
+
+    public Extension(Extension ext) {
+        className = ext.className;
+        interfaceName = ext.interfaceName;
+        deferred = ext.deferred;
+        namespaces = ext.namespaces;
+        obj = ext.obj;
+        clazz = ext.clazz;
+        intf = ext.intf;
+        classloader = ext.classloader;
+        args = ext.args;
+        optional = ext.optional;
+    }
+
+    public void setOptional(boolean b) {
+        optional = b;
+    }
+    public boolean isOptional() {
+        return optional;
+    }
+
+    public String getName() {
+        return StringUtils.isEmpty(interfaceName) ? className : interfaceName;
+    }
+    public Object getLoadedObject() {
+        return obj;
+    }
+
+    public Extension cloneNoObject() {
+        Extension ext = new Extension(this);
+        ext.obj = null;
+        ext.clazz = null;
+        ext.intf = null;
+        return ext;
+    }
+
+    public String toString() {
+        StringBuilder buf = new StringBuilder(128);
+        buf.append("class: ");
+        buf.append(className);
+        buf.append(", interface: ");
+        buf.append(interfaceName);
+        buf.append(", deferred: ");
+        buf.append(deferred ? "true" : "false");
+        buf.append(", namespaces: (");
+        int n = 0;
+        for (String ns : namespaces) {
+            if (n > 0) {
+                buf.append(", ");
+            }
+            buf.append(ns);
+            n++;
+        }
+        buf.append(')');
+        return buf.toString();
+    }
+
+    public String getClassname() {
+        return className;
+    }
+
+    public void setClassname(String i) {
+        clazz = null;
+        notFound = false;
+        className = i;
+    }
+
+    public String getInterfaceName() {
+        return interfaceName;
+    }
+
+    public void setInterfaceName(String i) {
+        interfaceName = i;
+        notFound = false;
+    }
+
+    public boolean isDeferred() {
+        return deferred;
+    }
+
+    public void setDeferred(boolean d) {
+        deferred = d;
+    }
+
+    public Collection<String> getNamespaces() {
+        return namespaces;
+    }
+
+    public void setArgs(Object[] a) {
+        args = a;
+    }
+
+    protected Class<?> tryClass(String name, ClassLoader cl) {
+        Throwable origEx = null;
+        if (classloader != null) {
+            try {
+                return classloader.loadClass(name);
+            } catch (Throwable nex) {
+                //ignore, fall into the stuff below
+                //save the exception though as this is likely the important one
+                origEx = nex;
+            }
+        }
+        try {
+            return cl.loadClass(name);
+        } catch (Throwable ex) {
+            try {
+                // using the extension classloader as a fallback
+                return this.getClass().getClassLoader().loadClass(name);
+            } catch (Throwable nex) {
+                notFound = true;
+                if (!optional) {
+                    throw new ExtensionException(new Message("PROBLEM_LOADING_EXTENSION_CLASS", LOG, name),
+                        origEx != null ? origEx : ex);
+                }
+            }
+        }
+        return null;
+    }
+
+    public Class<?> getClassObject(ClassLoader cl) {
+        if (notFound) {
+            return null;
+        }
+        if (clazz != null) {
+            return clazz;
+        }
+        synchronized (this) {
+            if (clazz == null) {
+                clazz = tryClass(className, cl);
+            }
+        }
+        return clazz;
+    }
+    public Object load(ClassLoader cl, Bus b) {
+        if (obj != null) {
+            return obj;
+        }
+        Class<?> cls = getClassObject(cl);
+        try {
+            if (notFound) {
+                return null;
+            }
+            try {
+                //if there is a Bus constructor, use it.
+                if (b != null && args == null) {
+                    Constructor<?> con = cls.getConstructor(Bus.class);
+                    obj = con.newInstance(b);
+                    return obj;
+                } else if (b != null && args != null) {
+                    try {
+                        obj = cls.getConstructor(Bus.class, Object[].class).newInstance(b, args);
+                    } catch (NoSuchMethodException ex) { // no bus
+                        obj = cls.getConstructor(Object[].class).newInstance(args);
+                    }
+                    return obj;
+                } else if (args != null) {
+                    Constructor<?> con = cls.getConstructor(Object[].class);
+                    obj = con.newInstance(args);
+                    return obj;
+                }
+            } catch (InvocationTargetException ex) {
+                throw new ExtensionException(new Message(PROBLEM_CREATING_EXTENSION_CLASS, LOG, cls.getName()),
+                                             ex.getCause());
+            } catch (InstantiationException | SecurityException ex) {
+                throw new ExtensionException(new Message(PROBLEM_CREATING_EXTENSION_CLASS, LOG, cls.getName()), ex);
+            } catch (NoSuchMethodException e) {
+                //ignore
+            }
+            obj = cls.getConstructor().newInstance();
+        } catch (ExtensionException ex) {
+            notFound = true;
+            if (!optional) {
+                throw ex;
+            }
+            LOG.log(Level.FINE, "Could not load optional extension " + getName(), ex);
+        } catch (InvocationTargetException ex) {
+            notFound = true;
+            if (!optional) {
+                throw new ExtensionException(new Message(PROBLEM_CREATING_EXTENSION_CLASS, LOG, cls.getName()),
+                                             ex.getCause());
+            }
+            LOG.log(Level.FINE, "Could not load optional extension " + getName(), ex);
+        } catch (NoSuchMethodException ex) {
+            notFound = true;
+            List<Object> a = new ArrayList<>();
+            if (b != null) {
+                a.add(b);
+            }
+            if (args != null) {
+                a.add(args);
+            }
+            if (!optional) {
+                throw new ExtensionException(new Message("PROBLEM_FINDING_CONSTRUCTOR", LOG,
+                                                         cls.getName(), a), ex);
+            }
+            LOG.log(Level.FINE, "Could not load optional extension " + getName(), ex);
+        } catch (Throwable e) {
+            notFound = true;
+            if (!optional) {
+                throw new ExtensionException(new Message(PROBLEM_CREATING_EXTENSION_CLASS, LOG, cls.getName()), e);
+            }
+            LOG.log(Level.FINE, "Could not load optional extension " + getName(), e);
+        }
+        return obj;
+    }
+
+    public Class<?> loadInterface(ClassLoader cl) {
+        if (intf != null || notFound) {
+            return intf;
+        }
+        synchronized (this) {
+            if (intf == null) {
+                intf = tryClass(interfaceName, cl);
+            }
+        }
+        return intf;
+    }
+
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/bus/extension/ExtensionManagerImpl.java b/transform/src/patch/java/org/apache/cxf/bus/extension/ExtensionManagerImpl.java
new file mode 100644
index 0000000..d4ce0be
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/bus/extension/ExtensionManagerImpl.java
@@ -0,0 +1,381 @@
+/**
+ * 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.bus.extension;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.injection.ResourceInjector;
+import org.apache.cxf.configuration.ConfiguredBeanLocator;
+import org.apache.cxf.configuration.Configurer;
+import org.apache.cxf.resource.ObjectTypeResolver;
+import org.apache.cxf.resource.ResourceManager;
+import org.apache.cxf.resource.ResourceResolver;
+import org.apache.cxf.resource.SinglePropertyResolver;
+
+public class ExtensionManagerImpl implements ExtensionManager, ConfiguredBeanLocator {
+    public static final String EXTENSIONMANAGER_PROPERTY_NAME = "extensionManager";
+    public static final String ACTIVATION_NAMESPACES_PROPERTY_NAME = "activationNamespaces";
+    public static final String ACTIVATION_NAMESPACES_SETTER_METHOD_NAME = "setActivationNamespaces";
+    public static final String BUS_EXTENSION_RESOURCE = "META-INF/cxf/bus-extensions.txt";
+
+    private final ClassLoader loader;
+    private ResourceManager resourceManager;
+    private Map<String, Extension> all = new ConcurrentHashMap<>();
+    private List<Extension> ordered = new CopyOnWriteArrayList<>();
+    private final Map<Class<?>, Object> activated;
+    private final Bus bus;
+
+    public ExtensionManagerImpl(ClassLoader cl, Map<Class<?>, Object> initialExtensions,
+                                ResourceManager rm, Bus b) {
+        this(new String[] {BUS_EXTENSION_RESOURCE},
+                 cl, initialExtensions, rm, b);
+    }
+    public ExtensionManagerImpl(String resource,
+                                ClassLoader cl,
+                                Map<Class<?>, Object> initialExtensions,
+                                ResourceManager rm,
+                                Bus b) {
+        this(new String[] {resource}, cl, initialExtensions, rm, b);
+    }
+    public ExtensionManagerImpl(String[] resources,
+                                ClassLoader cl,
+                                Map<Class<?>, Object> initialExtensions,
+                                ResourceManager rm,
+                                Bus b) {
+
+        loader = cl;
+        bus = b;
+        activated = initialExtensions;
+        resourceManager = rm;
+
+        ResourceResolver extensionManagerResolver =
+            new SinglePropertyResolver(EXTENSIONMANAGER_PROPERTY_NAME, this);
+        resourceManager.addResourceResolver(extensionManagerResolver);
+        resourceManager.addResourceResolver(new ObjectTypeResolver(this));
+
+        load(resources);
+        for (Map.Entry<String, Extension> ext : ExtensionRegistry.getRegisteredExtensions().entrySet()) {
+            if (!all.containsKey(ext.getKey())) {
+                all.put(ext.getKey(), ext.getValue());
+                ordered.add(ext.getValue());
+            }
+        }
+    }
+
+    final void load(String[] resources) {
+        if (resources == null) {
+            return;
+        }
+        try {
+            for (String resource : resources) {
+                load(resource);
+            }
+        } catch (IOException ex) {
+            throw new ExtensionException(ex);
+        }
+    }
+    public void add(Extension ex) {
+        all.put(ex.getName(), ex);
+        ordered.add(ex);
+    }
+
+    public void initialize() {
+        for (Extension e : ordered) {
+            if (!e.isDeferred() && e.getLoadedObject() == null) {
+                loadAndRegister(e);
+            }
+        }
+    }
+
+    public void removeBeansOfNames(List<String> names) {
+        for (String s : names) {
+            Extension ex = all.remove(s);
+            if (ex != null) {
+                ordered.remove(ex);
+            }
+        }
+    }
+    public void activateAll() {
+        for (Extension e : ordered) {
+            if (e.getLoadedObject() == null) {
+                loadAndRegister(e);
+            }
+        }
+    }
+    public <T> void activateAllByType(Class<T> type) {
+        for (Extension e : ordered) {
+            if (e.getLoadedObject() == null) {
+                Class<?> cls = e.getClassObject(loader);
+                if (cls != null && type.isAssignableFrom(cls)) {
+                    synchronized (e) {
+                        loadAndRegister(e);
+                    }
+                }
+            }
+        }
+    }
+
+    public boolean hasBeanOfName(String name) {
+        return all.containsKey(name);
+    }
+
+    final void load(String resource) throws IOException {
+        if (loader != getClass().getClassLoader()) {
+            load(resource, getClass().getClassLoader());
+        }
+        load(resource, loader);
+    }
+    final synchronized void load(String resource, ClassLoader l) throws IOException {
+
+        Enumeration<URL> urls = l.getResources(resource);
+
+        while (urls.hasMoreElements()) {
+            final URL url = urls.nextElement();
+            try (InputStream is = AccessController.doPrivileged(new PrivilegedExceptionAction<InputStream>() {
+                    public InputStream run() throws Exception {
+                        return url.openStream();
+                    }
+                })) {
+                List<Extension> exts = new TextExtensionFragmentParser(loader).getExtensions(is);
+                for (Extension e : exts) {
+                    if (loader != l) {
+                        e.classloader = l;
+                    }
+                    if (!all.containsKey(e.getName())) {
+                        all.put(e.getName(), e);
+                        ordered.add(e);
+                    }
+                }
+            } catch (PrivilegedActionException pae) {
+                throw (IOException)pae.getException();
+            }
+        }
+    }
+
+    final void loadAndRegister(Extension e) {
+        Class<?> cls;
+        if (null != e.getInterfaceName() && !"".equals(e.getInterfaceName())) {
+            cls = e.loadInterface(loader);
+        }  else {
+            cls = e.getClassObject(loader);
+        }
+        if (null != activated && null != cls && null != activated.get(cls)) {
+            return;
+        }
+
+        synchronized (e) {
+            Object obj = e.load(loader, bus);
+            if (obj == null) {
+                return;
+            }
+
+            if (null != activated) {
+                Configurer configurer = (Configurer)(activated.get(Configurer.class));
+                if (null != configurer) {
+                    configurer.configureBean(obj);
+                }
+            }
+
+            // let the object know for which namespaces it has been activated
+            ResourceResolver namespacesResolver = null;
+            if (null != e.getNamespaces()) {
+                namespacesResolver = new SinglePropertyResolver(ACTIVATION_NAMESPACES_PROPERTY_NAME,
+                                                                e.getNamespaces());
+                resourceManager.addResourceResolver(namespacesResolver);
+            }
+
+            // Since we need to support spring2.5 by removing @Resource("activationNamespaces")
+            // Now we call the setActivationNamespaces method directly here
+            if (e.getNamespaces() != null && !e.getNamespaces().isEmpty()) {
+                invokeSetterActivationNSMethod(obj, e.getNamespaces());
+            }
+
+            ResourceInjector injector = new ResourceInjector(resourceManager);
+
+            try {
+                injector.inject(obj);
+                injector.construct(obj);
+            } finally {
+                if (null != namespacesResolver) {
+                    resourceManager.removeResourceResolver(namespacesResolver);
+                }
+            }
+
+            if (null != activated) {
+                if (cls == null) {
+                    cls = obj.getClass();
+                }
+                activated.put(cls, obj);
+            }
+        }
+    }
+
+    public <T> T getExtension(String name, Class<T> type) {
+        if (name == null) {
+            return null;
+        }
+        Extension e = all.get(name);
+        if (e != null) {
+            Class<?> cls = e.getClassObject(loader);
+            if (cls != null && type.isAssignableFrom(cls)) {
+                synchronized (e) {
+                    if (e.getLoadedObject() == null) {
+                        loadAndRegister(e);
+                    }
+                    return type.cast(e.getLoadedObject());
+                }
+            }
+        }
+        return null;
+    }
+
+    private void invokeSetterActivationNSMethod(Object target, Object value) {
+        Class<?> clazz = target.getClass();
+        String methodName = ACTIVATION_NAMESPACES_SETTER_METHOD_NAME;
+        while (clazz != Object.class) {
+            Method[] methods = clazz.getMethods();
+            for (int i = 0; i < methods.length; i++) {
+                Method method = methods[i];
+                Class<?>[] params = method.getParameterTypes();
+                if (method.getName().equals(methodName) && params.length == 1) {
+                    Class<?> paramType = params[0];
+                    if (paramType.isInstance(value)) {
+                        try {
+                            method.invoke(target, new Object[] {value});
+                        } catch (Exception e) {
+                            // do nothing here
+                        }
+                        return;
+                    }
+                }
+            }
+            clazz = clazz.getSuperclass();
+        }
+    }
+    public List<String> getBeanNamesOfType(Class<?> type) {
+        List<String> ret = new LinkedList<>();
+        for (Extension ex : ordered) {
+            Class<?> cls = ex.getClassObject(loader);
+            if (cls != null && type.isAssignableFrom(cls)) {
+                synchronized (ex) {
+                    ret.add(ex.getName());
+                }
+            }
+        }
+        return ret;
+    }
+    public <T> T getBeanOfType(String name, Class<T> type) {
+        if (name == null) {
+            return null;
+        }
+        Extension ex = all.get(name);
+        if (ex != null) {
+            if (ex.getLoadedObject() == null) {
+                loadAndRegister(ex);
+            }
+            return type.cast(ex.getLoadedObject());
+        }
+        return null;
+    }
+    public <T> Collection<? extends T> getBeansOfType(Class<T> type) {
+        List<T> ret = new LinkedList<>();
+        Extension ext = all.get(type.getName());
+        if (ext != null) {
+            Class<?> cls = ext.getClassObject(loader);
+            if (cls != null && type.isAssignableFrom(cls)) {
+                synchronized (ext) {
+                    if (ext.getLoadedObject() == null) {
+                        loadAndRegister(ext);
+                    }
+                    if (ext.getLoadedObject() != null) {
+                        ret.add(type.cast(ext.getLoadedObject()));
+                    }
+                }
+            }
+        }
+        for (Extension ex : ordered) {
+            if (ex != ext) {
+                Class<?> cls = ex.getClassObject(loader);
+                if (cls != null && type.isAssignableFrom(cls)) {
+                    synchronized (ex) {
+                        if (ex.getLoadedObject() == null) {
+                            loadAndRegister(ex);
+                        }
+                        if (ex.getLoadedObject() != null) {
+                            ret.add(type.cast(ex.getLoadedObject()));
+                        }
+                    }
+                }
+            }
+        }
+        return ret;
+    }
+    public <T> boolean loadBeansOfType(Class<T> type, BeanLoaderListener<T> listener) {
+        boolean loaded = false;
+        for (Extension ex : ordered) {
+            Class<?> cls = ex.getClassObject(loader);
+            if (cls != null
+                && type.isAssignableFrom(cls)) {
+                synchronized (ex) {
+                    if (listener.loadBean(ex.getName(), cls.asSubclass(type))) {
+                        if (ex.getLoadedObject() == null) {
+                            loadAndRegister(ex);
+                        }
+                        if (listener.beanLoaded(ex.getName(), type.cast(ex.getLoadedObject()))) {
+                            return true;
+                        }
+                        loaded = true;
+                    }
+                }
+            }
+        }
+        return loaded;
+    }
+    public boolean hasConfiguredPropertyValue(String beanName, String propertyName, String value) {
+        if (beanName == null) {
+            return false;
+        }
+        Extension ex = all.get(beanName);
+        return ex != null && ex.getNamespaces() != null
+            && ex.getNamespaces().contains(value);
+    }
+    public void destroyBeans() {
+        for (Extension ex : ordered) {
+            if (ex.getLoadedObject() != null) {
+                ResourceInjector injector = new ResourceInjector(resourceManager);
+                injector.destroy(ex.getLoadedObject());
+            }
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/bus/managers/ServiceContractResolverRegistryImpl.java b/transform/src/patch/java/org/apache/cxf/bus/managers/ServiceContractResolverRegistryImpl.java
new file mode 100644
index 0000000..a0f7af0
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/bus/managers/ServiceContractResolverRegistryImpl.java
@@ -0,0 +1,113 @@
+/**
+ * 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.bus.managers;
+
+import java.net.URI;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.xml.namespace.QName;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.injection.NoJSR250Annotations;
+import org.apache.cxf.endpoint.ServiceContractResolver;
+import org.apache.cxf.endpoint.ServiceContractResolverRegistry;
+
+/**
+ * A simple contract resolver registry. It maintains a list of contract resolvers in an
+ * <code>ArrayList</code>.
+ */
+@NoJSR250Annotations(unlessNull = "bus")
+public class ServiceContractResolverRegistryImpl implements ServiceContractResolverRegistry {
+
+    private List<ServiceContractResolver> resolvers
+        = new CopyOnWriteArrayList<>();
+
+    public ServiceContractResolverRegistryImpl() {
+
+    }
+    public ServiceContractResolverRegistryImpl(Bus b) {
+        setBus(b);
+    }
+
+
+    /**
+     * Sets the bus with which the registry is associated.
+     *
+     * @param bus
+     */
+    public final void setBus(Bus bus) {
+        if (bus != null) {
+            bus.setExtension(this, ServiceContractResolverRegistry.class);
+        }
+    }
+
+    /**
+     * Calls each of the registered <code>ServiceContractResolver</code> instances
+     * to resolve the location of the service's contract. It returns the location
+     * from the first resolver that matches the QName to a location.
+     *
+     * @param qname QName to be resolved into a contract location
+     * @return URI representing the location of the contract
+    */
+    public URI getContractLocation(QName qname) {
+        for (ServiceContractResolver resolver : resolvers) {
+            URI contractLocation = resolver.getContractLocation(qname);
+            if (null != contractLocation) {
+                return contractLocation;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Tests if a resolver is alreadey registered with this registry.
+     *
+     * @param resolver the contract resolver for which to searche
+     * @return <code>true</code> if the resolver is registered
+     */
+    public boolean isRegistered(ServiceContractResolver resolver) {
+        return resolvers.contains(resolver);
+    }
+
+    /**
+     * Registers a contract resolver with this registry.
+     *
+     * @param resolver the contract resolver to register
+     */
+    public synchronized void register(ServiceContractResolver resolver) {
+        resolvers.add(resolver);
+    }
+
+    /**
+     * Removes a contract resolver from this registry.
+     *
+     * @param resolver the contract resolver to remove
+     */
+    public synchronized void unregister(ServiceContractResolver resolver) {
+        resolvers.remove(resolver);
+    }
+
+
+    protected List<ServiceContractResolver> getResolvers() {
+        return resolvers;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/bus/osgi/CXFActivator.java b/transform/src/patch/java/org/apache/cxf/bus/osgi/CXFActivator.java
new file mode 100644
index 0000000..6504276
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/bus/osgi/CXFActivator.java
@@ -0,0 +1,134 @@
+/**
+ * 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.bus.osgi;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.cxf.bus.blueprint.BlueprintNameSpaceHandlerFactory;
+import org.apache.cxf.bus.blueprint.NamespaceHandlerRegisterer;
+import org.apache.cxf.bus.extension.Extension;
+import org.apache.cxf.bus.extension.ExtensionRegistry;
+import org.apache.cxf.common.util.CollectionUtils;
+import org.apache.cxf.internal.CXFAPINamespaceHandler;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Is called in OSGi on start and stop of the cxf bundle.
+ * Manages
+ * - CXFBundleListener
+ * - Attaching ManagedWorkqueues to config admin service
+ * - OsgiBusListener
+ * - Blueprint namespaces
+ */
+public class CXFActivator implements BundleActivator {
+
+    private List<Extension> extensions;
+    private ManagedWorkQueueList workQueues;
+    private ServiceTracker<ConfigurationAdmin, ConfigurationAdmin> configAdminTracker;
+    private CXFExtensionBundleListener cxfBundleListener;
+    private ServiceRegistration<ManagedServiceFactory> workQueueServiceRegistration;
+
+
+
+    /** {@inheritDoc}*/
+    public void start(BundleContext context) throws Exception {
+        workQueues = new ManagedWorkQueueList();
+        cxfBundleListener = new CXFExtensionBundleListener(context.getBundle().getBundleId());
+        context.addBundleListener(cxfBundleListener);
+        cxfBundleListener.registerExistingBundles(context);
+
+        configAdminTracker = new ServiceTracker<>(context, ConfigurationAdmin.class, null);
+        configAdminTracker.open();
+        workQueues.setConfigAdminTracker(configAdminTracker);
+        workQueueServiceRegistration = registerManagedServiceFactory(context,
+                                                                     ManagedServiceFactory.class,
+                                                                     workQueues,
+                                                                     ManagedWorkQueueList.FACTORY_PID);
+
+        extensions = new ArrayList<>();
+        extensions.add(createOsgiBusListenerExtension(context));
+        extensions.add(createManagedWorkQueueListExtension(workQueues));
+        ExtensionRegistry.addExtensions(extensions);
+
+        BlueprintNameSpaceHandlerFactory factory = new BlueprintNameSpaceHandlerFactory() {
+
+            @Override
+            public Object createNamespaceHandler() {
+                return new CXFAPINamespaceHandler();
+            }
+        };
+        NamespaceHandlerRegisterer.register(context, factory,
+                                            "http://cxf.apache.org/blueprint/core",
+                                            "http://cxf.apache.org/configuration/beans",
+                                            "http://cxf.apache.org/configuration/parameterized-types",
+                                            "http://cxf.apache.org/configuration/security",
+                                            "http://schemas.xmlsoap.org/wsdl/",
+                                            "http://www.w3.org/2005/08/addressing",
+                                            "http://schemas.xmlsoap.org/ws/2004/08/addressing");
+
+    }
+
+    private <T> ServiceRegistration<T> registerManagedServiceFactory(BundleContext context,
+                                                                     Class<T> serviceClass,
+                                                                     T service,
+                                                                     String servicePid) {
+        return context.registerService(serviceClass, service, 
+                                       CollectionUtils.singletonDictionary(Constants.SERVICE_PID, servicePid));
+    }
+
+    private Extension createOsgiBusListenerExtension(BundleContext context) {
+        Extension busListener = new Extension(OSGIBusListener.class);
+        busListener.setArgs(new Object[] {context});
+        return busListener;
+    }
+
+    private static Extension createManagedWorkQueueListExtension(final ManagedWorkQueueList workQueues) {
+        return new Extension(ManagedWorkQueueList.class) {
+            
+            @Override
+            public Object getLoadedObject() {
+                return workQueues;
+            }
+
+            @Override
+            public Extension cloneNoObject() {
+                return this;
+            }
+        };
+    }
+
+    /** {@inheritDoc}*/
+    public void stop(BundleContext context) throws Exception {
+        context.removeBundleListener(cxfBundleListener);
+        cxfBundleListener.shutdown();
+        workQueues.shutDown();
+        workQueueServiceRegistration.unregister();
+        configAdminTracker.close();
+        ExtensionRegistry.removeExtensions(extensions);
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/bus/osgi/CXFExtensionBundleListener.java b/transform/src/patch/java/org/apache/cxf/bus/osgi/CXFExtensionBundleListener.java
new file mode 100644
index 0000000..ee9bb15
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/bus/osgi/CXFExtensionBundleListener.java
@@ -0,0 +1,181 @@
+/**
+ * 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.bus.osgi;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.logging.Logger;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.bus.extension.Extension;
+import org.apache.cxf.bus.extension.ExtensionException;
+import org.apache.cxf.bus.extension.ExtensionRegistry;
+import org.apache.cxf.bus.extension.TextExtensionFragmentParser;
+import org.apache.cxf.common.i18n.Message;
+import org.apache.cxf.common.logging.LogUtils;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.SynchronousBundleListener;
+
+public class CXFExtensionBundleListener implements SynchronousBundleListener {
+    private static final Logger LOG = LogUtils.getL7dLogger(CXFActivator.class);
+    private long id;
+    private ConcurrentMap<Long, List<OSGiExtension>> extensions
+        = new ConcurrentHashMap<>(16, 0.75f, 4);
+
+    public CXFExtensionBundleListener(long bundleId) {
+        this.id = bundleId;
+    }
+
+    public void registerExistingBundles(BundleContext context) {
+        for (Bundle bundle : context.getBundles()) {
+            if ((bundle.getState() == Bundle.RESOLVED
+                || bundle.getState() == Bundle.STARTING
+                || bundle.getState() == Bundle.ACTIVE
+                || bundle.getState() == Bundle.STOPPING)
+                && bundle.getBundleId() != context.getBundle().getBundleId()) {
+                register(bundle);
+            }
+        }
+    }
+
+    /** {@inheritDoc}*/
+    public void bundleChanged(BundleEvent event) {
+        if (event.getType() == BundleEvent.RESOLVED && id != event.getBundle().getBundleId()) {
+            register(event.getBundle());
+        } else if (event.getType() == BundleEvent.UNRESOLVED || event.getType() == BundleEvent.UNINSTALLED) {
+            unregister(event.getBundle().getBundleId());
+        }
+    }
+
+    protected void register(final Bundle bundle) {
+        Enumeration<?> e = bundle.findEntries("META-INF/cxf/", "bus-extensions.txt", false);
+        while (e != null && e.hasMoreElements()) {
+            List<Extension> orig = new TextExtensionFragmentParser(null).getExtensions((URL)e.nextElement());
+            addExtensions(bundle, orig);
+        }
+    }
+
+    private boolean addExtensions(final Bundle bundle, List<Extension> orig) {
+        if (orig.isEmpty()) {
+            return false;
+        }
+
+        List<String> names = new ArrayList<>(orig.size());
+        for (Extension ext : orig) {
+            names.add(ext.getName());
+        }
+        LOG.info("Adding the extensions from bundle " + bundle.getSymbolicName()
+                 + " (" + bundle.getBundleId() + ") " + names);
+        List<OSGiExtension> list = extensions.get(bundle.getBundleId());
+        if (list == null) {
+            list = new CopyOnWriteArrayList<>();
+            List<OSGiExtension> preList = extensions.putIfAbsent(bundle.getBundleId(), list);
+            if (preList != null) {
+                list = preList;
+            }
+        }
+        for (Extension ext : orig) {
+            list.add(new OSGiExtension(ext, bundle));
+        }
+        ExtensionRegistry.addExtensions(list);
+        return !list.isEmpty();
+    }
+
+    protected void unregister(final long bundleId) {
+        List<OSGiExtension> list = extensions.remove(bundleId);
+        if (list != null) {
+            LOG.info("Removing the extensions for bundle " + bundleId);
+            ExtensionRegistry.removeExtensions(list);
+        }
+    }
+
+    public void shutdown() {
+        while (!extensions.isEmpty()) {
+            unregister(extensions.keySet().iterator().next());
+        }
+    }
+
+    public class OSGiExtension extends Extension {
+        final Bundle bundle;
+        Object serviceObject;
+        public OSGiExtension(Extension e, Bundle b) {
+            super(e);
+            bundle = b;
+        }
+
+        public void setServiceObject(Object o) {
+            serviceObject = o;
+            obj = o;
+        }
+        
+        @Override
+        public Object load(ClassLoader cl, Bus b) {
+            if (interfaceName == null && bundle.getBundleContext() != null) {
+                ServiceReference<?> ref = bundle.getBundleContext().getServiceReference(className);
+                if (ref != null && ref.getBundle().getBundleId() == bundle.getBundleId()) {
+                    Object o = bundle.getBundleContext().getService(ref);
+                    serviceObject = o;
+                    obj = o;
+                    return obj;
+                }
+            }
+            return super.load(cl, b);
+        }
+        
+        @Override
+        protected Class<?> tryClass(String name, ClassLoader cl) {
+            Class<?> c = null;
+            Throwable origExc = null;
+            try {
+                c = bundle.loadClass(className);
+            } catch (Throwable e) {
+                origExc = e;
+            }
+            if (c == null) {
+                try {
+                    return super.tryClass(name, cl);
+                } catch (ExtensionException ee) {
+                    if (origExc != null) {
+                        throw new ExtensionException(new Message("PROBLEM_LOADING_EXTENSION_CLASS",
+                                                                 Extension.LOG, name),
+                                                     origExc);
+                    }
+                    throw ee;
+                }
+            }
+            return c;
+        }
+
+        @Override
+        public Extension cloneNoObject() {
+            OSGiExtension ext = new OSGiExtension(this, bundle);
+            ext.obj = serviceObject;
+            return ext;
+        }
+
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/bus/osgi/OSGIBusListener.java b/transform/src/patch/java/org/apache/cxf/bus/osgi/OSGIBusListener.java
new file mode 100644
index 0000000..aad270d
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/bus/osgi/OSGIBusListener.java
@@ -0,0 +1,224 @@
+/**
+ * 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.bus.osgi;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.bus.extension.ExtensionManagerImpl;
+import org.apache.cxf.buslifecycle.BusCreationListener;
+import org.apache.cxf.buslifecycle.BusLifeCycleListener;
+import org.apache.cxf.buslifecycle.BusLifeCycleManager;
+import org.apache.cxf.common.util.CollectionUtils;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.configuration.ConfiguredBeanLocator;
+import org.apache.cxf.endpoint.ClientLifeCycleListener;
+import org.apache.cxf.endpoint.ClientLifeCycleManager;
+import org.apache.cxf.endpoint.ServerLifeCycleListener;
+import org.apache.cxf.endpoint.ServerLifeCycleManager;
+import org.apache.cxf.feature.Feature;
+import org.apache.cxf.workqueue.WorkQueueManager;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.Version;
+
+public class OSGIBusListener implements BusLifeCycleListener {
+    public static final String CONTEXT_SYMBOLIC_NAME_PROPERTY = "cxf.context.symbolicname";
+    public static final String CONTEXT_VERSION_PROPERTY = "cxf.context.version";
+    public static final String CONTEXT_NAME_PROPERTY = "cxf.bus.id";
+
+    private static final String SERVICE_PROPERTY_PRIVATE = "org.apache.cxf.bus.private.extension";
+    private static final String SERVICE_PROPERTY_RESTRICTED = "org.apache.cxf.bus.restricted.extension";
+    private static final String BUS_EXTENSION_BUNDLES_EXCLUDES = "bus.extension.bundles.excludes";
+    Bus bus;
+    ServiceRegistration<?> service;
+    BundleContext defaultContext;
+    private Pattern extensionBundlesExcludesPattern;
+
+    public OSGIBusListener(Bus b) {
+        this(b, null);
+    }
+    public OSGIBusListener(Bus b, Object[] args) {
+        bus = b;
+        if (args != null && args.length > 0
+            && args[0] instanceof BundleContext) {
+            defaultContext = (BundleContext)args[0];
+        }
+        String extExcludes = (String)bus.getProperty(BUS_EXTENSION_BUNDLES_EXCLUDES);
+        if (!StringUtils.isEmpty(extExcludes)) {
+            try {
+                extensionBundlesExcludesPattern = Pattern.compile(extExcludes);
+            } catch (IllegalArgumentException e) {
+                // ignore
+            }
+        }
+        BusLifeCycleManager manager = bus.getExtension(BusLifeCycleManager.class);
+        manager.registerLifeCycleListener(this);
+        registerConfiguredBeanLocator();
+        registerClientLifeCycleListeners();
+        registerServerLifecycleListeners();
+        registerBusFeatures();
+        sendBusCreatedToBusCreationListeners();
+
+    }
+    private void registerConfiguredBeanLocator() {
+        final ConfiguredBeanLocator cbl = bus.getExtension(ConfiguredBeanLocator.class);
+        if (cbl instanceof ExtensionManagerImpl) {
+            // wire in the OSGi things
+            bus.setExtension(new OSGiBeanLocator(cbl, defaultContext),
+                             ConfiguredBeanLocator.class);
+        }
+    }
+
+    public void initComplete() {
+        ManagedWorkQueueList wqList = bus.getExtension(ManagedWorkQueueList.class);
+        if (wqList != null) {
+            WorkQueueManager manager = bus.getExtension(WorkQueueManager.class);
+            wqList.addAllToWorkQueueManager(manager);
+        }
+        registerBusAsService();
+    }
+
+
+    public void preShutdown() {
+    }
+
+    public void postShutdown() {
+        if (service != null) {
+            service.unregister();
+            service = null;
+        }
+    }
+
+    private static ServiceReference<?>[] getServiceReferences(BundleContext context, Class<?> serviceClass) {
+        ServiceReference<?>[] refs = null;
+        try {
+            refs = context.getServiceReferences(serviceClass.getName(), null);
+        } catch (InvalidSyntaxException e) {
+            // ignore
+        }
+        if (refs == null) {
+            refs = new ServiceReference<?>[]{};
+        }
+        return refs;
+    }
+
+    private void sendBusCreatedToBusCreationListeners() {
+        ServiceReference<?>[] refs = getServiceReferences(defaultContext, BusCreationListener.class);
+        for (ServiceReference<?> ref : refs) {
+            if (!isPrivate(ref) && !isExcluded(ref)) {
+                BusCreationListener listener = (BusCreationListener)defaultContext.getService(ref);
+                listener.busCreated(bus);
+            }
+        }
+    }
+
+    private void registerServerLifecycleListeners() {
+        ServiceReference<?>[] refs = getServiceReferences(defaultContext, ServerLifeCycleListener.class);
+        ServerLifeCycleManager clcm = bus.getExtension(ServerLifeCycleManager.class);
+        for (ServiceReference<?> ref : refs) {
+            if (!isPrivate(ref) && !isExcluded(ref)) {
+                ServerLifeCycleListener listener = (ServerLifeCycleListener)defaultContext.getService(ref);
+                clcm.registerListener(listener);
+            }
+        }
+    }
+    private void registerClientLifeCycleListeners() {
+        ServiceReference<?>[] refs = getServiceReferences(defaultContext, ClientLifeCycleListener.class);
+        ClientLifeCycleManager clcm = bus.getExtension(ClientLifeCycleManager.class);
+        for (ServiceReference<?> ref : refs) {
+            if (!isPrivate(ref) && !isExcluded(ref)) {
+                ClientLifeCycleListener listener = (ClientLifeCycleListener)defaultContext.getService(ref);
+                clcm.registerListener(listener);
+            }
+        }
+    }
+
+    private void registerBusFeatures() {
+        ServiceReference<?>[] refs = getServiceReferences(defaultContext, Feature.class);
+        for (ServiceReference<?> ref : refs) {
+            if (!isPrivate(ref) && !isExcluded(ref)) {
+                Feature feature = (Feature)defaultContext.getService(ref);
+                bus.getFeatures().add(feature);
+            }
+        }
+    }
+
+    private boolean isPrivate(ServiceReference<?> ref) {
+        Object o = ref.getProperty(SERVICE_PROPERTY_PRIVATE);
+        
+        if (o == null) {
+            return false;
+        }
+        
+        Boolean pvt = Boolean.FALSE;
+        if (o instanceof String) {
+            pvt = Boolean.parseBoolean((String)o);
+        } else if (o instanceof Boolean) {
+            pvt = (Boolean)o;
+        }
+        return pvt.booleanValue();
+    }
+
+    private boolean isExcluded(ServiceReference<?> ref) {
+        String o = (String)ref.getProperty(SERVICE_PROPERTY_RESTRICTED);
+        if (!StringUtils.isEmpty(o)) {
+            // if the service's restricted-regex is set, the service is excluded when the app not matching that regex
+            BundleContext app = bus.getExtension(BundleContext.class);
+            try {
+                if (app != null && !app.getBundle().getSymbolicName().matches(o)) {
+                    return true;
+                }
+            } catch (IllegalArgumentException e) {
+                // ignore
+            }
+        }
+        // if the excludes-regex is set, the service is excluded when matching that regex.
+        return extensionBundlesExcludesPattern != null
+            && extensionBundlesExcludesPattern.matcher(ref.getBundle().getSymbolicName()).matches();
+    }
+
+    private Version getBundleVersion(Bundle bundle) {
+        Dictionary<?, ?> headers = bundle.getHeaders();
+        String version = (String) headers.get(Constants.BUNDLE_VERSION);
+        return (version != null) ? Version.parseVersion(version) : Version.emptyVersion;
+    }
+
+
+
+    private void registerBusAsService() {
+        BundleContext context = bus.getExtension(BundleContext.class);
+        if (context != null) {
+            Map<String, Object> props = new HashMap<>();
+            props.put(CONTEXT_SYMBOLIC_NAME_PROPERTY, context.getBundle().getSymbolicName());
+            props.put(CONTEXT_VERSION_PROPERTY, getBundleVersion(context.getBundle()));
+            props.put(CONTEXT_NAME_PROPERTY, bus.getId());
+
+            service = context.registerService(Bus.class.getName(), bus, CollectionUtils.toDictionary(props));
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/transform/src/patch/java/org/apache/cxf/bus/resource/ResourceManagerImpl.java b/transform/src/patch/java/org/apache/cxf/bus/resource/ResourceManagerImpl.java
new file mode 100644
index 0000000..99c6ee9
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/bus/resource/ResourceManagerImpl.java
@@ -0,0 +1,85 @@
+/**
+ * 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.bus.resource;
+
+import java.util.List;
+
+import javax.annotation.Resource;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.injection.NoJSR250Annotations;
+import org.apache.cxf.configuration.ConfiguredBeanLocator;
+import org.apache.cxf.extension.BusExtension;
+import org.apache.cxf.resource.DefaultResourceManager;
+import org.apache.cxf.resource.ObjectTypeResolver;
+import org.apache.cxf.resource.ResourceManager;
+import org.apache.cxf.resource.ResourceResolver;
+
+@NoJSR250Annotations(unlessNull = "bus")
+public class ResourceManagerImpl extends DefaultResourceManager implements BusExtension {
+
+    private Bus bus;
+
+    public ResourceManagerImpl() {
+    }
+
+    public ResourceManagerImpl(Bus b) {
+        super();
+        setBus(b);
+    }
+    
+    @Override
+    protected void onFirstResolve() {
+        super.onFirstResolve();
+        if (bus != null) {
+            ConfiguredBeanLocator locator = bus.getExtension(ConfiguredBeanLocator.class);
+            if (locator != null) {
+                this.addResourceResolvers(locator.getBeansOfType(ResourceResolver.class));
+            }
+        }
+    }
+
+    /**
+     * Set the list of resolvers for this resource manager.
+     * @param resolvers
+     */
+    public final void setResolvers(List<? extends ResourceResolver> resolvers) {
+        registeredResolvers.clear();
+        registeredResolvers.addAll(resolvers);
+    }
+
+    @Resource
+    public final void setBus(Bus b) {
+        if (bus != b) {
+            bus = b;
+            firstCalled = false;
+            super.addResourceResolver(new ObjectTypeResolver(bus));
+            if (null != bus) {
+                bus.setExtension(this, ResourceManager.class);
+            }
+        }
+    }
+
+    public Class<?> getRegistrationType() {
+        return ResourceManager.class;
+    }
+
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/bus/spring/BusDefinitionParser.java b/transform/src/patch/java/org/apache/cxf/bus/spring/BusDefinitionParser.java
new file mode 100644
index 0000000..9d508b6
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/bus/spring/BusDefinitionParser.java
@@ -0,0 +1,310 @@
+/**
+ * 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.bus.spring;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.w3c.dom.Element;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.injection.NoJSR250Annotations;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.configuration.spring.AbstractBeanDefinitionParser;
+import org.apache.cxf.configuration.spring.BusWiringType;
+import org.apache.cxf.feature.Feature;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.interceptor.AbstractBasicInterceptorProvider;
+import org.apache.cxf.interceptor.Interceptor;
+import org.apache.cxf.message.Message;
+import org.springframework.beans.PropertyValue;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+public class BusDefinitionParser extends AbstractBeanDefinitionParser {
+    private static AtomicInteger counter = new AtomicInteger(0);
+
+    public BusDefinitionParser() {
+        super();
+        setBeanClass(BusConfig.class);
+    }
+
+    @Override
+    protected void doParse(Element element, ParserContext ctx, BeanDefinitionBuilder bean) {
+        String bus = element.getAttribute("bus");
+        if (StringUtils.isEmpty(bus)) {
+            bus = element.getAttribute("name");
+            if (StringUtils.isEmpty(bus)) {
+                element.setAttribute("bus", bus);
+            }
+        }
+        element.removeAttribute("name");
+        if (StringUtils.isEmpty(bus)) {
+            bus = "cxf";
+        }
+        String id = element.getAttribute("id");
+        if (!StringUtils.isEmpty(id)) {
+            bean.addPropertyValue("id", id);
+        }
+
+        super.doParse(element, ctx, bean);
+
+        if (ctx.getRegistry().containsBeanDefinition(bus)) {
+            BeanDefinition def = ctx.getRegistry().getBeanDefinition(bus);
+            copyProps(bean, def);
+            bean.addConstructorArgValue(bus);
+        } else if (!"cxf".equals(bus)) {
+            bean.getRawBeanDefinition().setBeanClass(SpringBus.class);
+            bean.setDestroyMethodName("shutdown");
+            try {
+                element.setUserData("ID", bus, null);
+                bean.getRawBeanDefinition().getPropertyValues().removePropertyValue("bus");
+            } catch (Throwable t) {
+                //likely not DOM level 3, ignore
+            }
+        } else {
+            addBusWiringAttribute(bean, BusWiringType.PROPERTY, bus, ctx);
+            bean.getRawBeanDefinition().setAttribute(WIRE_BUS_CREATE,
+                                                     resolveId(element, null, ctx));
+            bean.addConstructorArgValue(bus);
+        }
+    }
+    
+    @Override
+    protected boolean processBusAttribute(Element element, ParserContext ctx,
+                                          BeanDefinitionBuilder bean,
+                                          String val) {
+        return false;
+    }
+    
+    private void copyProps(BeanDefinitionBuilder src, BeanDefinition def) {
+        for (PropertyValue v : src.getBeanDefinition().getPropertyValues().getPropertyValues()) {
+            if (!"bus".equals(v.getName())) {
+                def.getPropertyValues().addPropertyValue(v.getName(), v.getValue());
+            }
+            src.getBeanDefinition().getPropertyValues().removePropertyValue(v);
+        }
+
+    }
+
+    @Override
+    protected void mapElement(ParserContext ctx,
+                              BeanDefinitionBuilder bean,
+                              Element e,
+                              String name) {
+        if ("inInterceptors".equals(name) || "inFaultInterceptors".equals(name)
+            || "outInterceptors".equals(name) || "outFaultInterceptors".equals(name)
+            || "features".equals(name)) {
+            List<?> list = ctx.getDelegate().parseListElement(e, bean.getBeanDefinition());
+            bean.addPropertyValue(name, list);
+        } else if ("properties".equals(name)) {
+            Map<?, ?> map = ctx.getDelegate().parseMapElement(e, bean.getBeanDefinition());
+            bean.addPropertyValue("properties", map);
+        }
+    }
+    @Override
+    protected String resolveId(Element element, AbstractBeanDefinition definition,
+                               ParserContext ctx) {
+        String bus = null;
+        try {
+            bus = (String)element.getUserData("ID");
+        } catch (Throwable t) {
+            //ignore
+        }
+        if (bus == null) {
+            bus = element.getAttribute("bus");
+            if (StringUtils.isEmpty(bus)) {
+                bus = element.getAttribute("name");
+            }
+            if (StringUtils.isEmpty(bus)) {
+                bus = Bus.DEFAULT_BUS_ID + ".config" + counter.getAndIncrement();
+            } else {
+                bus = bus + ".config";
+            }
+            try {
+                element.setUserData("ID", bus, null);
+            } catch (Throwable t) {
+                //maybe no DOM level 3, ignore, but, may have issues with the counter
+            }
+        }
+        return bus;
+    }
+
+    @NoJSR250Annotations
+    public static class BusConfig extends AbstractBasicInterceptorProvider
+        implements ApplicationContextAware {
+
+        Bus bus;
+        String busName;
+        String id;
+        Collection<Feature> features;
+        Map<String, Object> properties;
+
+        public BusConfig(String busName) {
+            this.busName = busName;
+        }
+
+        public void setBus(Bus bb) {
+            if (bus == bb) {
+                return;
+            }
+            if (properties != null) {
+                bb.setProperties(properties);
+                properties = null;
+            }
+            if (!getInInterceptors().isEmpty()) {
+                bb.getInInterceptors().addAll(getInInterceptors());
+            }
+            if (!getOutInterceptors().isEmpty()) {
+                bb.getOutInterceptors().addAll(getOutInterceptors());
+            }
+            if (!getInFaultInterceptors().isEmpty()) {
+                bb.getInFaultInterceptors().addAll(getInFaultInterceptors());
+            }
+            if (!getOutFaultInterceptors().isEmpty()) {
+                bb.getOutFaultInterceptors().addAll(getOutFaultInterceptors());
+            }
+            if (!StringUtils.isEmpty(id)) {
+                bb.setId(id);
+            }
+            if (features != null) {
+                bb.setFeatures(features);
+                features = null;
+            }
+            bus = bb;
+        }
+
+        public void setApplicationContext(ApplicationContext applicationContext) {
+            if (bus != null) {
+                return;
+            }
+        }
+
+        @Override
+        public List<Interceptor<? extends Message>> getOutFaultInterceptors() {
+            if (bus != null) {
+                return bus.getOutFaultInterceptors();
+            }
+            return super.getOutFaultInterceptors();
+        }
+
+        @Override
+        public List<Interceptor<? extends Message>> getInFaultInterceptors() {
+            if (bus != null) {
+                return bus.getInFaultInterceptors();
+            }
+            return super.getInFaultInterceptors();
+        }
+
+        @Override
+        public List<Interceptor<? extends Message>> getInInterceptors() {
+            if (bus != null) {
+                return bus.getInInterceptors();
+            }
+            return super.getInInterceptors();
+        }
+
+        @Override
+        public List<Interceptor<? extends Message>> getOutInterceptors() {
+            if (bus != null) {
+                return bus.getOutInterceptors();
+            }
+            return super.getOutInterceptors();
+        }
+
+        @Override
+        public void setInInterceptors(List<Interceptor<? extends Message>> interceptors) {
+            if (bus != null) {
+                bus.getInInterceptors().addAll(interceptors);
+            } else {
+                super.setInInterceptors(interceptors);
+            }
+        }
+
+        @Override
+        public void setInFaultInterceptors(List<Interceptor<? extends Message>> interceptors) {
+            if (bus != null) {
+                bus.getInFaultInterceptors().addAll(interceptors);
+            } else {
+                super.setInFaultInterceptors(interceptors);
+            }
+        }
+
+        @Override
+        public void setOutInterceptors(List<Interceptor<? extends Message>> interceptors) {
+            if (bus != null) {
+                bus.getOutInterceptors().addAll(interceptors);
+            } else {
+                super.setOutInterceptors(interceptors);
+            }
+        }
+
+        @Override
+        public void setOutFaultInterceptors(List<Interceptor<? extends Message>> interceptors) {
+            if (bus != null) {
+                bus.getOutFaultInterceptors().addAll(interceptors);
+            } else {
+                super.setOutFaultInterceptors(interceptors);
+            }
+        }
+
+        public Collection<Feature> getFeatures() {
+            if (bus != null) {
+                return bus.getFeatures();
+            }
+            return features;
+        }
+
+        public void setFeatures(Collection<? extends Feature> features) {
+            if (bus != null) {
+                bus.setFeatures(features);
+            } else {
+                this.features = CastUtils.cast(features);
+            }
+
+        }
+
+        public Map<String, Object> getProperties() {
+            if (bus != null) {
+                return bus.getProperties();
+            }
+            return properties;
+        }
+        public void setProperties(Map<String, Object> s) {
+            if (bus != null) {
+                bus.setProperties(s);
+            } else {
+                this.properties = s;
+            }
+        }
+
+        public void setId(String s) {
+            id = s;
+        }
+
+
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/bus/spring/BusEntityResolver.java b/transform/src/patch/java/org/apache/cxf/bus/spring/BusEntityResolver.java
new file mode 100644
index 0000000..1e95e28
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/bus/spring/BusEntityResolver.java
@@ -0,0 +1,94 @@
+/**
+ * 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.bus.spring;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.springframework.beans.factory.xml.DelegatingEntityResolver;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PropertiesLoaderUtils;
+import org.springframework.util.CollectionUtils;
+
+/**
+ *
+ */
+public class BusEntityResolver extends DelegatingEntityResolver  {
+
+    private static final Logger LOG = LogUtils.getL7dLogger(BusEntityResolver.class);
+
+    private EntityResolver dtdResolver;
+    private EntityResolver schemaResolver;
+    private Map<String, String> schemaMappings;
+    private ClassLoader classLoader;
+
+    public BusEntityResolver(ClassLoader loader, EntityResolver dr, EntityResolver sr) {
+        super(dr, sr);
+        classLoader = loader;
+        dtdResolver = dr;
+        schemaResolver = sr;
+
+        try {
+            Properties mappings = PropertiesLoaderUtils.loadAllProperties("META-INF/spring.schemas",
+                                                                          classLoader);
+            schemaMappings = new ConcurrentHashMap<>(mappings.size());
+            CollectionUtils.mergePropertiesIntoMap(mappings, schemaMappings);
+        } catch (IOException e) {
+            //ignore
+        }
+    }
+
+    @Override
+    public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
+        InputSource source = super.resolveEntity(publicId, systemId);
+        if (null == source && null != systemId) {
+            // try the schema and dtd resolver in turn, ignoring the suffix in publicId
+            LOG.log(Level.FINE, "Attempting to resolve systemId {0}", systemId);
+            source = schemaResolver.resolveEntity(publicId, systemId);
+            if (null == source) {
+                source = dtdResolver.resolveEntity(publicId, systemId);
+            }
+        }
+        
+        if (null == source) {
+            return null;
+        }
+        
+        String resourceLocation = schemaMappings.get(systemId);
+        if (resourceLocation != null && publicId == null) {
+            Resource resource = new ClassPathResource(resourceLocation, classLoader);
+            if (resource.exists()) {
+                source.setPublicId(systemId);
+                source.setSystemId(resource.getURL().toString());
+            }
+        }
+        return source;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/bus/spring/BusExtensionPostProcessor.java b/transform/src/patch/java/org/apache/cxf/bus/spring/BusExtensionPostProcessor.java
new file mode 100644
index 0000000..9389be6
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/bus/spring/BusExtensionPostProcessor.java
@@ -0,0 +1,71 @@
+/**
+ * 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.bus.spring;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.injection.NoJSR250Annotations;
+import org.apache.cxf.extension.BusExtension;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.core.Ordered;
+
+@NoJSR250Annotations
+public class BusExtensionPostProcessor implements BeanPostProcessor, ApplicationContextAware, Ordered {
+
+    private Bus bus;
+    private ApplicationContext context;
+
+    public void setApplicationContext(ApplicationContext ctx) {
+        context = ctx;
+    }
+
+    public int getOrder() {
+        return 1001;
+    }
+
+
+    @Override
+    public Object postProcessAfterInitialization(Object bean, String beanId) {
+        return bean;
+    }
+
+    @Override
+    public Object postProcessBeforeInitialization(Object bean, String beanId) {
+        if (bean instanceof BusExtension && null != getBus()) {
+            Class<? extends Object> cls = ((BusExtension)bean).getRegistrationType();
+            registerExt(bean, cls);
+        } else if (bean instanceof Bus && Bus.DEFAULT_BUS_ID.equals(beanId)) {
+            bus = (Bus)bean;
+        }
+        return bean;
+    }
+    private <T> void registerExt(Object bean, Class<T> cls) {
+        getBus().setExtension(cls.cast(bean), cls);
+    }
+
+    private Bus getBus() {
+        if (bus == null) {
+            bus = (Bus)context.getBean(Bus.DEFAULT_BUS_ID);
+        }
+        return bus;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/bus/spring/Jsr250BeanPostProcessor.java b/transform/src/patch/java/org/apache/cxf/bus/spring/Jsr250BeanPostProcessor.java
new file mode 100644
index 0000000..1fb0746
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/bus/spring/Jsr250BeanPostProcessor.java
@@ -0,0 +1,164 @@
+/**
+ * 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.bus.spring;
+
+
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.injection.ResourceInjector;
+import org.apache.cxf.resource.ResourceManager;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.core.Ordered;
+
+public class Jsr250BeanPostProcessor
+    implements DestructionAwareBeanPostProcessor, Ordered, ApplicationContextAware {
+
+    private ResourceManager resourceManager;
+    private ApplicationContext context;
+
+    private boolean isProcessing = true;
+    //private int count;
+    //private int count2;
+
+    Jsr250BeanPostProcessor() {
+    }
+
+    public void setApplicationContext(ApplicationContext applicationContext) {
+        context = applicationContext;
+        try {
+            Class<?> cls = Class
+                .forName("org.springframework.context.annotation.CommonAnnotationBeanPostProcessor");
+            isProcessing = context.getBeanNamesForType(cls, true, false).length == 0;
+        } catch (ClassNotFoundException e) {
+            isProcessing = true;
+        }
+    }
+
+    public int getOrder() {
+        return 1010;
+    }
+
+    private boolean injectable(Object bean, String beanId) {
+        return !"cxf".equals(beanId) && ResourceInjector.processable(bean.getClass(), bean);
+    }
+    private ResourceManager getResourceManager(Object bean) {
+        if (resourceManager == null) {
+            boolean temp = isProcessing;
+            isProcessing = false;
+            if (bean instanceof ResourceManager) {
+                resourceManager = (ResourceManager)bean;
+                resourceManager.addResourceResolver(new BusApplicationContextResourceResolver(context));
+            } else if (bean instanceof Bus) {
+                Bus b = (Bus)bean;
+                ResourceManager m = b.getExtension(ResourceManager.class);
+                if (m != null) {
+                    resourceManager = m;
+                    if (!(b instanceof SpringBus)) {
+                        resourceManager
+                            .addResourceResolver(new BusApplicationContextResourceResolver(context));
+                    }
+                }
+            } else {
+                ResourceManager m = null;
+                Bus b = null;
+                try {
+                    m = (ResourceManager)context.getBean(ResourceManager.class.getName());
+                } catch (NoSuchBeanDefinitionException t) {
+                    //ignore - no resource manager
+                }
+                if (m == null) {
+                    b = (Bus)context.getBean("cxf");
+                    m = b.getExtension(ResourceManager.class);
+                }
+                if (m != null) {
+                    resourceManager = m;
+                    if (!(b instanceof SpringBus)) {
+                        resourceManager
+                            .addResourceResolver(new BusApplicationContextResourceResolver(context));
+                    }
+                }
+            }
+            isProcessing = temp;
+        }
+        return resourceManager;
+    }
+    
+    @Override
+    public Object postProcessAfterInitialization(Object bean, String beanId) {
+        if (!isProcessing) {
+            if (resourceManager == null && bean instanceof ResourceManager) {
+                resourceManager = (ResourceManager)bean;
+                resourceManager.addResourceResolver(new BusApplicationContextResourceResolver(context));
+            }
+            return bean;
+        }
+        if (bean != null
+            && injectable(bean, beanId)) {
+            new ResourceInjector(getResourceManager(bean)).construct(bean);
+        }
+        return bean;
+    }
+
+    @Override
+    public Object postProcessBeforeInitialization(Object bean, String beanId) {
+        if (!isProcessing) {
+            return bean;
+        }
+        if (bean instanceof Bus) {
+            getResourceManager(bean);
+        }
+        /*
+        if (bean.getClass().getName().contains("Corb")) {
+            Thread.dumpStack();
+        }
+        */
+
+        if (bean != null
+            && injectable(bean, beanId)) {
+            new ResourceInjector(getResourceManager(bean)).inject(bean);
+            /*
+            System.out.println("p :" + (++count) + ": " + bean.getClass().getName() + " " + beanId);
+        } else if (bean != null) {
+            System.out.println("np: " + (++count2)
+                               + ": " + bean.getClass().getName() + " " + beanId);
+                               */
+        }
+        return bean;
+    }
+
+    public void postProcessBeforeDestruction(Object bean, String beanId) {
+        if (!isProcessing) {
+            return;
+        }
+        if (bean != null
+            && injectable(bean, beanId)) {
+            new ResourceInjector(getResourceManager(bean)).destroy(bean);
+        }
+    }
+
+    @Override
+    public boolean requiresDestruction(Object bean) {
+        return isProcessing;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/bus/spring/NamespaceHandler.java b/transform/src/patch/java/org/apache/cxf/bus/spring/NamespaceHandler.java
new file mode 100644
index 0000000..9211ea8
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/bus/spring/NamespaceHandler.java
@@ -0,0 +1,57 @@
+/**
+ * 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.bus.spring;
+
+import org.w3c.dom.Element;
+
+import org.apache.cxf.configuration.spring.SimpleBeanDefinitionParser;
+import org.apache.cxf.feature.FastInfosetFeature;
+import org.apache.cxf.workqueue.AutomaticWorkQueueImpl;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
+import org.springframework.beans.factory.xml.ParserContext;
+
+public class NamespaceHandler extends NamespaceHandlerSupport {
+    @SuppressWarnings("deprecation")
+    public void init() {
+        registerBeanDefinitionParser("bus",
+                                     new BusDefinitionParser());
+        registerBeanDefinitionParser("logging",
+                                     new SimpleBeanDefinitionParser(org.apache.cxf.feature.LoggingFeature.class));
+        registerBeanDefinitionParser("fastinfoset",
+                                     new SimpleBeanDefinitionParser(FastInfosetFeature.class));
+
+        registerBeanDefinitionParser("workqueue",
+                                     new SimpleBeanDefinitionParser(AutomaticWorkQueueImpl.class) {
+
+                @Override
+                protected void processNameAttribute(Element element,
+                                                ParserContext ctx,
+                                                BeanDefinitionBuilder bean,
+                                                String val) {
+                    bean.addPropertyValue("name", val);
+                    element.removeAttribute("name");
+                    if (!element.hasAttribute("id")) {
+                        element.setAttribute("id", "cxf.workqueue." + val);
+                    }
+
+                }
+            });
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/bus/spring/SpringBus.java b/transform/src/patch/java/org/apache/cxf/bus/spring/SpringBus.java
new file mode 100644
index 0000000..6616c8e
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/bus/spring/SpringBus.java
@@ -0,0 +1,142 @@
+/**
+ * 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.bus.spring;
+
+import org.apache.cxf.bus.extension.ExtensionManagerBus;
+import org.apache.cxf.configuration.ConfiguredBeanLocator;
+import org.apache.cxf.configuration.Configurer;
+import org.apache.cxf.configuration.spring.ConfigurerImpl;
+import org.apache.cxf.resource.ResourceManager;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextClosedEvent;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.context.support.AbstractApplicationContext;
+
+/**
+ *
+ */
+public class SpringBus extends ExtensionManagerBus
+    implements ApplicationContextAware {
+
+    AbstractApplicationContext ctx;
+    boolean closeContext;
+
+    public SpringBus() {
+    }
+
+    public void setBusConfig(BusDefinitionParser.BusConfig bc) {
+        bc.setBus(this);
+    }
+
+    /** {@inheritDoc}*/
+    public void setApplicationContext(ApplicationContext applicationContext) {
+        ctx = (AbstractApplicationContext)applicationContext;
+        @SuppressWarnings("rawtypes")
+        ApplicationListener listener = new ApplicationListener() {
+            public void onApplicationEvent(ApplicationEvent event) {
+                SpringBus.this.onApplicationEvent(event);
+            }
+        };
+        ctx.addApplicationListener(listener);
+        ApplicationContext ac = applicationContext.getParent();
+        while (ac != null) {
+            if (ac instanceof AbstractApplicationContext) {
+                ((AbstractApplicationContext)ac).addApplicationListener(listener);
+            }
+            ac = ac.getParent();
+        }
+
+        // set the classLoader extension with the application context classLoader
+        setExtension(applicationContext.getClassLoader(), ClassLoader.class);
+
+        setExtension(new ConfigurerImpl(applicationContext), Configurer.class);
+
+        ResourceManager m = getExtension(ResourceManager.class);
+        m.addResourceResolver(new BusApplicationContextResourceResolver(applicationContext));
+
+        setExtension(applicationContext, ApplicationContext.class);
+        ConfiguredBeanLocator loc = getExtension(ConfiguredBeanLocator.class);
+        if (!(loc instanceof SpringBeanLocator)) {
+            setExtension(new SpringBeanLocator(applicationContext, this), ConfiguredBeanLocator.class);
+        }
+        if (getState() != BusState.RUNNING) {
+            initialize();
+        }
+    }
+
+    public void onApplicationEvent(ApplicationEvent event) {
+        if (ctx == null) {
+            return;
+        }
+        boolean doIt = false;
+        ApplicationContext ac = ctx;
+        while (ac != null) {
+            if (event.getSource() == ac) {
+                doIt = true;
+                break;
+            }
+            ac = ac.getParent();
+        }
+        if (doIt) {
+            if (event instanceof ContextRefreshedEvent) {
+                if (getState() != BusState.RUNNING) {
+                    initialize();
+                }
+            } else if (event instanceof ContextClosedEvent && getState() == BusState.RUNNING) {
+                // The bus could be create by using SpringBusFactory.createBus("/cxf.xml");
+                // Just to make sure the shutdown is called rightly
+                shutdown();
+            }
+        }
+    }
+
+    @Override
+    public void destroyBeans() {
+        if (closeContext) {
+            ctx.close();
+        }
+        super.destroyBeans();
+    }
+
+    @Override
+    public String getId() {
+        if (id == null) {
+            try {
+                Class<?> clsbc = Class.forName("org.osgi.framework.BundleContext");
+                Class<?> clsb = Class.forName("org.osgi.framework.Bundle");
+                Object o = getExtension(clsbc);
+                Object o2 = clsbc.getMethod("getBundle").invoke(o);
+                String s = (String)clsb.getMethod("getSymbolicName").invoke(o2);
+                id = s + '-' + DEFAULT_BUS_ID + Integer.toString(this.hashCode());
+            } catch (Throwable t) {
+                id = super.getId();
+            }
+        }
+        return id;
+    }
+
+    public void setCloseContext(boolean b) {
+        closeContext = b;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/catalog/CatalogXmlSchemaURIResolver.java b/transform/src/patch/java/org/apache/cxf/catalog/CatalogXmlSchemaURIResolver.java
new file mode 100644
index 0000000..15a3ce2
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/catalog/CatalogXmlSchemaURIResolver.java
@@ -0,0 +1,102 @@
+/**
+ * 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.catalog;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.xml.sax.InputSource;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.resource.ExtendedURIResolver;
+import org.apache.cxf.transport.TransportURIResolver;
+import org.apache.ws.commons.schema.XmlSchemaException;
+import org.apache.ws.commons.schema.resolver.URIResolver;
+
+/**
+ * Resolves URIs using Apache Commons Resolver API.
+ */
+public class CatalogXmlSchemaURIResolver implements URIResolver {
+
+    private ExtendedURIResolver resolver;
+    private Bus bus;
+    private Map<String, String> resolved = new HashMap<>();
+
+    public CatalogXmlSchemaURIResolver(Bus bus) {
+        this.resolver = new TransportURIResolver(bus);
+        this.bus = bus;
+    }
+
+    public Map<String, String> getResolvedMap() {
+        return resolved;
+    }
+
+    public InputSource resolveEntity(String targetNamespace, String schemaLocation, String baseUri) {
+        final String resolvedSchemaLocation;
+        OASISCatalogManager catalogResolver = OASISCatalogManager.getCatalogManager(bus);
+        try {
+            resolvedSchemaLocation = new OASISCatalogManagerHelper().resolve(catalogResolver,
+                                          schemaLocation, baseUri);
+        } catch (Exception e) {
+            throw new RuntimeException("Catalog resolution failed", e);
+        }
+
+        final InputSource in;
+        if (resolvedSchemaLocation == null) {
+            in = this.resolver.resolve(schemaLocation, baseUri);
+        } else {
+            resolved.put(schemaLocation, resolvedSchemaLocation);
+            in = this.resolver.resolve(resolvedSchemaLocation, baseUri);
+        }
+
+        // If we return null, a NPE is raised in SchemaBuilder.
+        // If we return new InputSource(), a XmlSchemaException is raised
+        // but without any nice error message. So let's just throw a nice error here.
+        if (in == null) {
+            throw new XmlSchemaException("Unable to locate imported document "
+                                         + "at '" + schemaLocation + "'"
+                                         + (baseUri == null
+                                            ? "."
+                                            : ", relative to '" + baseUri + "'."));
+        } else if (in.getByteStream() != null
+            && !(in.getByteStream() instanceof ByteArrayInputStream)) {
+            //workaround bug in XmlSchema - XmlSchema is not closing the InputStreams
+            //that are returned for imports.  Thus, with a lot of services starting up
+            //or a lot of schemas imported or similar, it's easy to run out of
+            //file handles.  We'll just load the file into a byte[] and return that.
+            try {
+                InputStream ins = IOUtils.loadIntoBAIS(in.getByteStream());
+                in.setByteStream(ins);
+            } catch (IOException e) {
+                throw new XmlSchemaException("Unable to load imported document "
+                                             + "at '" + schemaLocation + "'"
+                                             + (baseUri == null
+                                                ? "."
+                                                : ", relative to '" + baseUri + "'."),
+                                                e);
+            }
+        }
+
+        return in;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/i18n/Exception.java b/transform/src/patch/java/org/apache/cxf/common/i18n/Exception.java
new file mode 100644
index 0000000..fcb38ad
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/i18n/Exception.java
@@ -0,0 +1,58 @@
+/**
+ * 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.common.i18n;
+
+
+
+public class Exception extends java.lang.Exception {
+
+    private static final long serialVersionUID = 1L;
+
+    private final Message message;
+
+    public Exception(Message msg) {
+        message = msg;
+    }
+
+    public Exception(Message msg, Throwable t) {
+        super(t);
+        message = msg;
+    }
+
+    public Exception(Throwable cause) {
+        super(cause);
+        message = null;
+    }
+
+    public String getCode() {
+        if (null != message) {
+            return message.getCode();
+        }
+        return null;
+    }
+
+    @Override
+    public String getMessage() {
+        if (null != message) {
+            return message.toString();
+        }
+        return null;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/i18n/Message.java b/transform/src/patch/java/org/apache/cxf/common/i18n/Message.java
new file mode 100644
index 0000000..d1a65c8
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/i18n/Message.java
@@ -0,0 +1,91 @@
+/**
+ * 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.common.i18n;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.text.MessageFormat;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.logging.Logger;
+
+public class Message implements Serializable {
+    private static final long serialVersionUID = 42L;
+
+    transient String code;
+    transient Object[] parameters;
+    transient ResourceBundle bundle;
+
+    /**
+     * Constructor.
+     *
+     * @param key the message catalog (resource bundle) key
+     * @param logger a logger with an associated resource bundle
+     * @param params the message substitution parameters
+     */
+    public Message(String key, Logger logger, Object...params) {
+        this(key, logger.getResourceBundle(), params);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param key the message catalog (resource bundle) key
+     * @param catalog the resource bundle
+     * @param params the message substitution parameters
+     */
+    public Message(String key, ResourceBundle catalog, Object...params) {
+        code = key;
+        bundle = catalog;
+        parameters = params;
+    }
+
+    public String toString() {
+        final String fmt;
+        try {
+            if (null == bundle) {
+                return code;
+            }
+            fmt = bundle.getString(code);
+        } catch (MissingResourceException ex) {
+            return code;
+        }
+        return MessageFormat.format(fmt, parameters);
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public Object[] getParameters() {
+        return parameters;
+    }
+
+    private void writeObject(java.io.ObjectOutputStream out)
+        throws IOException {
+        out.writeUTF(toString());
+    }
+    private void readObject(java.io.ObjectInputStream in)
+        throws IOException, ClassNotFoundException {
+        code = in.readUTF();
+        bundle = null;
+        parameters = null;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/i18n/UncheckedException.java b/transform/src/patch/java/org/apache/cxf/common/i18n/UncheckedException.java
new file mode 100644
index 0000000..ea15fd7
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/i18n/UncheckedException.java
@@ -0,0 +1,84 @@
+/**
+ * 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.common.i18n;
+
+import java.util.ResourceBundle;
+import java.util.logging.Logger;
+
+
+
+public class UncheckedException extends java.lang.RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    protected final Message message;
+
+    public UncheckedException(Message msg) {
+        message = msg;
+    }
+
+    public UncheckedException(Message msg, Throwable t) {
+        super(t);
+        message = msg;
+    }
+
+    public UncheckedException(Throwable cause) {
+        super(cause);
+        message = null;
+    }
+
+    public UncheckedException(Logger log, String msg, Object ... params) {
+        message = new org.apache.cxf.common.i18n.Message(msg,
+                                                         log,
+                                                         params);
+    }
+    public UncheckedException(ResourceBundle bundle, String msg, Object ... params) {
+        message = new org.apache.cxf.common.i18n.Message(msg,
+                                                         bundle,
+                                                         params);
+    }
+    public UncheckedException(Logger log, String msg, Throwable t, Object ... params) {
+        super(t);
+        message = new org.apache.cxf.common.i18n.Message(msg,
+                                                         log,
+                                                         params);
+    }
+    public UncheckedException(ResourceBundle bundle, String msg, Throwable t, Object ... params) {
+        super(t);
+        message = new org.apache.cxf.common.i18n.Message(msg,
+                                                         bundle,
+                                                         params);
+    }
+
+    public String getCode() {
+        if (null != message) {
+            return message.getCode();
+        }
+        return null;
+    }
+
+    @Override
+    public String getMessage() {
+        if (null != message) {
+            return message.toString();
+        }
+        return null;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/injection/ResourceInjector.java b/transform/src/patch/java/org/apache/cxf/common/injection/ResourceInjector.java
new file mode 100644
index 0000000..9c420ce
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/injection/ResourceInjector.java
@@ -0,0 +1,446 @@
+/**
+ * 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.common.injection;
+
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.annotation.Resource;
+import javax.annotation.Resources;
+
+
+import org.apache.cxf.common.annotation.AbstractAnnotationVisitor;
+import org.apache.cxf.common.annotation.AnnotationProcessor;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.ClassHelper;
+import org.apache.cxf.common.util.ReflectionUtil;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.resource.ResourceManager;
+import org.apache.cxf.resource.ResourceResolver;
+
+
+/**
+ * injects references specified using @Resource annotation
+ *
+ */
+public class ResourceInjector extends AbstractAnnotationVisitor {
+
+    private static final Logger LOG = LogUtils.getL7dLogger(ResourceInjector.class);
+    private static final List<Class<? extends Annotation>> ANNOTATIONS = new ArrayList<>();
+
+    static {
+        ANNOTATIONS.add(Resource.class);
+        ANNOTATIONS.add(Resources.class);
+    }
+
+
+    private final ResourceManager resourceManager;
+    private final List<ResourceResolver> resourceResolvers;
+
+    public ResourceInjector(ResourceManager resMgr) {
+        this(resMgr, resMgr == null ? null : resMgr.getResourceResolvers());
+    }
+
+    public ResourceInjector(ResourceManager resMgr, List<ResourceResolver> resolvers) {
+        super(ANNOTATIONS);
+        resourceManager = resMgr;
+        resourceResolvers = resolvers;
+    }
+
+    private static Field getField(Class<?> cls, String name) {
+        if (cls == null) {
+            return null;
+        }
+        try {
+            Field f = ReflectionUtil.getDeclaredField(cls, name);
+            if (f == null) {
+                f = getField(cls.getSuperclass(), name);
+            }
+            return f;
+        } catch (Exception ex) {
+            return getField(cls.getSuperclass(), name);
+        }
+    }
+
+    public static boolean processable(Class<?> cls, Object o) {
+        if (cls.getName().startsWith("java.")
+            || cls.getName().startsWith("javax.")) {
+            return false;
+        }
+        NoJSR250Annotations njsr = cls.getAnnotation(NoJSR250Annotations.class);
+        if (njsr != null) {
+            for (String s : njsr.unlessNull()) {
+                try {
+                    Field f = getField(cls, s);
+                    ReflectionUtil.setAccessible(f);
+                    if (f.get(o) == null) {
+                        return true;
+                    }
+                } catch (Exception ex) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        return true;
+    }
+
+    public void inject(Object o) {
+        inject(o, o.getClass());
+    }
+
+    public void inject(Object o, Class<?> claz) {
+        if (processable(claz, o)) {
+            AnnotationProcessor processor = new AnnotationProcessor(o);
+            processor.accept(this, claz);
+        }
+    }
+
+    public void construct(Object o) {
+        setTarget(o);
+        if (processable(targetClass, o)) {
+            invokePostConstruct();
+        }
+    }
+    public void construct(Object o, Class<?> cls) {
+        setTarget(o, cls);
+        if (processable(targetClass, o)) {
+            invokePostConstruct();
+        }
+    }
+
+
+    public void destroy(Object o) {
+        setTarget(o);
+        if (processable(targetClass, o)) {
+            invokePreDestroy();
+        }
+    }
+
+
+    // Implementation of org.apache.cxf.common.annotation.AnnotationVisitor
+
+    @Override
+    public final void visitClass(final Class<?> clz, final Annotation annotation) { //NOPMD
+
+        assert annotation instanceof Resource || annotation instanceof Resources : annotation;
+
+        if (annotation instanceof Resource) {
+            injectResourceClassLevel((Resource)annotation);
+        } else if (annotation instanceof Resources) {
+            Resources resources = (Resources)annotation;
+            for (Resource resource : resources.value()) {
+                injectResourceClassLevel(resource);
+            }
+        }
+
+    }
+
+    private void injectResourceClassLevel(Resource res) {
+        if (res.name() == null || "".equals(res.name())) {
+            LOG.log(Level.INFO, "RESOURCE_NAME_NOT_SPECIFIED", target.getClass().getName());
+            return;
+        }
+
+        Object resource;
+        // first find a setter that matches this resource
+        Method setter = findSetterForResource(res);
+        if (setter != null) {
+            Class<?> type = getResourceType(res, setter);
+            resource = resolveResource(res.name(), type);
+            if (resource == null) {
+                LOG.log(Level.INFO, "RESOURCE_RESOLVE_FAILED");
+                return;
+            }
+
+            invokeSetter(setter, resource);
+            return;
+        }
+
+        Field field = findFieldForResource(res);
+        if (field != null) {
+            Class<?> type = getResourceType(res, field);
+            resource = resolveResource(res.name(), type);
+            if (resource == null) {
+                LOG.log(Level.INFO, "RESOURCE_RESOLVE_FAILED");
+                return;
+            }
+            injectField(field, resource);
+            return;
+        }
+        LOG.log(Level.SEVERE, "NO_SETTER_OR_FIELD_FOR_RESOURCE", getTarget().getClass().getName());
+    }
+
+    public final void visitField(final Field field, final Annotation annotation) {
+
+        assert annotation instanceof Resource : annotation;
+
+        Resource res = (Resource)annotation;
+
+        String name = getFieldNameForResource(res, field);
+        Class<?> type = getResourceType(res, field);
+
+        Object resource = resolveResource(name, type);
+        if (resource == null
+            && "".equals(res.name())) {
+            resource = resolveResource(null, type);
+        }
+        if (resource != null) {
+            injectField(field, resource);
+        } else {
+            LOG.log(Level.INFO, "RESOURCE_RESOLVE_FAILED", name);
+        }
+    }
+
+    public final void visitMethod(final Method method, final Annotation annotation) {
+
+        assert annotation instanceof Resource : annotation;
+
+        Resource res = (Resource)annotation;
+
+        String resourceName = getResourceName(res, method);
+        Class<?> clz = getResourceType(res, method);
+
+        Object resource = resolveResource(resourceName, clz);
+        if (resource == null
+            && "".equals(res.name())) {
+            resource = resolveResource(null, clz);
+        }
+        if (resource != null) {
+            invokeSetter(method, resource);
+        } else {
+            LOG.log(Level.FINE, "RESOURCE_RESOLVE_FAILED", new Object[] {resourceName, clz});
+        }
+    }
+
+    private Field findFieldForResource(Resource res) {
+        assert target != null;
+        assert res.name() != null;
+
+        for (Field field : target.getClass().getFields()) {
+            if (field.getName().equals(res.name())) {
+                return field;
+            }
+        }
+
+        for (Field field : target.getClass().getDeclaredFields()) {
+            if (field.getName().equals(res.name())) {
+                return field;
+            }
+        }
+        return null;
+    }
+
+
+    private Method findSetterForResource(Resource res) {
+        assert target != null;
+
+        String setterName = resourceNameToSetter(res.name());
+        Method setterMethod = null;
+
+        for (Method method : getTarget().getClass().getMethods()) {
+            if (setterName.equals(method.getName())) {
+                setterMethod = method;
+                break;
+            }
+        }
+
+        if (setterMethod != null && setterMethod.getParameterTypes().length != 1) {
+            LOG.log(Level.WARNING, "SETTER_INJECTION_WITH_INCORRECT_TYPE", setterMethod);
+        }
+        return setterMethod;
+    }
+
+    private static String resourceNameToSetter(String resName) {
+        return "set" + StringUtils.capitalize(resName);
+    }
+
+    private void invokeSetter(Method method, Object resource) {
+        try {
+            ReflectionUtil.setAccessible(method);
+            if (method.getDeclaringClass().isAssignableFrom(getTarget().getClass())) {
+                method.invoke(getTarget(), resource);
+            } else { // deal with the proxy setter method
+                Method targetMethod = getTarget().getClass().getMethod(method.getName(),
+                                                                       method.getParameterTypes());
+                targetMethod.invoke(getTarget(), resource);
+            }
+        } catch (IllegalAccessException e) {
+            LOG.log(Level.SEVERE, "INJECTION_SETTER_NOT_VISIBLE", method);
+        } catch (InvocationTargetException | SecurityException e) {
+            LogUtils.log(LOG, Level.SEVERE, "INJECTION_SETTER_RAISED_EXCEPTION", e, method);
+        } catch (NoSuchMethodException e) {
+            LOG.log(Level.SEVERE, "INJECTION_SETTER_METHOD_NOT_FOUND", new Object[] {method.getName()});
+        }
+    }
+
+    private String getResourceName(Resource res, Method method) {
+        assert method != null;
+        assert res != null;
+        assert method.getName().startsWith("set") : method;
+
+        if (res.name() == null || res.name().isEmpty()) {
+            String name = method.getName().substring(3);
+            name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
+            return method.getDeclaringClass().getCanonicalName() + '/' + name;
+        }
+        return res.name();
+    }
+
+
+
+    private void injectField(Field field, Object resource) {
+        assert field != null;
+        assert resource != null;
+
+        boolean accessible = field.isAccessible();
+        try {
+            if (field.getType().isAssignableFrom(resource.getClass())) {
+                ReflectionUtil.setAccessible(field);
+                field.set(ClassHelper.getRealObject(getTarget()), resource);
+            }
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+            LOG.severe("FAILED_TO_INJECT_FIELD");
+        } finally {
+            ReflectionUtil.setAccessible(field, accessible);
+        }
+    }
+
+
+    public void invokePostConstruct() {
+
+        boolean accessible = false;
+        for (Method method : getPostConstructMethods()) {
+            PostConstruct pc = method.getAnnotation(PostConstruct.class);
+            if (pc != null) {
+                try {
+                    ReflectionUtil.setAccessible(method);
+                    method.invoke(target);
+                } catch (IllegalAccessException e) {
+                    LOG.log(Level.WARNING, "INJECTION_COMPLETE_NOT_VISIBLE", method);
+                } catch (InvocationTargetException e) {
+                    LOG.log(Level.WARNING, "INJECTION_COMPLETE_THREW_EXCEPTION", e);
+                } finally {
+                    ReflectionUtil.setAccessible(method, accessible);
+                }
+            }
+        }
+    }
+
+    public void invokePreDestroy() {
+
+        boolean accessible = false;
+        for (Method method : getPreDestroyMethods()) {
+            PreDestroy pd = method.getAnnotation(PreDestroy.class);
+            if (pd != null) {
+                try {
+                    ReflectionUtil.setAccessible(method);
+                    method.invoke(target);
+                } catch (IllegalAccessException e) {
+                    LOG.log(Level.WARNING, "PRE_DESTROY_NOT_VISIBLE", method);
+                } catch (InvocationTargetException e) {
+                    LOG.log(Level.WARNING, "PRE_DESTROY_THREW_EXCEPTION", e);
+                } finally {
+                    ReflectionUtil.setAccessible(method, accessible);
+                }
+            }
+        }
+    }
+
+
+    private Collection<Method> getPostConstructMethods() {
+        return getAnnotatedMethods(PostConstruct.class);
+    }
+
+    private Collection<Method> getPreDestroyMethods() {
+        return getAnnotatedMethods(PreDestroy.class);
+    }
+
+    private Collection<Method> getAnnotatedMethods(Class<? extends Annotation> acls) {
+
+        Collection<Method> methods = new LinkedList<>();
+        addAnnotatedMethods(acls, getTarget().getClass().getMethods(), methods);
+        addAnnotatedMethods(acls, ReflectionUtil.getDeclaredMethods(getTarget().getClass()), methods);
+        if (getTargetClass() != getTarget().getClass()) {
+            addAnnotatedMethods(acls, getTargetClass().getMethods(), methods);
+            addAnnotatedMethods(acls, ReflectionUtil.getDeclaredMethods(getTargetClass()), methods);
+        }
+        return methods;
+    }
+
+    private void addAnnotatedMethods(Class<? extends Annotation> acls, Method[] methods,
+        Collection<Method> annotatedMethods) {
+        for (Method method : methods) {
+            if (method.getAnnotation(acls) != null
+                && !annotatedMethods.contains(method)) {
+                annotatedMethods.add(method);
+            }
+        }
+    }
+
+
+    /**
+     * making this protected to keep pmd happy
+     */
+    protected Class<?> getResourceType(Resource res, Field field) {
+        assert res != null;
+        Class<?> type = res.type();
+        if (res.type() == null || Object.class == res.type()) {
+            type = field.getType();
+        }
+        return type;
+    }
+
+
+    private Class<?> getResourceType(Resource res, Method method) {
+        return res.type() != null && !Object.class.equals(res.type())
+            ? res.type()
+            : method.getParameterTypes()[0];
+    }
+
+
+    private String getFieldNameForResource(Resource res, Field field) {
+        assert res != null;
+        if (res.name() == null || "".equals(res.name())) {
+            return field.getDeclaringClass().getCanonicalName() + "/" + field.getName();
+        }
+        return res.name();
+    }
+
+    private Object resolveResource(String resourceName, Class<?> type) {
+        if (resourceManager == null) {
+            return null;
+        }
+        return resourceManager.resolveResource(resourceName, type, resourceResolvers);
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/jaxb/JAXBUtils.java b/transform/src/patch/java/org/apache/cxf/common/jaxb/JAXBUtils.java
new file mode 100644
index 0000000..f018e2a
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/jaxb/JAXBUtils.java
@@ -0,0 +1,1180 @@
+/**
+ * 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.common.jaxb;
+
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.charset.StandardCharsets;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.PropertyException;
+import javax.xml.bind.SchemaOutputResolver;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.attachment.AttachmentMarshaller;
+import javax.xml.bind.attachment.AttachmentUnmarshaller;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.stream.StreamResult;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.spi.ClassLoaderService;
+import org.apache.cxf.common.util.CachedClass;
+import org.apache.cxf.common.util.PackageUtils;
+import org.apache.cxf.common.util.ProxyHelper;
+import org.apache.cxf.common.util.ReflectionInvokationHandler;
+import org.apache.cxf.common.util.ReflectionInvokationHandler.WrapReturn;
+import org.apache.cxf.common.util.ReflectionUtil;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.common.util.SystemPropertyAction;
+import org.apache.cxf.common.xmlschema.SchemaCollection;
+import org.apache.cxf.helpers.JavaUtils;
+
+public final class JAXBUtils {
+    public static final String JAXB_URI = "http://java.sun.com/xml/ns/jaxb";
+
+    private static final Logger LOG = LogUtils.getL7dLogger(JAXBUtils.class);
+
+    public enum IdentifierType {
+        CLASS,
+        INTERFACE,
+        GETTER,
+        SETTER,
+        VARIABLE,
+        CONSTANT
+    };
+
+    private static final char[] XML_NAME_PUNCTUATION_CHARS = new char[] {
+        /* hyphen                       */ '\u002D',
+        /* period                       */ '\u002E',
+        /* colon                        */'\u003A',
+        /* dot                          */ '\u00B7',
+        /* greek ano teleia             */ '\u0387',
+        /* arabic end of ayah           */ '\u06DD',
+        /* arabic start of rub el hizb  */'\u06DE',
+        /* underscore                   */ '\u005F',
+    };
+
+    private static final String XML_NAME_PUNCTUATION_STRING = new String(XML_NAME_PUNCTUATION_CHARS);
+
+    private static final Map<String, String> BUILTIN_DATATYPES_MAP;
+    private static final Map<String, Class<?>> HOLDER_TYPES_MAP;
+    private static ClassLoader jaxbXjcLoader;
+    private static volatile Optional<Object> jaxbMinimumEscapeHandler;
+    private static volatile Optional<Object> jaxbNoEscapeHandler;
+    
+    static {
+        BUILTIN_DATATYPES_MAP = new HashMap<>();
+        BUILTIN_DATATYPES_MAP.put("string", "java.lang.String");
+        BUILTIN_DATATYPES_MAP.put("integer", "java.math.BigInteger");
+        BUILTIN_DATATYPES_MAP.put("int", "int");
+        BUILTIN_DATATYPES_MAP.put("long", "long");
+        BUILTIN_DATATYPES_MAP.put("short", "short");
+        BUILTIN_DATATYPES_MAP.put("decimal", "java.math.BigDecimal");
+        BUILTIN_DATATYPES_MAP.put("float", "float");
+        BUILTIN_DATATYPES_MAP.put("double", "double");
+        BUILTIN_DATATYPES_MAP.put("boolean", "boolean");
+        BUILTIN_DATATYPES_MAP.put("byte", "byte");
+        BUILTIN_DATATYPES_MAP.put("QName", "javax.xml.namespace.QName");
+        BUILTIN_DATATYPES_MAP.put("dateTime", "javax.xml.datatype.XMLGregorianCalendar");
+        BUILTIN_DATATYPES_MAP.put("base64Binary", "byte[]");
+        BUILTIN_DATATYPES_MAP.put("hexBinary", "byte[]");
+        BUILTIN_DATATYPES_MAP.put("unsignedInt", "long");
+        BUILTIN_DATATYPES_MAP.put("unsignedShort", "short");
+        BUILTIN_DATATYPES_MAP.put("unsignedByte", "byte");
+        BUILTIN_DATATYPES_MAP.put("time", "javax.xml.datatype.XMLGregorianCalendar");
+        BUILTIN_DATATYPES_MAP.put("date", "javax.xml.datatype.XMLGregorianCalendar");
+        BUILTIN_DATATYPES_MAP.put("gYear", "javax.xml.datatype.XMLGregorianCalendar");
+        BUILTIN_DATATYPES_MAP.put("gYearMonth", "javax.xml.datatype.XMLGregorianCalendar");
+        BUILTIN_DATATYPES_MAP.put("gMonth", "javax.xml.datatype.XMLGregorianCalendar");
+        BUILTIN_DATATYPES_MAP.put("gMonthDay", "javax.xml.datatype.XMLGregorianCalendar");
+        BUILTIN_DATATYPES_MAP.put("gDay", "javax.xml.datatype.XMLGregorianCalendar");
+        BUILTIN_DATATYPES_MAP.put("duration", "javax.xml.datatype.Duration");
+        BUILTIN_DATATYPES_MAP.put("NOTATION", "javax.xml.namespace.QName");
+
+        HOLDER_TYPES_MAP = new HashMap<>();
+        HOLDER_TYPES_MAP.put("int", java.lang.Integer.class);
+        HOLDER_TYPES_MAP.put("long", java.lang.Long.class);
+        HOLDER_TYPES_MAP.put("short", java.lang.Short.class);
+        HOLDER_TYPES_MAP.put("float", java.lang.Float.class);
+        HOLDER_TYPES_MAP.put("double", java.lang.Double.class);
+        HOLDER_TYPES_MAP.put("boolean", java.lang.Boolean.class);
+        HOLDER_TYPES_MAP.put("byte", java.lang.Byte.class);
+    }
+
+
+    /**
+     * prevents instantiation
+     *
+     */
+    private JAXBUtils() {
+    }
+
+    public static void closeUnmarshaller(Unmarshaller u) {
+        if (u instanceof Closeable) {
+            //need to do this to clear the ThreadLocal cache
+            //see https://java.net/jira/browse/JAXB-1000
+
+            try {
+                ((Closeable)u).close();
+            } catch (IOException e) {
+                //ignore
+            }
+        }
+    }
+    public static Object unmarshall(JAXBContext c, Element e) throws JAXBException {
+        Unmarshaller u = c.createUnmarshaller();
+        try {
+            u.setEventHandler(null);
+            return u.unmarshal(e);
+        } finally {
+            closeUnmarshaller(u);
+        }
+    }
+    public static <T> JAXBElement<T> unmarshall(JAXBContext c, Element e, Class<T> cls) throws JAXBException {
+        Unmarshaller u = c.createUnmarshaller();
+        try {
+            u.setEventHandler(null);
+            return u.unmarshal(e, cls);
+        } finally {
+            closeUnmarshaller(u);
+        }
+    }
+    public static Object unmarshall(JAXBContext c, Source s) throws JAXBException {
+        Unmarshaller u = c.createUnmarshaller();
+        try {
+            u.setEventHandler(null);
+            return u.unmarshal(s);
+        } finally {
+            closeUnmarshaller(u);
+        }
+    }
+    public static <T> JAXBElement<T> unmarshall(JAXBContext c,
+                                                XMLStreamReader reader,
+                                                Class<T> cls) throws JAXBException {
+        Unmarshaller u = c.createUnmarshaller();
+        try {
+            u.setEventHandler(null);
+            return u.unmarshal(reader, cls);
+        } finally {
+            closeUnmarshaller(u);
+        }
+    }
+    public static Object unmarshall(JAXBContext c,
+                                    XMLStreamReader reader) throws JAXBException {
+        Unmarshaller u = c.createUnmarshaller();
+        try {
+            u.setEventHandler(null);
+            return u.unmarshal(reader);
+        } finally {
+            closeUnmarshaller(u);
+        }
+    }
+
+    public static String builtInTypeToJavaType(String type) {
+        return BUILTIN_DATATYPES_MAP.get(type);
+    }
+
+    public static Class<?> holderClass(String type) {
+        return HOLDER_TYPES_MAP.get(type);
+    }
+
+    /**
+     * Checks if the specified word is a Java keyword (as defined in JavaUtils).
+     *
+     * @param word the word to check.
+     * @return true if the word is a keyword.
+     * @see org.apache.cxf.helpers.JavaUtils
+     */
+    protected static boolean isJavaKeyword(String word) {
+        return JavaUtils.isJavaKeyword(word);
+    }
+
+    /**
+     * Generates a Java package name from a URI according to the
+     * algorithm outlined in JAXB 2.0.
+     *
+     * @param namespaceURI the namespace URI.
+     * @return the package name.
+     */
+    public static String namespaceURIToPackage(String namespaceURI) {
+        try {
+            return nameSpaceURIToPackage(new URI(namespaceURI));
+        } catch (URISyntaxException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * Generates a Java package name from a URI according to the
+     * algorithm outlined in Appendix D of JAXB (2.0+).
+     *
+     * @param uri the namespace URI.
+     * @return the package name.
+     */
+    public static String nameSpaceURIToPackage(URI uri) {
+
+        StringBuilder packageName = new StringBuilder();
+        String authority = uri.getAuthority();
+        String scheme = uri.getScheme();
+        if (authority == null && "urn".equals(scheme)) {
+            authority = uri.getSchemeSpecificPart();
+        }
+
+        if (null != authority && !"".equals(authority)) {
+            if ("urn".equals(scheme)) {
+                packageName.append(authority);
+                /* JAXB 2.2 D.5.1, Rule #5 */
+                for (int i = 0; i < packageName.length(); i++) {
+                    if (packageName.charAt(i) == '-') {
+                        packageName.setCharAt(i, '.');
+                    }
+                }
+                authority = packageName.toString();
+                packageName.setLength(0);
+
+                StringTokenizer st = new StringTokenizer(authority, ":");
+                while (st.hasMoreTokens()) {
+                    String token = st.nextToken();
+                    if (packageName.length() > 0) {
+                        packageName.insert(0, '.');
+                        packageName.insert(0, normalizePackageNamePart(token));
+                    } else {
+                        packageName.insert(0, token);
+                    }
+                }
+                authority = packageName.toString();
+                packageName.setLength(0);
+
+            }
+
+            StringTokenizer st = new StringTokenizer(authority, ".");
+            if (st.hasMoreTokens()) {
+                while (st.hasMoreTokens()) {
+                    String token = st.nextToken();
+                    if (packageName.length() == 0) {
+                        if ("www".equals(token)) {
+                            continue;
+                        }
+                    } else {
+                        packageName.insert(0, '.');
+                    }
+                    packageName.insert(0, normalizePackageNamePart(token));
+                }
+            }
+
+            if (!("http".equalsIgnoreCase(scheme) || "urn".equalsIgnoreCase(scheme))) {
+                packageName.insert(0, '.');
+                packageName.insert(0, normalizePackageNamePart(scheme));
+            }
+
+        }
+
+        String path = uri.getPath();
+        if (path == null) {
+            path = "";
+        }
+        /* JAXB 2.2 D.5.1 Rule 2 - remove trailing .??, .???, or .html only. */
+        int index = path.lastIndexOf('.');
+        if (index < 0) {
+            index = path.length();
+        } else {
+            String ending = path.substring(index + 1);
+            if (ending.length() < 2 || (ending.length() > 3
+                && !"html".equalsIgnoreCase(ending))) {
+                index = path.length();
+            }
+        }
+        StringTokenizer st = new StringTokenizer(path.substring(0, index), "/");
+        while (st.hasMoreTokens()) {
+            String token = st.nextToken();
+            if (packageName.length() > 0) {
+                packageName.append('.');
+            }
+            packageName.append(normalizePackageNamePart(token));
+        }
+        return packageName.toString();
+    }
+
+    private static String normalizePackageNamePart(String name) {
+        StringBuilder sname = new StringBuilder(name.toLowerCase());
+
+        for (int i = 0; i < sname.length(); i++) {
+            sname.setCharAt(i, Character.toLowerCase(sname.charAt(i)));
+        }
+
+        for (int i = 0; i < sname.length(); i++) {
+            if (!Character.isJavaIdentifierPart(sname.charAt(i))) {
+                sname.setCharAt(i, '_');
+            }
+        }
+
+        if (isJavaKeyword(sname.toString())) {
+            sname.insert(0, '_');
+        }
+
+        if (!Character.isJavaIdentifierStart(sname.charAt(0))) {
+            sname.insert(0, '_');
+        }
+
+        return sname.toString();
+    }
+
+
+    /**
+     * Converts an XML name to a Java identifier according to the mapping
+     * algorithm outlined in the JAXB specification
+     *
+     * @param name the XML name
+     * @return the Java identifier
+     */
+    public static String nameToIdentifier(String name, IdentifierType type) {
+
+        if (null == name || name.length() == 0) {
+            return name;
+        }
+
+        // algorithm will not change an XML name that is already a legal and
+        // conventional (!) Java class, method, or constant identifier
+
+        StringBuilder buf = new StringBuilder(name);
+        boolean hasUnderscore = false;
+        boolean legalIdentifier = Character.isJavaIdentifierStart(buf.charAt(0));
+
+        for (int i = 1; i < name.length() && legalIdentifier; i++) {
+            legalIdentifier &= Character.isJavaIdentifierPart(buf.charAt(i));
+            hasUnderscore |= '_' == buf.charAt(i);
+        }
+
+        boolean conventionalIdentifier = isConventionalIdentifier(buf, type);
+        if (legalIdentifier && conventionalIdentifier) {
+            if (JAXBUtils.isJavaKeyword(name) && type == IdentifierType.VARIABLE) {
+                name = normalizePackageNamePart(name);
+            }
+            if (!hasUnderscore || IdentifierType.CLASS != type) {
+                return name;
+            }
+        }
+
+        // split into words
+
+        List<String> words = new ArrayList<>();
+
+        StringTokenizer st = new StringTokenizer(name, XML_NAME_PUNCTUATION_STRING);
+        while (st.hasMoreTokens()) {
+            words.add(st.nextToken());
+        }
+
+        for (int i = 0; i < words.size(); i++) {
+            splitWord(words, i);
+        }
+
+        return makeConventionalIdentifier(words, type);
+    }
+
+    private static void splitWord(List<String> words, int listIndex) {
+        String word = words.get(listIndex);
+        if (word.length() <= 1) {
+            return;
+        }
+        int index = listIndex + 1;
+        StringBuilder sword = new StringBuilder(word);
+        int first = 0;
+        char firstChar = sword.charAt(first);
+        if (Character.isLowerCase(firstChar)) {
+            sword.setCharAt(first, Character.toUpperCase(firstChar));
+        }
+        int i = 1;
+
+        while (i < sword.length()) {
+            if (Character.isDigit(firstChar)) {
+                while (i < sword.length() && Character.isDigit(sword.charAt(i))) {
+                    i++;
+                }
+            } else if (isCasedLetter(firstChar)) {
+                boolean previousIsLower = Character.isLowerCase(firstChar);
+                while (i < sword.length() && isCasedLetter(sword.charAt(i))) {
+                    if (Character.isUpperCase(sword.charAt(i)) && previousIsLower) {
+                        break;
+                    }
+                    previousIsLower = Character.isLowerCase(sword.charAt(i));
+                    i++;
+                }
+            } else {
+                // first must be a mark or an uncased letter
+                while (i < sword.length() && (isMark(sword.charAt(i)) || !isCasedLetter(sword.charAt(i)))) {
+                    i++;
+                }
+            }
+
+            // characters from first to i are all either
+            // * digits
+            // * upper or lower case letters, with only the first one an upper
+            // * uncased letters or marks
+
+
+            String newWord = sword.substring(first, i);
+            words.add(index, newWord);
+            index++;
+            if (i >= sword.length()) {
+                break;
+            }
+            first = i;
+            firstChar = sword.charAt(first);
+        }
+
+        if (index > (listIndex + 1)) {
+            words.remove(listIndex);
+        }
+    }
+
+    private static boolean isMark(char c) {
+        return Character.isJavaIdentifierPart(c) && !Character.isLetter(c) && !Character.isDigit(c);
+    }
+
+    private static boolean isCasedLetter(char c) {
+        return Character.isUpperCase(c) || Character.isLowerCase(c);
+    }
+
+    private static boolean isConventionalIdentifier(StringBuilder buf, IdentifierType type) {
+        if (null == buf || buf.length() == 0) {
+            return false;
+        }
+        final boolean result;
+        if (IdentifierType.CONSTANT == type) {
+            for (int i = 0; i < buf.length(); i++) {
+                if (Character.isLowerCase(buf.charAt(i))) {
+                    return false;
+                }
+            }
+            result = true;
+        } else if (IdentifierType.VARIABLE == type) {
+            result = Character.isLowerCase(buf.charAt(0));
+        } else {
+            int pos = 3;
+            if (IdentifierType.GETTER == type
+                && !(buf.length() >= pos
+                    && "get".equals(buf.subSequence(0, 3)))) {
+                return false;
+            } else if (IdentifierType.SETTER == type
+                && !(buf.length() >= pos && "set".equals(buf.subSequence(0, 3)))) {
+                return false;
+            } else {
+                pos = 0;
+            }
+            result = Character.isUpperCase(buf.charAt(pos));
+        }
+        return result;
+    }
+
+    private static String makeConventionalIdentifier(List<String> words, IdentifierType type) {
+        StringBuilder buf = new StringBuilder();
+        boolean firstWord = true;
+        if (IdentifierType.GETTER == type) {
+            buf.append("get");
+        } else if (IdentifierType.SETTER == type) {
+            buf.append("set");
+        }
+        for (String w : words) {
+            int l = buf.length();
+            if (l > 0 && IdentifierType.CONSTANT == type) {
+                buf.append('_');
+                l++;
+            }
+            buf.append(w);
+            if (IdentifierType.CONSTANT == type) {
+                for (int i = l; i < buf.length(); i++) {
+                    if (Character.isLowerCase(buf.charAt(i))) {
+                        buf.setCharAt(i, Character.toUpperCase(buf.charAt(i)));
+                    }
+                }
+            } else if (IdentifierType.VARIABLE == type) {
+                if (firstWord && Character.isUpperCase(buf.charAt(l))) {
+                    buf.setCharAt(l, Character.toLowerCase(buf.charAt(l)));
+                }
+            } else {
+                if (firstWord && Character.isLowerCase(buf.charAt(l))) {
+                    buf.setCharAt(l, Character.toUpperCase(buf.charAt(l)));
+                }
+            }
+            firstWord = false;
+        }
+        return buf.toString();
+    }
+
+    public static Class<?> getValidClass(Class<?> cls) {
+        if (cls.isEnum() || cls.isArray()) {
+            return cls;
+        }
+
+        if (cls == Object.class || cls == String.class || cls.isPrimitive() || cls.isAnnotation()
+            || "javax.xml.ws.Holder".equals(cls.getName())) {
+            return null;
+        } else if (cls.isInterface()
+            || "javax.xml.ws.wsaddressing.W3CEndpointReference".equals(cls.getName())) {
+            return cls;
+        }
+
+        Constructor<?> cons = ReflectionUtil.getDeclaredConstructor(cls);
+        if (cons == null) {
+            cons = ReflectionUtil.getConstructor(cls);
+            if (cons == null) {
+                return null;
+            }
+        }
+        return cls;
+    }
+
+    private static synchronized ClassLoader getXJCClassLoader() {
+        if (jaxbXjcLoader == null) {
+            try {
+                Class.forName("com.sun.tools.internal.xjc.api.XJC");
+                jaxbXjcLoader = ClassLoader.getSystemClassLoader();
+            } catch (Exception t2) {
+                //couldn't find either, probably cause tools.jar isn't on
+                //the classpath.   Let's see if we can find the tools jar
+                String s = SystemPropertyAction.getProperty("java.home");
+                if (!StringUtils.isEmpty(s)) {
+                    File home = new File(s);
+                    File jar = new File(home, "lib/tools.jar");
+                    if (!jar.exists()) {
+                        jar = new File(home, "../lib/tools.jar");
+                    }
+                    if (jar.exists()) {
+                        try {
+                            jaxbXjcLoader = new URLClassLoader(new URL[] {jar.toURI().toURL()});
+                            Class.forName("com.sun.tools.internal.xjc.api.XJC", false, jaxbXjcLoader);
+                        } catch (Exception e) {
+                            jaxbXjcLoader = null;
+                        }
+                    }
+                }
+            }
+        }
+        return jaxbXjcLoader;
+    }
+
+    public static Object setNamespaceMapper(Bus bus, final Map<String, String> nspref,
+                                            Marshaller marshaller) throws PropertyException {
+        ClassLoaderService classLoaderService = bus.getExtension(ClassLoaderService.class);
+        Object mapper = classLoaderService.createNamespaceWrapperInstance(marshaller.getClass(), nspref);
+        if (mapper != null) {
+            if (marshaller.getClass().getName().contains(".internal.")) {
+                marshaller.setProperty("com.sun.xml.internal.bind.namespacePrefixMapper",
+                                       mapper);
+            } else if (marshaller.getClass().getName().contains("com.sun")) {
+                marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper",
+                                       mapper);
+            } else if (marshaller.getClass().getName().contains("eclipse")) {
+                marshaller.setProperty("eclipselink.namespace-prefix-mapper",
+                                       mapper);
+            }
+        }
+        return mapper;
+    }
+    public static BridgeWrapper createBridge(Set<Class<?>> ctxClasses,
+                                      QName qname,
+                                      Class<?> refcls,
+                                      Annotation[] anns) throws JAXBException {
+        try {
+            Class<?> cls;
+            Class<?> refClass;
+            String pkg = "com.sun.xml.bind.";
+            try {
+                cls = Class.forName("com.sun.xml.bind.api.JAXBRIContext");
+                refClass = Class.forName(pkg + "api.TypeReference");
+            } catch (ClassNotFoundException e) {
+                cls = Class.forName("com.sun.xml.internal.bind.api.JAXBRIContext", true, getXJCClassLoader());
+                pkg = "com.sun.xml.internal.bind.";
+                refClass = Class.forName(pkg + "api.TypeReference", true, getXJCClassLoader());
+            }
+            Object ref = refClass.getConstructor(QName.class,
+                                                 Type.class,
+                                                 anns.getClass()).newInstance(qname, refcls, anns);
+            List<Object> typeRefs = new ArrayList<>();
+            typeRefs.add(ref);
+            List<Class<?>> clses = new ArrayList<>(ctxClasses);
+            clses.add(refClass.getField("type").get(ref).getClass());
+            if (!refcls.isInterface()) {
+                clses.add(refcls);
+            }
+
+            Object ctx = null;
+            for (Method m : cls.getDeclaredMethods()) {
+                if ("newInstance".equals(m.getName())
+                    && m.getParameterTypes().length == 6) {
+                    ctx = m.invoke(null, clses.toArray(new Class<?>[0]),
+                                         typeRefs, null, null, true, null);
+
+                }
+            }
+
+            if (ctx == null) {
+                throw new JAXBException("No ctx found");
+            }
+
+            Object bridge = ctx.getClass().getMethod("createBridge", refClass).invoke(ctx, ref);
+            return ReflectionInvokationHandler.createProxyWrapper(bridge,
+                                                                  BridgeWrapper.class);
+        } catch (Exception ex) {
+            throw new JAXBException(ex);
+        }
+    }
+    public interface BridgeWrapper {
+
+        Object unmarshal(XMLStreamReader source, AttachmentUnmarshaller am) throws JAXBException;
+
+        Object unmarshal(InputStream source) throws JAXBException;
+
+        Object unmarshal(Node source, AttachmentUnmarshaller am) throws JAXBException;
+
+        void marshal(Object elValue, XMLStreamWriter source, AttachmentMarshaller m) throws JAXBException;
+
+        void marshal(Object elValue, StreamResult s1) throws JAXBException;
+
+        void marshal(Object elValue, Node source, AttachmentMarshaller am) throws JAXBException;
+    }
+
+
+    public static SchemaCompiler createSchemaCompiler() throws JAXBException {
+        try {
+            Class<?> cls;
+            Object sc;
+            try {
+                cls = Class.forName("com.sun.tools.xjc.api.XJC");
+                sc = cls.getMethod("createSchemaCompiler").invoke(null);
+            } catch (Throwable e) {
+                cls = Class.forName("com.sun.tools.internal.xjc.api.XJC", true, getXJCClassLoader());
+                sc = cls.getMethod("createSchemaCompiler").invoke(null);
+            }
+
+            return ReflectionInvokationHandler.createProxyWrapper(sc,
+                                                                  SchemaCompiler.class);
+        } catch (Exception ex) {
+            throw new JAXBException(ex);
+        }
+    }
+
+    public static SchemaCompiler createSchemaCompilerWithDefaultAllocator(Set<String> allocatorSet) {
+
+        try {
+            SchemaCompiler compiler = JAXBUtils.createSchemaCompiler();
+            Object allocator = ReflectionInvokationHandler
+                .createProxyWrapper(new DefaultClassNameAllocator(allocatorSet),
+                                JAXBUtils.getParamClass(compiler, "setClassNameAllocator"));
+
+            compiler.setClassNameAllocator(allocator);
+            return compiler;
+        } catch (JAXBException e1) {
+            throw new IllegalStateException("Unable to create schema compiler", e1);
+        }
+
+    }
+
+    public static void logGeneratedClassNames(Logger logger, JCodeModel codeModel) {
+        if (!logger.isLoggable(Level.FINE)) {
+            return;
+        }
+
+        StringBuilder sb = new StringBuilder();
+        boolean first = true;
+        for (Iterator<JPackage> itr = codeModel.packages(); itr.hasNext();) {
+            JPackage package1 = itr.next();
+
+            for (Iterator<JDefinedClass> citr = package1.classes(); citr.hasNext();) {
+                if (!first) {
+                    sb.append(", ");
+                } else {
+                    first = false;
+                }
+                sb.append(citr.next().fullName());
+            }
+        }
+
+        logger.log(Level.FINE, "Created classes: " + sb.toString());
+    }
+
+    public static List<String> getGeneratedClassNames(JCodeModel codeModel) {
+        List<String> classes = new ArrayList<>();
+        for (Iterator<JPackage> itr = codeModel.packages(); itr.hasNext();) {
+            JPackage package1 = itr.next();
+
+            for (Iterator<JDefinedClass> citr = package1.classes(); citr.hasNext();) {
+                classes.add(citr.next().fullName());
+            }
+        }
+        return classes;
+    }
+    public static Object createFileCodeWriter(File f) throws JAXBException {
+        return createFileCodeWriter(f, StandardCharsets.UTF_8.name());
+    }
+    public static Object createFileCodeWriter(File f, String encoding) throws JAXBException {
+        try {
+            Class<?> cls;
+            try {
+                cls = Class.forName("com.sun.codemodel.writer.FileCodeWriter");
+            } catch (ClassNotFoundException e) {
+                cls = Class.forName("com.sun.codemodel.internal.writer.FileCodeWriter",
+                                    true, getXJCClassLoader());
+            }
+            if (encoding != null) {
+                try {
+                    return cls.getConstructor(File.class, String.class)
+                              .newInstance(f, encoding);
+                } catch (Exception ex) {
+                    // try a single argument constructor
+                }
+            }
+            return cls.getConstructor(File.class).newInstance(f);
+        } catch (Exception ex) {
+            throw new JAXBException(ex);
+        }
+    }
+
+    public static Class<?> getParamClass(SchemaCompiler sc, String method) {
+        Object o = ((ReflectionInvokationHandler)Proxy.getInvocationHandler(sc)).getTarget();
+        for (Method m : o.getClass().getMethods()) {
+            if (m.getName().equals(method) && m.getParameterTypes().length == 1) {
+                return m.getParameterTypes()[0];
+            }
+        }
+        return null;
+    }
+
+
+    public static List<DOMResult> generateJaxbSchemas(
+        JAXBContext context, final Map<String, DOMResult> builtIns) throws IOException {
+        final List<DOMResult> results = new ArrayList<>();
+
+        context.generateSchema(new SchemaOutputResolver() {
+            @Override
+            public Result createOutput(String ns, String file) throws IOException {
+                DOMResult result = new DOMResult();
+
+                if (builtIns.containsKey(ns)) {
+                    DOMResult dr = builtIns.get(ns);
+                    result.setSystemId(dr.getSystemId());
+                    results.add(dr);
+                    return result;
+                }
+                result.setSystemId(file);
+                results.add(result);
+                return result;
+            }
+        });
+        return results;
+    }
+
+    public static String getPackageNamespace(Class<?> cls) {
+        Package p = cls.getPackage();
+        if (p != null) {
+            javax.xml.bind.annotation.XmlSchema schemaAnn =
+                p.getAnnotation(javax.xml.bind.annotation.XmlSchema.class);
+            if (schemaAnn != null) {
+                return schemaAnn.namespace();
+            }
+        }
+        return null;
+    }
+
+    public static void scanPackages(Set<Class<?>> classes,
+                                    Map<Package, CachedClass> objectFactoryCache) {
+        scanPackages(classes, null, objectFactoryCache);
+    }
+    public static void scanPackages(Set<Class<?>> classes,
+                                    Class<?>[] extraClass,
+                                    Map<Package, CachedClass> objectFactoryCache) {
+
+        // add user extra class into jaxb context
+        if (extraClass != null && extraClass.length > 0) {
+            for (Class<?> clz : extraClass) {
+                classes.add(clz);
+            }
+        }
+
+        // try and read any jaxb.index files that are with the other classes.
+        // This should
+        // allow loading of extra classes (such as subclasses for inheritance
+        // reasons)
+        // that are in the same package. Also check for ObjectFactory classes
+        Map<String, InputStream> packages = new HashMap<>();
+        Map<String, ClassLoader> packageLoaders = new HashMap<>();
+        Set<Class<?>> objectFactories = new HashSet<>();
+        for (Class<?> jcls : classes) {
+            String pkgName = PackageUtils.getPackageName(jcls);
+            if (!packages.containsKey(pkgName)) {
+                Package pkg = jcls.getPackage();
+
+                packages.put(pkgName, jcls.getResourceAsStream("jaxb.index"));
+                packageLoaders.put(pkgName, getClassLoader(jcls));
+                String objectFactoryClassName = pkgName + "." + "ObjectFactory";
+                Class<?> ofactory = null;
+                CachedClass cachedFactory = null;
+                if (pkg != null && objectFactoryCache != null) {
+                    synchronized (objectFactoryCache) {
+                        cachedFactory = objectFactoryCache.get(pkg);
+                    }
+                }
+                if (cachedFactory != null) {
+                    ofactory = cachedFactory.getCachedClass();
+                }
+                if (ofactory == null) {
+                    try {
+                        ofactory = Class.forName(objectFactoryClassName, false, getClassLoader(jcls));
+                        objectFactories.add(ofactory);
+                        addToObjectFactoryCache(pkg, ofactory, objectFactoryCache);
+                    } catch (ClassNotFoundException e) {
+                        addToObjectFactoryCache(pkg, null, objectFactoryCache);
+                    }
+                } else {
+                    objectFactories.add(ofactory);
+                }
+            }
+        }
+        for (Map.Entry<String, InputStream> entry : packages.entrySet()) {
+            if (entry.getValue() != null) {
+                try (BufferedReader reader = new BufferedReader(
+                        new InputStreamReader(entry.getValue(), StandardCharsets.UTF_8))) {
+                    String pkg = entry.getKey();
+                    ClassLoader loader = packageLoaders.get(pkg);
+                    if (!StringUtils.isEmpty(pkg)) {
+                        pkg += ".";
+                    }
+
+                    String line = reader.readLine();
+                    while (line != null) {
+                        line = line.trim();
+                        if (line.indexOf('#') != -1) {
+                            line = line.substring(0, line.indexOf('#'));
+                        }
+                        if (!StringUtils.isEmpty(line)) {
+                            try {
+                                Class<?> ncls = Class.forName(pkg + line, false, loader);
+                                classes.add(ncls);
+                            } catch (Exception e) {
+                                // ignore
+                            }
+                        }
+                        line = reader.readLine();
+                    }
+                } catch (IOException e) {
+                    // ignore
+                } finally {
+                    try {
+                        entry.getValue().close();
+                    } catch (IOException e) {
+                        // ignore
+                    }
+                }
+            }
+        }
+        classes.addAll(objectFactories);
+    }
+
+    private static ClassLoader getClassLoader(final Class<?> clazz) {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+                @Override
+                public ClassLoader run() {
+                    return clazz.getClassLoader();
+                }
+            });
+        }
+        return clazz.getClassLoader();
+    }
+       
+    private static void addToObjectFactoryCache(Package objectFactoryPkg, 
+                                         Class<?> ofactory,
+                                         Map<Package, CachedClass> objectFactoryCache) {
+        if (objectFactoryPkg == null || objectFactoryCache == null) {
+            return;
+        }
+        synchronized (objectFactoryCache) {
+            objectFactoryCache.put(objectFactoryPkg,
+                                     new CachedClass(ofactory));
+        }
+    }
+
+    public static class DefaultClassNameAllocator {
+        private final Set<String> typesClassNames;
+
+        public DefaultClassNameAllocator() {
+            this(new HashSet<>());
+        }
+
+        public DefaultClassNameAllocator(Set<String> set) {
+            typesClassNames = set;
+        }
+
+        public String assignClassName(String packageName, String className) {
+            String fullClassName = className;
+            String fullPckClass = packageName + "." + fullClassName;
+            int cnt = 0;
+            while (typesClassNames.contains(fullPckClass)) {
+                cnt++;
+                fullClassName = className + cnt;
+                fullPckClass = packageName + "." + fullClassName;
+            }
+            typesClassNames.add(fullPckClass);
+            return fullClassName;
+        }
+
+    }
+
+    public interface SchemaCompiler {
+        void setEntityResolver(EntityResolver entityResolver);
+
+        void setErrorListener(Object elForRun);
+
+        void setClassNameAllocator(Object allocator);
+
+        @WrapReturn(S2JJAXBModel.class)
+        S2JJAXBModel bind();
+
+        void parseSchema(InputSource source);
+
+        void parseSchema(String key, Element el);
+        void parseSchema(String key, XMLStreamReader el);
+
+        @WrapReturn(Options.class)
+        Options getOptions();
+    }
+    public interface S2JJAXBModel {
+
+        @WrapReturn(JCodeModel.class)
+        JCodeModel generateCode(Object object, Object elForRun);
+
+        @WrapReturn(Mapping.class)
+        Mapping get(QName qn);
+
+        @WrapReturn(TypeAndAnnotation.class)
+        TypeAndAnnotation getJavaType(QName typeQName);
+    }
+    public interface Mapping {
+        @WrapReturn(TypeAndAnnotation.class)
+        TypeAndAnnotation getType();
+    }
+    public interface TypeAndAnnotation {
+        @WrapReturn(JType.class)
+        JType getTypeClass();
+    }
+    public interface JType {
+        boolean isArray();
+
+        @WrapReturn(JType.class)
+        JType elementType();
+
+        boolean isPrimitive();
+
+        String binaryName();
+
+        String fullName();
+
+        String name();
+
+        @WrapReturn(value = JType.class, iterator = true)
+        Iterator<JType> classes();
+    }
+    public interface Options {
+
+        void addGrammar(InputSource is);
+
+        void addBindFile(InputSource is);
+
+        void parseArguments(String[] args);
+
+        String getBuildID();
+    }
+    public interface JCodeModel {
+
+        void build(Object writer) throws IOException;
+
+        @WrapReturn(value = JPackage.class, iterator = true)
+        Iterator<JPackage> packages();
+    }
+    public interface JPackage {
+
+        String name();
+
+        @WrapReturn(value = JDefinedClass.class, iterator = true)
+        Iterator<JDefinedClass> classes();
+    }
+    public interface JDefinedClass {
+        String name();
+
+        String fullName();
+    }
+
+    public static boolean isJAXB22() {
+        Target t = XmlElement.class.getAnnotation(Target.class);
+        //JAXB 2.2 allows XmlElement on params.
+        for (ElementType et : t.value()) {
+            if (et == ElementType.PARAMETER) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static JAXBContextProxy createJAXBContextProxy(final JAXBContext ctx) {
+        return createJAXBContextProxy(ctx, null, null);
+    }
+    public static JAXBContextProxy createJAXBContextProxy(final JAXBContext ctx,
+                                                          final SchemaCollection collection,
+                                                          final String defaultNs) {
+        if (ctx.getClass().getName().contains("com.sun.")
+            || collection == null) {
+            return ReflectionInvokationHandler.createProxyWrapper(ctx, JAXBContextProxy.class);
+        }
+        return new SchemaCollectionContextProxy(ctx, collection, defaultNs);
+    }
+    public static JAXBBeanInfo getBeanInfo(JAXBContextProxy context, Class<?> cls) {
+        Object o = context.getBeanInfo(cls);
+        if (o == null) {
+            return null;
+        }
+        if (o instanceof JAXBBeanInfo) {
+            return (JAXBBeanInfo)o;
+        }
+        return ReflectionInvokationHandler.createProxyWrapper(o, JAXBBeanInfo.class);
+    }
+
+    private static String getPostfix(Class<?> cls) {
+        String className = cls.getName();
+        if (className.contains("com.sun.xml.internal")
+            || className.contains("eclipse")) {
+            //eclipse moxy accepts sun package CharacterEscapeHandler 
+            return ".internal";
+        } else if (className.contains("com.sun.xml.bind")
+            || className.startsWith("com.ibm.xml")) {
+            return "";
+        }
+        return null;
+    }
+
+    public static void setMinimumEscapeHandler(Marshaller marshaller) {
+        if (jaxbMinimumEscapeHandler == null) {
+            jaxbMinimumEscapeHandler = Optional.ofNullable(createMininumEscapeHandler(marshaller.getClass()));
+        }
+        jaxbMinimumEscapeHandler.ifPresent(p -> setEscapeHandler(marshaller, p));
+    }
+
+    public static void setNoEscapeHandler(final Marshaller marshaller) {
+        if (jaxbNoEscapeHandler == null) {
+            jaxbNoEscapeHandler = Optional.ofNullable(createNoEscapeHandler(marshaller.getClass()));
+        }
+        jaxbNoEscapeHandler.ifPresent(p -> setEscapeHandler(marshaller, p));
+    }
+    
+    public static void setEscapeHandler(Marshaller marshaller, Object escapeHandler) {
+        try {
+            String postFix = getPostfix(marshaller.getClass());
+            if (postFix != null && escapeHandler != null) {
+                marshaller.setProperty("com.sun.xml" + postFix + ".bind.characterEscapeHandler", escapeHandler);
+            }
+        } catch (PropertyException e) {
+            LOG.log(Level.INFO, "Failed to set MinumEscapeHandler to jaxb marshaller", e);
+        }
+    }
+    
+    public static Object createMininumEscapeHandler(Class<?> cls) {
+        return createEscapeHandler(cls, "MinimumEscapeHandler");
+    }
+    
+    public static Object createNoEscapeHandler(Class<?> cls) {
+        return createEscapeHandler(cls, "NoEscapeHandler");
+    }
+    
+    private static Object createEscapeHandler(Class<?> cls, String simpleClassName) {
+        try {
+            String postFix = getPostfix(cls);
+            if (postFix == null) {
+                LOG.log(Level.WARNING, "Failed to create" + simpleClassName + " for unknown jaxb class:"
+                    + cls);
+                return null;
+            }
+            Class<?> handlerClass = ClassLoaderUtils.loadClass("com.sun.xml" + postFix
+                                                                   + ".bind.marshaller." + simpleClassName,
+                                                               cls);
+            Class<?> handlerInterface = ClassLoaderUtils
+                .loadClass("com.sun.xml" + postFix + ".bind.marshaller.CharacterEscapeHandler",
+                           cls);
+            Object targetHandler = ReflectionUtil.getDeclaredField(handlerClass, "theInstance").get(null);
+            return ProxyHelper.getProxy(cls.getClassLoader(),
+                                        new Class[] {handlerInterface},
+                                        new EscapeHandlerInvocationHandler(targetHandler));
+        } catch (Exception e) {
+            if ("NoEscapeHandler".equals(simpleClassName)) {
+                //this class doesn't exist in JAXB 2.2 so expected
+                LOG.log(Level.FINER, "Failed to create " + simpleClassName);
+            } else {
+                LOG.log(Level.INFO, "Failed to create " + simpleClassName);
+            }
+        }
+        return null;
+    }
+    
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/logging/AbstractDelegatingLogger.java b/transform/src/patch/java/org/apache/cxf/common/logging/AbstractDelegatingLogger.java
new file mode 100644
index 0000000..dd3cbd5
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/logging/AbstractDelegatingLogger.java
@@ -0,0 +1,457 @@
+/**
+ * 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.common.logging;
+
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.logging.Filter;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+/**
+ * java.util.logging.Logger implementation delegating to another framework.
+ * All methods can be used except:
+ *   setLevel
+ *   addHandler / getHandlers
+ *   setParent / getParent
+ *   setUseParentHandlers / getUseParentHandlers
+ */
+public abstract class AbstractDelegatingLogger extends Logger {
+
+    protected AbstractDelegatingLogger(String name, String resourceBundleName) {
+        super(name, resourceBundleName);
+    }
+
+    @Override
+    public void log(LogRecord record) {
+        if (isLoggable(record.getLevel())) {
+            doLog(record);
+        }
+    }
+
+    @Override
+    public void log(Level level, String msg) {
+        if (isLoggable(level)) {
+            LogRecord lr = new LogRecord(level, msg);
+            doLog(lr);
+        }
+    }
+
+    @Override
+    public void log(Level level, String msg, Object param1) {
+        if (isLoggable(level)) {
+            LogRecord lr = new LogRecord(level, msg);
+            Object[] params = {param1 };
+            lr.setParameters(params);
+            doLog(lr);
+        }
+    }
+
+    @Override
+    public void log(Level level, String msg, Object[] params) {
+        if (isLoggable(level)) {
+            LogRecord lr = new LogRecord(level, msg);
+            lr.setParameters(params);
+            doLog(lr);
+        }
+    }
+
+    @Override
+    public void log(Level level, String msg, Throwable thrown) {
+        if (isLoggable(level)) {
+            LogRecord lr = new LogRecord(level, msg);
+            lr.setThrown(thrown);
+            doLog(lr);
+        }
+    }
+
+    @Override
+    public void logp(Level level, String sourceClass, String sourceMethod, String msg) {
+        if (isLoggable(level)) {
+            LogRecord lr = new LogRecord(level, msg);
+            lr.setSourceClassName(sourceClass);
+            lr.setSourceMethodName(sourceMethod);
+            doLog(lr);
+        }
+    }
+
+    @Override
+    public void logp(Level level, String sourceClass, String sourceMethod, String msg, Object param1) {
+        if (isLoggable(level)) {
+            LogRecord lr = new LogRecord(level, msg);
+            lr.setSourceClassName(sourceClass);
+            lr.setSourceMethodName(sourceMethod);
+            Object[] params = {param1 };
+            lr.setParameters(params);
+            doLog(lr);
+        }
+    }
+
+    @Override
+    public void logp(Level level, String sourceClass, String sourceMethod, String msg, Object[] params) {
+        if (isLoggable(level)) {
+            LogRecord lr = new LogRecord(level, msg);
+            lr.setSourceClassName(sourceClass);
+            lr.setSourceMethodName(sourceMethod);
+            lr.setParameters(params);
+            doLog(lr);
+        }
+    }
+
+    @Override
+    public void logp(Level level, String sourceClass, String sourceMethod, String msg, Throwable thrown) {
+        if (isLoggable(level)) {
+            LogRecord lr = new LogRecord(level, msg);
+            lr.setSourceClassName(sourceClass);
+            lr.setSourceMethodName(sourceMethod);
+            lr.setThrown(thrown);
+            doLog(lr);
+        }
+    }
+
+  
+    @Override
+    @Deprecated
+    public void logrb(Level level, String sourceClass, String sourceMethod, String bundleName, String msg) {
+        if (isLoggable(level)) {
+            LogRecord lr = new LogRecord(level, msg);
+            lr.setSourceClassName(sourceClass);
+            lr.setSourceMethodName(sourceMethod);
+            doLog(lr, bundleName);
+        }
+    }
+
+   
+    @Override
+    @Deprecated
+    public void logrb(Level level, String sourceClass, String sourceMethod,
+                      String bundleName, String msg, Object param1) {
+        if (isLoggable(level)) {
+            LogRecord lr = new LogRecord(level, msg);
+            lr.setSourceClassName(sourceClass);
+            lr.setSourceMethodName(sourceMethod);
+            Object[] params = {param1 };
+            lr.setParameters(params);
+            doLog(lr, bundleName);
+        }
+    }
+
+  
+    @Override
+    @Deprecated
+    public void logrb(Level level, String sourceClass, String sourceMethod,
+                      String bundleName, String msg, Object[] params) {
+        if (isLoggable(level)) {
+            LogRecord lr = new LogRecord(level, msg);
+            lr.setSourceClassName(sourceClass);
+            lr.setSourceMethodName(sourceMethod);
+            lr.setParameters(params);
+            doLog(lr, bundleName);
+        }
+    }
+
+    @Override
+    @Deprecated
+    public void logrb(Level level, String sourceClass, String sourceMethod,
+                      String bundleName, String msg, Throwable thrown) {
+        if (isLoggable(level)) {
+            LogRecord lr = new LogRecord(level, msg);
+            lr.setSourceClassName(sourceClass);
+            lr.setSourceMethodName(sourceMethod);
+            lr.setThrown(thrown);
+            doLog(lr, bundleName);
+        }
+    }
+
+    @Override
+    public void entering(String sourceClass, String sourceMethod) {
+        if (isLoggable(Level.FINER)) {
+            logp(Level.FINER, sourceClass, sourceMethod, "ENTRY");
+        }
+    }
+
+    @Override
+    public void entering(String sourceClass, String sourceMethod, Object param1) {
+        if (isLoggable(Level.FINER)) {
+            Object[] params = {param1 };
+            logp(Level.FINER, sourceClass, sourceMethod, "ENTRY {0}", params);
+        }
+    }
+
+    @Override
+    public void entering(String sourceClass, String sourceMethod, Object[] params) {
+        if (isLoggable(Level.FINER)) {
+            String msg = "ENTRY";
+            if (params == null) {
+                logp(Level.FINER, sourceClass, sourceMethod, msg);
+                return;
+            }
+            StringBuilder builder = new StringBuilder(msg);
+            for (int i = 0; i < params.length; i++) {
+                builder.append(" {");
+                builder.append(Integer.toString(i));
+                builder.append('}');
+            }
+            logp(Level.FINER, sourceClass, sourceMethod, builder.toString(), params);
+        }
+    }
+
+    @Override
+    public void exiting(String sourceClass, String sourceMethod) {
+        if (isLoggable(Level.FINER)) {
+            logp(Level.FINER, sourceClass, sourceMethod, "RETURN");
+        }
+    }
+
+    @Override
+    public void exiting(String sourceClass, String sourceMethod, Object result) {
+        if (isLoggable(Level.FINER)) {
+            Object[] params = {result };
+            logp(Level.FINER, sourceClass, sourceMethod, "RETURN {0}", params);
+        }
+    }
+
+    @Override
+    public void throwing(String sourceClass, String sourceMethod, Throwable thrown) {
+        if (isLoggable(Level.FINER)) {
+            LogRecord lr = new LogRecord(Level.FINER, "THROW");
+            lr.setSourceClassName(sourceClass);
+            lr.setSourceMethodName(sourceMethod);
+            lr.setThrown(thrown);
+            doLog(lr);
+        }
+    }
+
+    @Override
+    public void severe(String msg) {
+        if (isLoggable(Level.SEVERE)) {
+            LogRecord lr = new LogRecord(Level.SEVERE, msg);
+            doLog(lr);
+        }
+    }
+
+    @Override
+    public void warning(String msg) {
+        if (isLoggable(Level.WARNING)) {
+            LogRecord lr = new LogRecord(Level.WARNING, msg);
+            doLog(lr);
+        }
+    }
+
+    @Override
+    public void info(String msg) {
+        if (isLoggable(Level.INFO)) {
+            LogRecord lr = new LogRecord(Level.INFO, msg);
+            doLog(lr);
+        }
+    }
+
+    @Override
+    public void config(String msg) {
+        if (isLoggable(Level.CONFIG)) {
+            LogRecord lr = new LogRecord(Level.CONFIG, msg);
+            doLog(lr);
+        }
+    }
+
+    @Override
+    public void fine(String msg) {
+        if (isLoggable(Level.FINE)) {
+            LogRecord lr = new LogRecord(Level.FINE, msg);
+            doLog(lr);
+        }
+    }
+
+    @Override
+    public void finer(String msg) {
+        if (isLoggable(Level.FINER)) {
+            LogRecord lr = new LogRecord(Level.FINER, msg);
+            doLog(lr);
+        }
+    }
+
+    @Override
+    public void finest(String msg) {
+        if (isLoggable(Level.FINEST)) {
+            LogRecord lr = new LogRecord(Level.FINEST, msg);
+            doLog(lr);
+        }
+    }
+
+    @Override
+    public void setLevel(Level newLevel) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public abstract Level getLevel();
+
+    @Override
+    public boolean isLoggable(Level level) {
+        Level l = getLevel();
+        return level.intValue() >= l.intValue() && l != Level.OFF;
+    }
+
+    protected boolean supportsHandlers() {
+        return false;
+    }
+
+    @Override
+    public synchronized void addHandler(Handler handler) {
+        if (supportsHandlers()) {
+            super.addHandler(handler);
+            return;
+        }
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public synchronized void removeHandler(Handler handler) {
+        if (supportsHandlers()) {
+            super.removeHandler(handler);
+            return;
+        }
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public synchronized Handler[] getHandlers() {
+        if (supportsHandlers()) {
+            return super.getHandlers();
+        }
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public synchronized void setUseParentHandlers(boolean useParentHandlers) {
+        if (supportsHandlers()) {
+            super.setUseParentHandlers(useParentHandlers);
+            return;
+        }
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public synchronized boolean getUseParentHandlers() {
+        if (supportsHandlers()) {
+            return super.getUseParentHandlers();
+        }
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Logger getParent() {
+        return null;
+    }
+
+    @Override
+    public void setParent(Logger parent) {
+        throw new UnsupportedOperationException();
+    }
+
+    protected void doLog(LogRecord lr) {
+        lr.setLoggerName(getName());
+        String rbname = getResourceBundleName();
+        if (rbname != null) {
+            lr.setResourceBundleName(rbname);
+            lr.setResourceBundle(getResourceBundle());
+        }
+        internalLog(lr);
+    }
+
+    protected void doLog(LogRecord lr, String rbname) {
+        lr.setLoggerName(getName());
+        if (rbname != null) {
+            lr.setResourceBundleName(rbname);
+            lr.setResourceBundle(loadResourceBundle(rbname));
+        }
+        internalLog(lr);
+    }
+
+    protected void internalLog(LogRecord record) {
+        Filter filter = getFilter();
+        if (filter != null && !filter.isLoggable(record)) {
+            return;
+        }
+        String msg = formatMessage(record);
+        internalLogFormatted(msg, record);
+    }
+
+    protected abstract void internalLogFormatted(String msg, LogRecord record);
+
+    protected String formatMessage(LogRecord record) {
+        String format = record.getMessage();
+        ResourceBundle catalog = record.getResourceBundle();
+        if (catalog != null) {
+            try {
+                format = catalog.getString(record.getMessage());
+            } catch (MissingResourceException ex) {
+                format = record.getMessage();
+            }
+        }
+        try {
+            Object[] parameters = record.getParameters();
+            if (parameters == null || parameters.length == 0) {
+                return format;
+            }
+            if (format.indexOf("{0") >= 0 || format.indexOf("{1") >= 0
+                        || format.indexOf("{2") >= 0 || format.indexOf("{3") >= 0) {
+                return java.text.MessageFormat.format(format, parameters);
+            }
+            return format;
+        } catch (Exception ex) {
+            return format;
+        }
+    }
+
+    /**
+     * Load the specified resource bundle
+     *
+     * @param resourceBundleName
+     *            the name of the resource bundle to load, cannot be null
+     * @return the loaded resource bundle.
+     * @throws java.util.MissingResourceException
+     *             If the specified resource bundle can not be loaded.
+     */
+    static ResourceBundle loadResourceBundle(String resourceBundleName) {
+        // try context class loader to load the resource
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        if (null != cl) {
+            try {
+                return ResourceBundle.getBundle(resourceBundleName, Locale.getDefault(), cl);
+            } catch (MissingResourceException e) {
+                // Failed to load using context classloader, ignore
+            }
+        }
+        // try system class loader to load the resource
+        cl = ClassLoader.getSystemClassLoader();
+        if (null != cl) {
+            try {
+                return ResourceBundle.getBundle(resourceBundleName, Locale.getDefault(), cl);
+            } catch (MissingResourceException e) {
+                // Failed to load using system classloader, ignore
+            }
+        }
+        return null;
+    }
+
+}
\ No newline at end of file
diff --git a/transform/src/patch/java/org/apache/cxf/common/logging/LogUtils.java b/transform/src/patch/java/org/apache/cxf/common/logging/LogUtils.java
new file mode 100644
index 0000000..09727e6
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/logging/LogUtils.java
@@ -0,0 +1,485 @@
+/**
+ * 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.common.logging;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.text.MessageFormat;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import org.apache.cxf.common.i18n.BundleUtils;
+import org.apache.cxf.common.util.StringUtils;
+
+
+/**
+ * A container for static utility methods related to logging.
+ * By default, CXF logs to java.util.logging. An application can change this. To log to another system, the
+ * application must provide an object that extends {@link AbstractDelegatingLogger}, and advertise that class
+ * via one of the following mechanisms:
+ * <ul>
+ * <li>Create a file, in the classpath, named META-INF/cxf/org.apache.cxf.Logger.
+ * This file should contain the fully-qualified name
+ * of the class, with no comments, on a single line.</li>
+ * <li>Call {@link #setLoggerClass(Class)} with a Class<?> reference to the logger class.</li>
+ * </ul>
+ * CXF provides {@link Slf4jLogger} to use slf4j instead of java.util.logging.
+ */
+public final class LogUtils {
+    private static final String KEY = "org.apache.cxf.Logger";
+
+    private static final Object[] NO_PARAMETERS = new Object[0];
+
+
+    private static Class<?> loggerClass;
+
+    /**
+     * Prevents instantiation.
+     */
+    private LogUtils() {
+    }
+
+    static {
+        JDKBugHacks.doHacks();
+
+        try {
+
+            String cname = null;
+            try {
+                cname = AccessController.doPrivileged(new PrivilegedAction<String>() {
+                    public String run() {
+                        return System.getProperty(KEY);
+                    }
+                });
+            } catch (Throwable t) {
+                //ignore - likely security exception or similar that won't allow
+                //access to the system properties.   We'll continue with other methods
+            }
+            if (StringUtils.isEmpty(cname)) {
+                InputStream ins = Thread.currentThread().getContextClassLoader()
+                    .getResourceAsStream("META-INF/cxf/" + KEY);
+                if (ins == null) {
+                    ins = ClassLoader.getSystemResourceAsStream("META-INF/cxf/" + KEY);
+                }
+                if (ins != null) {
+                    try (BufferedReader din = new BufferedReader(new InputStreamReader(ins))) {
+                        cname = din.readLine();
+                    }
+                }
+            }
+            if (StringUtils.isEmpty(cname)) {
+                try {
+                    // This Class.forName likely will barf in OSGi, but it's OK
+                    // as we'll just use j.u.l and pax-logging will pick it up fine
+                    // If we don't call this and there isn't a slf4j impl avail,
+                    // you get warnings printed to stderr about NOPLoggers and such
+                    Class.forName("org.slf4j.impl.StaticLoggerBinder");
+                    Class<?> cls = Class.forName("org.slf4j.LoggerFactory");
+                    Class<?> fcls = cls.getMethod("getILoggerFactory").invoke(null).getClass();
+                    String clsName = fcls.getName();
+                    if (clsName.contains("NOPLogger")) {
+                        //no real slf4j implementation, use j.u.l
+                        cname = null;
+                    } else if (clsName.contains("JDK14")
+                        || clsName.contains("pax.logging")) {
+                        //both of these we can use the appropriate j.u.l API's
+                        //directly and have it work properly
+                        cname = null;
+                    } else {
+                        // Either we cannot really detect where it's logging
+                        // or we don't want to use a custom logger, so we'll
+                        // go ahead and use the Slf4jLogger directly
+                        cname = "org.apache.cxf.common.logging.Slf4jLogger";
+                    }
+                } catch (Throwable t) {
+                    //ignore - Slf4j not available
+                }
+            }
+            if (!StringUtils.isEmpty(cname)) {
+                try {
+                    loggerClass = Class.forName(cname.trim(), true,
+                                                Thread.currentThread().getContextClassLoader());
+                } catch (Throwable ex) {
+                    loggerClass = Class.forName(cname.trim());
+                }
+                getLogger(LogUtils.class).fine("Using " + loggerClass.getName() + " for logging.");
+            }
+        } catch (Throwable ex) {
+            //ignore - if we get here, some issue prevented the logger class from being loaded.
+            //maybe a ClassNotFound or NoClassDefFound or similar.   Just use j.u.l
+            loggerClass = null;
+        }
+    }
+
+
+    /**
+     * Specify a logger class that inherits from {@link AbstractDelegatingLogger}.
+     * Enable users to use their own logger implementation.
+     */
+    public static void setLoggerClass(Class<? extends AbstractDelegatingLogger> cls) {
+        loggerClass = cls;
+    }
+
+
+    /**
+     * Get a Logger with the associated default resource bundle for the class.
+     *
+     * @param cls the Class to contain the Logger
+     * @return an appropriate Logger
+     */
+    public static Logger getLogger(Class<?> cls) {
+        return createLogger(cls, null, cls.getName());
+    }
+
+    /**
+     * Get a Logger with an associated resource bundle.
+     *
+     * @param cls the Class to contain the Logger
+     * @param resourcename the resource name
+     * @return an appropriate Logger
+     */
+    public static Logger getLogger(Class<?> cls, String resourcename) {
+        return createLogger(cls, resourcename, cls.getName());
+    }
+
+    /**
+     * Get a Logger with an associated resource bundle.
+     *
+     * @param cls the Class to contain the Logger (to find resources)
+     * @param resourcename the resource name
+     * @param loggerName the full name for the logger
+     * @return an appropriate Logger
+     */
+    public static Logger getLogger(Class<?> cls,
+                                     String resourcename,
+                                     String loggerName) {
+        return createLogger(cls, resourcename, loggerName);
+    }
+
+    /**
+     * Get a Logger with the associated default resource bundle for the class.
+     *
+     * @param cls the Class to contain the Logger
+     * @return an appropriate Logger
+     */
+    public static Logger getL7dLogger(Class<?> cls) {
+        return createLogger(cls, null, cls.getName());
+    }
+
+    /**
+     * Get a Logger with an associated resource bundle.
+     *
+     * @param cls the Class to contain the Logger
+     * @param resourcename the resource name
+     * @return an appropriate Logger
+     */
+    public static Logger getL7dLogger(Class<?> cls, String resourcename) {
+        return createLogger(cls, resourcename, cls.getName());
+    }
+
+    /**
+     * Get a Logger with an associated resource bundle.
+     *
+     * @param cls the Class to contain the Logger (to find resources)
+     * @param resourcename the resource name
+     * @param loggerName the full name for the logger
+     * @return an appropriate Logger
+     */
+    public static Logger getL7dLogger(Class<?> cls,
+                                      String resourcename,
+                                      String loggerName) {
+        return createLogger(cls, resourcename, loggerName);
+    }
+
+    /**
+     * Create a logger
+     */
+    protected static Logger createLogger(Class<?> cls,
+                                         String name,
+                                         String loggerName) {
+        ClassLoader orig = getContextClassLoader();
+        ClassLoader n = getClassLoader(cls);
+        if (n != null) {
+            setContextClassLoader(n);
+        }
+        String bundleName = name;
+        try {
+            ResourceBundle b = null;
+            if (bundleName == null) {
+                //grab the bundle prior to the call to Logger.getLogger(...) so the
+                //ResourceBundle can be loaded outside the big sync block that getLogger really is
+                bundleName = BundleUtils.getBundleName(cls);
+                try {
+                    b = BundleUtils.getBundle(cls);
+                } catch (MissingResourceException rex) {
+                    //ignore
+                }
+            } else {
+                bundleName = BundleUtils.getBundleName(cls, bundleName);
+                try {
+                    b = BundleUtils.getBundle(cls, bundleName);
+                } catch (MissingResourceException rex) {
+                    //ignore
+                }
+            }
+            if (b != null) {
+                b.getLocale();
+            }
+
+            if (loggerClass != null) {
+                try {
+                    Constructor<?> cns = loggerClass.getConstructor(String.class, String.class);
+                    if (name == null) {
+                        try {
+                            return (Logger) cns.newInstance(loggerName, bundleName);
+                        } catch (InvocationTargetException ite) {
+                            if (ite.getTargetException() instanceof MissingResourceException) {
+                                return (Logger) cns.newInstance(loggerName, null);
+                            }
+                            throw ite;
+                        }
+                    }
+                    try {
+                        return (Logger) cns.newInstance(loggerName, bundleName);
+                    } catch (InvocationTargetException ite) {
+                        if (ite.getTargetException() instanceof MissingResourceException) {
+                            throw (MissingResourceException)ite.getTargetException();
+                        }
+                        throw ite;
+                    }
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+
+            Logger logger;
+            try {
+                logger = Logger.getLogger(loggerName, bundleName); //NOPMD
+            } catch (IllegalArgumentException | MissingResourceException ex) {
+                //likely a mismatch on the bundle name, just return the default
+                logger = Logger.getLogger(loggerName); //NOPMD
+            }
+            
+            return logger;
+        } finally {
+            if (n != orig) {
+                setContextClassLoader(orig);
+            }
+        }
+    }
+
+    private static void setContextClassLoader(final ClassLoader classLoader) {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            AccessController.doPrivileged(new PrivilegedAction<Object>() {
+                public Object run() {
+                    Thread.currentThread().setContextClassLoader(classLoader);
+                    return null;
+                }
+            });
+        } else {
+            Thread.currentThread().setContextClassLoader(classLoader);
+        }
+    }
+
+    private static ClassLoader getContextClassLoader() {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+                public ClassLoader run() {
+                    return Thread.currentThread().getContextClassLoader();
+                }
+            });
+        }
+        return Thread.currentThread().getContextClassLoader();
+    }
+
+    private static ClassLoader getClassLoader(final Class<?> clazz) {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+                public ClassLoader run() {
+                    return clazz.getClassLoader();
+                }
+            });
+        }
+        return clazz.getClassLoader();
+    }
+
+    /**
+     * Allows both parameter substitution and a typed Throwable to be logged.
+     *
+     * @param logger the Logger the log to
+     * @param level the severity level
+     * @param message the log message
+     * @param throwable the Throwable to log
+     * @param parameter the parameter to substitute into message
+     */
+    public static void log(Logger logger,
+                           Level level,
+                           String message,
+                           Throwable throwable,
+                           Object parameter) {
+        if (logger.isLoggable(level)) {
+            final String formattedMessage =
+                MessageFormat.format(localize(logger, message), parameter);
+            doLog(logger, level, formattedMessage, throwable);
+        }
+    }
+
+    /**
+     * Allows both parameter substitution and a typed Throwable to be logged.
+     *
+     * @param logger the Logger the log to
+     * @param level the severity level
+     * @param message the log message
+     * @param throwable the Throwable to log
+     * @param parameters the parameters to substitute into message
+     */
+    public static void log(Logger logger,
+                           Level level,
+                           String message,
+                           Throwable throwable,
+                           Object... parameters) {
+        if (logger.isLoggable(level)) {
+            final String formattedMessage =
+                MessageFormat.format(localize(logger, message), parameters);
+            doLog(logger, level, formattedMessage, throwable);
+        }
+    }
+
+    /**
+     * Checks log level and logs
+     *
+     * @param logger the Logger the log to
+     * @param level the severity level
+     * @param message the log message
+     */
+    public static void log(Logger logger,
+                           Level level,
+                           String message) {
+        log(logger, level, message, NO_PARAMETERS);
+    }
+
+    /**
+     * Checks log level and logs
+     *
+     * @param logger the Logger the log to
+     * @param level the severity level
+     * @param message the log message
+     * @param throwable the Throwable to log
+     */
+    public static void log(Logger logger,
+                           Level level,
+                           String message,
+                           Throwable throwable) {
+        log(logger, level, message, throwable, NO_PARAMETERS);
+    }
+
+    /**
+     * Checks log level and logs
+     *
+     * @param logger the Logger the log to
+     * @param level the severity level
+     * @param message the log message
+     * @param parameter the parameter to substitute into message
+     */
+    public static void log(Logger logger,
+                           Level level,
+                           String message,
+                           Object parameter) {
+        log(logger, level, message, new Object[] {parameter});
+    }
+
+    /**
+     * Checks log level and logs
+     *
+     * @param logger the Logger the log to
+     * @param level the severity level
+     * @param message the log message
+     * @param parameters the parameters to substitute into message
+     */
+    public static void log(Logger logger,
+                           Level level,
+                           String message,
+                           Object[] parameters) {
+        if (logger.isLoggable(level)) {
+            String msg = localize(logger, message);
+            try {
+                msg = MessageFormat.format(msg, parameters);
+            } catch (IllegalArgumentException ex) {
+                //ignore, log as is
+            }
+            doLog(logger, level, msg, null);
+        }
+    }
+
+    private static void doLog(Logger log, Level level, String msg, Throwable t) {
+        LogRecord record = new LogRecord(level, msg);
+
+        record.setLoggerName(log.getName());
+        record.setResourceBundleName(log.getResourceBundleName());
+        record.setResourceBundle(log.getResourceBundle());
+
+        if (t != null) {
+            record.setThrown(t);
+        }
+
+        //try to get the right class name/method name - just trace
+        //back the stack till we get out of this class
+        StackTraceElement[] stack = (new Throwable()).getStackTrace();
+        String cname = LogUtils.class.getName();
+        for (int x = 0; x < stack.length; x++) {
+            StackTraceElement frame = stack[x];
+            if (!frame.getClassName().equals(cname)) {
+                record.setSourceClassName(frame.getClassName());
+                record.setSourceMethodName(frame.getMethodName());
+                break;
+            }
+        }
+        log.log(record);
+    }
+
+    /**
+     * Retrieve localized message retrieved from a logger's resource
+     * bundle.
+     *
+     * @param logger the Logger
+     * @param message the message to be localized
+     */
+    private static String localize(Logger logger, String message) {
+        ResourceBundle bundle = logger.getResourceBundle();
+        try {
+            return bundle != null ? bundle.getString(message) : message;
+        } catch (MissingResourceException ex) {
+            //string not in the bundle
+            return message;
+        }
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/logging/RegexLoggingFilter.java b/transform/src/patch/java/org/apache/cxf/common/logging/RegexLoggingFilter.java
new file mode 100644
index 0000000..83178b9
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/logging/RegexLoggingFilter.java
@@ -0,0 +1,117 @@
+/**
+ * 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.common.logging;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class RegexLoggingFilter {
+
+    public static final String DEFAULT_REPLACEMENT = "*****";
+
+    private static class ReplaceRegEx {
+        private final Pattern pattern;
+        private final int group;
+        private final String replacement;
+
+        ReplaceRegEx(String pattern, int group, String replacement) {
+            this.pattern = Pattern.compile(pattern);
+            this.group = group;
+            this.replacement = replacement;
+        }
+
+        public CharSequence filter(CharSequence command) {
+            Matcher m = pattern.matcher(command);
+            int offset = 0;
+            while (m.find()) {
+                int origLen = command.length();
+                command = new StringBuilder(command)
+                    .replace(m.start(group) + offset, m.end(group) + offset, replacement).toString();
+                offset += command.length() - origLen;
+            }
+            return command;
+        }
+    }
+
+    private String regPattern;
+    private int regGroup = 1;
+    private String regReplacement = DEFAULT_REPLACEMENT;
+
+    private List<ReplaceRegEx> regexs = new ArrayList<>();
+
+    public CharSequence filter(CharSequence command) {
+        if (regPattern != null) {
+            command = new ReplaceRegEx(regPattern, regGroup, regReplacement).filter(command);
+        }
+        for (ReplaceRegEx regex : regexs) {
+            command = regex.filter(command);
+        }
+        return command;
+    }
+
+    public void addRegEx(String pattern) {
+        addRegEx(pattern, 1);
+    }
+
+    public void addRegEx(String pattern, int group) {
+        addRegEx(pattern, group, DEFAULT_REPLACEMENT);
+    }
+
+    public void addRegEx(String pattern, int group, String replacement) {
+        regexs.add(new ReplaceRegEx(pattern, group, replacement));
+    }
+
+    public void addCommandOption(String option, String... commands) {
+        StringBuilder pattern = new StringBuilder("(");
+        for (String command : commands) {
+            if (pattern.length() > 1) {
+                pattern.append('|');
+            }
+            pattern.append(Pattern.quote(command));
+        }
+        pattern.append(") +.*?").append(Pattern.quote(option)).append(" +([^ ]+)");
+        regexs.add(new ReplaceRegEx(pattern.toString(), 2, DEFAULT_REPLACEMENT));
+    }
+
+    public String getPattern() {
+        return regPattern;
+    }
+
+    public void setPattern(String pattern) {
+        this.regPattern = pattern;
+    }
+
+    public String getReplacement() {
+        return regReplacement;
+    }
+
+    public void setReplacement(String replacement) {
+        this.regReplacement = replacement;
+    }
+
+    public int getGroup() {
+        return regGroup;
+    }
+
+    public void setGroup(int group) {
+        this.regGroup = group;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/spi/ClassGeneratorClassLoader.java b/transform/src/patch/java/org/apache/cxf/common/spi/ClassGeneratorClassLoader.java
new file mode 100644
index 0000000..5906c7a
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/spi/ClassGeneratorClassLoader.java
@@ -0,0 +1,153 @@
+/**
+ * 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.common.spi;
+
+import java.lang.ref.WeakReference;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.common.util.WeakIdentityHashMap;
+
+/** Class loader used to store and retrieve class generated during runtime to avoid class generation each time.
+ *  inherited class use asmHelper to generate bytes and use @see #loadClass(String, Class&lt;?&gt;, byte[])
+ *  or @see #loadClass(String, ClassLoader, byte[]) to store generated class.Class can be generated during buildtime.
+ *  equivalent class is @see org.apache.cxf.common.spi.GeneratedClassClassLoader
+ * @author olivier dufour
+ */
+public class ClassGeneratorClassLoader {
+    protected static final Map<Class<?>, WeakReference<TypeHelperClassLoader>> CLASS_MAP
+            = new WeakIdentityHashMap<>();
+    protected static final Map<ClassLoader, WeakReference<TypeHelperClassLoader>> LOADER_MAP
+            = new WeakIdentityHashMap<>();
+    protected final Bus bus;
+
+    public ClassGeneratorClassLoader(final Bus bus) {
+        this.bus = bus == null ? BusFactory.getDefaultBus() : bus;
+    }
+
+    protected Class<?> loadClass(String className, Class<?> cls, byte[] bytes) {
+        GeneratedClassClassLoaderCapture capture = bus.getExtension(GeneratedClassClassLoaderCapture.class);
+        if (capture != null) {
+            capture.capture(className, bytes);
+        }
+        TypeHelperClassLoader loader = getOrCreateLoader(cls);
+        synchronized (loader) {
+            Class<?> clz = loader.lookupDefinedClass(className);
+            if (clz == null) {
+                return loader.defineClass(className, bytes);
+            }
+            return clz;
+        }
+    }
+    protected Class<?> loadClass(String className, ClassLoader l, byte[] bytes) {
+        GeneratedClassClassLoaderCapture capture = bus.getExtension(GeneratedClassClassLoaderCapture.class);
+        if (capture != null) {
+            capture.capture(className, bytes);
+        }
+        TypeHelperClassLoader loader = getOrCreateLoader(l);
+        synchronized (loader) {
+            Class<?> clz = loader.lookupDefinedClass(className);
+            if (clz == null) {
+                return loader.defineClass(className, bytes);
+            }
+            return clz;
+        }
+    }
+    protected Class<?> findClass(String className, Class<?> cls) {
+        return getOrCreateLoader(cls).lookupDefinedClass(className);
+    }
+
+    protected Class<?> findClass(String className, ClassLoader classLoader) {
+        return getOrCreateLoader(classLoader).lookupDefinedClass(className);
+    }
+    
+    private static synchronized TypeHelperClassLoader getOrCreateLoader(Class<?> cls) {
+        WeakReference<TypeHelperClassLoader> ref = CLASS_MAP.get(cls);
+        TypeHelperClassLoader ret;
+        if (ref == null || ref.get() == null) {
+            ret = new TypeHelperClassLoader(cls.getClassLoader());
+            CLASS_MAP.put(cls, new WeakReference<>(ret));
+        } else {
+            ret = ref.get();
+        }
+        return ret;
+    }
+    
+    private static synchronized TypeHelperClassLoader getOrCreateLoader(ClassLoader l) {
+        WeakReference<TypeHelperClassLoader> ref = LOADER_MAP.get(l);
+        TypeHelperClassLoader ret;
+        if (ref == null || ref.get() == null) {
+            ret = new TypeHelperClassLoader(l);
+            LOADER_MAP.put(l, new WeakReference<>(ret));
+        } else {
+            ret = ref.get();
+        }
+        return ret;
+    }
+
+    public static class TypeHelperClassLoader extends ClassLoader {
+        private final ConcurrentHashMap<String, Class<?>> defined = new ConcurrentHashMap<>();
+
+        TypeHelperClassLoader(ClassLoader parent) {
+            super(parent);
+        }
+        
+        public Class<?> lookupDefinedClass(String name) {
+            return defined.get(StringUtils.slashesToPeriod(name));
+        }
+
+        @Override
+        protected Class<?> findClass(String name) throws ClassNotFoundException {
+            if (name.endsWith("package-info")) {
+                return getParent().loadClass(name);
+            }
+            return super.findClass(name);
+        }
+
+        public Class<?> defineClass(String name, byte[] bytes) {
+            Class<?> ret = defined.get(StringUtils.slashesToPeriod(name));
+            if (ret != null) {
+                return ret;
+            }
+            if (name.endsWith("package-info")) {
+                String s = name.substring(0, name.length() - 13);
+                Package p = super.getPackage(s);
+                if (p == null) {
+                    definePackage(StringUtils.slashesToPeriod(s),
+                            null,
+                            null,
+                            null,
+                            null,
+                            null,
+                            null,
+                            null);
+                }
+            }
+
+            ret = defined.computeIfAbsent(StringUtils.slashesToPeriod(name),
+                key -> TypeHelperClassLoader.super.defineClass(key, bytes, 0, bytes.length));
+
+            return ret;
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/spi/NamespaceClassGenerator.java b/transform/src/patch/java/org/apache/cxf/common/spi/NamespaceClassGenerator.java
new file mode 100644
index 0000000..48c755b
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/spi/NamespaceClassGenerator.java
@@ -0,0 +1,450 @@
+/**
+ * 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.common.spi;
+
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.ASMHelper;
+import org.apache.cxf.common.util.OpcodesProxy;
+
+public class NamespaceClassGenerator extends ClassGeneratorClassLoader implements NamespaceClassCreator {
+
+    private static final Logger LOG = LogUtils.getL7dLogger(ClassGeneratorClassLoader.class);
+    private final ASMHelper helper;
+
+    public NamespaceClassGenerator(Bus bus) {
+        super(bus);
+        helper = bus.getExtension(ASMHelper.class);
+    }
+
+    @Override
+    public synchronized Class<?> createNamespaceWrapperClass(Class<?> mcls, Map<String, String> map) {
+        String postFix = "";
+
+        if (mcls.getName().contains("eclipse")) {
+            return createEclipseNamespaceMapper();
+        } else if (mcls.getName().contains(".internal")) {
+            postFix = "Internal";
+        } else if (mcls.getName().contains("com.sun")) {
+            postFix = "RI";
+        }
+
+        String className = "org.apache.cxf.jaxb.NamespaceMapper";
+        className += postFix;
+        Class<?> cls = findClass(className, NamespaceClassCreator.class);
+        Throwable t = null;
+        if (cls == null) {
+            try {
+                byte[] bts = createNamespaceWrapperInternal(postFix);
+                className = "org.apache.cxf.jaxb.NamespaceMapper" + postFix;
+                return loadClass(className, NamespaceClassCreator.class, bts);
+            } catch (RuntimeException ex) {
+                // continue
+                t = ex;
+            }
+        }
+        if (cls == null
+                && (!mcls.getName().contains(".internal.") && mcls.getName().contains("com.sun"))) {
+            try {
+                cls = ClassLoaderUtils.loadClass("org.apache.cxf.common.jaxb.NamespaceMapper",
+                        NamespaceClassCreator.class);
+            } catch (Throwable ex2) {
+                // ignore
+                t = ex2;
+            }
+        }
+        LOG.log(Level.INFO, "Could not create a NamespaceMapper compatible with Marshaller class " + mcls.getName(), t);
+        return cls;
+    }
+
+    private Class<?> createEclipseNamespaceMapper() {
+        String className = "org.apache.cxf.jaxb.EclipseNamespaceMapper";
+        Class<?> cls = findClass(className, NamespaceClassCreator.class);
+        if (cls != null) {
+            return cls;
+        }
+        byte[] bts = doCreateEclipseNamespaceMapper();
+        //previous code use mcls instead of NamespaceClassGenerator.class
+        return loadClass(className, NamespaceClassCreator.class, bts);
+    }
+
+    /*
+    // This is the "prototype" for the ASM generated class below
+    public static class MapNamespacePrefixMapper2
+        extends org.eclipse.persistence.internal.oxm.record.namespaces.MapNamespacePrefixMapper {
+
+        String[] nsctxt;
+
+        public MapNamespacePrefixMapper2(Map<String, String> foo) {
+            super(foo);
+        }
+        public String[] getPreDeclaredNamespaceUris() {
+            String[] sup = super.getPreDeclaredNamespaceUris();
+            if (nsctxt == null) {
+                return sup;
+            }
+            List<String> s = new ArrayList<>(Arrays.asList(sup));
+            for (int x = 1; x < nsctxt.length; x = x + 2) {
+                s.remove(nsctxt[x]);
+            }
+            return s.toArray(new String[s.size()]);
+        }
+        public void setContextualNamespaceDecls(String[] f) {
+            nsctxt = f;
+        }
+        public String[] getContextualNamespaceDecls() {
+            return nsctxt;
+        }
+    }
+    */
+    //CHECKSTYLE:OFF
+    //bunch of really long ASM based methods that cannot be shortened easily
+    private byte[] doCreateEclipseNamespaceMapper() {
+        OpcodesProxy Opcodes = helper.getOpCodes();
+        String slashedName = "org/apache/cxf/jaxb/EclipseNamespaceMapper";
+        ASMHelper.ClassWriter cw = helper.createClassWriter();
+        if (cw == null) {
+            return null;
+        }
+        String superName = "org/eclipse/persistence/internal/oxm/record/namespaces/MapNamespacePrefixMapper";
+        ASMHelper.FieldVisitor fv;
+        ASMHelper.MethodVisitor mv;
+        cw.visit(Opcodes.V1_6,
+                Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER,
+                slashedName, null,
+                superName, null);
+
+        cw.visitSource("EclipseNamespaceMapper.java", null);
+
+        fv = cw.visitField(Opcodes.ACC_PRIVATE, "nsctxt", "[Ljava/lang/String;", null, null);
+        fv.visitEnd();
+
+
+        mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "(Ljava/util/Map;)V",
+                "(Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;)V", null);
+        mv.visitCode();
+        ASMHelper.Label l0 = helper.createLabel();
+        mv.visitLabel(l0);
+        mv.visitVarInsn(Opcodes.ALOAD, 0);
+        mv.visitVarInsn(Opcodes.ALOAD, 1);
+        mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
+                superName, "<init>", "(Ljava/util/Map;)V", false);
+        ASMHelper.Label l1 = helper.createLabel();
+        mv.visitLabel(l1);
+        mv.visitInsn(Opcodes.RETURN);
+        ASMHelper.Label l2 = helper.createLabel();
+        mv.visitLabel(l2);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+
+        mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "setContextualNamespaceDecls", "([Ljava/lang/String;)V",
+                null, null);
+        mv.visitCode();
+        l0 = helper.createLabel();
+        mv.visitLabel(l0);
+        mv.visitLineNumber(47, l0);
+        mv.visitVarInsn(Opcodes.ALOAD, 0);
+        mv.visitVarInsn(Opcodes.ALOAD, 1);
+        mv.visitFieldInsn(Opcodes.PUTFIELD, slashedName, "nsctxt", "[Ljava/lang/String;");
+        l1 = helper.createLabel();
+        mv.visitLabel(l1);
+        mv.visitLineNumber(48, l1);
+        mv.visitInsn(Opcodes.RETURN);
+        l2 = helper.createLabel();
+        mv.visitLabel(l2);
+        mv.visitLocalVariable("this", "L" + slashedName + ";", null, l0, l2, 0);
+        mv.visitLocalVariable("contextualNamespaceDecls", "[Ljava/lang/String;", null, l0, l2, 1);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+        mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "getContextualNamespaceDecls", "()[Ljava/lang/String;", null, null);
+        mv.visitCode();
+        l0 = helper.createLabel();
+        mv.visitLabel(l0);
+        mv.visitLineNumber(51, l0);
+        mv.visitVarInsn(Opcodes.ALOAD, 0);
+        mv.visitFieldInsn(Opcodes.GETFIELD, slashedName, "nsctxt", "[Ljava/lang/String;");
+        mv.visitInsn(Opcodes.ARETURN);
+        l1 = helper.createLabel();
+
+        mv.visitLabel(l1);
+        mv.visitLocalVariable("this", "L" + slashedName + ";", null, l0, l1, 0);
+
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+
+        mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "getPreDeclaredNamespaceUris", "()[Ljava/lang/String;", null, null);
+        mv.visitCode();
+        l0 = helper.createLabel();
+        mv.visitLabel(l0);
+        mv.visitLineNumber(1036, l0);
+        mv.visitVarInsn(Opcodes.ALOAD, 0);
+        mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
+                superName,
+                "getPreDeclaredNamespaceUris", "()[Ljava/lang/String;", false);
+        mv.visitVarInsn(Opcodes.ASTORE, 1);
+        l1 = helper.createLabel();
+        mv.visitLabel(l1);
+        mv.visitLineNumber(1037, l1);
+        mv.visitVarInsn(Opcodes.ALOAD, 0);
+        mv.visitFieldInsn(Opcodes.GETFIELD, slashedName, "nsctxt", "[Ljava/lang/String;");
+        l2 = helper.createLabel();
+        mv.visitJumpInsn(Opcodes.IFNONNULL, l2);
+        ASMHelper.Label l3 = helper.createLabel();
+        mv.visitLabel(l3);
+        mv.visitLineNumber(1038, l3);
+        mv.visitVarInsn(Opcodes.ALOAD, 1);
+        mv.visitInsn(Opcodes.ARETURN);
+        mv.visitLabel(l2);
+        mv.visitLineNumber(1040, l2);
+        mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] {"[Ljava/lang/String;"}, 0, null);
+        mv.visitTypeInsn(Opcodes.NEW, "java/util/ArrayList");
+        mv.visitInsn(Opcodes.DUP);
+        mv.visitVarInsn(Opcodes.ALOAD, 1);
+        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "asList",
+                "([Ljava/lang/Object;)Ljava/util/List;", false);
+        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/util/ArrayList", "<init>",
+                "(Ljava/util/Collection;)V", false);
+        mv.visitVarInsn(Opcodes.ASTORE, 2);
+        ASMHelper.Label l4 = helper.createLabel();
+        mv.visitLabel(l4);
+        mv.visitLineNumber(1041, l4);
+        mv.visitInsn(Opcodes.ICONST_1);
+        mv.visitVarInsn(Opcodes.ISTORE, 3);
+        ASMHelper.Label l5 = helper.createLabel();
+        mv.visitLabel(l5);
+        ASMHelper.Label l6 = helper.createLabel();
+        mv.visitJumpInsn(Opcodes.GOTO, l6);
+        ASMHelper.Label l7 = helper.createLabel();
+        mv.visitLabel(l7);
+        mv.visitLineNumber(1042, l7);
+        mv.visitFrame(Opcodes.F_APPEND, 2, new Object[] {"java/util/List", Opcodes.INTEGER}, 0, null);
+        mv.visitVarInsn(Opcodes.ALOAD, 2);
+        mv.visitVarInsn(Opcodes.ALOAD, 0);
+        mv.visitFieldInsn(Opcodes.GETFIELD, slashedName, "nsctxt", "[Ljava/lang/String;");
+        mv.visitVarInsn(Opcodes.ILOAD, 3);
+        mv.visitInsn(Opcodes.AALOAD);
+        mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/List", "remove", "(Ljava/lang/Object;)Z", true);
+        mv.visitInsn(Opcodes.POP);
+        ASMHelper.Label l8 = helper.createLabel();
+        mv.visitLabel(l8);
+        mv.visitLineNumber(1041, l8);
+        mv.visitIincInsn(3, 2);
+        mv.visitLabel(l6);
+        mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+        mv.visitVarInsn(Opcodes.ILOAD, 3);
+        mv.visitVarInsn(Opcodes.ALOAD, 0);
+        mv.visitFieldInsn(Opcodes.GETFIELD,
+                slashedName,
+                "nsctxt", "[Ljava/lang/String;");
+        mv.visitInsn(Opcodes.ARRAYLENGTH);
+        mv.visitJumpInsn(Opcodes.IF_ICMPLT, l7);
+        ASMHelper.Label l9 = helper.createLabel();
+        mv.visitLabel(l9);
+        mv.visitLineNumber(1044, l9);
+        mv.visitVarInsn(Opcodes.ALOAD, 2);
+        mv.visitVarInsn(Opcodes.ALOAD, 2);
+        mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/List", "size", "()I", true);
+        mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
+        mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/List",
+                "toArray", "([Ljava/lang/Object;)[Ljava/lang/Object;", true);
+        mv.visitTypeInsn(Opcodes.CHECKCAST, "[Ljava/lang/String;");
+        mv.visitInsn(Opcodes.ARETURN);
+        ASMHelper.Label l10 = helper.createLabel();
+        mv.visitLabel(l10);
+        mv.visitLocalVariable("this", "L" + slashedName + ";",
+                null, l0, l10, 0);
+        mv.visitLocalVariable("sup", "[Ljava/lang/String;", null, l1, l10, 1);
+        mv.visitLocalVariable("s", "Ljava/util/List;", "Ljava/util/List<Ljava/lang/String;>;", l4, l10, 2);
+        mv.visitLocalVariable("x", "I", null, l5, l9, 3);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+        cw.visitEnd();
+
+        return cw.toByteArray();
+    }
+
+    private byte[] createNamespaceWrapperInternal(String postFix) {
+
+        String superName = "com/sun/xml/"
+                + ("RI".equals(postFix) ? "" : "internal/")
+                + "bind/marshaller/NamespacePrefixMapper";
+        String postFixedName = "org/apache/cxf/jaxb/NamespaceMapper" + postFix;
+        ASMHelper.ClassWriter cw = helper.createClassWriter();
+        if (cw == null) {
+            return null;
+        }
+        ASMHelper.FieldVisitor fv;
+        ASMHelper.MethodVisitor mv;
+        OpcodesProxy opcodes= helper.getOpCodes();
+        cw.visit(opcodes.V1_6,
+                opcodes.ACC_PUBLIC + opcodes.ACC_FINAL + opcodes.ACC_SUPER,
+                postFixedName, null,
+                superName, null);
+
+        cw.visitSource("NamespaceMapper.java", null);
+
+        fv = cw.visitField(opcodes.ACC_PRIVATE + opcodes.ACC_FINAL,
+                "nspref", "Ljava/util/Map;",
+                "Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;", null);
+        fv.visitEnd();
+
+        fv = cw.visitField(opcodes.ACC_PRIVATE, "nsctxt", "[Ljava/lang/String;", null, null);
+        fv.visitEnd();
+
+        fv = cw.visitField(opcodes.ACC_PRIVATE + opcodes.ACC_FINAL + opcodes.ACC_STATIC,
+                "EMPTY_STRING", "[Ljava/lang/String;", null, null);
+        fv.visitEnd();
+
+        mv = cw.visitMethod(opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
+        mv.visitCode();
+        ASMHelper.Label l0 = helper.createLabel();
+        mv.visitLabel(l0);
+        mv.visitLineNumber(30, l0);
+        mv.visitInsn(opcodes.ICONST_0);
+        mv.visitTypeInsn(opcodes.ANEWARRAY, "java/lang/String");
+        mv.visitFieldInsn(opcodes.PUTSTATIC, postFixedName, "EMPTY_STRING", "[Ljava/lang/String;");
+        mv.visitInsn(opcodes.RETURN);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+        mv = cw.visitMethod(opcodes.ACC_PUBLIC, "<init>",
+                "(Ljava/util/Map;)V",
+                "(Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;)V", null);
+        mv.visitCode();
+        l0 = helper.createLabel();
+        mv.visitLabel(l0);
+        mv.visitLineNumber(32, l0);
+        mv.visitVarInsn(opcodes.ALOAD, 0);
+        mv.visitMethodInsn(opcodes.INVOKESPECIAL, superName, "<init>", "()V", false);
+        ASMHelper.Label l1 = helper.createLabel();
+        mv.visitLabel(l1);
+        mv.visitLineNumber(29, l1);
+        mv.visitVarInsn(opcodes.ALOAD, 0);
+        mv.visitFieldInsn(opcodes.GETSTATIC, postFixedName, "EMPTY_STRING", "[Ljava/lang/String;");
+        mv.visitFieldInsn(opcodes.PUTFIELD, postFixedName, "nsctxt", "[Ljava/lang/String;");
+        ASMHelper.Label l2 = helper.createLabel();
+        mv.visitLabel(l2);
+        mv.visitLineNumber(33, l2);
+        mv.visitVarInsn(opcodes.ALOAD, 0);
+        mv.visitVarInsn(opcodes.ALOAD, 1);
+        mv.visitFieldInsn(opcodes.PUTFIELD, postFixedName, "nspref", "Ljava/util/Map;");
+        ASMHelper.Label l3 = helper.createLabel();
+        mv.visitLabel(l3);
+        mv.visitLineNumber(34, l3);
+        mv.visitInsn(opcodes.RETURN);
+        ASMHelper.Label l4 = helper.createLabel();
+        mv.visitLabel(l4);
+        mv.visitLocalVariable("this", "L" + postFixedName + ";", null, l0, l4, 0);
+        mv.visitLocalVariable("nspref",
+                "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;",
+                l0, l4, 1);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+        mv = cw.visitMethod(opcodes.ACC_PUBLIC, "getPreferredPrefix",
+                "(Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;",
+                null, null);
+        mv.visitCode();
+        l0 = helper.createLabel();
+        mv.visitLabel(l0);
+        mv.visitLineNumber(39, l0);
+        mv.visitVarInsn(opcodes.ALOAD, 0);
+        mv.visitFieldInsn(opcodes.GETFIELD, postFixedName, "nspref", "Ljava/util/Map;");
+        mv.visitVarInsn(opcodes.ALOAD, 1);
+        mv.visitMethodInsn(opcodes.INVOKEINTERFACE, "java/util/Map",
+                "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
+        mv.visitTypeInsn(opcodes.CHECKCAST, "java/lang/String");
+        mv.visitVarInsn(opcodes.ASTORE, 4);
+        l1 = helper.createLabel();
+        mv.visitLabel(l1);
+        mv.visitLineNumber(40, l1);
+        mv.visitVarInsn(opcodes.ALOAD, 4);
+        l2 = helper.createLabel();
+        mv.visitJumpInsn(opcodes.IFNULL, l2);
+        l3 = helper.createLabel();
+        mv.visitLabel(l3);
+        mv.visitLineNumber(41, l3);
+        mv.visitVarInsn(opcodes.ALOAD, 4);
+        mv.visitInsn(opcodes.ARETURN);
+        mv.visitLabel(l2);
+        mv.visitLineNumber(43, l2);
+        mv.visitFrame(opcodes.F_APPEND, 1, new Object[] {"java/lang/String"}, 0, null);
+        mv.visitVarInsn(opcodes.ALOAD, 2);
+        mv.visitInsn(opcodes.ARETURN);
+        l4 = helper.createLabel();
+        mv.visitLabel(l4);
+        mv.visitLocalVariable("this", "L" + postFixedName + ";", null, l0, l4, 0);
+        mv.visitLocalVariable("namespaceUri", "Ljava/lang/String;", null, l0, l4, 1);
+        mv.visitLocalVariable("suggestion", "Ljava/lang/String;", null, l0, l4, 2);
+        mv.visitLocalVariable("requirePrefix", "Z", null, l0, l4, 3);
+        mv.visitLocalVariable("prefix", "Ljava/lang/String;", null, l1, l4, 4);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+        mv = cw.visitMethod(opcodes.ACC_PUBLIC, "setContextualNamespaceDecls", "([Ljava/lang/String;)V", null, null);
+        mv.visitCode();
+        l0 = helper.createLabel();
+        mv.visitLabel(l0);
+        mv.visitLineNumber(47, l0);
+        mv.visitVarInsn(opcodes.ALOAD, 0);
+        mv.visitVarInsn(opcodes.ALOAD, 1);
+        mv.visitFieldInsn(opcodes.PUTFIELD, postFixedName, "nsctxt", "[Ljava/lang/String;");
+        l1 = helper.createLabel();
+        mv.visitLabel(l1);
+        mv.visitLineNumber(48, l1);
+        mv.visitInsn(opcodes.RETURN);
+        l2 = helper.createLabel();
+        mv.visitLabel(l2);
+        mv.visitLocalVariable("this", "L" + postFixedName + ";", null, l0, l2, 0);
+        mv.visitLocalVariable("contextualNamespaceDecls", "[Ljava/lang/String;", null, l0, l2, 1);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+        mv = cw.visitMethod(opcodes.ACC_PUBLIC, "getContextualNamespaceDecls", "()[Ljava/lang/String;", null, null);
+        mv.visitCode();
+        l0 = helper.createLabel();
+        mv.visitLabel(l0);
+        mv.visitLineNumber(51, l0);
+        mv.visitVarInsn(opcodes.ALOAD, 0);
+        mv.visitFieldInsn(opcodes.GETFIELD, postFixedName, "nsctxt", "[Ljava/lang/String;");
+        mv.visitInsn(opcodes.ARETURN);
+        l1 = helper.createLabel();
+
+        mv.visitLabel(l1);
+        mv.visitLocalVariable("this", "L" + postFixedName + ";", null, l0, l1, 0);
+
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+        cw.visitEnd();
+
+        return cw.toByteArray();
+    }
+    //CHECKSTYLE:ON
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/util/ASMHelperImpl.java b/transform/src/patch/java/org/apache/cxf/common/util/ASMHelperImpl.java
new file mode 100644
index 0000000..cc01f14
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/util/ASMHelperImpl.java
@@ -0,0 +1,273 @@
+/**
+ * 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.common.util;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+
+
+public class ASMHelperImpl implements ASMHelper {
+    protected static final Map<Class<?>, String> PRIMITIVE_MAP = new HashMap<>();
+    protected static final Map<Class<?>, String> NONPRIMITIVE_MAP = new HashMap<>();
+    protected static final Map<Class<?>, Integer> PRIMITIVE_ZERO_MAP = new HashMap<>();
+
+    protected boolean badASM;
+    private Class<?> cwClass;
+
+    public ASMHelperImpl() {
+
+    }
+
+    static {
+        PRIMITIVE_MAP.put(Byte.TYPE, "B");
+        PRIMITIVE_MAP.put(Boolean.TYPE, "Z");
+        PRIMITIVE_MAP.put(Long.TYPE, "J");
+        PRIMITIVE_MAP.put(Integer.TYPE, "I");
+        PRIMITIVE_MAP.put(Short.TYPE, "S");
+        PRIMITIVE_MAP.put(Character.TYPE, "C");
+        PRIMITIVE_MAP.put(Float.TYPE, "F");
+        PRIMITIVE_MAP.put(Double.TYPE, "D");
+
+        NONPRIMITIVE_MAP.put(Byte.TYPE, Byte.class.getName().replaceAll("\\.", "/"));
+        NONPRIMITIVE_MAP.put(Boolean.TYPE, Boolean.class.getName().replaceAll("\\.", "/"));
+        NONPRIMITIVE_MAP.put(Long.TYPE, Long.class.getName().replaceAll("\\.", "/"));
+        NONPRIMITIVE_MAP.put(Integer.TYPE, Integer.class.getName().replaceAll("\\.", "/"));
+        NONPRIMITIVE_MAP.put(Short.TYPE, Short.class.getName().replaceAll("\\.", "/"));
+        NONPRIMITIVE_MAP.put(Character.TYPE, Character.class.getName().replaceAll("\\.", "/"));
+        NONPRIMITIVE_MAP.put(Float.TYPE, Float.class.getName().replaceAll("\\.", "/"));
+        NONPRIMITIVE_MAP.put(Double.TYPE, Double.class.getName().replaceAll("\\.", "/"));
+    }
+
+    private void tryClass(String s) {
+        if (cwClass == null) {
+            try {
+                Class<?> c2 = ClassLoaderUtils.loadClass(s, ASMHelperImpl.class);
+
+                //old versions don't have this, but we need it
+                Class<?> cls = ClassLoaderUtils.loadClass(c2.getPackage().getName() + ".MethodVisitor", c2);
+                cls.getMethod("visitFrame", Integer.TYPE, Integer.TYPE,
+                        Object[].class,  Integer.TYPE, Object[].class);
+                cwClass = c2;
+            } catch (Throwable t) {
+                //ignore
+            }
+        }
+    }
+    private Class<?> getASMClassWriterClass() {
+        //force this to make sure the proper OSGi import is generated
+        return org.objectweb.asm.ClassWriter.class;
+    }
+
+    public synchronized Class<?> getASMClass() throws ClassNotFoundException {
+        if (cwClass == null) {
+            //try the "real" asm first, then the others
+            tryClass("org.objectweb.asm.ClassWriter");
+            tryClass("org.apache.xbean.asm9.ClassWriter");
+            tryClass("org.apache.xbean.asm8.ClassWriter");
+            tryClass("org.apache.xbean.asm7.ClassWriter");
+            tryClass("org.apache.xbean.asm5.ClassWriter");
+            tryClass("org.apache.xbean.asm6.ClassWriter");
+            tryClass("org.apache.xbean.asm4.ClassWriter");
+            tryClass("org.apache.xbean.asm.ClassWriter");
+            tryClass("org.springframework.asm.ClassWriter");
+            if (cwClass == null) {
+                cwClass = getASMClassWriterClass();
+            }
+        }
+        return cwClass;
+    }
+    public OpcodesProxy getOpCodes() {
+        OpcodesProxy ops = new OpcodesProxy(this);
+        PRIMITIVE_ZERO_MAP.put(Byte.TYPE, ops.ICONST_0);
+        PRIMITIVE_ZERO_MAP.put(Boolean.TYPE, ops.ICONST_0);
+        PRIMITIVE_ZERO_MAP.put(Long.TYPE, ops.LCONST_0);
+        PRIMITIVE_ZERO_MAP.put(Integer.TYPE, ops.ICONST_0);
+        PRIMITIVE_ZERO_MAP.put(Short.TYPE, ops.ICONST_0);
+        PRIMITIVE_ZERO_MAP.put(Character.TYPE, ops.ICONST_0);
+        PRIMITIVE_ZERO_MAP.put(Float.TYPE, ops.FCONST_0);
+        PRIMITIVE_ZERO_MAP.put(Double.TYPE, ops.DCONST_0);
+        return ops;
+    }
+    public void setBadASM(boolean b) {
+        badASM = b;
+    }
+
+    public String getMethodSignature(Method m) {
+        StringBuilder buf = new StringBuilder("(");
+        for (Class<?> cl : m.getParameterTypes()) {
+            buf.append(getClassCode(cl));
+        }
+        buf.append(')');
+        buf.append(getClassCode(m.getReturnType()));
+
+        return buf.toString();
+    }
+
+    @Override
+    public String getNonPrimitive(Class<?> tp) {
+        return NONPRIMITIVE_MAP.get(tp);
+    }
+    @Override
+    public String getPrimitive(Class<?> tp) {
+        return PRIMITIVE_MAP.get(tp);
+    }
+
+
+
+
+    public String getClassCode(Class<?> cl) {
+        if (cl == Void.TYPE) {
+            return "V";
+        }
+        if (cl.isPrimitive()) {
+            return PRIMITIVE_MAP.get(cl);
+        }
+        if (cl.isArray()) {
+            return "[" + getClassCode(cl.getComponentType());
+        }
+        return "L" + StringUtils.periodToSlashes(cl.getName()) + ";";
+    }
+    public String getClassCode(java.lang.reflect.Type type) {
+        if (type instanceof Class) {
+            return getClassCode((Class<?>)type);
+        } else if (type instanceof GenericArrayType) {
+            GenericArrayType at = (GenericArrayType)type;
+            return "[" + getClassCode(at.getGenericComponentType());
+        } else if (type instanceof TypeVariable) {
+            TypeVariable<?> tv = (TypeVariable<?>)type;
+            java.lang.reflect.Type[] bounds = tv.getBounds();
+            if (bounds != null && bounds.length == 1) {
+                return getClassCode(bounds[0]);
+            }
+            throw new IllegalArgumentException("Unable to determine type for: " + tv);
+        } else if (type instanceof ParameterizedType) {
+            ParameterizedType pt = (ParameterizedType)type;
+            StringBuilder a = new StringBuilder(getClassCode(pt.getRawType()));
+            if (!pt.getRawType().equals(Enum.class)) {
+                a.setLength(a.length() - 1);
+                a.append('<');
+
+                for (java.lang.reflect.Type t : pt.getActualTypeArguments()) {
+                    a.append(getClassCode(t));
+                }
+                a.append(">;");
+            }
+            return a.toString();
+        } else if (type instanceof WildcardType) {
+            WildcardType wt = (WildcardType)type;
+            StringBuilder a = new StringBuilder();
+            java.lang.reflect.Type[] lowBounds = wt.getLowerBounds();
+            java.lang.reflect.Type[] upBounds = wt.getUpperBounds();
+            for (java.lang.reflect.Type t : upBounds) {
+                a.append('+');
+                a.append(getClassCode(t));
+            }
+            for (java.lang.reflect.Type t : lowBounds) {
+                a.append('-');
+                a.append(getClassCode(t));
+            }
+            return a.toString();
+        }
+        return null;
+    }
+
+    public ClassWriter createClassWriter() {
+        Object newCw = null;
+        if (!badASM) {
+            if (cwClass == null) {
+                try {
+                    cwClass = getASMClass();
+                } catch (Throwable error) {
+                    badASM = true;
+                    throw new RuntimeException("No ASM ClassWriterFound", error);
+                }
+            }
+            // ASM >= 3.x (since cxf is java 8 min we don't care of asm 1/2)
+            try {
+                Constructor<?> cons = cwClass.getConstructor(Integer.TYPE);
+                int i = cwClass.getField("COMPUTE_MAXS").getInt(null);
+                i |= cwClass.getField("COMPUTE_FRAMES").getInt(null);
+                newCw = cons.newInstance(Integer.valueOf(i));
+            } catch (Throwable e1) {
+                // ignore
+            }
+        }
+        if (newCw != null) {
+            return ReflectionInvokationHandler.createProxyWrapper(newCw, ClassWriter.class);
+        }
+        return null;
+    }
+
+
+    public ASMType getType(final String type) {
+        try {
+            final Class<?> cls = ClassLoaderUtils.loadClass(cwClass.getPackage().getName() + ".Type", cwClass);
+            final Method m = cls.getMethod("getType", String.class);
+            final Method m2 = cls.getMethod("getOpcode", Integer.TYPE);
+            @SuppressWarnings("unused")
+            ASMType t = new ASMType() {
+                Object tp = ReflectionUtil.setAccessible(m).invoke(null, type);
+                public Object getValue() {
+                    return tp;
+                }
+                public Class<?> realType() {
+                    return cls;
+                }
+                public int getOpcode(int ireturn) {
+                    try {
+                        return (Integer)ReflectionUtil.setAccessible(m2).invoke(tp, ireturn);
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            };
+            return t;
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+    public Label createLabel() {
+        try {
+            final Class<?> cls = ClassLoaderUtils.loadClass(cwClass.getPackage().getName() + ".Label",
+                    cwClass);
+            @SuppressWarnings("unused")
+            Label l = new Label() {
+                Object l = cls.newInstance();
+                public Object getValue() {
+                    return l;
+                }
+                public Class<?> realType() {
+                    return cls;
+                }
+            };
+            return l;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/util/Base64Utility.java b/transform/src/patch/java/org/apache/cxf/common/util/Base64Utility.java
new file mode 100644
index 0000000..043eba7
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/util/Base64Utility.java
@@ -0,0 +1,474 @@
+/**
+ * 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.common.util;
+
+/**
+ * Base64Utility - this static class provides useful base64
+ *                 encoding utilities.
+ */
+
+// Java imports
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.CharBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.logging.Logger;
+
+import org.apache.cxf.common.i18n.Message;
+import org.apache.cxf.common.logging.LogUtils;
+
+
+/**
+ * This class converts to/from base64. The alternative conversions include:
+ *
+ * encode:
+ *    byte[]     into String
+ *    byte[]     into char[]
+ *    byte[]     into OutStream
+ *    byte[]     into Writer
+ * decode:
+ *    char[]     into byte[]
+ *    String     into byte[]
+ *    char[]     into OutStream
+ *    String     into OutStream
+ *
+ */
+public final class Base64Utility {
+
+    private static final Logger LOG = LogUtils.getL7dLogger(Base64Utility.class);
+
+
+    // base 64 character set
+    //
+    private static final char[] BCS = {
+        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+        'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+        'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+        'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+        'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+        'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
+        '8', '9', '+', '/'
+    };
+
+    private static final char[] BCS_URL_SAFE = Arrays.copyOf(BCS, BCS.length);
+
+    // base 64 padding
+    private static final char PAD = '=';
+
+    // size of base 64 decode table
+    private static final int BDTSIZE = 128;
+
+    // base 64 decode table
+    private static final byte[] BDT = new byte[128];
+
+
+    private static final int PAD_SIZE0 = 1;
+    private static final int PAD_SIZE4 = 2;
+    private static final int PAD_SIZE8 = 3;
+
+    // class static initializer for building decode table
+    static {
+        for (int i = 0;  i < BDTSIZE;  i++) {
+            BDT[i] = Byte.MAX_VALUE;
+        }
+
+        for (int i = 0;  i < BCS.length;  i++) {
+            BDT[BCS[i]] = (byte)i;
+        }
+
+        BCS_URL_SAFE[62] = '-';
+        BCS_URL_SAFE[63] = '_';
+    }
+
+
+    private Base64Utility() {
+        //utility class, never constructed
+    }
+
+
+
+    /**
+     * The <code>decode_chunk</code> routine decodes a chunk of data
+     * into its native encoding.
+     *
+     * base64 encodes each 3 octets of data into 4 characters from a
+     * limited 64 character set. The 3 octets are joined to form
+     * 24 bits which are then split into 4 x 6bit values. Each 6 bit
+     * value is then used as an index into the 64 character table of
+     * base64 chars. If the total data length is not a 3 octet multiple
+     * the '=' char is used as padding for the final 4 char group,
+     * either 1 octet + '==' or 2 octets + '='.
+     *
+     * @param   id  The input data to be processed
+     * @param   o   The offset from which to begin processing
+     * @param   l   The length (bound) at which processing is to end
+     * @return  The decoded data
+     * @exception   Base64Exception Thrown is processing fails due to
+     * formatting exceptions in the encoded data
+     */
+    public static byte[] decodeChunk(char[] id,
+                                     int o,
+                                     int l)
+        throws Base64Exception {
+
+        if (id != null && id.length == 0 && l == 0) {
+            return new byte[0];
+        }
+
+        // Keep it simple - must be >= 4. Unpadded
+        // base64 data contain < 3 octets is invalid.
+        //
+        if ((l - o) < 4) {
+            return null;
+        }
+
+        char[] ib = new char[4];
+        int ibcount = 0;
+
+        // cryan. Calc the num of octets. Each 4 chars of base64 chars
+        // (representing 24 bits) encodes 3 octets.
+        //
+        int octetCount = 3 * (l / 4);
+
+        // Final 4 chars may contain 3 octets or padded to contain
+        // 1 or 2 octets.
+        //
+        if (id[l - 1] == PAD) {
+            // TT== means last 4 chars encode 8 bits (ie subtract 2)
+            // TTT= means last 4 chars encode 16 bits (ie subtract 1)
+            octetCount -= (id[l - 2] == PAD) ? 2 : 1;
+        }
+
+        byte[] ob = new byte[octetCount];
+        int obcount = 0;
+
+        for (int i = o;  i < o + l && i < id.length;  i++) {
+            if (id[i] == PAD
+                || id[i] < BDT.length
+                && BDT[id[i]] != Byte.MAX_VALUE) {
+
+                ib[ibcount++] = id[i];
+
+                // Decode each 4 char sequence.
+                //
+                if (ibcount == ib.length) {
+                    ibcount = 0;
+                    obcount += processEncodeme(ib, ob, obcount);
+                }
+            }
+        }
+
+        if (obcount != ob.length) {
+            byte []tmp = new byte[obcount];
+            System.arraycopy(ob, 0, tmp, 0, obcount);
+            ob = tmp;
+        }
+
+        return ob;
+    }
+
+    public static byte[] decode(String id) throws Base64Exception {
+        return decode(id, false);
+    }
+
+    public static byte[] decode(String id, boolean urlSafe) throws Base64Exception {
+        if (urlSafe) {
+            id = id.replace('-', '+').replace('_', '/');
+            switch (id.length() % 4) {
+            case 0:
+                break;
+            case 2:
+                id += "==";
+                break;
+            case 3:
+                id += "=";
+                break;
+            default:
+                throw new Base64Exception(new Message("BASE64_RUNTIME_EXCEPTION", LOG));
+            }
+        }
+        try {
+            char[] cd = id.toCharArray();
+            return decodeChunk(cd, 0, cd.length);
+        } catch (Exception e) {
+            LOG.warning("Invalid base64 encoded string : " + id);
+            throw new Base64Exception(new Message("BASE64_RUNTIME_EXCEPTION", LOG), e);
+        }
+    }
+
+    public static void decode(char[] id,
+                             int o,
+                             int l,
+                             OutputStream ostream)
+        throws Base64Exception {
+
+        try {
+            ostream.write(decodeChunk(id, o, l));
+        } catch (Exception e) {
+            LOG.warning("Invalid base64 encoded string : " + new String(id));
+            throw new Base64Exception(new Message("BASE64_RUNTIME_EXCEPTION", LOG), e);
+        }
+    }
+
+    public static void decode(String id,
+                              OutputStream ostream)
+        throws Base64Exception {
+
+        try {
+            char[] cd = id.toCharArray();
+            ostream.write(decodeChunk(cd, 0, cd.length));
+        } catch (IOException ioe) {
+            throw new Base64Exception(new Message("BASE64_DECODE_IOEXCEPTION", LOG), ioe);
+        } catch (Exception e) {
+            LOG.warning("Invalid base64 encoded string : " + id);
+            throw new Base64Exception(new Message("BASE64_RUNTIME_EXCEPTION", LOG), e);
+        }
+    }
+
+    // Returns base64 representation of specified byte array.
+    //
+    public static String encode(byte[] id) {
+        return encode(id, false);
+    }
+
+    public static String encode(byte[] id, boolean urlSafe) {
+        char[] cd = encodeChunk(id, 0, id.length);
+        return new String(cd, 0, cd.length);
+    }
+
+    // Returns base64 representation of specified byte array.
+    //
+    public static char[] encodeChunk(byte[] id,
+                                     int o,
+                                     int l) {
+        return encodeChunk(id, o, l, false);
+    }
+
+    public static char[] encodeChunk(byte[] id,
+                                     int o,
+                                     int l,
+                                     boolean urlSafe) {
+        if (id != null && id.length == 0 && l == 0) {
+            return new char[0];
+        } else if (l <= 0) {
+            return null;
+        }
+
+        char[] out;
+
+        // If not a multiple of 3 octets then a final padded 4 char
+        // slot is needed.
+        //
+        if (l % 3 == 0) {
+            out = new char[l / 3 * 4];
+        } else {
+            int finalLen = !urlSafe ? 4 : l % 3 == 1 ? 2 : 3;
+            out = new char[l / 3 * 4 + finalLen];
+        }
+
+        int rindex = o;
+        int windex = 0;
+        int rest = l;
+
+        final char[] base64Table = urlSafe ? BCS_URL_SAFE : BCS;
+        while (rest >= 3) {
+            int i = ((id[rindex] & 0xff) << 16)
+                    + ((id[rindex + 1] & 0xff) << 8)
+                    + (id[rindex + 2] & 0xff);
+
+            out[windex++] = base64Table[i >> 18];
+            out[windex++] = base64Table[(i >> 12) & 0x3f];
+            out[windex++] = base64Table[(i >> 6) & 0x3f];
+            out[windex++] = base64Table[i & 0x3f];
+            rindex += 3;
+            rest -= 3;
+        }
+
+        if (rest == 1) {
+            int i = id[rindex] & 0xff;
+            out[windex++] = base64Table[i >> 2];
+            out[windex++] = base64Table[(i << 4) & 0x3f];
+            if (!urlSafe) {
+                out[windex++] = PAD;
+                out[windex] = PAD;
+            }
+        } else if (rest == 2) {
+            int i = ((id[rindex] & 0xff) << 8) + (id[rindex + 1] & 0xff);
+            out[windex++] = base64Table[i >> 10];
+            out[windex++] = base64Table[(i >> 4) & 0x3f];
+            out[windex++] = base64Table[(i << 2) & 0x3f];
+            if (!urlSafe) {
+                out[windex] = PAD;
+            }
+        }
+        return out;
+    }
+
+    public static void encodeAndStream(byte[] id,
+                                       int o,
+                                       int l,
+                                       OutputStream os) throws IOException {
+        encodeAndStream(id, o, l, false, os);
+    }
+
+    public static void encodeAndStream(byte[] id,
+                                           int o,
+                                           int l,
+                                           boolean urlSafe,
+                                           OutputStream os) throws IOException {
+        if (l <= 0) {
+            return;
+        }
+
+        int rindex = o;
+        int rest = l;
+        final char[] base64Table = urlSafe ? BCS_URL_SAFE : BCS;
+
+        char[] chunk = new char[4];
+        while (rest >= 3) {
+            int i = ((id[rindex] & 0xff) << 16)
+                    + ((id[rindex + 1] & 0xff) << 8)
+                    + (id[rindex + 2] & 0xff);
+            chunk[0] = base64Table[i >> 18];
+            chunk[1] = base64Table[(i >> 12) & 0x3f];
+            chunk[2] = base64Table[(i >> 6) & 0x3f];
+            chunk[3] = base64Table[i & 0x3f];
+            writeCharArrayToStream(chunk, 4, os);
+            rindex += 3;
+            rest -= 3;
+        }
+        if (rest == 0) {
+            return;
+        }
+        if (rest == 1) {
+            int i = id[rindex] & 0xff;
+            chunk[0] = base64Table[i >> 2];
+            chunk[1] = base64Table[(i << 4) & 0x3f];
+            if (!urlSafe) {
+                chunk[2] = PAD;
+                chunk[3] = PAD;
+            }
+        } else if (rest == 2) {
+            int i = ((id[rindex] & 0xff) << 8) + (id[rindex + 1] & 0xff);
+            chunk[0] = base64Table[i >> 10];
+            chunk[1] = base64Table[(i >> 4) & 0x3f];
+            chunk[2] = base64Table[(i << 2) & 0x3f];
+            if (!urlSafe) {
+                chunk[3] = PAD;
+            }
+        }
+        int finalLenToWrite = !urlSafe ? 4 : rest == 1 ? 2 : 3;
+        writeCharArrayToStream(chunk, finalLenToWrite, os);
+    }
+
+    private static void writeCharArrayToStream(char[] chunk, int len, OutputStream os) throws IOException {
+        // may be we can just cast to byte when creating chunk[] earlier on
+        byte[] bytes = StandardCharsets.UTF_8.encode(CharBuffer.wrap(chunk, 0, len)).array();
+        os.write(bytes);
... 27194 lines suppressed ...

[tomee-jakarta] 03/06: cxf-rt-transports-http changes since 3.4.3

Posted by db...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dblevins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomee-jakarta.git

commit 4aefc31fd3a2b11fdcc07bd9b84ef9033bcb833d
Author: David Blevins <da...@gmail.com>
AuthorDate: Sun May 2 21:52:12 2021 -0700

    cxf-rt-transports-http changes since 3.4.3
---
 .../transport/http/AbstractHTTPDestination.java    |  956 ++++++++++
 .../cxf/transport/http/CXFAuthenticator.java       |  177 ++
 .../org/apache/cxf/transport/http/HTTPConduit.java | 1952 ++++++++++++++++++++
 .../cxf/transport/http/HTTPTransportFactory.java   |  297 +++
 .../org/apache/cxf/transport/http/Headers.java     |  583 ++++++
 .../transport/http/HttpServletRequestSnapshot.java |  277 +++
 .../transport/http/ReferencingAuthenticator.java   |  234 +++
 .../http/Servlet3ContinuationProvider.java         |  281 +++
 .../transport/http/URLConnectionHTTPConduit.java   |  439 +++++
 .../cxf/transport/http/auth/HttpAuthHeader.java    |  154 ++
 .../transport/https/HttpsURLConnectionFactory.java |  247 +++
 .../transport/https/HttpsURLConnectionInfo.java    |  168 ++
 .../servicelist/ServiceListJAASAuthenticator.java  |  160 ++
 13 files changed, 5925 insertions(+)

diff --git a/transform/src/patch/java/org/apache/cxf/transport/http/AbstractHTTPDestination.java b/transform/src/patch/java/org/apache/cxf/transport/http/AbstractHTTPDestination.java
new file mode 100644
index 0000000..4bee0d3
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/transport/http/AbstractHTTPDestination.java
@@ -0,0 +1,956 @@
+/**
+ * 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.transport.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.namespace.QName;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.attachment.AttachmentDataSource;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.Base64Exception;
+import org.apache.cxf.common.util.Base64Utility;
+import org.apache.cxf.common.util.PropertyUtils;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.configuration.Configurable;
+import org.apache.cxf.configuration.security.AuthorizationPolicy;
+import org.apache.cxf.continuations.ContinuationProvider;
+import org.apache.cxf.continuations.SuspendedInvocationException;
+import org.apache.cxf.helpers.HttpHeaderHelper;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.interceptor.Interceptor;
+import org.apache.cxf.io.AbstractWrappedOutputStream;
+import org.apache.cxf.io.CopyingOutputStream;
+import org.apache.cxf.io.DelegatingInputStream;
+import org.apache.cxf.message.Attachment;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.ExchangeImpl;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageImpl;
+import org.apache.cxf.message.MessageUtils;
+import org.apache.cxf.policy.PolicyDataEngine;
+import org.apache.cxf.security.SecurityContext;
+import org.apache.cxf.security.transport.TLSSessionInfo;
+import org.apache.cxf.service.model.EndpointInfo;
+import org.apache.cxf.transport.AbstractDestination;
+import org.apache.cxf.transport.AbstractMultiplexDestination;
+import org.apache.cxf.transport.Assertor;
+import org.apache.cxf.transport.Conduit;
+import org.apache.cxf.transport.http.policy.impl.ServerPolicyCalculator;
+import org.apache.cxf.transport.https.CertConstraints;
+import org.apache.cxf.transport.https.CertConstraintsInterceptor;
+import org.apache.cxf.transports.http.configuration.HTTPServerPolicy;
+import org.apache.cxf.ws.addressing.AddressingProperties;
+import org.apache.cxf.ws.addressing.ContextUtils;
+import org.apache.cxf.ws.addressing.EndpointReferenceType;
+import org.apache.cxf.ws.addressing.EndpointReferenceUtils;
+
+/**
+ * Common base for HTTP Destination implementations.
+ */
+public abstract class AbstractHTTPDestination
+    extends AbstractMultiplexDestination
+    implements Configurable, Assertor {
+
+    public static final String HTTP_REQUEST = "HTTP.REQUEST";
+    public static final String HTTP_RESPONSE = "HTTP.RESPONSE";
+    public static final String HTTP_CONTEXT = "HTTP.CONTEXT";
+    public static final String HTTP_CONFIG = "HTTP.CONFIG";
+    public static final String HTTP_CONTEXT_MATCH_STRATEGY = "HTTP_CONTEXT_MATCH_STRATEGY";
+
+    public static final String RESPONSE_HEADERS_COPIED = "http.headers.copied";
+    public static final String RESPONSE_COMMITED = "http.response.done";
+    public static final String REQUEST_REDIRECTED = "http.request.redirected";
+    public static final String CXF_CONTINUATION_MESSAGE = "cxf.continuation.message";
+    public static final String CXF_ASYNC_CONTEXT = "cxf.async.context";
+
+    public static final String SERVICE_REDIRECTION = "http.service.redirection";
+    private static final String HTTP_BASE_PATH = "http.base.path";
+
+    private static final String SSL_CIPHER_SUITE_ATTRIBUTE = "javax.servlet.request.cipher_suite";
+    private static final String SSL_PEER_CERT_CHAIN_ATTRIBUTE = "javax.servlet.request.X509Certificate";
+
+    private static final String DECODE_BASIC_AUTH_WITH_ISO8859 = "decode.basicauth.with.iso8859";
+
+    private static final Logger LOG = LogUtils.getL7dLogger(AbstractHTTPDestination.class);
+
+    protected final Bus bus;
+    protected DestinationRegistry registry;
+    protected final String path;
+
+    // Configuration values
+    protected volatile HTTPServerPolicy serverPolicy;
+    protected String contextMatchStrategy = "stem";
+    protected boolean fixedParameterOrder;
+    protected boolean multiplexWithAddress;
+    protected CertConstraints certConstraints;
+    protected boolean isServlet3;
+    protected boolean decodeBasicAuthWithIso8859;
+    protected ContinuationProviderFactory cproviderFactory;
+    protected boolean enableWebSocket;
+
+    private volatile boolean serverPolicyCalced;
+
+    /**
+     * Constructor
+     *
+     * @param b the associated Bus
+     * @param registry the destination registry
+     * @param ei the endpoint info of the destination
+     * @param path the path
+     * @param dp true for adding the default port if it is missing
+     * @throws IOException
+     */
+    public AbstractHTTPDestination(Bus b,
+                                   DestinationRegistry registry,
+                                   EndpointInfo ei,
+                                   String path,
+                                   boolean dp)
+        throws IOException {
+        super(b, getTargetReference(getAddressValue(ei, dp), b), ei);
+        this.bus = b;
+        this.registry = registry;
+        this.path = path;
+        try {
+            ServletRequest.class.getMethod("isAsyncSupported");
+            isServlet3 = true;
+        } catch (Throwable t) {
+            //servlet 2.5 or earlier, no async support
+        }
+        decodeBasicAuthWithIso8859 = PropertyUtils.isTrue(bus.getProperty(DECODE_BASIC_AUTH_WITH_ISO8859));
+
+        initConfig();
+    }
+
+    public Bus getBus() {
+        return bus;
+    }
+
+    private AuthorizationPolicy getAuthorizationPolicyFromMessage(String credentials, SecurityContext sc) {
+        if (credentials == null || StringUtils.isEmpty(credentials.trim())) {
+            return null;
+        }
+
+        final String[] creds = credentials.split(" ");
+        String authType = creds[0];
+        if ("Basic".equals(authType) && creds.length == 2) {
+            String authEncoded = creds[1];
+            try {
+                byte[] authBytes = Base64Utility.decode(authEncoded);
+
+                if (authBytes == null) {
+                    throw new Base64Exception(new Throwable("Invalid Base64 data."));
+                }
+                
+                String authDecoded = decodeBasicAuthWithIso8859
+                    ? new String(authBytes, StandardCharsets.ISO_8859_1) : new String(authBytes);
+
+                int idx = authDecoded.indexOf(':');
+                final String username;
+                String password = null;
+                if (idx == -1) {
+                    username = authDecoded;
+                } else {
+                    username = authDecoded.substring(0, idx);
+                    if (idx < (authDecoded.length() - 1)) {
+                        password = authDecoded.substring(idx + 1);
+                    }
+                }
+
+                AuthorizationPolicy policy = sc.getUserPrincipal() == null
+                    ? new AuthorizationPolicy() : new PrincipalAuthorizationPolicy(sc);
+                policy.setUserName(username);
+                policy.setPassword(password);
+                policy.setAuthorizationType(authType);
+                return policy;
+            } catch (Base64Exception ex) {
+                // Invalid authentication => treat as not authenticated or use the Principal
+            }
+        }
+        if (sc.getUserPrincipal() != null) {
+            AuthorizationPolicy policy = new PrincipalAuthorizationPolicy(sc);
+            policy.setAuthorization(credentials);
+            policy.setAuthorizationType(authType);
+            return policy;
+        }
+        return null;
+    }
+    public static final class PrincipalAuthorizationPolicy extends AuthorizationPolicy {
+        final SecurityContext sc;
+        public PrincipalAuthorizationPolicy(SecurityContext sc) {
+            this.sc = sc;
+        }
+        public Principal getPrincipal() {
+            return sc.getUserPrincipal();
+        }
+        @Override
+        public String getUserName() {
+            String name = super.getUserName();
+            if (name != null) {
+                return name;
+            }
+            Principal pp = getPrincipal();
+            return pp != null ? pp.getName() : null;
+        }
+    }
+
+    /**
+     * @param message the message under consideration
+     * @return true iff the message has been marked as oneway
+     */
+    protected final boolean isOneWay(Message message) {
+        return MessageUtils.isOneWay(message);
+    }
+
+    public void invoke(final ServletConfig config,
+                       final ServletContext context,
+                       final HttpServletRequest req,
+                       final HttpServletResponse resp) throws IOException {
+        Message inMessage = retrieveFromContinuation(req);
+        if (inMessage == null) {
+            LOG.fine("Create a new message for processing");
+            inMessage = new MessageImpl();
+            ExchangeImpl exchange = new ExchangeImpl();
+            exchange.setInMessage(inMessage);
+            setupMessage(inMessage,
+                     config,
+                     context,
+                     req,
+                     resp);
+
+            exchange.setSession(new HTTPSession(req));
+            ((MessageImpl)inMessage).setDestination(this);
+        } else {
+            LOG.fine("Get the message from the request for processing");
+        }
+
+        copyKnownRequestAttributes(req, inMessage);
+
+        try {
+            incomingObserver.onMessage(inMessage);
+            invokeComplete(context, req, resp, inMessage);
+        } catch (SuspendedInvocationException ex) {
+            if (ex.getRuntimeException() != null) {
+                throw ex.getRuntimeException();
+            }
+            //else nothing to do, just finishing the processing
+        } catch (Fault ex) {
+            Throwable cause = ex.getCause();
+            if (cause instanceof RuntimeException) {
+                throw (RuntimeException)cause;
+            }
+            throw ex;
+        } catch (RuntimeException ex) {
+            throw ex;
+        } finally {
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.fine("Finished servicing http request on thread: " + Thread.currentThread());
+            }
+        }
+    }
+
+    protected void invokeComplete(final ServletContext context,
+                                  final HttpServletRequest req,
+                                  final HttpServletResponse resp,
+                                  Message m) throws IOException {
+        ContinuationProvider p = m.get(ContinuationProvider.class);
+        if (p != null) {
+            p.complete();
+        }
+    }
+
+    private void copyKnownRequestAttributes(HttpServletRequest request, Message message) {
+        message.put(SERVICE_REDIRECTION, request.getAttribute(SERVICE_REDIRECTION));
+    }
+
+    protected void setupMessage(final Message inMessage,
+                                final ServletConfig config,
+                                final ServletContext context,
+                                final HttpServletRequest req,
+                                final HttpServletResponse resp) throws IOException {
+        setupContinuation(inMessage,
+                          req,
+                          resp);
+
+        final Exchange exchange = inMessage.getExchange();
+        DelegatingInputStream in = new DelegatingInputStream(req.getInputStream()) {
+            public void cacheInput() {
+                if (!cached && (exchange.isOneWay() || isWSAddressingReplyToSpecified(exchange))) {
+                    //For one-ways and WS-Addressing invocations with ReplyTo address,
+                    //we need to cache the values of the HttpServletRequest
+                    //so they can be queried later for things like paths and schemes
+                    //and such like that.
+                    //Please note, exchange used to always get the "current" message
+                    exchange.getInMessage().put(HTTP_REQUEST, new HttpServletRequestSnapshot(req));
+                }
+                super.cacheInput();
+            }
+            private boolean isWSAddressingReplyToSpecified(Exchange ex) {
+                AddressingProperties map = ContextUtils.retrieveMAPs(ex.getInMessage(), false, false, false);
+                return map != null && !ContextUtils.isGenericAddress(map.getReplyTo());
+            }
+        };
+
+        inMessage.setContent(DelegatingInputStream.class, in);
+        inMessage.setContent(InputStream.class, in);
+        inMessage.put(HTTP_REQUEST, req);
+        inMessage.put(HTTP_RESPONSE, resp);
+        inMessage.put(HTTP_CONTEXT, context);
+        inMessage.put(HTTP_CONFIG, config);
+        inMessage.put(HTTP_CONTEXT_MATCH_STRATEGY, contextMatchStrategy);
+
+        inMessage.put(Message.HTTP_REQUEST_METHOD, req.getMethod());
+        String requestURI = req.getRequestURI();
+        inMessage.put(Message.REQUEST_URI, requestURI);
+        String requestURL = req.getRequestURL().toString();
+        inMessage.put(Message.REQUEST_URL, requestURL);
+        String contextPath = req.getContextPath();
+        if (contextPath == null) {
+            contextPath = "";
+        }
+        String servletPath = req.getServletPath();
+        if (servletPath == null) {
+            servletPath = "";
+        }
+        String contextServletPath = contextPath + servletPath;
+        String pathInfo = req.getPathInfo();
+        if (pathInfo != null) {
+            inMessage.put(Message.PATH_INFO, contextServletPath + pathInfo);
+        } else {
+            inMessage.put(Message.PATH_INFO, requestURI);
+        }
+        if (!StringUtils.isEmpty(requestURI)) {
+            int index = requestURL.indexOf(requestURI);
+            if (index > 0) {
+                // Can be useful for referencing resources with URIs not covered by CXFServlet.
+                // For example, if we a have web application name 'app' and CXFServlet listening
+                // on "/services/*" then having HTTP_BASE_PATH pointing to say
+                // http://localhost:8080/app will make it easy to refer to non CXF resources
+                String schemaInfo = requestURL.substring(0, index);
+                String basePathWithContextOnly = schemaInfo + contextPath;
+                inMessage.put(HTTP_BASE_PATH, basePathWithContextOnly);
+            }
+        } else if (!StringUtils.isEmpty(servletPath) && requestURL.endsWith(servletPath)) {
+            int index = requestURL.lastIndexOf(servletPath);
+            if (index > 0) {
+                inMessage.put(HTTP_BASE_PATH, requestURL.substring(0, index));
+            }
+        }
+        String contentType = req.getContentType();
+        inMessage.put(Message.CONTENT_TYPE, contentType);
+        setEncoding(inMessage, req, contentType);
+
+        inMessage.put(Message.QUERY_STRING, req.getQueryString());
+
+        inMessage.put(Message.ACCEPT_CONTENT_TYPE, req.getHeader("Accept"));
+        String basePath = getBasePath(contextServletPath);
+        if (!StringUtils.isEmpty(basePath)) {
+            inMessage.put(Message.BASE_PATH, basePath);
+        }
+        inMessage.put(Message.FIXED_PARAMETER_ORDER, isFixedParameterOrder());
+        inMessage.put(Message.ASYNC_POST_RESPONSE_DISPATCH, Boolean.TRUE);
+
+        SecurityContext httpSecurityContext = new SecurityContext() {
+            public Principal getUserPrincipal() {
+                return req.getUserPrincipal();
+            }
+            public boolean isUserInRole(String role) {
+                return req.isUserInRole(role);
+            }
+        };
+
+        inMessage.put(SecurityContext.class, httpSecurityContext);
+
+
+        Headers headers = new Headers(inMessage);
+        headers.copyFromRequest(req);
+        String credentials = headers.getAuthorization();
+        AuthorizationPolicy authPolicy = getAuthorizationPolicyFromMessage(credentials,
+                                                                           httpSecurityContext);
+        inMessage.put(AuthorizationPolicy.class, authPolicy);
+
+        propogateSecureSession(req, inMessage);
+
+        inMessage.put(CertConstraints.class.getName(), certConstraints);
+        inMessage.put(Message.IN_INTERCEPTORS,
+                Arrays.asList(new Interceptor[] {CertConstraintsInterceptor.INSTANCE}));
+
+    }
+    /**
+     * Propogate in the message a TLSSessionInfo instance representative
+     * of the TLS-specific information in the HTTP request.
+     *
+     * @param request the Jetty request
+     * @param message the Message
+     */
+    private static void propogateSecureSession(HttpServletRequest request,
+                                              Message message) {
+        final String cipherSuite =
+            (String) request.getAttribute(SSL_CIPHER_SUITE_ATTRIBUTE);
+        if (cipherSuite != null) {
+            final java.security.cert.Certificate[] certs =
+                (java.security.cert.Certificate[]) request.getAttribute(SSL_PEER_CERT_CHAIN_ATTRIBUTE);
+            message.put(TLSSessionInfo.class,
+                        new TLSSessionInfo(cipherSuite,
+                                           null,
+                                           certs));
+        }
+    }
+    private String setEncoding(final Message inMessage,
+                               final HttpServletRequest req,
+                               final String contentType) throws IOException {
+
+        String enc = HttpHeaderHelper.findCharset(contentType);
+        if (enc == null) {
+            enc = req.getCharacterEncoding();
+        }
+        // work around a bug with Jetty which results in the character
+        // encoding not being trimmed correctly.
+        if (enc != null && enc.endsWith("\"")) {
+            enc = enc.substring(0, enc.length() - 1);
+        }
+        if (enc != null || "POST".equals(req.getMethod()) || "PUT".equals(req.getMethod())) {
+            //allow gets/deletes/options to not specify an encoding
+            String normalizedEncoding = HttpHeaderHelper.mapCharset(enc);
+            if (normalizedEncoding == null) {
+                String m = new org.apache.cxf.common.i18n.Message("INVALID_ENCODING_MSG",
+                                                                  LOG, enc).toString();
+                LOG.log(Level.WARNING, m);
+                throw new IOException(m);
+            }
+            inMessage.put(Message.ENCODING, normalizedEncoding);
+        }
+        return contentType;
+    }
+    protected Message retrieveFromContinuation(HttpServletRequest req) {
+        if (!isServlet3) {
+            if (cproviderFactory != null) {
+                return cproviderFactory.retrieveFromContinuation(req);
+            }
+            return null;
+        }
+        return retrieveFromServlet3Async(req);
+    }
+
+    protected Message retrieveFromServlet3Async(HttpServletRequest req) {
+        try {
+            return (Message)req.getAttribute(CXF_CONTINUATION_MESSAGE);
+        } catch (Throwable ex) {
+            // the request may not implement the Servlet3 API
+        }
+        return null;
+    }
+
+    protected void setupContinuation(Message inMessage,
+                                     final HttpServletRequest req,
+                                     final HttpServletResponse resp) {
+        try {
+            if (isServlet3 && req.isAsyncSupported()) {
+                inMessage.put(ContinuationProvider.class.getName(),
+                              new Servlet3ContinuationProvider(req, resp, inMessage));
+            } else if (cproviderFactory != null) {
+                ContinuationProvider p = cproviderFactory.createContinuationProvider(inMessage, req, resp);
+                if (p != null) {
+                    inMessage.put(ContinuationProvider.class.getName(), p);
+                }
+            }
+        } catch (Throwable ex) {
+            // the request may not implement the Servlet3 API
+        }
+    }
+    protected String getBasePath(String contextPath) throws IOException {
+        if (StringUtils.isEmpty(endpointInfo.getAddress())) {
+            return "";
+        }
+        return new URL(endpointInfo.getAddress()).getPath();
+    }
+
+    protected static EndpointInfo getAddressValue(EndpointInfo ei) {
+        return getAddressValue(ei, true);
+    }
+
+    protected static EndpointInfo getAddressValue(EndpointInfo ei, boolean dp) {
+        if (dp) {
+
+            String eiAddress = ei.getAddress();
+            if (eiAddress == null) {
+                try {
+                    ServerSocket s = new ServerSocket(0);
+                    ei.setAddress("http://localhost:" + s.getLocalPort());
+                    s.close();
+                    return ei;
+                } catch (IOException ex) {
+                    // problem allocating a random port, go to the default one
+                    ei.setAddress("http://localhost");
+                }
+            }
+
+            String addr = StringUtils.addDefaultPortIfMissing(ei.getAddress());
+            if (addr != null) {
+                ei.setAddress(addr);
+            }
+        }
+        return ei;
+    }
+
+    /**
+     * @param inMessage the incoming message
+     * @return the inbuilt backchannel
+     */
+    protected Conduit getInbuiltBackChannel(Message inMessage) {
+        HttpServletResponse response = (HttpServletResponse)inMessage.get(HTTP_RESPONSE);
+        return new BackChannelConduit(response);
+    }
+
+    private void initConfig() {
+
+        cproviderFactory = bus.getExtension(ContinuationProviderFactory.class);
+    }
+
+    private synchronized HTTPServerPolicy calcServerPolicyInternal(Message m) {
+        HTTPServerPolicy sp = serverPolicy;
+        if (!serverPolicyCalced) {
+            PolicyDataEngine pde = bus.getExtension(PolicyDataEngine.class);
+            if (pde != null) {
+                sp = pde.getServerEndpointPolicy(m, endpointInfo, this, new ServerPolicyCalculator());
+            }
+            if (null == sp) {
+                sp = endpointInfo.getTraversedExtensor(
+                        new HTTPServerPolicy(), HTTPServerPolicy.class);
+            }
+            serverPolicy = sp;
+            serverPolicyCalced = true;
+        }
+        return sp;
+    }
+    private HTTPServerPolicy calcServerPolicy(Message m) {
+        HTTPServerPolicy sp = serverPolicy;
+        if (!serverPolicyCalced) {
+            sp = calcServerPolicyInternal(m);
+        }
+        return sp;
+    }
+
+    /**
+     * On first write, we need to make sure any attachments and such that are still on the incoming stream
+     * are read in.  Otherwise we can get into a deadlock where the client is still trying to send the
+     * request, but the server is trying to send the response.   Neither side is reading and both blocked
+     * on full buffers.  Not a good situation.
+     * @param outMessage
+     */
+    private void cacheInput(Message outMessage) {
+        if (outMessage.getExchange() == null) {
+            return;
+        }
+        Message inMessage = outMessage.getExchange().getInMessage();
+        if (inMessage == null) {
+            return;
+        }
+        Object o = inMessage.get("cxf.io.cacheinput");
+        DelegatingInputStream in = inMessage.getContent(DelegatingInputStream.class);
+        if (PropertyUtils.isTrue(o)) {
+            Collection<Attachment> atts = inMessage.getAttachments();
+            if (atts != null) {
+                for (Attachment a : atts) {
+                    if (a.getDataHandler().getDataSource() instanceof AttachmentDataSource) {
+                        try {
+                            ((AttachmentDataSource)a.getDataHandler().getDataSource()).cache(inMessage);
+                        } catch (IOException e) {
+                            throw new Fault(e);
+                        }
+                    }
+                }
+            }
+            if (in != null) {
+                in.cacheInput();
+            }
+        } else if (in != null) {
+            //We don't need to cache it, but we may need to consume it in order for the client
+            // to be able to receive a response. (could be blocked sending)
+            //However, also don't want to consume indefinitely.   We'll limit to 16M.
+            try {
+                IOUtils.consume(in, 16 * 1024 * 1024);
+            } catch (Exception ioe) {
+                //ignore
+            }
+        }
+    }
+
+    protected OutputStream flushHeaders(Message outMessage) throws IOException {
+        return flushHeaders(outMessage, true);
+    }
+    protected OutputStream flushHeaders(Message outMessage, boolean getStream) throws IOException {
+        if (isResponseRedirected(outMessage)) {
+            return null;
+        }
+
+        cacheInput(outMessage);
+        HTTPServerPolicy sp = calcServerPolicy(outMessage);
+        if (sp != null) {
+            new Headers(outMessage).setFromServerPolicy(sp);
+        }
+
+        OutputStream responseStream = null;
+        boolean oneWay = isOneWay(outMessage);
+
+        HttpServletResponse response = getHttpResponseFromMessage(outMessage);
+
+        int responseCode = MessageUtils.getReponseCodeFromMessage(outMessage);
+        if (responseCode >= 300) {
+            String ec = (String)outMessage.get(Message.ERROR_MESSAGE);
+            if (!StringUtils.isEmpty(ec)) {
+                response.sendError(responseCode, ec);
+                return null;
+            }
+        }
+        response.setStatus(responseCode);
+        new Headers(outMessage).copyToResponse(response);
+
+        outMessage.put(RESPONSE_HEADERS_COPIED, "true");
+
+        if (MessageUtils.hasNoResponseContent(outMessage)) {
+            response.setContentLength(0);
+            response.flushBuffer();
+            closeResponseOutputStream(response);
+        } else if (!getStream) {
+            closeResponseOutputStream(response);
+        } else {
+            responseStream = response.getOutputStream();
+        }
+
+        if (oneWay) {
+            outMessage.remove(HTTP_RESPONSE);
+        }
+        return responseStream;
+    }
+
+    private void closeResponseOutputStream(HttpServletResponse response) throws IOException {
+        try {
+            response.getOutputStream().close();
+        } catch (IllegalStateException ex) {
+            // response.getWriter() has already been called
+        }
+    }
+
+
+    private HttpServletResponse getHttpResponseFromMessage(Message message) throws IOException {
+        Object responseObj = message.get(HTTP_RESPONSE);
+        if (responseObj instanceof HttpServletResponse) {
+            return (HttpServletResponse)responseObj;
+        } else if (null != responseObj) {
+            String m = (new org.apache.cxf.common.i18n.Message("UNEXPECTED_RESPONSE_TYPE_MSG",
+                    LOG, responseObj.getClass())).toString();
+            LOG.log(Level.WARNING, m);
+            throw new IOException(m);
+        } else {
+            String m = (new org.apache.cxf.common.i18n.Message("NULL_RESPONSE_MSG", LOG)).toString();
+            LOG.log(Level.WARNING, m);
+            throw new IOException(m);
+        }
+    }
+
+    private boolean isResponseRedirected(Message outMessage) {
+        Exchange exchange = outMessage.getExchange();
+        return exchange != null
+               && Boolean.TRUE.equals(exchange.get(REQUEST_REDIRECTED));
+    }
+
+    /**
+     * Backchannel conduit.
+     */
+    public class BackChannelConduit
+        extends AbstractDestination.AbstractBackChannelConduit {
+
+        protected HttpServletResponse response;
+
+        BackChannelConduit(HttpServletResponse resp) {
+            response = resp;
+        }
+
+        /**
+         * Send an outbound message, assumed to contain all the name-value
+         * mappings of the corresponding input message (if any).
+         *
+         * @param message the message to be sent.
+         */
+        public void prepare(Message message) throws IOException {
+            message.put(HTTP_RESPONSE, response);
+            OutputStream os = message.getContent(OutputStream.class);
+            if (os == null) {
+                message.setContent(OutputStream.class,
+                               new WrappedOutputStream(message));
+            }
+        }
+
+        @Override
+        public void close(Message msg) throws IOException {
+            super.close(msg);
+            if (msg.getExchange() == null) {
+                return;
+            }
+            Message m = msg.getExchange().getInMessage();
+            if (m == null) {
+                return;
+            }
+            InputStream is = m.getContent(InputStream.class);
+            if (is != null) {
+                try {
+                    is.close();
+                    m.removeContent(InputStream.class);
+                } catch (IOException ioex) {
+                    //ignore
+                }
+            }
+        }
+    }
+
+    /**
+     * Wrapper stream responsible for flushing headers and committing outgoing
+     * HTTP-level response.
+     */
+    private class WrappedOutputStream extends AbstractWrappedOutputStream implements CopyingOutputStream {
+
+        private Message outMessage;
+
+        WrappedOutputStream(Message m) {
+            super();
+            this.outMessage = m;
+        }
+
+
+        @Override
+        public int copyFrom(InputStream in) throws IOException {
+            if (!written) {
+                onFirstWrite();
+                written = true;
+            }
+            if (wrappedStream != null) {
+                return IOUtils.copy(in, wrappedStream);
+            }
+            return IOUtils.copy(in, this, IOUtils.DEFAULT_BUFFER_SIZE);
+        }
+
+        /**
+         * Perform any actions required on stream flush (freeze headers,
+         * reset output stream ... etc.)
+         */
+        protected void onFirstWrite() throws IOException {
+            OutputStream responseStream = flushHeaders(outMessage);
+            if (null != responseStream) {
+                wrappedStream = responseStream;
+            }
+        }
+
+        /**
+         * Perform any actions required on stream closure (handle response etc.)
+         */
+        public void close() throws IOException {
+            if (!written && wrappedStream == null) {
+                OutputStream responseStream = flushHeaders(outMessage, false);
+                if (null != responseStream) {
+                    wrappedStream = responseStream;
+                }
+            }
+            if (wrappedStream != null) {
+                // closing the stream should indirectly call the servlet response's flushBuffer
+                wrappedStream.close();
+            }
+            /*
+            try {
+                //make sure the input stream is also closed in this
+                //case so that any resources it may have is cleaned up
+                Message m = outMessage.getExchange().getInMessage();
+                if (m != null) {
+                    InputStream ins = m.getContent(InputStream.class);
+                    if (ins != null) {
+                        ins.close();
+                    }
+                }
+            } catch (IOException ex) {
+                //ignore
+            }
+            */
+        }
+
+    }
+
+    protected boolean contextMatchOnExact() {
+        return "exact".equals(contextMatchStrategy);
+    }
+
+    public void finalizeConfig() {
+    }
+
+    public String getBeanName() {
+        String beanName = null;
+        if (endpointInfo.getName() != null) {
+            beanName = endpointInfo.getName().toString() + ".http-destination";
+        }
+        return beanName;
+    }
+
+    /*
+     * Implement multiplex via the address URL to avoid the need for ws-a.
+     * Requires contextMatchStrategy of stem.
+     *
+     * @see org.apache.cxf.transport.AbstractMultiplexDestination#getAddressWithId(java.lang.String)
+     */
+    public EndpointReferenceType getAddressWithId(String id) {
+        final EndpointReferenceType ref;
+
+        if (isMultiplexWithAddress()) {
+            String address = EndpointReferenceUtils.getAddress(reference);
+            ref = EndpointReferenceUtils.duplicate(reference);
+            if (address.endsWith("/")) {
+                EndpointReferenceUtils.setAddress(ref, address + id);
+            } else {
+                EndpointReferenceUtils.setAddress(ref, address + "/" + id);
+            }
+        } else {
+            ref = super.getAddressWithId(id);
+        }
+        return ref;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.apache.cxf.transport.AbstractMultiplexDestination#getId(java.util.Map)
+     */
+    @Override
+    public String getId(Map<String, Object> context) {
+        String id = null;
+
+        if (isMultiplexWithAddress()) {
+            String address = (String)context.get(Message.PATH_INFO);
+            if (null != address) {
+                int afterLastSlashIndex = address.lastIndexOf('/') + 1;
+                if (afterLastSlashIndex > 0
+                        && afterLastSlashIndex < address.length()) {
+                    id = address.substring(afterLastSlashIndex);
+                }
+            } else {
+                getLogger().log(Level.WARNING,
+                    new org.apache.cxf.common.i18n.Message(
+                            "MISSING_PATH_INFO", LOG).toString());
+            }
+        } else {
+            return super.getId(context);
+        }
+        return id;
+    }
+
+    public String getContextMatchStrategy() {
+        return contextMatchStrategy;
+    }
+
+    public void setContextMatchStrategy(String contextMatchStrategy) {
+        this.contextMatchStrategy = contextMatchStrategy;
+    }
+
+    public boolean isFixedParameterOrder() {
+        return fixedParameterOrder;
+    }
+
+    public void setFixedParameterOrder(boolean fixedParameterOrder) {
+        this.fixedParameterOrder = fixedParameterOrder;
+    }
+
+    public boolean isMultiplexWithAddress() {
+        return multiplexWithAddress;
+    }
+
+    public void setMultiplexWithAddress(boolean multiplexWithAddress) {
+        this.multiplexWithAddress = multiplexWithAddress;
+    }
+
+    public HTTPServerPolicy getServer() {
+        return calcServerPolicy(null);
+    }
+
+    public void setServer(HTTPServerPolicy server) {
+        this.serverPolicy = server;
+        if (server != null) {
+            serverPolicyCalced = true;
+        }
+    }
+
+    public void assertMessage(Message message) {
+        PolicyDataEngine pde = bus.getExtension(PolicyDataEngine.class);
+        pde.assertMessage(message, calcServerPolicy(message), new ServerPolicyCalculator());
+    }
+
+    public boolean canAssert(QName type) {
+        return new ServerPolicyCalculator().getDataClassName().equals(type);
+    }
+
+    public void releaseRegistry() {
+        registry = null;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    @Override
+    protected void activate() {
+        synchronized (this) {
+            if (registry != null) {
+                registry.addDestination(this);
+            }
+        }
+    }
+    @Override
+    protected void deactivate() {
+        synchronized (this) {
+            if (registry != null) {
+                registry.removeDestination(path);
+            }
+        }
+    }
+
+    @Override
+    public void shutdown() {
+        synchronized (this) {
+            if (registry != null) {
+                registry.removeDestination(path);
+            }
+        }
+        super.shutdown();
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/transport/http/CXFAuthenticator.java b/transform/src/patch/java/org/apache/cxf/transport/http/CXFAuthenticator.java
new file mode 100644
index 0000000..6b968f6
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/transport/http/CXFAuthenticator.java
@@ -0,0 +1,177 @@
+/**
+ * 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.transport.http;
+
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.Authenticator;
+import java.net.PasswordAuthentication;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import org.apache.cxf.common.util.ReflectionUtil;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.helpers.JavaUtils;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.PhaseInterceptorChain;
+import org.apache.cxf.transport.Conduit;
+
+/**
+ *
+ */
+public class CXFAuthenticator extends Authenticator {
+    static CXFAuthenticator instance;
+
+
+    public CXFAuthenticator() {
+    }
+
+    public static synchronized void addAuthenticator() {
+        if (instance == null) {
+            instance = new CXFAuthenticator();
+            Authenticator wrapped = null;
+            if (JavaUtils.isJava9Compatible()) {
+                try {
+                    Method m = ReflectionUtil.getMethod(Authenticator.class, "getDefault");
+                    wrapped = (Authenticator)m.invoke(null);
+                } catch (Exception e) {
+                    // ignore
+                }
+                
+
+            } else {
+                for (final Field f : ReflectionUtil.getDeclaredFields(Authenticator.class)) {
+                    if (f.getType().equals(Authenticator.class)) {
+                        ReflectionUtil.setAccessible(f);
+                        try {
+                            wrapped = (Authenticator)f.get(null);
+                            if (wrapped != null && wrapped.getClass().getName()
+                                .equals(ReferencingAuthenticator.class.getName())) {
+                                Method m = wrapped.getClass().getMethod("check");
+                                m.setAccessible(true);
+                                m.invoke(wrapped);
+                            }
+                            wrapped = (Authenticator)f.get(null);
+                        } catch (Exception e) {
+                            // ignore
+                        }
+                    }
+                }
+            }
+
+            try {
+                Class<?> cls;
+                InputStream ins = ReferencingAuthenticator.class
+                    .getResourceAsStream("ReferencingAuthenticator.class");
+                byte[] b = IOUtils.readBytesFromStream(ins);
+                if (JavaUtils.isJava9Compatible()) {
+                    Class<?> methodHandles = Class.forName("java.lang.invoke.MethodHandles");
+                    Method m = ReflectionUtil.getMethod(methodHandles, "lookup");
+                    Object lookup = m.invoke(null);
+                    m = ReflectionUtil.getMethod(lookup.getClass(), "findClass", String.class);
+                    try {
+                        cls = (Class<?>)m.invoke(lookup, "org.apache.cxf.transport.http.ReferencingAuthenticator");
+                    } catch (InvocationTargetException e) {
+                        //use defineClass as fallback
+                        m = ReflectionUtil.getMethod(lookup.getClass(), "defineClass", byte[].class);
+                        cls = (Class<?>)m.invoke(lookup, b);
+                    }
+                } else {
+                    ClassLoader loader = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+                        public ClassLoader run() {
+                            return new URLClassLoader(new URL[0], ClassLoader.getSystemClassLoader());
+                        }
+                    }, null);
+                    Method m = ReflectionUtil.getDeclaredMethod(ClassLoader.class, "defineClass",
+                                                                String.class, byte[].class, Integer.TYPE,
+                                                                Integer.TYPE);
+
+                    ReflectionUtil.setAccessible(m).invoke(loader, ReferencingAuthenticator.class.getName(),
+                                                           b, 0, b.length);
+                    cls = loader.loadClass(ReferencingAuthenticator.class.getName());
+                    try {
+                        //clear the acc field that can hold onto the webapp classloader
+                        Field f = ReflectionUtil.getDeclaredField(loader.getClass(), "acc");
+                        ReflectionUtil.setAccessible(f).set(loader, null);
+                    } catch (Throwable t) {
+                        //ignore
+                    }
+                }
+                final Authenticator auth = (Authenticator)cls.getConstructor(Authenticator.class, Authenticator.class)
+                    .newInstance(instance, wrapped);
+
+                if (System.getSecurityManager() == null) {
+                    Authenticator.setDefault(auth);
+                } else {
+                    AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
+                        public Boolean run() {
+                            Authenticator.setDefault(auth);
+                            return true;
+                        }
+                    });
+
+                }
+                
+            } catch (Throwable t) {
+                //ignore
+            }
+        }
+    }
+
+    protected PasswordAuthentication getPasswordAuthentication() {
+        PasswordAuthentication auth = null;
+        Message m = PhaseInterceptorChain.getCurrentMessage();
+        if (m != null) {
+            Exchange exchange = m.getExchange();
+            Conduit conduit = exchange.getConduit(m);
+            if (conduit instanceof HTTPConduit) {
+                HTTPConduit httpConduit = (HTTPConduit)conduit;
+                if (getRequestorType() == RequestorType.PROXY
+                    && httpConduit.getProxyAuthorization() != null) {
+                    String un = httpConduit.getProxyAuthorization().getUserName();
+                    String pwd = httpConduit.getProxyAuthorization().getPassword();
+                    if (un != null && pwd != null) {
+                        auth = new PasswordAuthentication(un, pwd.toCharArray());
+                    }
+                } else if (getRequestorType() == RequestorType.SERVER
+                    && httpConduit.getAuthorization() != null) {
+
+                    if ("basic".equals(getRequestingScheme()) || "digest".equals(getRequestingScheme())) {
+                        return null;
+                    }
+
+                    String un = httpConduit.getAuthorization().getUserName();
+                    String pwd = httpConduit.getAuthorization().getPassword();
+                    if (un != null && pwd != null) {
+                        auth = new PasswordAuthentication(un, pwd.toCharArray());
+                    }
+                }
+            }
+        }
+        // else PhaseInterceptorChain.getCurrentMessage() is null,
+        // this HTTP call has therefore not been generated by CXF
+        return auth;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/transport/http/HTTPConduit.java b/transform/src/patch/java/org/apache/cxf/transport/http/HTTPConduit.java
new file mode 100644
index 0000000..3904b10
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/transport/http/HTTPConduit.java
@@ -0,0 +1,1952 @@
+/**
+ * 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.transport.http;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.HttpRetryException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.namespace.QName;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.injection.NoJSR250Annotations;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.PropertyUtils;
+import org.apache.cxf.configuration.Configurable;
+import org.apache.cxf.configuration.jsse.TLSClientParameters;
+import org.apache.cxf.configuration.security.AuthorizationPolicy;
+import org.apache.cxf.configuration.security.CertificateConstraintsType;
+import org.apache.cxf.configuration.security.ProxyAuthorizationPolicy;
+import org.apache.cxf.endpoint.ClientCallback;
+import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.helpers.HttpHeaderHelper;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.helpers.LoadingByteArrayOutputStream;
+import org.apache.cxf.io.AbstractThresholdOutputStream;
+import org.apache.cxf.io.CacheAndWriteOutputStream;
+import org.apache.cxf.io.CachedOutputStream;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.ExchangeImpl;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageContentsList;
+import org.apache.cxf.message.MessageImpl;
+import org.apache.cxf.message.MessageUtils;
+import org.apache.cxf.phase.PhaseInterceptorChain;
+import org.apache.cxf.policy.PolicyDataEngine;
+import org.apache.cxf.service.model.EndpointInfo;
+import org.apache.cxf.transport.AbstractConduit;
+import org.apache.cxf.transport.Assertor;
+import org.apache.cxf.transport.Conduit;
+import org.apache.cxf.transport.MessageObserver;
+import org.apache.cxf.transport.http.auth.CustomAuthSupplier;
+import org.apache.cxf.transport.http.auth.DefaultBasicAuthSupplier;
+import org.apache.cxf.transport.http.auth.DigestAuthSupplier;
+import org.apache.cxf.transport.http.auth.HttpAuthHeader;
+import org.apache.cxf.transport.http.auth.HttpAuthSupplier;
+import org.apache.cxf.transport.http.auth.SpnegoAuthSupplier;
+import org.apache.cxf.transport.http.policy.impl.ClientPolicyCalculator;
+import org.apache.cxf.transport.https.CertConstraints;
+import org.apache.cxf.transport.https.CertConstraintsInterceptor;
+import org.apache.cxf.transport.https.CertConstraintsJaxBUtils;
+import org.apache.cxf.transport.https.HttpsURLConnectionInfo;
+import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
+import org.apache.cxf.workqueue.AutomaticWorkQueue;
+import org.apache.cxf.workqueue.WorkQueueManager;
+import org.apache.cxf.ws.addressing.EndpointReferenceType;
+
+/*
+ * HTTP Conduit implementation.
+ * <p>
+ * This implementation is a based on the java.net.URLConnection interface and
+ * dependent upon installed implementations of that URLConnection,
+ * HttpURLConnection, and HttpsURLConnection. Currently, this implementation
+ * has been known to work with the Sun JDK 1.5 default implementations. The
+ * HttpsURLConnection is part of Sun's implementation of the JSSE.
+ * Presently, the source code for the Sun JSSE implementation is unavailable
+ * and therefore we may only lay a guess of whether its HttpsURLConnection
+ * implementation correctly works as far as security is concerned.
+ * <p>
+ * The Trust Decision. If a MessageTrustDecider is configured/set for the
+ * Conduit, it is called upon the first flush of the headers in the
+ * WrappedOutputStream. This reason for this approach is two-fold.
+ * Theoretically, in order to get connection information out of the
+ * URLConnection, it must be "connected". We assume that its implementation will
+ * only follow through up to the point at which it will be ready to send
+ * one byte of data down to the endpoint, but through proxies, and the
+ * commpletion of a TLS handshake in the case of HttpsURLConnection.
+ * However, if we force the connect() call right away, the default
+ * implementations will not allow any calls to add/setRequestProperty,
+ * throwing an exception that the URLConnection is already connected.
+ * <p>
+ * We need to keep the semantic that later CXF interceptors may add to the
+ * PROTOCOL_HEADERS in the Message. This architectual decision forces us to
+ * delay the connection until after that point, then pulling the trust decision.
+ * <p>
+ * The security caveat is that we don't really know when the connection is
+ * really established. The call to "connect" is stated to force the
+ * "connection," but it is a no-op if the connection was already established.
+ * It is entirely possible that an implementation of an URLConnection may
+ * indeed connect at will and start sending the headers down the connection
+ * during calls to add/setRequestProperty!
+ * <p>
+ * We know that the JDK 1.5 sun.com.net.www.HttpURLConnection does not send
+ * this information before the "connect" call, because we can look at the
+ * source code. However, we can only assume, not verify, that the JSSE 1.5
+ * HttpsURLConnection does the same, in that it is probable that the
+ * HttpsURLConnection shares the HttpURLConnection implementation.
+ * <p>
+ * Due to these implementations following redirects without trust checks, we
+ * force the URLConnection implementations not to follow redirects. If
+ * client side policy dictates that we follow redirects, trust decisions are
+ * placed before each retransmit. On a redirect, any authorization information
+ * dynamically acquired by a BasicAuth UserPass supplier is removed before
+ * being retransmitted, as it may no longer be applicable to the new url to
+ * which the connection is redirected.
+ */
+
+/**
+ * This Conduit handles the "http" and "https" transport protocols. An
+ * instance is governed by policies either explicitly set or by
+ * configuration.
+ */
+@NoJSR250Annotations
+public abstract class HTTPConduit
+    extends AbstractConduit
+    implements Configurable, Assertor, PropertyChangeListener {
+
+
+    /**
+     *  This constant is the Message(Map) key for the HttpURLConnection that
+     *  is used to get the response.
+     */
+    public static final String KEY_HTTP_CONNECTION = "http.connection";
+    public static final String KEY_HTTP_CONNECTION_ADDRESS = "http.connection.address";
+
+    public static final String SET_HTTP_RESPONSE_MESSAGE = "org.apache.cxf.transport.http.set.response.message";
+    public static final String HTTP_RESPONSE_MESSAGE = "http.responseMessage";
+
+    public static final String PROCESS_FAULT_ON_HTTP_400 = "org.apache.cxf.transport.process_fault_on_http_400";
+    public static final String NO_IO_EXCEPTIONS = "org.apache.cxf.transport.no_io_exceptions";
+    /**
+     * The Logger for this class.
+     */
+    protected static final Logger LOG = LogUtils.getL7dLogger(HTTPConduit.class);
+
+    private static boolean hasLoggedAsyncWarning;
+
+    /**
+     * This constant holds the suffix ".http-conduit" that is appended to the
+     * Endpoint Qname to give the configuration name of this conduit.
+     */
+    private static final String SC_HTTP_CONDUIT_SUFFIX = ".http-conduit";
+
+    private static final String AUTO_REDIRECT_SAME_HOST_ONLY = "http.redirect.same.host.only";
+    private static final String AUTO_REDIRECT_ALLOW_REL_URI = "http.redirect.relative.uri";
+    private static final String AUTO_REDIRECT_ALLOWED_URI = "http.redirect.allowed.uri";
+    private static final String AUTO_REDIRECT_MAX_SAME_URI_COUNT = "http.redirect.max.same.uri.count";
+
+    private static final String HTTP_POST_METHOD = "POST";
+    private static final String HTTP_GET_METHOD = "GET";
+    private static final Set<String> KNOWN_HTTP_VERBS_WITH_NO_CONTENT =
+        new HashSet<>(Arrays.asList(new String[]{"GET", "HEAD", "OPTIONS", "TRACE"}));
+    /**
+     * This constant is the Message(Map) key for a list of visited URLs that
+     * is used in redirect loop protection.
+     */
+    private static final String KEY_VISITED_URLS = "VisitedURLs";
+
+    /**
+     * This constant is the Message(Map) key for a list of URLs that
+     * is used in authorization loop protection.
+     */
+    private static final String KEY_AUTH_URLS = "AuthURLs";
+
+    /**
+     *  This field holds a reference to the CXF bus associated this conduit.
+     */
+    protected final Bus bus;
+
+    /**
+     * This field is used for two reasons. First it provides the base name for
+     * the conduit for Spring configuration. The other is to hold default
+     * address information, should it not be supplied in the Message Map, by the
+     * Message.ENDPOINT_ADDRESS property.
+     */
+    protected final EndpointInfo endpointInfo;
+
+
+    /**
+     * This field holds the "default" URI for this particular conduit, which
+     * is created on demand.
+     */
+    protected volatile Address defaultAddress;
+
+    protected boolean fromEndpointReferenceType;
+
+    protected ProxyFactory proxyFactory;
+
+    // Configurable values
+
+    /**
+     * This field holds the QoS configuration settings for this conduit.
+     * This field is injected via spring configuration based on the conduit
+     * name.
+     */
+    protected HTTPClientPolicy clientSidePolicy;
+
+    /**
+     * This field holds the password authorization configuration.
+     * This field is injected via spring configuration based on the conduit
+     * name.
+    */
+    protected AuthorizationPolicy authorizationPolicy;
+
+    /**
+     * This field holds the password authorization configuration for the
+     * configured proxy. This field is injected via spring configuration based
+     * on the conduit name.
+     */
+    protected ProxyAuthorizationPolicy proxyAuthorizationPolicy;
+
+    /**
+     * This field holds the configuration TLS configuration which
+     * is programmatically configured.
+     */
+    protected TLSClientParameters tlsClientParameters;
+
+    /**
+     * This field contains the MessageTrustDecider.
+     */
+    protected MessageTrustDecider trustDecider;
+
+    /**
+     * Implements the authentication handling when talking to a server. If it is not set
+     * it will be created from the authorizationPolicy.authType
+     */
+    protected volatile HttpAuthSupplier authSupplier;
+
+    /**
+     * Implements the proxy authentication handling. If it is not set
+     * it will be created from the proxyAuthorizationPolicy.authType
+     */
+    protected volatile HttpAuthSupplier proxyAuthSupplier;
+
+    protected Cookies cookies;
+
+    protected CertConstraints certConstraints;
+
+    private volatile boolean clientSidePolicyCalced;
+
+
+    /**
+     * Constructor
+     *
+     * @param b the associated Bus
+     * @param ei the endpoint info of the initiator
+     * @throws IOException
+     */
+    public HTTPConduit(Bus b, EndpointInfo ei) throws IOException {
+        this(b,
+             ei,
+             null);
+    }
+
+    /**
+     * Constructor
+     *
+     * @param b the associated Bus.
+     * @param ei the endpoint info of the initiator.
+     * @param t the endpoint reference of the target.
+     * @throws IOException
+     */
+    public HTTPConduit(Bus b,
+                       EndpointInfo ei,
+                       EndpointReferenceType t) throws IOException {
+        super(getTargetReference(ei, t, b));
+
+        bus = b;
+        endpointInfo = ei;
+
+        if (t != null) {
+            fromEndpointReferenceType = true;
+        }
+        proxyFactory = new ProxyFactory();
+        cookies = new Cookies();
+    }
+
+    /**
+     * updates the HTTPClientPolicy that is compatible with the assertions
+     * included in the service, endpoint, operation and message policy subjects
+     * if a PolicyDataEngine is installed
+     *
+     * wsdl extensors are superseded by policies which in
+     * turn are superseded by injection
+     */
+    private void updateClientPolicy(Message m) {
+        if (!clientSidePolicyCalced) {
+            PolicyDataEngine policyEngine = bus.getExtension(PolicyDataEngine.class);
+            if (policyEngine != null && endpointInfo.getService() != null) {
+                clientSidePolicy = policyEngine.getClientEndpointPolicy(m,
+                                                                        endpointInfo,
+                                                                        this,
+                                                                        new ClientPolicyCalculator());
+                if (clientSidePolicy != null) {
+                    clientSidePolicy.removePropertyChangeListener(this); //make sure we aren't added twice
+                    clientSidePolicy.addPropertyChangeListener(this);
+                }
+            }
+        }
+        clientSidePolicyCalced = true;
+    }
+
+    private void updateClientPolicy() {
+        if (!clientSidePolicyCalced) {
+            //do no spend time on building Message and Exchange (which basically
+            //are ConcurrentHashMap instances) if the policy is already available
+            Message m = new MessageImpl();
+            m.setExchange(new ExchangeImpl());
+            m.getExchange().put(EndpointInfo.class, this.endpointInfo);
+            updateClientPolicy(m);
+        }
+    }
+
+    /**
+     * This method returns the registered Logger for this conduit.
+     */
+    protected Logger getLogger() {
+        return LOG;
+    }
+
+    /**
+     * This method returns the name of the conduit, which is based on the
+     * endpoint name plus the SC_HTTP_CONDUIT_SUFFIX.
+     * @return
+     */
+    public final String getConduitName() {
+        return endpointInfo.getName() + SC_HTTP_CONDUIT_SUFFIX;
+    }
+
+    private static void configureConduitFromEndpointInfo(HTTPConduit conduit,
+            EndpointInfo endpointInfo) {
+        if (conduit.getClient() == null) {
+            conduit.setClient(endpointInfo.getTraversedExtensor(
+                    new HTTPClientPolicy(), HTTPClientPolicy.class));
+        }
+        if (conduit.getAuthorization() == null) {
+            conduit.setAuthorization(endpointInfo.getTraversedExtensor(
+                    new AuthorizationPolicy(), AuthorizationPolicy.class));
+
+        }
+        if (conduit.getProxyAuthorization() == null) {
+            conduit.setProxyAuthorization(endpointInfo.getTraversedExtensor(
+                    new ProxyAuthorizationPolicy(),
+                    ProxyAuthorizationPolicy.class));
+
+        }
+        if (conduit.getTlsClientParameters() == null) {
+            conduit.setTlsClientParameters(endpointInfo.getTraversedExtensor(
+                    null, TLSClientParameters.class));
+        }
+        if (conduit.getTrustDecider() == null) {
+            conduit.setTrustDecider(endpointInfo.getTraversedExtensor(null,
+                    MessageTrustDecider.class));
+        }
+        if (conduit.getAuthSupplier() == null) {
+            conduit.setAuthSupplier(endpointInfo.getTraversedExtensor(null,
+                    HttpAuthSupplier.class));
+        }
+    }
+
+    private void logConfig() {
+        if (!LOG.isLoggable(Level.FINE)) {
+            return;
+        }
+        if (trustDecider == null) {
+            LOG.log(Level.FINE,
+                    "No Trust Decider configured for Conduit '"
+                    + getConduitName() + "'");
+        } else {
+            LOG.log(Level.FINE, "Message Trust Decider of class '"
+                    + trustDecider.getClass().getName()
+                    + "' with logical name of '"
+                    + trustDecider.getLogicalName()
+                    + "' has been configured for Conduit '"
+                    + getConduitName()
+                    + "'");
+        }
+        if (authSupplier == null) {
+            LOG.log(Level.FINE,
+                    "No Auth Supplier configured for Conduit '"
+                    + getConduitName() + "'");
+        } else {
+            LOG.log(Level.FINE, "HttpAuthSupplier of class '"
+                    + authSupplier.getClass().getName()
+                    + "' has been configured for Conduit '"
+                    + getConduitName()
+                    + "'");
+        }
+        if (this.tlsClientParameters != null) {
+            LOG.log(Level.FINE, "Conduit '" + getConduitName()
+                    + "' has been configured for TLS "
+                    + "keyManagers " + Arrays.toString(tlsClientParameters.getKeyManagers())
+                    + "trustManagers " + Arrays.toString(tlsClientParameters.getTrustManagers())
+                    + "secureRandom " + tlsClientParameters.getSecureRandom()
+                    + "Disable Common Name (CN) Check: " + tlsClientParameters.isDisableCNCheck());
+
+        } else {
+            LOG.log(Level.FINE, "Conduit '" + getConduitName()
+                    + "' has been configured for plain http.");
+        }
+    }
+
+    /**
+     * This call gets called by the HTTPTransportFactory after it
+     * causes an injection of the Spring configuration properties
+     * of this Conduit.
+     */
+    public void finalizeConfig() {
+        // See if not set by configuration, if there are defaults
+        // in order from the Endpoint, Service, or Bus.
+
+        configureConduitFromEndpointInfo(this, endpointInfo);
+        logConfig();
+
+        if (getClient().getDecoupledEndpoint() != null) {
+            this.endpointInfo.setProperty("org.apache.cxf.ws.addressing.replyto",
+                                          getClient().getDecoupledEndpoint());
+        }
+    }
+
+    /**
+     * Allow access to the cookies that the conduit is maintaining
+     * @return the sessionCookies map
+     */
+    public Map<String, Cookie> getCookies() {
+        return cookies.getSessionCookies();
+    }
+
+
+    protected abstract void setupConnection(Message message, Address address, HTTPClientPolicy csPolicy)
+        throws IOException;
+
+    /**
+     * Prepare to send an outbound HTTP message over this http conduit to a
+     * particular endpoint.
+     * <P>
+     * If the Message.PATH_INFO property is set it gets appended
+     * to the Conduit's endpoint URL. If the Message.QUERY_STRING
+     * property is set, it gets appended to the resultant URL following
+     * a "?".
+     * <P>
+     * If the Message.HTTP_REQUEST_METHOD property is NOT set, the
+     * Http request method defaults to "POST".
+     * <P>
+     * If the Message.PROTOCOL_HEADERS is not set on the message, it is
+     * initialized to an empty map.
+     * <P>
+     * This call creates the OutputStream for the content of the message.
+     * It also assigns the created Http(s)URLConnection to the Message
+     * Map.
+     *
+     * @param message The message to be sent.
+     */
+    public void prepare(Message message) throws IOException {
+        // This call can possibly change the conduit endpoint address and
+        // protocol from the default set in EndpointInfo that is associated
+        // with the Conduit.
+        Address currentAddress;
+        try {
+            currentAddress = setupAddress(message);
+        } catch (URISyntaxException e) {
+            throw new IOException(e);
+        }
+
+        // The need to cache the request is off by default
+        boolean needToCacheRequest = false;
+
+        HTTPClientPolicy csPolicy = getClient(message);
+        setupConnection(message, currentAddress, csPolicy);
+
+        // If the HTTP_REQUEST_METHOD is not set, the default is "POST".
+        String httpRequestMethod =
+            (String)message.get(Message.HTTP_REQUEST_METHOD);
+        if (httpRequestMethod == null) {
+            httpRequestMethod = "POST";
+            message.put(Message.HTTP_REQUEST_METHOD, "POST");
+        }
+
+        boolean isChunking = false;
+        int chunkThreshold = 0;
+        final AuthorizationPolicy effectiveAuthPolicy = getEffectiveAuthPolicy(message);
+        if (this.authSupplier == null) {
+            this.authSupplier = createAuthSupplier(effectiveAuthPolicy);
+        }
+
+        if (this.proxyAuthSupplier == null) {
+            this.proxyAuthSupplier = createAuthSupplier(proxyAuthorizationPolicy);
+        }
+
+        if (this.authSupplier.requiresRequestCaching()) {
+            needToCacheRequest = true;
+            isChunking = false;
+            LOG.log(Level.FINE,
+                    "Auth Supplier, but no Preemptive User Pass or Digest auth (nonce may be stale)"
+                    + " We must cache request.");
+        }
+        if (csPolicy.isAutoRedirect()) {
+            needToCacheRequest = true;
+            LOG.log(Level.FINE, "AutoRedirect is turned on.");
+        }
+        if (csPolicy.getMaxRetransmits() > 0) {
+            needToCacheRequest = true;
+            LOG.log(Level.FINE, "MaxRetransmits is set > 0.");
+        }
+        // DELETE does not work and empty PUTs cause misleading exceptions
+        // if chunking is enabled
+        // TODO : ensure chunking can be enabled for non-empty PUTs - if requested
+        if (csPolicy.isAllowChunking()
+            && isChunkingSupported(message, httpRequestMethod)) {
+            //TODO: The chunking mode be configured or at least some
+            // documented client constant.
+            //use -1 and allow the URL connection to pick a default value
+            isChunking = true;
+            chunkThreshold = csPolicy.getChunkingThreshold();
+        }
+        cookies.writeToMessageHeaders(message);
+
+        // The trust decision is relegated to after the "flushing" of the
+        // request headers.
+
+
+
+        if (certConstraints != null) {
+            message.put(CertConstraints.class.getName(), certConstraints);
+            message.getInterceptorChain().add(CertConstraintsInterceptor.INSTANCE);
+        }
+
+        setHeadersByAuthorizationPolicy(message, currentAddress.getURI());
+        new Headers(message).setFromClientPolicy(getClient(message));
+
+        // set the OutputStream on the ProxyOutputStream
+        ProxyOutputStream pos = message.getContent(ProxyOutputStream.class);
+        if (pos != null && message.getContent(OutputStream.class) != null) {
+            pos.setWrappedOutputStream(createOutputStream(message,
+                                                          needToCacheRequest,
+                                                          isChunking,
+                                                          chunkThreshold));
+        } else {
+            message.setContent(OutputStream.class,
+                               createOutputStream(message,
+                                                  needToCacheRequest,
+                                                  isChunking,
+                                                  chunkThreshold));
+        }
+        // We are now "ready" to "send" the message.
+    }
+
+    protected boolean isChunkingSupported(Message message, String httpMethod) {
+        if (HTTP_POST_METHOD.equals(httpMethod)) {
+            return true;
+        } else if (!HTTP_GET_METHOD.equals(httpMethod)) {
+            MessageContentsList objs = MessageContentsList.getContentsList(message);
+            if (objs != null && !objs.isEmpty()) {
+                Object obj = objs.get(0);
+                return obj.getClass() != String.class
+                    || (obj.getClass() == String.class && ((String)obj).length() > 0);
+            }
+        }
+        return false;
+    }
+
+    protected abstract OutputStream createOutputStream(Message message,
+                                                       boolean needToCacheRequest,
+                                                       boolean isChunking,
+                                                       int chunkThreshold) throws IOException;
+
+    private HttpAuthSupplier createAuthSupplier(AuthorizationPolicy authzPolicy) {
+        String authType = authzPolicy.getAuthorizationType();
+        if (HttpAuthHeader.AUTH_TYPE_NEGOTIATE.equals(authType)) {
+            return new SpnegoAuthSupplier();
+        } else if (HttpAuthHeader.AUTH_TYPE_DIGEST.equals(authType)) {
+            return new DigestAuthSupplier();
+        } else if (authType != null && !HttpAuthHeader.AUTH_TYPE_BASIC.equals(authType)
+            && authzPolicy.getAuthorization() != null) {
+            return new CustomAuthSupplier();
+        } else {
+            return new DefaultBasicAuthSupplier();
+        }
+    }
+
+    protected static int determineReceiveTimeout(Message message,
+            HTTPClientPolicy csPolicy) {
+        long rtimeout = csPolicy.getReceiveTimeout();
+        if (message.get(Message.RECEIVE_TIMEOUT) != null) {
+            Object obj = message.get(Message.RECEIVE_TIMEOUT);
+            try {
+                rtimeout = Long.parseLong(obj.toString());
+            } catch (NumberFormatException e) {
+                LOG.log(Level.WARNING, "INVALID_TIMEOUT_FORMAT", new Object[] {
+                    Message.RECEIVE_TIMEOUT, obj.toString()
+                });
+            }
+        }
+        if (rtimeout > Integer.MAX_VALUE) {
+            rtimeout = Integer.MAX_VALUE;
+        }
+        return (int)rtimeout;
+    }
+
+    protected static int determineConnectionTimeout(Message message,
+            HTTPClientPolicy csPolicy) {
+        long ctimeout = csPolicy.getConnectionTimeout();
+        if (message.get(Message.CONNECTION_TIMEOUT) != null) {
+            Object obj = message.get(Message.CONNECTION_TIMEOUT);
+            try {
+                ctimeout = Long.parseLong(obj.toString());
+            } catch (NumberFormatException e) {
+                LOG.log(Level.WARNING, "INVALID_TIMEOUT_FORMAT", new Object[] {
+                    Message.CONNECTION_TIMEOUT, obj.toString()
+                });
+            }
+        }
+        if (ctimeout > Integer.MAX_VALUE) {
+            ctimeout = Integer.MAX_VALUE;
+        }
+        return (int)ctimeout;
+    }
+
+    public void close(Message msg) throws IOException {
+        InputStream in = msg.getContent(InputStream.class);
+        try {
+            if (in != null) {
+                int count = 0;
+                byte[] buffer = new byte[1024];
+                while (in.read(buffer) != -1
+                    && count < 25) {
+                    //don't do anything, we just need to pull off the unread data (like
+                    //closing tags that we didn't need to read
+
+                    //however, limit it so we don't read off gigabytes of data we won't use.
+                    ++count;
+                }
+            }
+        } finally {
+            super.close(msg);
+        }
+    }
+
+    /**
+     * This function sets up a URL based on ENDPOINT_ADDRESS, PATH_INFO,
+     * and QUERY_STRING properties in the Message. The QUERY_STRING gets
+     * added with a "?" after the PATH_INFO. If the ENDPOINT_ADDRESS is not
+     * set on the Message, the endpoint address is taken from the
+     * "defaultEndpointURL".
+     * <p>
+     * The PATH_INFO is only added to the endpoint address string should
+     * the PATH_INFO not equal the end of the endpoint address string.
+     *
+     * @param message The message holds the addressing information.
+     *
+     * @return The full URL specifying the HTTP request to the endpoint.
+     *
+     * @throws MalformedURLException
+     * @throws URISyntaxException
+     */
+    private Address setupAddress(Message message) throws URISyntaxException {
+        String result = (String)message.get(Message.ENDPOINT_ADDRESS);
+        String pathInfo = (String)message.get(Message.PATH_INFO);
+        String queryString = (String)message.get(Message.QUERY_STRING);
+        setAndGetDefaultAddress();
+        if (result == null) {
+            if (pathInfo == null && queryString == null) {
+                if (defaultAddress != null) {
+                    message.put(Message.ENDPOINT_ADDRESS, defaultAddress.getString());
+                }
+                return defaultAddress;
+            }
+            if (defaultAddress != null) {
+                result = defaultAddress.getString();
+                message.put(Message.ENDPOINT_ADDRESS, result);
+            }
+        }
+
+        // REVISIT: is this really correct?
+        if (null != pathInfo && !result.endsWith(pathInfo)) {
+            result = result + pathInfo;
+        }
+        if (queryString != null) {
+            result = result + "?" + queryString;
+        }
+        if (defaultAddress == null) {
+            return setAndGetDefaultAddress(result);
+        }
+        return result.equals(defaultAddress.getString()) ? defaultAddress : new Address(result);
+    }
+
+    /**
+     * Close the conduit
+     */
+    public void close() {
+        if (clientSidePolicy != null) {
+            clientSidePolicy.removePropertyChangeListener(this);
+        }
+    }
+
+    /**
+     * @return the default target address
+     */
+    public String getAddress() {
+        if (defaultAddress != null) {
+            return defaultAddress.getString();
+        } else if (fromEndpointReferenceType) {
+            return getTarget().getAddress().getValue();
+        }
+        return endpointInfo.getAddress();
+    }
+
+    /**
+     * @return the default target URL
+     */
+    protected URI getURI() throws URISyntaxException {
+        return setAndGetDefaultAddress().getURI();
+    }
+
+    private Address setAndGetDefaultAddress() throws URISyntaxException {
+        if (defaultAddress == null) {
+            synchronized (this) {
+                if (defaultAddress == null) {
+                    if (fromEndpointReferenceType && getTarget().getAddress().getValue() != null) {
+                        defaultAddress = new Address(this.getTarget().getAddress().getValue());
+                    } else if (endpointInfo.getAddress() != null) {
+                        defaultAddress = new Address(endpointInfo.getAddress());
+                    }
+                }
+            }
+        }
+        return defaultAddress;
+    }
+
+    private Address setAndGetDefaultAddress(String curAddr) throws URISyntaxException {
+        if (defaultAddress == null) {
+            synchronized (this) {
+                if (defaultAddress == null) {
+                    if (curAddr != null) {
+                        defaultAddress = new Address(curAddr);
+                    } else {
+                        throw new URISyntaxException("<null>",
+                                                     "Invalid address. Endpoint address cannot be null.", 0);
+                    }
+                }
+            }
+        }
+        return defaultAddress;
+    }
+    /**
+     * This call places HTTP Header strings into the headers that are relevant
+     * to the Authorization policies that are set on this conduit by
+     * configuration.
+     * <p>
+     * An AuthorizationPolicy may also be set on the message. If so, those
+     * policies are merged. A user name or password set on the messsage
+     * overrides settings in the AuthorizationPolicy is retrieved from the
+     * configuration.
+     * <p>
+     * The precedence is as follows:
+     * 1. AuthorizationPolicy that is set on the Message, if exists.
+     * 2. Authorization from AuthSupplier, if exists.
+     * 3. AuthorizationPolicy set/configured for conduit.
+     *
+     * REVISIT: Since the AuthorizationPolicy is set on the message by class, then
+     * how does one override the ProxyAuthorizationPolicy which is the same
+     * type?
+     *
+     * @param message
+     * @param currentURI
+     */
+    protected void setHeadersByAuthorizationPolicy(
+            Message message,
+            URI currentURI
+    ) {
+        Headers headers = new Headers(message);
+        AuthorizationPolicy effectiveAuthPolicy = getEffectiveAuthPolicy(message);
+        String authString = authSupplier.getAuthorization(effectiveAuthPolicy, currentURI, message, null);
+        if (authString != null) {
+            headers.setAuthorization(authString);
+        }
+
+        String proxyAuthString = proxyAuthSupplier.getAuthorization(proxyAuthorizationPolicy,
+                                                               currentURI, message, null);
+        if (proxyAuthString != null) {
+            headers.setProxyAuthorization(proxyAuthString);
+        }
+    }
+
+    /**
+     * This is part of the Configurable interface which retrieves the
+     * configuration from spring injection.
+     */
+    // REVISIT:What happens when the endpoint/bean name is null?
+    public String getBeanName() {
+        if (endpointInfo.getName() != null) {
+            return endpointInfo.getName().toString() + ".http-conduit";
+        }
+        return null;
+    }
+
+    /**
+     * Determines effective auth policy from message, conduit and empty default
+     * with priority from first to last
+     *
+     * @param message
+     * @return effective AthorizationPolicy
+     */
+    public AuthorizationPolicy getEffectiveAuthPolicy(Message message) {
+        AuthorizationPolicy authPolicy = getAuthorization();
+        AuthorizationPolicy newPolicy = message.get(AuthorizationPolicy.class);
+        AuthorizationPolicy effectivePolicy = newPolicy;
+        if (effectivePolicy == null) {
+            effectivePolicy = authPolicy;
+        }
+        if (effectivePolicy == null) {
+            effectivePolicy = new AuthorizationPolicy();
+        }
+        return effectivePolicy;
+    }
+
+    /**
+     * This method gets the Authorization Policy that was configured or
+     * explicitly set for this HTTPConduit.
+     */
+    public AuthorizationPolicy getAuthorization() {
+        return authorizationPolicy;
+    }
+
+    /**
+     * This method is used to set the Authorization Policy for this conduit.
+     * Using this method will override any Authorization Policy set in
+     * configuration.
+     */
+    public void setAuthorization(AuthorizationPolicy authorization) {
+        this.authorizationPolicy = authorization;
+    }
+
+    public HTTPClientPolicy getClient(Message message) {
+        ClientPolicyCalculator cpc = new ClientPolicyCalculator();
+        HTTPClientPolicy pol = message.get(HTTPClientPolicy.class);
+        updateClientPolicy(message);
+        if (pol != null) {
+            pol = cpc.intersect(pol, clientSidePolicy);
+        } else {
+            pol = clientSidePolicy;
+        }
+
+        PolicyDataEngine policyDataEngine = bus.getExtension(PolicyDataEngine.class);
+        if (policyDataEngine == null) {
+            return pol;
+        }
+        return policyDataEngine.getPolicy(message, pol, cpc);
+    }
+
+    /**
+     * This method retrieves the Client Side Policy set/configured for this
+     * HTTPConduit.
+     */
+    public HTTPClientPolicy getClient() {
+        updateClientPolicy();
+        return clientSidePolicy;
+    }
+
+    /**
+     * This method sets the Client Side Policy for this HTTPConduit. Using this
+     * method will override any HTTPClientPolicy set in configuration.
+     */
+    public void setClient(HTTPClientPolicy client) {
+        if (this.clientSidePolicy != null) {
+            this.clientSidePolicy.removePropertyChangeListener(this);
+        }
+        this.clientSidePolicyCalced = true;
+        this.clientSidePolicy = client;
+        clientSidePolicy.removePropertyChangeListener(this); //make sure we aren't added twice
+        clientSidePolicy.addPropertyChangeListener(this);
+        endpointInfo.setProperty("org.apache.cxf.ws.addressing.replyto", client.getDecoupledEndpoint());
+    }
+
+    /**
+     * This method retrieves the Proxy Authorization Policy for a proxy that is
+     * set/configured for this HTTPConduit.
+     */
+    public ProxyAuthorizationPolicy getProxyAuthorization() {
+        return proxyAuthorizationPolicy;
+    }
+
+    /**
+     * This method sets the Proxy Authorization Policy for a specified proxy.
+     * Using this method overrides any Authorization Policy for the proxy
+     * that is set in the configuration.
+     */
+    public void setProxyAuthorization(
+            ProxyAuthorizationPolicy proxyAuthorization
+    ) {
+        this.proxyAuthorizationPolicy = proxyAuthorization;
+    }
+
+    /**
+     * This method returns the TLS Client Parameters that is set/configured
+     * for this HTTPConduit.
+     */
+    public TLSClientParameters getTlsClientParameters() {
+        return tlsClientParameters;
+    }
+
+    /**
+     * This method sets the TLS Client Parameters for this HTTPConduit.
+     * Using this method overrides any TLS Client Parameters that is configured
+     * for this HTTPConduit.
+     */
+    public void setTlsClientParameters(TLSClientParameters params) {
+        this.tlsClientParameters = params;
+        if (this.tlsClientParameters != null) {
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.log(Level.FINE, "Conduit '" + getConduitName()
+                    + "' has been (re) configured for TLS "
+                    + "keyManagers " + Arrays.toString(tlsClientParameters.getKeyManagers())
+                    + "trustManagers " + Arrays.toString(tlsClientParameters.getTrustManagers())
+                    + "secureRandom " + tlsClientParameters.getSecureRandom());
+            }
+            CertificateConstraintsType constraints = params.getCertConstraints();
+            if (constraints != null) {
+                certConstraints = CertConstraintsJaxBUtils.createCertConstraints(constraints);
+            }
+        } else {
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.log(Level.FINE, "Conduit '" + getConduitName()
+                    + "' has been (re)configured for plain http.");
+            }
+        }
+    }
+
+    /**
+     * This method gets the Trust Decider that was set/configured for this
+     * HTTPConduit.
+     * @return The Message Trust Decider or null.
+     */
+    public MessageTrustDecider getTrustDecider() {
+        return this.trustDecider;
+    }
+
+    /**
+     * This method sets the Trust Decider for this HTTP Conduit.
+     * Using this method overrides any trust decider configured for this
+     * HTTPConduit.
+     */
+    public void setTrustDecider(MessageTrustDecider decider) {
+        this.trustDecider = decider;
+    }
+
+    /**
+     * This method gets the Auth Supplier that was set/configured for this
+     * HTTPConduit.
+     * @return The Auth Supplier or null.
+     */
+    public HttpAuthSupplier getAuthSupplier() {
+        return this.authSupplier;
+    }
+
+    public void setAuthSupplier(HttpAuthSupplier supplier) {
+        this.authSupplier = supplier;
+    }
+
+    public HttpAuthSupplier getProxyAuthSupplier() {
+        return proxyAuthSupplier;
+    }
+
+    public void setProxyAuthSupplier(HttpAuthSupplier proxyAuthSupplier) {
+        this.proxyAuthSupplier = proxyAuthSupplier;
+    }
+
+
+    /**
+     * This method extracts the value of the "Location" Http
+     * Response header.
+     *
+     * @param headers The Http response headers.
+     * @return The value of the "Location" header, null if non-existent.
+     * @throws MalformedURLException
+     */
+    protected String extractLocation(Map<String, List<String>> headers) throws MalformedURLException {
+        for (Map.Entry<String, List<String>> head : headers.entrySet()) {
+            if ("Location".equalsIgnoreCase(head.getKey())) {
+                List<String> locs = head.getValue();
+                if (locs != null && !locs.isEmpty()) {
+                    String location = locs.get(0);
+                    if (location != null) {
+                        return location;
+                    }
+                    return null;
+                }
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * Used to set appropriate message properties, exchange etc.
+     * as required for an incoming decoupled response (as opposed
+     * what's normally set by the Destination for an incoming
+     * request).
+     */
+    protected class InterposedMessageObserver implements MessageObserver {
+        /**
+         * Called for an incoming message.
+         *
+         * @param inMessage
+         */
+        public void onMessage(Message inMessage) {
+            // disposable exchange, swapped with real Exchange on correlation
+            inMessage.setExchange(new ExchangeImpl());
+            inMessage.getExchange().put(Bus.class, bus);
+            inMessage.put(Message.DECOUPLED_CHANNEL_MESSAGE, Boolean.TRUE);
+            // REVISIT: how to get response headers?
+            //inMessage.put(Message.PROTOCOL_HEADERS, req.getXXX());
+            Headers.getSetProtocolHeaders(inMessage);
+            inMessage.put(Message.RESPONSE_CODE, HttpURLConnection.HTTP_OK);
+
+            // remove server-specific properties
+            inMessage.remove(AbstractHTTPDestination.HTTP_REQUEST);
+            inMessage.remove(AbstractHTTPDestination.HTTP_RESPONSE);
+            inMessage.remove(Message.ASYNC_POST_RESPONSE_DISPATCH);
+
+            //cache this inputstream since it's defer to use in case of async
+            try {
+                InputStream in = inMessage.getContent(InputStream.class);
+                if (in != null) {
+                    CachedOutputStream cos = new CachedOutputStream();
+                    IOUtils.copy(in, cos);
+                    inMessage.setContent(InputStream.class, cos.getInputStream());
+                }
+                incomingObserver.onMessage(inMessage);
+            } catch (IOException e) {
+                logStackTrace(e);
+            }
+        }
+    }
+
+    protected void logStackTrace(Throwable ex) {
+        StringWriter sw = new StringWriter();
+        ex.printStackTrace(new PrintWriter(sw));
+        LOG.warning(sw.toString());
+    }
+
+    public void assertMessage(Message message) {
+        PolicyDataEngine policyDataEngine = bus.getExtension(PolicyDataEngine.class);
+        policyDataEngine.assertMessage(message, getClient(), new ClientPolicyCalculator());
+    }
+
+    public boolean canAssert(QName type) {
+        return type.equals(new QName("http://cxf.apache.org/transports/http/configuration", "client"));
+    }
+
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (evt.getSource() == clientSidePolicy
+            && "decoupledEndpoint".equals(evt.getPropertyName())) {
+            this.endpointInfo.setProperty("org.apache.cxf.ws.addressing.replyto",
+                                          evt.getNewValue());
+        }
+    }
+
+
+
+    /**
+     * Wrapper output stream responsible for flushing headers and handling
+     * the incoming HTTP-level response (not necessarily the MEP response).
+     */
+    protected abstract class WrappedOutputStream extends AbstractThresholdOutputStream {
+        /**
+         * This boolean is true if the request must be cached.
+         */
+        protected boolean cachingForRetransmission;
+
+        /**
+         * If we are going to be chunking, we won't flush till close which causes
+         * new chunks, small network packets, etc..
+         */
+        protected final boolean chunking;
+
+        /**
+         * This field contains the output stream with which we cache
+         * the request. It maybe null if we are not caching.
+         */
+        protected CacheAndWriteOutputStream cachedStream;
+
+        protected Message outMessage;
+
+        protected String conduitName;
+
+        protected URI url;
+
+        protected WrappedOutputStream(
+                Message outMessage,
+                boolean possibleRetransmit,
+                boolean isChunking,
+                int chunkThreshold,
+                String conduitName,
+                URI url
+        ) {
+            super(chunkThreshold);
+            this.outMessage = outMessage;
+            this.cachingForRetransmission = possibleRetransmit;
+            this.chunking = isChunking;
+            this.conduitName = conduitName;
+            this.url = url;
+        }
+
+        // This construction makes extending the HTTPConduit more easier
+        protected WrappedOutputStream(WrappedOutputStream wos) {
+            super(wos.threshold);
+            this.outMessage = wos.outMessage;
+            this.cachingForRetransmission = wos.cachingForRetransmission;
+            this.chunking = wos.chunking;
+            this.conduitName = wos.conduitName;
+            this.url = wos.url;
+        }
+
+        @Override
+        public void thresholdNotReached() {
+            if (chunking) {
+                setFixedLengthStreamingMode(buffer.size());
+            }
+        }
+
+        // methods used for the outgoing side
+        protected abstract void setupWrappedStream() throws IOException;
+        protected abstract HttpsURLConnectionInfo getHttpsURLConnectionInfo() throws IOException;
+        protected abstract void setProtocolHeaders() throws IOException;
+        protected abstract void setFixedLengthStreamingMode(int i);
+
+
+        // methods used for the incoming side
+        protected abstract int getResponseCode() throws IOException;
+        protected abstract String getResponseMessage() throws IOException;
+        protected abstract void updateResponseHeaders(Message inMessage) throws IOException;
+        protected abstract void handleResponseAsync() throws IOException;
+        protected abstract void closeInputStream() throws IOException;
+        protected abstract boolean usingProxy();
+        protected abstract InputStream getInputStream() throws IOException;
+        protected abstract InputStream getPartialResponse() throws IOException;
+
+        //methods to support retransmission for auth or redirects
+        protected abstract void setupNewConnection(String newURL) throws IOException;
+        protected abstract void retransmitStream() throws IOException;
+        protected abstract void updateCookiesBeforeRetransmit() throws IOException;
+
+
+        protected void handleNoOutput() throws IOException {
+            //For GET and DELETE and such, this will be called
+            //For some implementations, this notice may be required to
+            //actually execute the request
+        }
+
+
+        protected void handleResponseOnWorkqueue(boolean allowCurrentThread, boolean forceWQ) throws IOException {
+            Runnable runnable = new Runnable() {
+                public void run() {
+                    try {
+                        handleResponseInternal();
+                    } catch (Throwable e) {
+                        ((PhaseInterceptorChain)outMessage.getInterceptorChain()).abort();
+                        outMessage.setContent(Exception.class, e);
+                        ((PhaseInterceptorChain)outMessage.getInterceptorChain()).unwind(outMessage);
+                        MessageObserver mo = outMessage.getInterceptorChain().getFaultObserver();
+                        if (mo == null) {
+                            mo = outMessage.getExchange().get(MessageObserver.class);
+                        }
+                        mo.onMessage(outMessage);
+                    }
+                }
+            };
+            HTTPClientPolicy policy = getClient(outMessage);
+            boolean exceptionSet = outMessage.getContent(Exception.class) != null;
+            if (!exceptionSet) {
+                try {
+                    Executor ex = outMessage.getExchange().get(Executor.class);
+                    if (forceWQ && ex != null) {
+                        final Executor ex2 = ex;
+                        final Runnable origRunnable = runnable;
+                        runnable = new Runnable() {
+                            public void run() {
+                                outMessage.getExchange().put(Executor.class.getName()
+                                                             + ".USING_SPECIFIED", Boolean.TRUE);
+                                ex2.execute(origRunnable);
+                            }
+                        };
+                    }
+                    if (ex == null || forceWQ) {
+                        WorkQueueManager mgr = outMessage.getExchange().getBus()
+                            .getExtension(WorkQueueManager.class);
+                        AutomaticWorkQueue qu = mgr.getNamedWorkQueue("http-conduit");
+                        if (qu == null) {
+                            qu = mgr.getAutomaticWorkQueue();
+                        }
+                        long timeout = 1000;
+                        if (policy != null && policy.isSetAsyncExecuteTimeout()) {
+                            timeout = policy.getAsyncExecuteTimeout();
+                        }
+                        if (timeout > 0) {
+                            qu.execute(runnable, timeout);
+                        } else {
+                            qu.execute(runnable);
+                        }
+                    } else {
+                        outMessage.getExchange().put(Executor.class.getName()
+                                                 + ".USING_SPECIFIED", Boolean.TRUE);
+                        ex.execute(runnable);
+                    }
+                } catch (RejectedExecutionException rex) {
+                    if (!allowCurrentThread
+                        || (policy != null
+                        && policy.isSetAsyncExecuteTimeoutRejection()
+                        && policy.isAsyncExecuteTimeoutRejection())) {
+                        throw rex;
+                    }
+                    if (!hasLoggedAsyncWarning) {
+                        LOG.warning("EXECUTOR_FULL_WARNING");
+                        hasLoggedAsyncWarning = true;
+                    }
+                    LOG.fine("EXECUTOR_FULL");
+                    handleResponseInternal();
+                }
+            }
+        }
+
+
+        protected void retransmit(String newURL) throws IOException {
+            setupNewConnection(newURL);
+            if (cachedStream != null && cachedStream.size() < Integer.MAX_VALUE) {
+                setFixedLengthStreamingMode((int)cachedStream.size());
+            }
+            setProtocolHeaders();
+
+            //
+            // This point is where the trust decision is made because the
+            // Sun implementation of URLConnection will not let us
+            // set/addRequestProperty after a connect() call, and
+            // makeTrustDecision needs to make a connect() call to
+            // make sure the proper information is available.
+            //
+            makeTrustDecision();
+
+            // If this is a GET method we must not touch the output
+            // stream as this automagically turns the request into a POST.
+            if ("GET".equals(getMethod()) || cachedStream == null) {
+                handleNoOutput();
+                return;
+            }
+
+            // Trust is okay, write the cached request
+            retransmitStream();
+
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.fine("Conduit \""
+                         + getConduitName()
+                         + "\" Retransmit message to: "
+                         + newURL
+                         + ": "
+                         + new String(cachedStream.getBytes()));
+            }
+        }
+
+
+        /**
+         * Perform any actions required on stream flush (freeze headers,
+         * reset output stream ... etc.)
+         */
+        @Override
+        protected void onFirstWrite() throws IOException {
+            try {
+                handleHeadersTrustCaching();
+            } catch (IOException e) {
+                if (e.getMessage() != null && e.getMessage().contains("HTTPS hostname wrong:")) {
+                    throw new IOException("The https URL hostname does not match the "
+                        + "Common Name (CN) on the server certificate in the client's truststore.  "
+                        + "Make sure server certificate is correct, or to disable this check "
+                        + "(NOT recommended for production) set the CXF client TLS "
+                        + "configuration property \"disableCNCheck\" to true.");
+                }
+                throw e;
+            }
+        }
+        protected String getMethod() {
+            return (String)outMessage.get(Message.HTTP_REQUEST_METHOD);
+        }
+
+
+        protected void handleHeadersTrustCaching() throws IOException {
+            // Need to set the headers before the trust decision
+            // because they are set before the connect().
+            setProtocolHeaders();
+
+            //
+            // This point is where the trust decision is made because the
+            // Sun implementation of URLConnection will not let us
+            // set/addRequestProperty after a connect() call, and
+            // makeTrustDecision needs to make a connect() call to
+            // make sure the proper information is available.
+            //
+            makeTrustDecision();
+
+            // Trust is okay, set up for writing the request.
+
+            String method = getMethod();
+            if (KNOWN_HTTP_VERBS_WITH_NO_CONTENT.contains(method)
+                || PropertyUtils.isTrue(outMessage.get(Headers.EMPTY_REQUEST_PROPERTY))) {
+                handleNoOutput();
+                return;
+            }
+            setupWrappedStream();
+        }
+
+
+        /**
+         * Perform any actions required on stream closure (handle response etc.)
+         */
+        public void close() throws IOException {
+            try {
+                if (buffer != null && buffer.size() > 0) {
+                    thresholdNotReached();
+                    LoadingByteArrayOutputStream tmp = buffer;
+                    buffer = null;
+                    super.write(tmp.getRawBytes(), 0, tmp.size());
+                }
+                boolean exceptionSet = outMessage.getContent(Exception.class) != null;
+                if (!written && !exceptionSet) {
+                    handleHeadersTrustCaching();
+                }
+                if (!cachingForRetransmission) {
+                    super.close();
+                } else if (cachedStream != null) {
+                    super.flush();
+                    cachedStream.getOut().close();
+                    cachedStream.closeFlowthroughStream();
+                }
+
+                try {
+                    handleResponse();
+                } finally {
+                    if (cachingForRetransmission && cachedStream != null) {
+                        cachedStream.close();
+                    }
+                }
+            } catch (HttpRetryException e) {
+                handleHttpRetryException(e);
+            } catch (IOException e) {
+                String origMessage = e.getMessage();
+                if (origMessage != null && origMessage.contains(url.toString())) {
+                    throw e;
+                }
+                throw mapException(e.getClass().getSimpleName()
+                                   + " invoking " + url + ": "
+                                   + e.getMessage(), e,
+                                   IOException.class);
+            } catch (RuntimeException e) {
+                throw mapException(e.getClass().getSimpleName()
+                                   + " invoking " + url + ": "
+                                   + e.getMessage(), e,
+                                   RuntimeException.class);
+            }
+        }
+
+        private <T extends Exception> T mapException(String msg,
+                                                     T ex, Class<T> cls) {
+            T ex2;
+            try {
+                ex2 = cls.cast(ex.getClass().getConstructor(String.class).newInstance(msg));
+                ex2.initCause(ex);
+            } catch (Throwable e) {
+                ex2 = ex;
+            }
+
+            return ex2;
+        }
+
+        /**
+         * This procedure handles all retransmits, if any.
+         *
+         * @throws IOException
+         */
+        protected void handleRetransmits() throws IOException {
+            // If we have a cachedStream, we are caching the request.
+            if (cachedStream != null
+                || getClient().isAutoRedirect() && KNOWN_HTTP_VERBS_WITH_NO_CONTENT.contains(getMethod())
+                || authSupplier != null && authSupplier.requiresRequestCaching()) {
+
+                if (LOG.isLoggable(Level.FINE) && cachedStream != null) {
+                    StringBuilder b = new StringBuilder(4096);
+                    b.append("Conduit \"").append(getConduitName())
+                        .append("\" Transmit cached message to: ")
+                        .append(url)
+                        .append(": ");
+                    cachedStream.writeCacheTo(b, 16L * 1024L);
+                    LOG.fine(b.toString());
+                }
+
+
+                int maxRetransmits = getMaxRetransmits();
+                updateCookiesBeforeRetransmit();
+                int nretransmits = 0;
+                while ((maxRetransmits < 0 || nretransmits < maxRetransmits) && processRetransmit()) {
+                    nretransmits++;
+                }
+            }
+        }
+        /**
+         * This function processes any retransmits at the direction of redirections
+         * or "unauthorized" responses.
+         *
+         * @return true if there was a retransmit
+         * @throws IOException
+         */
+        protected boolean processRetransmit() throws IOException {
+            int responseCode = getResponseCode();
+            if ((outMessage != null) && (outMessage.getExchange() != null)) {
+                outMessage.getExchange().put(Message.RESPONSE_CODE, responseCode);
+            }
+            // Process Redirects first.
+            switch(responseCode) {
+            case HttpURLConnection.HTTP_MOVED_PERM:
+            case HttpURLConnection.HTTP_MOVED_TEMP:
+            case HttpURLConnection.HTTP_SEE_OTHER:
+            case 307:
+            case 308:
+                return redirectRetransmit();
+            case HttpURLConnection.HTTP_UNAUTHORIZED:
+            case HttpURLConnection.HTTP_PROXY_AUTH:
+                return authorizationRetransmit();
+            default:
+                break;
+            }
+            return false;
+        }
+        protected boolean redirectRetransmit() throws IOException {
+            // If we are not redirecting by policy, then we don't.
+            if (!getClient(outMessage).isAutoRedirect()) {
+                return false;
+            }
+            Message m = new MessageImpl();
+            updateResponseHeaders(m);
+
+            String newURL = extractLocation(Headers.getSetProtocolHeaders(m));
+            String urlString = url.toString();
+
+            try {
+                newURL = convertToAbsoluteUrlIfNeeded(conduitName, urlString, newURL, outMessage);
+                detectRedirectLoop(conduitName, urlString, newURL, outMessage);
+                checkAllowedRedirectUri(conduitName, urlString, newURL, outMessage);
+            } catch (IOException ex) {
+                // Consider introducing ClientRedirectException instead - it will require
+                // those client runtimes which want to check for it have a direct link to it
+                outMessage.getExchange().put("client.redirect.exception", "true");
+                throw ex;
+            }
+
+            if (newURL != null) {
+                new Headers(outMessage).removeAuthorizationHeaders();
+
+                // If user configured this Conduit with preemptive authorization
+                // it is meant to make it to the end. (Too bad that information
+                // went to every URL along the way, but that's what the user
+                // wants!
+                try {
+                    setHeadersByAuthorizationPolicy(outMessage, new URI(newURL));
+                } catch (URISyntaxException e) {
+                    throw new IOException(e);
+                }
+                cookies.writeToMessageHeaders(outMessage);
+                outMessage.put("transport.retransmit.url", newURL);
+                retransmit(newURL);
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * This method performs a retransmit for authorization information.
+         *
+         * @return true if there was a retransmit
+         * @throws IOException
+         */
+        protected boolean authorizationRetransmit() throws IOException {
+            Message m = new MessageImpl();
+            updateResponseHeaders(m);
+            List<String> authHeaderValues = Headers.getSetProtocolHeaders(m).get("WWW-Authenticate");
+            if (authHeaderValues == null) {
+                LOG.warning("WWW-Authenticate response header is not set");
+                return false;
+            }
+            HttpAuthHeader authHeader = new HttpAuthHeader(authHeaderValues);
+            URI currentURI = url;
+            String realm = authHeader.getRealm();
+            detectAuthorizationLoop(getConduitName(), outMessage, currentURI, realm);
+            AuthorizationPolicy effectiveAthPolicy = getEffectiveAuthPolicy(outMessage);
+            String authorizationToken =
+                authSupplier.getAuthorization(
+                    effectiveAthPolicy, currentURI, outMessage, authHeader.getFullHeader());
+            if (authorizationToken == null) {
+                // authentication not possible => we give up
+                return false;
+            }
+
+            try {
+                closeInputStream();
+            } catch (Throwable t) {
+                //ignore
+            }
+            new Headers(outMessage).setAuthorization(authorizationToken);
+            cookies.writeToMessageHeaders(outMessage);
+            retransmit(url.toString());
+            return true;
+        }
+
+
+
+
+        private int getMaxRetransmits() {
+            HTTPClientPolicy policy = getClient(outMessage);
+            // Default MaxRetransmits is -1 which means unlimited.
+            return (policy == null) ? -1 : policy.getMaxRetransmits();
+        }
+
+        /**
+         * This procedure is called on the close of the output stream so
+         * we are ready to handle the response from the connection.
+         * We may retransmit until we finally get a response.
+         *
+         * @throws IOException
+         */
+        protected void handleResponse() throws IOException {
+            // Process retransmits until we fall out.
+            handleRetransmits();
+
+            if (outMessage == null
+                || outMessage.getExchange() == null
+                || outMessage.getExchange().isSynchronous()) {
+                handleResponseInternal();
+            } else {
+                handleResponseAsync();
+            }
+        }
+
+        /**
+         * This predicate returns true if the exchange indicates
+         * a oneway MEP.
+         *
+         * @param exchange The exchange in question
+         */
+        private boolean isOneway(Exchange exchange) {
+            return exchange != null && exchange.isOneWay();
+        }
+
+        private boolean doProcessResponse(Message message, int responseCode) {
+            // 1. Not oneWay
+            if (!isOneway(message.getExchange())) {
+                return true;
+            }
+            // 2. Robust OneWays could have a fault
+            return responseCode == 500 && MessageUtils.getContextualBoolean(message, Message.ROBUST_ONEWAY, false);
+        }
+
+        protected int doProcessResponseCode() throws IOException {
+            Exchange exchange = outMessage.getExchange();
+            int rc = getResponseCode();
+            if (rc == -1) {
+                LOG.warning("HTTP Response code appears to be corrupted");
+            }
+            if (exchange != null) {
+                exchange.put(Message.RESPONSE_CODE, rc);
+                if (rc == 404 || rc == 503 || rc == 429) {
+                    exchange.put("org.apache.cxf.transport.service_not_available", true);
+                }
+            }
+
+            // "org.apache.cxf.transport.no_io_exceptions" property should be set in case the exceptions
+            // should not be handled here; for example jax rs uses this
+
+            // "org.apache.cxf.transport.process_fault_on_http_400" property should be set in case a
+            // soap fault because of a HTTP 400 should be returned back to the client (SOAP 1.2 spec)
+
+            if (rc >= 400 && rc != 500
+                && !MessageUtils.getContextualBoolean(outMessage, NO_IO_EXCEPTIONS)
+                && (rc > 400 || !MessageUtils.getContextualBoolean(outMessage, PROCESS_FAULT_ON_HTTP_400))) {
+
+                throw new HTTPException(rc, getResponseMessage(), url.toURL());
+            }
+            return rc;
+        }
+
+        protected void handleResponseInternal() throws IOException {
+            Exchange exchange = outMessage.getExchange();
+            int responseCode = doProcessResponseCode();
+
+            InputStream in = null;
+            // oneway or decoupled twoway calls may expect HTTP 202 with no content
+
+            Message inMessage = new MessageImpl();
+            inMessage.setExchange(exchange);
+            updateResponseHeaders(inMessage);
+            inMessage.put(Message.RESPONSE_CODE, responseCode);
+            if (MessageUtils.getContextualBoolean(outMessage, SET_HTTP_RESPONSE_MESSAGE, false)) {
+                inMessage.put(HTTP_RESPONSE_MESSAGE, getResponseMessage());
+            }
+            propagateConduit(exchange, inMessage);
+
+            if ((!doProcessResponse(outMessage, responseCode)
+                || HttpURLConnection.HTTP_ACCEPTED == responseCode)
+                && MessageUtils.getContextualBoolean(outMessage, 
+                    Message.PROCESS_202_RESPONSE_ONEWAY_OR_PARTIAL, true)) {
+                in = getPartialResponse();
+                if (in == null
+                    || !MessageUtils.getContextualBoolean(outMessage, Message.PROCESS_ONEWAY_RESPONSE, false)) {
+                    // oneway operation or decoupled MEP without
+                    // partial response
+                    closeInputStream();
+                    if (isOneway(exchange) && responseCode > 300) {
+                        throw new HTTPException(responseCode, getResponseMessage(), url.toURL());
+                    }
+                    //REVISIT move the decoupled destination property name into api
+                    Endpoint ep = exchange.getEndpoint();
+                    if (null != ep && null != ep.getEndpointInfo() && null == ep.getEndpointInfo().
+                            getProperty("org.apache.cxf.ws.addressing.MAPAggregator.decoupledDestination")) {
+                        // remove callback so that it won't be invoked twice
+                        ClientCallback cc = exchange.remove(ClientCallback.class);
+                        if (null != cc) {
+                            cc.handleResponse(null, null);
+                        }
+                    }
+                    exchange.put("IN_CHAIN_COMPLETE", Boolean.TRUE);
+                    
+                    exchange.setInMessage(inMessage);
+                    return;
+                }
+            } else {
+                //not going to be resending or anything, clear out the stuff in the out message
+                //to free memory
+                outMessage.removeContent(OutputStream.class);
+                if (cachingForRetransmission && cachedStream != null) {
+                    cachedStream.close();
+                }
+                cachedStream = null;
+            }
+
+            String charset = HttpHeaderHelper.findCharset((String)inMessage.get(Message.CONTENT_TYPE));
+            String normalizedEncoding = HttpHeaderHelper.mapCharset(charset);
+            if (normalizedEncoding == null) {
+                String m = new org.apache.cxf.common.i18n.Message("INVALID_ENCODING_MSG",
+                                                                   LOG, charset).toString();
+                LOG.log(Level.WARNING, m);
+                throw new IOException(m);
+            }
+            inMessage.put(Message.ENCODING, normalizedEncoding);
+            if (in == null) {
+                in = getInputStream();
+            }
+            if (in == null) {
+                // Create an empty stream to avoid NullPointerExceptions
+                in = new ByteArrayInputStream(new byte[] {});
+            }
+            inMessage.setContent(InputStream.class, in);
+
+
+            incomingObserver.onMessage(inMessage);
+
+        }
+
+        protected void propagateConduit(Exchange exchange, Message in) {
+            if (exchange != null) {
+                Message out = exchange.getOutMessage();
+                if (out != null) {
+                    in.put(Conduit.class, out.get(Conduit.class));
+                }
+            }
+        }
+
+        protected void handleHttpRetryException(HttpRetryException e) throws IOException {
+            String msg = "HTTP response '" + e.responseCode() + ": "
+                + getResponseMessage() + "' invoking " + url;
+            switch (e.responseCode()) {
+            case HttpURLConnection.HTTP_MOVED_PERM: // 301
+            case HttpURLConnection.HTTP_MOVED_TEMP: // 302
+            case HttpURLConnection.HTTP_SEE_OTHER:  // 303
+            case 307:
+                msg += " that returned location header '" + e.getLocation() + "'";
+                break;
+            case HttpURLConnection.HTTP_UNAUTHORIZED: // 401
+                if (authorizationPolicy == null || authorizationPolicy.getUserName() == null) {
+                    msg += " with NO authorization username configured in conduit " + getConduitName();
+                } else {
+                    msg += " with authorization username '" + authorizationPolicy.getUserName() + "'";
+                }
+                break;
+            case HttpURLConnection.HTTP_PROXY_AUTH: // 407
+                if (proxyAuthorizationPolicy == null || proxyAuthorizationPolicy.getUserName() == null) {
+                    msg += " with NO proxy authorization configured in conduit " + getConduitName();
+                } else {
+                    msg += " with proxy authorization username '"
+                        + proxyAuthorizationPolicy.getUserName() + "'";
+                }
+                if (clientSidePolicy == null || clientSidePolicy.getProxyServer() == null) {
+                    if (usingProxy()) {
+                        msg += " using a proxy even if NONE is configured in CXF conduit "
+                            + getConduitName()
+                            + " (maybe one is configured by java.net.ProxySelector)";
+                    } else {
+                        msg += " but NO proxy was used by the connection (none configured in cxf "
+                            + "conduit and none selected by java.net.ProxySelector)";
+                    }
+                } else {
+                    msg += " using " + clientSidePolicy.getProxyServerType() + " proxy "
+                        + clientSidePolicy.getProxyServer() + ":"
+                        + clientSidePolicy.getProxyServerPort();
+                }
+                break;
+            default:
+                // No other type of HttpRetryException should be thrown
+                break;
+            }
+            throw new IOException(msg, e);
+        }
+
+        /**
+         * This call must take place before anything is written to the
+         * URLConnection. The URLConnection.connect() will be called in order
+         * to get the connection information.
+         *
+         * This method is invoked just after setURLRequestHeaders() from the
+         * WrappedOutputStream before it writes data to the URLConnection.
+         *
+         * If trust cannot be established the Trust Decider implemenation
+         * throws an IOException.
+         *
+         * @throws IOException This exception is thrown if trust cannot be
+         *                     established by the configured MessageTrustDecider.
+         * @see MessageTrustDecider
+         */
+        protected void makeTrustDecision() throws IOException {
+
+            MessageTrustDecider decider2 = outMessage.get(MessageTrustDecider.class);
+            if (trustDecider != null || decider2 != null) {
+                try {
+                    // We must connect or we will not get the credentials.
+                    // The call is (said to be) ignored internally if
+                    // already connected.
+                    HttpsURLConnectionInfo info = getHttpsURLConnectionInfo();
+                    if (trustDecider != null) {
+                        trustDecider.establishTrust(conduitName,
+                                                    info,
+                                                    outMessage);
+                        if (LOG.isLoggable(Level.FINE)) {
+                            LOG.log(Level.FINE, "Trust Decider "
+                                + trustDecider.getLogicalName()
+                                + " considers Conduit "
+                                + conduitName
+                                + " trusted.");
+                        }
+                    }
+                    if (decider2 != null) {
+                        decider2.establishTrust(conduitName,
+                                                info,
+                                                outMessage);
+                        if (LOG.isLoggable(Level.FINE)) {
+                            LOG.log(Level.FINE, "Trust Decider "
+                                + decider2.getLogicalName()
+                                + " considers Conduit "
+                                + conduitName
+                                + " trusted.");
+                        }
+                    }
+                } catch (UntrustedURLConnectionIOException untrustedEx) {
+                    if (LOG.isLoggable(Level.FINE)) {
+                        LOG.log(Level.FINE, "Trust Decider "
+                            + (trustDecider != null ? trustDecider.getLogicalName() : decider2.getLogicalName())
+                            + " considers Conduit "
+                            + conduitName
+                            + " untrusted.", untrustedEx);
+                    }
+                    throw untrustedEx;
+                }
+            } else {
+                // This case, when there is no trust decider, a trust
+                // decision should be a matter of policy.
+                if (LOG.isLoggable(Level.FINE)) {
+                    LOG.log(Level.FINE, "No Trust Decider for Conduit '"
+                        + conduitName
+                        + "'. An affirmative Trust Decision is assumed.");
+                }
+            }
+        }
+    }
+
+    private static void checkAllowedRedirectUri(String conduitName,
+                                                String lastURL,
+                                                String newURL,
+                                                Message message) throws IOException {
+        if (newURL != null) {
+            URI newUri = URI.create(newURL);
+
+            if (MessageUtils.getContextualBoolean(message, AUTO_REDIRECT_SAME_HOST_ONLY)) {
+
+                URI lastUri = URI.create(lastURL);
+
+                // This can be further restricted to make sure newURL completely contains lastURL
+                // though making sure the same HTTP scheme and host are preserved should be enough
+
+                if (!newUri.getScheme().equals(lastUri.getScheme())
+                    || !newUri.getHost().equals(lastUri.getHost())) {
+                    String msg = "Different HTTP Scheme or Host Redirect detected on Conduit '"
+                        + conduitName + "' on '" + newURL + "'";
+                    LOG.log(Level.INFO, msg);
+                    throw new IOException(msg);
+                }
+            }
+
+            String allowedRedirectURI = (String)message.getContextualProperty(AUTO_REDIRECT_ALLOWED_URI);
+            if (allowedRedirectURI != null && !newURL.startsWith(allowedRedirectURI)) {
+                String msg = "Forbidden Redirect URI " + newURL + "detected on Conduit '" + conduitName;
+                LOG.log(Level.INFO, msg);
+                throw new IOException(msg);
+            }
+
+        }
+    }
+
+    // http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-23#section-7.1.2
+    // Relative Location values are also supported
+    private static String convertToAbsoluteUrlIfNeeded(String conduitName,
+                                                       String lastURL,
+                                                       String newURL,
+                                                       Message message) throws IOException {
+        if (newURL != null && !newURL.startsWith("http")) {
+
+            if (MessageUtils.getContextualBoolean(message, AUTO_REDIRECT_ALLOW_REL_URI)) {
+                return URI.create(lastURL).resolve(newURL).toString();
+            }
+            String msg = "Relative Redirect detected on Conduit '"
+                + conduitName + "' on '" + newURL + "'";
+            LOG.log(Level.INFO, msg);
+            throw new IOException(msg);
+        }
+        return newURL;
+
+    }
+
+    private static void detectRedirectLoop(String conduitName,
+                                           String lastURL,
+                                           String newURL,
+                                           Message message) throws IOException {
+        Map<String, Integer> visitedURLs = CastUtils.cast((Map<?, ?>)message.get(KEY_VISITED_URLS));
+        if (visitedURLs == null) {
+            visitedURLs = new HashMap<>();
+            message.put(KEY_VISITED_URLS, visitedURLs);
+        }
+        Integer visitCount = visitedURLs.get(lastURL);
+        if (visitCount == null) {
+            visitCount = 1;
+        } else {
+            visitCount++;
+        }
+        visitedURLs.put(lastURL, visitCount);
+
+        Integer newURLCount = visitedURLs.get(newURL);
+        if (newURL != null && newURLCount != null) {
+            // See if we are being redirected in a loop as best we can,
+            // using string equality on URL.
+            boolean invalidLoopDetected = newURL.equals(lastURL);
+
+            Integer maxSameURICount = PropertyUtils.getInteger(message, AUTO_REDIRECT_MAX_SAME_URI_COUNT);
+
+            if (!invalidLoopDetected) {
+                // This new URI was already recorded earlier even though it is not equal to the last URI
+                // Example: a-b-a, where 'a' is the new URI. Check if a limited number of occurrences of this URI
+                // is allowed, fail by default.
+                if (maxSameURICount == null || newURLCount > maxSameURICount) {
+                    invalidLoopDetected = true;
+                }
+            } else if (maxSameURICount != null && newURLCount <= maxSameURICount) {
+                // This new URI was already recorded earlier and is the same as the last URI.
+                // Example: a-a. But we have a property supporting a limited number of occurrences of this URI.
+                // Continue the invocation.
+                invalidLoopDetected = false;
+            }
+            if (invalidLoopDetected) {
+                // We are in a redirect loop; -- bail
+                String msg = "Redirect loop detected on Conduit '"
+                    + conduitName + "' on '" + newURL + "'";
+                LOG.log(Level.INFO, msg);
+                throw new IOException(msg);
+            }
+        }
+    }
+    private static void detectAuthorizationLoop(String conduitName, Message message,
+                                                URI currentURL, String realm) throws IOException {
+        @SuppressWarnings("unchecked")
+        Set<String> authURLs = (Set<String>) message.get(KEY_AUTH_URLS);
+        if (authURLs == null) {
+            authURLs = new HashSet<>();
+            message.put(KEY_AUTH_URLS, authURLs);
+        }
+        // If we have been here (URL & Realm) before for this particular message
+        // retransmit, it means we have already supplied information
+        // which must have been wrong, or we wouldn't be here again.
+        // Otherwise, the server may be 401 looping us around the realms.
+        if (!authURLs.add(currentURL.toString() + realm)) {
+            String logMessage = "Authorization loop detected on Conduit \""
+                + conduitName
+                + "\" on URL \""
+                + currentURL
+                + "\" with realm \""
+                + realm
+                + "\"";
+            if (LOG.isLoggable(Level.INFO)) {
+                LOG.log(Level.INFO, logMessage);
+            }
+
+            throw new IOException(logMessage);
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/transport/http/HTTPTransportFactory.java b/transform/src/patch/java/org/apache/cxf/transport/http/HTTPTransportFactory.java
new file mode 100644
index 0000000..b9088b9
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/transport/http/HTTPTransportFactory.java
@@ -0,0 +1,297 @@
+/**
+ * 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.transport.http;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.injection.NoJSR250Annotations;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.configuration.Configurer;
+import org.apache.cxf.service.Service;
+import org.apache.cxf.service.model.BindingInfo;
+import org.apache.cxf.service.model.EndpointInfo;
+import org.apache.cxf.service.model.ServiceInfo;
+import org.apache.cxf.transport.AbstractTransportFactory;
+import org.apache.cxf.transport.Conduit;
+import org.apache.cxf.transport.ConduitInitiator;
+import org.apache.cxf.transport.Destination;
+import org.apache.cxf.transport.DestinationFactory;
+import org.apache.cxf.transport.servlet.ServletDestinationFactory;
+import org.apache.cxf.ws.addressing.EndpointReferenceType;
+import org.apache.cxf.wsdl.http.AddressType;
+
+/**
+ *
+ */
+@NoJSR250Annotations
+public class HTTPTransportFactory
+    extends AbstractTransportFactory
+    implements ConduitInitiator, DestinationFactory {
+
+
+    public static final List<String> DEFAULT_NAMESPACES
+        = Collections.unmodifiableList(Arrays.asList(
+            "http://cxf.apache.org/transports/http",
+            "http://cxf.apache.org/transports/http/configuration",
+            "http://schemas.xmlsoap.org/wsdl/http",
+            "http://schemas.xmlsoap.org/wsdl/http/"
+        ));
+
+    private static final Logger LOG = LogUtils.getL7dLogger(HTTPTransportFactory.class);
+
+    /**
+     * This constant holds the prefixes served by this factory.
+     */
+    private static final Set<String> URI_PREFIXES = new HashSet<>();
+    static {
+        URI_PREFIXES.add("http://");
+        URI_PREFIXES.add("https://");
+    }
+
+    protected DestinationRegistry registry;
+
+    private final ReadWriteLock lock = new ReentrantReadWriteLock();
+    private final Lock r = lock.readLock();
+    private final Lock w = lock.writeLock();
+
+    public HTTPTransportFactory() {
+        this(new DestinationRegistryImpl());
+    }
+
+    public HTTPTransportFactory(DestinationRegistry registry) {
+        this(DEFAULT_NAMESPACES, registry);
+    }
+
+    protected HTTPTransportFactory(List<String> transportIds, DestinationRegistry registry) {
+        super(transportIds);
+        if (registry == null) {
+            registry = new DestinationRegistryImpl();
+        }
+        this.registry = registry;
+    }
+
+    public DestinationRegistry getRegistry() {
+        return registry;
+    }
+
+    public void setRegistry(DestinationRegistry newRegistry) {
+        w.lock();
+        try {
+            if (registry.getDestinations().isEmpty()) {
+                this.registry = newRegistry;
+            } else {
+                String m = new org.apache.cxf.common.i18n.Message("CANNOT_CHANGE_REGISTRY_ALREADY_IN_USE",
+                                                                  LOG).toString();
+                LOG.log(Level.SEVERE, m);
+                throw new RuntimeException(m);
+            }
+        } finally {
+            w.unlock();
+        }
+    }
+
+    /**
+     * This call is used by CXF ExtensionManager to inject the activationNamespaces
+     * @param ans The transport ids.
+     */
+    public void setActivationNamespaces(Collection<String> ans) {
+        setTransportIds(new ArrayList<>(ans));
+    }
+
+    public EndpointInfo createEndpointInfo(
+        ServiceInfo serviceInfo,
+        BindingInfo b,
+        List<?>     ees
+    ) {
+        if (ees != null) {
+            for (Iterator<?> itr = ees.iterator(); itr.hasNext();) {
+                Object extensor = itr.next();
+
+                if (extensor instanceof AddressType) {
+                    final AddressType httpAdd = (AddressType)extensor;
+
+                    EndpointInfo info =
+                        new HttpEndpointInfo(serviceInfo,
+                                "http://schemas.xmlsoap.org/wsdl/http/");
+                    info.setAddress(httpAdd.getLocation());
+                    info.addExtensor(httpAdd);
+                    return info;
+                }
+            }
+        }
+
+        HttpEndpointInfo hei = new HttpEndpointInfo(serviceInfo,
+            "http://schemas.xmlsoap.org/wsdl/http/");
+        AddressType at = new AddressType();
+        hei.addExtensor(at);
+
+        return hei;
+    }
+
+    public void createPortExtensors(EndpointInfo ei, Service service) {
+        // TODO
+    }
+
+    public Set<String> getUriPrefixes() {
+        return URI_PREFIXES;
+    }
+
+    /**
+     * This call uses the Configurer from the bus to configure
+     * a bean.
+     *
+     * @param bean
+     */
+    protected void configure(Bus b, Object bean) {
+        configure(b, bean, null, null);
+    }
+
+    protected void configure(Bus bus, Object bean, String name, String extraName) {
+        Configurer configurer = bus.getExtension(Configurer.class);
+        if (null != configurer) {
+            configurer.configureBean(name, bean);
+            if (extraName != null) {
+                configurer.configureBean(extraName, bean);
+            }
+        }
+    }
+
+    private static class HttpEndpointInfo extends EndpointInfo {
+        AddressType saddress;
+        HttpEndpointInfo(ServiceInfo serv, String trans) {
+            super(serv, trans);
+        }
+        public void setAddress(String s) {
+            super.setAddress(s);
+            if (saddress != null) {
+                saddress.setLocation(s);
+            }
+        }
+
+        public void addExtensor(Object el) {
+            super.addExtensor(el);
+            if (el instanceof AddressType) {
+                saddress = (AddressType)el;
+            }
+        }
+    }
+
+    /**
+     * This call creates a new HTTPConduit for the endpoint. It is equivalent
+     * to calling getConduit without an EndpointReferenceType.
+     */
+    public Conduit getConduit(EndpointInfo endpointInfo, Bus bus) throws IOException {
+        return getConduit(endpointInfo, endpointInfo.getTarget(), bus);
+    }
+
+    /**
+     * This call creates a new HTTP Conduit based on the EndpointInfo and
+     * EndpointReferenceType.
+     * TODO: What are the formal constraints on EndpointInfo and
+     * EndpointReferenceType values?
+     */
+    public Conduit getConduit(
+            EndpointInfo endpointInfo,
+            EndpointReferenceType target,
+            Bus bus
+    ) throws IOException {
+
+        HTTPConduitFactory factory = findFactory(endpointInfo, bus);
+        HTTPConduit conduit = null;
+        if (factory != null) {
+            conduit = factory.createConduit(this, bus, endpointInfo, target);
+        }
+        if (conduit == null) {
+            conduit = new URLConnectionHTTPConduit(bus, endpointInfo, target);
+        }
+
+        // Spring configure the conduit.
+        String address = conduit.getAddress();
+        if (address != null && address.indexOf('?') != -1) {
+            address = address.substring(0, address.indexOf('?'));
+        }
+        HTTPConduitConfigurer c1 = bus.getExtension(HTTPConduitConfigurer.class);
+        if (c1 != null) {
+            c1.configure(conduit.getBeanName(), address, conduit);
+        }
+        configure(bus, conduit, conduit.getBeanName(), address);
+        conduit.finalizeConfig();
+        return conduit;
+    }
+
+    protected HTTPConduitFactory findFactory(EndpointInfo endpointInfo, Bus bus) {
+        HTTPConduitFactory f = endpointInfo.getProperty(HTTPConduitFactory.class.getName(), HTTPConduitFactory.class);
+        if (f == null) {
+            f = bus.getExtension(HTTPConduitFactory.class);
+        }
+        return f;
+    }
+    public Destination getDestination(EndpointInfo endpointInfo, Bus bus) throws IOException {
+        if (endpointInfo == null) {
+            throw new IllegalArgumentException("EndpointInfo cannot be null");
+        }
+        r.lock();
+        try {
+            synchronized (registry) {
+                AbstractHTTPDestination d = registry.getDestinationForPath(endpointInfo.getAddress());
+                if (d == null) {
+                    HttpDestinationFactory jettyFactory = bus.getExtension(HttpDestinationFactory.class);
+                    String addr = endpointInfo.getAddress();
+                    if (jettyFactory == null && addr != null && addr.startsWith("http")) {
+                        String m =
+                            new org.apache.cxf.common.i18n.Message("NO_HTTP_DESTINATION_FACTORY_FOUND",
+                                                                   LOG).toString();
+                        LOG.log(Level.SEVERE, m);
+                        throw new IOException(m);
+                    }
+                    final HttpDestinationFactory factory;
+                    if (jettyFactory != null && (addr == null || addr.startsWith("http"))) {
+                        factory = jettyFactory;
+                    } else {
+                        factory = new ServletDestinationFactory();
+                    }
+
+                    d = factory.createDestination(endpointInfo, bus, registry);
+                    registry.addDestination(d);
+                    configure(bus, d);
+                    d.finalizeConfig();
+                }
+                return d;
+            }
+        } finally {
+            r.unlock();
+        }
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/transport/http/Headers.java b/transform/src/patch/java/org/apache/cxf/transport/http/Headers.java
new file mode 100644
index 0000000..db2dbff
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/transport/http/Headers.java
@@ -0,0 +1,583 @@
+/**
+ * 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.transport.http;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URLConnection;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TimeZone;
+import java.util.TreeMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.PropertyUtils;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.helpers.HttpHeaderHelper;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageUtils;
+import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
+import org.apache.cxf.transports.http.configuration.HTTPServerPolicy;
+import org.apache.cxf.version.Version;
+
+public class Headers {
+    /**
+     *  This constant is the Message(Map) key for the HttpURLConnection that
+     *  is used to get the response.
+     */
+    public static final String KEY_HTTP_CONNECTION = "http.connection";
+    /**
+     * Each header value is added as a separate HTTP header, example, given A header with 'a' and 'b'
+     * values, two A headers will be added as opposed to a single A header with the "a,b" value.
+     */
+    public static final String ADD_HEADERS_PROPERTY = "org.apache.cxf.http.add-headers";
+
+    public static final String PROTOCOL_HEADERS_CONTENT_TYPE = Message.CONTENT_TYPE.toLowerCase();
+    public static final String HTTP_HEADERS_SETCOOKIE = "Set-Cookie";
+    public static final String HTTP_HEADERS_LINK = "Link";
+    public static final String EMPTY_REQUEST_PROPERTY = "org.apache.cxf.empty.request";
+    private static final String SET_EMPTY_REQUEST_CT_PROPERTY = "set.content.type.for.empty.request";
+    private static final TimeZone TIME_ZONE_GMT = TimeZone.getTimeZone("GMT");
+    private static final Logger LOG = LogUtils.getL7dLogger(Headers.class);
+
+    private static final List<String> SENSITIVE_HEADERS = Arrays.asList("Authorization", "Proxy-Authorization");
+    private static final List<Object> SENSITIVE_HEADER_MARKER = Arrays.asList("***");
+    private static final String ALLOW_LOGGING_SENSITIVE_HEADERS = "allow.logging.sensitive.headers";
+    private static final String USER_AGENT = initUserAgent();
+
+    private final Message message;
+    private final Map<String, List<String>> headers;
+
+    public Headers(Message message) {
+        this.message = message;
+        this.headers = getSetProtocolHeaders(message);
+    }
+    public Headers() {
+        this.headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        this.message = null;
+    }
+
+    public static String getUserAgent() {
+        return USER_AGENT;
+    }
+
+    private static String initUserAgent() {
+        String name = Version.getName();
+        if ("Apache CXF".equals(name)) {
+            name = "Apache-CXF";
+        }
+        String version = Version.getCurrentVersion();
+        return name + "/" + version;
+    }
+
+    /**
+     * Returns a traceable string representation of the passed-in headers map.
+     * The value for any keys in the map that are in the <code>SENSITIVE_HEADERS</code>
+     * array will be filtered out of the returned string.
+     * Note that this method is expensive as it will copy the map (except for the
+     * filtered keys), so it should be used sparingly - i.e. only when debug is
+     * enabled.
+     */
+    static String toString(Map<String, List<Object>> headers, boolean logSensitiveHeaders) {
+        Map<String, List<Object>> filteredHeaders = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        filteredHeaders.putAll(headers);
+        if (!logSensitiveHeaders) {
+            for (String filteredKey : SENSITIVE_HEADERS) {
+                filteredHeaders.put(filteredKey, SENSITIVE_HEADER_MARKER);
+            }
+        }
+        return filteredHeaders.toString();
+    }
+
+    public Map<String, List<String>> headerMap() {
+        return headers;
+    }
+
+
+    /**
+     * Write cookie header from given session cookies
+     *
+     * @param sessionCookies
+     */
+    public void writeSessionCookies(Map<String, Cookie> sessionCookies) {
+        List<String> cookies = null;
+        for (String s : headers.keySet()) {
+            if (HttpHeaderHelper.COOKIE.equalsIgnoreCase(s)) {
+                cookies = headers.remove(s);
+                break;
+            }
+        }
+        if (cookies == null) {
+            cookies = new ArrayList<>();
+        } else {
+            cookies = new ArrayList<>(cookies);
+        }
+        headers.put(HttpHeaderHelper.COOKIE, cookies);
+        for (Cookie c : sessionCookies.values()) {
+            cookies.add(c.requestCookieHeader());
+        }
+    }
+
+    /**
+     * This call places HTTP Header strings into the headers that are relevant
+     * to the ClientPolicy that is set on this conduit by configuration.
+     *
+     * REVISIT: A cookie is set statically from configuration?
+     */
+    void setFromClientPolicy(HTTPClientPolicy policy) {
+        if (policy == null) {
+            return;
+        }
+        if (policy.isSetCacheControl()) {
+            headers.put("Cache-Control",
+                    createMutableList(policy.getCacheControl()));
+        }
+        if (policy.isSetHost()) {
+            headers.put("Host",
+                    createMutableList(policy.getHost()));
+        }
+        if (policy.isSetConnection()) {
+            headers.put("Connection",
+                    createMutableList(policy.getConnection().value()));
+        }
+        if (policy.isSetAccept()) {
+            headers.put("Accept",
+                    createMutableList(policy.getAccept()));
+        } else if (!headers.containsKey("Accept")) {
+            headers.put("Accept", createMutableList("*/*"));
+        }
+        if (policy.isSetAcceptEncoding()) {
+            headers.put("Accept-Encoding",
+                    createMutableList(policy.getAcceptEncoding()));
+        }
+        if (policy.isSetAcceptLanguage()) {
+            headers.put("Accept-Language",
+                    createMutableList(policy.getAcceptLanguage()));
+        }
+        if (policy.isSetContentType()) {
+            message.put(Message.CONTENT_TYPE, policy.getContentType());
+        }
+        if (policy.isSetCookie()) {
+            headers.put("Cookie",
+                    createMutableList(policy.getCookie()));
+        }
+        if (policy.isSetBrowserType()) {
+            headers.put("User-Agent",
+                    createMutableList(policy.getBrowserType()));
+        }
+        if (policy.isSetReferer()) {
+            headers.put("Referer",
+                    createMutableList(policy.getReferer()));
+        }
+    }
+
+    void setFromServerPolicy(HTTPServerPolicy policy) {
+        if (policy.isSetCacheControl()) {
+            headers.put("Cache-Control",
+                        createMutableList(policy.getCacheControl()));
+        }
+        if (policy.isSetContentLocation()) {
+            headers.put("Content-Location",
+                        createMutableList(policy.getContentLocation()));
+        }
+        if (policy.isSetContentEncoding()) {
+            headers.put("Content-Encoding",
+                        createMutableList(policy.getContentEncoding()));
+        }
+        if (policy.isSetContentType()) {
+            headers.put(HttpHeaderHelper.CONTENT_TYPE,
+                        createMutableList(policy.getContentType()));
+        }
+        if (policy.isSetServerType()) {
+            headers.put("Server",
+                        createMutableList(policy.getServerType()));
+        }
+        if (policy.isSetHonorKeepAlive() && !policy.isHonorKeepAlive()) {
+            headers.put("Connection",
+                        createMutableList("close"));
+        } else if (policy.isSetKeepAliveParameters()) {
+            headers.put("Keep-Alive", createMutableList(policy.getKeepAliveParameters()));
+        }
+
+
+    /*
+     * TODO - hook up these policies
+    <xs:attribute name="SuppressClientSendErrors" type="xs:boolean" use="optional" default="false">
+    <xs:attribute name="SuppressClientReceiveErrors" type="xs:boolean" use="optional" default="false">
+    */
+    }
+
+    public void removeAuthorizationHeaders() {
+        headers.remove("Authorization");
+        headers.remove("Proxy-Authorization");
+    }
+
+    public void setAuthorization(String authorization) {
+        headers.put("Authorization",
+                createMutableList(authorization));
+    }
+
+    public void setProxyAuthorization(String authorization) {
+        headers.put("Proxy-Authorization",
+                createMutableList(authorization));
+    }
+
+
+    /**
+     * While extracting the Message.PROTOCOL_HEADERS property from the Message,
+     * this call ensures that the Message.PROTOCOL_HEADERS property is
+     * set on the Message. If it is not set, an empty map is placed there, and
+     * then returned.
+     *
+     * @param message The outbound message
+     * @return The PROTOCOL_HEADERS map
+     */
+    public static Map<String, List<String>> getSetProtocolHeaders(final Message message) {
+        Map<String, List<String>> headers =
+            CastUtils.cast((Map<?, ?>)message.get(Message.PROTOCOL_HEADERS));
+        if (null == headers) {
+            headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        } else if (headers instanceof HashMap) {
+            Map<String, List<String>> headers2
+                = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+            headers2.putAll(headers);
+            headers = headers2;
+        }
+        message.put(Message.PROTOCOL_HEADERS, headers);
+        return headers;
+    }
+
+    public void readFromConnection(HttpURLConnection connection) {
+        Map<String, List<String>> origHeaders = connection.getHeaderFields();
+        headers.clear();
+        for (Entry<String, List<String>> entry : origHeaders.entrySet()) {
+            if (entry.getKey() != null) {
+                String key = HttpHeaderHelper.getHeaderKey(entry.getKey());
+                List<String> old = headers.get(key);
+                if (old != null) {
+                    List<String> nl = new ArrayList<>(old.size() + entry.getValue().size());
+                    nl.addAll(old);
+                    nl.addAll(entry.getValue());
+                    headers.put(key, nl);
+                } else {
+                    headers.put(key, entry.getValue());
+                }
+            }
+        }
+    }
+
+    private static List<String> createMutableList(String val) {
+        return new ArrayList<>(Arrays.asList(val));
+    }
+
+    /**
+     * This procedure logs the PROTOCOL_HEADERS from the
+     * Message at the specified logging level.
+     *
+     * @param logger     The Logger to log to.
+     * @param level   The Logging Level.
+     * @param headersMap The Message protocol headers.
+     * @param logSensitiveHeaders whether to log sensitive headers
+     */
+    static void logProtocolHeaders(Logger logger, Level level,
+                                   Map<String, List<Object>> headersMap,
+                                   boolean logSensitiveHeaders) {
+        if (logger.isLoggable(level)) {
+            for (Map.Entry<String, List<Object>> entry : headersMap.entrySet()) {
+                String key = entry.getKey();
+                boolean sensitive = !logSensitiveHeaders && SENSITIVE_HEADERS.contains(key);
+                List<Object> headerList = sensitive ? SENSITIVE_HEADER_MARKER : entry.getValue();
+                for (Object value : headerList) {
+                    logger.log(level, key + ": "
+                        + (value == null ? "<null>" : value.toString()));
+                }
+            }
+        }
+    }
+
+    /**
+     * Set content type and protocol headers (Message.PROTOCOL_HEADERS) headers into the URL
+     * connection.
+     * Note, this does not mean they immediately get written to the output
+     * stream or the wire. They just just get set on the HTTP request.
+     *
+     * @param connection
+     * @throws IOException
+     */
+    public void setProtocolHeadersInConnection(HttpURLConnection connection) throws IOException {
+        // If no Content-Type is set for empty requests then HttpUrlConnection:
+        // - sets a form Content-Type for empty POST
+        // - replaces custom Accept value with */* if HTTP proxy is used
+        boolean contentTypeSet = headers.containsKey(Message.CONTENT_TYPE);
+        if (!contentTypeSet) {
+            // if CT is not set then assume it has to be set by default
+            boolean dropContentType = false;
+            boolean getRequest = "GET".equals(message.get(Message.HTTP_REQUEST_METHOD));
+            boolean emptyRequest = getRequest || PropertyUtils.isTrue(message.get(EMPTY_REQUEST_PROPERTY));
+            // If it is an empty request (without a request body) then check further if CT still needs be set
+            if (emptyRequest) {
+                Object setCtForEmptyRequestProp = message.getContextualProperty(SET_EMPTY_REQUEST_CT_PROPERTY);
+                if (setCtForEmptyRequestProp != null) {
+                    // If SET_EMPTY_REQUEST_CT_PROPERTY is set then do as a user prefers.
+                    // CT will be dropped if setting CT for empty requests was explicitly disabled
+                    dropContentType = PropertyUtils.isFalse(setCtForEmptyRequestProp);
+                } else if (getRequest) {
+                    // otherwise if it is GET then just drop it
+                    dropContentType = true;
+                }
+            }
+            if (!dropContentType) {
+                String ct = emptyRequest && !contentTypeSet ? "*/*" : determineContentType();
+                connection.setRequestProperty(HttpHeaderHelper.CONTENT_TYPE, ct);
+            }
+        } else {
+            connection.setRequestProperty(HttpHeaderHelper.CONTENT_TYPE, determineContentType());
+        }
+
+        transferProtocolHeadersToURLConnection(connection);
+
+        Map<String, List<Object>> theHeaders = CastUtils.cast(headers);
+        logProtocolHeaders(LOG, Level.FINE, theHeaders, logSensitiveHeaders());
+    }
+
+    public String determineContentType() {
+        String ct;
+        List<Object> ctList = CastUtils.cast(headers.get(Message.CONTENT_TYPE));
+        if (ctList != null && ctList.size() == 1 && ctList.get(0) != null) {
+            ct = ctList.get(0).toString();
+        } else {
+            ct = (String)message.get(Message.CONTENT_TYPE);
+        }
+
+        String enc = (String)message.get(Message.ENCODING);
+
+        if (null != ct) {
+            if (enc != null
+                && ct.indexOf("charset=") == -1
+                && !ct.toLowerCase().contains("multipart/related")) {
+                ct = ct + "; charset=" + enc;
+            }
+        } else if (enc != null) {
+            ct = "text/xml; charset=" + enc;
+        } else {
+            ct = "text/xml";
+        }
+        return ct;
+    }
+
+    /**
+     * This procedure sets the URLConnection request properties
+     * from the PROTOCOL_HEADERS in the message.
+     */
+    private void transferProtocolHeadersToURLConnection(URLConnection connection) {
+        boolean addHeaders = MessageUtils.getContextualBoolean(message, ADD_HEADERS_PROPERTY, false);
+        for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
+            String header = entry.getKey();
+            if (HttpHeaderHelper.CONTENT_TYPE.equalsIgnoreCase(header)) {
+                continue;
+            }
+
+            List<String> headerList = entry.getValue();
+            if (addHeaders || HttpHeaderHelper.COOKIE.equalsIgnoreCase(header)) {
+                headerList.forEach(s -> connection.addRequestProperty(header, s));
+            } else {
+                connection.setRequestProperty(header, String.join(",", headerList));
+            }
+        }
+        // make sure we don't add more than one User-Agent header
+        if (connection.getRequestProperty("User-Agent") == null) {
+            connection.addRequestProperty("User-Agent", USER_AGENT);
+        }
+    }
+
+    /**
+     * Copy the request headers into the message.
+     *
+     * @param req the current servlet request
+     */
+    protected void copyFromRequest(HttpServletRequest req) {
+
+        //TODO how to deal with the fields
+        for (Enumeration<String> e = req.getHeaderNames(); e.hasMoreElements();) {
+            String fname = e.nextElement();
+            String mappedName = HttpHeaderHelper.getHeaderKey(fname);
+            List<String> values = headers.get(mappedName);
+            if (values == null) {
+                values = new ArrayList<>();
+                headers.put(mappedName, values);
+            }
+            for (Enumeration<String> e2 = req.getHeaders(fname); e2.hasMoreElements();) {
+                String val = e2.nextElement();
+                if ("Accept".equals(mappedName) && !values.isEmpty()) {
+                    //ensure we collapse Accept into first line
+                    String firstAccept = values.get(0);
+                    firstAccept = firstAccept + ", " + val;
+                    values.set(0, firstAccept);
+                }
+                values.add(val);
+            }
+        }
+        if (!headers.containsKey(Message.CONTENT_TYPE)) {
+            headers.put(Message.CONTENT_TYPE, Collections.singletonList(req.getContentType()));
+        }
+        if (LOG.isLoggable(Level.FINE)) {
+            Map<String, List<Object>> theHeaders = CastUtils.cast(headers);
+            LOG.log(Level.FINE, "Request Headers: " + toString(theHeaders,
+                                                               logSensitiveHeaders()));
+        }
+    }
+
+    private boolean logSensitiveHeaders() {
+        // Not allowed by default
+        return PropertyUtils.isTrue(message.getContextualProperty(ALLOW_LOGGING_SENSITIVE_HEADERS));
+    }
+
+    private String getContentTypeFromMessage() {
+        final String ct = (String)message.get(Message.CONTENT_TYPE);
+        final String enc = (String)message.get(Message.ENCODING);
+
+        if (null != ct
+            && null != enc
+            && ct.indexOf("charset=") == -1
+            && !ct.toLowerCase().contains("multipart/related")) {
+            return ct + "; charset=" + enc;
+        }
+        return ct;
+    }
+
+    // Assumes that response body is not available only
+    // if Content-Length is available and set to 0
+    private boolean isResponseBodyAvailable() {
+        List<String> ctLen = headers.get("Content-Length");
+        if (ctLen == null || ctLen.size() != 1) {
+            return true;
+        }
+        try {
+            if (Integer.parseInt(ctLen.get(0)) == 0) {
+                return false;
+            }
+        } catch (NumberFormatException ex) {
+            // ignore
+        }
+        return true;
+    }
+
+    private boolean isSingleHeader(String header) {
+        return HTTP_HEADERS_SETCOOKIE.equalsIgnoreCase(header) || HTTP_HEADERS_LINK.equalsIgnoreCase(header);
+    }
+
+    /**
+     * Copy the response headers into the response.
+     *
+     * @param response the current ServletResponse
+     */
+    protected void copyToResponse(HttpServletResponse response) {
+        String contentType = getContentTypeFromMessage();
+
+        if (!headers.containsKey(Message.CONTENT_TYPE) && contentType != null
+            && isResponseBodyAvailable()) {
+            response.setContentType(contentType);
+        }
+
+        boolean addHeaders = MessageUtils.getContextualBoolean(message, ADD_HEADERS_PROPERTY, false);
+        for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
+            String header = entry.getKey();
+            List<?> headerList = entry.getValue();
+
+            if (addHeaders || isSingleHeader(header)) {
+                for (int i = 0; i < headerList.size(); i++) {
+                    Object headerObject = headerList.get(i);
+                    if (headerObject != null) {
+                        response.addHeader(header, headerObjectToString(headerObject));
+                    }
+                }
+            } else {
+                StringBuilder sb = new StringBuilder();
+                for (int i = 0; i < headerList.size(); i++) {
+                    Object headerObject = headerList.get(i);
+                    if (headerObject != null) {
+                        sb.append(headerObjectToString(headerObject));
+                    }
+
+                    if (i + 1 < headerList.size()) {
+                        sb.append(',');
+                    }
+                }
+                response.setHeader(header, sb.toString());
+            }
+        }
+    }
+
+    private String headerObjectToString(Object headerObject) {
+        if (headerObject.getClass() == String.class) {
+            // Most likely
+            return headerObject.toString();
+        }
+        String headerString;
+        if (headerObject instanceof Date) {
+            headerString = toHttpDate((Date)headerObject);
+        } else if (headerObject instanceof Locale) {
+            headerString = toHttpLanguage((Locale)headerObject);
+        } else {
+            headerString = headerObject.toString();
+        }
+        return headerString;
+    }
+
+    void removeContentType() {
+        headers.remove(PROTOCOL_HEADERS_CONTENT_TYPE);
+    }
+
+    public String getAuthorization() {
+        List<String> authorizationLines = headers.get("Authorization");
+        if (authorizationLines != null && !authorizationLines.isEmpty()) {
+            return authorizationLines.get(0);
+        }
+        return null;
+    }
+
+    public static SimpleDateFormat getHttpDateFormat() {
+        SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+        dateFormat.setTimeZone(TIME_ZONE_GMT);
+        return dateFormat;
+    }
+
+    public static String toHttpDate(Date date) {
+        SimpleDateFormat format = getHttpDateFormat();
+        return format.format(date);
+    }
+
+    public static String toHttpLanguage(Locale locale) {
+        return locale.toString().replace('_', '-');
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/transport/http/HttpServletRequestSnapshot.java b/transform/src/patch/java/org/apache/cxf/transport/http/HttpServletRequestSnapshot.java
new file mode 100644
index 0000000..6ae7a19
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/transport/http/HttpServletRequestSnapshot.java
@@ -0,0 +1,277 @@
+/**
+ * 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.transport.http;
+
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpSession;
+
+public class HttpServletRequestSnapshot extends HttpServletRequestWrapper {
+    private String authType;
+    private String characterEncoding;
+    private int contentLength;
+    private String contentType;
+    private String contextPath;
+    private Cookie[] cookies;
+    private String localAddr;
+    private Locale local;
+    @SuppressWarnings("rawtypes")
+    private Enumeration locals;
+    private String localName;
+    private final int localPort;
+    private String method;
+    private String pathInfo;
+    private String pathTranslated;
+    private String protocol;
+    private String queryString;
+    private String remoteAddr;
+    private String remoteHost;
+    private final int remotePort;
+    private String remoteUser;
+    private String requestURI;
+    private StringBuffer requestURL;
+    private String schema;
+    private String serverName;
+    private final int serverPort;
+    private String servletPath;
+    private HttpSession session;
+    private Principal principal;
+    private Enumeration<String> requestHeaderNames;
+    private Map<String, Enumeration<String>> headersMap =
+        new java.util.concurrent.ConcurrentHashMap<>();
+    private String requestedSessionId;
+
+    public HttpServletRequestSnapshot(HttpServletRequest request) {
+        super(request);
+        authType = request.getAuthType();
+        characterEncoding = request.getCharacterEncoding();
+        contentLength = request.getContentLength();
+        contentType = request.getContentType();
+        contextPath = request.getContextPath();
+        cookies = request.getCookies();
+        requestHeaderNames = request.getHeaderNames();
+        Enumeration<String> tmp = request.getHeaderNames();
+        while (tmp.hasMoreElements()) {
+            String key = tmp.nextElement();
+            headersMap.put(key, request.getHeaders(key));
+        }
+        localAddr = request.getLocalAddr();
+        local = request.getLocale();
+        localName = request.getLocalName();
+        localPort = request.getLocalPort();
+        method = request.getMethod();
+        pathInfo = request.getPathInfo();
+        pathTranslated = request.getPathTranslated();
+        protocol = request.getProtocol();
+        queryString = request.getQueryString();
+        remoteAddr = request.getRemoteAddr();
+        remoteHost = request.getRemoteHost();
+        remotePort = request.getRemotePort();
+        remoteUser = request.getRemoteUser();
+        requestURI = request.getRequestURI();
+        requestURL = request.getRequestURL();
+        requestedSessionId = request.getRequestedSessionId();
+        schema = request.getScheme();
+        serverName = request.getServerName();
+        serverPort = request.getServerPort();
+        servletPath = request.getServletPath();
+        if (request.isRequestedSessionIdValid()) {
+            session = request.getSession();
+        }
+        principal = request.getUserPrincipal();
+    }
+
+    @Override
+    public String getAuthType() {
+        return this.authType;
+    }
+
+    @Override
+    public String getContextPath() {
+        return this.contextPath;
+    }
+
+    @Override
+    public Cookie[] getCookies() {
+        return this.cookies;
+    }
+
+    @Override
+    public String getHeader(String name) {
+        if (headersMap.get(name) != null && headersMap.get(name).hasMoreElements()) {
+            return headersMap.get(name).nextElement();
+        }
+        return null;
+    }
+
+    @SuppressWarnings({
+        "unchecked", "rawtypes"
+    })
+    @Override
+    public Enumeration getHeaderNames() {
+        return this.requestHeaderNames;
+    }
+
+    @SuppressWarnings({
+        "unchecked", "rawtypes"
+    })
+    @Override
+    public Enumeration getHeaders(String name) {
+        return headersMap.get(name);
+    }
+
+    @Override
+    public String getMethod() {
+        return this.method;
+    }
+
+    @Override
+    public String getPathInfo() {
+        return this.pathInfo;
+    }
+
+    @Override
+    public String getPathTranslated() {
+        return this.pathTranslated;
+    }
+
+    @Override
+    public String getQueryString() {
+        return this.queryString;
+    }
+
+    @Override
+    public String getRemoteUser() {
+        return this.remoteUser;
+    }
+
+    @Override
+    public String getRequestURI() {
+        return this.requestURI;
+    }
+
+    @Override
+    public StringBuffer getRequestURL() {
+        return this.requestURL;
+    }
+
+    @Override
+    public String getRequestedSessionId() {
+        return this.requestedSessionId;
+    }
+
+    @Override
+    public String getServletPath() {
+        return this.servletPath;
+    }
+
+    @Override
+    public HttpSession getSession() {
+        return this.session;
+    }
+
+    @Override
+    public Principal getUserPrincipal() {
+        return this.principal;
+    }
+
+    @Override
+    public String getCharacterEncoding() {
+        return this.characterEncoding;
+    }
+
+    @Override
+    public int getContentLength() {
+        return this.contentLength;
+    }
+
+    @Override
+    public String getContentType() {
+        return this.contentType;
+    }
+
+    @Override
+    public String getLocalAddr() {
+        return this.localAddr;
+    }
+
+    @Override
+    public String getLocalName() {
+        return this.localName;
+    }
+
+    @Override
+    public int getLocalPort() {
+        return this.localPort;
+    }
+
+    @Override
+    public Locale getLocale() {
+        return this.local;
+    }
+
+    @SuppressWarnings({
+        "unchecked", "rawtypes"
+    })
+    @Override
+    public Enumeration getLocales() {
+        return this.locals;
+    }
+
+    @Override
+    public String getProtocol() {
+        return this.protocol;
+    }
+
+    @Override
+    public String getRemoteAddr() {
+        return this.remoteAddr;
+    }
+
+    @Override
+    public String getRemoteHost() {
+        return this.remoteHost;
+    }
+
+    @Override
+    public int getRemotePort() {
+        return this.remotePort;
+    }
+
+    @Override
+    public String getScheme() {
+        return this.schema;
+    }
+
+    @Override
+    public String getServerName() {
+        return this.serverName;
+    }
+
+    @Override
+    public int getServerPort() {
+        return this.serverPort;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/transport/http/ReferencingAuthenticator.java b/transform/src/patch/java/org/apache/cxf/transport/http/ReferencingAuthenticator.java
new file mode 100644
index 0000000..7872015
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/transport/http/ReferencingAuthenticator.java
@@ -0,0 +1,234 @@
+/**
+ * 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.transport.http;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.Authenticator;
+import java.net.InetAddress;
+import java.net.PasswordAuthentication;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+public class ReferencingAuthenticator extends Authenticator {
+    private static final boolean SKIPCHECK = System.getSecurityManager() == null;
+    final Reference<Authenticator> auth;
+    final Authenticator wrapped;
+
+    public ReferencingAuthenticator(Authenticator cxfauth, Authenticator wrapped) {
+        this.auth = new WeakReference<>(cxfauth);
+        this.wrapped = wrapped;
+    }
+
+    @Override
+    protected PasswordAuthentication getPasswordAuthentication() {
+        Authenticator cxfauth = auth.get();
+        if (cxfauth == null) {
+            remove();
+        }
+        PasswordAuthentication pauth = null;
+        if (wrapped != null) {
+            try {
+                pauth = tryWith(wrapped);
+                if (pauth != null) {
+                    return pauth;
+                }
+            } catch (Exception e) {
+                pauth = null;
+            }
+        }
+        if (cxfauth != null) {
+            try {
+                pauth = tryWith(cxfauth);
+            } catch (Exception e1) {
+                pauth = null;
+            }
+        }
+        return pauth;
+    }
+
+    public final void check() {
+        Authenticator cxfauth = auth.get();
+        if (cxfauth == null) {
+            remove();
+        }
+        if (wrapped != null && wrapped.getClass().getName().equals(ReferencingAuthenticator.class.getName())) {
+            try {
+                Method m = wrapped.getClass().getMethod("check");
+                m.setAccessible(true);
+                m.invoke(wrapped);
+            } catch (Throwable t) {
+                //ignore
+            }
+        }
+    }
+
+    private void remove() {
+        try { 
+            // Try Authenticator.getDefault() first, JDK9+
+            final MethodHandle mt = MethodHandles
+               .lookup()
+               .findStatic(Authenticator.class, "getDefault", MethodType.methodType(Authenticator.class));
+            removeInternal((Authenticator)mt.invoke());
+        } catch (final NoSuchMethodException | IllegalAccessException ex) {
+            removeInternal();
+        } catch (Throwable e) {
+            //ignore
+        }
+    }
+    
+    private void removeInternal(final Authenticator def) {
+        try {
+            if (def == this) {
+                //this is at the root of any chain of authenticators
+                Authenticator.setDefault(wrapped);
+            } else {
+                removeFromChain(def);
+            }
+        } catch (Throwable t) {
+            //ignore
+        }
+    }
+    
+    private void removeInternal() {
+        try {
+            for (final Field f : Authenticator.class.getDeclaredFields()) {
+                if (f.getType().equals(Authenticator.class)) {
+                    try {
+                        f.setAccessible(true);
+                        Authenticator o = (Authenticator) f.get(null);
+                        if (o == this) {
+                            //this is at the root of any chain of authenticators
+                            Authenticator.setDefault(wrapped);
+                        } else {
+                            removeFromChain(o);
+                        }
+                    } catch (Exception e) {
+                        //ignore
+                    }
+                }
+            }
+        } catch (Throwable t) {
+            //ignore
+        }
+    }
+
+    private void removeFromChain(Authenticator a) {
+        try {
+            if (a.getClass().getName().equals(ReferencingAuthenticator.class.getName())) {
+                //multiple referencing authenticators, we can remove ourself
+                Field f2 = a.getClass().getDeclaredField("wrapped");
+                f2.setAccessible(true);
+                Authenticator a2 = (Authenticator) f2.get(a);
+                if (a2 == this) {
+                    f2.set(a, wrapped);
+                } else {
+                    removeFromChain(a2);
+                }
+            }
+        } catch (Throwable t) {
+            //ignore
+        }
+    }
+
+    PasswordAuthentication tryWith(Authenticator a) throws Exception {
+        if (a == null) {
+            return null;
+        }
+        
+        try {
+            // Try Authenticator.requestPasswordAuthentication() first, JDK9+
+            final MethodHandle mt = MethodHandles
+               .lookup()
+               .findStatic(Authenticator.class, "requestPasswordAuthentication", 
+                   MethodType.methodType(PasswordAuthentication.class, new Class<?>[] {
+                       Authenticator.class,
+                       String.class,
+                       InetAddress.class,
+                       int.class,
+                       String.class,
+                       String.class,
+                       String.class,
+                       URL.class,
+                       RequestorType.class
+                   }));
+    
+            return (PasswordAuthentication)mt.invoke(a, getRequestingHost(), getRequestingSite(), 
+                getRequestingPort(), getRequestingProtocol(), getRequestingPrompt(), getRequestingScheme(), 
+                    getRequestingURL(), getRequestorType());
+        } catch (final NoSuchMethodException | IllegalAccessException ex) {
+            return tryWithInternal(a);
+        } catch (final Throwable ex) {
+            if (ex instanceof Exception) {
+                throw (Exception)ex;
+            } else {
+                throw new Exception(ex);
+            }
+        }
+    }
+    
+    private PasswordAuthentication tryWithInternal(Authenticator a) throws Exception {
+        if (a == null) {
+            return null;
+        }
+        final Field[] fields;
+        if (SKIPCHECK) {
+            fields = Authenticator.class.getDeclaredFields();
+        } else {
+            fields = AccessController.doPrivileged(
+                    (PrivilegedAction<Field[]>) () -> Authenticator.class.getDeclaredFields());
+
+        }
+
+        for (final Field f : fields) {
+            if (!Modifier.isStatic(f.getModifiers())) {
+                f.setAccessible(true);
+                Object o = f.get(this);
+                f.set(a, o);
+            }
+        }
+        Method method;
+        if (SKIPCHECK) {
+            method = Authenticator.class.getDeclaredMethod("getPasswordAuthentication");
+            method.setAccessible(true);
+        } else {
+            method = AccessController.doPrivileged(
+                    (PrivilegedAction<Method>) () -> {
+                try {
+                    return Authenticator.class.getDeclaredMethod("getPasswordAuthentication");
+                } catch (NoSuchMethodException e) {
+                    throw new RuntimeException(e);
+                }
+            });
+            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
+                method.setAccessible(true);
+                return null;
+            });
+        }
+
+        return (PasswordAuthentication) method.invoke(a);
+    }
+}
\ No newline at end of file
diff --git a/transform/src/patch/java/org/apache/cxf/transport/http/Servlet3ContinuationProvider.java b/transform/src/patch/java/org/apache/cxf/transport/http/Servlet3ContinuationProvider.java
new file mode 100644
index 0000000..f593a3f
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/transport/http/Servlet3ContinuationProvider.java
@@ -0,0 +1,281 @@
+/**
+ * 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.transport.http;
+
+import java.io.IOException;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.util.PropertyUtils;
+import org.apache.cxf.continuations.Continuation;
+import org.apache.cxf.continuations.ContinuationCallback;
+import org.apache.cxf.continuations.ContinuationProvider;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.PhaseInterceptorChain;
+
+/**
+ *
+ */
+public class Servlet3ContinuationProvider implements ContinuationProvider {
+    static final boolean IS_31;
+    static {
+        boolean is31;
+        try {
+            ClassLoaderUtils.loadClass("javax.servlet.WriteListener", HttpServletRequest.class);
+            is31 = true;
+        } catch (Throwable t) {
+            is31 = false;
+        }
+        IS_31 = is31;
+    }
+    
+    HttpServletRequest req;
+    HttpServletResponse resp;
+    Message inMessage;
+    Servlet3Continuation continuation;
+
+    public Servlet3ContinuationProvider(HttpServletRequest req,
+                                        HttpServletResponse resp,
+                                        Message inMessage) {
+        this.inMessage = inMessage;
+        this.req = req;
+        this.resp = resp;
+    }
+
+    public void complete() {
+        if (continuation != null) {
+            continuation.reset();
+            continuation = null;
+        }
+    }
+
+
+    /** {@inheritDoc}*/
+    public Continuation getContinuation() {
+        if (inMessage.getExchange().isOneWay()) {
+            return null;
+        }
+
+        if (continuation == null) {
+            continuation = IS_31 ? new Servlet31Continuation() : new Servlet3Continuation();
+        } else {
+            continuation.startAsyncAgain();
+        }
+        return continuation;
+    }
+
+    public class Servlet3Continuation implements Continuation, AsyncListener {
+        private static final String BLOCK_RESTART = "org.apache.cxf.continuation.block.restart";
+        AsyncContext context;
+        volatile boolean isNew = true;
+        volatile boolean isResumed;
+        volatile boolean isPending;
+        volatile boolean isComplete;
+        volatile boolean isTimeout;
+        volatile Object obj;
+        private ContinuationCallback callback;
+        private boolean blockRestart;
+        
+        public Servlet3Continuation() {
+            req.setAttribute(AbstractHTTPDestination.CXF_CONTINUATION_MESSAGE,
+                             inMessage.getExchange().getInMessage());
+            callback = inMessage.getExchange().get(ContinuationCallback.class);
+            blockRestart = PropertyUtils.isTrue(inMessage.getContextualProperty(BLOCK_RESTART));
+            context = req.startAsync();
+            context.addListener(this);
+        }
+
+        void startAsyncAgain() {
+            if (blockRestart) {
+                return;
+            }
+            AsyncContext old = context;
+            try {
+                context = req.startAsync();
+                context.addListener(this);
+                isComplete = false;
+            } catch (IllegalStateException ex) {
+                context = old;
+            }
+        }
+
+        public boolean suspend(long timeout) {
+            if (isPending && timeout != 0) {
+                long currentTimeout = context.getTimeout();
+                timeout = currentTimeout + timeout;
+            } else {
+                isPending = true;
+            }
+            isNew = false;
+            isResumed = false;
+
+            context.setTimeout(timeout);
+
+            updateMessageForSuspend();
+            return true;
+        }
+        protected void updateMessageForSuspend() {
+            inMessage.getExchange().getInMessage().getInterceptorChain().suspend();
+        }
+        public void redispatch() {
+            if (!isComplete) {
+                context.dispatch();
+            }
+        }
+        public void resume() {
+            isResumed = true;
+            isPending = false;
+            redispatch();
+        }
+
+        public void reset() {
+            isComplete = true;
+            try {
+                context.complete();
+            } catch (IllegalStateException ex) {
+                // ignore
+            }
+            isPending = false;
+            isResumed = false;
+            isNew = false;
+            isTimeout = false;
+            obj = null;
+            if (callback != null) {
+                final Exception ex = inMessage.getExchange().get(Exception.class);
+                Throwable cause = isCausedByIO(ex);
+
+                if (cause != null && isClientDisconnected(cause)) {
+                    callback.onDisconnect();
+                }
+            }
+        }
+
+        public boolean isNew() {
+            return isNew;
+        }
+
+        public boolean isPending() {
+            return isPending;
+        }
+
+        public boolean isResumed() {
+            return isResumed;
+        }
+
+        public Object getObject() {
+            return obj;
+        }
+
+        public void setObject(Object o) {
+            obj = o;
+        }
+
+        public void onComplete(AsyncEvent event) throws IOException {
+            inMessage.getExchange().getInMessage()
+                .remove(AbstractHTTPDestination.CXF_CONTINUATION_MESSAGE);
+            if (callback != null) {
+                final Exception ex = inMessage.getExchange().get(Exception.class);
+                if (ex == null) {
+                    callback.onComplete();
+                } else {
+                    callback.onError(ex);
+                }
+            }
+            isResumed = false;
+            isPending = false;
+        }
+        public void onError(AsyncEvent event) throws IOException {
+            if (callback != null) {
+                callback.onError(event.getThrowable());
+            }
+        }
+        public void onStartAsync(AsyncEvent event) throws IOException {
+        }
+        public void onTimeout(AsyncEvent event) throws IOException {
+            resume();
+            isTimeout = true;
+        }
+
+        private Throwable isCausedByIO(final Exception ex) {
+            Throwable cause = ex;
+
+            while (cause != null && !(cause instanceof IOException)) {
+                cause = cause.getCause();
+            }
+
+            return cause;
+        }
+
+        private boolean isClientDisconnected(Throwable ex) {
+            String exName = (String)inMessage.getContextualProperty("disconnected.client.exception.class");
+            if (exName != null) {
+                return exName.equals(IOException.class.getName()) || exName.equals(ex.getClass().getName());
+            }
+            return false;
+        }
+
+        @Override
+        public boolean isReadyForWrite() {
+            return true;
+        }
+
+        protected ServletOutputStream getOutputStream() {
+            try {
+                return resp.getOutputStream();
+            } catch (IOException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        @Override
+        public boolean isTimeout() {
+            return isTimeout;
+        }
+    }
+    public class Servlet31Continuation extends Servlet3Continuation {
+        public Servlet31Continuation() {
+        }
+
+        @Override
+        protected void updateMessageForSuspend() {
+            Message currentMessage = PhaseInterceptorChain.getCurrentMessage();
+            if (currentMessage.get(WriteListener.class) != null) {
+                // CXF Continuation WriteListener will likely need to be introduced
+                // for NIO supported with non-Servlet specific mechanisms
+                getOutputStream().setWriteListener(currentMessage.get(WriteListener.class));
+                currentMessage.getInterceptorChain().suspend();
+            } else {
+                inMessage.getExchange().getInMessage().getInterceptorChain().suspend();
+            }
+        }
+        
+        @Override
+        public boolean isReadyForWrite() {
+            return getOutputStream().isReady();
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/transport/http/URLConnectionHTTPConduit.java b/transform/src/patch/java/org/apache/cxf/transport/http/URLConnectionHTTPConduit.java
new file mode 100644
index 0000000..e31b2c5
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/transport/http/URLConnectionHTTPConduit.java
@@ -0,0 +1,439 @@
+/**
+ * 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.transport.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.ProtocolException;
+import java.net.Proxy;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.logging.Level;
+
+import javax.net.ssl.HttpsURLConnection;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.util.ReflectionUtil;
+import org.apache.cxf.common.util.SystemPropertyAction;
+import org.apache.cxf.configuration.jsse.TLSClientParameters;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.io.CacheAndWriteOutputStream;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageUtils;
+import org.apache.cxf.service.model.EndpointInfo;
+import org.apache.cxf.transport.https.HttpsURLConnectionFactory;
+import org.apache.cxf.transport.https.HttpsURLConnectionInfo;
+import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
+import org.apache.cxf.ws.addressing.EndpointReferenceType;
+
+/**
+ *
+ */
+public class URLConnectionHTTPConduit extends HTTPConduit {
+    public static final String HTTPURL_CONNECTION_METHOD_REFLECTION = "use.httpurlconnection.method.reflection";
+    public static final String SET_REASON_PHRASE_NOT_NULL = "set.reason.phrase.not.null";
+
+    private static final boolean DEFAULT_USE_REFLECTION;
+    private static final boolean SET_REASON_PHRASE;
+    static {
+        DEFAULT_USE_REFLECTION =
+            Boolean.valueOf(SystemPropertyAction.getProperty(HTTPURL_CONNECTION_METHOD_REFLECTION, "false"));
+        SET_REASON_PHRASE = 
+            Boolean.valueOf(SystemPropertyAction.getProperty(SET_REASON_PHRASE_NOT_NULL, "false"));
+    }
+
+    /**
+     * This field holds the connection factory, which primarily is used to
+     * factor out SSL specific code from this implementation.
+     * <p>
+     * This field is "protected" to facilitate some contrived UnitTesting so
+     * that an extended class may alter its value with an EasyMock URLConnection
+     * Factory.
+     */
+    protected HttpsURLConnectionFactory connectionFactory;
+
+
+    public URLConnectionHTTPConduit(Bus b, EndpointInfo ei) throws IOException {
+        super(b, ei);
+        connectionFactory = new HttpsURLConnectionFactory();
+        CXFAuthenticator.addAuthenticator();
+    }
+
+    public URLConnectionHTTPConduit(Bus b, EndpointInfo ei, EndpointReferenceType t) throws IOException {
+        super(b, ei, t);
+        connectionFactory = new HttpsURLConnectionFactory();
+        CXFAuthenticator.addAuthenticator();
+    }
+
+    /**
+     * Close the conduit
+     */
+    public void close() {
+        super.close();
+        if (defaultAddress != null) {
+            try {
+                URLConnection connect = defaultAddress.getURL().openConnection();
+                if (connect instanceof HttpURLConnection) {
+                    ((HttpURLConnection)connect).disconnect();
+                }
+            } catch (IOException ex) {
+                //ignore
+            }
+            //defaultEndpointURL = null;
+        }
+    }
+
+    private HttpURLConnection createConnection(Message message, Address address, HTTPClientPolicy csPolicy)
+        throws IOException {
+        URL url = address.getURL();
+        URI uri = address.getURI();
+        Proxy proxy = proxyFactory.createProxy(csPolicy, uri);
+        message.put("http.scheme", uri.getScheme());
+        // check tlsClientParameters from message header
+        TLSClientParameters clientParameters = message.get(TLSClientParameters.class);
+        if (clientParameters == null) {
+            clientParameters = tlsClientParameters;
+        }
+        return connectionFactory.createConnection(clientParameters,
+                                                  proxy != null ? proxy : address.getDefaultProxy(), url);
+    }
+    protected void setupConnection(Message message, Address address, HTTPClientPolicy csPolicy) throws IOException {
+        HttpURLConnection connection = createConnection(message, address, csPolicy);
+        connection.setDoOutput(true);
+
+        int ctimeout = determineConnectionTimeout(message, csPolicy);
+        connection.setConnectTimeout(ctimeout);
+
+        int rtimeout = determineReceiveTimeout(message, csPolicy);
+        connection.setReadTimeout(rtimeout);
+
+        connection.setUseCaches(false);
+        // We implement redirects in this conduit. We do not
+        // rely on the underlying URLConnection implementation
+        // because of trust issues.
+        connection.setInstanceFollowRedirects(false);
+
+        // If the HTTP_REQUEST_METHOD is not set, the default is "POST".
+        String httpRequestMethod =
+            (String)message.get(Message.HTTP_REQUEST_METHOD);
+        if (httpRequestMethod == null) {
+            httpRequestMethod = "POST";
+            message.put(Message.HTTP_REQUEST_METHOD, "POST");
+        }
+        try {
+            connection.setRequestMethod(httpRequestMethod);
+        } catch (java.net.ProtocolException ex) {
+            boolean b = MessageUtils.getContextualBoolean(message,
+                                                          HTTPURL_CONNECTION_METHOD_REFLECTION,
+                                                          DEFAULT_USE_REFLECTION);
+            if (b) {
+                try {
+                    java.lang.reflect.Field f = ReflectionUtil.getDeclaredField(HttpURLConnection.class, "method");
+                    if (connection instanceof HttpsURLConnection) {
+                        try {
+                            java.lang.reflect.Field f2 = ReflectionUtil.getDeclaredField(connection.getClass(),
+                                                                                         "delegate");
+                            Object c = ReflectionUtil.setAccessible(f2).get(connection);
+                            if (c instanceof HttpURLConnection) {
+                                ReflectionUtil.setAccessible(f).set(c, httpRequestMethod);
+                            }
+
+                            f2 = ReflectionUtil.getDeclaredField(c.getClass(), "httpsURLConnection");
+                            HttpsURLConnection c2 = (HttpsURLConnection)ReflectionUtil.setAccessible(f2)
+                                    .get(c);
+
+                            ReflectionUtil.setAccessible(f).set(c2, httpRequestMethod);
+                        } catch (Throwable t) {
+                            //ignore
+                            logStackTrace(t);
+                        }
+                    }
+                    ReflectionUtil.setAccessible(f).set(connection, httpRequestMethod);
+                    message.put(HTTPURL_CONNECTION_METHOD_REFLECTION, true);
+                } catch (Throwable t) {
+                    logStackTrace(t);
+                    throw ex;
+                }
+            } else {
+                throw ex;
+            }
+        }
+
+        // We place the connection on the message to pick it up
+        // in the WrappedOutputStream.
+        message.put(KEY_HTTP_CONNECTION, connection);
+        message.put(KEY_HTTP_CONNECTION_ADDRESS, address);
+    }
+
+
+    protected OutputStream createOutputStream(Message message,
+                                              boolean needToCacheRequest,
+                                              boolean isChunking,
+                                              int chunkThreshold) throws IOException {
+        HttpURLConnection connection = (HttpURLConnection)message.get(KEY_HTTP_CONNECTION);
+
+        if (isChunking && chunkThreshold <= 0) {
+            chunkThreshold = 0;
+            connection.setChunkedStreamingMode(-1);
+        }
+        try {
+            return new URLConnectionWrappedOutputStream(message, connection,
+                                           needToCacheRequest,
+                                           isChunking,
+                                           chunkThreshold,
+                                           getConduitName());
+        } catch (URISyntaxException e) {
+            throw new IOException(e);
+        }
+    }
+
+    private static URI computeURI(Message message, HttpURLConnection connection) throws URISyntaxException {
+        Address address = (Address)message.get(KEY_HTTP_CONNECTION_ADDRESS);
+        return address != null ? address.getURI() : connection.getURL().toURI();
+    }
+
+    class URLConnectionWrappedOutputStream extends WrappedOutputStream {
+        HttpURLConnection connection;
+        URLConnectionWrappedOutputStream(Message message, HttpURLConnection connection,
+                                         boolean needToCacheRequest, boolean isChunking,
+                                         int chunkThreshold, String conduitName) throws URISyntaxException {
+            super(message, needToCacheRequest, isChunking,
+                  chunkThreshold, conduitName,
+                  computeURI(message, connection));
+            this.connection = connection;
+        }
+
+        // This construction makes extending the HTTPConduit more easier
+        protected URLConnectionWrappedOutputStream(URLConnectionWrappedOutputStream wos) {
+            super(wos);
+            this.connection = wos.connection;
+        }
+        private OutputStream connectAndGetOutputStream(Boolean b) throws IOException {
+            OutputStream cout = null;
+
+            if (b != null && b) {
+                String method = connection.getRequestMethod();
+                connection.connect();
+                try {
+                    java.lang.reflect.Field f = ReflectionUtil.getDeclaredField(HttpURLConnection.class, "method");
+                    ReflectionUtil.setAccessible(f).set(connection, "POST");
+                    cout = connection.getOutputStream();
+                    ReflectionUtil.setAccessible(f).set(connection, method);
+                } catch (Throwable t) {
+                    logStackTrace(t);
+                }
+
+            } else {
+                cout = connection.getOutputStream();
+            }
+            return cout;
+        }
+        protected void setupWrappedStream() throws IOException {
+            // If we need to cache for retransmission, store data in a
+            // CacheAndWriteOutputStream. Otherwise write directly to the output stream.
+            OutputStream cout;
+            try {
+                try {
+//                    cout = connection.getOutputStream();
+                    if (System.getSecurityManager() != null) {
+                        try {
+                            cout = AccessController.doPrivileged(new PrivilegedExceptionAction<OutputStream>() {
+                                @Override
+                                public OutputStream run() throws IOException {
+                                    return connection.getOutputStream();
+                                }
+                            });
+                        } catch (PrivilegedActionException e) {
+                            throw (IOException) e.getException();
+                        }
+                    } else {
+                        cout = connection.getOutputStream();
+                    }
+                } catch (ProtocolException pe) {
+                    Boolean b = (Boolean)outMessage.get(HTTPURL_CONNECTION_METHOD_REFLECTION);
+                    cout = connectAndGetOutputStream(b);
+                }
+            } catch (Exception e) {
+                if ("Socket Closed".equals(e.getMessage())
+                    || "HostnameVerifier, socket reset for TTL".equals(e.getMessage())) {
+                    connection.connect();
+                    cout = connectAndGetOutputStream((Boolean)outMessage.get(HTTPURL_CONNECTION_METHOD_REFLECTION));
+                } else {
+                    throw e;
+                }
+            }
+            if (cachingForRetransmission) {
+                cachedStream =
+                    new CacheAndWriteOutputStream(cout);
+                wrappedStream = cachedStream;
+            } else {
+                wrappedStream = cout;
+            }
+        }
+
+        @Override
+        public void thresholdReached() {
+            if (chunking) {
+                connection.setChunkedStreamingMode(
+                    URLConnectionHTTPConduit.this.getClient().getChunkLength());
+            }
+        }
+        @Override
+        protected void onFirstWrite() throws IOException {
+            super.onFirstWrite();
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.fine("Sending "
+                    + connection.getRequestMethod()
+                    + " Message with Headers to "
+                    + url
+                    + " Conduit :"
+                    + conduitName
+                    + "\n");
+            }
+        }
+        protected void setProtocolHeaders() throws IOException {
+            new Headers(outMessage).setProtocolHeadersInConnection(connection);
+        }
+
+        protected HttpsURLConnectionInfo getHttpsURLConnectionInfo() throws IOException {
+            connection.connect();
+            return new HttpsURLConnectionInfo(connection);
+        }
+        protected void updateResponseHeaders(Message inMessage) {
+            Headers h = new Headers(inMessage);
+            h.readFromConnection(connection);
+            inMessage.put(Message.CONTENT_TYPE, connection.getContentType());
+            cookies.readFromHeaders(h);
+        }
+        protected void handleResponseAsync() throws IOException {
+            handleResponseOnWorkqueue(true, false);
+        }
+        protected void updateCookiesBeforeRetransmit() {
+            Headers h = new Headers();
+            h.readFromConnection(connection);
+            cookies.readFromHeaders(h);
+        }
+
+        protected InputStream getInputStream() throws IOException {
+            InputStream in;
+            if (getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST) {
+                in = connection.getErrorStream();
+                if (in == null) {
+                    try {
+                        // just in case - but this will most likely cause an exception
+                        in = connection.getInputStream();
+                    } catch (IOException ex) {
+                        // ignore
+                    }
+                }
+            } else {
+                in = connection.getInputStream();
+            }
+            return in;
+        }
+
+
+        protected void closeInputStream() throws IOException {
+            //try and consume any content so that the connection might be reusable
+            InputStream ins = connection.getErrorStream();
+            if (ins == null) {
+                ins = connection.getInputStream();
+            }
+            if (ins != null) {
+                IOUtils.consume(ins);
+                ins.close();
+            }
+        }
+        protected int getResponseCode() throws IOException {
+            try {
+                return AccessController.doPrivileged(new PrivilegedExceptionAction<Integer>() {
+
+                    @Override
+                    public Integer run() throws IOException {
+                        return connection.getResponseCode();
+                    } });
+            } catch (PrivilegedActionException e) {
+                Throwable t = e.getCause();
+                if (t instanceof IOException) {
+                    throw (IOException) t;
+                }
+                throw new RuntimeException(t);
+            }
+        }
+        protected String getResponseMessage() throws IOException {
+            boolean b = MessageUtils.getContextualBoolean(this.outMessage,
+                                                          SET_REASON_PHRASE_NOT_NULL,
+                                                          SET_REASON_PHRASE);
+            if (connection.getResponseMessage() == null && b) {
+                //some http server like tomcat 8.5+ won't return the
+                //reason phrase in response, return a informative value
+                //to tell user no reason phrase in the response instead of null
+                return "no reason phrase in the response";
+            }
+            return connection.getResponseMessage();
+        }
+        protected InputStream getPartialResponse() throws IOException {
+            return ChunkedUtil.getPartialResponse(connection, connection.getResponseCode());
+        }
+        protected boolean usingProxy() {
+            return connection.usingProxy();
+        }
+        protected void setFixedLengthStreamingMode(int i) {
+            // [CXF-6227] do not call connection.setFixedLengthStreamingMode(i)
+            // to prevent https://bugs.openjdk.java.net/browse/JDK-8044726
+        }
+        protected void handleNoOutput() throws IOException {
+            if ("POST".equals(getMethod())) {
+                connection.getOutputStream().close();
+            }
+        }
+        protected void setupNewConnection(String newURL) throws IOException {
+            HTTPClientPolicy cp = getClient(outMessage);
+            Address address;
+            try {
+                if (defaultAddress.getString().equals(newURL)) {
+                    address = defaultAddress;
+                } else {
+                    address = new Address(newURL);
+                }
+            } catch (URISyntaxException e) {
+                throw new IOException(e);
+            }
+            setupConnection(outMessage, address, cp);
+            this.url = address.getURI();
+            connection = (HttpURLConnection)outMessage.get(KEY_HTTP_CONNECTION);
+        }
+
+        @Override
+        protected void retransmitStream() throws IOException {
+            Boolean b = (Boolean)outMessage.get(HTTPURL_CONNECTION_METHOD_REFLECTION);
+            OutputStream out = connectAndGetOutputStream(b);
+            cachedStream.writeCacheTo(out);
+        }
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/transport/http/auth/HttpAuthHeader.java b/transform/src/patch/java/org/apache/cxf/transport/http/auth/HttpAuthHeader.java
new file mode 100644
index 0000000..ee72518
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/transport/http/auth/HttpAuthHeader.java
@@ -0,0 +1,154 @@
+/**
+ * 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.transport.http.auth;
+
+import java.io.IOException;
+import java.io.StreamTokenizer;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public final class HttpAuthHeader {
+    public static final String AUTH_TYPE_BASIC = "Basic";
+    public static final String AUTH_TYPE_DIGEST = "Digest";
+    public static final String AUTH_TYPE_NEGOTIATE = "Negotiate";
+
+    private final String fullHeader;
+    private final String authType;
+    private final String fullContent;
+    private final Map<String, String> params;
+
+    public HttpAuthHeader(String fullHeader) {
+        this.fullHeader = (fullHeader == null) ? "" : fullHeader;
+        int spacePos = this.fullHeader.indexOf(' ');
+        if (spacePos == -1) {
+            this.authType = this.fullHeader;
+            this.fullContent = "";
+        } else {
+            this.authType = this.fullHeader.substring(0, spacePos);
+            this.fullContent = this.fullHeader.substring(spacePos + 1);
+        }
+        this.params = parseHeader();
+    }
+    public HttpAuthHeader(List<String> params) {
+        fullHeader = String.join(", ", params);
+        int spacePos = this.fullHeader.indexOf(' ');
+        if (spacePos == -1) {
+            this.authType = this.fullHeader;
+            this.fullContent = "";
+        } else {
+            this.authType = this.fullHeader.substring(0, spacePos);
+            this.fullContent = this.fullHeader.substring(spacePos + 1);
+        }
+        this.params = parseHeader();
+    }
+
+    public HttpAuthHeader(String authType, Map<String, String> params) {
+        this.authType = authType;
+        this.params = params;
+        this.fullContent = paramsToString();
+        this.fullHeader = authType + " " + fullContent;
+    }
+
+    private String paramsToString() {
+        StringBuilder builder = new StringBuilder();
+        boolean first = true;
+        for (Map.Entry<String, String> entry : params.entrySet()) {
+            String param = entry.getValue();
+            if (param != null) {
+                if (!first) {
+                    builder.append(", ");
+                }
+                if ("nc".equals(entry.getKey())
+                    || "qop".equals(entry.getKey())
+                    || "algorithm".equals(entry.getKey())) {
+                    builder.append(entry.getKey()).append('=').append(param);
+                } else {
+                    builder.append(entry.getKey()).append("=\"").append(param).append('"');
+                }
+                first = false;
+            }
+        }
+        return builder.toString();
+    }
+
+    private Map<String, String> parseHeader() {
+        Map<String, String> map = new HashMap<>();
+        try {
+            StreamTokenizer tok = new StreamTokenizer(new StringReader(this.fullContent)) {
+                @Override
+                public void parseNumbers() {
+                    // skip parse numbers
+                    wordChars('0', '9');
+                    wordChars('.', '.');
+                    wordChars('-', '-');
+                }
+            };
+            tok.whitespaceChars('=', '=');
+            tok.whitespaceChars(',', ',');
+
+            while (tok.nextToken() != StreamTokenizer.TT_EOF) {
+                map.put(tok.sval, tok.nextToken() != StreamTokenizer.TT_EOF ? tok.sval : null);
+            }
+        } catch (IOException ex) {
+            //ignore can't happen for StringReader
+        }
+        return map;
+    }
+
+    /**
+     * Extracts the authorization realm from the
+     * "WWW-Authenticate" Http response header.
+     *
+     * @return The realm, or null if it is non-existent.
+     */
+    public String getRealm() {
+        return params.get("realm");
+    }
+
+    public boolean authTypeIsDigest() {
+        return AUTH_TYPE_DIGEST.equals(this.authType);
+    }
+
+    public boolean authTypeIsBasic() {
+        return AUTH_TYPE_BASIC.equals(this.authType);
+    }
+
+    public boolean authTypeIsNegotiate() {
+        return AUTH_TYPE_NEGOTIATE.equals(this.authType);
+    }
+
+    public String getAuthType() {
+        return authType;
+    }
+
+    public String getFullContent() {
+        return fullContent;
+    }
+
+    public String getFullHeader() {
+        return this.fullHeader;
+    }
+
+    public Map<String, String> getParams() {
+        return params;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/transport/https/HttpsURLConnectionFactory.java b/transform/src/patch/java/org/apache/cxf/transport/https/HttpsURLConnectionFactory.java
new file mode 100644
index 0000000..d42549d
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/transport/https/HttpsURLConnectionFactory.java
@@ -0,0 +1,247 @@
+/**
+ * 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.transport.https;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.net.HttpURLConnection;
+import java.net.Proxy;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.GeneralSecurityException;
+import java.security.PrivilegedAction;
+import java.util.logging.Handler;
+import java.util.logging.Logger;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.ReflectionInvokationHandler;
+import org.apache.cxf.common.util.ReflectionUtil;
+import org.apache.cxf.configuration.jsse.SSLUtils;
+import org.apache.cxf.configuration.jsse.TLSClientParameters;
+
+
+/**
+ * This HttpsURLConnectionFactory implements the HttpURLConnectionFactory
+ * for using the given SSL Policy to configure TLS connections for "https:"
+ * URLs.
+ */
+public class HttpsURLConnectionFactory {
+
+    /**
+     * This constant holds the URL Protocol Identifier for HTTPS
+     */
+    public static final String HTTPS_URL_PROTOCOL_ID = "https";
+
+    private static final Logger LOG =
+        LogUtils.getL7dLogger(HttpsURLConnectionFactory.class);
+
+    private static boolean weblogicWarned;
+
+    /**
+     * Cache the last SSLContext to avoid recreation
+     */
+    SSLSocketFactory socketFactory;
+    int lastTlsHash;
+
+    /**
+     * This constructor initialized the factory with the configured TLS
+     * Client Parameters for the HTTPConduit for which this factory is used.
+     */
+    public HttpsURLConnectionFactory() {
+    }
+
+    /**
+     * Create a HttpURLConnection, proxified if necessary.
+     *
+     *
+     * @param proxy This parameter is non-null if connection should be proxied.
+     * @param url   The target URL.
+     *
+     * @return The HttpURLConnection for the given URL.
+     * @throws IOException
+     */
+    public HttpURLConnection createConnection(TLSClientParameters tlsClientParameters,
+            Proxy proxy, URL url) throws IOException {
+
+        HttpURLConnection connection =
+            (HttpURLConnection) (proxy != null
+                                   ? url.openConnection(proxy)
+                                   : url.openConnection());
+        if (HTTPS_URL_PROTOCOL_ID.equals(url.getProtocol())) {
+
+            if (tlsClientParameters == null) {
+                tlsClientParameters = new TLSClientParameters();
+            }
+
+            try {
+                decorateWithTLS(tlsClientParameters, connection);
+            } catch (Throwable ex) {
+                throw new IOException("Error while initializing secure socket", ex);
+            }
+        }
+
+        return connection;
+    }
+
+    /**
+     * This method assigns the various TLS parameters on the HttpsURLConnection
+     * from the TLS Client Parameters. Connection parameter is of supertype HttpURLConnection,
+     * which allows internal cast to potentially divergent subtype (https) implementations.
+     */
+    protected synchronized void decorateWithTLS(TLSClientParameters tlsClientParameters,
+            HttpURLConnection connection) throws GeneralSecurityException {
+
+
+        int hash = tlsClientParameters.hashCode();
+        if (hash != lastTlsHash) {
+            lastTlsHash = hash;
+            socketFactory = null;
+        }
+
+        // always reload socketFactory from HttpsURLConnection.defaultSSLSocketFactory and
+        // tlsClientParameters.sslSocketFactory to allow runtime configuration change
+        if (tlsClientParameters.isUseHttpsURLConnectionDefaultSslSocketFactory()) {
+            socketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
+
+        } else if (tlsClientParameters.getSSLSocketFactory() != null) {
+            // see if an SSLSocketFactory was set. This allows easy interop
+            // with not-yet-commons-ssl.jar, or even just people who like doing their
+            // own JSSE.
+            socketFactory = tlsClientParameters.getSSLSocketFactory();
+
+        } else if (socketFactory == null) {
+
+            final SSLContext ctx;
+            if (tlsClientParameters.getSslContext() != null) {
+                // Use the SSLContext which was set
+                ctx = tlsClientParameters.getSslContext();
+            } else {
+                // Create socketfactory with tlsClientParameters's Trust Managers, Key Managers, etc
+                ctx = org.apache.cxf.transport.https.SSLUtils.getSSLContext(tlsClientParameters);
+            }
+
+            String[] cipherSuites =
+                SSLUtils.getCiphersuitesToInclude(tlsClientParameters.getCipherSuites(),
+                                                  tlsClientParameters.getCipherSuitesFilter(),
+                                                  ctx.getSocketFactory().getDefaultCipherSuites(),
+                                                  SSLUtils.getSupportedCipherSuites(ctx),
+                                                  LOG);
+
+            // The SSLSocketFactoryWrapper enables certain cipher suites from the policy.
+            String protocol = tlsClientParameters.getSecureSocketProtocol() != null ? tlsClientParameters
+                .getSecureSocketProtocol() : ctx.getProtocol();
+            socketFactory = new SSLSocketFactoryWrapper(ctx.getSocketFactory(), cipherSuites,
+                                                        protocol);
+            //recalc the hashcode since some of the above MAY have changed the tlsClientParameters
+            lastTlsHash = tlsClientParameters.hashCode();
+        } else {
+           // ssl socket factory already initialized, reuse it to benefit of keep alive
+        }
+
+
+        HostnameVerifier verifier = org.apache.cxf.transport.https.SSLUtils
+            .getHostnameVerifier(tlsClientParameters);
+
+        if (connection instanceof HttpsURLConnection) {
+            // handle the expected case (javax.net.ssl)
+            HttpsURLConnection conn = (HttpsURLConnection) connection;
+            conn.setHostnameVerifier(verifier);
+            AccessController.doPrivileged(new PrivilegedAction<Void>() {
+
+                @Override
+                public Void run() {
+                    conn.setSSLSocketFactory(socketFactory);
+                    return null;
+                } });
+        } else {
+            // handle the deprecated sun case and other possible hidden API's
+            // that are similar to the Sun cases
+            try {
+                Method method = connection.getClass().getMethod("getHostnameVerifier");
+
+                InvocationHandler handler = new ReflectionInvokationHandler(verifier) {
+                    public Object invoke(Object proxy,
+                                         Method method,
+                                         Object[] args) throws Throwable {
+                        try {
+                            return super.invoke(proxy, method, args);
+                        } catch (Exception ex) {
+                            return false;
+                        }
+                    }
+                };
+                Object proxy = java.lang.reflect.Proxy.newProxyInstance(this.getClass().getClassLoader(),
+                                                                        new Class[] {method.getReturnType()},
+                                                                        handler);
+
+                method = connection.getClass().getMethod("setHostnameVerifier", method.getReturnType());
+                method.invoke(connection, proxy);
+            } catch (Exception ex) {
+                //Ignore this one
+            }
+            try {
+                Method getSSLSocketFactory = connection.getClass().getMethod("getSSLSocketFactory");
+                Method setSSLSocketFactory = connection.getClass()
+                    .getMethod("setSSLSocketFactory", getSSLSocketFactory.getReturnType());
+                if (getSSLSocketFactory.getReturnType().isInstance(socketFactory)) {
+                    setSSLSocketFactory.invoke(connection, socketFactory);
+                } else {
+                    //need to see if we can create one - mostly the weblogic case.   The
+                    //weblogic SSLSocketFactory has a protected constructor that can take
+                    //a JSSE SSLSocketFactory so we'll try and use that
+                    Constructor<?> c = getSSLSocketFactory.getReturnType()
+                        .getDeclaredConstructor(SSLSocketFactory.class);
+                    ReflectionUtil.setAccessible(c);
+                    setSSLSocketFactory.invoke(connection, c.newInstance(socketFactory));
+                }
+            } catch (Exception ex) {
+                if (connection.getClass().getName().contains("weblogic")) {
+                    if (!weblogicWarned) {
+                        weblogicWarned = true;
+                        LOG.warning("Could not configure SSLSocketFactory on Weblogic.  "
+                                    + " Use the Weblogic control panel to configure the SSL settings.");
+                    }
+                    return;
+                }
+                //if we cannot set the SSLSocketFactory, we're in serious trouble.
+                throw new IllegalArgumentException("Error decorating connection class "
+                        + connection.getClass().getName(), ex);
+            }
+        }
+    }
+
+    /*
+     *  For development and testing only
+     */
+    protected void addLogHandler(Handler handler) {
+        LOG.addHandler(handler);
+    }
+
+}
+
+
+
diff --git a/transform/src/patch/java/org/apache/cxf/transport/https/HttpsURLConnectionInfo.java b/transform/src/patch/java/org/apache/cxf/transport/https/HttpsURLConnectionInfo.java
new file mode 100644
index 0000000..31bff30
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/transport/https/HttpsURLConnectionInfo.java
@@ -0,0 +1,168 @@
+/**
+ * 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.transport.https;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.security.Principal;
+import java.security.cert.Certificate;
+
+import javax.net.ssl.HttpsURLConnection;
+
+import org.apache.cxf.transport.http.HttpURLConnectionInfo;
+
+/**
+ * This class holds information about the HttpsURLConnection. This
+ * class should be used when the getURL().getProtocol() is "https".
+ */
+public class HttpsURLConnectionInfo extends HttpURLConnectionInfo {
+
+    /**
+     * This field contains the cipherSuite enabled in the
+     * HTTPS URLconnection.
+     */
+    protected String enabledCipherSuite;
+
+    /**
+     * This field contains the certificates that were used to
+     * authenticate the connection to the peer.
+     */
+    protected Certificate[] localCertificates;
+
+    /**
+     * This field contains the Principal that authenticated to the
+     * peer.
+     */
+    protected Principal localPrincipal;
+
+    /**
+     * This field contains the certificates the server presented
+     * to authenticate.
+     */
+    protected Certificate[] serverCertificates;
+
+    /**
+     * This field contains the Principal that represents the
+     * authenticated peer.
+     */
+    protected Principal peerPrincipal;
+
+
+    public HttpsURLConnectionInfo(URI uri,
+                                  String method,
+                                  String cipherSuite,
+                                  Certificate[] localCerts,
+                                  Principal principal,
+                                  Certificate[] serverCerts,
+                                  Principal peer) {
+        super(uri, method);
+        enabledCipherSuite = cipherSuite;
+        localCertificates = localCerts;
+        localPrincipal = principal;
+        serverCertificates = serverCerts;
+        peerPrincipal = peer;
+    }
+
+
+
+    /**
+     * This constructor is used to create the info object
+     * representing the this HttpsURLConnection. Connection parameter is
+     * of supertype HttpURLConnection, which allows internal cast to
+     * potentially divergent subtype (Https) implementations.
+     */
+    public HttpsURLConnectionInfo(HttpURLConnection connection)
+        throws IOException {
+        super(connection.getURL(), connection.getRequestMethod());
+        if (connection instanceof HttpsURLConnection) {
+            HttpsURLConnection conn = (HttpsURLConnection) connection;
+            enabledCipherSuite = conn.getCipherSuite();
+            localCertificates = conn.getLocalCertificates();
+            localPrincipal = conn.getLocalPrincipal();
+            serverCertificates = conn.getServerCertificates();
+            peerPrincipal = conn.getPeerPrincipal();
+        } else {
+            try {
+                Method method = connection.getClass().getMethod("getCipherSuite", (Class[]) null);
+                enabledCipherSuite = (String) method.invoke(connection, (Object[]) null);
+                method = connection.getClass().getMethod("getLocalCertificates", (Class[]) null);
+                localCertificates = (Certificate[]) method.invoke(connection, (Object[]) null);
+                method = connection.getClass().getMethod("getServerCertificates", (Class[]) null);
+                serverCertificates = (Certificate[]) method.invoke(connection, (Object[]) null);
+
+                //TODO Obtain localPrincipal and peerPrincipal using the com.sun.net.ssl api
+            } catch (Exception e) {
+                Throwable ex = e;
+                if (e instanceof InvocationTargetException) {
+                    ex = ((InvocationTargetException) e).getTargetException();
+                }
+                if (ex instanceof IOException) {
+                    throw (IOException) ex;
+                }
+                throw new IOException("Error constructing HttpsURLConnectionInfo "
+                                                  + "for connection class "
+                                                  + connection.getClass().getName(), ex);
+            }
+        }
+    }
+
+    /**
+     * This method returns the cipher suite employed in this
+     * HttpsURLConnection.
+     */
+    public String getEnabledCipherSuite() {
+        return enabledCipherSuite;
+    }
+
+    /**
+     * This method returns the certificates that were used to
+     * authenticate to the peer.
+     */
+    public Certificate[] getLocalCertificates() {
+        return localCertificates;
+    }
+
+    /**
+     * This method returns the Princpal that authenticated to
+     * the peer.
+     */
+    public Principal getLocalPrincipal() {
+        return localPrincipal;
+    }
+
+    /**
+     * This method returns the certificates presented by the
+     * peer for authentication.
+     */
+    public Certificate[] getServerCertificates() {
+        return serverCertificates;
+    }
+
+    /**
+     * This method returns the Principal that represents the
+     * authenticated peer.
+     */
+    public Principal getPeerPrincipal() {
+        return peerPrincipal;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/transport/servlet/servicelist/ServiceListJAASAuthenticator.java b/transform/src/patch/java/org/apache/cxf/transport/servlet/servicelist/ServiceListJAASAuthenticator.java
new file mode 100644
index 0000000..8562cd3
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/transport/servlet/servicelist/ServiceListJAASAuthenticator.java
@@ -0,0 +1,160 @@
+/**
+ * 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.transport.servlet.servicelist;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.AccountException;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.Base64Exception;
+import org.apache.cxf.common.util.Base64Utility;
+import org.apache.cxf.transport.http.blueprint.HttpDestinationBPBeanDefinitionParser;
+
+
+
+public class ServiceListJAASAuthenticator {
+
+    private static final Logger LOG = LogUtils.getL7dLogger(HttpDestinationBPBeanDefinitionParser.class);
+
+    private static final String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate";
+
+    private static final String HEADER_AUTHORIZATION = "Authorization";
+
+    private static final String AUTHENTICATION_SCHEME_BASIC = "Basic";
+
+    private String realm;
+
+    public String getRealm() {
+        return realm;
+    }
+
+    public void setRealm(String realm) {
+        this.realm = realm;
+    }
+
+
+    public Object authenticate(final String username, final String password) {
+        return doAuthenticate(username, password);
+    }
+
+    public Subject doAuthenticate(final String username, final String password) {
+        try {
+            Subject subject = new Subject();
+            LoginContext loginContext = new LoginContext(realm, subject, new CallbackHandler() {
+                public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+                    for (int i = 0; i < callbacks.length; i++) {
+                        if (callbacks[i] instanceof NameCallback) {
+                            ((NameCallback)callbacks[i]).setName(username);
+                        } else if (callbacks[i] instanceof PasswordCallback) {
+                            ((PasswordCallback)callbacks[i]).setPassword(password.toCharArray());
+                        } else {
+                            throw new UnsupportedCallbackException(callbacks[i]);
+                        }
+                    }
+                }
+            });
+            loginContext.login();
+            return subject;
+        } catch (FailedLoginException e) {
+            LOG.log(Level.FINE, "Login failed ", e);
+            return null;
+        } catch (AccountException e) {
+            LOG.log(Level.WARNING, "Account failure ",  e);
+            return null;
+        } catch (GeneralSecurityException e) {
+            LOG.log(Level.SEVERE, "General Security Exception ", e);
+            return null;
+        }
+    }
+
+    public boolean authenticate(HttpServletRequest request, HttpServletResponse response) {
+        // Return immediately if the header is missing
+        String authHeader = request.getHeader(HEADER_AUTHORIZATION);
+        if (authHeader != null && authHeader.length() > 0) {
+
+            // Get the authType (Basic, Digest) and authInfo (user/password)
+            // from the header
+            authHeader = authHeader.trim();
+            int blank = authHeader.indexOf(' ');
+            if (blank > 0) {
+                String authType = authHeader.substring(0, blank);
+                String authInfo = authHeader.substring(blank).trim();
+
+
+                if (authType.equalsIgnoreCase(AUTHENTICATION_SCHEME_BASIC)) {
+                    try {
+                        String srcString = base64Decode(authInfo);
+
+                        int i = srcString.indexOf(':');
+                        String username = srcString.substring(0, i);
+                        String password = srcString.substring(i + 1);
+
+                        // authenticate
+                        Subject subject = doAuthenticate(username, password);
+                        if (subject != null) {
+                            return true;
+                        }
+
+                    } catch (Exception e) {
+                        // Ignore
+                    }
+                }
+            }
+        }
+
+        // request authentication
+        try {
+            response.setHeader(HEADER_WWW_AUTHENTICATE, AUTHENTICATION_SCHEME_BASIC + " realm=\""
+                                                        + this.realm + "\"");
+            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+            response.setContentLength(0);
+            response.flushBuffer();
+        } catch (IOException ioe) {
+            // failed sending the response ... cannot do anything about it
+        }
+
+        // inform HttpService that authentication failed
+        return false;
+    }
+
+    private static String base64Decode(String srcString) {
+        try {
+            byte[] transformed = Base64Utility.decode(srcString);
+            return new String(transformed, StandardCharsets.ISO_8859_1);
+        } catch (Base64Exception e) {
+            return srcString;
+        }
+    }
+
+}