cxf-core and cxf-rt-frontend-jaxrs changes since 3.4.3
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);
+    }
+
+    //
+    // Outputs base64 representation of the specified byte array
+    // to a byte stream.
+    //
+    public static void encodeChunk(byte[] id,
+                                   int o,
+                                   int l,
+                                   OutputStream ostream) throws Base64Exception {
+        try {
+            ostream.write(new String(encodeChunk(id, o, l)).getBytes());
+        } catch (IOException e) {
+            throw new Base64Exception(new Message("BASE64_ENCODE_IOEXCEPTION", LOG), e);
+        }
+    }
+
+    // Outputs base64 representation of the specified byte
+    // array to a character stream.
+    //
+    public static void encode(byte[] id,
+                              int o,
+                              int l,
+                              Writer writer) throws Base64Exception {
+        try {
+            writer.write(encodeChunk(id, o, l));
+        } catch (IOException e) {
+            throw new Base64Exception(new Message("BASE64_ENCODE_WRITER_IOEXCEPTION", LOG), e);
+        }
+    }
+    //---- Private static methods --------------------------------------
+
+    /**
+     * The <code>process</code> routine processes an atomic base64
+     * unit of encoding (encodeme) into its native encoding. This class is
+     * used by decode routines to do the grunt work of decoding
+     * base64 encoded information
+     *
+     * @param   ib  Input character buffer of encoded bytes
+     * @param   ob  Output byte buffer of decoded bytes
+     * @param   p   Pointer to the encodeme of interest
+     * @return  The decoded encodeme
+     * @exception   Base64Exception Thrown is processing fails due to
+     * formatting exceptions in the encoded data
+     */
+    private static int processEncodeme(char[] ib,
+                                       byte[] ob,
+                                       int p)
+        throws Base64Exception {
+
+
+        int spad = PAD_SIZE8;
+        if (ib[3] == PAD) {
+            spad = PAD_SIZE4;
+        }
+        if (ib[2] == PAD) {
+            spad = PAD_SIZE0;
+        }
+
+        int b0 = BDT[ib[0]];
+        int b1 = BDT[ib[1]];
+        int b2 = BDT[ib[2]];
+        int b3 = BDT[ib[3]];
+
+        switch (spad) {
+        case PAD_SIZE0:
+            ob[p] = (byte)(b0 << 2 & 0xfc | b1 >> 4 & 0x3);
+            return PAD_SIZE0;
+        case PAD_SIZE4:
+            ob[p++] = (byte)(b0 << 2 & 0xfc | b1 >> 4 & 0x3);
+            ob[p] = (byte)(b1 << 4 & 0xf0 | b2 >> 2 & 0xf);
+            return PAD_SIZE4;
+        case PAD_SIZE8:
+            ob[p++] = (byte)(b0 << 2 & 0xfc | b1 >> 4 & 0x3);
+            ob[p++] = (byte)(b1 << 4 & 0xf0 | b2 >> 2 & 0xf);
+            ob[p] = (byte)(b2 << 6 & 0xc0 | b3 & 0x3f);
+            return PAD_SIZE8;
+        default:
+            // We should never get here
+            throw new IllegalStateException();
+        }
+    }
+
+    public static boolean isValidBase64(int ch) {
+        return ch == PAD || BDT[ch] != Byte.MAX_VALUE;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/util/CachedClass.java b/transform/src/patch/java/org/apache/cxf/common/util/CachedClass.java
new file mode 100644
index 0000000..fe2df06
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/util/CachedClass.java
@@ -0,0 +1,37 @@
+/**
+ * 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.ref.WeakReference;
+
+public class CachedClass {
+    private WeakReference<Class<?>> cachedClazz;
+
+    public CachedClass(Class<?> cachedClass) {
+        this.cachedClazz = new WeakReference<>(cachedClass);
+    }
+
+    public Class<?> getCachedClass() {
+        return cachedClazz == null ? null : cachedClazz.get();
+    }
+
+    public void setCachedClass(Class<?> cachedClass) {
+        this.cachedClazz = new WeakReference<>(cachedClass);
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/util/ClassHelper.java b/transform/src/patch/java/org/apache/cxf/common/util/ClassHelper.java
new file mode 100644
index 0000000..adec469
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/util/ClassHelper.java
@@ -0,0 +1,140 @@
+/**
+ * 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.Proxy;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+
+/**
+ *
+ */
+public class ClassHelper {
+
+    public static final String USE_DEFAULT_CLASS_HELPER = "org.apache.cxf.useDefaultClassHelpers";
+
+    static final ClassHelper HELPER;
+    static final ClassUnwrapper DEFAULT_UNWRAPPER;
+    static final ClassUnwrapper UNWRAPPER;
+    
+    /**
+     * Default class unwrapper implementation which delegates to the ClassHelper
+     * internal methods.
+     *
+     */
+    private static class DefaultClassUnwrapper implements ClassUnwrapper {
+        private final ClassHelper helper;
+        
+        DefaultClassUnwrapper(ClassHelper helper) {
+            this.helper = helper;
+        }
+        
+        @Override
+        public Class<?> getRealClassFromClass(Class<?> clazz) {
+            return helper.getRealClassFromClassInternal(clazz);
+        }
+        
+        @Override
+        public Class<?> getRealClass(Object o) {
+            return helper.getRealClassInternal(o);
+        }
+        
+        @Override
+        public Object getRealObject(Object o) {
+            return helper.getRealObjectInternal(o);
+        }
+    }
+
+    static {
+        HELPER = new ClassHelper();
+        DEFAULT_UNWRAPPER = new DefaultClassUnwrapper(HELPER);
+        UNWRAPPER = getClassUnwrapper(DEFAULT_UNWRAPPER);
+    }
+
+    protected ClassHelper() {
+    }
+
+    private static ClassUnwrapper getClassUnwrapper(ClassUnwrapper defaultHelper) {
+        boolean useSpring = true;
+        String s = SystemPropertyAction.getPropertyOrNull("org.apache.cxf.useSpringClassHelpers");
+        if (!StringUtils.isEmpty(s)) {
+            useSpring = "1".equals(s) || Boolean.parseBoolean(s);
+        }
+        if (useSpring) {
+            try {
+                return new SpringClassUnwrapper();
+            } catch (Throwable ex) {
+                // ignore
+            }
+        }
+        return defaultHelper;
+    }
+
+    private Class<?> getRealClassInternal(Object o) {
+        return getRealObjectInternal(o).getClass();
+    }
+
+    private Class<?> getRealClassFromClassInternal(Class<?> cls) {
+        return cls;
+    }
+
+    private Object getRealObjectInternal(Object o) {
+        return o instanceof Proxy ? Proxy.getInvocationHandler(o) : o;
+    }
+
+    public static Class<?> getRealClass(Object o) {
+        return getRealClass(null, o);
+    }
+
+    public static Class<?> getRealClassFromClass(Class<?> cls) {
+        return getRealClassFromClass(null, cls);
+    }
+
+    public static Class<?> getRealClassFromClass(Bus bus, Class<?> cls) {
+        return getContextClassUnwrapper(getBus(bus)).getRealClassFromClass(cls);
+    }
+
+    public static Object getRealObject(Object o) {
+        return getContextClassUnwrapper(getBus(null)).getRealObject(o);
+    }
+
+    public static Class<?> getRealClass(Bus bus, Object o) {
+        return getContextClassUnwrapper(getBus(bus)).getRealClass(o);
+    }
+
+    private static ClassUnwrapper getContextClassUnwrapper(Bus bus) {
+        if (bus != null && bus.getProperty(ClassUnwrapper.class.getName()) != null) {
+            return  (ClassUnwrapper) bus.getProperty(ClassUnwrapper.class.getName());
+        }
+
+        return (DEFAULT_UNWRAPPER == UNWRAPPER || checkUseDefaultClassHelper(bus)) ? DEFAULT_UNWRAPPER : UNWRAPPER;
+    }
+
+    private static Bus getBus(Bus bus) {
+        return bus == null ? BusFactory.getThreadDefaultBus() : bus;
+    }
+
+    private static boolean checkUseDefaultClassHelper(Bus bus) {
+        return bus != null && Boolean.TRUE.equals(bus.getProperty(USE_DEFAULT_CLASS_HELPER));
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/util/CollectionUtils.java b/transform/src/patch/java/org/apache/cxf/common/util/CollectionUtils.java
new file mode 100644
index 0000000..4e888c3
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/util/CollectionUtils.java
@@ -0,0 +1,126 @@
+/**
+ * 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.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+
+public final class CollectionUtils {
+    private CollectionUtils() {
+
+    }
+
+    public static <T> Collection<T> diff(Collection<T> c1, Collection<T> c2) {
+        if (c1 == null || c1.isEmpty() || c2 == null || c2.isEmpty()) {
+            return c1;
+        }
+        Collection<T> difference = new ArrayList<>();
+        for (T item : c1) {
+            if (!c2.contains(item)) {
+                difference.add(item);
+            }
+        }
+        return difference;
+    }
+
+    public static <T> boolean isEmpty(Collection<T> c) {
+        if (c != null && !c.isEmpty()) {
+            for (T item : c) {
+                if (item != null) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    public static <K, V> boolean isEmpty(Map<K, V> m) {
+        return m == null || m.isEmpty();
+    }
+
+    public static <S, T> Dictionary<S, T> singletonDictionary(S s, T t) {
+        return toDictionary(Collections.singletonMap(s, t));
+    }
+
+    public static <S, T> Dictionary<S, T> toDictionary(Map<S, T> map) {
+        return new MapToDictionary<>(map);
+    }
+
+    static class MapToDictionary<S, T> extends Dictionary<S, T> {
+        /**
+         * Map source.
+         **/
+        private final Map<S, T> map;
+
+        MapToDictionary(Map<S, T> map) {
+            this.map = map;
+        }
+
+
+        public Enumeration<T> elements() {
+            return map != null ? new IteratorToEnumeration<>(map.values().iterator()) : null;
+        }
+
+        public T get(Object key) {
+            return map != null ? map.get(key) : null;
+        }
+
+        public boolean isEmpty() {
+            return map == null || map.isEmpty();
+        }
+
+        public Enumeration<S> keys() {
+            return map != null ? new IteratorToEnumeration<>(map.keySet().iterator()) : null;
+        }
+
+        public T put(S key, T value) {
+            throw new UnsupportedOperationException();
+        }
+
+        public T remove(Object key) {
+            throw new UnsupportedOperationException();
+        }
+
+        public int size() {
+            return map != null ? map.size() : 0;
+        }
+
+        static class IteratorToEnumeration<X> implements Enumeration<X> {
+            private final Iterator<X> iter;
+
+            IteratorToEnumeration(Iterator<X> iter) {
+                this.iter = iter;
+            }
+
+            public boolean hasMoreElements() {
+                return iter != null && iter.hasNext();
+            }
+
+            public X nextElement() {
+                return iter != null ? iter.next() : null;
+            }
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/util/Compiler.java b/transform/src/patch/java/org/apache/cxf/common/util/Compiler.java
new file mode 100644
index 0000000..9d0c819
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/util/Compiler.java
@@ -0,0 +1,382 @@
+/**
+ * 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.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaCompiler.CompilationTask;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+
+import org.apache.cxf.helpers.FileUtils;
+
+public class Compiler {
+    private long maxMemory = Runtime.getRuntime().maxMemory();
+    private boolean verbose;
+    private String target;
+    private String outputDir;
+    private String classPath;
+    private String encoding;
+    private boolean forceFork = Boolean.getBoolean(Compiler.class.getName() + "-fork");
+    private File classpathTmpFile;
+    private List<String> errors = new LinkedList<>();
+    private List<String> warnings = new LinkedList<>();
+
+    public Compiler() {
+    }
+
+    public List<String> getErrors() {
+        return errors;
+    }
+    public List<String> getWarnings() {
+        return warnings;
+    }
+
+    public void setMaxMemory(long l) {
+        maxMemory = l;
+    }
+    public void setVerbose(boolean b) {
+        verbose = b;
+    }
+    public void setTarget(String s) {
+        target = s;
+    }
+    public void setOutputDir(File s) {
+        if (s != null) {
+            outputDir = s.getAbsolutePath().replace(File.pathSeparatorChar, '/');
+        } else {
+            outputDir = null;
+        }
+    }
+    public void setOutputDir(String s) {
+        outputDir = s.replace(File.pathSeparatorChar, '/');
+    }
+    public void setClassPath(String s) {
+        classPath = StringUtils.isEmpty(s) ? null : s;
+    }
+
+    // https://issues.apache.org/jira/browse/CXF-8049
+    private String getSystemClassPath() {
+        String javaClasspath = SystemPropertyAction.getProperty("java.class.path");
+
+        if (!StringUtils.isEmpty(javaClasspath)) {
+            List<String> correctedEntries = new ArrayList<>();
+
+            String[] toks = javaClasspath.split(File.pathSeparator);
+            
+            for (String tok: toks) {
+                // if any classpath entry contains a whitespace char, 
+                // enclose the entry in double quotes
+                if (tok.matches(".*\\s+.*")) {
+                    correctedEntries.add("\"" + tok + "\"");
+                } else {
+                    correctedEntries.add(tok);
+                }
+            }
+
+            return String.join(File.pathSeparator, correctedEntries);
+        }
+
+        return javaClasspath;
+    }
+
+    protected void addArgs(List<String> list) {
+        if (!StringUtils.isEmpty(encoding)) {
+            list.add("-encoding");
+            list.add(encoding);
+        }
+        if (!StringUtils.isEmpty(target)) {
+            list.add("-target");
+            list.add(target);
+            list.add("-source");
+            list.add(target);
+        }
+
+        if (!StringUtils.isEmpty(outputDir)) {
+            list.add("-d");
+            list.add(outputDir);
+        }
+
+        if (StringUtils.isEmpty(classPath)) {
+            String javaClasspath = getSystemClassPath();
+            boolean classpathSetted = !StringUtils.isEmpty(javaClasspath);
+            if (!classpathSetted) {
+                File f = new File(getClass().getClassLoader().getResource(".").getFile());
+                f = new File(f, "../lib");
+                if (f.exists() && f.isDirectory()) {
+                    list.add("-extdirs");
+                    list.add(f.toString());
+                }
+            } else {
+                list.add("-classpath");
+                list.add(javaClasspath);
+            }
+        } else {
+            list.add("-classpath");
+            list.add(classPath);
+        }
+
+    }
+    public boolean compileFiles(File[] files) {
+        List<String> f = new ArrayList<>(files.length);
+        for (File file : files) {
+            f.add(file.getAbsolutePath());
+        }
+        return compileFiles(f.toArray(new String[0]));
+    }
+    public boolean compileFiles(List<File> files) {
+        List<String> f = new ArrayList<>(files.size());
+        for (File file : files) {
+            f.add(file.getAbsolutePath());
+        }
+        return compileFiles(f.toArray(new String[0]));
+    }
+    public boolean compileFiles(String[] files) {
+        String endorsed = SystemPropertyAction.getProperty("java.endorsed.dirs");
+        if (!forceFork) {
+            return useJava6Compiler(files);
+        }
+
+        List<String> list = new ArrayList<>();
+
+        // Start of honoring java.home for used javac
+        String fsep = File.separator;
+        String javacstr = "javac";
+        String platformjavacname = "javac";
+
+        if (SystemPropertyAction.getProperty("os.name").toLowerCase().indexOf("windows") > -1) {
+            platformjavacname = "javac.exe";
+        }
+
+        if (new File(SystemPropertyAction.getProperty("java.home") + fsep + platformjavacname).exists()) {
+            // check if java.home is jdk home
+            javacstr = SystemPropertyAction.getProperty("java.home") + fsep + platformjavacname;
+        } else if (new File(SystemPropertyAction.getProperty("java.home") + fsep + ".." + fsep + "bin" + fsep
+                            + platformjavacname).exists()) {
+            // check if java.home is jre home
+            javacstr = SystemPropertyAction.getProperty("java.home") + fsep + ".." + fsep + "bin" + fsep
+                       + platformjavacname;
+        } else if (new File(SystemPropertyAction.getProperty("java.home") + fsep + "bin" + fsep
+                            + platformjavacname).exists()) {
+            //java9
+            javacstr = SystemPropertyAction.getProperty("java.home") + fsep + "bin" + fsep
+                + platformjavacname;
+        }
+        list.add(javacstr);
+        // End of honoring java.home for used javac
+
+        if (!StringUtils.isEmpty(endorsed)) {
+            list.add("-endorseddirs");
+            list.add(endorsed);
+        }
+
+        //fix for CXF-2081, set maximum heap of this VM to javac.
+        list.add("-J-Xmx" + maxMemory);
+
+        addArgs(list);
+        int classpathIdx = list.indexOf("-classpath");
+        String classpath = list.get(classpathIdx + 1);
+        checkLongClasspath(classpath, list, classpathIdx);
+        int idx = list.size();
+        Collections.addAll(list, files);
+
+        return internalCompile(list.toArray(new String[0]), idx);
+    }
+
+    protected boolean useJava6Compiler(String[] files) {
+        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+        if (compiler == null) {
+            throw new IllegalStateException(
+                "No compiler detected, make sure you are running on top of a JDK instead of a JRE.");
+        }
+        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
+        Iterable<? extends JavaFileObject> fileList = fileManager.getJavaFileObjectsFromStrings(Arrays
+            .asList(files));
+
+        return internalJava6Compile(compiler, wrapJavaFileManager(fileManager), setupDiagnosticListener(),
+                                    fileList);
+    }
+
+    protected JavaFileManager wrapJavaFileManager(StandardJavaFileManager standardJavaFileManger) {
+        return standardJavaFileManger;
+    }
+
+    protected DiagnosticListener<JavaFileObject> setupDiagnosticListener() {
+        return new DiagnosticListener<JavaFileObject>() {
+            public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+                switch (diagnostic.getKind()) {
+                case ERROR:
+                    errors.add(diagnostic.toString());
+                    if (verbose) {
+                        System.err.println(diagnostic.toString());
+                    }
+                    break;
+                case WARNING:
+                case MANDATORY_WARNING:
+                    warnings.add(diagnostic.toString());
+                    if (verbose) {
+                        System.err.println(diagnostic.toString());
+                    }
+                    break;
+                default:
+                    break;
+                }
+            }
+        };
+    }
+
+    protected boolean internalJava6Compile(JavaCompiler compiler, JavaFileManager fileManager,
+                                           DiagnosticListener<JavaFileObject> listener,
+                                           Iterable<? extends JavaFileObject> fileList) {
+        List<String> args = new ArrayList<>();
+        addArgs(args);
+        CompilationTask task = compiler.getTask(null, fileManager, listener, args, null, fileList);
+        Boolean ret = task.call();
+        try {
+            fileManager.close();
+        } catch (IOException e) {
+            System.err.print("[ERROR] IOException during compiling.");
+            e.printStackTrace();
+        }
+        return ret;
+    }
+
+    public boolean internalCompile(String[] args, int sourceFileIndex) {
+        File tmpFile = null;
+        try {
+            final String[] cmdArray;
+            if (isLongCommandLines(args) && sourceFileIndex >= 0) {
+                tmpFile = FileUtils.createTempFile("cxf-compiler", null);
+                try (PrintWriter out = new PrintWriter(new FileWriter(tmpFile))) {
+                    for (int i = sourceFileIndex; i < args.length; i++) {
+                        if (args[i].indexOf(' ') > -1) {
+                            args[i] = args[i].replace(File.separatorChar, '/');
+                            //
+                            // javac gives an error if you use forward slashes
+                            // with package-info.java. Refer to:
+                            // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6198196
+                            //
+                            if (args[i].indexOf("package-info.java") > -1
+                                && SystemPropertyAction.getProperty("os.name")
+                                    .toLowerCase().indexOf("windows") > -1) {
+                                out.println('"' + args[i].replaceAll("/", "\\\\\\\\") + '"');
+                            } else {
+                                out.println('"' + args[i] + '"');
+                            }
+                        } else {
+                            out.println(args[i]);
+                        }
+                    }
+                    out.flush();
+                }
+                cmdArray = new String[sourceFileIndex + 1];
+                System.arraycopy(args, 0, cmdArray, 0, sourceFileIndex);
+                cmdArray[sourceFileIndex] = "@" + tmpFile;
+            } else {
+                cmdArray = new String[args.length];
+                System.arraycopy(args, 0, cmdArray, 0, args.length);
+            }
+
+            if (SystemPropertyAction.getProperty("os.name").toLowerCase().indexOf("windows") > -1) {
+                for (int i = 0; i < cmdArray.length; i++) {
+                    if (cmdArray[i].indexOf("package-info") == -1) {
+                        cmdArray[i] = cmdArray[i].replace('\\', '/');
+                    }
+                }
+            }
+
+            final Process p = Runtime.getRuntime().exec(cmdArray);
+
+            if (p.getErrorStream() != null) {
+                StreamPrinter errorStreamPrinter = new StreamPrinter(p.getErrorStream(), "", System.out);
+                errorStreamPrinter.start();
+            }
+
+            if (p.getInputStream() != null) {
+                StreamPrinter infoStreamPrinter = new StreamPrinter(p.getInputStream(), "[INFO]", System.out);
+                infoStreamPrinter.start();
+            }
+
+            return p.waitFor() == 0 ? true : false;
+        } catch (SecurityException e) {
+            System.err.println("[ERROR] SecurityException during exec() of compiler \"" + args[0] + "\".");
+        } catch (InterruptedException e) {
+            // ignore
+
+        } catch (IOException e) {
+            System.err.print("[ERROR] IOException during exec() of compiler \"" + args[0] + "\"");
+            System.err.println(". Check your path environment variable.");
+        } finally {
+            if (tmpFile != null && tmpFile.exists()) {
+                FileUtils.delete(tmpFile);
+            }
+            if (classpathTmpFile != null && classpathTmpFile.exists()) {
+                FileUtils.delete(classpathTmpFile);
+            }
+        }
+
+        return false;
+    }
+
+    private boolean isLongCommandLines(String[] args) {
+        StringBuilder strBuffer = new StringBuilder();
+        for (int i = 0; i < args.length; i++) {
+            strBuffer.append(args[i]);
+        }
+        return strBuffer.length() > 4096;
+    }
+
+    private boolean isLongClasspath(String classpath) {
+        return classpath.length() > 2048;
+    }
+
+    private void checkLongClasspath(String classpath, List<String> list, int classpathIdx) {
+        if (isLongClasspath(classpath)) {
+            try {
+                classpathTmpFile = FileUtils.createTempFile("cxf-compiler-classpath", null);
+                try (PrintWriter out = new PrintWriter(new FileWriter(classpathTmpFile))) {
+                    out.println(classpath);
+                    out.flush();
+                }
+                list.set(classpathIdx + 1, "@" + classpathTmpFile);
+            } catch (IOException e) {
+                System.err.print("[ERROR] can't write long classpath to @argfile");
+            }
+        }
+    }
+
+    public void setEncoding(String string) {
+        encoding = string;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/util/ModCountCopyOnWriteArrayList.java b/transform/src/patch/java/org/apache/cxf/common/util/ModCountCopyOnWriteArrayList.java
new file mode 100644
index 0000000..daa6407
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/util/ModCountCopyOnWriteArrayList.java
@@ -0,0 +1,156 @@
+/**
+ * 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.util.Collection;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public final class ModCountCopyOnWriteArrayList<T> extends CopyOnWriteArrayList<T> {
+    private static final long serialVersionUID = 1783937035760941219L;
+    private AtomicInteger modCount = new AtomicInteger();
+
+    public ModCountCopyOnWriteArrayList() {
+        super();
+    }
+    public ModCountCopyOnWriteArrayList(Collection<? extends T> c) {
+        super(c);
+        if (c instanceof ModCountCopyOnWriteArrayList) {
+            modCount.set(((ModCountCopyOnWriteArrayList<?>)c).getModCount());
+        }
+    }
+
+    public int getModCount() {
+        return modCount.get();
+    }
+
+    public void setModCount(int i) {
+        modCount.set(i);
+    }
+
+    @Override
+    public void add(int index, T element) {
+        super.add(index, element);
+        modCount.incrementAndGet();
+    }
+
+    @Override
+    public boolean add(T element) {
+        if (super.add(element)) {
+            modCount.incrementAndGet();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean addAll(Collection<? extends T> c) {
+        if (super.addAll(c)) {
+            modCount.incrementAndGet();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean addAll(int index, Collection<? extends T> c) {
+        if (super.addAll(index, c)) {
+            modCount.incrementAndGet();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public int addAllAbsent(Collection<? extends T> c) {
+        int i = super.addAllAbsent(c);
+        if (i > 0) {
+            modCount.incrementAndGet();
+        }
+        return i;
+    }
+
+    @Override
+    public boolean addIfAbsent(T element) {
+        if (super.addIfAbsent(element)) {
+            modCount.incrementAndGet();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void clear() {
+        super.clear();
+        modCount.incrementAndGet();
+    }
+
+    @Override
+    public T remove(int index) {
+        T t = super.remove(index);
+        if (t != null) {
+            modCount.incrementAndGet();
+        }
+        return t;
+    }
+
+    @Override
+    public boolean remove(Object o) {
+        if (super.remove(o)) {
+            modCount.incrementAndGet();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean removeAll(Collection<?> c) {
+        if (super.removeAll(c)) {
+            modCount.incrementAndGet();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean retainAll(Collection<?> c) {
+        if (super.retainAll(c)) {
+            modCount.incrementAndGet();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return super.hashCode() + modCount.get();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof ModCountCopyOnWriteArrayList) {
+            return super.equals(o) && modCount.get()
+                == ((ModCountCopyOnWriteArrayList<?>)o).getModCount();
+        }
+        return false;
+    }
+
+}
+
diff --git a/transform/src/patch/java/org/apache/cxf/common/util/PackageUtils.java b/transform/src/patch/java/org/apache/cxf/common/util/PackageUtils.java
new file mode 100644
index 0000000..9d014c8
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/util/PackageUtils.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.common.util;
+
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.apache.cxf.helpers.JavaUtils;
+
+
+public final class PackageUtils {
+
+    private PackageUtils() {
+
+    }
+
+    static String getPackageName(String className) {
+        int pos = className.lastIndexOf('.');
+        if (pos != -1) {
+            return className.substring(0, pos);
+        }
+        return "";
+    }
+
+    public static String getPackageName(Class<?> clazz) {
+        String className = clazz.getName();
+        if (className.startsWith("[L")) {
+            className = className.substring(2);
+        }
+        return getPackageName(className);
+    }
+
+    public static String getSharedPackageName(List<Class<?>> classes) {
+        if (classes.isEmpty()) {
+            return "";
+        }
+        List<List<String>> lParts = new ArrayList<>(classes.size());
+        List<String> currentParts = new ArrayList<>();
+        for (Class<?> cls : classes) {
+            if (!Proxy.isProxyClass(cls)) {
+                lParts.add(Arrays.asList(getPackageName(cls).split("\\.")));
+            }
+        }
+        for (int i = 0; i < lParts.get(0).size(); i++) {
+            int j = 1;
+            for (; j < lParts.size(); j++) {
+                if (i > (lParts.get(j).size() - 1) || !lParts.get(j).get(i).equals(lParts.get(0).get(i))) {
+                    break;
+                }
+            }
+            if (j == lParts.size()) {
+                currentParts.add(lParts.get(j - 1).get(i));
+            } else {
+                break;
+            }
+        }
+        return String.join(".", currentParts);
+    }
+
+    public static String parsePackageName(String namespace, String defaultPackageName) {
+        return (defaultPackageName != null && !defaultPackageName.trim().isEmpty())
+            ? defaultPackageName : getPackageNameByNameSpaceURI(namespace.trim());
+    }
+
+    public static String getPackageNameByNameSpaceURI(String nameSpaceURI) {
+        int idx = nameSpaceURI.indexOf(':');
+        boolean urnScheme = false;
+        if (idx >= 0) {
+            final String scheme = nameSpaceURI.substring(0, idx);
+            urnScheme = "urn".equalsIgnoreCase(scheme);
+            if ("http".equalsIgnoreCase(scheme) || urnScheme) {
+                nameSpaceURI = nameSpaceURI.substring(idx + (urnScheme ? 1 : 3)); //
+            }
+        }
+
+        List<String> tokens = tokenize(nameSpaceURI, "/:");
+        if (tokens.isEmpty()) {
+            return null;
+        }
+
+        if (tokens.size() > 1) {
+            String lastToken = tokens.get(tokens.size() - 1);
+            idx = lastToken.lastIndexOf('.');
+            if (idx > 0) {
+                lastToken = lastToken.replace('.', '_');
+                tokens.set(tokens.size() - 1, lastToken);
+            }
+        }
+
+        String domain = tokens.remove(0);
+        List<String> r = tokenize(domain, urnScheme ? ".-" : ".");
+        Collections.reverse(r);
+        if ("www".equalsIgnoreCase(r.get(r.size() - 1))) {
+            // remove leading www
+            r.remove(r.size() - 1);
+        }
+
+        // replace the domain name with tokenized items
+        tokens.addAll(0, r);
+
+        // iterate through the tokens and apply xml->java name algorithm
+        for (int i = 0; i < tokens.size(); i++) {
+
+            // get the token and remove illegal chars
+            String token = tokens.get(i);
+            token = removeIllegalIdentifierChars(token);
+
+            token = token.toLowerCase();
+
+            // this will check for reserved keywords
+            if (JavaUtils.isJavaKeyword(token)) {
+                token = '_' + token;
+            }
+
+            tokens.set(i, token);
+        }
+
+        // concat all the pieces and return it
+        return String.join(".", tokens);
+    }
+
+    private static List<String> tokenize(String str, String sep) {
+        StringTokenizer tokens = new StringTokenizer(str, sep);
+        List<String> r = new ArrayList<>();
+
+        while (tokens.hasMoreTokens()) {
+            r.add(tokens.nextToken());
+        }
+        return r;
+    }
+
+    private static String removeIllegalIdentifierChars(String token) {
+        StringBuilder newToken = new StringBuilder();
+        for (int i = 0; i < token.length(); i++) {
+            char c = token.charAt(i);
+
+            if (i == 0 && !Character.isJavaIdentifierStart(c)) {
+                // prefix an '_' if the first char is illegal
+                newToken.append('_').append(c);
+            } else if (!Character.isJavaIdentifierPart(c)) {
+                // replace the char with an '_' if it is illegal
+                newToken.append('_');
+            } else {
+                // add the legal char
+                newToken.append(c);
+            }
+        }
+        return newToken.toString();
+    }
+
+    public static String getNamespace(String packageName) {
+        if (packageName == null || packageName.isEmpty()) {
+            return null;
+        }
+        final List<String> parts = Arrays.asList(packageName.split("\\."));
+        Collections.reverse(parts);
+        return "http://" + String.join(".", parts) + '/';
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/util/ProxyClassLoader.java b/transform/src/patch/java/org/apache/cxf/common/util/ProxyClassLoader.java
new file mode 100644
index 0000000..073ca46
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/util/ProxyClassLoader.java
@@ -0,0 +1,89 @@
+/**
+ * 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.net.URL;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Utility class loader that can be used to create proxies in cases where
+ * the the client classes are not visible to the loader of the
+ * service class.
+ */
+public class ProxyClassLoader extends ClassLoader {
+    private final Class<?>[] classes;
+    private final Set<ClassLoader> loaders = new HashSet<>();
+    private boolean checkSystem;
+
+    public ProxyClassLoader(ClassLoader parent) {
+        super(parent);
+        classes = null;
+    }
+
+    public ProxyClassLoader(ClassLoader parent, Class<?>[] cls) {
+        super(parent);
+        classes = cls;
+    }
+
+    public void addLoader(ClassLoader loader) {
+        if (loader == null) {
+            checkSystem = true;
+        } else {
+            loaders.add(loader);
+        }
+    }
+
+    @Override
+    public Class<?> findClass(String name) throws ClassNotFoundException {
+        if (classes != null) {
+            for (Class<?> c : classes) {
+                if (name.equals(c.getName())) {
+                    return c;
+                }
+            }
+        }
+        for (ClassLoader loader : loaders) {
+            try {
+                return loader.loadClass(name);
+            } catch (ClassNotFoundException | NoClassDefFoundError cnfe) {
+                // Try next
+            }
+        }
+        if (checkSystem) {
+            try {
+                return getSystemClassLoader().loadClass(name);
+            } catch (ClassNotFoundException | NoClassDefFoundError cnfe) {
+                // Try next
+            }
+        }
+        throw new ClassNotFoundException(name);
+    }
+
+    @Override
+    public URL findResource(String name) {
+        for (ClassLoader loader : loaders) {
+            URL url = loader.getResource(name);
+            if (url != null) {
+                return url;
+            }
+        }
+        return null;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/util/ProxyHelper.java b/transform/src/patch/java/org/apache/cxf/common/util/ProxyHelper.java
new file mode 100644
index 0000000..380e2bb
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/util/ProxyHelper.java
@@ -0,0 +1,140 @@
+/**
+ * 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.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.logging.LogUtils;
+
+/**
+ *
+ */
+public class ProxyHelper {
+    static final ProxyHelper HELPER;
+    static {
+        ProxyHelper theHelper;
+        try {
+            theHelper = new CglibProxyHelper();
+        } catch (Throwable ex) {
+            theHelper = new ProxyHelper();
+        }
+        HELPER = theHelper;
+    }
+   
+    private static final Logger LOG = LogUtils.getL7dLogger(ProxyHelper.class);
+    
+    protected ProxyClassLoaderCache proxyClassLoaderCache = 
+        new ProxyClassLoaderCache();
+      
+    
+    protected ProxyHelper() {
+    }
+
+    protected Object getProxyInternal(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) {
+        ClassLoader combinedLoader = getClassLoaderForInterfaces(loader, interfaces);
+        return Proxy.newProxyInstance(combinedLoader, interfaces, handler);
+    }
+
+    /**
+     * Return a classloader that can see all the given interfaces If the given loader can see all interfaces
+     * then it is used. If not then a combined classloader of all interface classloaders is returned.
+     *
+     * @param loader use supplied class loader
+     * @param interfaces
+     * @return classloader that sees all interfaces
+     */
+    private ClassLoader getClassLoaderForInterfaces(final ClassLoader loader, final Class<?>[] interfaces) {
+        if (canSeeAllInterfaces(loader, interfaces)) {
+            LOG.log(Level.FINE, "current classloader " + loader + " can see all interface");
+            return loader;
+        }
+        String sortedNameFromInterfaceArray = getSortedNameFromInterfaceArray(interfaces);
+        ClassLoader cachedLoader = proxyClassLoaderCache.getProxyClassLoader(loader, interfaces);
+        if (canSeeAllInterfaces(cachedLoader, interfaces)) {
+            LOG.log(Level.FINE, "find required loader from ProxyClassLoader cache with key" 
+                 + sortedNameFromInterfaceArray);
+            return cachedLoader;
+        } else {
+            LOG.log(Level.FINE, "find a loader from ProxyClassLoader cache with interfaces " 
+                + sortedNameFromInterfaceArray
+                + " but can't see all interfaces");
+            for (Class<?> currentInterface : interfaces) {
+                String ifName = currentInterface.getName();
+                
+                if (!ifName.startsWith("org.apache.cxf") && !ifName.startsWith("java")) {
+                    // remove the stale ProxyClassLoader and recreate one
+                    proxyClassLoaderCache.removeStaleProxyClassLoader(currentInterface);
+                    cachedLoader = proxyClassLoaderCache.getProxyClassLoader(loader, interfaces);
+                    
+                }
+            }
+        }
+               
+        return cachedLoader;
+    }
+    
+    private String getSortedNameFromInterfaceArray(Class<?>[] interfaces) {
+        SortedArraySet<String> arraySet = new SortedArraySet<>();
+        for (Class<?> currentInterface : interfaces) {
+            arraySet.add(currentInterface.getName() + ClassLoaderUtils.getClassLoaderName(currentInterface));
+        }
+        return arraySet.toString();
+    }
+
+
+    private boolean canSeeAllInterfaces(ClassLoader loader, Class<?>[] interfaces) {
+        for (Class<?> currentInterface : interfaces) {
+            String ifName = currentInterface.getName();
+            try {
+                Class<?> ifClass = Class.forName(ifName, true, loader);
+                if (ifClass != currentInterface) {
+                    return false;
+                }
+                //we need to check all the params/returns as well as the Proxy creation
+                //will try to create methods for all of this even if they aren't used
+                //by the client and not available in the clients classloader
+                for (Method m : ifClass.getMethods()) {
+                    Class<?> returnType = m.getReturnType();
+                    if (!returnType.isPrimitive()) {
+                        Class.forName(returnType.getName(), true, loader);
+                    }
+                    for (Class<?> p : m.getParameterTypes()) {
+                        if (!p.isPrimitive()) {
+                            Class.forName(p.getName(), true, loader);
+                        }
+                    }
+                }
+            } catch (NoClassDefFoundError | ClassNotFoundException e) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static Object getProxy(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) {
+        return HELPER.getProxyInternal(loader, interfaces, handler);
+    }
+    
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/util/ReflectionInvokationHandler.java b/transform/src/patch/java/org/apache/cxf/common/util/ReflectionInvokationHandler.java
new file mode 100644
index 0000000..1b4d904
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/util/ReflectionInvokationHandler.java
@@ -0,0 +1,199 @@
+/**
+ * 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.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Iterator;
+
+/**
+ *
+ */
+public class ReflectionInvokationHandler implements InvocationHandler {
+    private Object target;
+
+    public ReflectionInvokationHandler(Object obj) {
+        target = obj;
+    }
+
+    public Object getTarget() {
+        return target;
+    }
+
+    /** {@inheritDoc}*/
+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+        WrapReturn wr = method.getAnnotation(WrapReturn.class);
+        final Class<?> targetClass = target.getClass();
+        final Class<?>[] parameterTypes = getParameterTypes(method, args);
+        try {
+            Method m;
+            try {
+                m = targetClass.getMethod(method.getName(), parameterTypes);
+            } catch (NoSuchMethodException nsme) {
+
+                boolean[] optionals = new boolean[method.getParameterTypes().length];
+                int i = 0;
+                int optionalNumber = 0;
+                for (final Annotation[] a : method.getParameterAnnotations()) {
+                    optionals[i] = false;
+                    for (final Annotation potential : a) {
+                        if (Optional.class.equals(potential.annotationType())) {
+                            optionals[i] = true;
+                            optionalNumber++;
+                            break;
+                        }
+                    }
+                    i++;
+                }
+
+                Class<?>[] newParams = new Class<?>[args.length - optionalNumber];
+                Object[] newArgs = new Object[args.length - optionalNumber];
+                int argI = 0;
+                for (int j = 0; j < parameterTypes.length; j++) {
+                    if (optionals[j]) {
+                        continue;
+                    }
+                    newArgs[argI] = args[j];
+                    newParams[argI] = parameterTypes[j];
+                    argI++;
+                }
+                m = targetClass.getMethod(method.getName(), newParams);
+                args = newArgs;
+            }
+            ReflectionUtil.setAccessible(m);
+            return wrapReturn(wr, m.invoke(target, args));
+        } catch (InvocationTargetException e) {
+            throw e.getCause();
+        } catch (NoSuchMethodException e) {
+            for (Method m2 : targetClass.getMethods()) {
+                if (m2.getName().equals(method.getName())
+                    && m2.getParameterTypes().length == method.getParameterTypes().length) {
+                    boolean found = true;
+                    for (int x = 0; x < m2.getParameterTypes().length; x++) {
+                        if (args[x] != null
+                            && !m2.getParameterTypes()[x].isInstance(args[x])) {
+                            found = false;
+                        }
+                    }
+                    if (found) {
+                        ReflectionUtil.setAccessible(m2);
+                        return wrapReturn(wr, m2.invoke(target, args));
+                    }
+                }
+            }
+            throw e;
+        }
+    }
+    private Class<?>[] getParameterTypes(Method method, Object[] args) {
+        Class<?>[] types = method.getParameterTypes();
+        final Annotation[][] parAnnotations = method.getParameterAnnotations();
+        for (int x = 0; x < types.length; x++) {
+            UnwrapParam p = getUnwrapParam(parAnnotations[x]);
+            if (p != null) {
+                String s = p.methodName();
+                String tn = p.typeMethodName();
+                try {
+                    Method m = args[x].getClass().getMethod(s);
+                    if ("#default".equals(tn)) {
+                        types[x] = m.getReturnType();
+                    } else {
+                        Method m2 = args[x].getClass().getMethod(tn);
+                        types[x] = (Class<?>)ReflectionUtil.setAccessible(m2).invoke(args[x]);
+                    }
+                    args[x] = ReflectionUtil.setAccessible(m).invoke(args[x]);
+                } catch (Exception ex) {
+                    throw new RuntimeException(ex);
+                }
+            }
+        }
+        return types;
+    }
+
+    private UnwrapParam getUnwrapParam(Annotation[] annotations) {
+        for (Annotation a : annotations) {
+            if (a instanceof UnwrapParam) {
+                return (UnwrapParam)a;
+            }
+        }
+        return null;
+    }
+
+    private static Object wrapReturn(WrapReturn wr, Object t) {
+        if (wr == null || t == null) {
+            return t;
+        }
+        if (wr.iterator()) {
+            return new WrapperIterator(wr.value(), (Iterator<?>)t);
+        }
+        return createProxyWrapper(t, wr.value());
+    }
+
+    public static <T> T createProxyWrapper(Object target, Class<T> inf) {
+        InvocationHandler h = new ReflectionInvokationHandler(target);
+        return inf.cast(Proxy.newProxyInstance(inf.getClassLoader(), new Class<?>[] {inf}, h));
+    }
+
+    @Target(ElementType.PARAMETER)
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface Optional {
+    }
+
+    @Target(ElementType.METHOD)
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface WrapReturn {
+        Class<?> value();
+        boolean iterator() default false;
+    }
+
+    @Target(ElementType.PARAMETER)
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface UnwrapParam {
+        String methodName() default "getValue";
+        String typeMethodName() default "#default";
+    }
+
+    private static class WrapperIterator implements Iterator<Object> {
+        Class<?> cls;
+        Iterator<?> internal;
+        WrapperIterator(Class<?> c, Iterator<?> it) {
+            internal = it;
+            cls = c;
+        }
+        public boolean hasNext() {
+            return internal.hasNext();
+        }
+        public Object next() {
+            Object obj = internal.next();
+            return createProxyWrapper(obj, cls);
+        }
+        
+        @Override
+        public void remove() {
+            internal.remove();
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/util/SortedArraySet.java b/transform/src/patch/java/org/apache/cxf/common/util/SortedArraySet.java
new file mode 100644
index 0000000..9c2b139
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/util/SortedArraySet.java
@@ -0,0 +1,266 @@
+/**
+ * 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.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.SortedSet;
+import java.util.concurrent.atomic.AtomicReference;
+
+
+/**
+ * This class implements most of the <tt>Set</tt> interface, backed by a
+ * sorted Array.  This makes iterators very fast, lookups are log(n), but
+ * adds are fairly expensive.
+ *
+ * This class is also threadsafe, but without synchronizations.   Lookups
+ * and iterators will iterate over the state of the Set when the iterator
+ * was created.
+ *
+ * If no data is stored in the Set, it uses very little memory.  The backing
+ * array is created on demand.
+ *
+ * This class is primarly useful for stuff that will be setup at startup, but
+ * then iterated over MANY times during runtime.
+ *
+ * @param <T>
+ */
+public final class SortedArraySet<T> implements SortedSet<T> {
+    final AtomicReference<T[]> data = new AtomicReference<>();
+
+    public void clear() {
+        data.set(null);
+    }
+
+    public boolean isEmpty() {
+        T[] tmp = data.get();
+        return tmp == null || tmp.length == 0;
+    }
+
+    public Iterator<T> iterator() {
+        return new SASIterator<>(data.get());
+    }
+
+    public int size() {
+        T[] tmp = data.get();
+        return tmp == null ? 0 : tmp.length;
+    }
+
+    @SuppressWarnings("unchecked")
+    private T[] newArray(int size) {
+        return (T[])new Object[size];
+    }
+
+    public boolean add(T o) {
+        if (!contains(o)) {
+            T[] tmp = data.get();
+            T[] tmp2;
+            if (tmp == null) {
+                tmp2 = newArray(1);
+                tmp2[0] = o;
+            } else {
+                tmp2 = newArray(tmp.length + 1);
+                System.arraycopy(tmp, 0, tmp2, 0, tmp.length);
+                tmp2[tmp2.length - 1] = o;
+                Arrays.sort(tmp2);
+            }
+
+            if (!data.compareAndSet(tmp, tmp2)) {
+                return add(o);
+            }
+            return true;
+        }
+        return false;
+    }
+    public boolean addAll(Collection<? extends T> c) {
+        boolean val = false;
+        for (T t : c) {
+            val |= add(t);
+        }
+        return val;
+    }
+    public boolean containsAll(Collection<?> c) {
+        boolean val = false;
+        for (Object t : c) {
+            val |= contains(t);
+        }
+        return val;
+    }
+
+    public boolean contains(Object o) {
+        T[] tmp = data.get();
+        if (tmp == null) {
+            return false;
+        }
+        return Arrays.binarySearch(tmp, o) >= 0;
+    }
+
+    public boolean removeAll(Collection<?> c) {
+        boolean val = false;
+        for (Object t : c) {
+            val |= remove(t);
+        }
+        return val;
+    }
+    public boolean retainAll(Collection<?> c) {
+        boolean val = false;
+        for (T t : this) {
+            if (!c.contains(t)) {
+                val |= remove(t);
+            }
+        }
+        return val;
+    }
+
+    public boolean remove(Object o) {
+        T[] tmp = data.get();
+
+        if (tmp == null) {
+            return false;
+        }
+        int idx = Arrays.binarySearch(tmp, o);
+        if (idx != -1) {
+            if (tmp.length == 1
+                && !data.compareAndSet(tmp, null)) {
+                return remove(o);
+            }
+            T[] tmp2 = newArray(tmp.length - 1);
+            System.arraycopy(tmp, 0, tmp2, 0, idx);
+            System.arraycopy(tmp, idx + 1, tmp2, idx, tmp.length - 1 - idx);
+            if (!data.compareAndSet(tmp, tmp2)) {
+                return remove(o);
+            }
+            return true;
+        }
+        return false;
+    }
+
+
+    public Object[] toArray() {
+        T[] tmp = data.get();
+        if (tmp == null) {
+            return new Object[0];
+        }
+        T[] tmp2 = newArray(tmp.length);
+        System.arraycopy(tmp, 0, tmp2, 0, tmp.length);
+        return tmp2;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <X> X[] toArray(X[] a) {
+        T[] tmp = data.get();
+        if (tmp == null) {
+            if (a.length != 0) {
+                return (X[])java.lang.reflect.Array.
+                    newInstance(a.getClass().getComponentType(), 0);
+            }
+            return a;
+        }
+
+        if (a.length < tmp.length) {
+            a = (X[])java.lang.reflect.Array.
+                newInstance(a.getClass().getComponentType(), tmp.length);
+        }
+        System.arraycopy(tmp, 0, a, 0, tmp.length);
+        if (a.length > tmp.length) {
+            a[tmp.length] = null;
+        }
+        return a;
+    }
+
+    @SuppressWarnings("unchecked")
+    public boolean equals(Object o) {
+        if (!(o instanceof SortedArraySet)) {
+            return false;
+        }
+        SortedArraySet<T> as = (SortedArraySet<T>)o;
+        return Arrays.equals(data.get(), as.data.get());
+    }
+    public String toString() {
+        return Arrays.toString(data.get());
+    }
+    public int hashCode() {
+        return Arrays.hashCode(data.get());
+    }
+
+
+    private class SASIterator<X> implements Iterator<X> {
+        final X[] data;
+        int idx;
+
+        SASIterator(X[] d) {
+            data = d;
+        }
+
+        public boolean hasNext() {
+            return data != null && idx != data.length;
+        }
+
+        public X next() {
+            if (data == null || idx == data.length) {
+                throw new NoSuchElementException();
+            }
+            return data[idx++];
+        }
+
+        @Override
+        public void remove() {
+            if (idx > 0) {
+                SortedArraySet.this.remove((Object)data[idx - 1]);
+            }
+        }
+    }
+
+
+    public Comparator<? super T> comparator() {
+        return null;
+    }
+
+    public T first() {
+        T[] tmp = data.get();
+        if (tmp == null || tmp.length == 0) {
+            return null;
+        }
+        return tmp[0];
+    }
+
+    public T last() {
+        T[] tmp = data.get();
+        if (tmp == null || tmp.length == 0) {
+            return null;
+        }
+        return tmp[tmp.length - 1];
+    }
+
+    public SortedSet<T> headSet(T toElement) {
+        throw new UnsupportedOperationException();
+    }
+
+    public SortedSet<T> subSet(T fromElement, T toElement) {
+        throw new UnsupportedOperationException();
+    }
+
+    public SortedSet<T> tailSet(T fromElement) {
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/util/SpringClassUnwrapper.java b/transform/src/patch/java/org/apache/cxf/common/util/SpringClassUnwrapper.java
new file mode 100644
index 0000000..a196eb0
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/util/SpringClassUnwrapper.java
@@ -0,0 +1,111 @@
+/**
+ * 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 org.springframework.aop.TargetSource;
+import org.springframework.aop.framework.Advised;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.util.ClassUtils;
+
+/**
+ *
+ */
+class SpringClassUnwrapper implements ClassUnwrapper {
+    SpringClassUnwrapper() throws ClassNotFoundException {
+        Class.forName("org.springframework.aop.support.AopUtils");
+        Class.forName("org.springframework.aop.framework.Advised");
+    }
+
+    @Override
+    public Class<?> getRealClassFromClass(Class<?> cls) {
+        if (ClassUtils.isCglibProxyClass(cls)) {
+            final Class<?> superclass = cls.getSuperclass();
+            // Lambda's generated class names also contain $$ which makes them trigger CGLIB
+            // proxy path. Adding more checks to handle this particular case.
+            if (superclass != null && (superclass != Object.class || wasCglibEnhanced(cls))) {
+                return getRealClassFromClass(superclass);
+            }
+        }
+        return cls;
+    }
+
+    @Override
+    public Object getRealObject(Object o) {
+        if (o instanceof Advised) {
+            try {
+
+                Advised advised = (Advised)o;
+                Object target = advised.getTargetSource().getTarget();
+                //could be a proxy of a proxy.....
+                return getRealObject(target);
+            } catch (Exception ex) {
+                // ignore
+            }
+        }
+        return o;
+    }
+
+    @Override
+    public Class<?> getRealClass(Object o) {
+        if (AopUtils.isAopProxy(o) && (o instanceof Advised)) {
+            Advised advised = (Advised)o;
+            try {
+                TargetSource targetSource = advised.getTargetSource();
+
+                final Object target;
+                try {
+                    target = targetSource.getTarget();
+                } catch (BeanCreationException ex) {
+                    // some scopes such as 'request' may not
+                    // be active on the current thread yet
+                    return getRealClassFromClass(targetSource.getTargetClass());
+                }
+
+                if (target == null) {
+                    Class<?> targetClass = AopUtils.getTargetClass(o);
+                    if (targetClass != null) {
+                        return getRealClassFromClass(targetClass);
+                    }
+                } else {
+                    return getRealClass(target);
+                }
+            } catch (Exception ex) {
+                // ignore
+            }
+
+        } else if (ClassUtils.isCglibProxyClass(o.getClass())) {
+            return getRealClassFromClass(AopUtils.getTargetClass(o));
+        }
+        
+        return o.getClass();
+    }
+
+    /**
+     * This additional check is not very reliable since CGLIB allows to
+     * supply own NamingPolicy implementations. However, it works with native
+     * CGLIB proxies ("byCGLIB$$") as well as Spring CGLIB proxies (by "BySpringCGLIB$$").
+     * More expensive approach is to use reflection and inspect the class declared methods, 
+     * looking for CGLIB-specific ones like CGLIB$BIND_CALLBACKS. 
+     */
+    private static boolean wasCglibEnhanced(Class<?> cls) {
+        return cls.getName().contains("CGLIB");
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/util/SpringClasspathScanner.java b/transform/src/patch/java/org/apache/cxf/common/util/SpringClasspathScanner.java
new file mode 100644
index 0000000..9113d77
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/util/SpringClasspathScanner.java
@@ -0,0 +1,202 @@
+/**
+ * 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.io.IOException;
+import java.lang.annotation.Annotation;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
+import org.springframework.core.type.classreading.MetadataReader;
+import org.springframework.core.type.classreading.MetadataReaderFactory;
+import org.springframework.util.ClassUtils;
+
+class SpringClasspathScanner extends ClasspathScanner {
+
+    private static final boolean IN_OSGI =  isSpringInOsgi();
+    
+    
+    SpringClasspathScanner() throws Exception {
+        Class.forName("org.springframework.core.io.support.PathMatchingResourcePatternResolver");
+        Class.forName("org.springframework.core.type.classreading.CachingMetadataReaderFactory");
+    }
+    private static boolean isSpringInOsgi() {
+        try {
+            Class.forName("org.springframework.osgi.io.OsgiBundleResourcePatternResolver");
+            Class.forName("org.springframework.osgi.util.BundleDelegatingClassLoader");
+            return true;
+        } catch (Throwable ex) {
+            return false;
+        }
+    }
+
+    @Override
+    protected Map< Class< ? extends Annotation >, Collection< Class< ? > > > findClassesInternal(
+        Collection< String > basePackages,
+        List<Class< ? extends Annotation > > annotations,
+        ClassLoader loader)
+        throws IOException, ClassNotFoundException {
+
+        ResourcePatternResolver resolver = getResolver(loader);
+        MetadataReaderFactory factory = new CachingMetadataReaderFactory(resolver);
+
+        final Map< Class< ? extends Annotation >, Collection< Class< ? > > > classes =
+            new HashMap<>();
+        final Map< Class< ? extends Annotation >, Collection< String > > matchingInterfaces =
+            new HashMap<>();
+        final Map<String, String[]> nonMatchingClasses = new HashMap<>();
+
+        for (Class< ? extends Annotation > annotation: annotations) {
+            classes.put(annotation, new HashSet<>());
+            matchingInterfaces.put(annotation, new HashSet<>());
+        }
+
+        if (basePackages == null || basePackages.isEmpty()) {
+            return classes;
+        }
+
+        for (final String basePackage: basePackages) {
+            final boolean scanAllPackages = basePackage.equals(WILDCARD);
+            final String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+                + (scanAllPackages ? "" : ClassUtils.convertClassNameToResourcePath(basePackage))
+                + ALL_CLASS_FILES;
+
+            final Resource[] resources = resolver.getResources(packageSearchPath);
+
+
+            for (final Resource resource: resources) {
+                final MetadataReader reader = factory.getMetadataReader(resource);
+                final AnnotationMetadata metadata = reader.getAnnotationMetadata();
+
+                if (scanAllPackages && shouldSkip(metadata.getClassName())) {
+                    continue;
+                }
+
+                for (Class< ? extends Annotation > annotation: annotations) {
+                    boolean concreteClass = !metadata.isInterface() && !metadata.isAbstract();
+                    if (metadata.isAnnotated(annotation.getName())) {
+                        if (concreteClass) {
+                            classes.get(annotation).add(loadClass(metadata.getClassName(), loader));
+                        } else {
+                            matchingInterfaces.get(annotation).add(metadata.getClassName());
+                        }
+                    } else if (concreteClass && metadata.getInterfaceNames().length > 0) {
+                        nonMatchingClasses.put(metadata.getClassName(), metadata.getInterfaceNames());
+                    }
+                }
+            }
+        }
+        if (!nonMatchingClasses.isEmpty()) {
+            for (Map.Entry<Class<? extends Annotation>, Collection<String>> e1 : matchingInterfaces.entrySet()) {
+                for (Map.Entry<String, String[]> e2 : nonMatchingClasses.entrySet()) {
+                    for (String intName : e2.getValue()) {
+                        if (e1.getValue().contains(intName)) {
+                            classes.get(e1.getKey()).add(loadClass(e2.getKey(), loader));
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        for (Map.Entry<Class<? extends Annotation>, Collection<String>> e : matchingInterfaces.entrySet()) {
+            if (classes.get(e.getKey()).isEmpty()) {
+                for (String intName : e.getValue()) {
+                    classes.get(e.getKey()).add(loadClass(intName, loader));
+                }
+            }
+        }
+
+        return classes;
+    }
+
+    @Override
+    protected List<URL> findResourcesInternal(Collection<String> basePackages,
+                                              String extension,
+                                              ClassLoader loader)
+        throws IOException {
+        final List<URL> resourceURLs = new ArrayList<>();
+        if (basePackages == null || basePackages.isEmpty()) {
+            return resourceURLs;
+        }
+        ResourcePatternResolver resolver = getResolver(loader);
+
+        for (final String basePackage: basePackages) {
+            final boolean scanAllPackages = basePackage.equals(WILDCARD);
+
+            String theBasePackage = basePackage;
+            if (theBasePackage.startsWith(CLASSPATH_URL_SCHEME)) {
+                theBasePackage = theBasePackage.substring(CLASSPATH_URL_SCHEME.length());
+            }
+
+            final String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+                + (scanAllPackages ? "" : basePackage.contains(WILDCARD) ? basePackage
+                    : ClassUtils.convertClassNameToResourcePath(theBasePackage)) + ALL_FILES + "." + extension;
+
+            final Resource[] resources = resolver.getResources(packageSearchPath);
+            for (final Resource resource: resources) {
+                resourceURLs.add(resource.getURL());
+            }
+        }
+
+        return resourceURLs;
+    }
+
+    private ResourcePatternResolver getResolver(ClassLoader loader) {
+        ResourcePatternResolver resolver = null;
+        
+        if (IN_OSGI) {
+            resolver = SpringOsgiUtil.getResolver(loader);
+        }
+        if (resolver == null) {
+            resolver = loader != null
+                ? new PathMatchingResourcePatternResolver(loader) : new PathMatchingResourcePatternResolver();
+        }
+        return resolver;
+    }
+
+    private boolean shouldSkip(final String classname) {
+        for (String packageToSkip: PACKAGES_TO_SKIP) {
+            if (classname.startsWith(packageToSkip)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private Class<?> loadClass(String className, ClassLoader loader)
+        throws ClassNotFoundException {
+        if (loader == null) {
+            return ClassLoaderUtils.loadClass(className, getClass());
+        }
+        return loader.loadClass(className);
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/util/StreamPrinter.java b/transform/src/patch/java/org/apache/cxf/common/util/StreamPrinter.java
new file mode 100644
index 0000000..fa88287
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/util/StreamPrinter.java
@@ -0,0 +1,63 @@
+/**
+ * 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.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+
+class StreamPrinter extends Thread {
+    private InputStream is;
+    private String msg;
+    private OutputStream os;
+
+    StreamPrinter(InputStream stream, String type, OutputStream redirect) {
+        is = stream;
+        msg = type;
+        os = redirect;
+    }
+
+    @Override
+    public void run() {
+        try {
+            PrintWriter pw = null;
+            if (os != null) {
+                pw = new PrintWriter(os);
+            }
+            InputStreamReader isr = new InputStreamReader(is);
+            BufferedReader br = new BufferedReader(isr);
+            String line = br.readLine();
+            while (line != null) {
+                if (pw != null) {
+                    pw.println(msg + " " + line);
+                }
+                line = br.readLine();
+            }
+            if (pw != null) {
+                pw.flush();
+            }
+        } catch (IOException ioe) {
+            throw new RuntimeException(ioe);
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/util/URIParserUtil.java b/transform/src/patch/java/org/apache/cxf/common/util/URIParserUtil.java
new file mode 100644
index 0000000..462896f
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/util/URIParserUtil.java
@@ -0,0 +1,209 @@
+/**
+ * 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.io.File;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.StringTokenizer;
+
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+
+public final class URIParserUtil {
+    private static final String EXCLUDED_CHARS = "<>\"{}|\\^`";
+
+    private URIParserUtil() {
+        // complete
+    }
+
+    private static boolean isExcluded(char ch) {
+        return ch <= 0x20 || ch >= 0x7F || EXCLUDED_CHARS.indexOf(ch) != -1;
+    }
+
+    public static URL[] pathToURLs(String path) {
+        StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
+        URL[] urls = new URL[st.countTokens()];
+        int count = 0;
+        while (st.hasMoreTokens()) {
+            File file = new File(st.nextToken());
+            URL url = null;
+            try {
+                url = file.toURI().toURL();
+            } catch (MalformedURLException e) {
+                e.printStackTrace();
+            }
+            if (url != null) {
+                urls[count++] = url;
+            }
+        }
+        if (urls.length != count) {
+            URL[] tmp = new URL[count];
+            System.arraycopy(urls, 0, tmp, 0, count);
+            urls = tmp;
+        }
+        return urls;
+    }
+
+    public static String escapeChars(String s) {
+        StringBuilder sb = new StringBuilder(s.length());
+
+        for (int x = 0; x < s.length(); x++) {
+            char ch = s.charAt(x);
+            if (isExcluded(ch)) {
+                byte[] bytes = Character.toString(ch).getBytes(StandardCharsets.UTF_8);
+                for (byte b : bytes) {
+                    sb.append('%');
+                    StringUtils.byteToHex(b, sb);
+                }
+            } else {
+                sb.append(ch);
+            }
+        }
+        return sb.toString();
+    }
+    public static String normalize(final String uri) {
+        URL url = null;
+        String result;
+        try {
+            url = new URL(uri);
+            result = escapeChars(url.toURI().normalize().toString().replace('\\', '/'));
+        } catch (MalformedURLException e1) {
+            try {
+                if (uri.startsWith("classpath:")) {
+                    url = ClassLoaderUtils.getResource(uri.substring(10), URIParserUtil.class);
+                    return url != null ? url.toExternalForm() : uri;
+                }
+                File file = new File(uri);
+                if (file.exists()) {
+                    return file.toURI().normalize().toString();
+                }
+                final String f;
+                if (uri.indexOf(':') != -1 && !uri.startsWith("/")) {
+                    f = "file:/" + uri;
+                } else {
+                    f = "file:" + uri;
+                }
+                url = new URL(f);
+                return escapeChars(url.toString().replace("\\", "/"));
+            } catch (Exception e2) {
+                return escapeChars(uri.replace("\\", "/"));
+            }
+        } catch (URISyntaxException e) {
+            result = escapeChars(url.toString().replace("\\", "/"));
+        }
+        return result;
+    }
+
+    public static String getAbsoluteURI(final String arg) {
+        if (arg == null) {
+            return null;
+        }
+
+        try {
+            URI uri = new URI(arg);
+            if ("file".equalsIgnoreCase(uri.getScheme())) {
+                if (!uri.isOpaque()) {
+                    return uri.normalize().toString();
+                }
+                return new File("").toURI().resolve(uri.getPath()).toString();
+            }
+            return normalize(arg);
+        } catch (Exception e2) {
+            return normalize(arg);
+        }
+    }
+
+    public static String relativize(String base, String toBeRelativized) throws URISyntaxException {
+        if (base == null || toBeRelativized == null) {
+            return null;
+        }
+        return relativize(new URI(base), new URI(toBeRelativized));
+    }
+
+    /**
+     * This is a custom implementation for doing what URI.relativize(URI uri) should be
+     * doing but is not actually doing when URI roots do not fully match.
+     * See http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6226081
+     *
+     * @param baseURI              The base URI
+     * @param toBeRelativizedURI   The URI to be relativized
+     * @return                  The string value of the URI you'd expect to get as result
+     *                          of calling baseURI.relativize(toBeRelativizedURI).
+     *                          null is returned if the parameters are null or are not
+     *                          both absolute or not absolute.
+     * @throws URISyntaxException
+     */
+    public static String relativize(URI baseURI, URI toBeRelativizedURI) throws URISyntaxException {
+        if (baseURI == null || toBeRelativizedURI == null) {
+            return null;
+        }
+        if (baseURI.isAbsolute() ^ toBeRelativizedURI.isAbsolute()) {
+            return null;
+        }
+        final String base = baseURI.getSchemeSpecificPart();
+        final String toBeRelativized = toBeRelativizedURI.getSchemeSpecificPart();
+        final int l1 = base.length();
+        final int l2 = toBeRelativized.length();
+        if (l1 == 0) {
+            return toBeRelativized;
+        }
+        int slashes = 0;
+        StringBuilder sb = new StringBuilder();
+        boolean differenceFound = false;
+        for (int i = 0; i < l1; i++) {
+            char c = base.charAt(i);
+            if (i < l2) {
+                if (!differenceFound && c == toBeRelativized.charAt(i)) {
+                    sb.append(c);
+                } else {
+                    differenceFound = true;
+                    if (c == '/') {
+                        slashes++;
+                    }
+                }
+            } else {
+                if (c == '/') {
+                    slashes++;
+                }
+            }
+        }
+        String rResolved = new URI(getRoot(sb.toString())).relativize(new URI(toBeRelativized)).toString();
+        StringBuilder relativizedPath = new StringBuilder();
+        for (int i = 0; i < slashes; i++) {
+            relativizedPath.append("../");
+        }
+        relativizedPath.append(rResolved);
+        return relativizedPath.toString();
+    }
+
+    private static String getRoot(String uri) {
+        int idx = uri.lastIndexOf('/');
+        if (idx == uri.length() - 1) {
+            return uri;
+        } else if (idx == -1) {
+            return "";
+        } else {
+            return uri.substring(0, idx + 1);
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/common/xmlschema/SchemaCollection.java b/transform/src/patch/java/org/apache/cxf/common/xmlschema/SchemaCollection.java
new file mode 100644
index 0000000..0f3a579
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/common/xmlschema/SchemaCollection.java
@@ -0,0 +1,389 @@
+/**
+ * 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.xmlschema;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.namespace.QName;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.ws.commons.schema.XmlSchema;
+import org.apache.ws.commons.schema.XmlSchemaAll;
+import org.apache.ws.commons.schema.XmlSchemaAttribute;
+import org.apache.ws.commons.schema.XmlSchemaAttributeGroupRef;
+import org.apache.ws.commons.schema.XmlSchemaAttributeOrGroupRef;
+import org.apache.ws.commons.schema.XmlSchemaChoice;
+import org.apache.ws.commons.schema.XmlSchemaCollection;
+import org.apache.ws.commons.schema.XmlSchemaComplexContentExtension;
+import org.apache.ws.commons.schema.XmlSchemaComplexContentRestriction;
+import org.apache.ws.commons.schema.XmlSchemaComplexType;
+import org.apache.ws.commons.schema.XmlSchemaContent;
+import org.apache.ws.commons.schema.XmlSchemaContentModel;
+import org.apache.ws.commons.schema.XmlSchemaElement;
+import org.apache.ws.commons.schema.XmlSchemaParticle;
+import org.apache.ws.commons.schema.XmlSchemaSequence;
+import org.apache.ws.commons.schema.XmlSchemaSequenceMember;
+import org.apache.ws.commons.schema.XmlSchemaSimpleContentExtension;
+import org.apache.ws.commons.schema.XmlSchemaSimpleContentRestriction;
+import org.apache.ws.commons.schema.XmlSchemaType;
+import org.apache.ws.commons.schema.extensions.ExtensionRegistry;
+import org.apache.ws.commons.schema.resolver.URIResolver;
+import org.apache.ws.commons.schema.utils.NamespaceMap;
+import org.apache.ws.commons.schema.utils.NamespacePrefixList;
+import org.apache.ws.commons.schema.utils.XmlSchemaObjectBase;
+
+/**
+ * Wrapper class for XmlSchemaCollection that deals with various quirks and bugs.
+ */
+public class SchemaCollection {
+
+    private final XmlSchemaCollection xmlSchemaCollection;
+    private final Map<XmlSchema, Set<XmlSchemaType>> xmlTypesCheckedForCrossImportsPerSchema
+        = new HashMap<>();
+
+    public SchemaCollection() {
+        this(new XmlSchemaCollection());
+    }
+
+    public SchemaCollection(XmlSchemaCollection col) {
+        xmlSchemaCollection = col;
+        if (xmlSchemaCollection.getNamespaceContext() == null) {
+            // an empty prefix map avoids extra checks for null.
+            xmlSchemaCollection.setNamespaceContext(new NamespaceMap());
+        }
+    }
+
+    public XmlSchemaCollection getXmlSchemaCollection() {
+        return xmlSchemaCollection;
+    }
+
+    public boolean equals(Object obj) {
+        if (obj instanceof SchemaCollection) {
+            return xmlSchemaCollection.equals(((SchemaCollection)obj).xmlSchemaCollection);
+        } else if (obj instanceof XmlSchemaCollection) {
+            return xmlSchemaCollection.equals(obj);
+        }
+        return false;
+    }
+
+    public XmlSchemaElement getElementByQName(QName qname) {
+        return xmlSchemaCollection.getElementByQName(qname);
+    }
+
+    public XmlSchemaAttribute getAttributeByQName(QName qname) {
+        return xmlSchemaCollection.getAttributeByQName(qname);
+    }
+
+    public ExtensionRegistry getExtReg() {
+        return xmlSchemaCollection.getExtReg();
+    }
+
+    public NamespacePrefixList getNamespaceContext() {
+        return xmlSchemaCollection.getNamespaceContext();
+    }
+
+    public XmlSchemaType getTypeByQName(QName schemaTypeName) {
+        return xmlSchemaCollection.getTypeByQName(schemaTypeName);
+    }
+
+    public XmlSchema[] getXmlSchema(String systemId) {
+        return xmlSchemaCollection.getXmlSchema(systemId);
+    }
+
+    public XmlSchema[] getXmlSchemas() {
+        return xmlSchemaCollection.getXmlSchemas();
+    }
+
+    public int hashCode() {
+        return xmlSchemaCollection.hashCode();
+    }
+
+    public void init() {
+        xmlSchemaCollection.init();
+    }
+
+    public XmlSchema read(Element elem, String uri) {
+        return xmlSchemaCollection.read(elem, uri);
+    }
+
+    public XmlSchema read(Document d, String uri) {
+        return xmlSchemaCollection.read(d, uri);
+    }
+
+    public XmlSchema read(Element elem) {
+        return xmlSchemaCollection.read(elem);
+    }
+
+    public void setBaseUri(String baseUri) {
+        xmlSchemaCollection.setBaseUri(baseUri);
+    }
+
+    public void setExtReg(ExtensionRegistry extReg) {
+        xmlSchemaCollection.setExtReg(extReg);
+    }
+
+    public void setNamespaceContext(NamespacePrefixList namespaceContext) {
+        xmlSchemaCollection.setNamespaceContext(namespaceContext);
+    }
+
+    public void setSchemaResolver(URIResolver schemaResolver) {
+        xmlSchemaCollection.setSchemaResolver(schemaResolver);
+    }
+
+    /**
+     * This function is not part of the XmlSchema API. Who knows why?
+     *
+     * @param namespaceURI targetNamespace
+     * @return schema, or null.
+     */
+    public XmlSchema getSchemaByTargetNamespace(String namespaceURI) {
+        for (XmlSchema schema : xmlSchemaCollection.getXmlSchemas()) {
+            if (namespaceURI != null && namespaceURI.equals(schema.getTargetNamespace())
+                || namespaceURI == null && schema.getTargetNamespace() == null) {
+                return schema;
+            }
+        }
+        return null;
+    }
+
+    public XmlSchema getSchemaForElement(QName name) {
+        for (XmlSchema schema : xmlSchemaCollection.getXmlSchemas()) {
+            if (name.getNamespaceURI().equals(schema.getTargetNamespace())) {
+
+                if (schema.getElementByName(name.getLocalPart()) != null) {
+                    return schema;
+                } else if (schema.getElementByName(name) != null) {
+                    return schema;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Once upon a time, XmlSchema had a bug in the constructor used in this function. So this wrapper was
+     * created to hold a workaround.
+     *
+     * @param namespaceURI TNS for new schema.
+     * @return new schema
+     */
+
+    public XmlSchema newXmlSchemaInCollection(String namespaceURI) {
+        return new XmlSchema(namespaceURI, xmlSchemaCollection);
+    }
+
+    /**
+     * Validate that a qualified name points to some namespace in the schema.
+     *
+     * @param qname
+     */
+    public void validateQNameNamespace(QName qname) {
+        // astonishingly, xmlSchemaCollection has no accessor by target URL.
+        if ("".equals(qname.getNamespaceURI())) {
+            return; // references to the 'unqualified' namespace are OK even if there is no schema for it.
+        }
+        for (XmlSchema schema : xmlSchemaCollection.getXmlSchemas()) {
+            if (schema.getTargetNamespace().equals(qname.getNamespaceURI())) {
+                return;
+            }
+        }
+        throw new InvalidXmlSchemaReferenceException(qname + " refers to unknown namespace.");
+    }
+
+    public void validateElementName(QName referrer, QName elementQName) {
+        XmlSchemaElement element = xmlSchemaCollection.getElementByQName(elementQName);
+        if (element == null) {
+            throw new InvalidXmlSchemaReferenceException(referrer + " references non-existent element "
+                                                         + elementQName);
+        }
+    }
+
+    public void validateTypeName(QName referrer, QName typeQName) {
+        XmlSchemaType type = xmlSchemaCollection.getTypeByQName(typeQName);
+        if (type == null) {
+            throw new InvalidXmlSchemaReferenceException(referrer + " references non-existent type "
+                                                         + typeQName);
+        }
+    }
+
+    public void addCrossImports() {
+        /*
+         * We need to inventory all the cross-imports to see if any are missing.
+         */
+        for (XmlSchema schema : xmlSchemaCollection.getXmlSchemas()) {
+            addOneSchemaCrossImports(schema);
+        }
+    }
+
+    private void addOneSchemaCrossImports(XmlSchema schema) {
+        /*
+         * We need to visit all the top-level items.
+         */
+        for (XmlSchemaElement element : schema.getElements().values()) {
+            addElementCrossImportsElement(schema, element);
+        }
+        for (XmlSchemaAttribute attribute : schema.getAttributes().values()) {
+            XmlSchemaUtils.addImportIfNeeded(schema, attribute.getRef().getTargetQName());
+            XmlSchemaUtils.addImportIfNeeded(schema, attribute.getSchemaTypeName());
+        }
+        for (XmlSchemaType type : schema.getSchemaTypes().values()) {
+            addCrossImportsType(schema, type);
+        }
+    }
+
+    private void addElementCrossImportsElement(XmlSchema schema, XmlSchemaElement item) {
+        XmlSchemaElement element = item;
+        XmlSchemaUtils.addImportIfNeeded(schema, element.getRef().getTargetQName());
+        XmlSchemaUtils.addImportIfNeeded(schema, element.getSchemaTypeName());
+        // if there's an anonymous type, it might have element refs in it.
+        XmlSchemaType schemaType = element.getSchemaType();
+        if (!crossImportsAdded(schema, schemaType)) {
+            addCrossImportsType(schema, schemaType);
+        }
+    }
+
+    /**
+     * Determines whether the schema has already received (cross) imports for the schemaType
+     *
+     * @param schema
+     * @param schemaType
+     * @return false if cross imports for schemaType must still be added to schema
+     */
+    private boolean crossImportsAdded(XmlSchema schema, XmlSchemaType schemaType) {
+        boolean result = true;
+        if (schemaType != null) {
+            Set<XmlSchemaType> xmlTypesCheckedForCrossImports;
+            if (!xmlTypesCheckedForCrossImportsPerSchema.containsKey(schema)) {
+                xmlTypesCheckedForCrossImports = new HashSet<>();
+                xmlTypesCheckedForCrossImportsPerSchema.put(schema, xmlTypesCheckedForCrossImports);
+            } else {
+                xmlTypesCheckedForCrossImports = xmlTypesCheckedForCrossImportsPerSchema.get(schema);
+            }
+            if (!xmlTypesCheckedForCrossImports.contains(schemaType)) {
+                // cross imports for this schemaType have not yet been added
+                xmlTypesCheckedForCrossImports.add(schemaType);
+                result = false;
+            }
+        }
+        return result;
+    }
+
+    private void addCrossImportsType(XmlSchema schema, XmlSchemaType schemaType) {
+        // the base type might cross schemas.
+        if (schemaType instanceof XmlSchemaComplexType) {
+            XmlSchemaComplexType complexType = (XmlSchemaComplexType)schemaType;
+            XmlSchemaUtils.addImportIfNeeded(schema, complexType.getBaseSchemaTypeName());
+            addCrossImports(schema, complexType.getContentModel());
+            addCrossImportsAttributeList(schema, complexType.getAttributes());
+            // could it be a choice or something else?
+
+            if (complexType.getParticle() instanceof XmlSchemaChoice) {
+                XmlSchemaChoice choice = (XmlSchemaChoice)complexType.getParticle();
+                addCrossImports(schema, choice);
+            } else if (complexType.getParticle() instanceof XmlSchemaAll) {
+                XmlSchemaAll all = (XmlSchemaAll)complexType.getParticle();
+                addCrossImports(schema, all);
+            } else if (complexType.getParticle() instanceof XmlSchemaSequence) {
+                XmlSchemaSequence sequence = (XmlSchemaSequence)complexType.getParticle();
+                addCrossImports(schema, sequence);
+            }
+        }
+    }
+    private void addCrossImports(XmlSchema schema, XmlSchemaAll all) {
+        for (XmlSchemaObjectBase seqMember : all.getItems()) {
+            if (seqMember instanceof XmlSchemaElement) {
+                addElementCrossImportsElement(schema, (XmlSchemaElement)seqMember);
+            }
+        }
+    }
+
+    private void addCrossImports(XmlSchema schema, XmlSchemaChoice choice) {
+        for (XmlSchemaObjectBase seqMember : choice.getItems()) {
+            if (seqMember instanceof XmlSchemaElement) {
+                addElementCrossImportsElement(schema, (XmlSchemaElement)seqMember);
+            }
+        }
+    }
+    private void addCrossImports(XmlSchema schema, XmlSchemaSequence sequence) {
+        for (XmlSchemaSequenceMember seqMember : sequence.getItems()) {
+            if (seqMember instanceof XmlSchemaElement) {
+                addElementCrossImportsElement(schema, (XmlSchemaElement)seqMember);
+            }
+        }
+    }
+
+    private void addCrossImportsAttributeList(XmlSchema schema, List<XmlSchemaAttributeOrGroupRef> list) {
+        for (XmlSchemaAttributeOrGroupRef attr : list) {
+            final QName ref;
+            if (attr instanceof XmlSchemaAttribute) {
+                ref = ((XmlSchemaAttribute)attr).getRef().getTargetQName();
+            } else {
+                XmlSchemaAttributeGroupRef groupRef = (XmlSchemaAttributeGroupRef)attr;
+                ref = groupRef.getRef().getTargetQName();
+            }
+
+            if (ref != null) {
+                XmlSchemaUtils.addImportIfNeeded(schema, ref);
+            }
+        }
+    }
+
+    private void addCrossImports(XmlSchema schema, XmlSchemaContentModel contentModel) {
+        if (contentModel == null) {
+            return;
+        }
+        XmlSchemaContent content = contentModel.getContent();
+        if (content == null) {
+            return;
+        }
+        if (content instanceof XmlSchemaComplexContentExtension) {
+            XmlSchemaComplexContentExtension extension = (XmlSchemaComplexContentExtension)content;
+            XmlSchemaUtils.addImportIfNeeded(schema, extension.getBaseTypeName());
+            addCrossImportsAttributeList(schema, extension.getAttributes());
+            XmlSchemaParticle particle = extension.getParticle();
+            if (particle instanceof XmlSchemaSequence) {
+                addCrossImports(schema, (XmlSchemaSequence)particle);
+            } else if (particle instanceof XmlSchemaChoice) {
+                addCrossImports(schema, (XmlSchemaChoice)particle);
+            } else if (particle instanceof XmlSchemaAll) {
+                addCrossImports(schema, (XmlSchemaAll)particle);
+            }
+        } else if (content instanceof XmlSchemaComplexContentRestriction) {
+            XmlSchemaComplexContentRestriction restriction = (XmlSchemaComplexContentRestriction)content;
+            XmlSchemaUtils.addImportIfNeeded(schema, restriction.getBaseTypeName());
+            addCrossImportsAttributeList(schema, restriction.getAttributes());
+        } else if (content instanceof XmlSchemaSimpleContentExtension) {
+            XmlSchemaSimpleContentExtension extension = (XmlSchemaSimpleContentExtension)content;
+            XmlSchemaUtils.addImportIfNeeded(schema, extension.getBaseTypeName());
+            addCrossImportsAttributeList(schema, extension.getAttributes());
+        } else if (content instanceof XmlSchemaSimpleContentRestriction) {
+            XmlSchemaSimpleContentRestriction restriction = (XmlSchemaSimpleContentRestriction)content;
+            XmlSchemaUtils.addImportIfNeeded(schema, restriction.getBaseTypeName());
+            addCrossImportsAttributeList(schema, restriction.getAttributes());
+        }
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/configuration/jsse/MultiKeyPasswordKeyManager.java b/transform/src/patch/java/org/apache/cxf/configuration/jsse/MultiKeyPasswordKeyManager.java
new file mode 100644
index 0000000..6a1aee5
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/configuration/jsse/MultiKeyPasswordKeyManager.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.configuration.jsse;
+
+import java.net.Socket;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.X509KeyManager;
+
+public class MultiKeyPasswordKeyManager implements X509KeyManager {
+    private final KeyStore mKeyStore;
+    private final String mKeyAlias;
+    private final String mKeyPassword;
+
+    public MultiKeyPasswordKeyManager(KeyStore keystore, String keyAlias, String keyPassword) {
+        mKeyStore = keystore;
+        mKeyAlias = keyAlias;
+        mKeyPassword = keyPassword;
+    }
+
+    public String[] getClientAliases(String keyType, Principal[] issuers) {
+        return new String[] {
+            mKeyAlias
+        };
+    }
+
+    public String[] getServerAliases(String keyType, Principal[] issuers) {
+        return new String[] {
+            mKeyAlias
+        };
+    }
+    public X509Certificate[] getCertificateChain(String alias) {
+        final Certificate[] chain;
+        try {
+            chain = mKeyStore.getCertificateChain(alias);
+        } catch (KeyStoreException kse) {
+            throw new RuntimeException(kse);
+        }
+        final X509Certificate[] certChain = new X509Certificate[chain.length];
+        for (int i = 0; i < chain.length; i++) {
+            certChain[i] = (X509Certificate)chain[i];
+        }
+        return certChain;
+    }
+
+    public PrivateKey getPrivateKey(String alias) {
+        try {
+            return (PrivateKey)mKeyStore.getKey(alias, mKeyPassword.toCharArray());
+        } catch (GeneralSecurityException gse) {
+            throw new RuntimeException(gse);
+        }
+    }
+
+    public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
+        return mKeyAlias;
+    }
+
+    public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
+        return mKeyAlias;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/configuration/jsse/TLSClientParameters.java b/transform/src/patch/java/org/apache/cxf/configuration/jsse/TLSClientParameters.java
new file mode 100644
index 0000000..3e0099a
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/configuration/jsse/TLSClientParameters.java
@@ -0,0 +1,258 @@
+/**
+ * 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.configuration.jsse;
+
+import java.util.List;
+import java.util.Objects;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * This class extends {@link TLSParameterBase} with client-specific
+ * SSL/TLS parameters.
+ *
+ */
+public class TLSClientParameters extends TLSParameterBase {
+    private boolean disableCNCheck;
+    private SSLSocketFactory sslSocketFactory;
+    private int sslCacheTimeout = 86400;
+    private boolean useHttpsURLConnectionDefaultSslSocketFactory;
+    private boolean useHttpsURLConnectionDefaultHostnameVerifier;
+    private HostnameVerifier hostnameVerifier;
+    private SSLContext sslContext;
+
+    /**
+     * Set custom HostnameVerifier
+     * @param verifier hostname verifier
+     */
+    public void setHostnameVerifier(HostnameVerifier verifier) {
+        hostnameVerifier = verifier;
+    }
+
+    /**
+     * Get custom HostnameVerifier
+     * @return hostname verifier
+     */
+    public HostnameVerifier getHostnameVerifier() {
+        return hostnameVerifier;
+    }
+    /**
+     * Set whether or not JSEE should omit checking if the host name
+     * specified in the URL matches that of the Common Name
+     * (CN) on the server's certificate. Default is false;
+     * this attribute should not be set to true during production use.
+     */
+    public void setDisableCNCheck(boolean disableCNCheck) {
+        this.disableCNCheck = disableCNCheck;
+    }
+
+    /**
+     * Returns whether or not JSSE omits checking if the
+     * host name specified in the URL matches that of the Common Name
+     * (CN) on the server's certificate.
+     */
+    public boolean isDisableCNCheck() {
+        return disableCNCheck;
+    }
+
+    /**
+     * This sets the SSLSocketFactory to use, causing all other properties of
+     * this bean (and its superclass) to get ignored (this takes precendence).
+     */
+    public final void setSSLSocketFactory(SSLSocketFactory factory) {
+        sslSocketFactory = factory;
+    }
+
+
+    /**
+     * Returns the SSLSocketFactory to be used, or null if none has been set.
+     */
+    public final SSLSocketFactory getSSLSocketFactory() {
+        return sslSocketFactory;
+    }
+
+    /**
+     * Returns the SSL cache timeout in seconds if it has been configured or the default value
+     */
+    public int getSslCacheTimeout() {
+        return sslCacheTimeout;
+    }
+
+    /**
+     * This sets the SSL Session Cache timeout value in seconds for client sessions handled by CXF
+     */
+    public void setSslCacheTimeout(int sslCacheTimeout) {
+        this.sslCacheTimeout = sslCacheTimeout;
+    }
+
+
+    /**
+     * Returns whether or not {@link javax.net.ssl.HttpsURLConnection#getDefaultSSLSocketFactory()} should be
+     * used to create https connections. If <code>true</code> , {@link #getJsseProvider()} ,
+     * {@link #getSecureSocketProtocol()}, {@link #getTrustManagers()}, {@link #getKeyManagers()},
+     * {@link #getSecureRandom()}, {@link #getCipherSuites()} and {@link #getCipherSuitesFilter()} are
+     * ignored.
+     */
+    public boolean isUseHttpsURLConnectionDefaultSslSocketFactory() {
+        return useHttpsURLConnectionDefaultSslSocketFactory;
+    }
+
+    /**
+     * Sets whether or not {@link javax.net.ssl.HttpsURLConnection#getDefaultSSLSocketFactory()} should be
+     * used to create https connections.
+     *
+     * @see #isUseHttpsURLConnectionDefaultSslSocketFactory()
+     */
+    public void setUseHttpsURLConnectionDefaultSslSocketFactory(
+                      boolean useHttpsURLConnectionDefaultSslSocketFactory) {
+        this.useHttpsURLConnectionDefaultSslSocketFactory = useHttpsURLConnectionDefaultSslSocketFactory;
+    }
+
+    /**
+     * Returns whether or not {@link javax.net.ssl.HttpsURLConnection#getDefaultHostnameVerifier()} should be
+     * used to create https connections. If <code>true</code>, {@link #isDisableCNCheck()} is ignored.
+     */
+    public boolean isUseHttpsURLConnectionDefaultHostnameVerifier() {
+        return useHttpsURLConnectionDefaultHostnameVerifier;
+    }
+
+    /**
+     * Sets whether or not {@link javax.net.ssl.HttpsURLConnection#getDefaultHostnameVerifier()} should be
+     * used to create https connections.
+     *
+     * @see #isUseHttpsURLConnectionDefaultHostnameVerifier()
+     */
+    public void setUseHttpsURLConnectionDefaultHostnameVerifier(
+                      boolean useHttpsURLConnectionDefaultHostnameVerifier) {
+        this.useHttpsURLConnectionDefaultHostnameVerifier = useHttpsURLConnectionDefaultHostnameVerifier;
+    }
+
+    public int hashCode() {
+        int hash = disableCNCheck ? 37 : 17;
+        if (sslSocketFactory != null) {
+            hash = hash * 41 + System.identityHashCode(sslSocketFactory);
+        }
+        if (sslContext != null) {
+            hash = hash * 41 + System.identityHashCode(sslContext);
+        }
+        hash = hash(hash, useHttpsURLConnectionDefaultSslSocketFactory);
+        hash = hash(hash, useHttpsURLConnectionDefaultHostnameVerifier);
+        hash = hash(hash, sslCacheTimeout);
+        hash = hash(hash, secureRandom);
+        hash = hash(hash, protocol);
+        hash = hash(hash, certAlias);
+        hash = hash(hash, provider);
+        for (String cs : ciphersuites) {
+            hash = hash(hash, cs);
+        }
+        hash = hash(hash, keyManagers);
+        hash = hash(hash, trustManagers);
+        if (cipherSuiteFilters != null) {
+            hash = hash(hash, cipherSuiteFilters.getInclude());
+            hash = hash(hash, cipherSuiteFilters.getExclude());
+        }
+        if (certConstraints != null) {
+            hash = hash(hash, certConstraints.getIssuerDNConstraints());
+            hash = hash(hash, certConstraints.getSubjectDNConstraints());
+        }
+        return hash;
+    }
+    private int hash(int i, Object o) {
+        if (o != null) {
+            return i * 37 + o.hashCode();
+        }
+        return i;
+    }
+    private int hash(int i, Object[] os) {
+        if (os == null) {
+            return i;
+        }
+        for (Object o : os) {
+            i = hash(i, o);
+        }
+        return i;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (o instanceof TLSClientParameters) {
+            TLSClientParameters that = (TLSClientParameters)o;
+            boolean eq = disableCNCheck == that.disableCNCheck;
+            eq &= sslSocketFactory == that.sslSocketFactory;
+            eq &= sslContext == that.sslContext;
+            eq &= useHttpsURLConnectionDefaultSslSocketFactory == that.useHttpsURLConnectionDefaultSslSocketFactory;
+            eq &= useHttpsURLConnectionDefaultHostnameVerifier == that.useHttpsURLConnectionDefaultHostnameVerifier;
+            eq &= sslCacheTimeout == that.sslCacheTimeout;
+            eq &= secureRandom == that.secureRandom;
+            eq &= Objects.equals(certAlias, that.certAlias);
+            eq &= Objects.equals(protocol, that.protocol);
+            eq &= Objects.equals(provider, that.provider);
+            eq &= equals(ciphersuites, that.ciphersuites);
+            eq &= Objects.deepEquals(keyManagers, that.keyManagers);
+            eq &= Objects.deepEquals(trustManagers, that.trustManagers);
+            if (cipherSuiteFilters != null) {
+                if (that.cipherSuiteFilters != null) {
+                    eq &= equals(cipherSuiteFilters.getExclude(), that.cipherSuiteFilters.getExclude());
+                    eq &= equals(cipherSuiteFilters.getInclude(), that.cipherSuiteFilters.getInclude());
+                } else {
+                    eq = false;
+                }
+            } else {
+                eq &= that.cipherSuiteFilters == null;
+            }
+            if (certConstraints != null) {
+                if (that.certConstraints != null) {
+                    eq &= Objects.equals(certConstraints.getIssuerDNConstraints(),
+                                 that.certConstraints.getIssuerDNConstraints());
+                    eq &= Objects.equals(certConstraints.getSubjectDNConstraints(),
+                                 that.certConstraints.getSubjectDNConstraints());
+                } else {
+                    eq = false;
+                }
+            } else {
+                eq &= that.certConstraints == null;
+            }
+            return eq;
+        }
+        return false;
+    }
+
+    private static boolean equals(final List<?> obj1, final List<?> obj2) {
+        return obj1.equals(obj2);
+    }
+
+    /**
+     * Get the SSLContext parameter to use (if it has been set)
+     */
+    public SSLContext getSslContext() {
+        return sslContext;
+    }
+
+    /**
+     * Set an SSLContext parameter to use to create https connections
+     */
+    public void setSslContext(SSLContext sslContext) {
+        this.sslContext = sslContext;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/configuration/jsse/TLSParameterJaxBUtils.java b/transform/src/patch/java/org/apache/cxf/configuration/jsse/TLSParameterJaxBUtils.java
new file mode 100644
index 0000000..bd16aa4
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/configuration/jsse/TLSParameterJaxBUtils.java
@@ -0,0 +1,420 @@
+/**
+ * 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.configuration.jsse;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.net.ssl.CertPathTrustManagerParameters;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.configuration.security.CertStoreType;
+import org.apache.cxf.configuration.security.KeyManagersType;
+import org.apache.cxf.configuration.security.KeyStoreType;
+import org.apache.cxf.configuration.security.SecureRandomParameters;
+import org.apache.cxf.configuration.security.TrustManagersType;
+import org.apache.cxf.resource.ResourceManager;
+
+/**
+ * This class provides some functionality to convert the JAXB
+ * generated types in the security.xsd to the items needed
+ * to programatically configure the HTTPConduit and HTTPDestination
+ * with TLSClientParameters and TLSServerParameters respectively.
+ */
+public final class TLSParameterJaxBUtils {
+
+    private static final Logger LOG =
+        LogUtils.getL7dLogger(TLSParameterJaxBUtils.class);
+
+    private TLSParameterJaxBUtils() {
+        // empty
+    }
+    /**
+     * This method converts the JAXB generated type into a SecureRandom.
+     */
+    public static SecureRandom getSecureRandom(
+            SecureRandomParameters secureRandomParams
+    ) throws GeneralSecurityException {
+
+        SecureRandom secureRandom = null;
+        if (secureRandomParams != null) {
+            String secureRandomAlg =
+                secureRandomParams.getAlgorithm();
+            String randomProvider =
+                secureRandomParams.getProvider();
+            if (randomProvider != null) {
+                secureRandom = secureRandomAlg != null
+                               ? SecureRandom.getInstance(
+                                       secureRandomAlg,
+                                       randomProvider)
+                               : null;
+            } else {
+                secureRandom = secureRandomAlg != null
+                               ? SecureRandom.getInstance(
+                                       secureRandomAlg)
+                               : null;
+            }
+        }
+        return secureRandom;
+    }
+
+    public static KeyStore getKeyStore(KeyStoreType kst) throws GeneralSecurityException, IOException {
+        return getKeyStore(kst, false);
+    }
+
+    /**
+     * This method converts a JAXB generated KeyStoreType into a KeyStore.
+     */
+    public static KeyStore getKeyStore(KeyStoreType kst, boolean trustStore)
+        throws GeneralSecurityException,
+               IOException {
+
+        if (kst == null) {
+            return null;
+        }
+        final String type;
+        if (trustStore) {
+            type = SSLUtils.getTrustStoreType(kst.isSetType()
+                                     ? kst.getType() : null, LOG, KeyStore.getDefaultType());
+        } else {
+            type = SSLUtils.getKeystoreType(kst.isSetType()
+                                 ? kst.getType() : null, LOG, KeyStore.getDefaultType());
+        }
+
+        char[] password = kst.isSetPassword()
+                    ? deobfuscate(kst.getPassword())
+                    : null;
+        if (password == null) {
+            final String tmp;
+            if (trustStore) {
+                tmp = SSLUtils.getTruststorePassword(null, LOG);
+            } else {
+                tmp = SSLUtils.getKeystorePassword(null, LOG);
+            }
+            if (tmp != null) {
+                password = tmp.toCharArray();
+            }
+        }
+        final String provider;
+        if (trustStore) {
+            provider = SSLUtils.getTruststoreProvider(kst.isSetProvider() ? kst.getProvider() : null, LOG);
+        } else {
+            provider = SSLUtils.getKeystoreProvider(kst.isSetProvider() ? kst.getProvider() : null, LOG);
+        }
+        KeyStore keyStore = provider == null
+                    ? KeyStore.getInstance(type)
+                    : KeyStore.getInstance(type, provider);
+
+        if (kst.isSetFile()) {
+            try (InputStream kstInputStream = Files.newInputStream(Paths.get(kst.getFile()))) {
+                keyStore.load(kstInputStream, password);
+            }
+        } else if (kst.isSetResource()) {
+            final InputStream is = getResourceAsStream(kst.getResource());
+            if (is == null) {
+                final String msg =
+                    "Could not load keystore resource " + kst.getResource();
+                LOG.severe(msg);
+                throw new IOException(msg);
+            }
+            keyStore.load(is, password);
+        } else if (kst.isSetUrl()) {
+            keyStore.load(new URL(kst.getUrl()).openStream(), password);
+        } else {
+            final String loc;
+            if (trustStore) {
+                loc = SSLUtils.getTruststore(null, LOG);
+            } else {
+                loc = SSLUtils.getKeystore(null, LOG);
+            }
+            if (loc != null) {
+                try (InputStream ins = Files.newInputStream(Paths.get(loc))) {
+                    keyStore.load(ins, password);
+                } catch (NoSuchFileException ex) {
+                    // Fall back to load the location as a stream
+                    try (InputStream ins = getResourceAsStream(loc)) {
+                        keyStore.load(ins, password);
+                    }
+                }
+            }
+        }
+        return keyStore;
+    }
+
+    /**
+     * This method converts a JAXB generated CertStoreType into a KeyStore.
+     */
+    public static KeyStore getKeyStore(final CertStoreType pst)
+        throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
+
+        if (pst == null) {
+            return null;
+        }
+        String type;
+        if (pst.isSetType()) {
+            type = pst.getType();
+        } else {
+            type = KeyStore.getDefaultType();
+        }
+        if (pst.isSetFile()) {
+            InputStream is = Files.newInputStream(Paths.get(pst.getFile()));
+            return createTrustStore(is, type);
+        }
+        if (pst.isSetResource()) {
+            final InputStream is = getResourceAsStream(pst.getResource());
+            if (is == null) {
+                final String msg =
+                    "Could not load truststore resource " + pst.getResource();
+                LOG.severe(msg);
+                throw new IOException(msg);
+            }
+            return createTrustStore(is, type);
+        }
+        if (pst.isSetUrl()) {
+            return createTrustStore(new URL(pst.getUrl()).openStream(), type);
+        }
+        throw new IllegalArgumentException("Could not create KeyStore based on information in CertStoreType");
+    }
+
+    private static InputStream getResourceAsStream(String resource) {
+        InputStream is = ClassLoaderUtils.getResourceAsStream(resource, TLSParameterJaxBUtils.class);
+        if (is == null) {
+            Bus bus = BusFactory.getThreadDefaultBus(true);
+            ResourceManager rm = bus.getExtension(ResourceManager.class);
+            if (rm != null) {
+                is = rm.getResourceAsStream(resource);
+            }
+        }
+        return is;
+    }
+
+    /**
+     * Create a KeyStore containing the trusted CA certificates contained
+     * in the supplied input stream.
+     */
+    private static KeyStore createTrustStore(final InputStream is, String type)
+        throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
+
+        final Collection<? extends Certificate> certs = loadCertificates(is);
+        final KeyStore keyStore =
+            KeyStore.getInstance(type);
+        keyStore.load(null, null);
+        for (Certificate cert : certs) {
+            final X509Certificate xcert = (X509Certificate) cert;
+            keyStore.setCertificateEntry(
+                xcert.getSubjectX500Principal().getName(),
+                cert
+            );
+        }
+        return keyStore;
+    }
+
+    /**
+     * load the certificates as X.509 certificates
+     */
+    private static Collection<? extends Certificate> loadCertificates(final InputStream is)
+        throws IOException, CertificateException {
+
+        final CertificateFactory factory = CertificateFactory.getInstance("X.509");
+        return factory.generateCertificates(is);
+    }
+
+    private static char[] deobfuscate(String s) {
+        // From the Jetty org.eclipse.jetty.http.security.Password class
+        if (!s.startsWith("OBF:")) {
+            return s.toCharArray();
+        }
+        s = s.substring(4);
+
+        char[] b = new char[s.length() / 2];
+        int l = 0;
+        for (int i = 0; i < s.length(); i += 4) {
+            String x = s.substring(i, i + 4);
+            int i0 = Integer.parseInt(x, 36);
+            int i1 = i0 / 256;
+            int i2 = i0 % 256;
+            b[l++] = (char) ((i1 + i2 - 254) / 2);
+        }
+
+        return new String(b, 0, l).toCharArray();
+    }
+
+    /**
+     * This method converts the JAXB KeyManagersType into a list of
+     * JSSE KeyManagers.
+     */
+    public static KeyManager[] getKeyManagers(KeyManagersType kmc)
+        throws GeneralSecurityException,
+               IOException {
+
+        KeyStore keyStore = getKeyStore(kmc.getKeyStore(), false);
+
+        String alg = kmc.isSetFactoryAlgorithm()
+                     ? kmc.getFactoryAlgorithm()
+                     : KeyManagerFactory.getDefaultAlgorithm();
+
+        char[] keyPass = getKeyPassword(kmc);
+
+        KeyManagerFactory fac =
+                     kmc.isSetProvider()
+                     ? KeyManagerFactory.getInstance(alg, kmc.getProvider())
+                     : KeyManagerFactory.getInstance(alg);
+
+        fac.init(keyStore, keyPass);
+
+        return fac.getKeyManagers();
+    }
+
+    /**
+     * This method converts the JAXB KeyManagersType into a list of
+     * JSSE KeyManagers.
+     */
+    public static KeyManager[] getKeyManagers(KeyManagersType kmc, String alias)
+        throws GeneralSecurityException,
+               IOException {
+
+        KeyStore keyStore = getKeyStore(kmc.getKeyStore(), false);
+
+        String alg = kmc.isSetFactoryAlgorithm()
+                     ? kmc.getFactoryAlgorithm()
+                     : KeyManagerFactory.getDefaultAlgorithm();
+
+        char[] keyPass = getKeyPassword(kmc);
+
+        KeyManagerFactory fac =
+                     kmc.isSetProvider()
+                     ? KeyManagerFactory.getInstance(alg, kmc.getProvider())
+                     : KeyManagerFactory.getInstance(alg);
+
+        try {
+            fac.init(keyStore, keyPass);
+
+            return fac.getKeyManagers();
+        } catch (java.security.UnrecoverableKeyException uke) {
+            //jsse has the restriction that different key in keystore
+            //cannot has different password, use MultiKeyPasswordKeyManager
+            //as fallback when this happen
+            MultiKeyPasswordKeyManager manager
+                = new MultiKeyPasswordKeyManager(keyStore, alias,
+                                             new String(keyPass));
+            return new KeyManager[]{manager};
+        }
+    }
+
+    private static char[] getKeyPassword(KeyManagersType kmc) {
+        char[] keyPass = kmc.isSetKeyPassword()
+            ? deobfuscate(kmc.getKeyPassword())
+            : null;
+
+        if (keyPass != null) {
+            return keyPass;
+        }
+
+        String callbackHandlerClass = kmc.getKeyPasswordCallbackHandler();
+        if (callbackHandlerClass == null) {
+            return null;
+        }
+        try {
+            final CallbackHandler ch = (CallbackHandler) ClassLoaderUtils
+                .loadClass(callbackHandlerClass, TLSParameterJaxBUtils.class).newInstance();
+            String prompt = kmc.getKeyStore().getFile();
+            if (prompt == null) {
+                prompt = kmc.getKeyStore().getResource();
+            }
+            PasswordCallback pwCb = new PasswordCallback(prompt, false);
+            PasswordCallback[] callbacks = new PasswordCallback[] {pwCb};
+            ch.handle(callbacks);
+            keyPass = callbacks[0].getPassword();
+        } catch (Exception e) {
+            LOG.log(Level.WARNING,
+                    "Cannot load key password from callback handler: " + e.getMessage(), e);
+        }
+        return keyPass;
+    }
+
+    /**
+     * This method converts the JAXB TrustManagersType into a list of
+     * JSSE TrustManagers.
+     */
+    @Deprecated
+    public static TrustManager[] getTrustManagers(TrustManagersType tmc)
+        throws GeneralSecurityException,
+               IOException {
+        return getTrustManagers(tmc, false);
+    }
+
+    public static TrustManager[] getTrustManagers(TrustManagersType tmc, boolean enableRevocation)
+        throws GeneralSecurityException,
+               IOException {
+
+        final KeyStore keyStore =
+            tmc.isSetKeyStore()
+                ? getKeyStore(tmc.getKeyStore(), true)
+                : (tmc.isSetCertStore()
+                    ? getKeyStore(tmc.getCertStore())
+                    : null);
+
+        String alg = tmc.isSetFactoryAlgorithm()
+                     ? tmc.getFactoryAlgorithm()
+                     : TrustManagerFactory.getDefaultAlgorithm();
+
+        TrustManagerFactory fac =
+                     tmc.isSetProvider()
+                     ? TrustManagerFactory.getInstance(alg, tmc.getProvider())
+                     : TrustManagerFactory.getInstance(alg);
+
+        if (enableRevocation) {
+            PKIXBuilderParameters param = new PKIXBuilderParameters(keyStore, new X509CertSelector());
+            param.setRevocationEnabled(true);
+
+            fac.init(new CertPathTrustManagerParameters(param));
+        } else {
+            fac.init(keyStore);
+        }
+
+        return fac.getTrustManagers();
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/configuration/spring/ConfigurerImpl.java b/transform/src/patch/java/org/apache/cxf/configuration/spring/ConfigurerImpl.java
new file mode 100644
index 0000000..d920936
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/configuration/spring/ConfigurerImpl.java
@@ -0,0 +1,288 @@
+/**
+ * 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.configuration.spring;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.apache.cxf.common.injection.NoJSR250Annotations;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.configuration.Configurable;
+import org.apache.cxf.configuration.Configurer;
+import org.apache.cxf.extension.BusExtension;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.AbstractBeanFactory;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.wiring.BeanConfigurerSupport;
+import org.springframework.beans.factory.wiring.BeanWiringInfo;
+import org.springframework.beans.factory.wiring.BeanWiringInfoResolver;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ConfigurableApplicationContext;
+
+@NoJSR250Annotations
+public class ConfigurerImpl extends BeanConfigurerSupport
+    implements Configurer, ApplicationContextAware, BusExtension {
+
+    private static final Logger LOG = LogUtils.getL7dLogger(ConfigurerImpl.class);
+
+    private Set<ApplicationContext> appContexts;
+    private final Map<String, List<MatcherHolder>> wildCardBeanDefinitions = new TreeMap<>();
+    private BeanFactory beanFactory;
+
+    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() {
+        // complete
+    }
+
+    public ConfigurerImpl(ApplicationContext ac) {
+        setApplicationContext(ac);
+    }
+
+    @Override
+    public void setBeanFactory(BeanFactory beanFactory) {
+        this.beanFactory = beanFactory;
+        super.setBeanFactory(beanFactory);
+    }
+
+    private void initWildcardDefinitionMap() {
+        if (null != appContexts) {
+            for (ApplicationContext appContext : appContexts) {
+                for (String n : appContext.getBeanDefinitionNames()) {
+                    if (isWildcardBeanName(n)) {
+                        AutowireCapableBeanFactory bf = appContext.getAutowireCapableBeanFactory();
+                        BeanDefinitionRegistry bdr = (BeanDefinitionRegistry) bf;
+                        BeanDefinition bd = bdr.getBeanDefinition(n);
+                        String className = bd.getBeanClassName();
+                        if (null != className) {
+                            final String name = n.charAt(0) != '*' ? n
+                                    : "." + n.replaceAll("\\.", "\\."); //old wildcard
+                            try {
+                                Matcher matcher = Pattern.compile(name).matcher("");
+                                List<MatcherHolder> m = wildCardBeanDefinitions.get(className);
+                                if (m == null) {
+                                    m = new ArrayList<>();
+                                    wildCardBeanDefinitions.put(className, m);
+                                }
+                                MatcherHolder holder = new MatcherHolder(n, matcher);
+                                m.add(holder);
+                            } catch (PatternSyntaxException npe) {
+                                //not a valid patter, we'll ignore
+                            }
+                        } else {
+                            LogUtils.log(LOG, Level.WARNING, "WILDCARD_BEAN_ID_WITH_NO_CLASS_MSG", n);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    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 == appContexts) {
+            return;
+        }
+
+        if (null == bn) {
+            bn = getBeanName(beanInstance);
+            if (null == bn) {
+                return;
+            }
+        }
+
+        if (checkWildcards) {
+            configureWithWildCard(bn, beanInstance);
+        }
+
+        final String beanName = bn;
+        setBeanWiringInfoResolver(new BeanWiringInfoResolver() {
+            public BeanWiringInfo resolveWiringInfo(Object instance) {
+                if (!beanName.isEmpty()) {
+                    return new BeanWiringInfo(beanName);
+                }
+                return null;
+            }
+        });
+
+        for (ApplicationContext appContext : appContexts) {
+            if (appContext.containsBean(bn)) {
+                this.setBeanFactory(appContext.getAutowireCapableBeanFactory());
+            }
+        }
+
+        try {
+            //this will prevent a call into the AbstractBeanFactory.markBeanAsCreated(...)
+            //which saves ALL the names into a HashSet.  For URL based configuration,
+            //this can leak memory
+            if (beanFactory instanceof AbstractBeanFactory) {
+                ((AbstractBeanFactory)beanFactory).getMergedBeanDefinition(bn);
+            }
+            super.configureBean(beanInstance);
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.fine("Successfully performed injection.");
+            }
+        } catch (NoSuchBeanDefinitionException ex) {
+            // users often wonder why the settings in their configuration files seem
+            // to have no effect - the most common cause is that they have been using
+            // incorrect bean ids
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.log(Level.FINE, "NO_MATCHING_BEAN_MSG", beanName);
+            }
+        }
+    }
+
+    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();
+            }
+        }
+    }
+
+    private boolean isWildcardBeanName(String bn) {
+        return bn.indexOf('*') != -1 || bn.indexOf('?') != -1
+            || (bn.indexOf('(') != -1 && bn.indexOf(')') != -1);
+    }
+
+    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;
+    }
+
+    public final void setApplicationContext(ApplicationContext ac) {
+        appContexts = new CopyOnWriteArraySet<>();
+        addApplicationContext(ac);
+        this.beanFactory = ac.getAutowireCapableBeanFactory();
+        super.setBeanFactory(this.beanFactory);
+    }
+
+    public final void addApplicationContext(ApplicationContext ac) {
+        if (!appContexts.contains(ac)) {
+            appContexts.add(ac);
+            List<ApplicationContext> inactiveApplicationContexts = new ArrayList<>();
+            Iterator<ApplicationContext> it = appContexts.iterator();
+            while (it.hasNext()) {
+                ApplicationContext c = it.next();
+                if (c instanceof ConfigurableApplicationContext
+                    && !((ConfigurableApplicationContext)c).isActive()) {
+                    inactiveApplicationContexts.add(c);
+                }
+            }
+            // Remove the inactive application context here can avoid the UnsupportedOperationException
+            for (ApplicationContext context : inactiveApplicationContexts) {
+                appContexts.remove(context);
+            }
+            initWildcardDefinitionMap();
+        }
+    }
+
+    @Override
+    public void destroy() {
+        super.destroy();
+        appContexts.clear();
+    }
+
+    public Class<?> getRegistrationType() {
+        return Configurer.class;
+    }
+
+    protected Set<ApplicationContext> getAppContexts() {
+        return appContexts;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/databinding/source/SourceDataBinding.java b/transform/src/patch/java/org/apache/cxf/databinding/source/SourceDataBinding.java
new file mode 100644
index 0000000..5321779
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/databinding/source/SourceDataBinding.java
@@ -0,0 +1,104 @@
+/**
+ * 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.databinding.source;
+
+
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.w3c.dom.Node;
+
+import org.apache.cxf.common.xmlschema.SchemaCollection;
+import org.apache.cxf.databinding.DataReader;
+import org.apache.cxf.databinding.DataWriter;
+import org.apache.cxf.service.Service;
+import org.apache.cxf.service.ServiceModelVisitor;
+import org.apache.cxf.service.model.MessagePartInfo;
+import org.apache.cxf.service.model.ServiceInfo;
+import org.apache.ws.commons.schema.constants.Constants;
+
+/**
+ * A simple databinding implementation which reads and writes Source objects.
+ */
+public class SourceDataBinding extends org.apache.cxf.databinding.AbstractDataBinding {
+
+    public static final String PREFERRED_FORMAT = "source-preferred-format";
+
+    final Class<?> preferred;
+
+    public SourceDataBinding() {
+        super();
+        preferred = null;
+    }
+    public SourceDataBinding(Class<?> pref) {
+        super();
+        preferred = pref;
+    }
+
+    public void initialize(Service service) {
+        for (ServiceInfo serviceInfo : service.getServiceInfos()) {
+            SchemaCollection schemaCollection = serviceInfo.getXmlSchemaCollection();
+            if (schemaCollection.getXmlSchemas().length > 1) {
+                // Schemas are already populated.
+                continue;
+            }
+            new ServiceModelVisitor(serviceInfo) {
+                @Override
+                public void begin(MessagePartInfo part) {
+                    if (part.getTypeQName() != null || part.getElementQName() != null) {
+                        return;
+                    }
+                    part.setTypeQName(Constants.XSD_ANYTYPE);
+                }
+            } .walk();
+        }
+    }
+
+
+    @SuppressWarnings("unchecked")
+    public <T> DataReader<T> createReader(Class<T> cls) {
+        if (cls == XMLStreamReader.class) {
+            return (DataReader<T>) new XMLStreamDataReader(preferred);
+        } else if (cls == Node.class) {
+            return (DataReader<T>) new NodeDataReader();
+        } else {
+            throw new UnsupportedOperationException("The type " + cls.getName() + " is not supported.");
+        }
+    }
+
+    public Class<?>[] getSupportedReaderFormats() {
+        return new Class<?>[] {XMLStreamReader.class, Node.class};
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> DataWriter<T> createWriter(Class<T> cls) {
+        if (cls == XMLStreamWriter.class) {
+            return (DataWriter<T>) new XMLStreamDataWriter();
+        } else if (cls == Node.class) {
+            return (DataWriter<T>) new NodeDataWriter();
+        }
+        throw new UnsupportedOperationException("The type " + cls.getName() + " is not supported.");
+    }
+
+    public Class<?>[] getSupportedWriterFormats() {
+        return new Class<?>[] {XMLStreamWriter.class, Node.class};
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/databinding/stax/StaxDataBinding.java b/transform/src/patch/java/org/apache/cxf/databinding/stax/StaxDataBinding.java
new file mode 100644
index 0000000..3cb836b
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/databinding/stax/StaxDataBinding.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.databinding.stax;
+
+import java.util.Collection;
+import java.util.logging.Logger;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.validation.Schema;
+
+import org.w3c.dom.Node;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.xmlschema.SchemaCollection;
+import org.apache.cxf.databinding.AbstractInterceptorProvidingDataBinding;
+import org.apache.cxf.databinding.DataReader;
+import org.apache.cxf.databinding.DataWriter;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.interceptor.StaxInEndingInterceptor;
+import org.apache.cxf.message.Attachment;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.AbstractPhaseInterceptor;
+import org.apache.cxf.phase.Phase;
+import org.apache.cxf.service.Service;
+import org.apache.cxf.service.ServiceModelVisitor;
+import org.apache.cxf.service.model.MessagePartInfo;
+import org.apache.cxf.service.model.ServiceInfo;
+import org.apache.cxf.staxutils.StaxUtils;
+import org.apache.ws.commons.schema.constants.Constants;
+
+/**
+ * A simple databinding implementation which reads and writes Source objects.
+ * This will not work with the standard databinding interceptors.
+ */
+public class StaxDataBinding extends AbstractInterceptorProvidingDataBinding {
+
+    private XMLStreamDataReader xsrReader;
+    private XMLStreamDataWriter xswWriter;
+
+    public StaxDataBinding() {
+        super();
+        this.xsrReader = new XMLStreamDataReader();
+        this.xswWriter = new XMLStreamDataWriter();
+        inInterceptors.add(new StaxInEndingInterceptor(Phase.POST_INVOKE));
+        inFaultInterceptors.add(new StaxInEndingInterceptor(Phase.POST_INVOKE));
+
+        inInterceptors.add(RemoveStaxInEndingInterceptor.INSTANCE);
+        inFaultInterceptors.add(RemoveStaxInEndingInterceptor.INSTANCE);
+    }
+
+    static class RemoveStaxInEndingInterceptor extends AbstractPhaseInterceptor<Message> {
+        static final RemoveStaxInEndingInterceptor INSTANCE = new RemoveStaxInEndingInterceptor();
+
+        RemoveStaxInEndingInterceptor() {
+            super(Phase.PRE_INVOKE);
+            addBefore(StaxInEndingInterceptor.class.getName());
+        }
+
+        public void handleMessage(Message message) {
+            message.getInterceptorChain().remove(StaxInEndingInterceptor.INSTANCE);
+        }
+    }
+
+
+    public void initialize(Service service) {
+        for (ServiceInfo serviceInfo : service.getServiceInfos()) {
+            SchemaCollection schemaCollection = serviceInfo.getXmlSchemaCollection();
+            if (schemaCollection.getXmlSchemas().length > 1) {
+                // Schemas are already populated.
+                continue;
+            }
+            new ServiceModelVisitor(serviceInfo) {
+                
+                @Override
+                public void begin(MessagePartInfo part) {
+                    if (part.getTypeQName() != null || part.getElementQName() != null) {
+                        return;
+                    }
+                    part.setTypeQName(Constants.XSD_ANYTYPE);
+                }
+            } .walk();
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> DataReader<T> createReader(Class<T> cls) {
+        if (cls == XMLStreamReader.class) {
+            return (DataReader<T>) xsrReader;
+        }
+        throw new UnsupportedOperationException("The type " + cls.getName() + " is not supported.");
+    }
+
+    public Class<?>[] getSupportedReaderFormats() {
+        return new Class<?>[] {XMLStreamReader.class};
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> DataWriter<T> createWriter(Class<T> cls) {
+        if (cls == XMLStreamWriter.class) {
+            return (DataWriter<T>) xswWriter;
+        }
+        throw new UnsupportedOperationException("The type " + cls.getName() + " is not supported.");
+    }
+
+    public Class<?>[] getSupportedWriterFormats() {
+        return new Class<?>[] {XMLStreamWriter.class, Node.class};
+    }
+
+    public static class XMLStreamDataReader implements DataReader<XMLStreamReader> {
+
+        public Object read(MessagePartInfo part, XMLStreamReader input) {
+            return read(null, input, part.getTypeClass());
+        }
+
+        public Object read(QName name, XMLStreamReader input, Class<?> type) {
+            return input;
+        }
+
+        public Object read(XMLStreamReader reader) {
+            return reader;
+        }
+
+        public void setSchema(Schema s) {
+        }
+
+        public void setAttachments(Collection<Attachment> attachments) {
+        }
+
+        public void setProperty(String prop, Object value) {
+        }
+    }
+
+    public static class XMLStreamDataWriter implements DataWriter<XMLStreamWriter> {
+        private static final Logger LOG = LogUtils.getL7dLogger(XMLStreamDataWriter.class);
+
+        public void write(Object obj, MessagePartInfo part, XMLStreamWriter output) {
+            write(obj, output);
+        }
+
+        public void write(Object obj, XMLStreamWriter writer) {
+            try {
+                if (obj instanceof XMLStreamReader) {
+                    XMLStreamReader xmlStreamReader = (XMLStreamReader) obj;
+                    StaxUtils.copy(xmlStreamReader, writer);
+                    xmlStreamReader.close();
+                } else if (obj instanceof XMLStreamWriterCallback) {
+                    ((XMLStreamWriterCallback) obj).write(writer);
+                } else {
+                    throw new UnsupportedOperationException("Data types of "
+                                                            + obj.getClass() + " are not supported.");
+                }
+            } catch (XMLStreamException e) {
+                throw new Fault("COULD_NOT_READ_XML_STREAM", LOG, e);
+            }
+        }
+
+        public void setSchema(Schema s) {
+        }
+
+        public void setAttachments(Collection<Attachment> attachments) {
+        }
+
+        public void setProperty(String key, Object value) {
+        }
+
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/endpoint/AbstractConduitSelector.java b/transform/src/patch/java/org/apache/cxf/endpoint/AbstractConduitSelector.java
new file mode 100644
index 0000000..250e523
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/endpoint/AbstractConduitSelector.java
@@ -0,0 +1,308 @@
+/**
+ * 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.endpoint;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.logging.Logger;
+
+import org.apache.cxf.BusException;
+import org.apache.cxf.common.util.PropertyUtils;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageUtils;
+import org.apache.cxf.service.model.EndpointInfo;
+import org.apache.cxf.transport.Conduit;
+import org.apache.cxf.transport.ConduitInitiator;
+import org.apache.cxf.transport.ConduitInitiatorManager;
+import org.apache.cxf.transport.MessageObserver;
+import org.apache.cxf.ws.addressing.AttributedURIType;
+import org.apache.cxf.ws.addressing.EndpointReferenceType;
+
+
+/**
+ * Abstract base class holding logic common to any ConduitSelector
+ * that retrieves a Conduit from the ConduitInitiator.
+ */
+public abstract class AbstractConduitSelector implements ConduitSelector, Closeable {
+    public static final String CONDUIT_COMPARE_FULL_URL
+        = "org.apache.cxf.ConduitSelector.compareFullUrl";
+    protected static final String KEEP_CONDUIT_ALIVE = "KeepConduitAlive";
+
+
+    //collection of conduits that were created so we can close them all at the end
+    protected List<Conduit> conduits = new CopyOnWriteArrayList<>();
+
+   
+    protected Endpoint endpoint;
+
+
+    public AbstractConduitSelector() {
+    }
+
+    /**
+     * Constructor, allowing a specific conduit to override normal selection.
+     *
+     * @param c specific conduit
+     */
+    public AbstractConduitSelector(Conduit c) {
+        if (c != null) {
+            conduits.add(c);
+        }
+    }
+
+    public void close() {
+        for (Conduit c : conduits) {
+            c.close();
+        }
+        conduits.clear();
+    }
+
+    protected void removeConduit(Conduit conduit) {
+        if (conduit != null) {
+            conduit.close();
+            conduits.remove(conduit);
+        }
+    }
+
+    /**
+     * Mechanics to actually get the Conduit from the ConduitInitiator
+     * if necessary.
+     *
+     * @param message the current Message
+     */
+    protected Conduit getSelectedConduit(Message message) {
+        Conduit c = findCompatibleConduit(message);
+        if (c == null) {
+            Exchange exchange = message.getExchange();
+            EndpointInfo ei = endpoint.getEndpointInfo();
+            String transportID = ei.getTransportId();
+            try {
+                ConduitInitiatorManager conduitInitiatorMgr = exchange.getBus()
+                    .getExtension(ConduitInitiatorManager.class);
+                if (conduitInitiatorMgr != null) {
+                    ConduitInitiator conduitInitiator =
+                        conduitInitiatorMgr.getConduitInitiator(transportID);
+                    if (conduitInitiator != null) {
+                        c = createConduit(message, exchange, conduitInitiator);
+                    } else {
+                        getLogger().warning("ConduitInitiator not found: "
+                                            + ei.getAddress());
+                    }
+                } else {
+                    getLogger().warning("ConduitInitiatorManager not found");
+                }
+            } catch (BusException | IOException ex) {
+                throw new Fault(ex);
+            }
+        }
+        if (c != null && c.getTarget() != null && c.getTarget().getAddress() != null) {
+            replaceEndpointAddressPropertyIfNeeded(message, c.getTarget().getAddress().getValue(), c);
+        }
+        //the search for the conduit could cause extra properties to be reset/loaded.
+        message.resetContextCache();
+        message.put(Conduit.class, c);
+        return c;
+    }
+
+    protected Conduit createConduit(Message message, Exchange exchange, ConduitInitiator conduitInitiator)
+        throws IOException {
+        Conduit c;
+        synchronized (endpoint) {
+            if (!conduits.isEmpty()) {
+                c = findCompatibleConduit(message);
+                if (c != null) {
+                    return c;
+                }
+            }
+            EndpointInfo ei = endpoint.getEndpointInfo();
+            String add = (String)message.get(Message.ENDPOINT_ADDRESS);
+            String basePath = (String)message.get(Message.BASE_PATH);
+            if (StringUtils.isEmpty(add)
+                || add.equals(ei.getAddress())) {
+                c = conduitInitiator.getConduit(ei, exchange.getBus());
+                replaceEndpointAddressPropertyIfNeeded(message, add, c);
+            } else {
+                EndpointReferenceType epr = new EndpointReferenceType();
+                AttributedURIType ad = new AttributedURIType();
+                ad.setValue(StringUtils.isEmpty(basePath) ? add : basePath);
+                epr.setAddress(ad);
+                c = conduitInitiator.getConduit(ei, epr, exchange.getBus());
+            }
+            MessageObserver observer =
+                exchange.get(MessageObserver.class);
+            if (observer != null) {
+                c.setMessageObserver(observer);
+            } else {
+                getLogger().warning("MessageObserver not found");
+            }
+            conduits.add(c);
+        }
+        return c;
+    }
+
+    // Some conduits may replace the endpoint address after it has already been prepared
+    // but before the invocation has been done (ex, org.apache.cxf.clustering.LoadDistributorTargetSelector)
+    // which may affect JAX-RS clients where actual endpoint address property may include additional path
+    // segments.
+    protected boolean replaceEndpointAddressPropertyIfNeeded(Message message,
+                                                             String endpointAddress,
+                                                             Conduit cond) {
+        return false;
+    }
+
+    /**
+     * @return the encapsulated Endpoint
+     */
+    public Endpoint getEndpoint() {
+        return endpoint;
+    }
+
+    /**
+     * @param ep the endpoint to encapsulate
+     */
+    public void setEndpoint(Endpoint ep) {
+        endpoint = ep;
+    }
+
+    /**
+     * Called on completion of the MEP for which the Conduit was required.
+     *
+     * @param exchange represents the completed MEP
+     */
+    public void complete(Exchange exchange) {
+        // Clients expecting explicit InputStream responses
+        // will need to keep low level conduits operating on InputStreams open
+        // and will be responsible for closing the streams
+
+        if (PropertyUtils.isTrue(exchange.get(KEEP_CONDUIT_ALIVE))) {
+            return;
+        }
+        try {
+            if (exchange.getInMessage() != null) {
+                Conduit c = exchange.getOutMessage().get(Conduit.class);
+                if (c == null) {
+                    getSelectedConduit(exchange.getInMessage()).close(exchange.getInMessage());
+                } else {
+                    c.close(exchange.getInMessage());
+                }
+            }
+        } catch (IOException e) {
+            //IGNORE
+        }
+    }
+    /**
+     * @return the logger to use
+     */
+    protected abstract Logger getLogger();
+
+    /**
+     * If address protocol was changed, conduit should be re-initialised
+     *
+     * @param message the current Message
+     */
+    protected Conduit findCompatibleConduit(Message message) {
+        Conduit c = message.get(Conduit.class);
+        if (c == null
+            && message.getExchange() != null
+            && message.getExchange().getOutMessage() != null
+            && message.getExchange().getOutMessage() != message) {
+            c = message.getExchange().getOutMessage().get(Conduit.class);
+        }
+        if (c != null) {
+            return c;
+        }
+        ContextualBooleanGetter cbg = new ContextualBooleanGetter(message);
+        for (Conduit c2 : conduits) {
+            if (c2.getTarget() == null
+                || c2.getTarget().getAddress() == null
+                || c2.getTarget().getAddress().getValue() == null) {
+                continue;
+            }
+            String conduitAddress = c2.getTarget().getAddress().getValue();
+
+            EndpointInfo ei = endpoint.getEndpointInfo();
+            String actualAddress = ei.getAddress();
+
+            String messageAddress = (String)message.get(Message.ENDPOINT_ADDRESS);
+            if (messageAddress != null) {
+                actualAddress = messageAddress;
+            }
+
+            if (matchAddresses(conduitAddress, actualAddress, cbg)) {
+                return c2;
+            }
+        }
+        for (Conduit c2 : conduits) {
+            if (c2.getTarget() == null
+                || c2.getTarget().getAddress() == null
+                || c2.getTarget().getAddress().getValue() == null) {
+                return c2;
+            }
+        }
+        return null;
+    }
+
+    private boolean matchAddresses(String conduitAddress, String actualAddress, ContextualBooleanGetter cbg) {
+        if (conduitAddress.length() == actualAddress.length()) {
+            //let's be optimistic and try full comparison first, regardless of CONDUIT_COMPARE_FULL_URL value,
+            //which can be expensive to fetch; as a matter of fact, anyway, if the addresses fully match,
+            //their hosts also match
+            if (conduitAddress.equalsIgnoreCase(actualAddress)) {
+                return true;
+            }
+            return !cbg.isFullComparison() && matchAddressSubstrings(conduitAddress, actualAddress);
+        }
+        return !cbg.isFullComparison() && matchAddressSubstrings(conduitAddress, actualAddress);
+    }
+
+    //smart address substring comparison that tries to avoid building and comparing substrings unless strictly required
+    private boolean matchAddressSubstrings(String conduitAddress, String actualAddress) {
+        int idx = conduitAddress.indexOf(':');
+        if (idx == actualAddress.indexOf(':')) {
+            if (idx <= 0) {
+                return true;
+            }
+            return conduitAddress.substring(0, idx).equalsIgnoreCase(actualAddress.substring(0, idx));
+        }
+        //no possible match as for sure the substrings before idx will be different
+        return false;
+    }
+
+    private static final class ContextualBooleanGetter {
+        private Boolean value;
+        private final Message message;
+
+        ContextualBooleanGetter(Message message) {
+            this.message = message;
+        }
+
+        public boolean isFullComparison() {
+            if (value == null) {
+                value = MessageUtils.getContextualBoolean(message, CONDUIT_COMPARE_FULL_URL, false);
+            }
+            return value;
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/endpoint/ClientCallback.java b/transform/src/patch/java/org/apache/cxf/endpoint/ClientCallback.java
new file mode 100644
index 0000000..467b5a8
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/endpoint/ClientCallback.java
@@ -0,0 +1,166 @@
+/**
+ * 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.endpoint;
+
+import java.util.Map;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.cxf.message.Message;
+
+/**
+ * Asynchronous callback object for calls to {@link Client#invoke(ClientCallback, String, Object...)}
+ * and related functions.
+ *
+ * The default behavior of this expects the following pattern:
+ * <ol>
+ * <li>ClientCallback cb = new ClientCallback();<>
+ * <li>client.invoke(cb, "someMethod", ....);</li>
+ * <li>cb.wait();</li>
+ * <li>// CXF calls notify on the callback object when the operation is complete.</li>
+ * </ol>
+ */
+public class ClientCallback implements Future<Object[]> {
+    protected final CompletableFuture<Object[]> delegate = new CompletableFuture<>();
+    protected Map<String, Object> context;
+    protected boolean started;
+
+    public ClientCallback() {
+    }
+
+    /**
+     * Called when a message is first received prior to any actions
+     * being applied to the message.   The InterceptorChain is setup so
+     * modifications to that can be done.
+     */
+    public void start(Message msg) {
+        started = true;
+    }
+
+    /**
+     * If the processing of the incoming message proceeds normally, this
+     * method is called with the response context values and the resulting objects.
+     *
+     * The default behavior just stores the objects and calls notifyAll to wake
+     * up threads waiting for the response.
+     *
+     * @param ctx
+     * @param res
+     */
+    public void handleResponse(Map<String, Object> ctx, Object[] res) {
+        context = ctx;
+        delegate.complete(res);
+
+        synchronized (this) {
+            notifyAll();
+        }
+    }
+
+    /**
+     * If processing of the incoming message results in an exception, this
+     * method is called with the resulting exception.
+     *
+     * The default behavior just stores the objects and calls notifyAll to wake
+     * up threads waiting for the response.
+     *
+     * @param ctx
+     * @param ex
+     */
+    public void handleException(Map<String, Object> ctx, Throwable ex) {
+        context = ctx;
+        delegate.completeExceptionally(ex);
+        
+        synchronized (this) {
+            notifyAll();
+        }
+    }
+
+
+    public boolean cancel(boolean mayInterruptIfRunning) {
+        if (!started) {
+            delegate.cancel(mayInterruptIfRunning);
+            
+            synchronized (this) {
+                notifyAll();
+            }
+            
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * return the map of items returned from an operation.
+     * @return the response context
+     * @throws InterruptedException if the operation was cancelled.
+     * @throws ExecutionException if the operation resulted in a fault.
+     */
+    public Map<String, Object> getResponseContext() throws InterruptedException, ExecutionException {
+        synchronized (this) {
+            if (!delegate.isDone()) {
+                wait();
+            }
+        }
+        if (delegate.isCancelled()) {
+            throw new InterruptedException("Operation Cancelled");
+        }
+        if (delegate.isCompletedExceptionally()) {
+            delegate.get();
+        }
+        return context;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object[] get() throws InterruptedException, ExecutionException {
+        try {
+            return delegate.get();
+        } catch (final CancellationException ex) {
+            // Preserving the exception raised by former implementation
+            throw new InterruptedException("Operation has been cancelled");
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object[] get(long timeout, TimeUnit unit) throws InterruptedException, 
+            ExecutionException, TimeoutException {
+        try {
+            return delegate.get(timeout, unit);
+        } catch (final CancellationException ex) {
+            // Preserving the exception raised by former implementation
+            throw new InterruptedException("Operation has been cancelled");
+        }
+    }
+
+    public boolean isCancelled() {
+        return delegate.isCancelled();
+    }
+
+    public boolean isDone() {
+        return delegate.isDone();
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/endpoint/ClientImpl.java b/transform/src/patch/java/org/apache/cxf/endpoint/ClientImpl.java
new file mode 100644
index 0000000..53d3a43
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/endpoint/ClientImpl.java
@@ -0,0 +1,1192 @@
+/**
+ * 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.endpoint;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.namespace.QName;
+import javax.xml.ws.handler.MessageContext;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.binding.Binding;
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.classloader.ClassLoaderUtils.ClassLoaderHolder;
+import org.apache.cxf.common.i18n.UncheckedException;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.PropertyUtils;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.interceptor.AbstractBasicInterceptorProvider;
+import org.apache.cxf.interceptor.ClientOutFaultObserver;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.interceptor.Interceptor;
+import org.apache.cxf.interceptor.InterceptorChain;
+import org.apache.cxf.interceptor.InterceptorProvider;
+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.phase.PhaseChainCache;
+import org.apache.cxf.phase.PhaseInterceptorChain;
+import org.apache.cxf.phase.PhaseManager;
+import org.apache.cxf.service.Service;
+import org.apache.cxf.service.model.BindingInfo;
+import org.apache.cxf.service.model.BindingMessageInfo;
+import org.apache.cxf.service.model.BindingOperationInfo;
+import org.apache.cxf.service.model.EndpointInfo;
+import org.apache.cxf.service.model.MessageInfo;
+import org.apache.cxf.service.model.ServiceInfo;
+import org.apache.cxf.transport.Conduit;
+import org.apache.cxf.transport.MessageObserver;
+import org.apache.cxf.workqueue.SynchronousExecutor;
+
+public class ClientImpl
+    extends AbstractBasicInterceptorProvider
+    implements Client, Retryable, MessageObserver {
+
+    public static final String THREAD_LOCAL_REQUEST_CONTEXT = "thread.local.request.context";
+    /**
+     * When a synchronous request/response invoke is done using an asynchronous transport mechanism,
+     * this is the timeout used for waiting for the response.  Default is 60 seconds.
+     */
+    public static final String SYNC_TIMEOUT = "cxf.synchronous.timeout";
+
+    public static final String FINISHED = "exchange.finished";
+
+    private static final Logger LOG = LogUtils.getL7dLogger(ClientImpl.class);
+
+    protected Bus bus;
+    protected ConduitSelector conduitSelector;
+    protected ClientOutFaultObserver outFaultObserver;
+    protected int synchronousTimeout = 60000; // default 60 second timeout
+
+    protected PhaseChainCache outboundChainCache = new PhaseChainCache();
+    protected PhaseChainCache inboundChainCache = new PhaseChainCache();
+
+    protected Map<String, Object> currentRequestContext = new ConcurrentHashMap<>(8, 0.75f, 4);
+    protected Thread latestContextThread;
+    protected Map<Thread, EchoContext> requestContext
+        = Collections.synchronizedMap(new WeakHashMap<Thread, EchoContext>());
+
+    protected Map<Thread, ResponseContext> responseContext
+        = Collections.synchronizedMap(new WeakHashMap<Thread, ResponseContext>());
+
+    protected Executor executor;
+
+    public ClientImpl(Bus b, Endpoint e) {
+        this(b, e, (ConduitSelector)null);
+    }
+
+    public ClientImpl(Bus b, Endpoint e, Conduit c) {
+        this(b, e, new PreexistingConduitSelector(c));
+    }
+
+    public ClientImpl(Bus b, Endpoint e, ConduitSelector sc) {
+        bus = b;
+        outFaultObserver = new ClientOutFaultObserver(bus);
+        getConduitSelector(sc).setEndpoint(e);
+        notifyLifecycleManager();
+    }
+
+    /**
+     * Create a Client that uses a specific EndpointImpl.
+     * @param bus
+     * @param svc
+     * @param port
+     * @param endpointImplFactory
+     */
+    public ClientImpl(Bus bus, Service svc, QName port,
+                      EndpointImplFactory endpointImplFactory) {
+        this.bus = bus;
+        outFaultObserver = new ClientOutFaultObserver(bus);
+        EndpointInfo epfo = findEndpoint(svc, port);
+
+        try {
+            if (endpointImplFactory != null) {
+                getConduitSelector().setEndpoint(endpointImplFactory.newEndpointImpl(bus, svc, epfo));
+            } else {
+                getConduitSelector().setEndpoint(new EndpointImpl(bus, svc, epfo));
+            }
+        } catch (EndpointException epex) {
+            throw new IllegalStateException("Unable to create endpoint: " + epex.getMessage(), epex);
+        }
+        notifyLifecycleManager();
+    }
+
+    public Bus getBus() {
+        return bus;
+    }
+
+    public void destroy() {
+        if (bus == null) {
+            return;
+        }
+        if (getEndpoint() != null) {
+            for (Closeable c : getEndpoint().getCleanupHooks()) {
+                try {
+                    c.close();
+                } catch (IOException e) {
+                    //ignore
+                }
+            }
+        }
+        ClientLifeCycleManager mgr = bus.getExtension(ClientLifeCycleManager.class);
+        if (null != mgr) {
+            mgr.clientDestroyed(this);
+        }
+
+        if (conduitSelector != null) {
+            if (conduitSelector instanceof Closeable) {
+                try {
+                    ((Closeable)conduitSelector).close();
+                } catch (IOException e) {
+                    //ignore, we're destroying anyway
+                }
+            } else {
+                getConduit().close();
+            }
+        }
+
+        bus = null;
+        conduitSelector = null;
+        outFaultObserver = null;
+        outboundChainCache = null;
+        inboundChainCache = null;
+
+        currentRequestContext = null;
+        requestContext.clear();
+        requestContext = null;
+        responseContext.clear();
+        responseContext = null;
+        executor = null;
+    }
+
+    private void notifyLifecycleManager() {
+        ClientLifeCycleManager mgr = bus.getExtension(ClientLifeCycleManager.class);
+        if (null != mgr) {
+            mgr.clientCreated(this);
+        }
+    }
+
+    private EndpointInfo findEndpoint(Service svc, QName port) {
+        if (port != null) {
+            EndpointInfo epfo = svc.getEndpointInfo(port);
+            if (epfo == null) {
+                throw new IllegalArgumentException("The service " + svc.getName()
+                                                   + " does not have an endpoint " + port + ".");
+            }
+            return epfo;
+        }
+        
+        for (ServiceInfo svcfo : svc.getServiceInfos()) {
+            for (EndpointInfo e : svcfo.getEndpoints()) {
+                BindingInfo bfo = e.getBinding();
+                String bid = bfo.getBindingId();
+                if ("http://schemas.xmlsoap.org/wsdl/soap/".equals(bid)
+                    || "http://schemas.xmlsoap.org/wsdl/soap12/".equals(bid)) {
+                    for (Object o : bfo.getExtensors().get()) {
+                        try {
+                            String s = (String)o.getClass().getMethod("getTransportURI").invoke(o);
+                            if (s != null && s.endsWith("http")) {
+                                return e;
+                            }
+                        } catch (Throwable t) {
+                            //ignore
+                        }
+                    }
+                }
+            }
+        }
+        throw new UnsupportedOperationException(
+             "Only document-style SOAP 1.1 and 1.2 http are supported "
+             + "for auto-selection of endpoint; none were found.");
+    }
+
+    public Endpoint getEndpoint() {
+        return getConduitSelector().getEndpoint();
+    }
+
+    public void releaseThreadContexts() {
+        final Thread t = Thread.currentThread();
+        requestContext.remove(t);
+        responseContext.remove(t);
+    }
+
+    @Override
+    public Contexts getContexts() {
+        return new Contexts() {
+            @Override
+            public void close() throws Exception {
+                releaseThreadContexts();
+            }
+            @Override
+            public Map<String, Object> getRequestContext() {
+                return ClientImpl.this.getRequestContext();
+            }
+            @Override
+            public Map<String, Object> getResponseContext() {
+                return ClientImpl.this.getResponseContext();
+            }
+        };
+    }
+
+    public Map<String, Object> getRequestContext() {
+        if (isThreadLocalRequestContext()) {
+            final Thread t = Thread.currentThread();
+            requestContext.computeIfAbsent(t, k -> new EchoContext(currentRequestContext));
+            latestContextThread = t;
+            return requestContext.get(t);
+        }
+        return currentRequestContext;
+    }
+    public Map<String, Object> getResponseContext() {
+        if (!responseContext.containsKey(Thread.currentThread())) {
+            final Thread t = Thread.currentThread();
+            responseContext.put(t, new ResponseContext(responseContext));
+        }
+        return responseContext.get(Thread.currentThread());
+    }
+    protected Map<String, Object> setResponseContext(Map<String, Object> ctx) {
+        if (ctx instanceof ResponseContext) {
+            ResponseContext c = (ResponseContext)ctx;
+            responseContext.put(Thread.currentThread(), c);
+            return c;
+        }
+        ResponseContext c = new ResponseContext(ctx, responseContext);
+        responseContext.put(Thread.currentThread(), c);
+        return c;
+    }
+    public boolean isThreadLocalRequestContext() {
+        Object o = currentRequestContext.get(THREAD_LOCAL_REQUEST_CONTEXT);
+        if (o != null) {
+            final boolean local;
+            if (o instanceof Boolean) {
+                local = ((Boolean)o).booleanValue();
+            } else {
+                local = Boolean.parseBoolean(o.toString());
+            }
+            return local;
+        }
+        return false;
+    }
+    public void setThreadLocalRequestContext(boolean b) {
+        currentRequestContext.put(THREAD_LOCAL_REQUEST_CONTEXT, b);
+    }
+
+
+    public Object[] invoke(BindingOperationInfo oi, Object... params) throws Exception {
+        return invoke(oi, params, null);
+    }
+
+    public Object[] invoke(String operationName, Object... params) throws Exception {
+        QName q = new QName(getEndpoint().getService().getName().getNamespaceURI(), operationName);
+
+        return invoke(q, params);
+    }
+
+    public Object[] invoke(QName operationName, Object... params) throws Exception {
+        BindingOperationInfo op = getEndpoint().getEndpointInfo().getBinding().getOperation(operationName);
+        if (op == null) {
+            throw new UncheckedException(
+                new org.apache.cxf.common.i18n.Message("NO_OPERATION", LOG, operationName));
+        }
+
+        if (op.isUnwrappedCapable()) {
+            op = op.getUnwrappedOperation();
+        }
+
+        return invoke(op, params);
+    }
+
+    public Object[] invokeWrapped(String operationName, Object... params) throws Exception {
+        QName q = new QName(getEndpoint().getService().getName().getNamespaceURI(), operationName);
+
+        return invokeWrapped(q, params);
+    }
+
+    public Object[] invokeWrapped(QName operationName, Object... params) throws Exception {
+        BindingOperationInfo op = getEndpoint().getEndpointInfo().getBinding().getOperation(operationName);
+        if (op == null) {
+            throw new UncheckedException(
+                new org.apache.cxf.common.i18n.Message("NO_OPERATION", LOG, operationName));
+        }
+        return invoke(op, params);
+    }
+
+    public Object[] invoke(BindingOperationInfo oi,
+                           Object[] params,
+                           Exchange exchange) throws Exception {
+        Map<String, Object> context = new HashMap<>();
+        return invoke(oi, params, context, exchange);
+    }
+    public Object[] invoke(BindingOperationInfo oi,
+                           Object[] params,
+                           Map<String, Object> context) throws Exception {
+        return invoke(oi, params, context, (Exchange)null);
+    }
+
+    public void invoke(ClientCallback callback,
+                       String operationName,
+                       Object... params) throws Exception {
+        QName q = new QName(getEndpoint().getService().getName().getNamespaceURI(), operationName);
+        invoke(callback, q, params);
+    }
+
+    public void invoke(ClientCallback callback,
+                       QName operationName,
+                       Object... params) throws Exception {
+        BindingOperationInfo op = getEndpoint().getEndpointInfo().getBinding().getOperation(operationName);
+        if (op == null) {
+            throw new UncheckedException(
+                new org.apache.cxf.common.i18n.Message("NO_OPERATION", LOG, operationName));
+        }
+
+        if (op.isUnwrappedCapable()) {
+            op = op.getUnwrappedOperation();
+        }
+
+        invoke(callback, op, params);
+    }
+
+
+    public void invokeWrapped(ClientCallback callback,
+                              String operationName,
+                              Object... params)
+        throws Exception {
+        QName q = new QName(getEndpoint().getService().getName().getNamespaceURI(), operationName);
+        invokeWrapped(callback, q, params);
+    }
+
+    public void invokeWrapped(ClientCallback callback,
+                              QName operationName,
+                              Object... params)
+        throws Exception {
+        BindingOperationInfo op = getEndpoint().getEndpointInfo().getBinding().getOperation(operationName);
+        if (op == null) {
+            throw new UncheckedException(
+                new org.apache.cxf.common.i18n.Message("NO_OPERATION", LOG, operationName));
+        }
+        invoke(callback, op, params);
+    }
+
+
+    public void invoke(ClientCallback callback,
+                       BindingOperationInfo oi,
+                       Object... params) throws Exception {
+        invoke(callback, oi, params, null, null);
+    }
+
+    public void invoke(ClientCallback callback,
+                       BindingOperationInfo oi,
+                       Object[] params,
+                       Map<String, Object> context) throws Exception {
+        invoke(callback, oi, params, context, null);
+    }
+
+    public void invoke(ClientCallback callback,
+                       BindingOperationInfo oi,
+                       Object[] params,
+                       Exchange exchange) throws Exception {
+        invoke(callback, oi, params, null, exchange);
+    }
+
+    public void invoke(ClientCallback callback,
+                       BindingOperationInfo oi,
+                       Object[] params,
+                       Map<String, Object> context,
+                       Exchange exchange) throws Exception {
+        doInvoke(callback, oi, params, context, exchange);
+    }
+
+    public Object[] invoke(BindingOperationInfo oi,
+                           Object[] params,
+                           Map<String, Object> context,
+                           Exchange exchange) throws Exception {
+        return doInvoke(null, oi, params, context, exchange);
+    }
+
+    private Object[] doInvoke(final ClientCallback callback,
+                              BindingOperationInfo oi,
+                              Object[] params,
+                              Map<String, Object> context,
+                              Exchange exchange) throws Exception {
+        Bus origBus = BusFactory.getAndSetThreadDefaultBus(bus);
+        ClassLoaderHolder origLoader = null;
+        Map<String, Object> resContext = null;
+        try {
+            ClassLoader loader = bus.getExtension(ClassLoader.class);
+            if (loader != null) {
+                origLoader = ClassLoaderUtils.setThreadContextClassloader(loader);
+            }
+            if (exchange == null) {
+                exchange = new ExchangeImpl();
+            }
+            exchange.setSynchronous(callback == null);
+            Endpoint endpoint = getEndpoint();
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.fine("Invoke, operation info: " + oi + ", params: " + Arrays.toString(params));
+            }
+            Message message = endpoint.getBinding().createMessage();
+
+            // Make sure INVOCATION CONTEXT, REQUEST_CONTEXT and RESPONSE_CONTEXT are present
+            // on message
+            if (context == null) {
+                context = new HashMap<>();
+            }
+            Map<String, Object> reqContext = CastUtils.cast((Map<?, ?>)context.get(REQUEST_CONTEXT));
+            resContext = CastUtils.cast((Map<?, ?>)context.get(RESPONSE_CONTEXT));
+            if (reqContext == null) {
+                reqContext = new HashMap<>(getRequestContext());
+                context.put(REQUEST_CONTEXT, reqContext);
+            }
+            if (resContext == null) {
+                resContext = new ResponseContext(responseContext);
+                context.put(RESPONSE_CONTEXT, resContext);
+            }
+
+            message.put(Message.INVOCATION_CONTEXT, context);
+            setContext(reqContext, message);
+            exchange.putAll(reqContext);
+
+            setParameters(params, message);
+
+            if (null != oi) {
+                exchange.setOneWay(oi.getOutput() == null);
+            }
+
+            exchange.setOutMessage(message);
+            exchange.put(ClientCallback.class, callback);
+
+            setOutMessageProperties(message, oi);
+            setExchangeProperties(exchange, endpoint, oi);
+
+            PhaseInterceptorChain chain = setupInterceptorChain(endpoint);
+            message.setInterceptorChain(chain);
+            if (callback == null) {
+                chain.setFaultObserver(outFaultObserver);
+            } else {
+                // We need to wrap the outFaultObserver if the callback is not null
+                // calling the conduitSelector.complete to make sure the fail over feature works
+                chain.setFaultObserver(new MessageObserver() {
+                    public void onMessage(Message message) {
+                        Exception ex = message.getContent(Exception.class);
+                        if (ex != null) {
+                            completeExchange(message.getExchange());
+                            if (message.getContent(Exception.class) == null) {
+                                // handle the right response
+                                Message inMsg = message.getExchange().getInMessage();
+                                Map<String, Object> ctx = responseContext.get(Thread.currentThread());
+                                List<Object> resList = CastUtils.cast(inMsg.getContent(List.class));
+                                Object[] result = resList == null ? null : resList.toArray();
+                                callback.handleResponse(ctx, result);
+                                return;
+                            }
+                        }
+                        outFaultObserver.onMessage(message);
+                    }
+                });
+            }
+            prepareConduitSelector(message);
+
+            // add additional interceptors and such
+            modifyChain(chain, message, false);
+            try {
+                chain.doIntercept(message);
+            } catch (Fault fault) {
+                enrichFault(fault);
+                throw fault;
+            }
+
+            if (callback != null) {
+                return null;
+            }
+            return processResult(message, exchange, oi, resContext);
+        } finally {
+            //ensure ResponseContext has HTTP RESPONSE CODE
+            if (null != exchange) {
+                Integer responseCode = (Integer)exchange.get(Message.RESPONSE_CODE);
+                resContext.put(MessageContext.HTTP_RESPONSE_CODE, responseCode);
+                resContext.put(org.apache.cxf.message.Message.RESPONSE_CODE, responseCode);
+                setResponseContext(resContext);
+            }
+            if (origLoader != null) {
+                origLoader.reset();
+            }
+            if (origBus != bus) {
+                BusFactory.setThreadDefaultBus(origBus);
+            }
+        }
+    }
+
+    private void completeExchange(Exchange exchange) {
+        getConduitSelector().complete(exchange);
+    }
+
+    /**
+     * TODO This is SOAP specific code and should not be in cxf core
+     * @param fault
+     */
+    private void enrichFault(Fault fault) {
+        if (fault.getCause().getCause() instanceof IOException
+                || fault.getCause() instanceof IOException) {
+            String soap11NS = "http://schemas.xmlsoap.org/soap/envelope/";
+            String soap12NS = "http://www.w3.org/2003/05/soap-envelope";
+            QName faultCode = fault.getFaultCode();
+            //for SoapFault, if it's underlying cause is IOException,
+            //it means something like server is down or can't create
+            //connection, according to soap spec we should set fault as
+            //Server Fault
+            if (faultCode.getNamespaceURI().equals(
+                    soap11NS)
+                    && "Client".equals(faultCode.getLocalPart())) {
+                faultCode = new QName(soap11NS, "Server");
+                fault.setFaultCode(faultCode);
+            }
+            if (faultCode.getNamespaceURI().equals(
+                    soap12NS)
+                    && "Sender".equals(faultCode.getLocalPart())) {
+                faultCode = new QName(soap12NS, "Receiver");
+                fault.setFaultCode(faultCode);
+            }
+        }
+    }
+
+    protected Object[] processResult(Message message,
+                                   Exchange exchange,
+                                   BindingOperationInfo oi,
+                                   Map<String, Object> resContext) throws Exception {
+        Exception ex = null;
+        // Check to see if there is a Fault from the outgoing chain if it's an out Message
+        if (!message.get(Message.INBOUND_MESSAGE).equals(Boolean.TRUE)) {
+            ex = message.getContent(Exception.class);
+        }
+        boolean mepCompleteCalled = false;
+        if (ex != null) {
+            completeExchange(exchange);
+            mepCompleteCalled = true;
+            if (message.getContent(Exception.class) != null) {
+                throw ex;
+            }
+        }
+        ex = message.getExchange().get(Exception.class);
+        if (ex != null) {
+            if (!mepCompleteCalled) {
+                completeExchange(exchange);
+            }
+            throw ex;
+        }
+
+        //REVISIT
+        // - use a protocol neutral no-content marker instead of 202?
+        // - move the decoupled destination property name into api
+        Integer responseCode = (Integer)exchange.get(Message.RESPONSE_CODE);
+        if (null != responseCode && 202 == responseCode) {
+            Endpoint ep = exchange.getEndpoint();
+            if (null != ep && null != ep.getEndpointInfo() && null == ep.getEndpointInfo().
+                getProperty("org.apache.cxf.ws.addressing.MAPAggregator.decoupledDestination")) {
+                return null;
+            }
+        }
+
+        // Wait for a response if we need to
+        if (oi != null && !oi.getOperationInfo().isOneWay()) {
+            waitResponse(exchange);
+        }
+
+        // leave the input stream open for the caller
+        Boolean keepConduitAlive = (Boolean)exchange.get(Client.KEEP_CONDUIT_ALIVE);
+        if (keepConduitAlive == null || !keepConduitAlive) {
+            completeExchange(exchange);
+        }
+
+        // Grab the response objects if there are any
+        List<Object> resList = null;
+        Message inMsg = exchange.getInMessage();
+        if (inMsg != null) {
+            if (null != resContext) {
+                resContext.putAll(inMsg);
+                // remove the recursive reference if present
+                resContext.remove(Message.INVOCATION_CONTEXT);
+                setResponseContext(resContext);
+            }
+            resList = CastUtils.cast(inMsg.getContent(List.class));
+        }
+
+        // check for an incoming fault
+        ex = getException(exchange);
+
+        if (ex != null) {
+            throw ex;
+        }
+
+        if (resList == null   
+            && oi != null && !oi.getOperationInfo().isOneWay()) {
+            
+            BindingOperationInfo boi = oi;
+            if (boi.isUnwrapped()) {
+                boi = boi.getWrappedOperation();
+            }
+            if (!boi.getOutput().getMessageParts().isEmpty()) {
+                //we were supposed to get some output, but didn't.
+                throw new IllegalEmptyResponseException("Response message did not contain proper response data."
+                    + " Expected: " + boi.getOutput().getMessageParts().get(0).getConcreteName());
+            }
+        }
+        if (resList != null) {
+            return resList.toArray();
+        }
+
+        return null;
+    }
+    protected Exception getException(Exchange exchange) {
+        if (exchange.getInFaultMessage() != null) {
+            return exchange.getInFaultMessage().getContent(Exception.class);
+        } else if (exchange.getOutFaultMessage() != null) {
+            return exchange.getOutFaultMessage().getContent(Exception.class);
+        } else if (exchange.getInMessage() != null) {
+            return exchange.getInMessage().getContent(Exception.class);
+        }
+        return null;
+    }
+
+    protected void setContext(Map<String, Object> ctx, Message message) {
+        if (ctx != null) {
+            message.putAll(ctx);
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.fine("set requestContext to message be" + ctx);
+            }
+        }
+    }
+
+    protected void waitResponse(Exchange exchange) throws IOException {
+        synchronized (exchange) {
+            long remaining = synchronousTimeout;
+            Long o = PropertyUtils.getLong(exchange.getOutMessage(), SYNC_TIMEOUT);
+            if (o != null) {
+                remaining = o;
+            }
+            while (!Boolean.TRUE.equals(exchange.get(FINISHED)) && remaining > 0) {
+                long start = System.currentTimeMillis();
+                try {
+                    exchange.wait(remaining);
+                } catch (InterruptedException ex) {
+                    // ignore
+                }
+                long end = System.currentTimeMillis();
+                remaining -= (int)(end - start);
+            }
+            if (!Boolean.TRUE.equals(exchange.get(FINISHED))) {
+                LogUtils.log(LOG, Level.WARNING, "RESPONSE_TIMEOUT",
+                    exchange.getBindingOperationInfo().getOperationInfo().getName().toString());
+                String msg = new org.apache.cxf.common.i18n.Message("RESPONSE_TIMEOUT", LOG, exchange
+                    .getBindingOperationInfo().getOperationInfo().getName().toString()).toString();
+                throw new IOException(msg);
+            }
+        }
+    }
+
+    protected void setParameters(Object[] params, Message message) {
+        MessageContentsList contents = new MessageContentsList(params);
+        message.setContent(List.class, contents);
+    }
+
+    public void onMessage(Message message) {
+        if (bus == null) {
+            throw new IllegalStateException("Message received on a Client that has been closed or destroyed.");
+        }
+        Endpoint endpoint = message.getExchange().getEndpoint();
+        if (endpoint == null) {
+            // in this case correlation will occur outside the transport,
+            // however there's a possibility that the endpoint may have been
+            // rebased in the meantime, so that the response will be mediated
+            // via a set of in interceptors provided by a *different* endpoint
+            //
+            endpoint = getConduitSelector().getEndpoint();
+            message.getExchange().put(Endpoint.class, endpoint);
+        }
+        message = endpoint.getBinding().createMessage(message);
+        message.getExchange().setInMessage(message);
+        message.put(Message.REQUESTOR_ROLE, Boolean.TRUE);
+        message.put(Message.INBOUND_MESSAGE, Boolean.TRUE);
+        PhaseManager pm = bus.getExtension(PhaseManager.class);
+
+        List<Interceptor<? extends Message>> i1 = bus.getInInterceptors();
+        if (LOG.isLoggable(Level.FINE)) {
+            LOG.fine("Interceptors contributed by bus: " + i1);
+        }
+        List<Interceptor<? extends Message>> i2 = getInInterceptors();
+        if (LOG.isLoggable(Level.FINE)) {
+            LOG.fine("Interceptors contributed by client: " + i2);
+        }
+        List<Interceptor<? extends Message>> i3 = endpoint.getInInterceptors();
+        if (LOG.isLoggable(Level.FINE)) {
+            LOG.fine("Interceptors contributed by endpoint: " + i3);
+        }
+        List<Interceptor<? extends Message>> i4 = endpoint.getBinding().getInInterceptors();
+        if (LOG.isLoggable(Level.FINE)) {
+            LOG.fine("Interceptors contributed by binding: " + i4);
+        }
+
+        PhaseInterceptorChain chain;
+        if (endpoint.getService().getDataBinding() instanceof InterceptorProvider) {
+            InterceptorProvider p = (InterceptorProvider)endpoint.getService().getDataBinding();
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.fine("Interceptors contributed by databinding: " + p.getInInterceptors());
+            }
+            chain = inboundChainCache.get(pm.getInPhases(), i1, i2, i3, i4,
+                                          p.getInInterceptors());
+        } else {
+            chain = inboundChainCache.get(pm.getInPhases(), i1, i2, i3, i4);
+        }
+        message.setInterceptorChain(chain);
+
+        chain.setFaultObserver(outFaultObserver);
+        modifyChain(chain, message, true);
+        modifyChain(chain, message.getExchange().getOutMessage(), true);
+
+        Bus origBus = BusFactory.getAndSetThreadDefaultBus(bus);
+        // execute chain
+        ClientCallback callback = message.getExchange().get(ClientCallback.class);
+        try {
+            if (callback != null) {
+                if (callback.isCancelled()) {
+                    completeExchange(message.getExchange());
+                    return;
+                }
+                callback.start(message);
+            }
+
+            String startingAfterInterceptorID = (String) message.get(
+                InterceptorChain.STARTING_AFTER_INTERCEPTOR_ID);
+            String startingInterceptorID = (String) message.get(
+                InterceptorChain.STARTING_AT_INTERCEPTOR_ID);
+            if (startingAfterInterceptorID != null) {
+                chain.doInterceptStartingAfter(message, startingAfterInterceptorID);
+            } else if (startingInterceptorID != null) {
+                chain.doInterceptStartingAt(message, startingInterceptorID);
+            } else if (message.getContent(Exception.class) != null) {
+                outFaultObserver.onMessage(message);
+            } else {
+                callback = message.getExchange().get(ClientCallback.class);
+
+                if (callback != null && !isPartialResponse(message)) {
+                    try {
+                        chain.doIntercept(message);
+                    } catch (Throwable error) {
+                        //so that asyn callback handler get chance to
+                        //handle non-runtime exceptions
+                        message.getExchange().setInMessage(message);
+                        Map<String, Object> resCtx = CastUtils
+                                .cast((Map<?, ?>) message.getExchange()
+                                        .getOutMessage().get(
+                                                Message.INVOCATION_CONTEXT));
+                        resCtx = CastUtils.cast((Map<?, ?>) resCtx
+                                .get(RESPONSE_CONTEXT));
+                        if (resCtx != null) {
+                            setResponseContext(resCtx);
+                        }
+                        // remove callback so that it won't be invoked twice
+                        callback = message.getExchange().remove(ClientCallback.class);
+                        if (callback != null) {
+                            callback.handleException(resCtx, error);
+                        }
+                    }
+                } else {
+                    chain.doIntercept(message);
+                }
+
+            }
+
+            callback = message.getExchange().get(ClientCallback.class);
+            if (callback == null || isPartialResponse(message)) {
+                return;
+            }
+
+            // remove callback so that it won't be invoked twice
+            callback = message.getExchange().remove(ClientCallback.class);
+            if (callback != null) {
+                message.getExchange().setInMessage(message);
+                Map<String, Object> resCtx = CastUtils.cast((Map<?, ?>)message
+                                                                .getExchange()
+                                                                .getOutMessage()
+                                                                .get(Message.INVOCATION_CONTEXT));
+                resCtx = CastUtils.cast((Map<?, ?>)resCtx.get(RESPONSE_CONTEXT));
+                if (resCtx != null && responseContext != null) {
+                    setResponseContext(resCtx);
+                }
+                try {
+                    Object[] obj = processResult(message, message.getExchange(),
+                                                 null, resCtx);
+
+                    callback.handleResponse(resCtx, obj);
+                } catch (Throwable ex) {
+                    callback.handleException(resCtx, ex);
+                }
+            }
+        } finally {
+            if (origBus != bus) {
+                BusFactory.setThreadDefaultBus(origBus);
+            }
+            synchronized (message.getExchange()) {
+                if (!isPartialResponse(message)
+                    || message.getContent(Exception.class) != null) {
+                    message.getExchange().put(FINISHED, Boolean.TRUE);
+                    message.getExchange().setInMessage(message);
+                    message.getExchange().notifyAll();
+                }
+            }
+        }
+    }
+
+    public Conduit getConduit() {
+        Message message = new MessageImpl();
+        Exchange exchange = new ExchangeImpl();
+        message.setExchange(exchange);
+        message.putAll(getRequestContext());
+        setExchangeProperties(exchange, getEndpoint(), null);
+        return getConduitSelector().selectConduit(message);
+    }
+
+    protected void prepareConduitSelector(Message message) {
+        getConduitSelector().prepare(message);
+        message.getExchange().put(ConduitSelector.class, getConduitSelector());
+    }
+
+    protected void setOutMessageProperties(Message message, BindingOperationInfo boi) {
+        message.put(Message.REQUESTOR_ROLE, Boolean.TRUE);
+        message.put(Message.INBOUND_MESSAGE, Boolean.FALSE);
+        if (null != boi) {
+            message.put(BindingMessageInfo.class, boi.getInput());
+            message.put(MessageInfo.class, boi.getOperationInfo().getInput());
+        }
+    }
+
+    protected void setExchangeProperties(Exchange exchange,
+                                         Endpoint endpoint,
+                                         BindingOperationInfo boi) {
+        if (endpoint != null) {
+            exchange.put(Endpoint.class, endpoint);
+            exchange.put(Service.class, endpoint.getService());
+            exchange.put(Binding.class, endpoint.getBinding());
+        }
+        if (boi != null) {
+            exchange.put(BindingOperationInfo.class, boi);
+        }
+
+        if (exchange.isSynchronous() || executor == null) {
+            exchange.put(MessageObserver.class, this);
+        } else {
+            exchange.put(Executor.class, executor);
+            exchange.put(MessageObserver.class, new MessageObserver() {
+                public void onMessage(final Message message) {
+                    if (!message.getExchange()
+                        .containsKey(Executor.class.getName() + ".USING_SPECIFIED")) {
+
+                        executor.execute(new Runnable() {
+                            public void run() {
+                                ClientImpl.this.onMessage(message);
+                            }
+                        });
+                    } else {
+                        ClientImpl.this.onMessage(message);
+                    }
+                }
+            });
+        }
+        exchange.put(Retryable.class, this);
+        exchange.put(Client.class, this);
+        exchange.put(Bus.class, bus);
+
+        if (endpoint != null) {
+            EndpointInfo endpointInfo = endpoint.getEndpointInfo();
+            if (boi != null) {
+                exchange.put(Message.WSDL_OPERATION, boi.getName());
+            }
+
+            QName serviceQName = endpointInfo.getService().getName();
+            exchange.put(Message.WSDL_SERVICE, serviceQName);
+
+            QName interfaceQName = endpointInfo.getService().getInterface().getName();
+            exchange.put(Message.WSDL_INTERFACE, interfaceQName);
+
+            QName portQName = endpointInfo.getName();
+            exchange.put(Message.WSDL_PORT, portQName);
+            URI wsdlDescription = endpointInfo.getProperty("URI", URI.class);
+            if (wsdlDescription == null) {
+                String address = endpointInfo.getAddress();
+                try {
+                    wsdlDescription = new URI(address + "?wsdl");
+                } catch (URISyntaxException e) {
+                    // do nothing
+                }
+                endpointInfo.setProperty("URI", wsdlDescription);
+            }
+            exchange.put(Message.WSDL_DESCRIPTION, wsdlDescription);
+        }
+    }
+
+    protected PhaseInterceptorChain setupInterceptorChain(Endpoint endpoint) {
+
+        PhaseManager pm = bus.getExtension(PhaseManager.class);
+
+        List<Interceptor<? extends Message>> i1 = bus.getOutInterceptors();
+        if (LOG.isLoggable(Level.FINE)) {
+            LOG.fine("Interceptors contributed by bus: " + i1);
+        }
+        List<Interceptor<? extends Message>> i2 = getOutInterceptors();
+        if (LOG.isLoggable(Level.FINE)) {
+            LOG.fine("Interceptors contributed by client: " + i2);
+        }
+        List<Interceptor<? extends Message>> i3 = endpoint.getOutInterceptors();
+        if (LOG.isLoggable(Level.FINE)) {
+            LOG.fine("Interceptors contributed by endpoint: " + i3);
+        }
+        List<Interceptor<? extends Message>> i4 = endpoint.getBinding().getOutInterceptors();
+        if (LOG.isLoggable(Level.FINE)) {
+            LOG.fine("Interceptors contributed by binding: " + i4);
+        }
+        List<Interceptor<? extends Message>> i5 = null;
+        if (endpoint.getService().getDataBinding() instanceof InterceptorProvider) {
+            i5 = ((InterceptorProvider)endpoint.getService().getDataBinding()).getOutInterceptors();
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.fine("Interceptors contributed by databinding: " + i5);
+            }
+        }
+        if (i5 != null) {
+            return outboundChainCache.get(pm.getOutPhases(), i1, i2, i3, i4, i5);
+        }
+        return outboundChainCache.get(pm.getOutPhases(), i1, i2, i3, i4);
+    }
+
+    protected void modifyChain(InterceptorChain chain, Message ctx, boolean in) {
+        if (ctx == null) {
+            return;
+        }
+        Collection<InterceptorProvider> providers
+            = CastUtils.cast((Collection<?>)ctx.get(Message.INTERCEPTOR_PROVIDERS));
+        if (providers != null) {
+            for (InterceptorProvider p : providers) {
+                if (in) {
+                    chain.add(p.getInInterceptors());
+                } else {
+                    chain.add(p.getOutInterceptors());
+                }
+            }
+        }
+        String key = in ? Message.IN_INTERCEPTORS : Message.OUT_INTERCEPTORS;
+        Collection<Interceptor<? extends Message>> is
+            = CastUtils.cast((Collection<?>)ctx.get(key));
+        if (is != null) {
+            chain.add(is);
+        }
+    }
+
+    protected void setEndpoint(Endpoint e) {
+        getConduitSelector().setEndpoint(e);
+    }
+
+    public int getSynchronousTimeout() {
+        return synchronousTimeout;
+    }
+
+    public void setSynchronousTimeout(int synchronousTimeout) {
+        this.synchronousTimeout = synchronousTimeout;
+    }
+
+    public final ConduitSelector getConduitSelector() {
+        return getConduitSelector(null);
+    }
+
+    protected final ConduitSelector getConduitSelector(
+        ConduitSelector override
+    ) {
+        if (null == conduitSelector) {
+            setConduitSelector(override);
+        }
+        return conduitSelector;
+    }
+
+    public final synchronized void setConduitSelector(ConduitSelector selector) {
+        conduitSelector = selector == null ? new UpfrontConduitSelector() : selector;
+    }
+
+    private boolean isPartialResponse(Message in) {
+        return Boolean.TRUE.equals(in.get(Message.PARTIAL_RESPONSE_MESSAGE));
+    }
+
+    @Override
+    public void close() throws Exception {
+        destroy();
+    }
+
+
+    public class EchoContext extends ConcurrentHashMap<String, Object> {
+        private static final long serialVersionUID = 1L;
+        public EchoContext(Map<String, Object> sharedMap) {
+            super(8, 0.75f, 4);
+            putAll(sharedMap);
+        }
+
+        public void reload() {
+            super.clear();
+            super.putAll(requestContext.get(latestContextThread));
+        }
+        
+        @Override
+        public void clear() {
+            super.clear();
+            try {
+                for (Map.Entry<Thread, EchoContext> ent : requestContext.entrySet()) {
+                    if (ent.getValue() == this) {
+                        requestContext.remove(ent.getKey());
+                        return;
+                    }
+                }
+            } catch (Throwable t) {
+                //ignore
+            }
+        }
+    }
+
+    /**
+     * Class to handle the response contexts.   The clear is overloaded to remove
+     * this context from the threadLocal caches in the ClientImpl
+     */
+    static class ResponseContext implements Map<String, Object>, Serializable {
+        private static final long serialVersionUID = 2L;
+        final Map<String, Object> wrapped;
+        final Map<Thread, ResponseContext> responseContext;
+        
+        ResponseContext(Map<String, Object> origMap, Map<Thread, ResponseContext> rc) {
+            wrapped = origMap;
+            responseContext = rc;
+        }
+
+        ResponseContext(Map<Thread, ResponseContext> rc) {
+            wrapped = new HashMap<>();
+            responseContext = rc;
+        }
+
+        @Override
+        public void clear() {
+            wrapped.clear();
+            try {
+                for (Map.Entry<Thread, ResponseContext> ent : responseContext.entrySet()) {
+                    if (ent.getValue() == this) {
+                        responseContext.remove(ent.getKey());
+                        return;
+                    }
+                }
+            } catch (Throwable t) {
+                //ignore
+            }
+        }
+
+        @Override
+        public int size() {
+            return wrapped.size();
+        }
+        @Override
+        public boolean isEmpty() {
+            return wrapped.isEmpty();
+        }
+        @Override
+        public boolean containsKey(Object key) {
+            return wrapped.containsKey(key);
+        }
+        @Override
+        public boolean containsValue(Object value) {
+            return wrapped.containsKey(value);
+        }
+        @Override
+        public Object get(Object key) {
+            return wrapped.get(key);
+        }
+        @Override
+        public Object put(String key, Object value) {
+            return wrapped.put(key, value);
+        }
+        @Override
+        public Object remove(Object key) {
+            return wrapped.remove(key);
+        }
+        @Override
+        public void putAll(Map<? extends String, ? extends Object> m) {
+            wrapped.putAll(m);
+        }
+        @Override
+        public Set<String> keySet() {
+            return wrapped.keySet();
+        }
+        @Override
+        public Collection<Object> values() {
+            return wrapped.values();
+        }
+        @Override
+        public Set<Entry<String, Object>> entrySet() {
+            return wrapped.entrySet();
+        }
+    }
+
+    public void setExecutor(Executor executor) {
+        if (!SynchronousExecutor.isA(executor)) {
+            this.executor = executor;
+        }
+    }
+
+    
+    public class IllegalEmptyResponseException extends IllegalStateException {
+        private static final long serialVersionUID = 1L;
+
+        public IllegalEmptyResponseException() {
+            super();
+        }
+
+        public IllegalEmptyResponseException(String message) {
+            super(message);
+        }
+
+        public IllegalEmptyResponseException(String message, Throwable cause) {
+            super(message, cause);
+        }
+
+        public IllegalEmptyResponseException(Throwable cause) {
+            super(cause);
+        }
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/endpoint/EndpointImpl.java b/transform/src/patch/java/org/apache/cxf/endpoint/EndpointImpl.java
new file mode 100644
index 0000000..340211b
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/endpoint/EndpointImpl.java
@@ -0,0 +1,220 @@
+/**
+ * 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.endpoint;
+
+import java.io.Closeable;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
+import java.util.logging.Logger;
+
+import javax.xml.namespace.QName;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusException;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.binding.Binding;
+import org.apache.cxf.binding.BindingFactory;
+import org.apache.cxf.binding.BindingFactoryManager;
+import org.apache.cxf.common.i18n.Message;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.configuration.Configurable;
+import org.apache.cxf.feature.Feature;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.interceptor.AbstractAttributedInterceptorProvider;
+import org.apache.cxf.interceptor.ClientFaultConverter;
+import org.apache.cxf.interceptor.InFaultChainInitiatorObserver;
+import org.apache.cxf.interceptor.MessageSenderInterceptor;
+import org.apache.cxf.interceptor.OutFaultChainInitiatorObserver;
+import org.apache.cxf.service.Service;
+import org.apache.cxf.service.model.BindingInfo;
+import org.apache.cxf.service.model.EndpointInfo;
+import org.apache.cxf.transport.MessageObserver;
+
+public class EndpointImpl extends AbstractAttributedInterceptorProvider implements Endpoint, Configurable {
+
+    private static final long serialVersionUID = -7660560719050162091L;
+    private static final Logger LOG = LogUtils.getL7dLogger(EndpointImpl.class);
+    private static final ResourceBundle BUNDLE = LOG.getResourceBundle();
+
+    private Service service;
+    private Binding binding;
+    private EndpointInfo endpointInfo;
+    private Executor executor;
+    private Bus bus;
+    private MessageObserver inFaultObserver;
+    private MessageObserver outFaultObserver;
+    private List<Feature> activeFeatures;
+    private List<Closeable> cleanupHooks;
+
+    public EndpointImpl(Bus bus, Service s, QName endpointName) throws EndpointException {
+        this(bus, s, s.getEndpointInfo(endpointName));
+    }
+
+    public EndpointImpl(Bus bus, Service s, EndpointInfo ei) throws EndpointException {
+        if (ei == null) {
+            throw new NullPointerException("EndpointInfo can not be null!");
+        }
+
+        if (bus == null) {
+            this.bus = BusFactory.getThreadDefaultBus();
+        } else {
+            this.bus = bus;
+        }
+        service = s;
+        endpointInfo = ei;
+
+        createBinding(endpointInfo.getBinding());
+
+        inFaultObserver = new InFaultChainInitiatorObserver(bus);
+        outFaultObserver = new OutFaultChainInitiatorObserver(bus);
+
+        getInFaultInterceptors().add(new ClientFaultConverter());
+        getOutInterceptors().add(new MessageSenderInterceptor());
+        getOutFaultInterceptors().add(new MessageSenderInterceptor());
+    }
+
+    public String getBeanName() {
+        return endpointInfo.getName().toString() + ".endpoint";
+    }
+
+
+    public EndpointInfo getEndpointInfo() {
+        return endpointInfo;
+    }
+
+    public Service getService() {
+        return service;
+    }
+
+    public Binding getBinding() {
+        return binding;
+    }
+
+    public Executor getExecutor() {
+        return executor == null ? service.getExecutor() : executor;
+    }
+
+    public void setExecutor(Executor e) {
+        executor = e;
+    }
+
+    public Bus getBus() {
+        return bus;
+    }
+
+    public void setBus(Bus bus) {
+        this.bus = bus;
+    }
+
+    final void createBinding(BindingInfo bi) throws EndpointException {
+        if (null != bi) {
+            String namespace = bi.getBindingId();
+            try {
+                final BindingFactory bf = bus.getExtension(BindingFactoryManager.class).getBindingFactory(namespace);
+                if (null == bf) {
+                    Message msg = new Message("NO_BINDING_FACTORY", BUNDLE, namespace);
+                    throw new EndpointException(msg);
+                }
+                binding = bf.createBinding(bi);
+            } catch (BusException ex) {
+                throw new EndpointException(ex);
+            }
+        }
+    }
+
+
+    public MessageObserver getInFaultObserver() {
+        return inFaultObserver;
+    }
+
+    public MessageObserver getOutFaultObserver() {
+        return outFaultObserver;
+    }
+
+    public void setInFaultObserver(MessageObserver observer) {
+        inFaultObserver = observer;
+    }
+
+    public void setOutFaultObserver(MessageObserver observer) {
+        outFaultObserver = observer;
+
+    }
+
+    /**
+     * Utility method to make it easy to set properties from Spring.
+     *
+     * @param properties
+     */
+    public void setProperties(Map<String, Object> properties) {
+        this.putAll(properties);
+    }
+
+    /**
+     * @return the list of features <b>already</b> activated for this endpoint.
+     */
+    public List<Feature> getActiveFeatures() {
+        return activeFeatures;
+    }
+
+    /**
+     * @param features the list of features <b>already</b> activated for this endpoint.
+     */
+    public void initializeActiveFeatures(List<? extends Feature> features) {
+        activeFeatures = CastUtils.cast(features);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof EndpointImpl)) {
+            return false;
+        }
+
+        return super.equals(obj);
+    }
+
+    /**
+     * Returns the hashCode based on the EndpointInfo so that this object
+     * can be used as a map key.
+     */
+    @Override
+    public int hashCode() {
+        return endpointInfo.hashCode();
+    }
+
+    public synchronized void addCleanupHook(Closeable c) {
+        if (cleanupHooks == null) {
+            cleanupHooks = new CopyOnWriteArrayList<>();
+        }
+        cleanupHooks.add(c);
+    }
+    public List<Closeable> getCleanupHooks() {
+        if (cleanupHooks == null) {
+            return Collections.emptyList();
+        }
+        return cleanupHooks;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/endpoint/ServerImpl.java b/transform/src/patch/java/org/apache/cxf/endpoint/ServerImpl.java
new file mode 100644
index 0000000..c417cb6
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/endpoint/ServerImpl.java
@@ -0,0 +1,221 @@
+/**
+ * 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.endpoint;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.management.JMException;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusException;
+import org.apache.cxf.binding.BindingFactory;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.logging.RegexLoggingFilter;
+import org.apache.cxf.management.InstrumentationManager;
+import org.apache.cxf.service.model.EndpointInfo;
+import org.apache.cxf.transport.Destination;
+import org.apache.cxf.transport.DestinationFactory;
+import org.apache.cxf.transport.DestinationFactoryManager;
+import org.apache.cxf.transport.MessageObserver;
+import org.apache.cxf.transport.MultipleEndpointObserver;
+
+public class ServerImpl implements Server {
+    private static final Logger LOG = LogUtils.getL7dLogger(ServerImpl.class);
+
+    protected final Endpoint endpoint;
+    protected final Bus bus;
+    protected final BindingFactory bindingFactory;
+
+    private Destination destination;
+    private ServerRegistry serverRegistry;
+    private ServerLifeCycleManager slcMgr;
+    private InstrumentationManager iMgr;
+    private ManagedEndpoint mep;
+    private boolean stopped = true;
+    private boolean destroyDest = true;
+
+    public ServerImpl(Bus bus,
+                      Endpoint endpoint,
+                      DestinationFactory destinationFactory,
+                      BindingFactory bindingFactory) throws BusException, IOException {
+        this.endpoint = endpoint;
+        this.bus = bus;
+        this.bindingFactory = bindingFactory;
+
+        initDestination(destinationFactory);
+    }
+
+    private void initDestination(DestinationFactory destinationFactory) throws BusException, IOException {
+        EndpointInfo ei = endpoint.getEndpointInfo();
+
+        //Treat local transport as a special case, transports loaded by transportId can be replaced
+        //by local transport when the publishing address is a local transport protocol.
+        //Of course its not an ideal situation here to use a hard-coded prefix. To be refactored.
+        if (destinationFactory == null) {
+            if (ei.getAddress() != null && ei.getAddress().indexOf("local://") != -1) {
+                destinationFactory = bus.getExtension(DestinationFactoryManager.class)
+                    .getDestinationFactoryForUri(ei.getAddress());
+            }
+
+            if (destinationFactory == null) {
+                destinationFactory = bus.getExtension(DestinationFactoryManager.class)
+                    .getDestinationFactory(ei.getTransportId());
+            }
+        }
+
+        destination = destinationFactory.getDestination(ei, bus);
+        String wantFilter = ei.getAddress();
+        
+        if (wantFilter != null && wantFilter.startsWith("jms")) {
+            RegexLoggingFilter filter = new RegexLoggingFilter();
+            filter.setPattern("jms(.*?)password=+([^ ]+)");
+            filter.setGroup(2);
+            wantFilter = filter.filter(wantFilter).toString();
+        }
+        LOG.info("Setting the server's publish address to be " + wantFilter);
+        serverRegistry = bus.getExtension(ServerRegistry.class);
+
+        mep = new ManagedEndpoint(bus, endpoint, this);
+
+        slcMgr = bus.getExtension(ServerLifeCycleManager.class);
+        if (slcMgr != null) {
+            slcMgr.registerListener(mep);
+        }
+
+        iMgr = bus.getExtension(InstrumentationManager.class);
+        if (iMgr != null) {
+            try {
+                iMgr.register(mep);
+            } catch (JMException jmex) {
+                LOG.log(Level.WARNING, "Registering ManagedEndpoint failed.", jmex);
+            }
+        }
+    }
+
+    public Destination getDestination() {
+        return destination;
+    }
+
+    public void setDestination(Destination destination) {
+        this.destination = destination;
+    }
+
+    public void start() {
+        if (!stopped) {
+            return;
+        }
+        LOG.fine("Server is starting.");
+        
+        try {
+            bindingFactory.addListener(destination, endpoint);
+        } catch (RuntimeException e) {
+            if (e.getMessage().contains("endpoint already registered on address")) {
+                //this destination is used by another endpoint with same endpoint address
+                //so shouldn't be destroyed by this server
+                this.destroyDest = false;
+            }
+            throw e;
+        }
+
+        // register the active server to run
+        if (null != serverRegistry) {
+            LOG.fine("register the server to serverRegistry ");
+            serverRegistry.register(this);
+        }
+        if (slcMgr == null) {
+            slcMgr = bus.getExtension(ServerLifeCycleManager.class);
+            if (slcMgr != null && mep != null) {
+                slcMgr.registerListener(mep);
+            }
+        }
+        if (slcMgr != null) {
+            slcMgr.startServer(this);
+        }
+        stopped = false;
+    }
+
+    public boolean isStopped() {
+        return stopped;
+    }
+    public boolean isStarted() {
+        return !stopped;
+    }
+
+    public void stop() {
+        if (stopped) {
+            return;
+        }
+
+        LOG.fine("Server is stopping.");
+
+        for (Closeable c : endpoint.getCleanupHooks()) {
+            try {
+                c.close();
+            } catch (IOException e) {
+                //ignore
+            }
+        }
+        if (slcMgr != null) {
+            slcMgr.stopServer(this);
+        }
+
+        MessageObserver mo = getDestination().getMessageObserver();
+        if (mo instanceof MultipleEndpointObserver) {
+            ((MultipleEndpointObserver)mo).getEndpoints().remove(endpoint);
+            if (((MultipleEndpointObserver)mo).getEndpoints().isEmpty()) {
+                getDestination().setMessageObserver(null);
+            }
+        } else {
+            getDestination().setMessageObserver(null);
+        }
+        stopped = true;
+    }
+
+    public void destroy() {
+        stop();
+        if (this.destroyDest) {
+            //we should shutdown the destination here
+            getDestination().shutdown();
+        }
+
+        if (null != serverRegistry) {
+            LOG.fine("unregister the server to serverRegistry ");
+            serverRegistry.unregister(this);
+        }
+
+        if (iMgr != null) {
+            try {
+                iMgr.unregister(mep);
+            } catch (JMException jmex) {
+                LOG.log(Level.WARNING, "Unregistering ManagedEndpoint failed.", jmex);
+            }
+            iMgr = null;
+        }
+
+    }
+
+    public Endpoint getEndpoint() {
+        return endpoint;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/feature/FastInfosetFeature.java b/transform/src/patch/java/org/apache/cxf/feature/FastInfosetFeature.java
new file mode 100644
index 0000000..daa4bd3
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/feature/FastInfosetFeature.java
@@ -0,0 +1,110 @@
+/**
+ * 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.feature;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.injection.NoJSR250Annotations;
+import org.apache.cxf.interceptor.FIStaxInInterceptor;
+import org.apache.cxf.interceptor.FIStaxOutInterceptor;
+import org.apache.cxf.interceptor.InterceptorProvider;
+
+
+/**
+ * <pre>
+ * <![CDATA[
+    <jaxws:endpoint ...>
+      <jaxws:features>
+       <bean class="org.apache.cxf.feature.FastInfosetFeature"/>
+      </jaxws:features>
+    </jaxws:endpoint>
+  ]]>
+  </pre>
+ */
+@NoJSR250Annotations
+public class FastInfosetFeature extends DelegatingFeature<FastInfosetFeature.Portable> {
+    public FastInfosetFeature() {
+        super(new Portable());
+    }
+
+    public void setForce(boolean b) {
+        delegate.setForce(b);
+    }
+
+    public boolean getForce() {
+        return delegate.getForce();
+    }
+
+    public static class Portable implements AbstractPortableFeature {
+        boolean force;
+        private Integer serializerAttributeValueMapMemoryLimit;
+        private Integer serializerMinAttributeValueSize;
+        private Integer serializerMaxAttributeValueSize;
+        private Integer serializerCharacterContentChunkMapMemoryLimit;
+        private Integer serializerMinCharacterContentChunkSize;
+        private Integer serializerMaxCharacterContentChunkSize;
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+
+            FIStaxInInterceptor in = new FIStaxInInterceptor();
+
+            FIStaxOutInterceptor out = new FIStaxOutInterceptor(force);
+            if (serializerAttributeValueMapMemoryLimit != null && serializerAttributeValueMapMemoryLimit > 0) {
+                out.setSerializerAttributeValueMapMemoryLimit(serializerAttributeValueMapMemoryLimit);
+            }
+            if (serializerMinAttributeValueSize != null && serializerMinAttributeValueSize > 0) {
+                out.setSerializerMinAttributeValueSize(serializerMinAttributeValueSize);
+            }
+            if (serializerMaxAttributeValueSize != null && serializerMaxAttributeValueSize > 0) {
+                out.setSerializerMaxAttributeValueSize(serializerMaxAttributeValueSize);
+            }
+            if (serializerCharacterContentChunkMapMemoryLimit != null
+                    && serializerCharacterContentChunkMapMemoryLimit > 0) {
+                out.setSerializerCharacterContentChunkMapMemoryLimit(
+                        serializerCharacterContentChunkMapMemoryLimit);
+            }
+            if (serializerMinCharacterContentChunkSize != null && serializerMinCharacterContentChunkSize > 0) {
+                out.setSerializerMinCharacterContentChunkSize(serializerMinCharacterContentChunkSize);
+            }
+            if (serializerMaxCharacterContentChunkSize != null && serializerMaxCharacterContentChunkSize > 0) {
+                out.setSerializerMaxCharacterContentChunkSize(serializerMaxCharacterContentChunkSize);
+            }
+
+            provider.getInInterceptors().add(in);
+            provider.getInFaultInterceptors().add(in);
+            provider.getOutInterceptors().add(out);
+            provider.getOutFaultInterceptors().add(out);
+        }
+
+        /**
+         * Set if FastInfoset is always used without negotiation
+         * @param b
+         */
+        public void setForce(boolean b) {
+            force = b;
+        }
+
+        /**
+         * Retrieve the value set with {@link #setForce(boolean)}.
+         */
+        public boolean getForce() {
+            return force;
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/feature/WrappedFeature.java b/transform/src/patch/java/org/apache/cxf/feature/WrappedFeature.java
new file mode 100644
index 0000000..2236a38
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/feature/WrappedFeature.java
@@ -0,0 +1,61 @@
+/**
+ * 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.feature;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.endpoint.Client;
+import org.apache.cxf.endpoint.Server;
+import org.apache.cxf.interceptor.InterceptorProvider;
+
+/**
+ * A Feature is something that is able to customize a Server, Client, or Bus, typically
+ * adding capabilities. For instance, there may be a LoggingFeature which configures
+ * one of the above to log each of their messages.
+ * <p>
+ * By default the initialize methods all delegate to initializeProvider(InterceptorProvider).
+ * If you're simply adding interceptors to a Server, Client, or Bus, this allows you to add
+ * them easily.
+ */
+public class WrappedFeature extends AbstractFeature {
+    final Feature wrapped;
+    public WrappedFeature(Feature f) {
+        wrapped = f;
+    }
+
+    @Override
+    public void initialize(Server server, Bus bus) {
+        wrapped.initialize(server, bus);
+    }
+
+    @Override
+    public void initialize(Client client, Bus bus) {
+        wrapped.initialize(client, bus);
+    }
+
+    @Override
+    public void initialize(InterceptorProvider interceptorProvider, Bus bus) {
+        wrapped.initialize(interceptorProvider, bus);
+    }
+
+    @Override
+    public void initialize(Bus bus) {
+        wrapped.initialize(bus);
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/feature/transform/XSLTOutInterceptor.java b/transform/src/patch/java/org/apache/cxf/feature/transform/XSLTOutInterceptor.java
new file mode 100644
index 0000000..53605cc
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/feature/transform/XSLTOutInterceptor.java
@@ -0,0 +1,210 @@
+/**
+ * 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.feature.transform;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.logging.Logger;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.Templates;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.interceptor.AbstractOutDatabindingInterceptor;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.interceptor.StaxOutInterceptor;
+import org.apache.cxf.io.CachedOutputStream;
+import org.apache.cxf.io.CachedOutputStreamCallback;
+import org.apache.cxf.io.CachedWriter;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.Phase;
+import org.apache.cxf.staxutils.DelegatingXMLStreamWriter;
+import org.apache.cxf.staxutils.StaxUtils;
+
+/** Class provides XSLT transformation of outgoing message.
+ * Actually it breaks streaming (can be fixed in further versions when XSLT engine supports XML stream)
+ */
+public class XSLTOutInterceptor extends AbstractXSLTInterceptor {
+    private static final Logger LOG = LogUtils.getL7dLogger(XSLTOutInterceptor.class);
+
+    public XSLTOutInterceptor(String xsltPath) {
+        super(Phase.PRE_STREAM, StaxOutInterceptor.class, null, xsltPath);
+    }
+
+    public XSLTOutInterceptor(String phase, Class<?> before, Class<?> after, String xsltPath) {
+        super(phase, before, after, xsltPath);
+    }
+
+    @Override
+    public void handleMessage(Message message) {
+        if (checkContextProperty(message)) {
+            return;
+        }
+
+        // 1. Try to get and transform XMLStreamWriter message content
+        XMLStreamWriter xWriter = message.getContent(XMLStreamWriter.class);
+        if (xWriter != null) {
+            transformXWriter(message, xWriter);
+        } else {
+            // 2. Try to get and transform OutputStream message content
+            OutputStream out = message.getContent(OutputStream.class);
+            if (out != null) {
+                transformOS(message, out);
+            } else {
+                // 3. Try to get and transform Writer message content (actually used for JMS TextMessage)
+                Writer writer = message.getContent(Writer.class);
+                if (writer != null) {
+                    transformWriter(message, writer);
+                }
+            }
+        }
+    }
+
+    protected void transformXWriter(Message message, XMLStreamWriter xWriter) {
+        CachedWriter writer = new CachedWriter();
+        XMLStreamWriter delegate = StaxUtils.createXMLStreamWriter(writer);
+        XSLTStreamWriter wrapper = new XSLTStreamWriter(getXSLTTemplate(), writer, delegate, xWriter);
+        message.setContent(XMLStreamWriter.class, wrapper);
+        message.put(AbstractOutDatabindingInterceptor.DISABLE_OUTPUTSTREAM_OPTIMIZATION,
+                    Boolean.TRUE);
+    }
+
+    protected void transformOS(Message message, OutputStream out) {
+        CachedOutputStream wrapper = new CachedOutputStream();
+        CachedOutputStreamCallback callback = new XSLTCachedOutputStreamCallback(getXSLTTemplate(), out);
+        wrapper.registerCallback(callback);
+        message.setContent(OutputStream.class, wrapper);
+    }
+
+    protected void transformWriter(Message message, Writer writer) {
+        XSLTCachedWriter wrapper = new XSLTCachedWriter(getXSLTTemplate(), writer);
+        message.setContent(Writer.class, wrapper);
+    }
+
+
+    public static class XSLTStreamWriter extends DelegatingXMLStreamWriter {
+        private final Templates xsltTemplate;
+        private final CachedWriter cachedWriter;
+        private final XMLStreamWriter origXWriter;
+
+        public XSLTStreamWriter(Templates xsltTemplate, CachedWriter cachedWriter,
+                                XMLStreamWriter delegateXWriter, XMLStreamWriter origXWriter) {
+            super(delegateXWriter);
+            this.xsltTemplate = xsltTemplate;
+            this.cachedWriter = cachedWriter;
+            this.origXWriter = origXWriter;
+        }
+
+        @Override
+        public void close() {
+            Reader transformedReader = null;
+            try {
+                super.flush();
+                transformedReader = XSLTUtils.transform(xsltTemplate, cachedWriter.getReader());
+                StaxUtils.copy(new StreamSource(transformedReader), origXWriter);
+            } catch (XMLStreamException e) {
+                throw new Fault("STAX_COPY", LOG, e, e.getMessage());
+            } catch (IOException e) {
+                throw new Fault("GET_CACHED_INPUT_STREAM", LOG, e, e.getMessage());
+            } finally {
+                try {
+                    if (transformedReader != null) {
+                        transformedReader.close();
+                    }
+                    cachedWriter.close();
+                    StaxUtils.close(origXWriter);
+                    super.close();
+                } catch (Exception e) {
+                    LOG.warning("Cannot close stream after transformation: " + e.getMessage());
+                }
+            }
+        }
+    }
+
+    public static class XSLTCachedOutputStreamCallback implements CachedOutputStreamCallback {
+        private final Templates xsltTemplate;
+        private final OutputStream origStream;
+
+        public XSLTCachedOutputStreamCallback(Templates xsltTemplate, OutputStream origStream) {
+            this.xsltTemplate = xsltTemplate;
+            this.origStream = origStream;
+        }
+
+        @Override
+        public void onFlush(CachedOutputStream wrapper) {
+        }
+
+        @Override
+        public void onClose(CachedOutputStream wrapper) {
+            InputStream transformedStream;
+            Exception exceptionOnClose = null;
+            try {
+                transformedStream = XSLTUtils.transform(xsltTemplate, wrapper.getInputStream());
+                IOUtils.copyAndCloseInput(transformedStream, origStream);
+            } catch (IOException e) {
+                throw new Fault("STREAM_COPY", LOG, e, e.getMessage());
+            } finally {
+                try {
+                    origStream.close();
+                } catch (Exception e) {
+                    exceptionOnClose = e;
+                }
+            }
+            
+            if (exceptionOnClose == null) {
+                return;
+            }
+            throw new Fault(exceptionOnClose);
+        }
+    }
+
+    public static class XSLTCachedWriter extends CachedWriter {
+        private final Templates xsltTemplate;
+        private final Writer origWriter;
+
+        public XSLTCachedWriter(Templates xsltTemplate, Writer origWriter) {
+            this.xsltTemplate = xsltTemplate;
+            this.origWriter = origWriter;
+        }
+
+        @Override
+        protected void doClose() {
+            try {
+                final Reader transformedReader = XSLTUtils.transform(xsltTemplate, getReader());
+                IOUtils.copyAndCloseInput(transformedReader, origWriter, IOUtils.DEFAULT_BUFFER_SIZE);
+            } catch (IOException e) {
+                throw new Fault("READER_COPY", LOG, e, e.getMessage());
+            } finally {
+                try {
+                    origWriter.close();
+                } catch (IOException e) {
+                    LOG.warning("Cannot close stream after transformation: " + e.getMessage());
+                }
+            }
+        }
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/headers/Header.java b/transform/src/patch/java/org/apache/cxf/headers/Header.java
new file mode 100644
index 0000000..f933c14
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/headers/Header.java
@@ -0,0 +1,78 @@
+/**
+ * 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.headers;
+
+import javax.xml.namespace.QName;
+
+import org.apache.cxf.databinding.DataBinding;
+
+public class Header {
+    public enum Direction  {
+        DIRECTION_IN,
+        DIRECTION_OUT,
+        DIRECTION_INOUT
+    }
+
+    public static final String HEADER_LIST = Header.class.getName() + ".list";
+
+
+    private DataBinding dataBinding;
+    private QName name;
+    private Object object;
+
+    private Direction direction = Header.Direction.DIRECTION_OUT;
+
+    public Header(QName q, Object o) {
+        this(q, o, null);
+    }
+
+    public Header(QName q, Object o, DataBinding b) {
+        object = o;
+        name = q;
+        dataBinding = b;
+    }
+
+    public DataBinding getDataBinding() {
+        return dataBinding;
+    }
+    public void setDataBinding(DataBinding dataBinding) {
+        this.dataBinding = dataBinding;
+    }
+    public QName getName() {
+        return name;
+    }
+    public void setName(QName name) {
+        this.name = name;
+    }
+    public Object getObject() {
+        return object;
+    }
+    public void setObject(Object object) {
+        this.object = object;
+    }
+
+    public void setDirection(Direction hdrDirection) {
+        this.direction = hdrDirection;
+    }
+
+    public Direction getDirection() {
+        return direction;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/helpers/DOMUtils.java b/transform/src/patch/java/org/apache/cxf/helpers/DOMUtils.java
new file mode 100644
index 0000000..37bde67
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/helpers/DOMUtils.java
@@ -0,0 +1,895 @@
+/**
+ * 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.helpers;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.ReflectionUtil;
+import org.apache.cxf.common.util.StringUtils;
+
+/**
+ * Few simple utils to read DOM. This is originally from the Jakarta Commons Modeler.
+ */
+public final class DOMUtils {
+    private static boolean isJre9SAAJ;
+    private static final Map<ClassLoader, DocumentBuilder> DOCUMENT_BUILDERS
+        = Collections.synchronizedMap(new WeakHashMap<ClassLoader, DocumentBuilder>());
+    private static final String XMLNAMESPACE = "xmlns";
+    private static volatile Document emptyDocument;
+
+    private static final ClassValue<Method> GET_DOM_ELEMENTS_METHODS = new ClassValue<Method>() {
+        @Override
+        protected Method computeValue(Class<?> type) {
+            try {
+                return ReflectionUtil.getMethod(type, "getDomElement");
+            } catch (NoSuchMethodException e) {
+                //best effort to try, do nothing if NoSuchMethodException
+                return null;
+            }
+        }
+    };
+    private static final ClassValue<Field> GET_DOCUMENT_FRAGMENT_FIELDS = new ClassValue<Field>() {
+        @Override
+        protected Field computeValue(Class<?> type) {
+            return ReflectionUtil.getDeclaredField(type, "documentFragment");
+        }
+
+    };
+
+    static {
+        try {
+            Method[] methods = DOMUtils.class.getClassLoader().
+                loadClass("com.sun.xml.messaging.saaj.soap.SOAPDocumentImpl").getMethods();
+            for (Method method : methods) {
+                if ("register".equals(method.getName())) {
+                    //this is the 1.4+ SAAJ impl
+                    setJava9SAAJ(true);
+                    break;
+                }
+            }
+        } catch (ClassNotFoundException cnfe) {
+            LogUtils.getL7dLogger(DOMUtils.class).finest(
+                "can't load class com.sun.xml.messaging.saaj.soap.SOAPDocumentImpl");
+
+            try {
+                Method[] methods = DOMUtils.class.getClassLoader().
+                    loadClass("com.sun.xml.internal.messaging.saaj.soap.SOAPDocumentImpl").getMethods();
+                for (Method method : methods) {
+                    if ("register".equals(method.getName())) {
+                        //this is the SAAJ impl in JDK9
+                        setJava9SAAJ(true);
+                        break;
+                    }
+                }
+            } catch (ClassNotFoundException cnfe1) {
+                LogUtils.getL7dLogger(DOMUtils.class).finest(
+                    "can't load class com.sun.xml.internal.messaging.saaj.soap.SOAPDocumentImpl");
+            }
+        } catch (Throwable throwable) {
+            LogUtils.getL7dLogger(DOMUtils.class).finest(
+                "Other JDK vendor");
+        }
+    }
+
+    private DOMUtils() {
+    }
+
+    private static DocumentBuilder getDocumentBuilder() throws ParserConfigurationException {
+        ClassLoader loader = getContextClassLoader();
+        if (loader == null) {
+            loader = getClassLoader(DOMUtils.class);
+        }
+        if (loader == null) {
+            return createDocumentBuilder();
+        }
+        DocumentBuilder factory = DOCUMENT_BUILDERS.get(loader);
+        if (factory == null) {
+            factory = createDocumentBuilder();
+            DOCUMENT_BUILDERS.put(loader, factory);
+        }
+        return factory;
+    }
+
+    private static DocumentBuilder createDocumentBuilder() throws ParserConfigurationException {
+        DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
+        f.setNamespaceAware(true);
+        f.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
+        f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+        return f.newDocumentBuilder();
+    }
+
+    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();
+    }
+
+    /**
+     * Creates a new Document object
+     * @throws ParserConfigurationException
+     */
+    public static Document newDocument() {
+        return createDocument();
+    }
+    public static Document createDocument() {
+        try {
+            return getDocumentBuilder().newDocument();
+        } catch (ParserConfigurationException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static synchronized Document createEmptyDocument() {
+        if (emptyDocument == null) {
+            emptyDocument = createDocument();
+
+            // uncomment this to see if anything is actually setting anything into the empty doc
+            /*
+            final Document doc  = createDocument();
+            emptyDocument = (Document)org.apache.cxf.common.util.ProxyHelper.getProxy(
+                DOMUtils.class.getClassLoader(),
+                new Class<?>[] {Document.class},
+                new java.lang.reflect.InvocationHandler() {
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        if (method.getName().contains("create")) {
+                            return method.invoke(doc, args);
+                        }
+                        throw new IllegalStateException("Cannot modify factory document");
+                    }
+                });
+            */
+        }
+        return emptyDocument;
+    }
+    /**
+     * Returns a static Document that should always be "empty".  It's useful as a factory for
+     * for creating Elements and other nodes that will be traversed later and don't need to
+     * be attached into a document
+     * @return an empty document
+     */
+    public static Document getEmptyDocument() {
+        Document doc = emptyDocument;
+        if (doc == null) {
+            doc = createEmptyDocument();
+        }
+        return doc;
+    }
+
+
+    /**
+     * This function is much like getAttribute, but returns null, not "", for a nonexistent attribute.
+     *
+     * @param e
+     * @param attributeName
+     */
+    public static String getAttributeValueEmptyNull(Element e, String attributeName) {
+        Attr node = e.getAttributeNode(attributeName);
+        if (node == null) {
+            return null;
+        }
+        return node.getValue();
+    }
+
+    /**
+     * Get the text content of a node and all it's children or null if there is no text
+     */
+    public static String getAllContent(Node n) {
+        StringBuilder b = new StringBuilder();
+        getAllContent(n, b);
+        return b.toString();
+    }
+    private static void getAllContent(Node n, StringBuilder b) {
+        Node nd = n.getFirstChild();
+        while (nd != null) {
+            if (nd instanceof Text && !(nd instanceof Comment)) {
+                b.append(((Text)nd).getData());
+            } else {
+                getAllContent(nd, b);
+            }
+            nd = nd.getNextSibling();
+        }
+    }
+    /**
+     * Get the trimmed text content of a node or null if there is no text
+     */
+    public static String getContent(Node n) {
+        String s = getRawContent(n);
+        if (s != null) {
+            s = s.trim();
+        }
+        return s;
+    }
+
+    /**
+     * Get the raw text content of a node or null if there is no text
+     */
+    public static String getRawContent(Node n) {
+        if (n == null) {
+            return null;
+        }
+        StringBuilder b = null;
+        String s = null;
+        Node n1 = n.getFirstChild();
+        while (n1 != null) {
+            if (n1.getNodeType() == Node.TEXT_NODE || n1.getNodeType() == Node.CDATA_SECTION_NODE) {
+                if (b != null) {
+                    b.append(((Text)n1).getNodeValue());
+                } else if (s == null) {
+                    s = ((Text)n1).getNodeValue();
+                } else {
+                    b = new StringBuilder(s).append(((Text)n1).getNodeValue());
+                    s = null;
+                }
+            }
+            n1 = n1.getNextSibling();
+        }
+        if (b != null) {
+            return b.toString();
+        }
+        return s;
+    }
+
+    /**
+     * Get the first element child.
+     *
+     * @param parent lookup direct childs
+     * @param name name of the element. If null return the first element.
+     */
+    public static Node getChild(Node parent, String name) {
+        if (parent == null) {
+            return null;
+        }
+
+        Node first = parent.getFirstChild();
+        if (first == null) {
+            return null;
+        }
+
+        for (Node node = first; node != null; node = node.getNextSibling()) {
+            
+            if (node.getNodeType() != Node.ELEMENT_NODE) {
+                continue;
+            }
+            if (name != null && name.equals(node.getNodeName())) {
+                return node;
+            }
+            if (name == null) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+
+    public static boolean hasAttribute(Element element, String value) {
+        NamedNodeMap attributes = element.getAttributes();
+        for (int i = 0; i < attributes.getLength(); i++) {
+            Node node = attributes.item(i);
+            if (value.equals(node.getNodeValue())) {
+                return true;
+            }
+        }
+        return false;
+    }
+    public static String getAttribute(Node element, String attName) {
+        NamedNodeMap attrs = element.getAttributes();
+        if (attrs == null) {
+            return null;
+        }
+        Node attN = attrs.getNamedItem(attName);
+        if (attN == null) {
+            return null;
+        }
+        return attN.getNodeValue();
+    }
+
+    public static String getAttribute(Element element, QName attName) {
+        Attr attr;
+        if (StringUtils.isEmpty(attName.getNamespaceURI())) {
+            attr = element.getAttributeNode(attName.getLocalPart());
+        } else {
+            attr = element.getAttributeNodeNS(attName.getNamespaceURI(), attName.getLocalPart());
+        }
+        return attr == null ? null : attr.getValue();
+    }
+
+    public static void setAttribute(Node node, String attName, String val) {
+        NamedNodeMap attributes = node.getAttributes();
+        Node attNode = node.getOwnerDocument().createAttributeNS(null, attName);
+        attNode.setNodeValue(val);
+        attributes.setNamedItem(attNode);
+    }
+
+    public static void removeAttribute(Node node, String attName) {
+        NamedNodeMap attributes = node.getAttributes();
+        attributes.removeNamedItem(attName);
+    }
+
+    /**
+     * Set or replace the text value
+     */
+    public static void setText(Node node, String val) {
+        Node chld = DOMUtils.getChild(node, Node.TEXT_NODE);
+        if (chld == null) {
+            Node textN = node.getOwnerDocument().createTextNode(val);
+            node.appendChild(textN);
+            return;
+        }
+        // change the value
+        chld.setNodeValue(val);
+    }
+
+    /**
+     * Find the first direct child with a given attribute.
+     *
+     * @param parent
+     * @param elemName name of the element, or null for any
+     * @param attName attribute we're looking for
+     * @param attVal attribute value or null if we just want any
+     */
+    public static Element findChildWithAtt(Node parent, String elemName, String attName, String attVal) {
+
+        Element child = (Element)getChild(parent, Node.ELEMENT_NODE);
+        if (attVal == null) {
+            while (child != null && (elemName == null || elemName.equals(child.getNodeName()))
+                   && DOMUtils.getAttribute(child, attName) != null) {
+                child = (Element)getNext(child, elemName, Node.ELEMENT_NODE);
+            }
+        } else {
+            while (child != null && (elemName == null || elemName.equals(child.getNodeName()))
+                   && !attVal.equals(DOMUtils.getAttribute(child, attName))) {
+                child = (Element)getNext(child, elemName, Node.ELEMENT_NODE);
+            }
+        }
+        return child;
+    }
+
+    /**
+     * Get the first child's content ( ie it's included TEXT node ).
+     */
+    public static String getChildContent(Node parent, String name) {
+        Node first = parent.getFirstChild();
+        if (first == null) {
+            return null;
+        }
+        for (Node node = first; node != null; node = node.getNextSibling()) {
+          
+            if (name.equals(node.getNodeName())) {
+                return getRawContent(node);
+            }
+        }
+        return null;
+    }
+
+    public static QName getElementQName(Element el) {
+        return new QName(el.getNamespaceURI(), el.getLocalName());
+    }
+
+    /**
+     * Creates a QName object based on the qualified name
+     * and using the Node as a base to lookup the namespace
+     * for the prefix
+     * @param qualifiedName
+     * @param node
+     */
+    public static QName createQName(String qualifiedName, Node node) {
+        if (qualifiedName == null) {
+            return null;
+        }
+
+        int index = qualifiedName.indexOf(':');
+
+        if (index == -1) {
+            return new QName(qualifiedName);
+        }
+
+        String prefix = qualifiedName.substring(0, index);
+        String localName = qualifiedName.substring(index + 1);
+        String ns = node.lookupNamespaceURI(prefix);
+
+        if (ns == null) {
+            throw new RuntimeException("Invalid QName in mapping: " + qualifiedName);
+        }
+
+        return new QName(ns, localName, prefix);
+    }
+
+    public static QName convertStringToQName(String expandedQName) {
+        return convertStringToQName(expandedQName, "");
+    }
+
+    public static QName convertStringToQName(String expandedQName, String prefix) {
+        int ind1 = expandedQName.indexOf('{');
+        if (ind1 != 0) {
+            return new QName(expandedQName);
+        }
+
+        int ind2 = expandedQName.indexOf('}');
+        if (ind2 <= ind1 + 1 || ind2 >= expandedQName.length() - 1) {
+            return null;
+        }
+        String ns = expandedQName.substring(ind1 + 1, ind2);
+        String localName = expandedQName.substring(ind2 + 1);
+        return new QName(ns, localName, prefix);
+    }
+    public static Set<QName> convertStringsToQNames(List<String> expandedQNames) {
+        Set<QName> dropElements = Collections.emptySet();
+        if (expandedQNames != null) {
+            dropElements = new LinkedHashSet<>(expandedQNames.size());
+            for (String val : expandedQNames) {
+                dropElements.add(convertStringToQName(val));
+            }
+        }
+        return dropElements;
+    }
+
+
+    /**
+     * Get the first direct child with a given type
+     */
+    public static Element getFirstElement(Node parent) {
+        Node n = parent.getFirstChild();
+        while (n != null && Node.ELEMENT_NODE != n.getNodeType()) {
+            n = n.getNextSibling();
+        }
+        if (n == null) {
+            return null;
+        }
+        return (Element)n;
+    }
+
+    public static Element getNextElement(Element el) {
+        Node nd = el.getNextSibling();
+        while (nd != null) {
+            if (nd.getNodeType() == Node.ELEMENT_NODE) {
+                return (Element)nd;
+            }
+            nd = nd.getNextSibling();
+        }
+        return null;
+    }
+
+    /**
+     * Return the first element child with the specified qualified name.
+     *
+     * @param parent
+     * @param q
+     */
+    public static Element getFirstChildWithName(Element parent, QName q) {
+        String ns = q.getNamespaceURI();
+        String lp = q.getLocalPart();
+        return getFirstChildWithName(parent, ns, lp);
+    }
+
+    /**
+     * Return the first element child with the specified qualified name.
+     *
+     * @param parent
+     * @param ns
+     * @param lp
+     */
+    public static Element getFirstChildWithName(Element parent, String ns, String lp) {
+        for (Node n = parent.getFirstChild(); n != null; n = n.getNextSibling()) {
+            if (n instanceof Element) {
+                Element e = (Element)n;
+                String ens = (e.getNamespaceURI() == null) ? "" : e.getNamespaceURI();
+                if (ns.equals(ens) && lp.equals(e.getLocalName())) {
+                    return e;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Return child elements with specified name.
+     *
+     * @param parent
+     * @param ns
+     * @param localName
+     */
+    public static List<Element> getChildrenWithName(Element parent, String ns, String localName) {
+        List<Element> r = new ArrayList<>();
+        for (Node n = parent.getFirstChild(); n != null; n = n.getNextSibling()) {
+            if (n instanceof Element) {
+                Element e = (Element)n;
+                String eNs = (e.getNamespaceURI() == null) ? "" : e.getNamespaceURI();
+                if (ns.equals(eNs) && localName.equals(e.getLocalName())) {
+                    r.add(e);
+                }
+            }
+        }
+        return r;
+    }
+
+    /**
+     * Returns all child elements with specified namespace.
+     *
+     * @param parent the element to search under
+     * @param ns the namespace to find elements in
+     * @return all child elements with specified namespace
+     */
+    public static List<Element> getChildrenWithNamespace(Element parent, String ns) {
+        List<Element> r = new ArrayList<>();
+        for (Node n = parent.getFirstChild(); n != null; n = n.getNextSibling()) {
+            if (n instanceof Element) {
+                Element e = (Element)n;
+                String eNs = (e.getNamespaceURI() == null) ? "" : e.getNamespaceURI();
+                if (ns.equals(eNs)) {
+                    r.add(e);
+                }
+            }
+        }
+        return r;
+    }
+
+    /**
+     * Get the first child of the specified type.
+     *
+     * @param parent
+     * @param type
+     */
+    public static Node getChild(Node parent, int type) {
+        Node n = parent.getFirstChild();
+        while (n != null && type != n.getNodeType()) {
+            n = n.getNextSibling();
+        }
+        if (n == null) {
+            return null;
+        }
+        return n;
+    }
+
+    /**
+     * Get the next sibling with the same name and type
+     */
+    public static Node getNext(Node current) {
+        String name = current.getNodeName();
+        int type = current.getNodeType();
+        return getNext(current, name, type);
+    }
+
+    /**
+     * Return the next sibling with a given name and type
+     */
+    public static Node getNext(Node current, String name, int type) {
+        Node first = current.getNextSibling();
+        if (first == null) {
+            return null;
+        }
+
+        for (Node node = first; node != null; node = node.getNextSibling()) {
+
+            if (type >= 0 && node.getNodeType() != type) {
+                continue;
+            }
+
+            if (name == null) {
+                return node;
+            }
+            if (name.equals(node.getNodeName())) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    public static class NullResolver implements EntityResolver {
+        public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
+            return new InputSource(new StringReader(""));
+        }
+    }
+
+    public static String getPrefixRecursive(Element el, String ns) {
+        String prefix = getPrefix(el, ns);
+        if (prefix == null && el.getParentNode() instanceof Element) {
+            prefix = getPrefixRecursive((Element)el.getParentNode(), ns);
+        }
+        return prefix;
+    }
+
+    public static String getPrefix(Element el, String ns) {
+        NamedNodeMap atts = el.getAttributes();
+        for (int i = 0; i < atts.getLength(); i++) {
+            Node node = atts.item(i);
+            String name = node.getNodeName();
+            if (ns.equals(node.getNodeValue())
+                && (name != null && (XMLNAMESPACE.equals(name) || name.startsWith(XMLNAMESPACE + ":")))) {
+                return node.getLocalName();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Get all prefixes defined, up to the root, for a namespace URI.
+     *
+     * @param element
+     * @param namespaceUri
+     * @param prefixes
+     */
+    public static void getPrefixesRecursive(Element element, String namespaceUri, List<String> prefixes) {
+        getPrefixes(element, namespaceUri, prefixes);
+        Node parent = element.getParentNode();
+        if (parent instanceof Element) {
+            getPrefixesRecursive((Element)parent, namespaceUri, prefixes);
+        }
+    }
+
+    /**
+     * Get all prefixes defined on this element for the specified namespace.
+     *
+     * @param element
+     * @param namespaceUri
+     * @param prefixes
+     */
+    public static void getPrefixes(Element element, String namespaceUri, List<String> prefixes) {
+        NamedNodeMap atts = element.getAttributes();
+        for (int i = 0; i < atts.getLength(); i++) {
+            Node node = atts.item(i);
+            String name = node.getNodeName();
+            if (namespaceUri.equals(node.getNodeValue())
+                && (name != null && (XMLNAMESPACE.equals(name) || name.startsWith(XMLNAMESPACE + ":")))) {
+                prefixes.add(node.getPrefix());
+            }
+        }
+    }
+
+    public static String createNamespace(Element el, String ns) {
+        String p = "ns1";
+        int i = 1;
+        while (getPrefix(el, ns) != null) {
+            p = "ns" + i;
+            i++;
+        }
+        addNamespacePrefix(el, ns, p);
+        return p;
+    }
+
+    /**
+     * Starting from a node, find the namespace declaration for a prefix. for a matching namespace
+     * declaration.
+     *
+     * @param node search up from here to search for namespace definitions
+     * @param searchPrefix the prefix we are searching for
+     * @return the namespace if found.
+     */
+    public static String getNamespace(Node node, String searchPrefix) {
+
+        Element el;
+        while (!(node instanceof Element)) {
+            node = node.getParentNode();
+        }
+        el = (Element)node;
+
+        NamedNodeMap atts = el.getAttributes();
+        for (int i = 0; i < atts.getLength(); i++) {
+            Node currentAttribute = atts.item(i);
+            String currentLocalName = currentAttribute.getLocalName();
+            String currentPrefix = currentAttribute.getPrefix();
+            if (searchPrefix.equals(currentLocalName) && XMLNAMESPACE.equals(currentPrefix)) {
+                return currentAttribute.getNodeValue();
+            } else if (StringUtils.isEmpty(searchPrefix) && XMLNAMESPACE.equals(currentLocalName)
+                       && StringUtils.isEmpty(currentPrefix)) {
+                return currentAttribute.getNodeValue();
+            }
+        }
+
+        Node parent = el.getParentNode();
+        if (parent instanceof Element) {
+            return getNamespace(parent, searchPrefix);
+        }
+
+        return null;
+    }
+
+    public static List<Element> findAllElementsByTagNameNS(Element elem, String nameSpaceURI,
+                                                           String localName) {
+        List<Element> ret = new LinkedList<>();
+        findAllElementsByTagNameNS(elem, nameSpaceURI, localName, ret);
+        return ret;
+    }
+
+    /**
+     * Try to get the DOM Node from the SAAJ Node with JAVA9 afterwards
+     * @param node The original node we need check
+     * @return The DOM node
+     */
+    public static Node getDomElement(Node node) {
+        if (node != null && isJava9SAAJ()) {
+            //java9plus hack
+            Method method = GET_DOM_ELEMENTS_METHODS.get(node.getClass());
+            if (method != null) {
+                try {
+                    return (Node)ReflectionUtil.setAccessible(method).invoke(node);
+                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+        return node;
+    }
+
+    /**
+     * Try to get the DOM DocumentFragment from the SAAJ DocumentFragment with JAVA9 afterwards
+     * @param fragment The original documentFragment we need to check
+     * @return The DOM DocumentFragment
+     */
+    public static DocumentFragment getDomDocumentFragment(DocumentFragment fragment) {
+        if (fragment != null && isJava9SAAJ()) {
+            //java9 plus hack
+            Field f = GET_DOCUMENT_FRAGMENT_FIELDS.get(fragment.getClass());
+            if (f != null) {
+                return ReflectionUtil.accessDeclaredField(f, fragment, DocumentFragment.class);
+            }
+        }
+        return fragment;
+    }
+
+    private static void findAllElementsByTagNameNS(Element el, String nameSpaceURI, String localName,
+                                                   List<Element> elementList) {
+
+        if (el.getNamespaceURI() != null && localName.equals(el.getLocalName())
+            && nameSpaceURI.contains(el.getNamespaceURI())) {
+            elementList.add(el);
+        }
+        Element elem = getFirstElement(el);
+        while (elem != null) {
+            findAllElementsByTagNameNS(elem, nameSpaceURI, localName, elementList);
+            elem = getNextElement(elem);
+        }
+    }
+
+    public static List<Element> findAllElementsByTagName(Element elem, String tagName) {
+        List<Element> ret = new LinkedList<>();
+        findAllElementsByTagName(elem, tagName, ret);
+        return ret;
+    }
+
+    private static void findAllElementsByTagName(Element el, String tagName, List<Element> elementList) {
+
+        if (tagName.equals(el.getTagName())) {
+            elementList.add(el);
+        }
+        Element elem = getFirstElement(el);
+        while (elem != null) {
+            findAllElementsByTagName(elem, tagName, elementList);
+            elem = getNextElement(elem);
+        }
+    }
+    public static boolean hasElementWithName(Element el, String nameSpaceURI, String localName) {
+        if (el.getNamespaceURI() != null && localName.equals(el.getLocalName())
+            && nameSpaceURI.contains(el.getNamespaceURI())) {
+            return true;
+        }
+        Element elem = getFirstElement(el);
+        while (elem != null) {
+            if (hasElementWithName(elem, nameSpaceURI, localName)) {
+                return true;
+            }
+            elem = getNextElement(elem);
+        }
+        return false;
+    }
+    public static boolean hasElementInNS(Element el, String namespace) {
+
+        if (namespace.equals(el.getNamespaceURI())) {
+            return true;
+        }
+        Element elem = getFirstElement(el);
+        while (elem != null) {
+            if (hasElementInNS(elem, namespace)) {
+                return true;
+            }
+            elem = getNextElement(elem);
+        }
+        return false;
+    }
+    /**
+     * Set a namespace/prefix on an element if it is not set already. First off, it searches for the element
+     * for the prefix associated with the specified namespace. If the prefix isn't null, then this is
+     * returned. Otherwise, it creates a new attribute using the namespace/prefix passed as parameters.
+     *
+     * @param element
+     * @param namespace
+     * @param prefix
+     * @return the prefix associated with the set namespace
+     */
+    public static String setNamespace(Element element, String namespace, String prefix) {
+        String pre = getPrefixRecursive(element, namespace);
+        if (pre != null) {
+            return pre;
+        }
+        element.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + prefix, namespace);
+        return prefix;
+    }
+
+    /**
+     * Add a namespace prefix definition to an element.
+     *
+     * @param element
+     * @param namespaceUri
+     * @param prefix
+     */
+    public static void addNamespacePrefix(Element element, String namespaceUri, String prefix) {
+        element.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + prefix, namespaceUri);
+    }
+
+    public static boolean isJava9SAAJ() {
+        return isJre9SAAJ;
+    }
+
+    private static void setJava9SAAJ(boolean isJava9SAAJ) {
+        DOMUtils.isJre9SAAJ = isJava9SAAJ;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/helpers/IOUtils.java b/transform/src/patch/java/org/apache/cxf/helpers/IOUtils.java
new file mode 100644
index 0000000..74af28d
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/helpers/IOUtils.java
@@ -0,0 +1,428 @@
+/**
+ * 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.helpers;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PushbackInputStream;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+
+import org.apache.cxf.io.CopyingOutputStream;
+import org.apache.cxf.io.Transferable;
+
+public final class IOUtils {
+    public static final Charset UTF8_CHARSET = java.nio.charset.StandardCharsets.UTF_8;
+    public static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
+
+    private IOUtils() {
+
+    }
+    
+    /**
+     * Checks if input stream is empty. If the standard InputStream means do not provide
+     * such details, the stream might be wrapped into PushbackInputStream and is going
+     * to be returned instead of original one.
+     * @param is input stream to check
+     * @return "null" if original input stream is empty, otherwise original stream or 
+     * original stream wrapped into PushbackInputStream.
+     * @throws IOException
+     */
+    public static InputStream nullOrNotEmptyStream(InputStream is) throws IOException {
+        if (isEmpty(is)) {
+            return null;
+        } else if (!(is instanceof PushbackInputStream)) {
+            final byte[] bytes = new byte[1];
+            
+            final PushbackInputStream pbStream = new PushbackInputStream(is);
+            boolean isEmpty = isEof(pbStream.read(bytes));
+            
+            if (!isEmpty) {
+                pbStream.unread(bytes);
+                return pbStream;
+            }
+            
+            return null;
+        }
+        
+        return is;
+    }
+
+    public static boolean isEmpty(InputStream is) throws IOException {
+        if (is == null) {
+            return true;
+        }
+        try {
+            // if available is 0 it does not mean it is empty
+            if (is.available() > 0) {
+                return false;
+            }
+        } catch (IOException ioe) {
+            //Do nothing
+        }
+        final byte[] bytes = new byte[1];
+        if (is.markSupported()) {
+            is.mark(1);
+            try {
+                return isEof(is.read(bytes));
+            } finally {
+                is.reset();
+            }
+        }
+        
+        if (!(is instanceof PushbackInputStream)) {
+            return false;
+        }
+        
+        // it may be an attachment stream
+        PushbackInputStream pbStream = (PushbackInputStream)is;
+        boolean isEmpty = isEof(pbStream.read(bytes));
+        if (!isEmpty) {
+            pbStream.unread(bytes);
+        }
+        
+        return isEmpty;
+    }
+    private static boolean isEof(int result) {
+        return result == -1;
+    }
+    /**
+     * Use this function instead of new String(byte[], String) to avoid surprises from
+     * non-standard default encodings.
+     * @param bytes
+     * @param charsetName
+     */
+    public static String newStringFromBytes(byte[] bytes, String charsetName) {
+        try {
+            return new String(bytes, charsetName);
+        } catch (UnsupportedEncodingException e) {
+            throw
+                new RuntimeException("Impossible failure: Charset.forName(\""
+                                     + charsetName + "\") returns invalid name.");
+
+        }
+    }
+
+
+    /**
+     * Use this function instead of new String(byte[]) to avoid surprises from non-standard default encodings.
+     * @param bytes
+     */
+    public static String newStringFromBytes(byte[] bytes) {
+        return newStringFromBytes(bytes, UTF8_CHARSET.name());
+    }
+
+    /**
+     * Use this function instead of new String(byte[], int, int, String)
+     * to avoid surprises from non-standard default encodings.
+     * @param bytes
+     * @param charsetName
+     * @param start
+     * @param length
+     */
+    public static String newStringFromBytes(byte[] bytes, String charsetName, int start, int length) {
+        try {
+            return new String(bytes, start, length, charsetName);
+        } catch (UnsupportedEncodingException e) {
+            throw
+                new RuntimeException("Impossible failure: Charset.forName(\""
+                                     + charsetName + "\") returns invalid name.");
+
+        }
+    }
+
+    /**
+     * Use this function instead of new String(byte[], int, int)
+     * to avoid surprises from non-standard default encodings.
+     * @param bytes
+     * @param start
+     * @param length
+     */
+    public static String newStringFromBytes(byte[] bytes, int start, int length) {
+        return newStringFromBytes(bytes, UTF8_CHARSET.name(), start, length);
+    }
+
+    public static int copy(final InputStream input, final OutputStream output)
+        throws IOException {
+        if (input == null) {
+            return 0;
+        }
+        if (output instanceof CopyingOutputStream) {
+            return ((CopyingOutputStream)output).copyFrom(input);
+        }
+        return copy(input, output, DEFAULT_BUFFER_SIZE);
+    }
+
+    public static int copyAndCloseInput(final InputStream input,
+            final OutputStream output) throws IOException {
+        try (InputStream in = input) {
+            return copy(in, output);
+        }
+    }
+
+    public static int copyAndCloseInput(final InputStream input,
+            final OutputStream output, int bufferSize) throws IOException {
+        try (InputStream in = input) {
+            return copy(in, output, bufferSize);
+        }
+    }
+
+    public static void copyAndCloseInput(final Reader input,
+                                        final Writer output) throws IOException {
+        try (Reader r = input) {
+            copy(r, output, DEFAULT_BUFFER_SIZE);
+        }
+    }
+
+    public static void copyAndCloseInput(final Reader input,
+            final Writer output, int bufferSize) throws IOException {
+        try (Reader r = input) {
+            copy(r, output, bufferSize);
+        }
+    }
+
+    public static int copy(final InputStream input, final OutputStream output,
+            int bufferSize) throws IOException {
+        int avail = input.available();
+        if (avail > 262144) {
+            avail = 262144;
+        }
+        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[])");
+            }
+            output.write(buffer, 0, n);
+            total += n;
+            n = input.read(buffer);
+        }
+        return total;
+    }
+
+    /**
+     * Copy at least the specified number of bytes from the input to the output
+     * or until the inputstream is finished.
+     * @param input
+     * @param output
+     * @param atLeast
+     * @throws IOException
+     */
+    public static void copyAtLeast(final InputStream input,
+                               final OutputStream output,
+                               int atLeast) throws IOException {
+        final byte[] buffer = new byte[4096];
+        int n = atLeast > buffer.length ? buffer.length : atLeast;
+        n = input.read(buffer, 0, n);
+        while (-1 != n) {
+            if (n == 0) {
+                throw new IOException("0 bytes read in violation of InputStream.read(byte[])");
+            }
+            output.write(buffer, 0, n);
+            atLeast -= n;
+            if (atLeast <= 0) {
+                return;
+            }
+            n = atLeast > buffer.length ? buffer.length : atLeast;
+            n = input.read(buffer, 0, n);
+        }
+    }
+
+    public static void copyAtLeast(final Reader input,
+                                   final Writer output,
+                                   int atLeast) throws IOException {
+        final char[] buffer = new char[4096];
+        int n = atLeast > buffer.length ? buffer.length : atLeast;
+        n = input.read(buffer, 0, n);
+        while (-1 != n) {
+            if (n == 0) {
+                throw new IOException("0 bytes read in violation of Reader.read(char[])");
+            }
+            output.write(buffer, 0, n);
+            atLeast -= n;
+            if (atLeast <= 0) {
+                return;
+            }
+            n = atLeast > buffer.length ? buffer.length : atLeast;
+            n = input.read(buffer, 0, n);
+        }
+    }
+
+
+    public static void copy(final Reader input, final Writer output,
+            final int bufferSize) throws IOException {
+        final char[] buffer = new char[bufferSize];
+        int n = input.read(buffer);
+        while (-1 != n) {
+            output.write(buffer, 0, n);
+            n = input.read(buffer);
+        }
+    }
+
+    public static void transferTo(InputStream inputStream, File destinationFile) throws IOException {
+        if (Transferable.class.isAssignableFrom(inputStream.getClass())) {
+            ((Transferable)inputStream).transferTo(destinationFile);
+        } else {
+            try (OutputStream out = Files.newOutputStream(destinationFile.toPath())) {
+                copyAndCloseInput(inputStream, out);
+            }
+        }
+    }
+
+
+    public static String toString(final InputStream input) throws IOException {
+        return toString(input, DEFAULT_BUFFER_SIZE);
+    }
+    public static String toString(final InputStream input, String charset) throws IOException {
+        return toString(input, DEFAULT_BUFFER_SIZE, charset);
+    }
+    public static String toString(final InputStream input, int bufferSize)
+        throws IOException {
+        return toString(input, bufferSize, null);
+    }
+    public static String toString(final InputStream input, int bufferSize, String charset)
+        throws IOException {
+
+
+        int avail = input.available();
+        if (avail > bufferSize) {
+            bufferSize = avail;
+        }
+        Reader reader = charset == null ? new InputStreamReader(input, UTF8_CHARSET)
+            : new InputStreamReader(input, charset);
+        return toString(reader, bufferSize);
+    }
+
+    public static String toString(final Reader input) throws IOException {
+        return toString(input, DEFAULT_BUFFER_SIZE);
+    }
+    public static String toString(final Reader input, int bufSize) throws IOException {
+
+        StringBuilder buf = new StringBuilder();
+        final char[] buffer = new char[bufSize];
+        try (Reader r = input) {
+            int n = r.read(buffer);
+            while (-1 != n) {
+                if (n == 0) {
+                    throw new IOException("0 bytes read in violation of InputStream.read(byte[])");
+                }
+                buf.append(buffer, 0, n);
+                n = r.read(buffer);
+            }
+            return buf.toString();
+        }
+    }
+
+    public static String readStringFromStream(InputStream in)
+        throws IOException {
+        return toString(in);
+    }
+
+    /**
+     * Load the InputStream into memory and return a ByteArrayInputStream that
+     * represents it. Closes the in stream.
+     *
+     * @param in
+     * @throws IOException
+     */
+    public static ByteArrayInputStream loadIntoBAIS(InputStream in)
+        throws IOException {
+        int i = in.available();
+        if (i < DEFAULT_BUFFER_SIZE) {
+            i = DEFAULT_BUFFER_SIZE;
+        }
+        LoadingByteArrayOutputStream bout = new LoadingByteArrayOutputStream(i);
+        copy(in, bout);
+        in.close();
+        return bout.createInputStream();
+    }
+
+    public static void consume(InputStream in) throws IOException {
+        int i = in.available();
+        if (i == 0) {
+            //if i is 0, then we MAY have already hit the end of the stream
+            //so try a read and return rather than allocate a buffer and such
+            int i2 = in.read();
+            if (i2 == -1) {
+                return;
+            }
+            //reading the byte may have caused a buffer to fill
+            i = in.available();
+        }
+        if (i < DEFAULT_BUFFER_SIZE) {
+            i = DEFAULT_BUFFER_SIZE;
+        }
+        if (i > 65536) {
+            i = 65536;
+        }
+        byte[] bytes = new byte[i];
+        while (in.read(bytes) != -1) {
+            //nothing - just discarding
+        }
+    }
+
+    /**
+     * Consumes at least the given number of bytes from the input stream
+     * @param input
+     * @param atLeast
+     * @throws IOException
+     */
+    public static void consume(final InputStream input,
+                               int atLeast) throws IOException {
+        final byte[] buffer = new byte[4096];
+        int n = atLeast > buffer.length ? buffer.length : atLeast;
+        n = input.read(buffer, 0, n);
+        while (-1 != n) {
+            if (n == 0) {
+                throw new IOException("0 bytes read in violation of InputStream.read(byte[])");
+            }
+            atLeast -= n;
+            if (atLeast <= 0) {
+                return;
+            }
+            n = atLeast > buffer.length ? buffer.length : atLeast;
+            n = input.read(buffer, 0, n);
+        }
+    }
+
+    public static byte[] readBytesFromStream(InputStream in) throws IOException {
+        int i = in.available();
+        if (i < DEFAULT_BUFFER_SIZE) {
+            i = DEFAULT_BUFFER_SIZE;
+        }
+        try (InputStream input = in; ByteArrayOutputStream bos = new ByteArrayOutputStream(i)) {
+            copy(input, bos);
+            return bos.toByteArray();
+        }
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/helpers/ServiceUtils.java b/transform/src/patch/java/org/apache/cxf/helpers/ServiceUtils.java
new file mode 100644
index 0000000..a39ee94
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/helpers/ServiceUtils.java
@@ -0,0 +1,214 @@
+/**
+ * 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.helpers;
+
+import java.util.StringTokenizer;
+
+import org.apache.cxf.annotations.SchemaValidation.SchemaValidationType;
+import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageUtils;
+import org.apache.cxf.service.model.AbstractPropertiesHolder;
+import org.apache.cxf.service.model.BindingOperationInfo;
+import org.apache.cxf.service.model.EndpointInfo;
+import org.apache.cxf.service.model.OperationInfo;
+
+public final class ServiceUtils {
+
+    private ServiceUtils() {
+    }
+
+    /**
+     * A short cut method to be able to test for if Schema Validation should be enabled
+     * for IN or OUT without having to check BOTH and IN or OUT.
+     *
+     * @param message
+     * @param type
+     */
+    public static boolean isSchemaValidationEnabled(SchemaValidationType type, Message message) {
+        SchemaValidationType validationType = getSchemaValidationType(message);
+
+        boolean isRequestor = MessageUtils.isRequestor(message);
+        if (SchemaValidationType.REQUEST.equals(validationType)) {
+            if (isRequestor) {
+                validationType = SchemaValidationType.OUT;
+            } else {
+                validationType = SchemaValidationType.IN;
+            }
+        } else if (SchemaValidationType.RESPONSE.equals(validationType)) {
+            if (isRequestor) {
+                validationType = SchemaValidationType.IN;
+            } else {
+                validationType = SchemaValidationType.OUT;
+            }
+        }
+
+        return validationType.equals(type)
+            || ((SchemaValidationType.IN.equals(type) || SchemaValidationType.OUT.equals(type))
+                && SchemaValidationType.BOTH.equals(validationType));
+    }
+    /**
+     * A convenience method to check for schema validation config in the message context, and then in the service model.
+     * Does not modify the Message context (other than what is done in the getContextualProperty itself)
+     *
+     * @param message
+     */
+    public static SchemaValidationType getSchemaValidationType(Message message) {
+        SchemaValidationType validationType = getOverrideSchemaValidationType(message);
+        if (validationType == null) {
+            validationType = getSchemaValidationTypeFromModel(message);
+        }
+        if (validationType == null) {
+            Object obj = message.getContextualProperty(Message.SCHEMA_VALIDATION_ENABLED);
+            if (obj != null) {
+                validationType = getSchemaValidationType(obj);
+            }
+        }
+        if (validationType == null) {
+            validationType = SchemaValidationType.NONE;
+        }
+
+        return validationType;
+    }
+
+    private static SchemaValidationType getOverrideSchemaValidationType(Message message) {
+        Object obj = message.get(Message.SCHEMA_VALIDATION_ENABLED);
+        if (obj == null && message.getExchange() != null) {
+            obj = message.getExchange().get(Message.SCHEMA_VALIDATION_ENABLED);
+        }
+        if (obj != null) {
+            // this method will transform the legacy enabled as well
+            return getSchemaValidationType(obj);
+        }
+        return null;
+    }
+
+    private static SchemaValidationType getSchemaValidationTypeFromModel(Message message) {
+        Exchange exchange = message.getExchange();
+        SchemaValidationType validationType = null;
+
+        if (exchange != null) {
+
+            BindingOperationInfo boi = exchange.getBindingOperationInfo();
+            if (boi != null) {
+                OperationInfo opInfo = boi.getOperationInfo();
+                if (opInfo != null) {
+                    validationType = getSchemaValidationTypeFromModel(opInfo);
+                }
+            }
+
+            if (validationType == null) {
+                Endpoint endpoint = exchange.getEndpoint();
+                if (endpoint != null) {
+                    EndpointInfo ep = endpoint.getEndpointInfo();
+                    if (ep != null) {
+                        validationType = getSchemaValidationTypeFromModel(ep);
+                    }
+                }
+            }
+        }
+
+        return validationType;
+    }
+
+    private static SchemaValidationType getSchemaValidationTypeFromModel(
+        AbstractPropertiesHolder properties) {
+        Object obj = properties.getProperty(Message.SCHEMA_VALIDATION_TYPE);
+        if (obj != null) {
+            return getSchemaValidationType(obj);
+        }
+        return null;
+    }
+
+    public static SchemaValidationType getSchemaValidationType(Object obj) {
+        if (obj instanceof SchemaValidationType) {
+            return (SchemaValidationType)obj;
+        } else if (obj != null) {
+            String value = obj.toString().toUpperCase(); // handle boolean values as well
+            if ("TRUE".equals(value)) {
+                return SchemaValidationType.BOTH;
+            } else if ("FALSE".equals(value)) {
+                return SchemaValidationType.NONE;
+            } else if (value.length() > 0) {
+                return SchemaValidationType.valueOf(value);
+            }
+        }
+
+        // fall through default value
+        return SchemaValidationType.NONE;
+    }
+
+    /**
+     * Generates a suitable service name from a given class. The returned name
+     * is the simple name of the class, i.e. without the package name.
+     *
+     * @param clazz the class.
+     * @return the name.
+     */
+    public static String makeServiceNameFromClassName(Class<?> clazz) {
+        String name = clazz.getName();
+        int last = name.lastIndexOf('.');
+        if (last != -1) {
+            name = name.substring(last + 1);
+        }
+
+        int inner = name.lastIndexOf('$');
+        if (inner != -1) {
+            name = name.substring(inner + 1);
+        }
+
+        return name;
+    }
+
+    /**
+     * Generates the name of a XML namespace from a given class name and
+     * protocol. The returned namespace will take the form
+     * <code>protocol://domain</code>, where <code>protocol</code> is the
+     * given protocol, and <code>domain</code> the inversed package name of
+     * the given class name. <p/> For instance, if the given class name is
+     * <code>org.codehaus.xfire.services.Echo</code>, and the protocol is
+     * <code>http</code>, the resulting namespace would be
+     * <code>http://services.xfire.codehaus.org</code>.
+     *
+     * @param className the class name
+     * @param protocol the protocol (eg. <code>http</code>)
+     * @return the namespace
+     */
+    public static String makeNamespaceFromClassName(String className, String protocol) {
+        int index = className.lastIndexOf('.');
+
+        if (index == -1) {
+            return protocol + "://" + "DefaultNamespace";
+        }
+
+        String packageName = className.substring(0, index);
+
+        StringTokenizer st = new StringTokenizer(packageName, ".");
+        String[] words = new String[st.countTokens()];
+
+        for (int i = words.length - 1; i >= 0; --i) {
+            words[i] = st.nextToken();
+        }
+
+        return protocol + "://" + String.join(".", words) + "/";
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/interceptor/AbstractFaultChainInitiatorObserver.java b/transform/src/patch/java/org/apache/cxf/interceptor/AbstractFaultChainInitiatorObserver.java
new file mode 100644
index 0000000..70d5ae0
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/interceptor/AbstractFaultChainInitiatorObserver.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.interceptor;
+
+import java.util.SortedSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+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.logging.LogUtils;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.FaultMode;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageImpl;
+import org.apache.cxf.phase.Phase;
+import org.apache.cxf.phase.PhaseInterceptorChain;
+import org.apache.cxf.service.model.BindingFaultInfo;
+import org.apache.cxf.transport.MessageObserver;
+
+public abstract class AbstractFaultChainInitiatorObserver implements MessageObserver {
+
+    private static final Logger LOG = LogUtils.getL7dLogger(AbstractFaultChainInitiatorObserver.class);
+
+    private Bus bus;
+    private ClassLoader loader;
+
+    public AbstractFaultChainInitiatorObserver(Bus bus) {
+        this.bus = bus;
+        if (bus != null) {
+            loader = bus.getExtension(ClassLoader.class);
+        }
+    }
+
+    public void onMessage(Message message) {
+
+        assert null != message;
+
+        Bus origBus = BusFactory.getAndSetThreadDefaultBus(bus);
+        ClassLoaderHolder origLoader = null;
+        try {
+            if (loader != null) {
+                origLoader = ClassLoaderUtils.setThreadContextClassloader(loader);
+            }
+
+            Exchange exchange = message.getExchange();
+
+            Message faultMessage;
+
+            // now that we have switched over to the fault chain,
+            // prevent any further operations on the in/out message
+
+            if (isOutboundObserver()) {
+                Exception ex = message.getContent(Exception.class);
+                if (!(ex instanceof Fault)) {
+                    ex = new Fault(ex);
+                }
+                FaultMode mode = message.get(FaultMode.class);
+
+                faultMessage = exchange.getOutMessage();
+                if (null == faultMessage) {
+                    faultMessage = new MessageImpl();
+                    faultMessage.setExchange(exchange);
+                    faultMessage = exchange.getEndpoint().getBinding().createMessage(faultMessage);
+                }
+                faultMessage.setContent(Exception.class, ex);
+                if (null != mode) {
+                    faultMessage.put(FaultMode.class, mode);
+                }
+                //CXF-3981
+                if (message.get("javax.xml.ws.addressing.context.inbound") != null) {
+                    faultMessage.put("javax.xml.ws.addressing.context.inbound",
+                                     message.get("javax.xml.ws.addressing.context.inbound"));
+                }
+                exchange.setOutMessage(null);
+                exchange.setOutFaultMessage(faultMessage);
+                if (message.get(BindingFaultInfo.class) != null) {
+                    faultMessage.put(BindingFaultInfo.class, message.get(BindingFaultInfo.class));
+                }
+            } else {
+                faultMessage = message;
+                exchange.setInMessage(null);
+                exchange.setInFaultMessage(faultMessage);
+            }
+
+
+            // setup chain
+            PhaseInterceptorChain chain = new PhaseInterceptorChain(getPhases());
+            initializeInterceptors(faultMessage.getExchange(), chain);
+
+            faultMessage.setInterceptorChain(chain);
+            try {
+                chain.doIntercept(faultMessage);
+            } catch (RuntimeException exc) {
+                LOG.log(Level.SEVERE, "ERROR_DURING_ERROR_PROCESSING", exc);
+                throw exc;
+            } catch (Exception exc) {
+                LOG.log(Level.SEVERE, "ERROR_DURING_ERROR_PROCESSING", exc);
+                throw new RuntimeException(exc);
+            }
+        } finally {
+            if (origBus != bus) {
+                BusFactory.setThreadDefaultBus(origBus);
+            }
+            if (origLoader != null) {
+                origLoader.reset();
+            }
+        }
+    }
+
+    protected abstract boolean isOutboundObserver();
+
+    protected abstract SortedSet<Phase> getPhases();
+
+    protected void initializeInterceptors(Exchange ex, PhaseInterceptorChain chain) {
+
+    }
+
+    public Bus getBus() {
+        return bus;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/interceptor/AbstractLoggingInterceptor.java b/transform/src/patch/java/org/apache/cxf/interceptor/AbstractLoggingInterceptor.java
new file mode 100644
index 0000000..5178453
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/interceptor/AbstractLoggingInterceptor.java
@@ -0,0 +1,298 @@
+/**
+ * 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.interceptor;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.PropertyUtils;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.io.CachedOutputStream;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.AbstractPhaseInterceptor;
+import org.apache.cxf.service.model.EndpointInfo;
+import org.apache.cxf.service.model.InterfaceInfo;
+import org.apache.cxf.staxutils.PrettyPrintXMLStreamWriter;
+import org.apache.cxf.staxutils.StaxUtils;
+
+/**
+ * A simple logging handler which outputs the bytes of the message to the
+ * Logger.
+ * @deprecated
+ */
+@Deprecated
+public abstract class AbstractLoggingInterceptor extends AbstractPhaseInterceptor<Message> {
+    public static final int DEFAULT_LIMIT = 48 * 1024;
+    protected static final String BINARY_CONTENT_MESSAGE = "--- Binary Content ---";
+    protected static final String MULTIPART_CONTENT_MESSAGE = "--- Multipart Content ---";
+    private static final String MULTIPART_CONTENT_MEDIA_TYPE = "multipart";
+    private static final String  LIVE_LOGGING_PROP = "org.apache.cxf.logging.enable";
+    private static final List<String> BINARY_CONTENT_MEDIA_TYPES = Arrays.asList(
+            "application/octet-stream", "application/pdf", "image/png", "image/jpeg", "image/gif");
+
+    protected int limit = DEFAULT_LIMIT;
+    protected long threshold = -1;
+    protected PrintWriter writer;
+    protected boolean prettyLogging;
+    private boolean showBinaryContent;
+    private boolean showMultipartContent = true;
+    private List<String> binaryContentMediaTypes = BINARY_CONTENT_MEDIA_TYPES;
+
+    public AbstractLoggingInterceptor(String phase) {
+        super(phase);
+    }
+    public AbstractLoggingInterceptor(String id, String phase) {
+        super(id, phase);
+    }
+
+    protected static boolean isLoggingDisabledNow(Message message) {
+        Object liveLoggingProp = message.getContextualProperty(LIVE_LOGGING_PROP);
+        return liveLoggingProp != null && PropertyUtils.isFalse(liveLoggingProp);
+    }
+
+    protected abstract Logger getLogger();
+
+    Logger getMessageLogger(Message message) {
+        if (isLoggingDisabledNow(message)) {
+            return null;
+        }
+        Endpoint ep = message.getExchange().getEndpoint();
+        if (ep == null || ep.getEndpointInfo() == null) {
+            return getLogger();
+        }
+        EndpointInfo endpoint = ep.getEndpointInfo();
+        if (endpoint.getService() == null) {
+            return getLogger();
+        }
+        Logger logger = endpoint.getProperty("MessageLogger", Logger.class);
+        if (logger == null) {
+            String serviceName = endpoint.getService().getName().getLocalPart();
+            InterfaceInfo iface = endpoint.getService().getInterface();
+            String portName = endpoint.getName().getLocalPart();
+            String portTypeName = iface.getName().getLocalPart();
+            String logName = "org.apache.cxf.services." + serviceName + "."
+                + portName + "." + portTypeName;
+            logger = LogUtils.getL7dLogger(this.getClass(), null, logName);
+            endpoint.setProperty("MessageLogger", logger);
+        }
+        return logger;
+    }
+
+    public void setOutputLocation(String s) {
+        if (s == null || "<logger>".equals(s)) {
+            writer = null;
+        } else if ("<stdout>".equals(s)) {
+            writer = new PrintWriter(System.out, true);
+        } else if ("<stderr>".equals(s)) {
+            writer = new PrintWriter(System.err, true);
+        } else {
+            try {
+                URI uri = new URI(s);
+                File file = new File(uri);
+                writer = new PrintWriter(new FileWriter(file, true), true);
+            } catch (Exception ex) {
+                getLogger().log(Level.WARNING, "Error configuring log location " + s, ex);
+            }
+        }
+    }
+
+    public void setPrintWriter(PrintWriter w) {
+        writer = w;
+    }
+
+    public PrintWriter getPrintWriter() {
+        return writer;
+    }
+
+    public void setLimit(int lim) {
+        limit = lim;
+    }
+
+    public int getLimit() {
+        return limit;
+    }
+
+    public void setPrettyLogging(boolean flag) {
+        prettyLogging = flag;
+    }
+
+    public boolean isPrettyLogging() {
+        return prettyLogging;
+    }
+
+    public void setInMemThreshold(long t) {
+        threshold = t;
+    }
+
+    public long getInMemThreshold() {
+        return threshold;
+    }
+
+    protected void writePayload(StringBuilder builder, CachedOutputStream cos,
+                                String encoding, String contentType, boolean truncated)
+        throws Exception {
+        // Just transform the XML message when the cos has content
+        if (!truncated && isPrettyLogging() && contentType != null && contentType.contains("xml")
+            && !contentType.toLowerCase().contains("multipart/related") && cos.size() > 0) {
+
+            StringWriter swriter = new StringWriter();
+            XMLStreamWriter xwriter = StaxUtils.createXMLStreamWriter(swriter);
+            xwriter = new PrettyPrintXMLStreamWriter(xwriter, 2);
+            try (InputStream in = cos.getInputStream()) {
+                InputStreamReader inputStreamReader = StringUtils.isEmpty(encoding)
+                    ? new InputStreamReader(in) : new InputStreamReader(in, encoding);
+                StaxUtils.copy(new StreamSource(inputStreamReader), xwriter);
+            } catch (XMLStreamException xse) {
+                //ignore
+            } finally {
+                try {
+                    xwriter.flush();
+                    xwriter.close();
+                } catch (XMLStreamException xse2) {
+                    //ignore
+                }
+            }
+
+            String result = swriter.toString();
+            if (result.length() < limit || limit == -1) {
+                builder.append(result);
+            } else {
+                builder.append(result.substring(0, limit));
+            }
+
+        } else {
+            if (StringUtils.isEmpty(encoding)) {
+                cos.writeCacheTo(builder, limit);
+            } else {
+                cos.writeCacheTo(builder, encoding, limit);
+            }
+        }
+    }
+    protected void writePayload(StringBuilder builder,
+                                StringWriter stringWriter,
+                                String contentType)
+        throws Exception {
+        if (isPrettyLogging()
+            && contentType != null
+            && contentType.contains("xml")
+            && stringWriter.getBuffer().length() > 0) {
+            try {
+                writePrettyPayload(builder, stringWriter, contentType);
+                return;
+            } catch (Exception ex) {
+                // log it as is
+            }
+        }
+        StringBuffer buffer = stringWriter.getBuffer();
+        if (buffer.length() > limit) {
+            builder.append(buffer.subSequence(0, limit));
+        } else {
+            builder.append(buffer);
+        }
+    }
+    protected void writePrettyPayload(StringBuilder builder,
+                                StringWriter stringWriter,
+                                String contentType)
+        throws Exception {
+        // Just transform the XML message when the cos has content
+
+        StringWriter swriter = new StringWriter();
+        XMLStreamWriter xwriter = StaxUtils.createXMLStreamWriter(swriter);
+        xwriter = new PrettyPrintXMLStreamWriter(xwriter, 2);
+        StaxUtils.copy(new StreamSource(new StringReader(stringWriter.getBuffer().toString())), xwriter);
+        xwriter.close();
+
+        String result = swriter.toString();
+        if (result.length() < limit || limit == -1) {
+            builder.append(swriter.toString());
+        } else {
+            builder.append(swriter.toString().substring(0, limit));
+        }
+    }
+
+
+    /**
+     * Transform the string before display. The implementation in this class
+     * does nothing. Override this method if you wish to change the contents of the
+     * logged message before it is delivered to the output.
+     * For example, you can use this to mask out sensitive information.
+     * @param originalLogString the raw log message.
+     * @return transformed data
+     */
+    protected String transform(String originalLogString) {
+        return originalLogString;
+    }
+
+    protected void log(Logger logger, String message) {
+        message = transform(message);
+        if (writer != null) {
+            writer.println(message);
+            // Flushing the writer to make sure the message is written
+            writer.flush();
+        } else if (logger.isLoggable(Level.INFO)) {
+            LogRecord lr = new LogRecord(Level.INFO, message);
+            lr.setSourceClassName(logger.getName());
+            lr.setSourceMethodName(null);
+            lr.setLoggerName(logger.getName());
+            logger.log(lr);
+        }
+    }
+    public void setShowBinaryContent(boolean showBinaryContent) {
+        this.showBinaryContent = showBinaryContent;
+    }
+    public boolean isShowBinaryContent() {
+        return showBinaryContent;
+    }
+    protected boolean isBinaryContent(String contentType) {
+        return contentType != null && binaryContentMediaTypes.contains(contentType);
+    }
+    public boolean isShowMultipartContent() {
+        return showMultipartContent;
+    }
+    public void setShowMultipartContent(boolean showMultipartContent) {
+        this.showMultipartContent = showMultipartContent;
+    }
+    protected boolean isMultipartContent(String contentType) {
+        return contentType != null && contentType.startsWith(MULTIPART_CONTENT_MEDIA_TYPE);
+    }
+    public List<String> getBinaryContentMediaTypes() {
+        return binaryContentMediaTypes;
+    }
+    public void setBinaryContentMediaTypes(List<String> binaryContentMediaTypes) {
+        this.binaryContentMediaTypes = binaryContentMediaTypes;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/interceptor/AnnotationInterceptors.java b/transform/src/patch/java/org/apache/cxf/interceptor/AnnotationInterceptors.java
new file mode 100644
index 0000000..8cc757e
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/interceptor/AnnotationInterceptors.java
@@ -0,0 +1,151 @@
+/**
+ * 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.interceptor;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ResourceBundle;
+
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.i18n.BundleUtils;
+import org.apache.cxf.feature.Feature;
+import org.apache.cxf.feature.Features;
+import org.apache.cxf.message.Message;
+
+public class AnnotationInterceptors {
+
+    private static final ResourceBundle BUNDLE = BundleUtils.getBundle(AnnotationInterceptors.class);
+
+    private Class<?>[] clazzes;
+
+    public AnnotationInterceptors(Class<?> ... clz) {
+        clazzes = clz;
+    }
+
+    private <T> List<T> getAnnotationObject(Class<? extends Annotation> annotationClazz, Class<T> type) {
+
+        for (Class<?> cls : clazzes) {
+            Annotation annotation = cls.getAnnotation(annotationClazz);
+            if (annotation != null) {
+                return initializeAnnotationObjects(annotation, type);
+            }
+        }
+        return null;
+    }
+
+    private <T> List<T> initializeAnnotationObjects(Annotation annotation,
+                                             Class<T> type) {
+        List<T> list = new ArrayList<>();
+        for (String cn : getAnnotationObjectNames(annotation)) {
+            list.add(initializeAnnotationObject(cn, type));
+        }
+        for (Class<? extends T> cn : getAnnotationObjectClasses(annotation, type)) {
+            list.add(initializeAnnotationObject(cn));
+        }
+        return list;
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T> Class<? extends T>[] getAnnotationObjectClasses(Annotation ann, Class<T> type) { //NOPMD
+        if (ann instanceof InFaultInterceptors) {
+            return (Class<? extends T>[])((InFaultInterceptors)ann).classes();
+        } else if (ann instanceof InInterceptors) {
+            return (Class<? extends T>[])((InInterceptors)ann).classes();
+        } else if (ann instanceof OutFaultInterceptors) {
+            return (Class<? extends T>[])((OutFaultInterceptors)ann).classes();
+        } else if (ann instanceof OutInterceptors) {
+            return (Class<? extends T>[])((OutInterceptors)ann).classes();
+        } else if (ann instanceof Features) {
+            return (Class<? extends T>[])((Features)ann).classes();
+        }
+        throw new UnsupportedOperationException("Doesn't support the annotation: " + ann);
+    }
+
+    private String[] getAnnotationObjectNames(Annotation ann) {
+        if (ann instanceof InFaultInterceptors) {
+            return ((InFaultInterceptors)ann).interceptors();
+        } else if (ann instanceof InInterceptors) {
+            return ((InInterceptors)ann).interceptors();
+        } else if (ann instanceof OutFaultInterceptors) {
+            return ((OutFaultInterceptors)ann).interceptors();
+        } else if (ann instanceof OutInterceptors) {
+            return ((OutInterceptors)ann).interceptors();
+        } else if (ann instanceof Features) {
+            return ((Features)ann).features();
+        }
+
+        throw new UnsupportedOperationException("Doesn't support the annotation: " + ann);
+    }
+
+    private <T> T initializeAnnotationObject(String annObjectName, Class<T> type) {
+        try {
+            final Object object = ClassLoaderUtils.loadClass(annObjectName, this.getClass()).newInstance();
+            return type.cast(object);
+        } catch (Throwable e) {
+            throw new Fault(new org.apache.cxf.common.i18n.Message(
+                                            "COULD_NOT_CREATE_ANNOTATION_OBJECT",
+                                            BUNDLE, annObjectName), e);
+        }
+    }
+    private <T> T initializeAnnotationObject(Class<T> type) {
+        try {
+            return type.cast(type.newInstance());
+        } catch (Throwable e) {
+            throw new Fault(new org.apache.cxf.common.i18n.Message(
+                                            "COULD_NOT_CREATE_ANNOTATION_OBJECT",
+                                            BUNDLE, type.getName()), e);
+        }
+    }
+
+    private List<Interceptor<? extends Message>> getAnnotationInterceptorList(Class<? extends Annotation> t) {
+        @SuppressWarnings("rawtypes")
+        List<Interceptor> i = getAnnotationObject(t, Interceptor.class);
+        if (i == null) {
+            return null;
+        }
+        List<Interceptor<? extends Message>> m = new ArrayList<>();
+        for (Interceptor<?> i2 : i) {
+            m.add(i2);
+        }
+        return m;
+    }
+
+    public List<Interceptor<? extends Message>> getInFaultInterceptors() {
+        return getAnnotationInterceptorList(InFaultInterceptors.class);
+    }
+
+    public List<Interceptor<? extends Message>> getInInterceptors() {
+        return getAnnotationInterceptorList(InInterceptors.class);
+    }
+
+    public List<Interceptor<? extends Message>> getOutFaultInterceptors() {
+        return getAnnotationInterceptorList(OutFaultInterceptors.class);
+    }
+
+    public List<Interceptor<? extends Message>> getOutInterceptors() {
+        return getAnnotationInterceptorList(OutInterceptors.class);
+    }
+
+    public List<Feature> getFeatures() {
+        return getAnnotationObject(Features.class, Feature.class);
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/interceptor/AttachmentInInterceptor.java b/transform/src/patch/java/org/apache/cxf/interceptor/AttachmentInInterceptor.java
new file mode 100644
index 0000000..0a56be1
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/interceptor/AttachmentInInterceptor.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.interceptor;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Logger;
+
+import org.apache.cxf.attachment.AttachmentDeserializer;
+import org.apache.cxf.attachment.AttachmentUtil;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.AbstractPhaseInterceptor;
+import org.apache.cxf.phase.Phase;
+
+public class AttachmentInInterceptor extends AbstractPhaseInterceptor<Message> {
+
+    private static final Logger LOG = LogUtils.getL7dLogger(AttachmentInInterceptor.class);
+
+    private static final List<String> TYPES = Collections.singletonList("multipart/related");
+
+    public AttachmentInInterceptor() {
+        super(Phase.RECEIVE);
+    }
+
+    public void handleMessage(Message message) {
+        if (isGET(message)) {
+            LOG.fine("AttachmentInInterceptor skipped in HTTP GET method");
+            return;
+        }
+        if (message.getContent(InputStream.class) == null) {
+            return;
+        }
+
+        String contentType = (String) message.get(Message.CONTENT_TYPE);
+        if (AttachmentUtil.isTypeSupported(contentType, getSupportedTypes())) {
+            AttachmentDeserializer ad = new AttachmentDeserializer(message, getSupportedTypes());
+            try {
+                ad.initializeAttachments();
+            } catch (IOException e) {
+                throw new Fault(e);
+            }
+        }
+    }
+
+    @Override
+    public void handleFault(Message messageParam) {
+    }
+
+    protected List<String> getSupportedTypes() {
+        return TYPES;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/interceptor/ClientOutFaultObserver.java b/transform/src/patch/java/org/apache/cxf/interceptor/ClientOutFaultObserver.java
new file mode 100644
index 0000000..341b3b0
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/interceptor/ClientOutFaultObserver.java
@@ -0,0 +1,68 @@
+/**
+ * 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.interceptor;
+
+import java.util.Map;
+import java.util.SortedSet;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.endpoint.Client;
+import org.apache.cxf.endpoint.ClientCallback;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.Phase;
+import org.apache.cxf.phase.PhaseManager;
+
+public class ClientOutFaultObserver extends AbstractFaultChainInitiatorObserver {
+
+    public ClientOutFaultObserver(Bus bus) {
+        super(bus);
+    }
+
+    @Override
+    protected SortedSet<Phase> getPhases() {
+        return getBus().getExtension(PhaseManager.class).getOutPhases();
+    }
+
+    /**
+     * override the super class method
+     */
+    @Override
+    public void onMessage(Message m) {
+        if (m.get(Message.INBOUND_MESSAGE).equals(Boolean.TRUE)) {
+            //it's outbound fault observer so only take care of outbound fault
+            return;
+        }
+        Exception ex = m.getContent(Exception.class);
+        // remove callback so that it won't be invoked twice
+        ClientCallback callback = m.getExchange().remove(ClientCallback.class);
+
+        if (callback != null) {
+            Map<String, Object> resCtx = CastUtils.cast((Map<?, ?>) m.getExchange().getOutMessage().get(
+                    Message.INVOCATION_CONTEXT));
+            resCtx = CastUtils.cast((Map<?, ?>) resCtx.get(Client.RESPONSE_CONTEXT));
+            callback.handleException(resCtx, ex);
+        }
+    }
+
+    protected boolean isOutboundObserver() {
+        return true;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/interceptor/InFaultChainInitiatorObserver.java b/transform/src/patch/java/org/apache/cxf/interceptor/InFaultChainInitiatorObserver.java
new file mode 100644
index 0000000..fd35182
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/interceptor/InFaultChainInitiatorObserver.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.interceptor;
+
+import java.util.Collection;
+import java.util.SortedSet;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.endpoint.Client;
+import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.Phase;
+import org.apache.cxf.phase.PhaseInterceptorChain;
+import org.apache.cxf.phase.PhaseManager;
+
+public class InFaultChainInitiatorObserver extends AbstractFaultChainInitiatorObserver {
+
+    public InFaultChainInitiatorObserver(Bus bus) {
+        super(bus);
+    }
+
+    @Override
+    protected void initializeInterceptors(Exchange ex, PhaseInterceptorChain chain) {
+        Endpoint e = ex.getEndpoint();
+        Client c = ex.get(Client.class);
+        InterceptorProvider ip = ex.get(InterceptorProvider.class);
+
+        chain.add(getBus().getInFaultInterceptors());
+        if (c != null) {
+            chain.add(c.getInFaultInterceptors());
+        } else if (ip != null) {
+            chain.add(ip.getInFaultInterceptors());
+        }
+        chain.add(e.getService().getInFaultInterceptors());
+        chain.add(e.getInFaultInterceptors());
+        chain.add(e.getBinding().getInFaultInterceptors());
+        if (e.getService().getDataBinding() instanceof InterceptorProvider) {
+            chain.add(((InterceptorProvider)e.getService().getDataBinding()).getInFaultInterceptors());
+        }
+
+        addToChain(chain, ex.getInFaultMessage());
+        addToChain(chain, ex.getOutMessage());
+    }
+    private void addToChain(PhaseInterceptorChain chain, Message m) {
+        Collection<InterceptorProvider> providers
+            = CastUtils.cast((Collection<?>)m.get(Message.INTERCEPTOR_PROVIDERS));
+        if (providers != null) {
+            for (InterceptorProvider p : providers) {
+                chain.add(p.getInFaultInterceptors());
+            }
+        }
+        Collection<Interceptor<? extends Message>> is
+            = CastUtils.cast((Collection<?>)m.get(Message.FAULT_IN_INTERCEPTORS));
+        if (is != null) {
+            chain.add(is);
+        }
+    }
+
+    protected SortedSet<Phase> getPhases() {
+        return getBus().getExtension(PhaseManager.class).getInPhases();
+    }
+
+    protected boolean isOutboundObserver() {
+        return false;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/interceptor/InterceptorChain.java b/transform/src/patch/java/org/apache/cxf/interceptor/InterceptorChain.java
new file mode 100644
index 0000000..da5deba
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/interceptor/InterceptorChain.java
@@ -0,0 +1,109 @@
+/**
+ * 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.interceptor;
+
+import java.util.Collection;
+import java.util.ListIterator;
+
+import org.apache.cxf.message.Message;
+import org.apache.cxf.transport.MessageObserver;
+
+/**
+ * Base interface for all interceptor chains.  An interceptor chain is an
+ * ordered list of interceptors associated with one portion of the message
+ * processing pipeline. Interceptor chains are defined for a client's request
+ * processing, response processing, and incoming SOAP fault processing. Interceptor
+ * chains are defined for a service's request processing, response processing, and
+ * outgoing SOAP fault processing.
+ */
+public interface InterceptorChain extends Iterable<Interceptor<? extends Message>> {
+
+    enum State {
+        PAUSED,
+        SUSPENDED,
+        EXECUTING,
+        COMPLETE,
+        ABORTED,
+    }
+
+    String STARTING_AFTER_INTERCEPTOR_ID = "starting_after_interceptor_id";
+    String STARTING_AT_INTERCEPTOR_ID = "starting_at_interceptor_id";
+
+    /**
+     * Adds a single interceptor to the interceptor chain.
+     *
+     * @param i the interceptor to add
+     */
+    void add(Interceptor<? extends Message> i);
+
+    /**
+     * Adds multiple interceptors to the interceptor chain.
+     * @param i the interceptors to add to the chain
+     */
+    void add(Collection<Interceptor<? extends Message>> i);
+
+    void remove(Interceptor<? extends Message> i);
+
+    boolean doIntercept(Message message);
+
+    boolean doInterceptStartingAfter(Message message, String startingAfterInterceptorID);
+
+    boolean doInterceptStartingAt(Message message, String startingAtInterceptorID);
+
+    /**
+     * Pauses the current chain.   When the stack unwinds, the chain will just
+     * return from the doIntercept method normally.
+     */
+    void pause();
+
+    /**
+     * Suspends the current chain.  When the stack unwinds, the chain back up
+     * the iterator by one (so on resume, the interceptor that called pause will
+     * be re-entered) and then throw a SuspendedInvocationException to the caller
+     */
+    void suspend();
+
+    /**
+     * Resumes the chain.  The chain will use the current thread to continue processing
+     * the last message that was passed into doIntercept
+     */
+    void resume();
+
+    /**
+     * If the chain is marked as paused, this will JUST mark the chain as
+     * in the EXECUTING phase.   This is useful if an interceptor pauses the chain,
+     * but then immediately decides it should not have done that.   It can unpause
+     * the chain and return normally and the normal processing will continue.
+     */
+    void unpause();
+
+
+    void reset();
+
+    State getState();
+
+    ListIterator<Interceptor<? extends Message>> getIterator();
+
+    MessageObserver getFaultObserver();
+
+    void setFaultObserver(MessageObserver i);
+
+    void abort();
+}
diff --git a/transform/src/patch/java/org/apache/cxf/interceptor/LoggingMessage.java b/transform/src/patch/java/org/apache/cxf/interceptor/LoggingMessage.java
new file mode 100644
index 0000000..661e3db
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/interceptor/LoggingMessage.java
@@ -0,0 +1,133 @@
+/**
+ * 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.interceptor;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @deprecated use the logging module rt/features/logging instead
+ */
+@Deprecated
+public final class LoggingMessage {
+    public static final String ID_KEY = LoggingMessage.class.getName() + ".ID";
+    private static final AtomicInteger ID = new AtomicInteger();
+
+    private final String heading;
+    private final StringBuilder address;
+    private final StringBuilder contentType;
+    private final StringBuilder encoding;
+    private final StringBuilder httpMethod;
+    private final StringBuilder header;
+    private final StringBuilder message;
+    private final StringBuilder payload;
+    private final StringBuilder responseCode;
+    private final String id;
+
+
+    public LoggingMessage(String h, String i) {
+        heading = h;
+        id = i;
+
+        contentType = new StringBuilder();
+        address = new StringBuilder();
+        encoding = new StringBuilder();
+        httpMethod = new StringBuilder();
+        header = new StringBuilder();
+        message = new StringBuilder();
+        payload = new StringBuilder();
+        responseCode = new StringBuilder();
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public static String nextId() {
+        return Integer.toString(ID.incrementAndGet());
+    }
+
+
+    public StringBuilder getAddress() {
+        return address;
+    }
+
+    public StringBuilder getEncoding() {
+        return encoding;
+    }
+
+    public StringBuilder getHeader() {
+        return header;
+    }
+
+    public StringBuilder getHttpMethod() {
+        return httpMethod;
+    }
+
+    public StringBuilder getContentType() {
+        return contentType;
+    }
+
+    public StringBuilder getMessage() {
+        return message;
+    }
+
+    public StringBuilder getPayload() {
+        return payload;
+    }
+
+    public StringBuilder getResponseCode() {
+        return responseCode;
+    }
+
+    public String toString() {
+        StringBuilder buffer = new StringBuilder(128);
+        buffer.append(heading);
+        buffer.append("\nID: ").append(id);
+        if (address.length() > 0) {
+            buffer.append("\nAddress: ");
+            buffer.append(address);
+        }
+        if (responseCode.length() > 0) {
+            buffer.append("\nResponse-Code: ");
+            buffer.append(responseCode);
+        }
+        if (encoding.length() > 0) {
+            buffer.append("\nEncoding: ");
+            buffer.append(encoding);
+        }
+        if (httpMethod.length() > 0) {
+            buffer.append("\nHttp-Method: ");
+            buffer.append(httpMethod);
+        }
+        buffer.append("\nContent-Type: ");
+        buffer.append(contentType);
+        buffer.append("\nHeaders: ");
+        buffer.append(header);
+        if (message.length() > 0) {
+            buffer.append("\nMessages: ");
+            buffer.append(message);
+        }
+        if (payload.length() > 0) {
+            buffer.append("\nPayload: ");
+            buffer.append(payload);
+        }
+        buffer.append("\n--------------------------------------");
+        return buffer.toString();
+    }
+}
\ No newline at end of file
diff --git a/transform/src/patch/java/org/apache/cxf/interceptor/OneWayProcessorInterceptor.java b/transform/src/patch/java/org/apache/cxf/interceptor/OneWayProcessorInterceptor.java
new file mode 100644
index 0000000..b140601
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/interceptor/OneWayProcessorInterceptor.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.interceptor;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.logging.Logger;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.io.DelegatingInputStream;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageImpl;
+import org.apache.cxf.message.MessageUtils;
+import org.apache.cxf.phase.AbstractPhaseInterceptor;
+import org.apache.cxf.phase.Phase;
+import org.apache.cxf.transport.Conduit;
+import org.apache.cxf.workqueue.WorkQueueManager;
+
+
+/**
+ *
+ */
+public class OneWayProcessorInterceptor extends AbstractPhaseInterceptor<Message> {
+    public static final String USE_ORIGINAL_THREAD
+        = OneWayProcessorInterceptor.class.getName() + ".USE_ORIGINAL_THREAD";
+    private static final Logger LOG = LogUtils.getL7dLogger(OneWayProcessorInterceptor.class);
+
+    public OneWayProcessorInterceptor() {
+        super(Phase.PRE_LOGICAL);
+    }
+    public OneWayProcessorInterceptor(String phase) {
+        super(phase);
+    }
+    
+    @Override
+    public void handleFault(Message message) {
+        if (message.getExchange().isOneWay()
+            && !isRequestor(message)) {
+            //in a one way, if an exception is thrown, the stream needs to be closed
+            InputStream in = message.getContent(InputStream.class);
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    //ignore
+                }
+            }
+
+        }
+    }
+    public void handleMessage(Message message) {
+
+        if (message.getExchange().isOneWay()
+            && !isRequestor(message)
+            && message.get(OneWayProcessorInterceptor.class) == null
+            && message.getExchange().get(Executor.class) == null) {
+            //one way on server side, fork the rest of this chain onto the
+            //workqueue, call the Outgoing chain directly.
+
+            message.put(OneWayProcessorInterceptor.class, this);
+            final InterceptorChain chain = message.getInterceptorChain();
+
+            boolean robust =
+                MessageUtils.getContextualBoolean(message, Message.ROBUST_ONEWAY, false);
+
+            boolean useOriginalThread =
+                MessageUtils.getContextualBoolean(message, USE_ORIGINAL_THREAD, false);
+
+            if (!useOriginalThread && !robust) {
+                //need to suck in all the data from the input stream as
+                //the transport might discard any data on the stream when this
+                //thread unwinds or when the empty response is sent back
+                DelegatingInputStream in = message.getContent(DelegatingInputStream.class);
+                if (in != null) {
+                    in.cacheInput();
+                }
+            }
+
+            if (robust) {
+                // continue to invoke the chain
+                chain.pause();
+                chain.resume();
+                if (message.getContent(Exception.class) != null) {
+                    // CXF-5629 fault has been delivered alread in resume()
+                    return;
+                }
+            }
+
+            try {
+                Message partial = createMessage(message.getExchange());
+                partial.remove(Message.CONTENT_TYPE);
+                partial.setExchange(message.getExchange());
+                Conduit conduit = message.getExchange().getDestination()
+                    .getBackChannel(message);
+                if (conduit != null) {
+                    message.getExchange().setInMessage(null);
+                    //for a one-way, the back channel could be
+                    //null if it knows it cannot send anything.
+                    conduit.prepare(partial);
+                    conduit.close(partial);
+                    message.getExchange().setInMessage(message);
+                }
+            } catch (IOException e) {
+                //IGNORE
+            }
+
+            if (!useOriginalThread && !robust) {
+                chain.pause();
+                try {
+                    final Object lock = new Object();
+                    synchronized (lock) {
+                        message.getExchange().getBus().getExtension(WorkQueueManager.class)
+                            .getAutomaticWorkQueue().execute(new Runnable() {
+                                public void run() {
+                                    synchronized (lock) {
+                                        lock.notifyAll();
+                                    }
+                                    chain.resume();
+                                }
+                            });
+                        //wait a few milliseconds for the background thread to start processing
+                        //Mostly just to make an attempt at keeping the ordering of the
+                        //messages coming in from a client.  Not guaranteed though.
+                        lock.wait(20L);
+                    }
+                } catch (RejectedExecutionException e) {
+                    LOG.warning(
+                        "Executor queue is full, run the oneway invocation task in caller thread."
+                        + "  Users can specify a larger executor queue to avoid this.");
+                    // only block the thread if the prop is unset or set to false, otherwise let it go
+                    if (!MessageUtils.getContextualBoolean(message,
+                            "org.apache.cxf.oneway.rejected_execution_exception", false)) {
+                        //the executor queue is full, so run the task in the caller thread
+                        chain.unpause();
+                    }
+
+                } catch (InterruptedException e) {
+                    //ignore - likely a busy work queue so we'll just let the one-way go
+                }
+            }
+        }
+    }
+
+    private static Message createMessage(Exchange exchange) {
+        Endpoint ep = exchange.getEndpoint();
+        Message msg = null;
+        if (ep != null) {
+            msg = new MessageImpl();
+            msg.setExchange(exchange);
+            msg = ep.getBinding().createMessage(msg);
+        }
+        return msg;
+    }
+
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/interceptor/OutFaultChainInitiatorObserver.java b/transform/src/patch/java/org/apache/cxf/interceptor/OutFaultChainInitiatorObserver.java
new file mode 100644
index 0000000..a00e403
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/interceptor/OutFaultChainInitiatorObserver.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.interceptor;
+
+import java.util.Collection;
+import java.util.SortedSet;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.endpoint.Client;
+import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.Phase;
+import org.apache.cxf.phase.PhaseInterceptorChain;
+import org.apache.cxf.phase.PhaseManager;
+
+public class OutFaultChainInitiatorObserver extends AbstractFaultChainInitiatorObserver {
+
+    public OutFaultChainInitiatorObserver(Bus bus) {
+        super(bus);
+    }
+
+    @Override
+    protected void initializeInterceptors(Exchange ex, PhaseInterceptorChain chain) {
+        Endpoint e = ex.getEndpoint();
+        Client c = ex.get(Client.class);
+
+        chain.add(getBus().getOutFaultInterceptors());
+        if (c != null) {
+            chain.add(c.getOutFaultInterceptors());
+        }
+        chain.add(e.getService().getOutFaultInterceptors());
+        chain.add(e.getOutFaultInterceptors());
+        chain.add(e.getBinding().getOutFaultInterceptors());
+        if (e.getService().getDataBinding() instanceof InterceptorProvider) {
+            chain.add(((InterceptorProvider)e.getService().getDataBinding()).getOutFaultInterceptors());
+        }
+
+        addToChain(chain, ex.getInMessage());
+        addToChain(chain, ex.getOutFaultMessage());
+    }
+    private void addToChain(PhaseInterceptorChain chain, Message m) {
+        Collection<InterceptorProvider> providers
+            = CastUtils.cast((Collection<?>)m.get(Message.INTERCEPTOR_PROVIDERS));
+        if (providers != null) {
+            for (InterceptorProvider p : providers) {
+                chain.add(p.getOutFaultInterceptors());
+            }
+        }
+        Collection<Interceptor<? extends Message>> is
+            = CastUtils.cast((Collection<?>)m.get(Message.FAULT_OUT_INTERCEPTORS));
+        if (is != null) {
+            chain.add(is);
+        }
+        if (m.getDestination() instanceof InterceptorProvider) {
+            chain.add(((InterceptorProvider)m.getDestination()).getOutFaultInterceptors());
+        }
+    }
+
+    protected SortedSet<Phase> getPhases() {
+        return getBus().getExtension(PhaseManager.class).getOutPhases();
+    }
+
+    protected boolean isOutboundObserver() {
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/transform/src/patch/java/org/apache/cxf/interceptor/security/DefaultSecurityContext.java b/transform/src/patch/java/org/apache/cxf/interceptor/security/DefaultSecurityContext.java
new file mode 100644
index 0000000..9b712f3
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/interceptor/security/DefaultSecurityContext.java
@@ -0,0 +1,188 @@
+/**
+ * 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.interceptor.security;
+
+
+import java.lang.reflect.Method;
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.security.auth.Subject;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.security.GroupPrincipal;
+import org.apache.cxf.common.util.ReflectionUtil;
+import org.apache.cxf.security.LoginSecurityContext;
+
+/**
+ * SecurityContext which implements isUserInRole using the
+ * following approach : skip the first Subject principal, and then checks
+ * Groups the principal is a member of
+ */
+public class DefaultSecurityContext implements LoginSecurityContext {
+    private static final Logger LOG = LogUtils.getL7dLogger(DefaultSecurityContext.class);
+    private static Class<?> javaGroup; 
+    private static Class<?> karafGroup;
+    
+    private Principal p;
+    private Subject subject;
+
+    static {
+        try {
+            javaGroup = Class.forName("java.security.acl.Group");
+        } catch (Exception e) {
+            javaGroup = null;
+        }
+        try {
+            karafGroup = Class.forName("org.apache.karaf.jaas.boot.principal.Group");
+        } catch (Exception e) {
+            karafGroup = null;
+        }
+    }
+    
+    public DefaultSecurityContext(Subject subject) {
+        this.p = findPrincipal(null, subject);
+        this.subject = subject;
+    }
+
+    public DefaultSecurityContext(String principalName, Subject subject) {
+        this.p = findPrincipal(principalName, subject);
+        this.subject = subject;
+    }
+
+    public DefaultSecurityContext(Principal p, Subject subject) {
+        this.p = p;
+        this.subject = subject;
+        if (p == null) {
+            this.p = findPrincipal(null, subject);
+        }
+    }
+
+    private static Principal findPrincipal(String principalName, Subject subject) {
+        if (subject == null) {
+            return null;
+        }
+
+        for (Principal principal : subject.getPrincipals()) {
+            if (!isGroupPrincipal(principal)
+                && (principalName == null || principal.getName().equals(principalName))) {
+                return principal;
+            }
+        }
+
+        // No match for the principalName. Just return first non-Group Principal
+        if (principalName != null) {
+            for (Principal principal : subject.getPrincipals()) {
+                if (!isGroupPrincipal(principal)) {
+                    return principal;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    public Principal getUserPrincipal() {
+        return p;
+    }
+
+    public boolean isUserInRole(String role) {
+        if (subject != null) {
+            for (Principal principal : subject.getPrincipals()) {
+                if (isGroupPrincipal(principal) 
+                    && checkGroup(principal, role)) {
+                    return true;
+                } else if (p != principal
+                           && role.equals(principal.getName())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+
+    protected boolean checkGroup(Principal principal, String role) {
+        if (principal.getName().equals(role)) {
+            return true;
+        }
+
+        Enumeration<? extends Principal> members;
+        try {
+            Method m = ReflectionUtil.getMethod(principal.getClass(), "members");
+            m.setAccessible(true);
+            @SuppressWarnings("unchecked")
+            Enumeration<? extends Principal> ms = (Enumeration<? extends Principal>)m.invoke(principal);
+            members = ms;
+        } catch (Exception e) {
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.fine("Unable to invoke memebers in " + principal.getName() + ":" + e.getMessage());
+            }
+            return false;
+        }
+        
+        while (members.hasMoreElements()) {
+            // this might be a plain role but could represent a group consisting of other groups/roles
+            Principal member = members.nextElement();
+            if (member.getName().equals(role)
+                || isGroupPrincipal(member) 
+                && checkGroup((GroupPrincipal)member, role)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    public Subject getSubject() {
+        return subject;
+    }
+
+    public Set<Principal> getUserRoles() {
+        Set<Principal> roles = new HashSet<>();
+        if (subject != null) {
+            for (Principal principal : subject.getPrincipals()) {
+                if (principal != p) {
+                    roles.add(principal);
+                }
+            }
+        }
+        return roles;
+    }
+    
+    
+    private static boolean instanceOfGroup(Object obj) { 
+        try {
+            return (javaGroup != null && javaGroup.isInstance(obj)) 
+                || (karafGroup != null && karafGroup.isInstance(obj));
+        } catch (Exception ex) {
+            return false;
+        }
+    }
+    
+    public static boolean isGroupPrincipal(Principal principal) {
+        return principal instanceof GroupPrincipal
+            || instanceOfGroup(principal);
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/interceptor/security/JAASLoginInterceptor.java b/transform/src/patch/java/org/apache/cxf/interceptor/security/JAASLoginInterceptor.java
new file mode 100644
index 0000000..3363c20
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/interceptor/security/JAASLoginInterceptor.java
@@ -0,0 +1,233 @@
+/**
+ * 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.interceptor.security;
+
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+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.login.Configuration;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.interceptor.InterceptorChain;
+import org.apache.cxf.interceptor.security.callback.CallbackHandlerProvider;
+import org.apache.cxf.interceptor.security.callback.CallbackHandlerProviderAuthPol;
+import org.apache.cxf.interceptor.security.callback.CallbackHandlerProviderUsernameToken;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.AbstractPhaseInterceptor;
+import org.apache.cxf.phase.Phase;
+import org.apache.cxf.security.SecurityContext;
+
+public class JAASLoginInterceptor extends AbstractPhaseInterceptor<Message> {
+    public static final String ROLE_CLASSIFIER_PREFIX = "prefix";
+    public static final String ROLE_CLASSIFIER_CLASS_NAME = "classname";
+
+    private static final Logger LOG = LogUtils.getL7dLogger(JAASLoginInterceptor.class);
+
+    private String contextName = "";
+    private Configuration loginConfig;
+    private String roleClassifier;
+    private String roleClassifierType = ROLE_CLASSIFIER_PREFIX;
+    private boolean reportFault;
+    private boolean useDoAs = true;
+    private List<CallbackHandlerProvider> callbackHandlerProviders;
+    private boolean allowAnonymous = true;
+    private boolean allowNamedPrincipals;
+
+    public JAASLoginInterceptor() {
+        this(Phase.UNMARSHAL);
+    }
+
+    public JAASLoginInterceptor(String phase) {
+        super(phase);
+        this.callbackHandlerProviders = new ArrayList<>();
+        this.callbackHandlerProviders.add(new CallbackHandlerProviderAuthPol());
+        this.callbackHandlerProviders.add(new CallbackHandlerProviderUsernameToken());
+    }
+
+    public void setContextName(String name) {
+        contextName = name;
+    }
+
+    public String getContextName() {
+        return contextName;
+    }
+
+    /**
+     * @deprecated replaced by {@link #setRoleClassifier(String)}
+     * @param name
+     */
+    @Deprecated
+    public void setRolePrefix(String name) {
+        setRoleClassifier(name);
+    }
+
+    public void setRoleClassifier(String value) {
+        roleClassifier = value;
+    }
+
+    public String getRoleClassifier() {
+        return roleClassifier;
+    }
+
+    public void setRoleClassifierType(String value) {
+        if (!ROLE_CLASSIFIER_PREFIX.equals(value)
+            && !ROLE_CLASSIFIER_CLASS_NAME.equals(value)) {
+            throw new IllegalArgumentException("Unsupported role classifier");
+        }
+        roleClassifierType = value;
+    }
+
+    public String getRoleClassifierType() {
+        return roleClassifierType;
+    }
+
+    public void setReportFault(boolean reportFault) {
+        this.reportFault = reportFault;
+    }
+
+    public void setUseDoAs(boolean useDoAs) {
+        this.useDoAs = useDoAs;
+    }
+
+    private CallbackHandler getFirstCallbackHandler(Message message) {
+        for (CallbackHandlerProvider cbp : callbackHandlerProviders) {
+            CallbackHandler cbh = cbp.create(message);
+            if (cbh != null) {
+                return cbh;
+            }
+        }
+        return null;
+    }
+
+    public void handleMessage(final Message message) {
+        if (allowNamedPrincipals) {
+            SecurityContext sc = message.get(SecurityContext.class);
+            if (sc != null && sc.getUserPrincipal() != null
+                && sc.getUserPrincipal().getName() != null) {
+                return;
+            }
+        }
+
+        CallbackHandler handler = getFirstCallbackHandler(message);
+
+        if (handler == null && !allowAnonymous) {
+            throw new AuthenticationException("Authentication required but no authentication information was supplied");
+        }
+
+        try {
+            LoginContext ctx = new LoginContext(getContextName(), null, handler, loginConfig);
+            ctx.login();
+            Subject subject = ctx.getSubject();
+            String name = getUsername(handler);
+            message.put(SecurityContext.class, createSecurityContext(name, subject));
+
+            // Run the further chain in the context of this subject.
+            // This allows other code to retrieve the subject using pure JAAS
+            if (useDoAs) {
+                Subject.doAs(subject, new PrivilegedAction<Void>() {
+
+                    @Override
+                    public Void run() {
+                        InterceptorChain chain = message.getInterceptorChain();
+                        if (chain != null) {
+                            message.put("suspend.chain.on.current.interceptor", Boolean.TRUE);
+                            chain.doIntercept(message);
+                        }
+                        return null;
+                    }
+                });
+            }
+
+        } catch (LoginException ex) {
+            String errorMessage = "Authentication failed: " + ex.getMessage();
+            LOG.log(Level.FINE, errorMessage, ex);
+            if (reportFault) {
+                AuthenticationException aex = new AuthenticationException(errorMessage);
+                aex.initCause(ex);
+                throw aex;
+
+            }
+            throw new AuthenticationException("Authentication failed (details can be found in server log)");
+        }
+    }
+
+    private String getUsername(CallbackHandler handler) {
+        if (handler == null) {
+            return null;
+        }
+        try {
+            NameCallback usernameCallBack = new NameCallback("user");
+            handler.handle(new Callback[]{usernameCallBack });
+            return usernameCallBack.getName();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    protected CallbackHandler getCallbackHandler(String name, String password) {
+        return new NamePasswordCallbackHandler(name, password);
+    }
+
+    protected SecurityContext createSecurityContext(String name, Subject subject) {
+        if (getRoleClassifier() != null) {
+            return new RolePrefixSecurityContextImpl(subject, getRoleClassifier(),
+                                                     getRoleClassifierType());
+        }
+        return new DefaultSecurityContext(name, subject);
+    }
+
+    public Configuration getLoginConfig() {
+        return loginConfig;
+    }
+
+    public void setLoginConfig(Configuration loginConfig) {
+        this.loginConfig = loginConfig;
+    }
+
+    public List<CallbackHandlerProvider> getCallbackHandlerProviders() {
+        return callbackHandlerProviders;
+    }
+
+    public void setCallbackHandlerProviders(List<CallbackHandlerProvider> callbackHandlerProviders) {
+        this.callbackHandlerProviders.clear();
+        this.callbackHandlerProviders.addAll(callbackHandlerProviders);
+    }
+
+    public void addCallbackHandlerProviders(List<CallbackHandlerProvider> callbackHandlerProviders2) {
+        this.callbackHandlerProviders.addAll(callbackHandlerProviders2);
+    }
+
+    public void setAllowAnonymous(boolean allowAnonymous) {
+        this.allowAnonymous = allowAnonymous;
+    }
+
+    public void setAllowNamedPrincipals(boolean allowNamedPrincipals) {
+        this.allowNamedPrincipals = allowNamedPrincipals;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/internal/CXFAPINamespaceHandler.java b/transform/src/patch/java/org/apache/cxf/internal/CXFAPINamespaceHandler.java
new file mode 100644
index 0000000..e3d432d
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/internal/CXFAPINamespaceHandler.java
@@ -0,0 +1,127 @@
+/**
+ * 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.internal;
+
+import java.net.URL;
+import java.util.Set;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.apache.aries.blueprint.NamespaceHandler;
+import org.apache.aries.blueprint.Namespaces;
+import org.apache.aries.blueprint.ParserContext;
+import org.apache.aries.blueprint.mutable.MutableBeanMetadata;
+import org.apache.cxf.bus.blueprint.BusDefinitionParser;
+import org.apache.cxf.configuration.blueprint.SimpleBPBeanDefinitionParser;
+import org.apache.cxf.feature.FastInfosetFeature;
+import org.apache.cxf.workqueue.AutomaticWorkQueueImpl;
+import org.osgi.service.blueprint.reflect.ComponentMetadata;
+import org.osgi.service.blueprint.reflect.Metadata;
+
+@Namespaces({"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"})
+public class CXFAPINamespaceHandler implements NamespaceHandler {
+
+    public URL getSchemaLocation(String namespace) {
+        String location = null;
+
+        // when schema is being resolved for custom namespace elements, "namespace" is real namespace
+        // (from xmlns:prefix="<namespace>"
+        // but when namespace is <xsd:import>ed, aries/xerces uses systemID (schemaLocation)
+
+        if ("http://cxf.apache.org/configuration/beans".equals(namespace)
+                || "http://cxf.apache.org/schemas/configuration/cxf-beans.xsd".equals(namespace)) {
+            location = "schemas/configuration/cxf-beans.xsd";
+        } else if ("http://cxf.apache.org/configuration/parameterized-types".equals(namespace)
+                || "http://cxf.apache.org/schemas/configuration/parameterized-types.xsd".equals(namespace)) {
+            location = "schemas/configuration/parameterized-types.xsd";
+        } else if ("http://cxf.apache.org/configuration/security".equals(namespace)
+                || "http://cxf.apache.org/schemas/configuration/security.xsd".equals(namespace)) {
+            location = "schemas/configuration/security.xsd";
+        } else if ("http://schemas.xmlsoap.org/wsdl/".equals(namespace)
+                || "http://schemas.xmlsoap.org/wsdl/2003-02-11.xsd".equals(namespace)) {
+            location = "schemas/wsdl/wsdl.xsd";
+        } else if ("http://www.w3.org/2005/08/addressing".equals(namespace)
+                || "http://www.w3.org/2006/03/addressing/ws-addr.xsd".equals(namespace)) {
+            location = "schemas/wsdl/ws-addr.xsd";
+        } else if ("http://schemas.xmlsoap.org/ws/2004/08/addressing".equals(namespace)) {
+            location = "schemas/wsdl/addressing.xsd";
+        } else if ("http://cxf.apache.org/blueprint/core".equals(namespace)) {
+            location = "schemas/blueprint/core.xsd";
+        }
+        if (location != null) {
+            return getClass().getClassLoader().getResource(location);
+        }
+        return null;
+    }
+
+
+    @SuppressWarnings("deprecation")
+    public Metadata parse(Element element, ParserContext context) {
+        String s = element.getLocalName();
+        if ("bus".equals(s)) {
+            //parse bus
+            return new BusDefinitionParser().parse(element, context);
+        } else if ("logging".equals(s)) {
+            //logging feature
+            return new SimpleBPBeanDefinitionParser(org.apache.cxf.feature.LoggingFeature.class)
+                .parse(element, context);
+        } else if ("fastinfoset".equals(s)) {
+            //fastinfosetfeature
+            return new SimpleBPBeanDefinitionParser(FastInfosetFeature.class).parse(element, context);
+        } else if ("workqueue".equals(s)) {
+            return new SimpleBPBeanDefinitionParser(AutomaticWorkQueueImpl.class) {
+                
+                @Override
+                public String getId(Element element, ParserContext context) {
+                    String id = element.hasAttribute("id") ? element.getAttribute("id") : null;
+                    if (id == null) {
+                        id = "cxf.workqueue.";
+                        id += element.hasAttribute("name") ? element.getAttribute("name") : "def";
+                    }
+                    return id;
+                }
+
+                @Override
+                protected void processNameAttribute(Element element, ParserContext ctx,
+                                                    MutableBeanMetadata bean, String val) {
+                    bean.addProperty("name", createValue(ctx, val));
+                }
+            } .parse(element, context);
+        }
+        return null;
+    }
+
+    @SuppressWarnings("rawtypes")
+    public Set<Class> getManagedClasses() {
+        //probably should have the various stuff in cxf-api in here?
+        return null;
+    }
+    public ComponentMetadata decorate(Node node, ComponentMetadata component, ParserContext context) {
+        return null;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/io/CacheAndWriteOutputStream.java b/transform/src/patch/java/org/apache/cxf/io/CacheAndWriteOutputStream.java
new file mode 100644
index 0000000..d89edb9
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/io/CacheAndWriteOutputStream.java
@@ -0,0 +1,96 @@
+/**
+ * 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.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This outputstream implementation will both write to the outputstream
+ * that is specified and cache the data at the same time. This allows us
+ * to go back and retransmit the data at a later time if necessary.
+ *
+ */
+public class CacheAndWriteOutputStream extends CachedOutputStream {
+    OutputStream flowThroughStream;
+    long count;
+    long limit = Long.MAX_VALUE;
+    private boolean isClosed;
+
+    public CacheAndWriteOutputStream(OutputStream stream) {
+        super();
+        if (stream == null) {
+            throw new IllegalArgumentException("Stream may not be null");
+        }
+        flowThroughStream = stream;
+    }
+
+    public void setCacheLimit(long l) {
+        limit = l;
+    }
+
+    public void closeFlowthroughStream() throws IOException {
+        postClose();
+    }
+
+    protected void postClose() throws IOException {
+        if (!isClosed) {
+            flowThroughStream.flush();
+            flowThroughStream.close();
+            isClosed = true;
+        }
+    }
+
+    public OutputStream getFlowThroughStream() {
+        return flowThroughStream;
+    }
+    
+    @Override
+    protected void onWrite() throws IOException {
+        // does nothing
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        flowThroughStream.write(b);
+        if (count <= limit) {
+            super.write(b);
+        }
+        count++;
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        flowThroughStream.write(b, off, len);
+        if (count <= limit) {
+            super.write(b, off, len);
+        }
+        count += len;
+    }
+
+    @Override
+    public void write(byte[] b) throws IOException {
+        flowThroughStream.write(b);
+        if (count <= limit) {
+            super.write(b);
+        }
+        count += b.length;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/io/CachedWriter.java b/transform/src/patch/java/org/apache/cxf/io/CachedWriter.java
new file mode 100644
index 0000000..0178563
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/io/CachedWriter.java
@@ -0,0 +1,665 @@
+/**
+ * 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.io;
+
+import java.io.BufferedOutputStream;
+import java.io.CharArrayReader;
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.common.util.SystemPropertyAction;
+import org.apache.cxf.helpers.FileUtils;
+import org.apache.cxf.helpers.IOUtils;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class CachedWriter extends Writer {
+    private static final File DEFAULT_TEMP_DIR;
+    private static int defaultThreshold;
+    private static long defaultMaxSize;
+    private static String defaultCipherTransformation;
+
+    static {
+
+        String s = SystemPropertyAction.getPropertyOrNull(CachedConstants.OUTPUT_DIRECTORY_SYS_PROP);
+        if (s == null) {
+            // lookup the deprecated property
+            s = SystemPropertyAction.getPropertyOrNull("org.apache.cxf.io.CachedWriter.OutputDirectory");
+        }
+        if (s != null) {
+            File f = new File(s);
+            if (f.exists() && f.isDirectory()) {
+                DEFAULT_TEMP_DIR = f;
+            } else {
+                DEFAULT_TEMP_DIR = null;
+            }
+        } else {
+            DEFAULT_TEMP_DIR = null;
+        }
+
+        setDefaultThreshold(-1);
+        setDefaultMaxSize(-1);
+        setDefaultCipherTransformation(null);
+    }
+
+    protected boolean outputLocked;
+    protected Writer currentStream;
+
+    private boolean cosClosed;
+    private long threshold = defaultThreshold;
+    private long maxSize = defaultMaxSize;
+    private File outputDir = DEFAULT_TEMP_DIR;
+    private String cipherTransformation = defaultCipherTransformation;
+
+    private long totalLength;
+
+    private boolean inmem;
+
+    private boolean tempFileFailed;
+    private File tempFile;
+    private boolean allowDeleteOfFile = true;
+    private CipherPair ciphers;
+
+    private List<CachedWriterCallback> callbacks;
+
+    private List<Object> streamList = new ArrayList<>();
+
+
+    static class LoadingCharArrayWriter extends CharArrayWriter {
+        LoadingCharArrayWriter() {
+            super(1024);
+        }
+        public char[] rawCharArray() {
+            return super.buf;
+        }
+    }
+
+
+    public CachedWriter() {
+        this(defaultThreshold);
+
+        inmem = true;
+    }
+
+    public CachedWriter(long threshold) {
+        this.threshold = threshold;
+        currentStream = new LoadingCharArrayWriter();
+        inmem = true;
+        readBusProperties();
+    }
+
+    private void readBusProperties() {
+        Bus b = BusFactory.getThreadDefaultBus(false);
+        if (b != null) {
+            String v = getBusProperty(b, CachedConstants.THRESHOLD_BUS_PROP, null);
+            if (v != null && threshold == defaultThreshold) {
+                threshold = Integer.parseInt(v);
+            }
+            v = getBusProperty(b, CachedConstants.MAX_SIZE_BUS_PROP, null);
+            if (v != null) {
+                maxSize = Integer.parseInt(v);
+            }
+            v = getBusProperty(b, CachedConstants.CIPHER_TRANSFORMATION_BUS_PROP, null);
+            if (v != null) {
+                cipherTransformation = v;
+            }
+            v = getBusProperty(b, CachedConstants.OUTPUT_DIRECTORY_BUS_PROP, null);
+            if (v != null) {
+                File f = new File(v);
+                if (f.exists() && f.isDirectory()) {
+                    outputDir = f;
+                }
+            }
+        }
+    }
+
+    private static String getBusProperty(Bus b, String key, String dflt) {
+        String v = (String)b.getProperty(key);
+        return v != null ? v : dflt;
+    }
+
+    public void holdTempFile() {
+        allowDeleteOfFile = false;
+    }
+    public void releaseTempFileHold() {
+        allowDeleteOfFile = true;
+    }
+
+    public void registerCallback(CachedWriterCallback cb) {
+        if (null == callbacks) {
+            callbacks = new ArrayList<>();
+        }
+        callbacks.add(cb);
+    }
+
+    public void deregisterCallback(CachedWriterCallback cb) {
+        if (null != callbacks) {
+            callbacks.remove(cb);
+        }
+    }
+
+    public List<CachedWriterCallback> getCallbacks() {
+        return callbacks == null ? null : Collections.unmodifiableList(callbacks);
+    }
+
+    /**
+     * Perform any actions required on stream flush (freeze headers, reset
+     * output stream ... etc.)
+     */
+    protected void doFlush() throws IOException {
+
+    }
+
+    public void flush() throws IOException {
+        if (!cosClosed) {
+            currentStream.flush();
+        }
+
+        if (null != callbacks) {
+            for (CachedWriterCallback cb : callbacks) {
+                cb.onFlush(this);
+            }
+        }
+        doFlush();
+    }
+
+    /**
+     * Perform any actions required on stream closure (handle response etc.)
+     */
+    protected void doClose() throws IOException {
+
+    }
+
+    /**
+     * Perform any actions required after stream closure (close the other related stream etc.)
+     */
+    protected void postClose() throws IOException {
+
+    }
+
+    /**
+     * Locks the output stream to prevent additional writes, but maintains
+     * a pointer to it so an InputStream can be obtained
+     * @throws IOException
+     */
+    public void lockOutputStream() throws IOException {
+        if (outputLocked) {
+            return;
+        }
+        currentStream.flush();
+        outputLocked = true;
+        if (null != callbacks) {
+            for (CachedWriterCallback cb : callbacks) {
+                cb.onClose(this);
+            }
+        }
+        doClose();
+        streamList.remove(currentStream);
+    }
+
+    public void close() throws IOException {
+        if (!cosClosed) {
+            currentStream.flush();
+        }
+        outputLocked = true;
+        if (null != callbacks) {
+            for (CachedWriterCallback cb : callbacks) {
+                cb.onClose(this);
+            }
+        }
+        doClose();
+        currentStream.close();
+        maybeDeleteTempFile(currentStream);
+        if (ciphers != null) {
+            ciphers.clean();
+        }
+        postClose();
+    }
+
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj instanceof CachedWriter) {
+            return currentStream.equals(((CachedWriter)obj).currentStream);
+        }
+        return currentStream.equals(obj);
+    }
+
+    /**
+     * Replace the original stream with the new one, optionally copying the content of the old one
+     * into the new one.
+     * When with Attachment, needs to replace the xml writer stream with the stream used by
+     * AttachmentSerializer or copy the cached output stream to the "real"
+     * output stream, i.e. onto the wire.
+     *
+     * @param out the new output stream
+     * @param copyOldContent flag indicating if the old content should be copied
+     * @throws IOException
+     */
+    public void resetOut(Writer out, boolean copyOldContent) throws IOException {
+        if (out == null) {
+            out = new LoadingCharArrayWriter();
+        }
+
+        if (currentStream instanceof CachedWriter) {
+            CachedWriter ac = (CachedWriter) currentStream;
+            Reader in = ac.getReader();
+            IOUtils.copyAndCloseInput(in, out);
+        } else {
+            if (inmem) {
+                if (currentStream instanceof LoadingCharArrayWriter) {
+                    LoadingCharArrayWriter byteOut = (LoadingCharArrayWriter) currentStream;
+                    if (copyOldContent && byteOut.size() > 0) {
+                        byteOut.writeTo(out);
+                    }
+                } else {
+                    throw new IOException("Unknown format of currentStream");
+                }
+            } else {
+                // read the file
+                currentStream.close();
+                if (copyOldContent) {
+                    InputStreamReader fin = createInputStreamReader(tempFile);
+                    IOUtils.copyAndCloseInput(fin, out);
+                }
+                streamList.remove(currentStream);
+                deleteTempFile();
+                inmem = true;
+            }
+        }
+        currentStream = out;
+        outputLocked = false;
+    }
+
+
+    public long size() {
+        return totalLength;
+    }
+
+    public char[] getChars() throws IOException {
+        flush();
+        if (inmem) {
+            if (currentStream instanceof LoadingCharArrayWriter) {
+                return ((LoadingCharArrayWriter)currentStream).toCharArray();
+            }
+            throw new IOException("Unknown format of currentStream");
+        }
+        // read the file
+        try (Reader fin = createInputStreamReader(tempFile)) {
+            CharArrayWriter out = new CharArrayWriter((int)tempFile.length());
+            char[] bytes = new char[1024];
+            int x = fin.read(bytes);
+            while (x != -1) {
+                out.write(bytes, 0, x);
+                x = fin.read(bytes);
+            }
+            return out.toCharArray();
+        }
+    }
+
+    public void writeCacheTo(Writer out) throws IOException {
+        flush();
+        if (inmem) {
+            if (currentStream instanceof LoadingCharArrayWriter) {
+                ((LoadingCharArrayWriter)currentStream).writeTo(out);
+            } else {
+                throw new IOException("Unknown format of currentStream");
+            }
+        } else {
+            // read the file
+            try (Reader fin = createInputStreamReader(tempFile)) {
+                char[] bytes = new char[1024];
+                int x = fin.read(bytes);
+                while (x != -1) {
+                    out.write(bytes, 0, x);
+                    x = fin.read(bytes);
+                }
+            }
+        }
+    }
+
+    public void writeCacheTo(StringBuilder out, long limit) throws IOException {
+        flush();
+        if (totalLength < limit
+            || limit == -1) {
+            writeCacheTo(out);
+            return;
+        }
+
+        long count = 0;
+        if (inmem) {
+            if (currentStream instanceof LoadingCharArrayWriter) {
+                LoadingCharArrayWriter s = (LoadingCharArrayWriter)currentStream;
+                out.append(s.rawCharArray(), 0, (int)limit);
+            } else {
+                throw new IOException("Unknown format of currentStream");
+            }
+        } else {
+            // read the file
+            try (Reader fin = createInputStreamReader(tempFile)) {
+                char[] bytes = new char[1024];
+                long x = fin.read(bytes);
+                while (x != -1) {
+                    if ((count + x) > limit) {
+                        x = limit - count;
+                    }
+                    out.append(bytes, 0, (int)x);
+                    count += x;
+
+                    if (count >= limit) {
+                        x = -1;
+                    } else {
+                        x = fin.read(bytes);
+                    }
+                }
+            }
+        }
+    }
+
+    public void writeCacheTo(StringBuilder out) throws IOException {
+        flush();
+        if (inmem) {
+            if (currentStream instanceof LoadingCharArrayWriter) {
+                LoadingCharArrayWriter lcaw = (LoadingCharArrayWriter)currentStream;
+                out.append(lcaw.rawCharArray(), 0, lcaw.size());
+            } else {
+                throw new IOException("Unknown format of currentStream");
+            }
+        } else {
+            // read the file
+            try (Reader r = createInputStreamReader(tempFile)) {
+                char[] chars = new char[1024];
+                int x = r.read(chars);
+                while (x != -1) {
+                    out.append(chars, 0, x);
+                    x = r.read(chars);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * @return the underlying output stream
+     */
+    public Writer getOut() {
+        return currentStream;
+    }
+
+    public int hashCode() {
+        return currentStream.hashCode();
+    }
+
+    public String toString() {
+        StringBuilder builder = new StringBuilder().append('[')
+            .append(CachedWriter.class.getName())
+            .append(" Content: ");
+        try {
+            writeCacheTo(builder);
+        } catch (IOException e) {
+            //ignore
+        }
+        return builder.append(']').toString();
+    }
+
+    protected void onWrite() throws IOException {
+
+    }
+
+    private void enforceLimits() throws IOException {
+        if (maxSize > 0 && totalLength > maxSize) {
+            throw new CacheSizeExceededException();
+        }
+        if (inmem && totalLength > threshold && currentStream instanceof LoadingCharArrayWriter) {
+            createFileOutputStream();
+        }
+    }
+
+
+    public void write(char[] cbuf, int off, int len) throws IOException {
+        if (!outputLocked) {
+            onWrite();
+            this.totalLength += len;
+            enforceLimits();
+            currentStream.write(cbuf, off, len);
+        }
+    }
+
+    private void createFileOutputStream() throws IOException {
+        if (tempFileFailed) {
+            return;
+        }
+        LoadingCharArrayWriter bout = (LoadingCharArrayWriter)currentStream;
+        try {
+            if (outputDir == null) {
+                tempFile = FileUtils.createTempFile("cos", "tmp");
+            } else {
+                tempFile = FileUtils.createTempFile("cos", "tmp", outputDir, false);
+            }
+            currentStream = createOutputStreamWriter(tempFile);
+            bout.writeTo(currentStream);
+            inmem = false;
+            streamList.add(currentStream);
+        } catch (Exception ex) {
+            //Could be IOException or SecurityException or other issues.
+            //Don't care what, just keep it in memory.
+            tempFileFailed = true;
+            if (currentStream != bout) {
+                currentStream.close();
+            }
+            deleteTempFile();
+            inmem = true;
+            currentStream = bout;
+        }
+    }
+
+    public File getTempFile() {
+        return tempFile != null && tempFile.exists() ? tempFile : null;
+    }
+
+    public Reader getReader() throws IOException {
+        flush();
+        if (inmem) {
+            if (currentStream instanceof LoadingCharArrayWriter) {
+                LoadingCharArrayWriter lcaw = (LoadingCharArrayWriter)currentStream;
+                return new CharArrayReader(lcaw.rawCharArray(), 0, lcaw.size());
+            }
+            return null;
+        }
+        try {
+            InputStream fileInputStream = new FileInputStream(tempFile) {
+                boolean closed;
+                
+                @Override
+                public void close() throws IOException {
+                    if (!closed) {
+                        super.close();
+                        maybeDeleteTempFile(this);
+                    }
+                    closed = true;
+                }
+            };
+            streamList.add(fileInputStream);
+            if (cipherTransformation != null) {
+                fileInputStream = new CipherInputStream(fileInputStream, ciphers.getDecryptor()) {
+                    boolean closed;
+                    
+                    @Override
+                    public void close() throws IOException {
+                        if (!closed) {
+                            super.close();
+                            closed = true;
+                        }
+                    }
+                };
+            }
+            return new InputStreamReader(fileInputStream, StandardCharsets.UTF_8);
+        } catch (FileNotFoundException e) {
+            throw new IOException("Cached file was deleted, " + e.toString());
+        }
+    }
+
+    private synchronized void deleteTempFile() {
+        if (tempFile != null) {
+            File file = tempFile;
+            tempFile = null;
+            FileUtils.delete(file);
+        }
+    }
+    private void maybeDeleteTempFile(Object stream) {
+        streamList.remove(stream);
+        if (!inmem && tempFile != null && streamList.isEmpty() && allowDeleteOfFile) {
+            if (currentStream != null) {
+                try {
+                    currentStream.close();
+                    postClose();
+                } catch (Exception e) {
+                    //ignore
+                }
+            }
+            deleteTempFile();
+            currentStream = new LoadingCharArrayWriter();
+            inmem = true;
+        }
+    }
+
+    public void setOutputDir(File outputDir) throws IOException {
+        this.outputDir = outputDir;
+    }
+    public void setThreshold(long threshold) {
+        this.threshold = threshold;
+    }
+
+    public void setMaxSize(long maxSize) {
+        this.maxSize = maxSize;
+    }
+
+    public void setCipherTransformation(String cipherTransformation) {
+        this.cipherTransformation = cipherTransformation;
+    }
+
+    public static void setDefaultMaxSize(long l) {
+        if (l == -1) {
+            String s = System.getProperty(CachedConstants.MAX_SIZE_SYS_PROP);
+            if (s == null) {
+                // lookup the deprecated property
+                s = System.getProperty("org.apache.cxf.io.CachedWriter.MaxSize", "-1");
+            }
+            l = Long.parseLong(s);
+        }
+        defaultMaxSize = l;
+    }
+
+    public static void setDefaultThreshold(int i) {
+        if (i == -1) {
+            i = SystemPropertyAction.getInteger(CachedConstants.THRESHOLD_SYS_PROP, -1);
+            if (i == -1) {
+                // lookup the deprecated property
+                i = SystemPropertyAction.getInteger("org.apache.cxf.io.CachedWriter.Threshold", -1);
+            }
+            if (i <= 0) {
+                i = 64 * 1024;
+            }
+        }
+        defaultThreshold = i;
+
+    }
+
+    public static void setDefaultCipherTransformation(String n) {
+        if (n == null) {
+            n = SystemPropertyAction.getPropertyOrNull(CachedConstants.CIPHER_TRANSFORMATION_SYS_PROP);
+        }
+        defaultCipherTransformation = n;
+    }
+
+    private OutputStreamWriter createOutputStreamWriter(File file) throws IOException {
+        OutputStream out = new BufferedOutputStream(Files.newOutputStream(file.toPath()));
+        if (cipherTransformation != null) {
+            try {
+                if (ciphers == null) {
+                    ciphers = new CipherPair(cipherTransformation);
+                }
+            } catch (GeneralSecurityException e) {
+                out.close();
+                throw new IOException(e.getMessage(), e);
+            }
+            out = new CipherOutputStream(out, ciphers.getEncryptor()) {
+                
+                @Override
+                public void close() throws IOException {
+                    if (!cosClosed) {
+                        super.close();
+                        cosClosed = true;
+                    }
+                }
+            };
+        }
+        return new OutputStreamWriter(out, UTF_8) {
+            
+            @Override
+            public void close() throws IOException {
+                if (!cosClosed) {
+                    super.close();
+                    cosClosed = true;
+                }
+            }
+        };
+    }
+
+    private InputStreamReader createInputStreamReader(File file) throws IOException {
+        InputStream in = Files.newInputStream(file.toPath());
+        if (cipherTransformation != null) {
+            in = new CipherInputStream(in, ciphers.getDecryptor()) {
+                boolean closed;
+                
+                @Override
+                public void close() throws IOException {
+                    if (!closed) {
+                        super.close();
+                        closed = true;
+                    }
+                }
+            };
+        }
+        return new InputStreamReader(in, UTF_8);
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/io/ReaderInputStream.java b/transform/src/patch/java/org/apache/cxf/io/ReaderInputStream.java
new file mode 100644
index 0000000..e95ed88
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/io/ReaderInputStream.java
@@ -0,0 +1,294 @@
+/**
+ * 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.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+
+/**
+ * {@link InputStream} implementation that reads a character stream from a {@link Reader}
+ * and transforms it to a byte stream using a specified charset encoding. The stream
+ * is transformed using a {@link CharsetEncoder} object, guaranteeing that all charset
+ * encodings supported by the JRE are handled correctly. In particular for charsets such as
+ * UTF-16, the implementation ensures that one and only one byte order marker
+ * is produced.
+ * <p>
+ * Since in general it is not possible to predict the number of characters to be read from the
+ * {@link Reader} to satisfy a read request on the {@link ReaderInputStream}, all reads from
+ * the {@link Reader} are buffered. There is therefore no well defined correlation
+ * between the current position of the {@link Reader} and that of the {@link ReaderInputStream}.
+ * This also implies that in general there is no need to wrap the underlying {@link Reader}
+ * in a {@link java.io.BufferedReader}.
+ * <p>
+ * {@link ReaderInputStream} implements the inverse transformation of {@link java.io.InputStreamReader};
+ * in the following example, reading from <tt>in2</tt> would return the same byte
+ * sequence as reading from <tt>in</tt> (provided that the initial byte sequence is legal
+ * with respect to the charset encoding):
+ * <pre>
+ * InputStream in = ...
+ * Charset cs = ...
+ * InputStreamReader reader = new InputStreamReader(in, cs);
+ * ReaderInputStream in2 = new ReaderInputStream(reader, cs);</pre>
+ * {@link ReaderInputStream} implements the same transformation as {@link java.io.OutputStreamWriter},
+ * except that the control flow is reversed: both classes transform a character stream
+ * into a byte stream, but {@link java.io.OutputStreamWriter} pushes data to the underlying stream,
+ * while {@link ReaderInputStream} pulls it from the underlying stream.
+ * <p>
+ * Note that while there are use cases where there is no alternative to using
+ * this class, very often the need to use this class is an indication of a flaw
+ * in the design of the code. This class is typically used in situations where an existing
+ * API only accepts an {@link InputStream}, but where the most natural way to produce the data
+ * is as a character stream, i.e. by providing a {@link Reader} instance. An example of a situation
+ * where this problem may appear is when implementing the {@link javax.activation.DataSource}
+ * interface from the Java Activation Framework.
+ * <p>
+ * Given the fact that the {@link Reader} class doesn't provide any way to predict whether the next
+ * read operation will block or not, it is not possible to provide a meaningful
+ * implementation of the {@link InputStream#available()} method. A call to this method
+ * will always return 0. Also, this class doesn't support {@link InputStream#mark(int)}.
+ * <p>
+ * Instances of {@link ReaderInputStream} are not thread safe.
+ *
+ * @since 2.0
+ */
+public class ReaderInputStream extends InputStream {
+    private static final int DEFAULT_BUFFER_SIZE = 1024;
+
+    private final Reader reader;
+    private final CharsetEncoder encoder;
+
+    /**
+     * CharBuffer used as input for the decoder. It should be reasonably
+     * large as we read data from the underlying Reader into this buffer.
+     */
+    private final CharBuffer encoderIn;
+
+    /**
+     * ByteBuffer used as output for the decoder. This buffer can be small
+     * as it is only used to transfer data from the decoder to the
+     * buffer provided by the caller.
+     */
+    private final ByteBuffer encoderOut;
+
+    private CoderResult lastCoderResult;
+    private boolean endOfInput;
+
+    /**
+     * Construct a new {@link ReaderInputStream}.
+     *
+     * @param reader the target {@link Reader}
+     * @param encoder the charset encoder
+     * @since 2.1
+     */
+    public ReaderInputStream(Reader reader, CharsetEncoder encoder) {
+        this(reader, encoder, DEFAULT_BUFFER_SIZE);
+    }
+
+    /**
+     * Construct a new {@link ReaderInputStream}.
+     *
+     * @param reader the target {@link Reader}
+     * @param encoder the charset encoder
+     * @param bufferSize the size of the input buffer in number of characters
+     * @since 2.1
+     */
+    public ReaderInputStream(Reader reader, CharsetEncoder encoder, int bufferSize) {
+        this.reader = reader;
+        this.encoder = encoder;
+        this.encoderIn = CharBuffer.allocate(bufferSize);
+        encoderIn.flip();
+        this.encoderOut = ByteBuffer.allocate(128);
+        encoderOut.flip();
+    }
+
+    /**
+     * Construct a new {@link ReaderInputStream}.
+     *
+     * @param reader the target {@link Reader}
+     * @param charset the charset encoding
+     * @param bufferSize the size of the input buffer in number of characters
+     */
+    public ReaderInputStream(Reader reader, Charset charset, int bufferSize) {
+        this(reader,
+             charset.newEncoder()
+                    .onMalformedInput(CodingErrorAction.REPLACE)
+                    .onUnmappableCharacter(CodingErrorAction.REPLACE),
+             bufferSize);
+    }
+
+    /**
+     * Construct a new {@link ReaderInputStream} with a default input buffer size of
+     * 1024 characters.
+     *
+     * @param reader the target {@link Reader}
+     * @param charset the charset encoding
+     */
+    public ReaderInputStream(Reader reader, Charset charset) {
+        this(reader, charset, DEFAULT_BUFFER_SIZE);
+    }
+
+    /**
+     * Construct a new {@link ReaderInputStream}.
+     *
+     * @param reader the target {@link Reader}
+     * @param charsetName the name of the charset encoding
+     * @param bufferSize the size of the input buffer in number of characters
+     */
+    public ReaderInputStream(Reader reader, String charsetName, int bufferSize) {
+        this(reader, Charset.forName(charsetName), bufferSize);
+    }
+
+    /**
+     * Construct a new {@link ReaderInputStream} with a default input buffer size of
+     * 1024 characters.
+     *
+     * @param reader the target {@link Reader}
+     * @param charsetName the name of the charset encoding
+     */
+    public ReaderInputStream(Reader reader, String charsetName) {
+        this(reader, charsetName, DEFAULT_BUFFER_SIZE);
+    }
+
+    /**
+     * Construct a new {@link ReaderInputStream} that uses the default character encoding
+     * with a default input buffer size of 1024 characters.
+     *
+     * @param reader the target {@link Reader}
+     */
+    public ReaderInputStream(Reader reader) {
+        this(reader, Charset.defaultCharset());
+    }
+
+    /**
+     * Fills the internal char buffer from the reader.
+     *
+     * @throws IOException
+     *             If an I/O error occurs
+     */
+    private void fillBuffer() throws IOException {
+        if (!endOfInput && (lastCoderResult == null || lastCoderResult.isUnderflow())) {
+            encoderIn.compact();
+            int position = encoderIn.position();
+            // We don't use Reader#read(CharBuffer) here because it is more efficient
+            // to write directly to the underlying char array (the default implementation
+            // copies data to a temporary char array).
+            int c = reader.read(encoderIn.array(), position, encoderIn.remaining());
+            if (c == -1) {
+                endOfInput = true;
+            } else {
+                encoderIn.position(position + c);
+            }
+            encoderIn.flip();
+        }
+        encoderOut.compact();
+        lastCoderResult = encoder.encode(encoderIn, encoderOut, endOfInput);
+        encoderOut.flip();
+    }
+
+    /**
+     * Read the specified number of bytes into an array.
+     *
+     * @param b the byte array to read into
+     * @param off the offset to start reading bytes into
+     * @param len the number of bytes to read
+     * @return the number of bytes read or <code>-1</code>
+     *         if the end of the stream has been reached
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        if (b == null) {
+            throw new NullPointerException("Byte array must not be null");
+        }
+        if (len < 0 || off < 0 || (off + len) > b.length) {
+            throw new IndexOutOfBoundsException("Array Size=" + b.length
+                    + ", offset=" + off + ", length=" + len);
+        }
+        int read = 0;
+        if (len == 0) {
+            return 0; // Always return 0 if len == 0
+        }
+        while (len > 0) {
+            if (encoderOut.hasRemaining()) {
+                int c = Math.min(encoderOut.remaining(), len);
+                encoderOut.get(b, off, c);
+                off += c;
+                len -= c;
+                read += c;
+            } else {
+                fillBuffer();
+                if (endOfInput && !encoderOut.hasRemaining()) {
+                    break;
+                }
+            }
+        }
+        return read == 0 && endOfInput ? -1 : read;
+    }
+
+    /**
+     * Read the specified number of bytes into an array.
+     *
+     * @param b the byte array to read into
+     * @return the number of bytes read or <code>-1</code>
+     *         if the end of the stream has been reached
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    public int read(byte[] b) throws IOException {
+        return read(b, 0, b.length);
+    }
+
+    /**
+     * Read a single byte.
+     *
+     * @return either the byte read or <code>-1</code> if the end of the stream
+     *         has been reached
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    public int read() throws IOException {
+        for (;;) {
+            if (encoderOut.hasRemaining()) {
+                return encoderOut.get() & 0xFF;
+            } else {
+                fillBuffer();
+                if (endOfInput && !encoderOut.hasRemaining()) {
+                    return -1;
+                }
+            }
+        }
+    }
+
+    /**
+     * Close the stream. This method will cause the underlying {@link Reader}
+     * to be closed.
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    public void close() throws IOException {
+        reader.close();
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/io/WriteOnCloseOutputStream.java b/transform/src/patch/java/org/apache/cxf/io/WriteOnCloseOutputStream.java
new file mode 100644
index 0000000..17f4579
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/io/WriteOnCloseOutputStream.java
@@ -0,0 +1,46 @@
+/**
+ * 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.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This outputstream implementation will cache the message until close()
+ * is called, at which point it will write the message to the OutputStream
+ * supplied via the constructor.
+ */
+public class WriteOnCloseOutputStream extends CachedOutputStream {
+
+    OutputStream flowThroughStream;
+
+    public WriteOnCloseOutputStream(OutputStream stream) {
+        super();
+        flowThroughStream = stream;
+    }
+
+
+    @Override
+    protected void doClose() throws IOException {
+        resetOut(flowThroughStream, true);
+        flowThroughStream.flush();
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/JAXRSInvoker.java b/transform/src/patch/java/org/apache/cxf/jaxrs/JAXRSInvoker.java
new file mode 100644
index 0000000..163ff78
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/JAXRSInvoker.java
@@ -0,0 +1,460 @@
+/**
+ * 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;
+
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Collections;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.CompletionStage;
+import java.util.logging.Logger;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+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.ClassHelper;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.interceptor.InterceptorChain.State;
+import org.apache.cxf.jaxrs.impl.AsyncResponseImpl;
+import org.apache.cxf.jaxrs.impl.MetadataMap;
+import org.apache.cxf.jaxrs.impl.ResourceContextImpl;
+import org.apache.cxf.jaxrs.lifecycle.ResourceProvider;
+import org.apache.cxf.jaxrs.model.ClassResourceInfo;
+import org.apache.cxf.jaxrs.model.OperationResourceInfo;
+import org.apache.cxf.jaxrs.model.ProviderInfo;
+import org.apache.cxf.jaxrs.model.URITemplate;
+import org.apache.cxf.jaxrs.provider.ServerProviderFactory;
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
+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;
+import org.apache.cxf.message.MessageContentsList;
+import org.apache.cxf.service.invoker.AbstractInvoker;
+
+public class JAXRSInvoker extends AbstractInvoker {
+    private static final Logger LOG = LogUtils.getL7dLogger(JAXRSInvoker.class);
+    private static final ResourceBundle BUNDLE = BundleUtils.getBundle(JAXRSInvoker.class);
+    private static final String SERVICE_LOADER_AS_CONTEXT = "org.apache.cxf.serviceloader-context";
+    private static final String SERVICE_OBJECT_SCOPE = "org.apache.cxf.service.scope";
+    private static final String REQUEST_SCOPE = "request";
+    private static final String LAST_SERVICE_OBJECT = "org.apache.cxf.service.object.last";
+    private static final String PROXY_INVOCATION_ERROR_FRAGMENT
+        = "object is not an instance of declaring class";
+
+    public JAXRSInvoker() {
+    }
+
+    public Object invoke(Exchange exchange, Object request) {
+        MessageContentsList responseList = checkExchangeForResponse(exchange);
+        if (responseList != null) {
+            return responseList;
+        }
+        AsyncResponse asyncResp = exchange.get(AsyncResponse.class);
+        if (asyncResp != null) {
+            AsyncResponseImpl asyncImpl = (AsyncResponseImpl)asyncResp;
+            asyncImpl.prepareContinuation();
+            try {
+                asyncImpl.handleTimeout();
+                return handleAsyncResponse(exchange, asyncImpl);
+            } catch (Throwable t) {
+                return handleAsyncFault(exchange, asyncImpl, t);
+            }
+        }
+
+        ResourceProvider provider = getResourceProvider(exchange);
+        Object rootInstance = null;
+        Message inMessage = exchange.getInMessage();
+        try {
+            rootInstance = getServiceObject(exchange);
+            Object serviceObject = getActualServiceObject(exchange, rootInstance);
+
+            return invoke(exchange, request, serviceObject);
+        } catch (WebApplicationException ex) {
+            responseList = checkExchangeForResponse(exchange);
+            if (responseList != null) {
+                return responseList;
+            }
+            return handleFault(ex, inMessage);
+        } finally {
+            boolean suspended = isSuspended(exchange);
+            if (suspended || exchange.isOneWay() || inMessage.get(Message.THREAD_CONTEXT_SWITCHED) != null) {
+                ServerProviderFactory.clearThreadLocalProxies(inMessage);
+            }
+            if (suspended || isServiceObjectRequestScope(inMessage)) {
+                persistRoots(exchange, rootInstance, provider);
+            } else {
+                provider.releaseInstance(inMessage, rootInstance);
+            }
+        }
+    }
+
+    private boolean isSuspended(Exchange exchange) {
+        return exchange.getInMessage().getInterceptorChain().getState() == State.SUSPENDED;
+    }
+
+    private Object handleAsyncResponse(Exchange exchange, AsyncResponseImpl ar) {
+        Object asyncObj = ar.getResponseObject();
+        if (asyncObj instanceof Throwable) {
+            final Throwable throwable = (Throwable)asyncObj;
+            Throwable cause = throwable;
+
+            if (throwable instanceof CompletionException) {
+                cause = throwable.getCause();
+            }
+
+            return handleAsyncFault(exchange, ar, (cause != null) ? cause : throwable);
+        }
+        setResponseContentTypeIfNeeded(exchange.getInMessage(), asyncObj);
+        return new MessageContentsList(asyncObj);
+    }
+
+    private Object handleAsyncFault(Exchange exchange, AsyncResponseImpl ar, Throwable t) {
+        try {
+            return handleFault(new Fault(t), exchange.getInMessage(), null, null);
+        } catch (Fault ex) {
+            ar.setUnmappedThrowable(ex.getCause() == null ? ex : ex.getCause());
+            if (isSuspended(exchange)) {
+                ar.reset();
+                exchange.getInMessage().getInterceptorChain().unpause();
+            }
+            return new MessageContentsList(Response.serverError().build());
+        }
+    }
+
+    private void persistRoots(Exchange exchange, Object rootInstance, Object provider) {
+        exchange.put(JAXRSUtils.ROOT_INSTANCE, rootInstance);
+        exchange.put(JAXRSUtils.ROOT_PROVIDER, provider);
+    }
+
+    @SuppressWarnings("unchecked")
+    public Object invoke(Exchange exchange, Object request, Object resourceObject) {
+
+        final OperationResourceInfo ori = exchange.get(OperationResourceInfo.class);
+        final ClassResourceInfo cri = ori.getClassResourceInfo();
+        final Message inMessage = exchange.getInMessage();
+        final ServerProviderFactory providerFactory = ServerProviderFactory.getInstance(inMessage);
+        cri.injectContexts(resourceObject, ori, inMessage);
+
+        if (cri.isRoot()) {
+            ProviderInfo<Application> appProvider = providerFactory.getApplicationProvider();
+            if (appProvider != null) {
+                InjectionUtils.injectContexts(appProvider.getProvider(),
+                                              appProvider,
+                                              inMessage);
+            }
+        }
+
+
+        Method methodToInvoke = getMethodToInvoke(cri, ori, resourceObject);
+
+        List<Object> params = null;
+        if (request instanceof List) {
+            params = CastUtils.cast((List<?>)request);
+        } else if (request != null) {
+            params = new MessageContentsList(request);
+        }
+
+        Object result = null;
+        ClassLoaderHolder contextLoader = null;
+        AsyncResponseImpl asyncResponse = null;
+        try {
+            if (setServiceLoaderAsContextLoader(inMessage)) {
+                contextLoader = ClassLoaderUtils
+                    .setThreadContextClassloader(resourceObject.getClass().getClassLoader());
+            }
+            if (!ori.isSubResourceLocator()) {
+                asyncResponse = (AsyncResponseImpl)inMessage.get(AsyncResponse.class);
+            }
+            result = invoke(exchange, resourceObject, methodToInvoke, params);
+            if (asyncResponse == null && !ori.isSubResourceLocator()) {
+                asyncResponse = checkFutureResponse(inMessage, checkResultObject(result));
+            }
+            if (asyncResponse != null) {
+                if (!asyncResponse.suspendContinuationIfNeeded()) {
+                    result = handleAsyncResponse(exchange, asyncResponse);
+                } else {
+                    providerFactory.clearThreadLocalProxies();
+                }
+            }
+        } catch (Fault ex) {
+            Object faultResponse;
+            if (asyncResponse != null) {
+                faultResponse = handleAsyncFault(exchange, asyncResponse,
+                                                 ex.getCause() == null ? ex : ex.getCause());
+            } else {
+                faultResponse = handleFault(ex, inMessage, cri, methodToInvoke);
+            }
+            return faultResponse;
+        } finally {
+            exchange.put(LAST_SERVICE_OBJECT, resourceObject);
+            if (contextLoader != null) {
+                contextLoader.reset();
+            }
+        }
+        ClassResourceInfo subCri = null;
+        if (ori.isSubResourceLocator()) {
+            try {
+                MultivaluedMap<String, String> values = getTemplateValues(inMessage);
+                String subResourcePath = values.getFirst(URITemplate.FINAL_MATCH_GROUP);
+                String httpMethod = (String)inMessage.get(Message.HTTP_REQUEST_METHOD);
+                String contentType = (String)inMessage.get(Message.CONTENT_TYPE);
+                if (contentType == null) {
+                    contentType = "*/*";
+                }
+                List<MediaType> acceptContentType =
+                    (List<MediaType>)exchange.get(Message.ACCEPT_CONTENT_TYPE);
+
+                result = checkSubResultObject(result, subResourcePath);
+
+                final Class<?> subResponseType;
+                if (result.getClass() == Class.class) {
+                    ResourceContext rc = new ResourceContextImpl(inMessage, ori);
+                    result = rc.getResource((Class<?>)result);
+                    subResponseType = InjectionUtils.getActualType(methodToInvoke.getGenericReturnType());
+                } else {
+                    subResponseType = methodToInvoke.getReturnType();
+                }
+                
+                subCri = cri.getSubResource(subResponseType,
+                    ClassHelper.getRealClass(exchange.getBus(), result), result);
+                if (subCri == null) {
+                    org.apache.cxf.common.i18n.Message errorM =
+                        new org.apache.cxf.common.i18n.Message("NO_SUBRESOURCE_FOUND",
+                                                               BUNDLE,
+                                                               subResourcePath);
+                    LOG.severe(errorM.toString());
+                    throw ExceptionUtils.toNotFoundException(null, null);
+                }
+
+                OperationResourceInfo subOri = JAXRSUtils.findTargetMethod(
+                                                         Collections.singletonMap(subCri, values),
+                                                         inMessage,
+                                                         httpMethod,
+                                                         values,
+                                                         contentType,
+                                                         acceptContentType);
+                exchange.put(OperationResourceInfo.class, subOri);
+                inMessage.put(URITemplate.TEMPLATE_PARAMETERS, values);
+                inMessage.put(URITemplate.URI_TEMPLATE, JAXRSUtils.getUriTemplate(inMessage, subCri, ori, subOri));
+
+                if (!subOri.isSubResourceLocator()
+                    && JAXRSUtils.runContainerRequestFilters(providerFactory,
+                                                             inMessage,
+                                                             false,
+                                                             subOri.getNameBindings())) {
+                    return new MessageContentsList(exchange.get(Response.class));
+                }
+
+                // work out request parameters for the sub-resource class. Here we
+                // presume InputStream has not been consumed yet by the root resource class.
+                List<Object> newParams = JAXRSUtils.processParameters(subOri, values, inMessage);
+                inMessage.setContent(List.class, newParams);
+
+                return this.invoke(exchange, newParams, result);
+            } catch (IOException ex) {
+                Response resp = JAXRSUtils.convertFaultToResponse(ex, inMessage);
+                if (resp == null) {
+                    resp = JAXRSUtils.convertFaultToResponse(ex, inMessage);
+                }
+                return new MessageContentsList(resp);
+            } catch (WebApplicationException ex) {
+                Response excResponse;
+                if (JAXRSUtils.noResourceMethodForOptions(ex.getResponse(),
+                        (String)inMessage.get(Message.HTTP_REQUEST_METHOD))) {
+                    excResponse = JAXRSUtils.createResponse(Collections.singletonList(subCri),
+                                                            null, null, 200, true);
+                } else {
+                    excResponse = JAXRSUtils.convertFaultToResponse(ex, inMessage);
+                }
+                return new MessageContentsList(excResponse);
+            }
+        }
+        setResponseContentTypeIfNeeded(inMessage, result);
+        return result;
+    }
+
+    protected AsyncResponseImpl checkFutureResponse(Message inMessage, Object result) {
+        if (result instanceof CompletionStage) {
+            final CompletionStage<?> stage = (CompletionStage<?>)result;
+            final AsyncResponseImpl asyncResponse = new AsyncResponseImpl(inMessage);
+            stage.whenComplete((v, t) -> {
+                if (t instanceof CancellationException) {
+                    asyncResponse.cancel();
+                } else {
+                    asyncResponse.resume(v != null ? v : t);
+                }
+            });
+            return asyncResponse;
+        }
+        return null;
+    }
+
+    protected Method getMethodToInvoke(ClassResourceInfo cri, OperationResourceInfo ori, Object resourceObject) {
+        Method resourceMethod = cri.getMethodDispatcher().getMethod(ori);
+
+        Method methodToInvoke;
+        if (Proxy.class.isInstance(resourceObject)) {
+            methodToInvoke = cri.getMethodDispatcher().getProxyMethod(resourceMethod);
+            if (methodToInvoke == null) {
+                methodToInvoke = InjectionUtils.checkProxy(resourceMethod, resourceObject);
+                cri.getMethodDispatcher().addProxyMethod(resourceMethod, methodToInvoke);
+            }
+        } else {
+            methodToInvoke = resourceMethod;
+        }
+        return methodToInvoke;
+    }
+
+    private MessageContentsList checkExchangeForResponse(Exchange exchange) {
+        Response r = exchange.get(Response.class);
+        if (r != null) {
+            JAXRSUtils.setMessageContentType(exchange.getInMessage(), r);
+            return new MessageContentsList(r);
+        }
+        return null;
+    }
+
+    private void setResponseContentTypeIfNeeded(Message inMessage, Object response) {
+        if (response instanceof Response) {
+            JAXRSUtils.setMessageContentType(inMessage, (Response)response);
+        }
+    }
+    private Object handleFault(Throwable ex, Message inMessage) {
+        return handleFault(new Fault(ex), inMessage, null, null);
+    }
+    private Object handleFault(Fault ex, Message inMessage,
+                               ClassResourceInfo cri, Method methodToInvoke) {
+        String errorMessage = ex.getMessage();
+        if (errorMessage != null && cri != null
+            && errorMessage.contains(PROXY_INVOCATION_ERROR_FRAGMENT)) {
+            org.apache.cxf.common.i18n.Message errorM =
+                new org.apache.cxf.common.i18n.Message("PROXY_INVOCATION_FAILURE",
+                                                       BUNDLE,
+                                                       methodToInvoke,
+                                                       cri.getServiceClass().getName());
+            LOG.severe(errorM.toString());
+        }
+        Response excResponse =
+            JAXRSUtils.convertFaultToResponse(ex.getCause() == null ? ex : ex.getCause(), inMessage);
+        if (excResponse == null) {
+            inMessage.getExchange().put(Message.PROPOGATE_EXCEPTION,
+                                        ExceptionUtils.propogateException(inMessage));
+            throw ex;
+        }
+        return new MessageContentsList(excResponse);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected MultivaluedMap<String, String> getTemplateValues(Message msg) {
+        MultivaluedMap<String, String> values = new MetadataMap<>();
+        MultivaluedMap<String, String> oldValues =
+            (MultivaluedMap<String, String>)msg.get(URITemplate.TEMPLATE_PARAMETERS);
+        if (oldValues != null) {
+            values.putAll(oldValues);
+        }
+        return values;
+    }
+
+    private boolean setServiceLoaderAsContextLoader(Message inMessage) {
+        Object en = inMessage.getContextualProperty(SERVICE_LOADER_AS_CONTEXT);
+        return Boolean.TRUE.equals(en) || "true".equals(en);
+    }
+
+    private boolean isServiceObjectRequestScope(Message inMessage) {
+        Object scope = inMessage.getContextualProperty(SERVICE_OBJECT_SCOPE);
+        return REQUEST_SCOPE.equals(scope);
+    }
+
+    private ResourceProvider getResourceProvider(Exchange exchange) {
+        Object provider = exchange.remove(JAXRSUtils.ROOT_PROVIDER);
+        if (provider == null) {
+            OperationResourceInfo ori = exchange.get(OperationResourceInfo.class);
+            ClassResourceInfo cri = ori.getClassResourceInfo();
+            return cri.getResourceProvider();
+        }
+        return (ResourceProvider)provider;
+    }
+
+    public Object getServiceObject(Exchange exchange) {
+
+        Object root = exchange.remove(JAXRSUtils.ROOT_INSTANCE);
+        if (root != null) {
+            return root;
+        }
+
+        OperationResourceInfo ori = exchange.get(OperationResourceInfo.class);
+        ClassResourceInfo cri = ori.getClassResourceInfo();
+
+        return cri.getResourceProvider().getInstance(exchange.getInMessage());
+    }
+
+    protected Object getActualServiceObject(Exchange exchange, Object rootInstance) {
+
+        Object last = exchange.get(LAST_SERVICE_OBJECT);
+        return last !=  null ? last : rootInstance;
+    }
+
+
+
+    private static Object checkResultObject(Object result) {
+
+        if (result != null) {
+            if (result instanceof MessageContentsList) {
+                result = ((MessageContentsList)result).get(0);
+            } else if (result instanceof List) {
+                result = ((List<?>)result).get(0);
+            } else if (result.getClass().isArray()) {
+                result = ((Object[])result)[0];
+            }
+        }
+        return result;
+    }
+    private static Object checkSubResultObject(Object result, String subResourcePath) {
+        result = checkResultObject(result);
+        if (result == null) {
+            org.apache.cxf.common.i18n.Message errorM =
+                new org.apache.cxf.common.i18n.Message("NULL_SUBRESOURCE",
+                                                       BUNDLE,
+                                                       subResourcePath);
+            LOG.info(errorM.toString());
+            throw ExceptionUtils.toNotFoundException(null, null);
+        }
+
+        return result;
+    }
+
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/JAXRSServerFactoryBean.java b/transform/src/patch/java/org/apache/cxf/jaxrs/JAXRSServerFactoryBean.java
new file mode 100644
index 0000000..7871226
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/JAXRSServerFactoryBean.java
@@ -0,0 +1,467 @@
+/**
+ * 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;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+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 javax.ws.rs.core.Application;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.classloader.ClassLoaderUtils.ClassLoaderHolder;
+import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.endpoint.Server;
+import org.apache.cxf.endpoint.ServerImpl;
+import org.apache.cxf.feature.Feature;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.jaxrs.ext.ResourceComparator;
+import org.apache.cxf.jaxrs.impl.RequestPreprocessor;
+import org.apache.cxf.jaxrs.lifecycle.PerRequestResourceProvider;
+import org.apache.cxf.jaxrs.lifecycle.ResourceProvider;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.jaxrs.model.ApplicationInfo;
+import org.apache.cxf.jaxrs.model.ClassResourceInfo;
+import org.apache.cxf.jaxrs.provider.ServerProviderFactory;
+import org.apache.cxf.jaxrs.utils.AnnotationUtils;
+import org.apache.cxf.jaxrs.utils.InjectionUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.service.factory.FactoryBeanListener;
+import org.apache.cxf.service.factory.ServiceConstructionException;
+import org.apache.cxf.service.invoker.Invoker;
+
+
+/**
+ * Bean to help easily create Server endpoints for JAX-RS. Example:
+ * <pre>
+ * JAXRSServerFactoryBean sf = JAXRSServerFactoryBean();
+ * sf.setResourceClasses(Book.class);
+ * sf.setBindingId(JAXRSBindingFactory.JAXRS_BINDING_ID);
+ * sf.setAddress("http://localhost:9080/");
+ * Server myServer = sf.create();
+ * </pre>
+ * This will start a server for you and register it with the ServerManager.  Note
+ * you should explicitly close the {@link org.apache.cxf.endpoint.Server} created
+ * when finished with it:
+ * <pre>
+ * myServer.close();
+ * myServer.destroy(); // closes first if close() not previously called
+ * </pre>
+ */
+public class JAXRSServerFactoryBean extends AbstractJAXRSFactoryBean {
+
+    protected Map<Class<?>, ResourceProvider> resourceProviders = new HashMap<>();
+
+    private Server server;
+    private boolean start = true;
+    private Map<Object, Object> languageMappings;
+    private Map<Object, Object> extensionMappings;
+    private ResourceComparator rc;
+    private ApplicationInfo appProvider;
+    private String documentLocation;
+
+    public JAXRSServerFactoryBean() {
+        this(new JAXRSServiceFactoryBean());
+    }
+
+    public JAXRSServerFactoryBean(JAXRSServiceFactoryBean sf) {
+        super(sf);
+    }
+
+    /**
+     * Saves the reference to the JAX-RS {@link Application}
+     * @param app
+     */
+    public void setApplication(Application app) {
+        setApplicationInfo(new ApplicationInfo(app, getBus()));
+    }
+
+    public void setApplicationInfo(ApplicationInfo provider) {
+        appProvider = provider;
+        Set<String> appNameBindings = AnnotationUtils.getNameBindings(bus, provider.getProvider().getClass());
+        for (ClassResourceInfo cri : getServiceFactory().getClassResourceInfo()) {
+            Set<String> clsNameBindings = new LinkedHashSet<>(appNameBindings);
+            clsNameBindings.addAll(AnnotationUtils.getNameBindings(bus, cri.getServiceClass()));
+            cri.setNameBindings(clsNameBindings);
+        }
+    }
+
+    /**
+     * Resource comparator which may be used to customize the way
+     * a root resource or resource method is selected
+     * @param rcomp comparator
+     */
+    public void setResourceComparator(ResourceComparator rcomp) {
+        rc = rcomp;
+    }
+
+    /**
+     * By default the subresources are resolved dynamically, mainly due to
+     * the JAX-RS specification allowing Objects being returned from the subresource
+     * locators. Setting this property to true enables the runtime to do the
+     * early resolution.
+     *
+     * @param enableStatic enabling the static resolution if set to true
+     */
+    public void setStaticSubresourceResolution(boolean enableStatic) {
+        serviceFactory.setEnableStaticResolution(enableStatic);
+    }
+
+
+    /**
+     * Creates the JAX-RS Server instance
+     * @return the server
+     */
+    public void init() {
+        if (server == null) {
+            create();
+        }
+    }
+
+    /**
+     * Creates the JAX-RS Server instance
+     * @return the server
+     */
+    public Server create() {
+        ClassLoaderHolder origLoader = null;
+        try {
+            Bus bus = getBus();
+            ClassLoader loader = bus.getExtension(ClassLoader.class);
+            if (loader != null) {
+                origLoader = ClassLoaderUtils.setThreadContextClassloader(loader);
+            }
+            serviceFactory.setBus(bus);
+            checkResources(true);
+            if (serviceFactory.getService() == null) {
+                serviceFactory.create();
+            }
+
+            Endpoint ep = createEndpoint();
+
+            getServiceFactory().sendEvent(FactoryBeanListener.Event.PRE_SERVER_CREATE,
+                                          server);
+
+            server = new ServerImpl(getBus(),
+                                    ep,
+                                    getDestinationFactory(),
+                                    getBindingFactory());
+
+            Invoker invoker = serviceFactory.getInvoker();
+            if (invoker == null) {
+                ep.getService().setInvoker(createInvoker());
+            } else {
+                ep.getService().setInvoker(invoker);
+            }
+
+            ServerProviderFactory factory = setupFactory(ep);
+            
+            ep.put(Application.class.getName(), appProvider);
+            factory.setRequestPreprocessor(
+                new RequestPreprocessor(languageMappings, extensionMappings));
+            ep.put(Bus.class.getName(), getBus());
+            if (documentLocation != null) {
+                ep.put(JAXRSUtils.DOC_LOCATION, documentLocation);
+            }
+            if (rc != null) {
+                ep.put("org.apache.cxf.jaxrs.comparator", rc);
+            }
+            checkPrivateEndpoint(ep);
+
+            applyBusFeatures(getBus());
+            applyFeatures();
+
+            updateClassResourceProviders(ep);
+            injectContexts(factory, (ApplicationInfo)ep.get(Application.class.getName()));
+            factory.applyDynamicFeatures(getServiceFactory().getClassResourceInfo());
+            
+            
+            getServiceFactory().sendEvent(FactoryBeanListener.Event.SERVER_CREATED,
+                                          server,
+                                          null,
+                                          null);
+
+
+            if (start) {
+                try {
+                    server.start();
+                } catch (RuntimeException re) {
+                    if (!(re instanceof ServiceConstructionException 
+                        && re.getMessage().startsWith("There is an endpoint already running on"))) {
+                        //avoid destroying another server on the same endpoint url
+                        server.destroy(); // prevent resource leak if server really started by itself
+                        
+                    }
+                    throw re;
+                }
+            }
+        } catch (Exception e) {
+            throw new ServiceConstructionException(e);
+        } finally {
+            if (origLoader != null) {
+                origLoader.reset();
+            }
+        }
+
+        return server;
+    }
+
+    public Server getServer() {
+        return server;
+    }
+
+    protected ServerProviderFactory setupFactory(Endpoint ep) {
+        ServerProviderFactory factory = ServerProviderFactory.createInstance(getBus());
+        setBeanInfo(factory);
+        factory.setApplicationProvider(appProvider);
+        super.setupFactory(factory, ep);
+        return factory;
+    }
+
+    protected void setBeanInfo(ServerProviderFactory factory) {
+        List<ClassResourceInfo> cris = serviceFactory.getClassResourceInfo();
+        for (ClassResourceInfo cri : cris) {
+            cri.initBeanParamInfo(factory);
+        }
+
+    }
+
+    protected void applyBusFeatures(final Bus bus) {
+        if (bus.getFeatures() != null) {
+            for (Feature feature : bus.getFeatures()) {
+                feature.initialize(server, bus);
+            }
+        }
+    }
+
+    protected void applyFeatures() {
+        if (getFeatures() != null) {
+            for (Feature feature : getFeatures()) {
+                feature.initialize(server, getBus());
+            }
+        }
+    }
+
+    protected Invoker createInvoker() {
+        return serviceFactory.createInvoker();
+    }
+
+    /**
+     * Sets the language mappings,
+     * example, 'en' is the key and 'en-gb' is the value.
+     *
+     * @param lMaps the language mappings
+     */
+    public void setLanguageMappings(Map<Object, Object> lMaps) {
+        languageMappings = lMaps;
+    }
+
+    /**
+     * Sets the extension mappings,
+     * example, 'xml' is the key and 'text/xml' is the value.
+     *
+     * @param extMaps the extension mappings
+     */
+    public void setExtensionMappings(Map<Object, Object> extMaps) {
+        extensionMappings = extMaps;
+    }
+
+    public List<Class<?>> getResourceClasses() {
+        return serviceFactory.getResourceClasses();
+    }
+
+    /**
+     * This method is used primarily by Spring handler processing
+     * the jaxrs:server/@serviceClass attribute. It delegates to
+     * setResourceClasses method accepting the array of Class parameters.
+     * @param clazz the service/resource class
+     */
+    public void setServiceClass(Class<?> clazz) {
+        serviceFactory.setResourceClasses(clazz);
+    }
+
+    /**
+     * Sets one or more root resource classes
+     * @param classes the list of resource classes
+     */
+    public void setResourceClasses(List<Class<?>> classes) {
+        serviceFactory.setResourceClasses(classes);
+    }
+
+    /**
+     * Sets one or more root resource classes
+     * @param classes the array of resource classes
+     */
+    public void setResourceClasses(Class<?>... classes) {
+        serviceFactory.setResourceClasses(classes);
+    }
+
+    /**
+     * Sets the resource beans. If this is set then the JAX-RS runtime
+     * will not be responsible for the life-cycle of resource classes.
+     *
+     * @param beans the array of resource instances
+     */
+    public void setServiceBeanObjects(Object... beans) {
+        setServiceBeans(Arrays.asList(beans));
+    }
+
+    /**
+     * Sets the single resource bean. If this is set then the JAX-RS runtime
+     * will not be responsible for the life-cycle of resource classes.
+     * Please avoid setting the resource class of this bean explicitly,
+     * the runtime will determine it itself.
+     *
+     * @param bean resource instance
+     */
+    public void setServiceBean(Object bean) {
+        setServiceBeans(Arrays.asList(bean));
+    }
+
+    /**
+     * Sets the resource beans. If this is set then the JAX-RS runtime
+     * will not be responsible for the life-cycle of resource classes.
+     *
+     * @param beans the list of resource instances
+     */
+    public void setServiceBeans(List<Object> beans) {
+        List<Object> newBeans = new ArrayList<>();
+        addToBeans(newBeans, beans);
+        serviceFactory.setResourceClassesFromBeans(newBeans);
+    }
+
+    /**
+     * Sets the provider managing the life-cycle of the given resource class
+     * <pre>
+     * Example:
+     *  setResourceProvider(BookStoreInterface.class, new SingletonResourceProvider(new BookStore()));
+     * </pre>
+     * @param c resource class
+     * @param rp resource provider
+     */
+    public void setResourceProvider(Class<?> c, ResourceProvider rp) {
+        resourceProviders.put(c, rp);
+    }
+
+    /**
+     * Sets the provider managing the life-cycle of the resource class
+     * <pre>
+     * Example:
+     *  setResourceProvider(new SingletonResourceProvider(new BookStore()));
+     * </pre>
+     * @param rp resource provider
+     */
+    public void setResourceProvider(ResourceProvider rp) {
+        setResourceProviders(CastUtils.cast(Collections.singletonList(rp), ResourceProvider.class));
+    }
+
+    /**
+     * Sets the list of providers managing the life-cycle of the resource classes
+     *
+     * @param rps resource providers
+     */
+    public void setResourceProviders(List<ResourceProvider> rps) {
+        for (ResourceProvider rp : rps) {
+            Class<?> c = rp.getResourceClass();
+            setServiceClass(c);
+            resourceProviders.put(c, rp);
+        }
+    }
+
+    /**
+     * Sets the custom Invoker which can be used to customize the way
+     * the default JAX-RS invoker calls on the service bean
+     * @param invoker
+     */
+    public void setInvoker(Invoker invoker) {
+        serviceFactory.setInvoker(invoker);
+    }
+
+    /**
+     * Determines whether Services are automatically started during the create() call.  Default is true.
+     * If false will need to call start() method on Server to activate it.
+     * @param start Whether (true) or not (false) to start the Server during Server creation.
+     */
+    public void setStart(boolean start) {
+        this.start = start;
+    }
+
+    protected void injectContexts(ServerProviderFactory factory, ApplicationInfo fallback) {
+        // Sometimes the application provider (ApplicationInfo) is injected through
+        // the endpoint, not JAXRSServerFactoryBean (like for example OpenApiFeature
+        // or Swagger2Feature do). As such, without consulting the endpoint, the injection
+        // may not work properly.
+        final ApplicationInfo appInfoProvider = (appProvider == null) ? fallback : appProvider;
+        final Application application = appInfoProvider == null ? null : appInfoProvider.getProvider();
+
+        for (ClassResourceInfo cri : serviceFactory.getClassResourceInfo()) {
+            if (cri.isSingleton()) {
+                InjectionUtils.injectContextProxiesAndApplication(cri,
+                                                    cri.getResourceProvider().getInstance(null),
+                                                    application,
+                                                    factory);
+            }
+        }
+        if (application != null) {
+            InjectionUtils.injectContextProxiesAndApplication(appInfoProvider,
+                                                              application, null, null);
+        }
+    }
+
+    protected void updateClassResourceProviders(Endpoint ep) {
+        for (ClassResourceInfo cri : serviceFactory.getClassResourceInfo()) {
+            if (cri.getResourceProvider() == null) {
+                ResourceProvider rp = resourceProviders.get(cri.getResourceClass());
+                if (rp != null) {
+                    cri.setResourceProvider(rp);
+                } else {
+                    setDefaultResourceProvider(cri);
+                }
+            }
+            if (cri.getResourceProvider() instanceof SingletonResourceProvider) {
+                ((SingletonResourceProvider)cri.getResourceProvider()).init(ep);
+            }
+        }
+    }
+
+    protected void setDefaultResourceProvider(ClassResourceInfo cri) {
+        cri.setResourceProvider(new PerRequestResourceProvider(cri.getResourceClass()));
+    }
+
+    /**
+     * Set the reference to the document (WADL, etc) describing the endpoint
+     * @param docLocation document location
+     */
+    public void setDocLocation(String docLocation) {
+        this.documentLocation = docLocation;
+    }
+
+    /**
+     * Get the reference to the document (WADL, etc) describing the endpoint
+     * @return document location
+     */
+    public String getDocLocation() {
+        return documentLocation;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/impl/EntityTagHeaderProvider.java b/transform/src/patch/java/org/apache/cxf/jaxrs/impl/EntityTagHeaderProvider.java
new file mode 100644
index 0000000..0deb1c3
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/impl/EntityTagHeaderProvider.java
@@ -0,0 +1,77 @@
+/**
+ * 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.impl;
+
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.ext.RuntimeDelegate.HeaderDelegate;
+
+public class EntityTagHeaderProvider implements HeaderDelegate<EntityTag> {
+
+    private static final String WEAK_PREFIX = "W/";
+
+    public EntityTag fromString(String header) {
+
+
+        if (header == null) {
+            throw new IllegalArgumentException("ETag value can not be null");
+        }
+
+        if ("*".equals(header)) {
+            return new EntityTag("*");
+        }
+
+        String tag;
+        boolean weak = false;
+        int i = header.indexOf(WEAK_PREFIX);
+        if (i != -1) {
+            weak = true;
+            if (i + 2 < header.length()) {
+                tag = header.substring(i + 2);
+            } else {
+                return new EntityTag("", weak);
+            }
+        }  else {
+            tag = header;
+        }
+        if (tag.length() > 0 && !tag.startsWith("\"") && !tag.endsWith("\"")) {
+            return new EntityTag(tag, weak);
+        }
+        if (tag.length() < 2 || !tag.startsWith("\"") || !tag.endsWith("\"")) {
+            throw new IllegalArgumentException("Misformatted ETag : " + header);
+        }
+        tag = tag.length() == 2 ? "" : tag.substring(1, tag.length() - 1);
+        return new EntityTag(tag, weak);
+    }
+
+    public String toString(EntityTag tag) {
+        StringBuilder sb = new StringBuilder();
+        if (tag.isWeak()) {
+            sb.append(WEAK_PREFIX);
+        }
+        String tagValue = tag.getValue();
+        if (!tagValue.startsWith("\"")) {
+            sb.append('"').append(tagValue).append('"');
+        } else {
+            sb.append(tagValue);
+        }
+        return sb.toString();
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/impl/MediaTypeHeaderProvider.java b/transform/src/patch/java/org/apache/cxf/jaxrs/impl/MediaTypeHeaderProvider.java
new file mode 100644
index 0000000..a885a33
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/impl/MediaTypeHeaderProvider.java
@@ -0,0 +1,221 @@
+/**
+ * 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.impl;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.ext.RuntimeDelegate.HeaderDelegate;
+
+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.message.Message;
+import org.apache.cxf.message.MessageUtils;
+import org.apache.cxf.phase.PhaseInterceptorChain;
+
+public class MediaTypeHeaderProvider implements HeaderDelegate<MediaType> {
+    private static final Logger LOG = LogUtils.getL7dLogger(MediaTypeHeaderProvider.class);
+    private static final String STRICT_MEDIA_TYPE_CHECK =
+        "org.apache.cxf.jaxrs.mediaTypeCheck.strict";
+    private static final Pattern COMPLEX_PARAMETERS =
+        Pattern.compile("(([\\w-]+=\"[^\"]*\")|([\\w-]+=[\\w-/\\+]+))");
+
+    private static Map<String, MediaType> map = new ConcurrentHashMap<>();
+    private static final int MAX_MT_CACHE_SIZE =
+        SystemPropertyAction.getInteger("org.apache.cxf.jaxrs.max_mediatype_cache_size", 200);
+
+    public MediaType fromString(String mType) {
+
+        return valueOf(mType);
+    }
+
+    public static MediaType valueOf(String mType) {
+        if (mType == null) {
+            throw new IllegalArgumentException("Media type value can not be null");
+        }
+
+        MediaType result = map.get(mType);
+        if (result == null) {
+            result = internalValueOf(mType);
+            final int size = map.size();
+            if (size >= MAX_MT_CACHE_SIZE) {
+                map.clear();
+            }
+            map.put(mType, result);
+        }
+        return result;
+    }
+
+    public static MediaType internalValueOf(String mType) {
+
+        int i = mType.indexOf('/');
+        if (i == -1) {
+            return handleMediaTypeWithoutSubtype(mType.trim());
+        } else if (i == 0) {
+            throw new IllegalArgumentException("Invalid media type string: " + mType);
+        }
+
+        int paramsStart = mType.indexOf(';', i + 1);
+        int end = paramsStart == -1  ? mType.length() : paramsStart;
+
+        String type = mType.substring(0, i).trim();
+        String subtype = mType.substring(i + 1, end).trim();
+        if (!isValid(type) || !isValid(subtype)) {
+            throw new IllegalArgumentException("Invalid media type string: " + mType);
+        }
+
+        Map<String, String> parameters = Collections.emptyMap();
+        if (paramsStart != -1) {
+
+            parameters = new LinkedHashMap<>();
+
+            String paramString = mType.substring(paramsStart + 1);
+            if (paramString.contains("\"")) {
+                Matcher m = COMPLEX_PARAMETERS.matcher(paramString);
+                while (m.find()) {
+                    String val = m.group().trim();
+                    addParameter(parameters, val);
+                }
+            } else {
+                StringTokenizer st = new StringTokenizer(paramString, ";");
+                while (st.hasMoreTokens()) {
+                    addParameter(parameters, st.nextToken());
+                }
+            }
+        }
+
+        return new MediaType(type.toLowerCase(),
+                             subtype.toLowerCase(),
+                             parameters);
+    }
+
+    private static void addParameter(Map<String, String> parameters, String token) {
+        int equalSign = token.indexOf('=');
+        if (equalSign == -1) {
+            throw new IllegalArgumentException("Wrong media type parameter, separator is missing");
+        }
+        parameters.put(token.substring(0, equalSign).trim().toLowerCase(),
+                       token.substring(equalSign + 1).trim());
+    }
+
+    public String toString(MediaType type) {
+        return typeToString(type);
+    }
+    public static String typeToString(MediaType type) {
+        return typeToString(type, null);
+    }
+    // Max number of parameters that may be ignored is 3, at least as known
+    // to the implementation
+    public static String typeToString(MediaType type, List<String> ignoreParams) {
+        if (type == null) {
+            throw new IllegalArgumentException("MediaType parameter is null");
+        }
+        StringBuilder sb = new StringBuilder();
+        sb.append(type.getType()).append('/').append(type.getSubtype());
+
+        Map<String, String> params = type.getParameters();
+        if (params != null) {
+            for (Map.Entry<String, String> entry : params.entrySet()) {
+                if (ignoreParams != null && ignoreParams.contains(entry.getKey())) {
+                    continue;
+                }
+                sb.append(';').append(entry.getKey()).append('=').append(entry.getValue());
+            }
+        }
+
+        return sb.toString();
+    }
+
+    private static MediaType handleMediaTypeWithoutSubtype(String mType) {
+        if (mType.startsWith(MediaType.MEDIA_TYPE_WILDCARD)) {
+            String mTypeNext = mType.length() == 1 ? "" : mType.substring(1).trim();
+            boolean mTypeNextEmpty = StringUtils.isEmpty(mTypeNext);
+            if (mTypeNextEmpty || mTypeNext.startsWith(";")) {
+                if (!mTypeNextEmpty) {
+                    Map<String, String> parameters = new LinkedHashMap<>();
+                    StringTokenizer st = new StringTokenizer(mType.substring(2).trim(), ";");
+                    while (st.hasMoreTokens()) {
+                        addParameter(parameters, st.nextToken());
+                    }
+                    return new MediaType(MediaType.MEDIA_TYPE_WILDCARD,
+                                         MediaType.MEDIA_TYPE_WILDCARD,
+                                         parameters);
+                }
+                return MediaType.WILDCARD_TYPE;
+
+            }
+        }
+        Message message = PhaseInterceptorChain.getCurrentMessage();
+        if (message != null
+            && !MessageUtils.getContextualBoolean(message, STRICT_MEDIA_TYPE_CHECK, false)) {
+            final MediaType mt;
+            if (mType.equals(MediaType.TEXT_PLAIN_TYPE.getType())) {
+                mt = MediaType.TEXT_PLAIN_TYPE;
+            } else if (mType.equals(MediaType.APPLICATION_XML_TYPE.getSubtype())) {
+                mt = MediaType.APPLICATION_XML_TYPE;
+            } else {
+                mt = MediaType.WILDCARD_TYPE;
+            }
+            LOG.fine("Converting a malformed media type '" + mType + "' to '" + typeToString(mt) + "'");
+            return mt;
+        }
+        throw new IllegalArgumentException("Media type separator is missing");
+    }
+
+    // Determines whether the type or subtype contains any of the tspecials characters defined at:
+    // https://tools.ietf.org/html/rfc2045#section-5.1
+    private static boolean isValid(String str) {
+        final int len = str.length();
+        if (len == 0) {
+            return false;
+        }
+        for (int i = 0; i < len; i++) {
+            switch (str.charAt(i)) {
+            case '/':
+            case '\\':
+            case '?':
+            case ':':
+            case '<':
+            case '>':
+            case ';':
+            case '(':
+            case ')':
+            case '@':
+            case ',':
+            case '[':
+            case ']':
+            case '=':
+                return false;
+            default:
+                continue;
+            }
+        }
+        return true;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/impl/RequestImpl.java b/transform/src/patch/java/org/apache/cxf/jaxrs/impl/RequestImpl.java
new file mode 100644
index 0000000..5921f1a
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/impl/RequestImpl.java
@@ -0,0 +1,388 @@
+/**
+ * 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.impl;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.Variant;
+
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.jaxrs.utils.HttpUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.PhaseInterceptorChain;
+
+/**
+ * TODO : deal with InvalidStateExceptions
+ *
+ */
+
+
+public class RequestImpl implements Request {
+    private final Message m;
+    private final HttpHeaders headers;
+
+    public RequestImpl(Message m) {
+        this.m = m;
+        this.headers = new HttpHeadersImpl(m);
+    }
+
+
+
+    public Variant selectVariant(List<Variant> vars) throws IllegalArgumentException {
+        if (vars == null || vars.isEmpty()) {
+            throw new IllegalArgumentException("List of Variants is either null or empty");
+        }
+        List<MediaType> acceptMediaTypes = headers.getAcceptableMediaTypes();
+        List<Locale> acceptLangs = headers.getAcceptableLanguages();
+        List<String> acceptEncs = parseAcceptEnc(
+            headers.getRequestHeaders().getFirst(HttpHeaders.ACCEPT_ENCODING));
+        List<Variant> requestVariants = sortAllCombinations(acceptMediaTypes, acceptLangs, acceptEncs);
+        List<Object> varyValues = new LinkedList<>();
+        for (Variant requestVar : requestVariants) {
+            for (Variant var : vars) {
+                MediaType mt = var.getMediaType();
+                Locale lang = var.getLanguage();
+                String enc = var.getEncoding();
+
+                boolean mtMatched = mt == null || requestVar.getMediaType().isCompatible(mt);
+                if (mtMatched) {
+                    handleVaryValues(varyValues, HttpHeaders.ACCEPT);
+                }
+
+                boolean langMatched = lang == null || isLanguageMatched(requestVar.getLanguage(), lang);
+                if (langMatched) {
+                    handleVaryValues(varyValues, HttpHeaders.ACCEPT_LANGUAGE);
+                }
+
+                boolean encMatched = acceptEncs.isEmpty() || enc == null 
+                    || isEncMatached(requestVar.getEncoding(), enc);
+                if (encMatched) {
+                    handleVaryValues(varyValues, HttpHeaders.ACCEPT_ENCODING);
+                }
+
+                if (mtMatched && encMatched && langMatched) {
+                    addVaryHeader(varyValues);
+                    return var;
+                }
+            }
+        }
+        return null;
+    }
+
+    private static List<Variant> sortAllCombinations(List<MediaType> mediaTypes,
+                                                     List<Locale> langs,
+                                                     List<String> encs) {
+        List<Variant> requestVars = new LinkedList<>();
+        for (MediaType mt : mediaTypes) {
+            for (Locale lang : langs) {
+                if (encs.isEmpty()) {
+                    requestVars.add(new Variant(mt, lang, null));
+                } else {
+                    for (String enc : encs) {
+                        requestVars.add(new Variant(mt, lang, enc));
+                    }
+                }
+            }
+        }
+        Collections.sort(requestVars, VariantComparator.INSTANCE);
+        return requestVars;
+    }
+
+
+    private static void handleVaryValues(List<Object> varyValues, String ...values) {
+        for (String v : values) {
+            if (v != null && !varyValues.contains(v)) {
+                varyValues.add(v);
+            }
+        }
+    }
+
+    private static void addVaryHeader(List<Object> varyValues) {
+        // at this point we still have no out-bound message so lets
+        // use HttpServletResponse. If needed we can save the header on the exchange
+        // and then copy it into the out-bound message's headers
+        Message message = PhaseInterceptorChain.getCurrentMessage();
+        if (message != null) {
+            Object httpResponse = message.get("HTTP.RESPONSE");
+            if (httpResponse != null) {
+                StringBuilder sb = new StringBuilder();
+                for (int i = 0; i < varyValues.size(); i++) {
+                    if (i > 0) {
+                        sb.append(',');
+                    }
+                    sb.append(varyValues.get(i).toString());
+                }
+                ((javax.servlet.http.HttpServletResponse)httpResponse)
+                    .setHeader(HttpHeaders.VARY, sb.toString());
+            }
+        }
+    }
+
+    private static boolean isLanguageMatched(Locale locale, Locale l) {
+
+        String language = locale.getLanguage();
+        return "*".equals(language) 
+            || language.equalsIgnoreCase(l.getLanguage());
+    }
+
+    private static boolean isEncMatached(String accepts, String enc) {
+        return accepts == null || "*".equals(accepts) || accepts.contains(enc);
+    }
+
+    private static List<String> parseAcceptEnc(String acceptEnc) {
+        if (StringUtils.isEmpty(acceptEnc)) {
+            return Collections.emptyList();
+        }
+        List<String> list = new LinkedList<>();
+        String[] values = acceptEnc.split(",");
+        for (String value : values) {
+            String[] pair = value.trim().split(";");
+            // ignore encoding qualifiers if any for now
+            list.add(pair[0]);
+        }
+        return list;
+    }
+
+    public ResponseBuilder evaluatePreconditions(EntityTag eTag) {
+        if (eTag == null) {
+            throw new IllegalArgumentException("ETag is null");
+        }
+        return evaluateAll(eTag, null);
+    }
+
+    private ResponseBuilder evaluateAll(EntityTag eTag, Date lastModified) {
+        // http://tools.ietf.org/search/draft-ietf-httpbis-p4-conditional-25#section-5
+        // Check If-Match. If it is not available proceed to checking If-Not-Modified-Since
+        // if it is available and the preconditions are not met - return, otherwise:
+        // Check If-Not-Match. If it is not available proceed to checking If-Modified-Since
+        // otherwise return the evaluation result
+
+        ResponseBuilder rb = evaluateIfMatch(eTag, lastModified);
+        if (rb == null) {
+            rb = evaluateIfNonMatch(eTag, lastModified);
+        }
+        return rb;
+    }
+
+    private ResponseBuilder evaluateIfMatch(EntityTag eTag, Date date) {
+        List<String> ifMatch = headers.getRequestHeader(HttpHeaders.IF_MATCH);
+
+        if (ifMatch == null || ifMatch.isEmpty()) {
+            return date == null ? null : evaluateIfNotModifiedSince(date);
+        }
+
+        try {
+            for (String value : ifMatch) {
+                if ("*".equals(value)) {
+                    return null;
+                }
+                EntityTag requestTag = EntityTag.valueOf(value);
+                // must be a strong comparison
+                if (!requestTag.isWeak() && !eTag.isWeak() && requestTag.equals(eTag)) {
+                    return null;
+                }
+            }
+        } catch (IllegalArgumentException ex) {
+            // ignore
+        }
+        return Response.status(Response.Status.PRECONDITION_FAILED).tag(eTag);
+    }
+
+    private ResponseBuilder evaluateIfNonMatch(EntityTag eTag, Date lastModified) {
+        List<String> ifNonMatch = headers.getRequestHeader(HttpHeaders.IF_NONE_MATCH);
+
+        if (ifNonMatch == null || ifNonMatch.isEmpty()) {
+            return lastModified == null ? null : evaluateIfModifiedSince(lastModified);
+        }
+
+        String method = getMethod();
+        boolean getOrHead = HttpMethod.GET.equals(method) || HttpMethod.HEAD.equals(method);
+        try {
+            for (String value : ifNonMatch) {
+                boolean result = "*".equals(value);
+                if (!result) {
+                    EntityTag requestTag = EntityTag.valueOf(value);
+                    result = getOrHead ? requestTag.equals(eTag)
+                        : !requestTag.isWeak() && !eTag.isWeak() && requestTag.equals(eTag);
+                }
+                if (result) {
+                    Response.Status status = getOrHead ? Response.Status.NOT_MODIFIED
+                        : Response.Status.PRECONDITION_FAILED;
+                    return Response.status(status).tag(eTag);
+                }
+            }
+        } catch (IllegalArgumentException ex) {
+            // ignore
+        }
+        return null;
+    }
+
+    public ResponseBuilder evaluatePreconditions(Date lastModified) {
+        if (lastModified == null) {
+            throw new IllegalArgumentException("Date is null");
+        }
+        ResponseBuilder rb = evaluateIfNotModifiedSince(lastModified);
+        if (rb == null) {
+            rb = evaluateIfModifiedSince(lastModified);
+        }
+        return rb;
+    }
+
+    private ResponseBuilder evaluateIfModifiedSince(Date lastModified) {
+        List<String> ifModifiedSince = headers.getRequestHeader(HttpHeaders.IF_MODIFIED_SINCE);
+
+        if (ifModifiedSince == null || ifModifiedSince.isEmpty()) {
+            return null;
+        }
+
+        SimpleDateFormat dateFormat = HttpUtils.getHttpDateFormat();
+
+        dateFormat.setLenient(false);
+        final Date dateSince;
+        try {
+            dateSince = dateFormat.parse(ifModifiedSince.get(0));
+        } catch (ParseException ex) {
+            // invalid header value, request should continue
+            return Response.status(Response.Status.PRECONDITION_FAILED);
+        }
+
+        if (dateSince.before(lastModified)) {
+            // request should continue
+            return null;
+        }
+
+        return Response.status(Response.Status.NOT_MODIFIED);
+    }
+
+    private ResponseBuilder evaluateIfNotModifiedSince(Date lastModified) {
+        List<String> ifNotModifiedSince = headers.getRequestHeader(HttpHeaders.IF_UNMODIFIED_SINCE);
+
+        if (ifNotModifiedSince == null || ifNotModifiedSince.isEmpty()) {
+            return null;
+        }
+
+        SimpleDateFormat dateFormat = HttpUtils.getHttpDateFormat();
+
+        dateFormat.setLenient(false);
+        final Date dateSince;
+        try {
+            dateSince = dateFormat.parse(ifNotModifiedSince.get(0));
+        } catch (ParseException ex) {
+            // invalid header value, request should continue
+            return Response.status(Response.Status.PRECONDITION_FAILED);
+        }
+
+        if (dateSince.before(lastModified)) {
+            return Response.status(Response.Status.PRECONDITION_FAILED);
+        }
+
+        return null;
+    }
+
+
+
+    public ResponseBuilder evaluatePreconditions(Date lastModified, EntityTag eTag) {
+        if (eTag == null || lastModified == null) {
+            throw new IllegalArgumentException("ETag or Date is null");
+        }
+        return evaluateAll(eTag, lastModified);
+    }
+
+    public String getMethod() {
+        return m.get(Message.HTTP_REQUEST_METHOD).toString();
+    }
+
+
+
+    public ResponseBuilder evaluatePreconditions() {
+        List<String> ifMatch = headers.getRequestHeader(HttpHeaders.IF_MATCH);
+        if (ifMatch != null) {
+            for (String value : ifMatch) {
+                if (!"*".equals(value)) {
+                    return Response.status(Response.Status.PRECONDITION_FAILED).tag(EntityTag.valueOf(value));
+                }
+            }
+        }
+        return null;
+    }
+
+    private static class VariantComparator implements Comparator<Variant> {
+
+        static final VariantComparator INSTANCE = new VariantComparator();
+
+        public int compare(Variant v1, Variant v2) {
+            int result = compareMediaTypes(v1.getMediaType(), v2.getMediaType());
+
+            if (result != 0) {
+                return result;
+            }
+
+            result = compareLanguages(v1.getLanguage(), v2.getLanguage());
+
+            if (result == 0) {
+                result = compareEncodings(v1.getEncoding(), v2.getEncoding());
+            }
+
+            return result;
+        }
+
+        private static int compareMediaTypes(MediaType mt1, MediaType mt2) {
+            if (mt1 != null && mt2 == null) {
+                return -1;
+            } else if (mt1 == null && mt2 != null) {
+                return 1;
+            }
+            return JAXRSUtils.compareMediaTypes(mt1, mt2);
+        }
+
+        private static int compareLanguages(Locale l1, Locale l2) {
+            if (l1 != null && l2 == null) {
+                return -1;
+            } else if (l1 == null && l2 != null) {
+                return 1;
+            }
+            return 0;
+        }
+
+        private static int compareEncodings(String enc1, String enc2) {
+            if (enc1 != null && enc2 == null) {
+                return -1;
+            } else if (enc1 == null && enc2 != null) {
+                return 1;
+            }
+            return 0;
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/impl/ResourceContextImpl.java b/transform/src/patch/java/org/apache/cxf/jaxrs/impl/ResourceContextImpl.java
new file mode 100644
index 0000000..ca1e629
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/impl/ResourceContextImpl.java
@@ -0,0 +1,67 @@
+/**
+ * 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.impl;
+
+import javax.ws.rs.container.ResourceContext;
+
+import org.apache.cxf.jaxrs.ext.ResourceContextProvider;
+import org.apache.cxf.jaxrs.lifecycle.PerRequestResourceProvider;
+import org.apache.cxf.jaxrs.lifecycle.ResourceProvider;
+import org.apache.cxf.jaxrs.model.ClassResourceInfo;
+import org.apache.cxf.jaxrs.model.OperationResourceInfo;
+import org.apache.cxf.jaxrs.provider.ServerProviderFactory;
+import org.apache.cxf.message.Message;
+
+public class ResourceContextImpl implements ResourceContext {
+    private static final String CONTEXT_PROVIDER_PROP = "org.apache.cxf.jaxrs.resource.context.provider";
+    private final ClassResourceInfo cri;
+    private final Class<?> subClass;
+    private final Message m;
+    public ResourceContextImpl(Message m, OperationResourceInfo ori) {
+        this.m = m;
+        this.cri = ori.getClassResourceInfo();
+        this.subClass = ori.getMethodToInvoke().getReturnType();
+    }
+
+    @Override
+    public <T> T getResource(Class<T> cls) {
+        final ResourceProvider rp;
+
+        Object propValue = m.getContextualProperty(CONTEXT_PROVIDER_PROP);
+        if (propValue instanceof ResourceContextProvider) {
+            rp = ((ResourceContextProvider)propValue).getResourceProvider(cls);
+        } else {
+            rp = new PerRequestResourceProvider(cls);
+        }
+        T resource = cls.cast(rp.getInstance(m));
+        return doInitResource(cls, resource);
+    }
+
+    @Override
+    public <T> T initResource(T resource) {
+        return doInitResource(resource.getClass(), resource);
+    }
+
+    private <T> T doInitResource(Class<?> cls, T resource) {
+        ClassResourceInfo sub = cri.getSubResource(subClass, cls, resource, true, m);
+        sub.initBeanParamInfo(ServerProviderFactory.getInstance(m));
+        sub.injectContexts(resource, m.getExchange().get(OperationResourceInfo.class), m);
+        return resource;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/impl/tl/ThreadLocalInvocationHandler.java b/transform/src/patch/java/org/apache/cxf/jaxrs/impl/tl/ThreadLocalInvocationHandler.java
new file mode 100644
index 0000000..bb27b5a
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/impl/tl/ThreadLocalInvocationHandler.java
@@ -0,0 +1,53 @@
+/**
+ * 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.impl.tl;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+
+public class ThreadLocalInvocationHandler<T> extends AbstractThreadLocalProxy<T>
+    implements InvocationHandler {
+
+    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
+        final Object target;
+        if (m.getDeclaringClass() == ThreadLocalProxy.class) {
+            target = this;
+        } else {
+            target = get();
+            if (target == null) {
+                if (m.getName().endsWith("toString")) {
+                    return null;
+                }
+                Class<?> contextCls = m.getDeclaringClass();
+                throw new NullPointerException(
+                                               contextCls.getName()
+                                                   + " context class has not been injected."
+                                                   + " Check if ContextProvider supporting this class is registered");
+            }
+        }
+        try {
+            return m.invoke(target, args);
+        } catch (InvocationTargetException e) {
+            throw e.getCause();
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/interceptor/JAXRSInInterceptor.java b/transform/src/patch/java/org/apache/cxf/jaxrs/interceptor/JAXRSInInterceptor.java
new file mode 100644
index 0000000..3c9c5d8
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/interceptor/JAXRSInInterceptor.java
@@ -0,0 +1,274 @@
+/**
+ * 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.interceptor;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.apache.cxf.common.i18n.BundleUtils;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.PropertyUtils;
+import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.interceptor.OutgoingChainInterceptor;
+import org.apache.cxf.jaxrs.impl.MetadataMap;
+import org.apache.cxf.jaxrs.impl.RequestPreprocessor;
+import org.apache.cxf.jaxrs.impl.UriInfoImpl;
+import org.apache.cxf.jaxrs.lifecycle.ResourceProvider;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.jaxrs.model.ClassResourceInfo;
+import org.apache.cxf.jaxrs.model.OperationResourceInfo;
+import org.apache.cxf.jaxrs.model.URITemplate;
+import org.apache.cxf.jaxrs.provider.ServerProviderFactory;
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
+import org.apache.cxf.jaxrs.utils.HttpUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageContentsList;
+import org.apache.cxf.phase.AbstractPhaseInterceptor;
+import org.apache.cxf.phase.Phase;
+
+public class JAXRSInInterceptor extends AbstractPhaseInterceptor<Message> {
+
+    private static final Logger LOG = LogUtils.getL7dLogger(JAXRSInInterceptor.class);
+    private static final ResourceBundle BUNDLE = BundleUtils.getBundle(JAXRSInInterceptor.class);
+    private static final String RESOURCE_METHOD = "org.apache.cxf.resource.method";
+    private static final String RESOURCE_OPERATION_NAME = "org.apache.cxf.resource.operation.name";
+
+    public JAXRSInInterceptor() {
+        super(Phase.UNMARSHAL);
+    }
+
+    public void handleMessage(Message message) {
+        final Exchange exchange = message.getExchange();
+
+        exchange.put(Message.REST_MESSAGE, Boolean.TRUE);
+        Response response = exchange.get(Response.class);
+        if (response == null) {
+            try {
+                processRequest(message, exchange);
+                if (exchange.isOneWay()) {
+                    ServerProviderFactory.getInstance(message).clearThreadLocalProxies();
+                }
+            } catch (Fault ex) {
+                convertExceptionToResponseIfPossible(ex.getCause(), message);
+            } catch (RuntimeException ex) {
+                convertExceptionToResponseIfPossible(ex, message);
+            } catch (IOException ex) {
+                convertExceptionToResponseIfPossible(ex, message);
+            }
+        }
+
+        response = exchange.get(Response.class);
+        if (response != null) {
+            createOutMessage(message, response);
+            message.getInterceptorChain().doInterceptStartingAt(message,
+                                                                OutgoingChainInterceptor.class.getName());
+        }
+    }
+
+    private void processRequest(Message message, Exchange exchange) throws IOException {
+
+        ServerProviderFactory providerFactory = ServerProviderFactory.getInstance(message);
+
+        RequestPreprocessor rp = providerFactory.getRequestPreprocessor();
+        if (rp != null) {
+            rp.preprocess(message, new UriInfoImpl(message, null));
+        }
+
+        // Global pre-match request filters
+        if (JAXRSUtils.runContainerRequestFilters(providerFactory, message, true, null)) {
+            return;
+        }
+        // HTTP method
+        String httpMethod = HttpUtils.getProtocolHeader(message, Message.HTTP_REQUEST_METHOD,
+                                                        HttpMethod.POST, true);
+
+        // Path to match
+        String rawPath = HttpUtils.getPathToMatch(message, true);
+
+        Map<String, List<String>> protocolHeaders = CastUtils.cast((Map<?, ?>)message.get(Message.PROTOCOL_HEADERS));
+
+        // Content-Type
+        String requestContentType = null;
+        List<String> ctHeaderValues = protocolHeaders.get(Message.CONTENT_TYPE);
+        if (ctHeaderValues != null && !ctHeaderValues.isEmpty()) {
+            requestContentType = ctHeaderValues.get(0);
+            message.put(Message.CONTENT_TYPE, requestContentType);
+        }
+        if (requestContentType == null) {
+            requestContentType = (String)message.get(Message.CONTENT_TYPE);
+
+            if (requestContentType == null) {
+                requestContentType = MediaType.WILDCARD;
+            }
+        }
+
+        // Accept
+        String acceptTypes = null;
+        List<String> acceptHeaderValues = protocolHeaders.get(Message.ACCEPT_CONTENT_TYPE);
+        if (acceptHeaderValues != null && !acceptHeaderValues.isEmpty()) {
+            acceptTypes = acceptHeaderValues.get(0);
+            message.put(Message.ACCEPT_CONTENT_TYPE, acceptTypes);
+        }
+
+        if (acceptTypes == null) {
+            acceptTypes = HttpUtils.getProtocolHeader(message, Message.ACCEPT_CONTENT_TYPE, null);
+            if (acceptTypes == null) {
+                acceptTypes = "*/*";
+                message.put(Message.ACCEPT_CONTENT_TYPE, acceptTypes);
+            }
+        }
+        final List<MediaType> acceptContentTypes;
+        try {
+            acceptContentTypes = JAXRSUtils.sortMediaTypes(acceptTypes, JAXRSUtils.MEDIA_TYPE_Q_PARAM);
+        } catch (IllegalArgumentException ex) {
+            throw ExceptionUtils.toNotAcceptableException(null, null);
+        }
+        exchange.put(Message.ACCEPT_CONTENT_TYPE, acceptContentTypes);
+
+        //1. Matching target resource class
+        List<ClassResourceInfo> resources = JAXRSUtils.getRootResources(message);
+        Map<ClassResourceInfo, MultivaluedMap<String, String>> matchedResources =
+            JAXRSUtils.selectResourceClass(resources, rawPath, message);
+        if (matchedResources == null) {
+            org.apache.cxf.common.i18n.Message errorMsg =
+                new org.apache.cxf.common.i18n.Message("NO_ROOT_EXC",
+                                                   BUNDLE,
+                                                   message.get(Message.REQUEST_URI),
+                                                   rawPath);
+            Level logLevel = JAXRSUtils.getExceptionLogLevel(message, NotFoundException.class);
+            LOG.log(logLevel == null ? Level.FINE : logLevel, errorMsg.toString());
+            Response resp = JAXRSUtils.createResponse(resources, message, errorMsg.toString(),
+                    Response.Status.NOT_FOUND.getStatusCode(), false);
+            throw ExceptionUtils.toNotFoundException(null, resp);
+        }
+
+        MultivaluedMap<String, String> matchedValues = new MetadataMap<>();
+
+        final OperationResourceInfo ori;
+
+        try {
+            ori = JAXRSUtils.findTargetMethod(matchedResources, message,
+                      httpMethod, matchedValues, requestContentType, acceptContentTypes, true, true);
+            setExchangeProperties(message, exchange, ori, matchedValues, resources.size());
+        } catch (WebApplicationException ex) {
+            if (JAXRSUtils.noResourceMethodForOptions(ex.getResponse(), httpMethod)) {
+                Response response = JAXRSUtils.createResponse(resources, null, null, 200, true);
+                exchange.put(Response.class, response);
+                return;
+            }
+            throw ex;
+        }
+
+
+        if (LOG.isLoggable(Level.FINE)) {
+            LOG.fine("Request path is: " + rawPath);
+            LOG.fine("Request HTTP method is: " + httpMethod);
+            LOG.fine("Request contentType is: " + requestContentType);
+            LOG.fine("Accept contentType is: " + acceptTypes);
+
+            LOG.fine("Found operation: " + ori.getMethodToInvoke().getName());
+        }
+
+        // Global and name-bound post-match request filters
+        if (!ori.isSubResourceLocator()
+            && JAXRSUtils.runContainerRequestFilters(providerFactory,
+                                                      message,
+                                                      false,
+                                                      ori.getNameBindings())) {
+            return;
+        }
+
+
+        //Process parameters
+        List<Object> params = JAXRSUtils.processParameters(ori, matchedValues, message);
+        message.setContent(List.class, params);
+    }
+
+    private void convertExceptionToResponseIfPossible(Throwable ex, Message message) {
+        Response excResponse = JAXRSUtils.convertFaultToResponse(ex, message);
+        if (excResponse == null) {
+            ServerProviderFactory.getInstance(message).clearThreadLocalProxies();
+            message.getExchange().put(Message.PROPOGATE_EXCEPTION,
+                                      ExceptionUtils.propogateException(message));
+            throw ex instanceof RuntimeException ? (RuntimeException)ex
+                : ExceptionUtils.toInternalServerErrorException(ex, null);
+        }
+        message.getExchange().put(Response.class, excResponse);
+        message.getExchange().put(Throwable.class, ex);
+    }
+
+    private void setExchangeProperties(Message message,
+                                       Exchange exchange,
+                                       OperationResourceInfo ori,
+                                       MultivaluedMap<String, String> values,
+                                       int numberOfResources) {
+        final ClassResourceInfo cri = ori.getClassResourceInfo();
+        exchange.put(OperationResourceInfo.class, ori);
+        exchange.put(JAXRSUtils.ROOT_RESOURCE_CLASS, cri);
+        message.put(RESOURCE_METHOD, ori.getMethodToInvoke());
+        message.put(URITemplate.TEMPLATE_PARAMETERS, values);
+        message.put(URITemplate.URI_TEMPLATE, JAXRSUtils.getUriTemplate(message, cri, ori));
+
+        String plainOperationName = ori.getMethodToInvoke().getName();
+        if (numberOfResources > 1) {
+            plainOperationName = cri.getServiceClass().getSimpleName() + "#" + plainOperationName;
+        }
+        exchange.put(RESOURCE_OPERATION_NAME, plainOperationName);
+
+        if (ori.isOneway()
+            || PropertyUtils.isTrue(HttpUtils.getProtocolHeader(message, Message.ONE_WAY_REQUEST, null))) {
+            exchange.setOneWay(true);
+        }
+        ResourceProvider rp = cri.getResourceProvider();
+        if (rp instanceof SingletonResourceProvider) {
+            //cri.isSingleton is not guaranteed to indicate we have a 'pure' singleton
+            exchange.put(Message.SERVICE_OBJECT, rp.getInstance(message));
+        }
+    }
+
+    private Message createOutMessage(Message inMessage, Response r) {
+        Endpoint e = inMessage.getExchange().getEndpoint();
+        Message mout = e.getBinding().createMessage();
+        mout.setContent(List.class, new MessageContentsList(r));
+        mout.setExchange(inMessage.getExchange());
+        mout.setInterceptorChain(
+             OutgoingChainInterceptor.getOutInterceptorChain(inMessage.getExchange()));
+        inMessage.getExchange().setOutMessage(mout);
+        if (r.getStatus() >= Response.Status.BAD_REQUEST.getStatusCode()) {
+            inMessage.getExchange().put("cxf.io.cacheinput", Boolean.FALSE);
+        }
+        return mout;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/interceptor/JAXRSOutInterceptor.java b/transform/src/patch/java/org/apache/cxf/jaxrs/interceptor/JAXRSOutInterceptor.java
new file mode 100644
index 0000000..ad75c0f
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/interceptor/JAXRSOutInterceptor.java
@@ -0,0 +1,498 @@
+/**
+ * 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.interceptor;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.Produces;
+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.ext.MessageBodyWriter;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.stream.events.XMLEvent;
+
+import org.apache.cxf.common.i18n.BundleUtils;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.PropertyUtils;
+import org.apache.cxf.interceptor.AbstractOutDatabindingInterceptor;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.io.CachedOutputStream;
+import org.apache.cxf.jaxrs.impl.ResponseImpl;
+import org.apache.cxf.jaxrs.impl.WriterInterceptorMBW;
+import org.apache.cxf.jaxrs.model.OperationResourceInfo;
+import org.apache.cxf.jaxrs.provider.AbstractConfigurableProvider;
+import org.apache.cxf.jaxrs.provider.ServerProviderFactory;
+import org.apache.cxf.jaxrs.utils.AnnotationUtils;
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
+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.Exchange;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageContentsList;
+import org.apache.cxf.message.MessageUtils;
+import org.apache.cxf.phase.Phase;
+import org.apache.cxf.staxutils.CachingXmlEventWriter;
+import org.apache.cxf.staxutils.StaxUtils;
+import org.apache.cxf.transport.http.AbstractHTTPDestination;
+
+public class JAXRSOutInterceptor extends AbstractOutDatabindingInterceptor {
+    private static final Logger LOG = LogUtils.getL7dLogger(JAXRSOutInterceptor.class);
+    private static final ResourceBundle BUNDLE = BundleUtils.getBundle(JAXRSOutInterceptor.class);
+
+    public JAXRSOutInterceptor() {
+        super(Phase.MARSHAL);
+    }
+
+    public void handleMessage(Message message) {
+        ServerProviderFactory providerFactory = ServerProviderFactory.getInstance(message);
+        try {
+            processResponse(providerFactory, message);
+        } finally {
+            ServerProviderFactory.releaseRequestState(providerFactory, message);
+        }
+
+
+    }
+
+    @SuppressWarnings("resource") // Response shouldn't be closed here
+    private void processResponse(ServerProviderFactory providerFactory, Message message) {
+
+        if (isResponseAlreadyHandled(message)) {
+            return;
+        }
+        MessageContentsList objs = MessageContentsList.getContentsList(message);
+        if (objs == null || objs.isEmpty()) {
+            return;
+        }
+
+        Object responseObj = objs.get(0);
+
+        final Response response;
+        if (responseObj instanceof Response) {
+            response = (Response)responseObj;
+            if (response.getStatus() == 500
+                && message.getExchange().get(JAXRSUtils.EXCEPTION_FROM_MAPPER) != null) {
+                message.put(Message.RESPONSE_CODE, 500);
+                return;
+            }
+        } else {
+            int status = getStatus(message, responseObj != null ? 200 : 204);
+            response = JAXRSUtils.toResponseBuilder(status).entity(responseObj).build();
+        }
+
+        Exchange exchange = message.getExchange();
+        OperationResourceInfo ori = (OperationResourceInfo)exchange.get(OperationResourceInfo.class
+            .getName());
+
+        serializeMessage(providerFactory, message, response, ori, true);
+    }
+
+
+
+    private int getStatus(Message message, int defaultValue) {
+        Object customStatus = message.getExchange().get(Message.RESPONSE_CODE);
+        return customStatus == null ? defaultValue : (Integer)customStatus;
+    }
+
+    private void serializeMessage(ServerProviderFactory providerFactory,
+                                  Message message,
+                                  Response theResponse,
+                                  OperationResourceInfo ori,
+                                  boolean firstTry) {
+
+        ResponseImpl response = (ResponseImpl)JAXRSUtils.copyResponseIfNeeded(theResponse);
+
+        final Exchange exchange = message.getExchange();
+
+        boolean headResponse = response.getStatus() == 200 && firstTry
+            && ori != null && HttpMethod.HEAD.equals(ori.getHttpMethod());
+        Object entity = response.getActualEntity();
+        if (headResponse && entity != null) {
+            LOG.info(new org.apache.cxf.common.i18n.Message("HEAD_WITHOUT_ENTITY", BUNDLE).toString());
+            entity = null;
+        }
+
+
+        Method invoked = ori == null ? null : ori.getAnnotatedMethod() != null
+            ? ori.getAnnotatedMethod() : ori.getMethodToInvoke();
+
+        Annotation[] annotations;
+        Annotation[] staticAnns = ori != null ? ori.getOutAnnotations() : new Annotation[]{};
+        Annotation[] responseAnns = response.getEntityAnnotations();
+        if (responseAnns != null) {
+            annotations = new Annotation[staticAnns.length + responseAnns.length];
+            System.arraycopy(staticAnns, 0, annotations, 0, staticAnns.length);
+            System.arraycopy(responseAnns, 0, annotations, staticAnns.length, responseAnns.length);
+        } else {
+            annotations = staticAnns;
+        }
+
+        response.setStatus(getActualStatus(response.getStatus(), entity));
+        response.setEntity(entity, annotations);
+
+        // Prepare the headers
+        MultivaluedMap<String, Object> responseHeaders =
+            prepareResponseHeaders(message, response, entity, firstTry);
+
+        // Run the filters
+        try {
+            JAXRSUtils.runContainerResponseFilters(providerFactory, response, message, ori, invoked);
+        } catch (Throwable ex) {
+            handleWriteException(providerFactory, message, ex, firstTry);
+            return;
+        }
+
+        // Write the entity
+        entity = InjectionUtils.getEntity(response.getActualEntity());
+        setResponseStatus(message, getActualStatus(response.getStatus(), entity));
+        if (entity == null) {
+            if (!headResponse) {
+                responseHeaders.putSingle(HttpHeaders.CONTENT_LENGTH, "0");
+                if (MessageUtils.getContextualBoolean(message, "remove.content.type.for.empty.response", false)) {
+                    responseHeaders.remove(HttpHeaders.CONTENT_TYPE);
+                    message.remove(Message.CONTENT_TYPE);
+                }
+            }
+            HttpUtils.convertHeaderValuesToString(responseHeaders, true);
+            return;
+        }
+
+        Object ignoreWritersProp = exchange.get(JAXRSUtils.IGNORE_MESSAGE_WRITERS);
+        boolean ignoreWriters =
+            ignoreWritersProp != null && Boolean.valueOf(ignoreWritersProp.toString());
+        if (ignoreWriters) {
+            writeResponseToStream(message.getContent(OutputStream.class), entity);
+            return;
+        }
+
+        MediaType responseMediaType =
+            getResponseMediaType(responseHeaders.getFirst(HttpHeaders.CONTENT_TYPE));
+
+        Class<?> serviceCls = invoked != null ? ori.getClassResourceInfo().getServiceClass() : null;
+        Class<?> targetType = InjectionUtils.getRawResponseClass(entity);
+        Type genericType = InjectionUtils.getGenericResponseType(invoked, serviceCls,
+                                                                 response.getActualEntity(), targetType, exchange);
+        targetType = InjectionUtils.updateParamClassToTypeIfNeeded(targetType, genericType);
+        annotations = response.getEntityAnnotations();
+
+        List<WriterInterceptor> writers = providerFactory
+            .createMessageBodyWriterInterceptor(targetType, genericType, annotations, responseMediaType, message,
+                                                ori == null ? null : ori.getNameBindings());
+
+        OutputStream outOriginal = message.getContent(OutputStream.class);
+        if (writers == null || writers.isEmpty()) {
+            writeResponseErrorMessage(message, outOriginal, "NO_MSG_WRITER", targetType, responseMediaType);
+            return;
+        }
+        try {
+            boolean checkWriters = false;
+            if (responseMediaType.isWildcardSubtype()) {
+                Produces pM = AnnotationUtils.getMethodAnnotation(ori == null ? null : ori.getAnnotatedMethod(),
+                                                                              Produces.class);
+                Produces pC = AnnotationUtils.getClassAnnotation(serviceCls, Produces.class);
+                checkWriters = pM == null && pC == null;
+            }
+            responseMediaType = checkFinalContentType(responseMediaType, writers, checkWriters);
+        } catch (Throwable ex) {
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.log(Level.FINE, ex.getMessage(), ex);
+            }
+            handleWriteException(providerFactory, message, ex, firstTry);
+            return;
+        }
+        String finalResponseContentType = JAXRSUtils.mediaTypeToString(responseMediaType);
+        if (LOG.isLoggable(Level.FINE)) {
+            LOG.fine("Response content type is: " + finalResponseContentType);
+        }
+        responseHeaders.putSingle(HttpHeaders.CONTENT_TYPE, finalResponseContentType);
+        message.put(Message.CONTENT_TYPE, finalResponseContentType);
+
+        boolean enabled = checkBufferingMode(message, writers, firstTry);
+        try {
+
+            try {       // NOPMD
+                JAXRSUtils.writeMessageBody(writers,
+                        entity,
+                        targetType,
+                        genericType,
+                        annotations,
+                        responseMediaType,
+                        responseHeaders,
+                        message);
+
+                if (isResponseRedirected(message)) {
+                    return;
+                }
+                checkCachedStream(message, outOriginal, enabled);
+            } finally {
+                if (enabled) {
+                    OutputStream os = message.getContent(OutputStream.class);
+                    if (os != outOriginal && os instanceof CachedOutputStream) {
+                        os.close();
+                    }
+                    message.setContent(OutputStream.class, outOriginal);
+                    message.put(XMLStreamWriter.class.getName(), null);
+                }
+            }
+
+        } catch (Throwable ex) {
+            logWriteError(firstTry, targetType, responseMediaType);
+            handleWriteException(providerFactory, message, ex, firstTry);
+        }
+    }
+
+    private MultivaluedMap<String, Object> prepareResponseHeaders(Message message,
+                                                                  ResponseImpl response,
+                                                                  Object entity,
+                                                                  boolean firstTry) {
+        MultivaluedMap<String, Object> responseHeaders = response.getMetadata();
+        @SuppressWarnings("unchecked")
+        Map<String, List<Object>> userHeaders = (Map<String, List<Object>>)message.get(Message.PROTOCOL_HEADERS);
+        if (firstTry && userHeaders != null) {
+            responseHeaders.putAll(userHeaders);
+        }
+        if (entity != null) {
+            Object customContentType = responseHeaders.getFirst(HttpHeaders.CONTENT_TYPE);
+            if (customContentType == null) {
+                String initialResponseContentType = (String)message.get(Message.CONTENT_TYPE);
+                if (initialResponseContentType != null) {
+                    responseHeaders.putSingle(HttpHeaders.CONTENT_TYPE, initialResponseContentType);
+                }
+            } else {
+                message.put(Message.CONTENT_TYPE, customContentType.toString());
+            }
+        }
+        message.put(Message.PROTOCOL_HEADERS, responseHeaders);
+        setResponseDate(responseHeaders, firstTry);
+        return responseHeaders;
+    }
+
+    private MediaType getResponseMediaType(Object mediaTypeHeader) {
+        MediaType responseMediaType;
+        if (mediaTypeHeader instanceof MediaType) {
+            responseMediaType = (MediaType)mediaTypeHeader;
+        } else {
+            responseMediaType = mediaTypeHeader == null ? MediaType.WILDCARD_TYPE
+                : JAXRSUtils.toMediaType(mediaTypeHeader.toString());
+        }
+        return responseMediaType;
+    }
+
+    private int getActualStatus(int status, Object responseObj) {
+        if (status == -1) {
+            return responseObj == null ? 204 : 200;
+        }
+        return status;
+    }
+
+    private boolean checkBufferingMode(Message m, List<WriterInterceptor> writers, boolean firstTry) {
+        if (!firstTry) {
+            return false;
+        }
+        WriterInterceptor last = writers.get(writers.size() - 1);
+        MessageBodyWriter<Object> w = ((WriterInterceptorMBW)last).getMBW();
+        Object outBuf = m.getContextualProperty(OUT_BUFFERING);
+        boolean enabled = PropertyUtils.isTrue(outBuf);
+        boolean configurableProvider = w instanceof AbstractConfigurableProvider;
+        if (!enabled && outBuf == null && configurableProvider) {
+            enabled = ((AbstractConfigurableProvider)w).getEnableBuffering();
+        }
+        if (enabled) {
+            boolean streamingOn = configurableProvider
+                && ((AbstractConfigurableProvider)w).getEnableStreaming();
+            if (streamingOn) {
+                m.setContent(XMLStreamWriter.class, new CachingXmlEventWriter());
+            } else {
+                m.setContent(OutputStream.class, new CachedOutputStream());
+            }
+        }
+        return enabled;
+    }
+
+    private void checkCachedStream(Message m, OutputStream osOriginal, boolean enabled) throws Exception {
+        final XMLStreamWriter writer;
+        if (enabled) {
+            writer = m.getContent(XMLStreamWriter.class);
+        } else {
+            writer = (XMLStreamWriter)m.get(XMLStreamWriter.class.getName());
+        }
+        if (writer instanceof CachingXmlEventWriter) {
+            CachingXmlEventWriter cache = (CachingXmlEventWriter)writer;
+            if (!cache.getEvents().isEmpty()) {
+                XMLStreamWriter origWriter = null;
+                try {
+                    origWriter = StaxUtils.createXMLStreamWriter(osOriginal);
+                    for (XMLEvent event : cache.getEvents()) {
+                        StaxUtils.writeEvent(event, origWriter);
+                    }
+                } finally {
+                    StaxUtils.close(origWriter);
+                }
+            }
+            m.setContent(XMLStreamWriter.class, null);
+            return;
+        }
+        if (enabled) {
+            OutputStream os = m.getContent(OutputStream.class);
+            if (os != osOriginal && os instanceof CachedOutputStream) {
+                CachedOutputStream cos = (CachedOutputStream)os;
+                if (cos.size() != 0) {
+                    cos.writeCacheTo(osOriginal);
+                }
+            }
+        }
+    }
+
+    private void logWriteError(boolean firstTry, Class<?> cls, MediaType ct) {
+        if (firstTry) {
+            JAXRSUtils.logMessageHandlerProblem("MSG_WRITER_PROBLEM", cls, ct);
+        }
+    }
+
+    private void handleWriteException(ServerProviderFactory pf,
+                                      Message message,
+                                      Throwable ex,
+                                      boolean firstTry) {
+        Response excResponse = null;
+        if (firstTry) {
+            excResponse = JAXRSUtils.convertFaultToResponse(ex, message);
+        } else {
+            message.getExchange().put(JAXRSUtils.SECOND_JAXRS_EXCEPTION, Boolean.TRUE);
+        }
+        if (excResponse == null) {
+            setResponseStatus(message, 500);
+            throw new Fault(ex);
+        }
+        serializeMessage(pf, message, excResponse, null, false);
+
+    }
+
+
+    private void writeResponseErrorMessage(Message message, OutputStream out,
+                                           String name, Class<?> cls, MediaType ct) {
+        message.put(Message.CONTENT_TYPE, "text/plain");
+        message.put(Message.RESPONSE_CODE, 500);
+        try {
+            String errorMessage = JAXRSUtils.logMessageHandlerProblem(name, cls, ct);
+            if (out != null) {
+                out.write(errorMessage.getBytes(StandardCharsets.UTF_8));
+            }
+        } catch (IOException another) {
+            // ignore
+        }
+    }
+
+
+    private MediaType checkFinalContentType(MediaType mt, List<WriterInterceptor> writers, boolean checkWriters) {
+        if (checkWriters) {
+            int mbwIndex = writers.size() == 1 ? 0 : writers.size() - 1;
+            MessageBodyWriter<Object> writer = ((WriterInterceptorMBW)writers.get(mbwIndex)).getMBW();
+            Produces pm = writer.getClass().getAnnotation(Produces.class);
+            if (pm != null) {
+                List<MediaType> sorted =
+                    JAXRSUtils.sortMediaTypes(JAXRSUtils.getMediaTypes(pm.value()), JAXRSUtils.MEDIA_TYPE_QS_PARAM);
+                mt = JAXRSUtils.intersectMimeTypes(sorted, mt).get(0);
+            }
+        }
+        if (mt.isWildcardType() || mt.isWildcardSubtype()) {
+            if ("application".equals(mt.getType()) || mt.isWildcardType()) {
+                mt = MediaType.APPLICATION_OCTET_STREAM_TYPE;
+            } else {
+                throw ExceptionUtils.toNotAcceptableException(null,  null);
+            }
+        }
+        return mt;
+    }
+
+    private void setResponseDate(MultivaluedMap<String, Object> headers, boolean firstTry) {
+        if (!firstTry || headers.containsKey(HttpHeaders.DATE)) {
+            return;
+        }
+        SimpleDateFormat format = HttpUtils.getHttpDateFormat();
+        headers.putSingle(HttpHeaders.DATE, format.format(new Date()));
+    }
+
+    private boolean isResponseAlreadyHandled(Message m) {
+        return isResponseAlreadyCommited(m) || isResponseRedirected(m);
+    }
+
+    private boolean isResponseAlreadyCommited(Message m) {
+        return Boolean.TRUE.equals(m.getExchange().get(AbstractHTTPDestination.RESPONSE_COMMITED));
+    }
+
+    private boolean isResponseRedirected(Message m) {
+        return Boolean.TRUE.equals(m.getExchange().get(AbstractHTTPDestination.REQUEST_REDIRECTED));
+    }
+
+    private void writeResponseToStream(OutputStream os, Object responseObj) {
+        try {
+            byte[] bytes = responseObj.toString().getBytes(StandardCharsets.UTF_8);
+            os.write(bytes, 0, bytes.length);
+        } catch (Exception ex) {
+            LOG.severe("Problem with writing the data to the output stream");
+            ex.printStackTrace();
+            throw new RuntimeException(ex);
+        }
+    }
+
+    private void setResponseStatus(Message message, int status) {
+        message.put(Message.RESPONSE_CODE, status);
+        boolean responseHeadersCopied = isResponseHeadersCopied(message);
+        if (responseHeadersCopied) {
+            HttpServletResponse response =
+                (HttpServletResponse)message.get(AbstractHTTPDestination.HTTP_RESPONSE);
+            response.setStatus(status);
+        }
+    }
+
+    // Some CXF interceptors such as FIStaxOutInterceptor will indirectly initiate
+    // an early copying of response code and headers into the HttpServletResponse
+    // TODO : Pushing the filter processing and copying response headers into say
+    // PRE-LOGICAl and PREPARE_SEND interceptors will most likely be a good thing
+    // however JAX-RS MessageBodyWriters are also allowed to add response headers
+    // which is reason why a MultipartMap parameter in MessageBodyWriter.writeTo
+    // method is modifiable. Thus we do need to know if the initial copy has already
+    // occurred: for now we will just use to ensure the correct status is set
+    private boolean isResponseHeadersCopied(Message message) {
+        return PropertyUtils.isTrue(message.get(AbstractHTTPDestination.RESPONSE_HEADERS_COPIED));
+    }
+
+    public void handleFault(Message message) {
+        // complete
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/model/AbstractResourceInfo.java b/transform/src/patch/java/org/apache/cxf/jaxrs/model/AbstractResourceInfo.java
new file mode 100644
index 0000000..edf010e
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/model/AbstractResourceInfo.java
@@ -0,0 +1,389 @@
+/**
+ * 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.model;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Logger;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.ReflectionUtil;
+import org.apache.cxf.jaxrs.impl.tl.ThreadLocalProxy;
+import org.apache.cxf.jaxrs.utils.InjectionUtils;
+
+public abstract class AbstractResourceInfo {
+    public static final String CONSTRUCTOR_PROXY_MAP = "jaxrs-constructor-proxy-map";
+    private static final Logger LOG = LogUtils.getL7dLogger(AbstractResourceInfo.class);
+    private static final String FIELD_PROXY_MAP = "jaxrs-field-proxy-map";
+    private static final String SETTER_PROXY_MAP = "jaxrs-setter-proxy-map";
+
+    protected boolean root;
+    protected Class<?> resourceClass;
+    protected Class<?> serviceClass;
+
+    private Map<Class<?>, List<Field>> contextFields;
+    private Map<Class<?>, Map<Class<?>, Method>> contextMethods;
+    private Bus bus;
+    private boolean constructorProxiesAvailable;
+    private boolean contextsAvailable;
+
+    protected AbstractResourceInfo(Bus bus) {
+        this.bus = bus;
+    }
+
+    protected AbstractResourceInfo(Class<?> resourceClass, Class<?> serviceClass,
+                                   boolean isRoot, boolean checkContexts, Bus bus) {
+        this(resourceClass, serviceClass, isRoot, checkContexts, null, bus, null);
+    }
+
+    protected AbstractResourceInfo(Class<?> resourceClass,
+                                   Class<?> serviceClass,
+                                   boolean isRoot,
+                                   boolean checkContexts,
+                                   Map<Class<?>, ThreadLocalProxy<?>> constructorProxies,
+                                   Bus bus,
+                                   Object provider) {
+        this.bus = bus;
+        this.serviceClass = serviceClass;
+        this.resourceClass = resourceClass;
+        root = isRoot;
+        if (checkContexts && resourceClass != null) {
+            findContexts(serviceClass, provider, constructorProxies);
+        }
+    }
+
+    private void findContexts(Class<?> cls, Object provider,
+                              Map<Class<?>, ThreadLocalProxy<?>> constructorProxies) {
+        findContextFields(cls, provider);
+        findContextSetterMethods(cls, provider);
+        if (constructorProxies != null) {
+            Map<Class<?>, Map<Class<?>, ThreadLocalProxy<?>>> proxies = getConstructorProxyMap();
+            proxies.put(serviceClass, constructorProxies);
+            constructorProxiesAvailable = true;
+        }
+
+
+        contextsAvailable = contextFields != null && !contextFields.isEmpty()
+            || contextMethods != null && !contextMethods.isEmpty()
+            || constructorProxiesAvailable;
+    }
+
+    public boolean contextsAvailable() {
+        return contextsAvailable;
+    }
+
+    public Bus getBus() {
+        return bus;
+    }
+
+    public void setResourceClass(Class<?> rClass) {
+        resourceClass = rClass;
+        if (serviceClass.isInterface() && resourceClass != null && !resourceClass.isInterface()) {
+            findContexts(resourceClass, null, null);
+        }
+    }
+
+    public Class<?> getServiceClass() {
+        return serviceClass;
+    }
+
+    private void findContextFields(Class<?> cls, Object provider) {
+        if (cls == Object.class || cls == null) {
+            return;
+        }
+        for (Field f : ReflectionUtil.getDeclaredFields(cls)) {
+            for (Annotation a : f.getAnnotations()) {
+                if (a.annotationType() == Context.class
+                    && (f.getType().isInterface() || f.getType() == Application.class)) {
+                    contextFields = addContextField(contextFields, f);
+                    checkContextClass(f.getType());
+                    if (!InjectionUtils.VALUE_CONTEXTS.contains(f.getType().getName())) {
+                        addToMap(getFieldProxyMap(true), f, getFieldThreadLocalProxy(f, provider));
+                    }
+                }
+            }
+        }
+        findContextFields(cls.getSuperclass(), provider);
+    }
+
+    private static ThreadLocalProxy<?> getFieldThreadLocalProxy(Field f, Object provider) {
+        if (provider != null) {
+            Object proxy = null;
+            synchronized (provider) {
+                try {
+                    proxy = InjectionUtils.extractFieldValue(f, provider);
+                } catch (Throwable t) {
+                    // continue
+                }
+                if (!(proxy instanceof ThreadLocalProxy)) {
+                    proxy = InjectionUtils.createThreadLocalProxy(f.getType());
+                    InjectionUtils.injectFieldValue(f, provider, proxy);
+                }
+            }
+            return (ThreadLocalProxy<?>)proxy;
+        }
+        return InjectionUtils.createThreadLocalProxy(f.getType());
+    }
+
+    private static ThreadLocalProxy<?> getMethodThreadLocalProxy(Method m, Object provider) {
+        if (provider != null) {
+            Object proxy = null;
+            synchronized (provider) {
+                try {
+                    proxy = InjectionUtils.extractFromMethod(provider,
+                                                             InjectionUtils.getGetterFromSetter(m),
+                                                             false);
+                } catch (Throwable t) {
+                    // continue
+                }
+                if (!(proxy instanceof ThreadLocalProxy)) {
+                    proxy = InjectionUtils.createThreadLocalProxy(m.getParameterTypes()[0]);
+                    InjectionUtils.injectThroughMethod(provider, m, proxy);
+                }
+            }
+            return (ThreadLocalProxy<?>)proxy;
+        }
+        return InjectionUtils.createThreadLocalProxy(m.getParameterTypes()[0]);
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T> Map<Class<?>, Map<T, ThreadLocalProxy<?>>> getProxyMap(String prop, boolean create) {
+        // Avoid synchronizing on the bus for a ConcurrentHashMAp
+        if (bus.getProperties() instanceof ConcurrentHashMap) {
+            return (Map<Class<?>, Map<T, ThreadLocalProxy<?>>>) bus.getProperties().computeIfAbsent(prop, k ->
+                new ConcurrentHashMap<Class<?>, Map<T, ThreadLocalProxy<?>>>(2)
+            );
+        }
+
+        Object property;
+        synchronized (bus) {
+            property = bus.getProperty(prop);
+            if (property == null && create) {
+                Map<Class<?>, Map<T, ThreadLocalProxy<?>>> map
+                    = new ConcurrentHashMap<>(2);
+                bus.setProperty(prop, map);
+                property = map;
+            }
+        }
+        return (Map<Class<?>, Map<T, ThreadLocalProxy<?>>>)property;
+    }
+
+    public Map<Class<?>, ThreadLocalProxy<?>> getConstructorProxies() {
+        if (constructorProxiesAvailable) {
+            return getConstructorProxyMap().get(serviceClass);
+        }
+        return null;
+    }
+
+    @SuppressWarnings("unchecked")
+    private Map<Class<?>, Map<Class<?>, ThreadLocalProxy<?>>> getConstructorProxyMap() {
+        Object property = bus.getProperty(CONSTRUCTOR_PROXY_MAP);
+        if (property == null) {
+            Map<Class<?>, Map<Class<?>, ThreadLocalProxy<?>>> map
+                = new ConcurrentHashMap<>(2);
+            bus.setProperty(CONSTRUCTOR_PROXY_MAP, map);
+            property = map;
+        }
+        return (Map<Class<?>, Map<Class<?>, ThreadLocalProxy<?>>>)property;
+    }
+
+    private Map<Class<?>, Map<Field, ThreadLocalProxy<?>>> getFieldProxyMap(boolean create) {
+        return getProxyMap(FIELD_PROXY_MAP, create);
+    }
+
+    private Map<Class<?>, Map<Method, ThreadLocalProxy<?>>> getSetterProxyMap(boolean create) {
+        return getProxyMap(SETTER_PROXY_MAP, create);
+    }
+
+    private void findContextSetterMethods(Class<?> cls, Object provider) {
+
+        for (Method m : cls.getMethods()) {
+
+            if (!m.getName().startsWith("set") || m.getParameterTypes().length != 1) {
+                continue;
+            }
+            for (Annotation a : m.getAnnotations()) {
+                if (a.annotationType() == Context.class) {
+                    checkContextMethod(m, provider);
+                    break;
+                }
+            }
+        }
+        Class<?>[] interfaces = cls.getInterfaces();
+        for (Class<?> i : interfaces) {
+            findContextSetterMethods(i, provider);
+        }
+        Class<?> superCls = cls.getSuperclass();
+        if (superCls != null && superCls != Object.class) {
+            findContextSetterMethods(superCls, provider);
+        }
+    }
+
+    private void checkContextMethod(Method m, Object provider) {
+        Class<?> type = m.getParameterTypes()[0];
+        if (type.isInterface() || type == Application.class) {
+            checkContextClass(type);
+            addContextMethod(type, m, provider);
+        }
+    }
+    private void checkContextClass(Class<?> type) {
+        if (!InjectionUtils.STANDARD_CONTEXT_CLASSES.contains(type.getName())) {
+            LOG.fine("Injecting a custom context " + type.getName()
+                     + ", ContextProvider is required for this type");
+        }
+    }
+
+    public Map<Class<?>, Method> getContextMethods() {
+        Map<Class<?>, Method> methods = contextMethods == null ? null : contextMethods.get(getServiceClass());
+        return methods == null ? Collections.emptyMap()
+                                      : Collections.unmodifiableMap(methods);
+    }
+
+    private void addContextMethod(Class<?> contextClass, Method m, Object provider) {
+        if (contextMethods == null) {
+            contextMethods = new HashMap<>();
+        }
+        addToMap(contextMethods, contextClass, m);
+        if (!InjectionUtils.VALUE_CONTEXTS.contains(m.getParameterTypes()[0].getName())) {
+            addToMap(getSetterProxyMap(true), m, getMethodThreadLocalProxy(m, provider));
+        }
+    }
+
+    public boolean isRoot() {
+        return root;
+    }
+
+    public Class<?> getResourceClass() {
+        return resourceClass;
+    }
+
+    public List<Field> getContextFields() {
+        return getList(contextFields);
+    }
+
+    public ThreadLocalProxy<?> getContextFieldProxy(Field f) {
+        return getProxy(getFieldProxyMap(true), f);
+    }
+
+    public ThreadLocalProxy<?> getContextSetterProxy(Method m) {
+        return getProxy(getSetterProxyMap(true), m);
+    }
+
+    public abstract boolean isSingleton();
+
+    @SuppressWarnings("rawtypes")
+    public static void clearAllMaps() {
+        Bus bus = BusFactory.getThreadDefaultBus(false);
+        if (bus != null) {
+            Object property = bus.getProperty(FIELD_PROXY_MAP);
+            if (property != null) {
+                ((Map)property).clear();
+            }
+            property = bus.getProperty(SETTER_PROXY_MAP);
+            if (property != null) {
+                ((Map)property).clear();
+            }
+            property = bus.getProperty(CONSTRUCTOR_PROXY_MAP);
+            if (property != null) {
+                ((Map)property).clear();
+            }
+        }
+    }
+
+    public void clearThreadLocalProxies() {
+        clearProxies(getFieldProxyMap(false));
+        clearProxies(getSetterProxyMap(false));
+        clearProxies(getConstructorProxyMap());
+    }
+
+    private <T> void clearProxies(Map<Class<?>, Map<T, ThreadLocalProxy<?>>> tlps) {
+        Map<T, ThreadLocalProxy<?>> proxies = tlps == null ? null : tlps.get(getServiceClass());
+        if (proxies == null) {
+            return;
+        }
+        for (ThreadLocalProxy<?> tlp : proxies.values()) {
+            if (tlp != null) {
+                tlp.remove();
+            }
+        }
+    }
+
+    private Map<Class<?>, List<Field>> addContextField(Map<Class<?>, List<Field>> theFields, Field f) {
+        if (theFields == null) {
+            theFields = new HashMap<>();
+        }
+
+        List<Field> fields = theFields.get(serviceClass);
+        if (fields == null) {
+            fields = new ArrayList<>();
+            theFields.put(serviceClass, fields);
+        }
+        if (!fields.contains(f)) {
+            fields.add(f);
+        }
+        return theFields;
+    }
+
+    private <T, V> void addToMap(Map<Class<?>, Map<T, V>> proxyMap,
+                                 T f,
+                                 V proxy) {
+        Map<T, V> proxies = proxyMap.get(serviceClass);
+        if (proxies == null) {
+            proxies = new ConcurrentHashMap<>();
+            proxyMap.put(serviceClass, proxies);
+        }
+        if (!proxies.containsKey(f)) {
+            proxies.put(f, proxy);
+        }
+    }
+
+    private List<Field> getList(Map<Class<?>, List<Field>> fields) {
+        List<Field> ret = fields == null ? null : fields.get(getServiceClass());
+        if (ret != null) {
+            ret = Collections.unmodifiableList(ret);
+        } else {
+            ret = Collections.emptyList();
+        }
+        return ret;
+    }
+
+    private <T> ThreadLocalProxy<?> getProxy(Map<Class<?>, Map<T, ThreadLocalProxy<?>>> proxies,
+                                             T key) {
+
+        Map<?, ThreadLocalProxy<?>> theMap = proxies == null ? null : proxies.get(getServiceClass());
+        ThreadLocalProxy<?> ret = null;
+        if (theMap != null) {
+            ret = theMap.get(key);
+        }
+        return ret;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/model/ClassResourceInfo.java b/transform/src/patch/java/org/apache/cxf/jaxrs/model/ClassResourceInfo.java
new file mode 100644
index 0000000..a69cac1
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/model/ClassResourceInfo.java
@@ -0,0 +1,366 @@
+/**
+ * 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.model;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.common.util.ClassHelper;
+import org.apache.cxf.jaxrs.lifecycle.ResourceProvider;
+import org.apache.cxf.jaxrs.provider.ServerProviderFactory;
+import org.apache.cxf.jaxrs.utils.AnnotationUtils;
+import org.apache.cxf.jaxrs.utils.InjectionUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.jaxrs.utils.ResourceUtils;
+import org.apache.cxf.message.Message;
+
+public class ClassResourceInfo extends BeanResourceInfo {
+
+    private URITemplate uriTemplate;
+    private MethodDispatcher methodDispatcher;
+    private ResourceProvider resourceProvider;
+    private ConcurrentHashMap<SubresourceKey, ClassResourceInfo> subResources
+        = new ConcurrentHashMap<>();
+
+    private boolean enableStatic;
+    private boolean createdFromModel;
+    private String consumesTypes;
+    private String producesTypes;
+    private List<MediaType> defaultConsumes = Collections.singletonList(JAXRSUtils.ALL_TYPES);
+    private List<MediaType> defaultProduces = Collections.singletonList(JAXRSUtils.ALL_TYPES);
+    private Set<String> nameBindings = Collections.emptySet();
+    private ClassResourceInfo parent;
+    private Set<String> injectedSubInstances = new HashSet<>();
+    public ClassResourceInfo(ClassResourceInfo cri) {
+        super(cri.getBus());
+        if (cri.isCreatedFromModel() && !InjectionUtils.isConcreteClass(cri.getServiceClass())) {
+            this.root = cri.root;
+            this.serviceClass = cri.serviceClass;
+            this.uriTemplate = cri.uriTemplate;
+            this.methodDispatcher = new MethodDispatcher(cri.methodDispatcher, this);
+            this.subResources = cri.subResources;
+            //CHECKSTYLE:OFF
+            this.paramFields = cri.paramFields;
+            this.paramMethods = cri.paramMethods;
+            //CHECKSTYLE:ON
+            this.enableStatic = true;
+            this.nameBindings = cri.nameBindings;
+            this.parent = cri.parent;
+        } else {
+            throw new IllegalArgumentException();
+        }
+
+    }
+
+    public ClassResourceInfo(Class<?> theResourceClass, Class<?> theServiceClass,
+                             boolean theRoot, boolean enableStatic, Bus bus) {
+        super(theResourceClass, theServiceClass, theRoot, theRoot || enableStatic, bus);
+        this.enableStatic = enableStatic;
+        if (resourceClass != null) {
+            nameBindings = AnnotationUtils.getNameBindings(bus, serviceClass);
+        }
+    }
+    //CHECKSTYLE:OFF
+    public ClassResourceInfo(Class<?> theResourceClass, Class<?> theServiceClass,
+                             boolean theRoot, boolean enableStatic, Bus bus, List<MediaType> defaultProduces,
+                             List<MediaType> defaultConsumes) {
+    //CHECKSTYLE:ON
+        this(theResourceClass, theServiceClass, theRoot, enableStatic, bus);
+        if (defaultProduces != null) {
+            this.defaultProduces = defaultProduces;
+        }
+        if (defaultConsumes != null) {
+            this.defaultConsumes = defaultConsumes;
+        }
+    }
+    public ClassResourceInfo(Class<?> theResourceClass, Class<?> theServiceClass,
+                             boolean theRoot, boolean enableStatic, boolean createdFromModel, Bus bus) {
+        this(theResourceClass, theServiceClass, theRoot, enableStatic, bus);
+        this.createdFromModel = createdFromModel;
+    }
+    //CHECKSTYLE:OFF
+    public ClassResourceInfo(Class<?> theResourceClass, Class<?> theServiceClass,
+                             boolean theRoot, boolean enableStatic, boolean createdFromModel,
+                             String consumesTypes, String producesTypes, Bus bus) {
+    //CHECKSTYLE:ON
+        this(theResourceClass, theServiceClass, theRoot, enableStatic, createdFromModel, bus);
+        this.consumesTypes = consumesTypes;
+        this.producesTypes = producesTypes;
+    }
+
+    // The following constructors are used by tests only
+    public ClassResourceInfo(Class<?> theResourceClass) {
+        this(theResourceClass, true);
+    }
+
+    public ClassResourceInfo(Class<?> theResourceClass, boolean theRoot) {
+        this(theResourceClass, theResourceClass, theRoot);
+    }
+
+    public ClassResourceInfo(Class<?> theResourceClass, Class<?> theServiceClass) {
+        this(theResourceClass, theServiceClass, false);
+    }
+
+    public ClassResourceInfo(Class<?> theResourceClass, Class<?> theServiceClass, boolean theRoot) {
+        this(theResourceClass, theServiceClass, theRoot, false, BusFactory.getDefaultBus(true));
+    }
+
+    public ClassResourceInfo findResource(Class<?> typedClass, Class<?> instanceClass) {
+        instanceClass = enableStatic ? typedClass : instanceClass;
+        SubresourceKey key = new SubresourceKey(typedClass, instanceClass);
+        return subResources.get(key);
+    }
+
+    @Override
+    public boolean contextsAvailable() {
+        // avoid re-injecting the contexts if the root acts as subresource
+        return super.contextsAvailable() && (isRoot() || parent != null);
+    }
+
+    public ClassResourceInfo getSubResource(Class<?> typedClass, Class<?> instanceClass) {
+        return getSubResource(typedClass, instanceClass, null, enableStatic, null);
+    }
+
+    public ClassResourceInfo getSubResource(Class<?> typedClass, Class<?> instanceClass, Object instance) {
+        instanceClass = enableStatic && typedClass != Object.class ? typedClass : instanceClass;
+        return getSubResource(typedClass, instanceClass, instance, enableStatic, JAXRSUtils.getCurrentMessage());
+    }
+
+    public ClassResourceInfo getSubResource(Class<?> typedClass,
+                                            Class<?> instanceClass,
+                                            Object instance,
+                                            boolean resolveContexts,
+                                            Message message) {
+
+        SubresourceKey key = new SubresourceKey(typedClass, instanceClass);
+        ClassResourceInfo cri = subResources.get(key);
+        if (cri == null) {
+            cri = ResourceUtils.createClassResourceInfo(typedClass, instanceClass, this, false, resolveContexts,
+                                                        getBus());
+            if (cri != null) {
+                if (message != null) {
+                    cri.initBeanParamInfo(ServerProviderFactory.getInstance(message));
+                }
+                subResources.putIfAbsent(key, cri);
+            }
+        }
+        // this branch will run only if ResourceContext is used
+        // or static resolution is enabled for subresources initialized
+        // from within singleton root resources (not default)
+        if (resolveContexts && cri != null && cri.isSingleton() && instance != null && cri.contextsAvailable()) {
+            synchronized (this) {
+                if (!injectedSubInstances.contains(instance.toString())) {
+                    Application app = null;
+                    if (message != null) {
+                        ProviderInfo<?> appProvider =
+                            (ProviderInfo<?>)message.getExchange().getEndpoint().get(Application.class.getName());
+                        if (appProvider != null) {
+                            app = (Application)appProvider.getProvider();
+                        }
+                    }
+                    InjectionUtils.injectContextProxiesAndApplication(cri, instance, app, null);
+                    injectedSubInstances.add(instance.toString());
+                }
+            }
+        }
+
+        return cri;
+    }
+
+    public void addSubClassResourceInfo(ClassResourceInfo cri) {
+        subResources.putIfAbsent(new SubresourceKey(cri.getResourceClass(),
+                                            cri.getServiceClass()),
+                                 cri);
+    }
+
+    public Collection<ClassResourceInfo> getSubResources() {
+        return Collections.unmodifiableCollection(subResources.values());
+    }
+
+    public Set<String> getNameBindings() {
+        if (parent == null) {
+            return nameBindings;
+        }
+        return parent.getNameBindings();
+    }
+
+    public void setNameBindings(Set<String> names) {
+        nameBindings = names;
+    }
+
+    public Set<String> getAllowedMethods() {
+        Set<String> methods = new HashSet<>();
+        for (OperationResourceInfo o : methodDispatcher.getOperationResourceInfos()) {
+            String method = o.getHttpMethod();
+            if (method != null) {
+                methods.add(method);
+            }
+        }
+        return methods;
+    }
+
+
+
+    public URITemplate getURITemplate() {
+        return uriTemplate;
+    }
+
+    public void setURITemplate(URITemplate u) {
+        uriTemplate = u;
+    }
+
+    public MethodDispatcher getMethodDispatcher() {
+        return methodDispatcher;
+    }
+
+    public void setMethodDispatcher(MethodDispatcher md) {
+        methodDispatcher = md;
+    }
+
+    public boolean hasSubResources() {
+        return !subResources.isEmpty();
+    }
+
+
+    public boolean isCreatedFromModel() {
+        return createdFromModel;
+    }
+
+    public ResourceProvider getResourceProvider() {
+        return resourceProvider;
+    }
+
+    public void setResourceProvider(ResourceProvider rp) {
+        resourceProvider = rp;
+    }
+
+    public List<MediaType> getProduceMime() {
+        if (producesTypes != null) {
+            return JAXRSUtils.parseMediaTypes(producesTypes);
+        }
+        Produces produces = AnnotationUtils.getClassAnnotation(getServiceClass(), Produces.class);
+        if (produces != null || parent == null) {
+            return JAXRSUtils.getProduceTypes(produces, defaultProduces);
+        }
+        return parent.getProduceMime();
+    }
+
+    public List<MediaType> getConsumeMime() {
+        if (consumesTypes != null) {
+            return JAXRSUtils.parseMediaTypes(consumesTypes);
+        }
+        Consumes consumes = AnnotationUtils.getClassAnnotation(getServiceClass(), Consumes.class);
+        if (consumes != null || parent == null) {
+            return JAXRSUtils.getConsumeTypes(consumes, defaultConsumes);
+        }
+        return parent.getConsumeMime();
+    }
+
+    public Path getPath() {
+        return AnnotationUtils.getClassAnnotation(getServiceClass(), Path.class);
+    }
+
+    @Override
+    public boolean isSingleton() {
+        if (parent == null) {
+            return resourceProvider != null && resourceProvider.isSingleton();
+        }
+        return parent.isSingleton();
+    }
+
+    public void setParent(ClassResourceInfo parent) {
+        this.parent = parent;
+    }
+
+    public ClassResourceInfo getParent() {
+        return parent;
+    }
+
+    public void initBeanParamInfo(ServerProviderFactory factory) {
+        if (factory != null) {
+            Set<OperationResourceInfo> oris = getMethodDispatcher().getOperationResourceInfos();
+            for (OperationResourceInfo ori : oris) {
+                List<Parameter> params = ori.getParameters();
+                for (Parameter param : params) {
+                    if (param.getType() == ParameterType.BEAN) {
+                        Class<?> cls = ori.getMethodToInvoke().getParameterTypes()[param.getIndex()];
+                        BeanParamInfo bpi = new BeanParamInfo(cls, getBus());
+                        factory.addBeanParamInfo(bpi);
+                    }
+                }
+            }
+            List<Method> methods = super.getParameterMethods();
+            for (Method m : methods) {
+                if (m.getAnnotation(BeanParam.class) != null) {
+                    BeanParamInfo bpi = new BeanParamInfo(m.getParameterTypes()[0], getBus());
+                    factory.addBeanParamInfo(bpi);
+                }
+            }
+            List<Field> fields = super.getParameterFields();
+            for (Field f : fields) {
+                if (f.getAnnotation(BeanParam.class) != null) {
+                    BeanParamInfo bpi = new BeanParamInfo(f.getType(), getBus());
+                    factory.addBeanParamInfo(bpi);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void clearThreadLocalProxies() {
+        super.clearThreadLocalProxies();
+        if (!injectedSubInstances.isEmpty()) {
+            for (ClassResourceInfo sub : subResources.values()) {
+                if (sub != this) {
+                    sub.clearThreadLocalProxies();
+                }
+            }
+        }
+    }
+
+    public void injectContexts(Object resourceObject, OperationResourceInfo ori, Message inMessage) {
+        final boolean contextsAvailable = contextsAvailable();
+        final boolean paramsAvailable = paramsAvailable();
+        if (contextsAvailable || paramsAvailable) {
+            Object realResourceObject = ClassHelper.getRealObject(resourceObject);
+            if (paramsAvailable) {
+                JAXRSUtils.injectParameters(ori, this, realResourceObject, inMessage);
+            }
+            if (contextsAvailable) {
+                InjectionUtils.injectContexts(realResourceObject, this, inMessage);
+            }
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/model/URITemplate.java b/transform/src/patch/java/org/apache/cxf/jaxrs/model/URITemplate.java
new file mode 100644
index 0000000..85fdd7e
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/model/URITemplate.java
@@ -0,0 +1,627 @@
+/**
+ * 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.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.PathSegment;
+
+import org.apache.cxf.common.util.SystemPropertyAction;
+import org.apache.cxf.jaxrs.utils.HttpUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+
+public final class URITemplate {
+
+    public static final String TEMPLATE_PARAMETERS = "jaxrs.template.parameters";
+    public static final String URI_TEMPLATE = "jaxrs.template.uri";
+    public static final String LIMITED_REGEX_SUFFIX = "(/.*)?";
+    public static final String FINAL_MATCH_GROUP = "FINAL_MATCH_GROUP";
+    private static final String DEFAULT_PATH_VARIABLE_REGEX = "([^/]+?)";
+    private static final String CHARACTERS_TO_ESCAPE = ".*+$()";
+    private static final String SLASH = "/";
+    private static final String SLASH_QUOTE = "/;";
+    private static final int MAX_URI_TEMPLATE_CACHE_SIZE = 
+        SystemPropertyAction.getInteger("org.apache.cxf.jaxrs.max_uri_template_cache_size", 2000);
+    private static final Map<String, URITemplate> URI_TEMPLATE_CACHE = new ConcurrentHashMap<>();
+    
+    private final String template;
+    private final List<String> variables = new ArrayList<>();
+    private final List<String> customVariables = new ArrayList<>();
+    private final Pattern templateRegexPattern;
+    private final String literals;
+    private final List<UriChunk> uriChunks;
+
+    public URITemplate(String theTemplate) {
+        template = theTemplate;
+        StringBuilder literalChars = new StringBuilder();
+        StringBuilder patternBuilder = new StringBuilder();
+        CurlyBraceTokenizer tok = new CurlyBraceTokenizer(template);
+        uriChunks = new ArrayList<>();
+        while (tok.hasNext()) {
+            String templatePart = tok.next();
+            UriChunk chunk = UriChunk.createUriChunk(templatePart);
+            uriChunks.add(chunk);
+            if (chunk instanceof Literal) {
+                String encodedValue = HttpUtils.encodePartiallyEncoded(chunk.getValue(), false);
+                String substr = escapeCharacters(encodedValue);
+                literalChars.append(substr);
+                patternBuilder.append(substr);
+            } else if (chunk instanceof Variable) {
+                Variable var = (Variable)chunk;
+                variables.add(var.getName());
+                String pattern = var.getPattern();
+                if (pattern != null) {
+                    customVariables.add(var.getName());
+                    // Add parenthesis to the pattern to identify a regex in the pattern, 
+                    // however do not add them if they already exist since that will cause the Matcher
+                    // to create extraneous values.  Parens identify a group so multiple parens would
+                    // indicate multiple groups.
+                    if (pattern.startsWith("(") && pattern.endsWith(")")) {
+                        patternBuilder.append(pattern);
+                    } else {
+                        patternBuilder.append('(');
+                        patternBuilder.append(pattern);
+                        patternBuilder.append(')');
+                    }
+                } else {
+                    patternBuilder.append(DEFAULT_PATH_VARIABLE_REGEX);
+                }
+            }
+        }
+        literals = literalChars.toString();
+
+        int endPos = patternBuilder.length() - 1;
+        boolean endsWithSlash = (endPos >= 0) && patternBuilder.charAt(endPos) == '/';
+        if (endsWithSlash) {
+            patternBuilder.deleteCharAt(endPos);
+        }
+        patternBuilder.append(LIMITED_REGEX_SUFFIX);
+
+        templateRegexPattern = Pattern.compile(patternBuilder.toString());
+    }
+
+    public String getLiteralChars() {
+        return literals;
+    }
+
+    public String getValue() {
+        return template;
+    }
+
+    public String getPatternValue() {
+        return templateRegexPattern.toString();
+    }
+
+    /**
+     * List of all variables in order of appearance in template.
+     *
+     * @return unmodifiable list of variable names w/o patterns, e.g. for "/foo/{v1:\\d}/{v2}" returned list
+     *         is ["v1","v2"].
+     */
+    public List<String> getVariables() {
+        return Collections.unmodifiableList(variables);
+    }
+
+    /**
+     * List of variables with patterns (regexps). List is subset of elements from {@link #getVariables()}.
+     *
+     * @return unmodifiable list of variables names w/o patterns.
+     */
+    public List<String> getCustomVariables() {
+        return Collections.unmodifiableList(customVariables);
+    }
+
+    private static String escapeCharacters(String expression) {
+
+        int length = expression.length();
+        int i = 0;
+        char ch = ' ';
+        for (; i < length; ++i) {
+            ch = expression.charAt(i);
+            if (isReservedCharacter(ch)) {
+                break;
+            }
+        }
+
+        if (i == length) {
+            return expression;
+        }
+
+        // Allows for up to 8 escaped characters before we start creating more
+        // StringBuilders. 8 is an arbitrary limit, but it seems to be
+        // sufficient in most cases.
+        StringBuilder sb = new StringBuilder(length + 8);
+        sb.append(expression, 0, i);
+        sb.append('\\');
+        sb.append(ch);
+        ++i;
+        for (; i < length; ++i) {
+            ch = expression.charAt(i);
+            if (isReservedCharacter(ch)) {
+                sb.append('\\');
+            }
+            sb.append(ch);
+        }
+        return sb.toString();
+    }
+
+    private static boolean isReservedCharacter(char ch) {
+        return CHARACTERS_TO_ESCAPE.indexOf(ch) != -1;
+    }
+
+    public boolean match(String uri, MultivaluedMap<String, String> templateVariableToValue) {
+
+        if (uri == null) {
+            return (templateRegexPattern == null) ? true : false;
+        }
+
+        if (templateRegexPattern == null) {
+            return false;
+        }
+
+        Matcher m = templateRegexPattern.matcher(uri);
+        if (!m.matches() || template.equals(SLASH) && uri.startsWith(SLASH_QUOTE)) {
+            if (uri.contains(";")) {
+                // we might be trying to match one or few path segments
+                // containing matrix
+                // parameters against a clear path segment as in @Path("base").
+                List<PathSegment> pList = JAXRSUtils.getPathSegments(template, false);
+                List<PathSegment> uList = JAXRSUtils.getPathSegments(uri, false);
+                StringBuilder sb = new StringBuilder();
+                for (int i = 0; i < uList.size(); i++) {
+                    final String segment;
+                    if (pList.size() > i && pList.get(i).getPath().indexOf('{') == -1) {
+                        segment = uList.get(i).getPath();
+                    } else {
+                        segment = HttpUtils.fromPathSegment(uList.get(i));
+                    }
+                    if (!segment.isEmpty()) {
+                        sb.append(SLASH);
+                    }
+                    sb.append(segment);
+                }
+                uri = sb.toString();
+                if (uri.isEmpty()) {
+                    uri = SLASH;
+                }
+                m = templateRegexPattern.matcher(uri);
+                if (!m.matches()) {
+                    return false;
+                }
+            } else {
+                return false;
+            }
+        }
+
+        // Assign the matched template values to template variables
+        int groupCount = m.groupCount();
+
+        int i = 1;
+        for (String name : variables) {
+            while (i <= groupCount) {
+                String value = m.group(i++);
+                if ((value == null || value.length() == 0 && i < groupCount)
+                    && variables.size() + 1 < groupCount) {
+                    continue;
+                }
+                templateVariableToValue.add(name, value);
+                break;
+            }
+        }
+        // The right hand side value, might be used to further resolve
+        // sub-resources.
+
+        String finalGroup = i > groupCount ? SLASH : m.group(groupCount);
+        if (finalGroup == null || finalGroup.startsWith(SLASH_QUOTE)) {
+            finalGroup = SLASH;
+        }
+
+        templateVariableToValue.putSingle(FINAL_MATCH_GROUP, finalGroup);
+
+        return true;
+    }
+
+    /**
+     * Substitutes template variables with listed values. List of values is counterpart for
+     * {@link #getVariables() list of variables}. When list of value is shorter than variables substitution
+     * is partial. When variable has pattern, value must fit to pattern, otherwise
+     * {@link IllegalArgumentException} is thrown.
+     * <p>
+     * Example1: for template "/{a}/{b}/{a}" {@link #getVariables()} returns "[a, b, a]"; providing here list
+     * of value "[foo, bar, baz]" results with "/foo/bar/baz".
+     * <p>
+     * Example2: for template "/{a}/{b}/{a}" providing list of values "[foo]" results with "/foo/{b}/{a}".
+     *
+     * @param values values for variables
+     * @return template with bound variables.
+     * @throws IllegalArgumentException when values is null, any value does not match pattern etc.
+     */
+    public String substitute(List<String> values) throws IllegalArgumentException {
+        if (values == null) {
+            throw new IllegalArgumentException("values is null");
+        }
+        Iterator<String> iter = values.iterator();
+        StringBuilder sb = new StringBuilder();
+        for (UriChunk chunk : uriChunks) {
+            if (chunk instanceof Variable) {
+                Variable var = (Variable)chunk;
+                if (iter.hasNext()) {
+                    String value = iter.next();
+                    if (!var.matches(value)) {
+                        throw new IllegalArgumentException("Value '" + value + "' does not match variable "
+                                                           + var.getName() + " with pattern "
+                                                           + var.getPattern());
+                    }
+                    sb.append(value);
+                } else {
+                    sb.append(var);
+                }
+            } else {
+                sb.append(chunk);
+            }
+        }
+        return sb.toString();
+    }
+
+    String substitute(Map<String, ? extends Object> valuesMap) throws IllegalArgumentException {
+        return this.substitute(valuesMap, Collections.<String>emptySet(), false);
+    }
+
+    /**
+     * Substitutes template variables with mapped values. Variables are mapped to values; if not all variables
+     * are bound result will still contain variables. Note that all variables with the same name are replaced
+     * by one value.
+     * <p>
+     * Example: for template "/{a}/{b}/{a}" {@link #getVariables()} returns "[a, b, a]"; providing here
+     * mapping "[a: foo, b: bar]" results with "/foo/bar/foo" (full substitution) and for mapping "[b: baz]"
+     * result is "{a}/baz/{a}" (partial substitution).
+     *
+     * @param valuesMap map variables to their values; on each value Object.toString() is called.
+     * @return template with bound variables.
+     */
+    public String substitute(Map<String, ? extends Object> valuesMap,
+                             Set<String> encodePathSlashVars,
+                             boolean allowUnresolved) throws IllegalArgumentException {
+        if (valuesMap == null) {
+            throw new IllegalArgumentException("valuesMap is null");
+        }
+        StringBuilder sb = new StringBuilder();
+        for (UriChunk chunk : uriChunks) {
+            if (chunk instanceof Variable) {
+                Variable var = (Variable)chunk;
+                Object value = valuesMap.get(var.getName());
+                if (value != null) {
+                    String sval = value.toString();
+                    if (!var.matches(sval)) {
+                        throw new IllegalArgumentException("Value '" + sval + "' does not match variable "
+                                                           + var.getName() + " with pattern "
+                                                           + var.getPattern());
+                    }
+                    if (encodePathSlashVars.contains(var.getName())) {
+                        sval = sval.replaceAll("/", "%2F");
+                    }
+                    sb.append(sval);
+                } else if (allowUnresolved) {
+                    sb.append(chunk);
+                } else {
+                    throw new IllegalArgumentException("Template variable " + var.getName()
+                                                       + " has no matching value");
+                }
+            } else {
+                sb.append(chunk);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Encoded literal characters surrounding template variables,
+     * ex. "a {id} b" will be encoded to "a%20{id}%20b"
+     * @return encoded value
+     */
+    public String encodeLiteralCharacters(boolean isQuery) {
+        final float encodedRatio = 1.5f;
+        StringBuilder sb = new StringBuilder((int)(encodedRatio * template.length()));
+        for (UriChunk chunk : uriChunks) {
+            String val = chunk.getValue();
+            if (chunk instanceof Literal) {
+                sb.append(HttpUtils.encodePartiallyEncoded(val, isQuery));
+            } else {
+                sb.append(val);
+            }
+        }
+        return sb.toString();
+    }
+
+    public static URITemplate createTemplate(Path path) {
+
+        return createTemplate(path == null ? null : path.value());
+    }
+
+    public static URITemplate createTemplate(String pathValue) {
+        if (pathValue == null) {
+            pathValue = "/";
+        } else if (!pathValue.startsWith("/")) {
+            pathValue = "/" + pathValue;
+        }
+        return createExactTemplate(pathValue);
+    }
+    
+    public static URITemplate createExactTemplate(String pathValue) {
+        URITemplate template = URI_TEMPLATE_CACHE.get(pathValue);
+        if (template == null) {
+            template = new URITemplate(pathValue);
+            if (URI_TEMPLATE_CACHE.size() >= MAX_URI_TEMPLATE_CACHE_SIZE) {
+                URI_TEMPLATE_CACHE.clear();
+            }
+            URI_TEMPLATE_CACHE.put(pathValue, template);
+        }
+        return template;
+    }
+    
+    public static int compareTemplates(URITemplate t1, URITemplate t2) {
+        int l1 = t1.getLiteralChars().length();
+        int l2 = t2.getLiteralChars().length();
+        // descending order
+        int result = l1 < l2 ? 1 : l1 > l2 ? -1 : 0;
+        if (result == 0) {
+            int g1 = t1.getVariables().size();
+            int g2 = t2.getVariables().size();
+            // descending order
+            result = g1 < g2 ? 1 : g1 > g2 ? -1 : 0;
+            if (result == 0) {
+                int gCustom1 = t1.getCustomVariables().size();
+                int gCustom2 = t2.getCustomVariables().size();
+                result = gCustom1 < gCustom2 ? 1 : gCustom1 > gCustom2 ? -1 : 0;
+                if (result == 0) {
+                    result = t1.getPatternValue().compareTo(t2.getPatternValue());
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Stringified part of URI. Chunk is not URI segment; chunk can span over multiple URI segments or one URI
+     * segments can have multiple chunks. Chunk is used to decompose URI of {@link URITemplate} into literals
+     * and variables. Example: "foo/bar/{baz}{blah}" is decomposed into chunks: "foo/bar", "{baz}" and
+     * "{blah}".
+     */
+    private abstract static class UriChunk {
+        /**
+         * Creates object form string.
+         *
+         * @param uriChunk stringified uri chunk
+         * @return If param has variable form then {@link Variable} instance is created, otherwise chunk is
+         *         treated as {@link Literal}.
+         */
+        public static UriChunk createUriChunk(String uriChunk) {
+            if (uriChunk == null || "".equals(uriChunk)) {
+                throw new IllegalArgumentException("uriChunk is empty");
+            }
+            UriChunk uriChunkRepresentation = Variable.create(uriChunk);
+            if (uriChunkRepresentation == null) {
+                uriChunkRepresentation = Literal.create(uriChunk);
+            }
+            return uriChunkRepresentation;
+        }
+
+        public abstract String getValue();
+
+        @Override
+        public String toString() {
+            return getValue();
+        }
+    }
+
+    private static final class Literal extends UriChunk {
+        private String value;
+
+        private Literal() {
+            // empty constructor
+        }
+
+        public static Literal create(String uriChunk) {
+            if (uriChunk == null || "".equals(uriChunk)) {
+                throw new IllegalArgumentException("uriChunk is empty");
+            }
+            Literal literal = new Literal();
+            literal.value = uriChunk;
+            return literal;
+        }
+
+        @Override
+        public String getValue() {
+            return value;
+        }
+
+    }
+
+    /**
+     * Variable of URITemplate. Variable has either "{varname:pattern}" syntax or "{varname}".
+     */
+    private static final class Variable extends UriChunk {
+        private static final Pattern VARIABLE_PATTERN = Pattern.compile("(\\w[-\\w\\.]*[ ]*)(\\:(.+))?");
+        private String name;
+        private Pattern pattern;
+
+        private Variable() {
+            // empty constructor
+        }
+
+        /**
+         * Creates variable from stringified part of URI.
+         *
+         * @param uriChunk uriChunk chunk that depicts variable
+         * @return Variable if variable was successfully created; null if uriChunk was not a variable
+         */
+        public static Variable create(String uriChunk) {
+            Variable newVariable = new Variable();
+            if (uriChunk == null || "".equals(uriChunk)) {
+                return null;
+            }
+            if (CurlyBraceTokenizer.insideBraces(uriChunk)) {
+                uriChunk = CurlyBraceTokenizer.stripBraces(uriChunk).trim();
+                Matcher matcher = VARIABLE_PATTERN.matcher(uriChunk);
+                if (matcher.matches()) {
+                    newVariable.name = matcher.group(1).trim();
+                    if (matcher.group(2) != null && matcher.group(3) != null) {
+                        String patternExpression = matcher.group(3).trim();
+                        newVariable.pattern = Pattern.compile(patternExpression);
+                    }
+                    return newVariable;
+                }
+            }
+            return null;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getPattern() {
+            return pattern != null ? pattern.pattern() : null;
+        }
+
+        /**
+         * Checks whether value matches variable. If variable has pattern its checked against, otherwise true
+         * is returned.
+         *
+         * @param value value of variable
+         * @return true if value is valid for variable, false otherwise.
+         */
+        public boolean matches(String value) {
+            if (pattern == null) {
+                return true;
+            }
+            return pattern.matcher(value).matches();
+        }
+
+        @Override
+        public String getValue() {
+            if (pattern != null) {
+                return "{" + name + ":" + pattern + "}";
+            }
+            return "{" + name + "}";
+        }
+    }
+
+    /**
+     * Splits string into parts inside and outside curly braces. Nested curly braces are ignored and treated
+     * as part inside top-level curly braces. Example: string "foo{bar{baz}}blah" is split into three tokens,
+     * "foo","{bar{baz}}" and "blah". When closed bracket is missing, whole unclosed part is returned as one
+     * token, e.g.: "foo{bar" is split into "foo" and "{bar". When opening bracket is missing, closing
+     * bracket is ignored and taken as part of current token e.g.: "foo{bar}baz}blah" is split into "foo",
+     * "{bar}" and "baz}blah".
+     * <p>
+     * This is helper class for {@link URITemplate} that enables recurring literals appearing next to regular
+     * expressions e.g. "/foo/{zipcode:[0-9]{5}}/". Nested expressions with closed sections, like open-closed
+     * brackets causes expression to be out of regular grammar (is context-free grammar) which are not
+     * supported by Java regexp version.
+     */
+    static class CurlyBraceTokenizer {
+
+        private List<String> tokens = new ArrayList<>();
+        private int tokenIdx;
+
+        CurlyBraceTokenizer(String string) {
+            boolean outside = true;
+            int level = 0;
+            int lastIdx = 0;
+            int idx;
+            for (idx = 0; idx < string.length(); idx++) {
+                if (string.charAt(idx) == '{') {
+                    if (outside) {
+                        if (lastIdx < idx) {
+                            tokens.add(string.substring(lastIdx, idx));
+                        }
+                        lastIdx = idx;
+                        outside = false;
+                    } else {
+                        level++;
+                    }
+                } else if (string.charAt(idx) == '}' && !outside) {
+                    if (level > 0) {
+                        level--;
+                    } else {
+                        if (lastIdx < idx) {
+                            tokens.add(string.substring(lastIdx, idx + 1));
+                        }
+                        lastIdx = idx + 1;
+                        outside = true;
+                    }
+                }
+            }
+            if (lastIdx < idx) {
+                tokens.add(string.substring(lastIdx, idx));
+            }
+        }
+
+        /**
+         * Token is enclosed by curly braces.
+         *
+         * @param token
+         *            text to verify
+         * @return true if enclosed, false otherwise.
+         */
+        public static boolean insideBraces(String token) {
+            return token.charAt(0) == '{' && token.charAt(token.length() - 1) == '}';
+        }
+
+        /**
+         * Strips token from enclosed curly braces. If token is not enclosed method
+         * has no side effect.
+         *
+         * @param token
+         *            text to verify
+         * @return text stripped from curly brace begin-end pair.
+         */
+        public static String stripBraces(String token) {
+            if (insideBraces(token)) {
+                return token.substring(1, token.length() - 1);
+            }
+            return token;
+        }
+
+        public boolean hasNext() {
+            return tokens.size() > tokenIdx;
+        }
+
+        public String next() {
+            if (hasNext()) {
+                return tokens.get(tokenIdx++);
+            }
+            throw new IllegalStateException("no more elements");
+        }
+    }
+}
+
+
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/CachingMessageBodyReader.java b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/CachingMessageBodyReader.java
new file mode 100644
index 0000000..ecdc8f1
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/CachingMessageBodyReader.java
@@ -0,0 +1,100 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.jaxrs.provider;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.List;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
+
+@Provider
+public class CachingMessageBodyReader<T> extends AbstractCachingMessageProvider<T>
+    implements MessageBodyReader<T> {
+
+    private List<MessageBodyReader<T>> delegatingReaders;
+
+    public boolean isReadable(Class<?> type, Type gType, Annotation[] anns, MediaType mt) {
+        if (delegatingReaders != null) {
+            return getDelegatingReader(type, gType, anns, mt) != null;
+        }
+        return isProviderKeyNotSet();
+    }
+
+    private MessageBodyReader<T> getDelegatingReader(Class<?> type, Type gType, Annotation[] anns, MediaType mt) {
+        for (MessageBodyReader<T> reader: delegatingReaders) {
+            if (reader.isReadable(type, gType, anns, mt)) {
+                return reader;
+            }
+        }
+        return null;
+    }
+
+    public T readFrom(Class<T> type, Type gType, Annotation[] anns, MediaType mt,
+                        MultivaluedMap<String, String> theheaders, InputStream is)
+        throws IOException, WebApplicationException {
+        this.setObject(
+            getReader(type, gType, anns, mt).readFrom(type, gType, anns, mt, theheaders, is));
+        return getObject();
+    }
+
+
+    protected MessageBodyReader<T> getReader(Class<?> type, Type gType, Annotation[] anns, MediaType mt) {
+        if (delegatingReaders != null) {
+            return getDelegatingReader(type, gType, anns, mt);
+        }
+        final MessageBodyReader<T> r;
+
+        mc.put(ACTIVE_JAXRS_PROVIDER_KEY, this);
+        try {
+            @SuppressWarnings("unchecked")
+            Class<T> actualType = (Class<T>)type;
+            r = mc.getProviders().getMessageBodyReader(actualType, gType, anns, mt);
+        } finally {
+            mc.put(ACTIVE_JAXRS_PROVIDER_KEY, null);
+        }
+
+        if (r == null) {
+            org.apache.cxf.common.i18n.Message message =
+                new org.apache.cxf.common.i18n.Message("NO_MSG_READER", BUNDLE, type);
+            LOG.severe(message.toString());
+            throw ExceptionUtils.toNotAcceptableException(null, null);
+        }
+        return r;
+    }
+
+    public void setDelegatingReader(MessageBodyReader<T> reader) {
+        this.delegatingReaders = Collections.singletonList(reader);
+    }
+
+    public void setDelegatingReaders(List<MessageBodyReader<T>> readers) {
+        this.delegatingReaders = readers;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/CachingMessageBodyWriter.java b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/CachingMessageBodyWriter.java
new file mode 100644
index 0000000..21eab42
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/CachingMessageBodyWriter.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.jaxrs.provider;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.List;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
+
+@Provider
+public class CachingMessageBodyWriter<T> extends AbstractCachingMessageProvider<T>
+    implements MessageBodyWriter<T> {
+
+    private List<MessageBodyWriter<T>> delegatingWriters;
+
+    public long getSize(T t, Class<?> type, Type gType, Annotation[] anns, MediaType mediaType) {
+        return -1;
+    }
+
+    public boolean isWriteable(Class<?> type, Type gType, Annotation[] anns, MediaType mt) {
+        if (delegatingWriters != null) {
+            return getDelegatingWriter(type, gType, anns, mt) != null;
+        }
+        return isProviderKeyNotSet();
+    }
+
+    private MessageBodyWriter<T> getDelegatingWriter(Class<?> type, Type gType, Annotation[] anns, MediaType mt) {
+        for (MessageBodyWriter<T> writer: delegatingWriters) {
+            if (writer.isWriteable(type, gType, anns, mt)) {
+                return writer;
+            }
+        }
+        return null;
+    }
+
+    public void writeTo(T obj, Class<?> type, Type gType, Annotation[] anns, MediaType mt,
+                        MultivaluedMap<String, Object> theheaders, OutputStream os)
+        throws IOException, WebApplicationException {
+        this.setObject(obj);
+        getWriter(type, gType, anns, mt).writeTo(getObject(), type, gType, anns, mt, theheaders, os);
+    }
+
+
+    protected MessageBodyWriter<T> getWriter(Class<?> type, Type gType, Annotation[] anns, MediaType mt) {
+        if (delegatingWriters != null) {
+            return getDelegatingWriter(type, gType, anns, mt);
+        }
+        MessageBodyWriter<T> w;
+
+        mc.put(ACTIVE_JAXRS_PROVIDER_KEY, this);
+        try {
+            @SuppressWarnings("unchecked")
+            Class<T> actualType = (Class<T>)type;
+            w = mc.getProviders().getMessageBodyWriter(actualType, gType, anns, mt);
+        } finally {
+            mc.put(ACTIVE_JAXRS_PROVIDER_KEY, null);
+        }
+
+        if (w == null) {
+            org.apache.cxf.common.i18n.Message message =
+                new org.apache.cxf.common.i18n.Message("NO_MSG_WRITER", BUNDLE, type);
+            LOG.severe(message.toString());
+            throw ExceptionUtils.toInternalServerErrorException(null, null);
+        }
+        return w;
+    }
+
+    public void setDelegatingWriter(MessageBodyWriter<T> writer) {
+        this.delegatingWriters = Collections.singletonList(writer);
+    }
+
+    public void setDelegatingWriters(List<MessageBodyWriter<T>> writers) {
+        this.delegatingWriters = writers;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/DataSourceProvider.java b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/DataSourceProvider.java
new file mode 100644
index 0000000..efecac4
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/DataSourceProvider.java
@@ -0,0 +1,110 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.jaxrs.provider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.logging.Logger;
+
+import javax.activation.DataHandler;
+import javax.activation.DataSource;
+import javax.activation.FileDataSource;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.jaxrs.ext.multipart.InputStreamDataSource;
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+
+@Provider
+public class DataSourceProvider<T> implements MessageBodyReader<T>, MessageBodyWriter<T> {
+    protected static final Logger LOG = LogUtils.getL7dLogger(DataSourceProvider.class);
+    private boolean useDataSourceContentType;
+
+    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mt) {
+        return isSupported(type);
+    }
+
+    public T readFrom(Class<T> cls, Type genericType, Annotation[] annotations,
+                               MediaType type,
+                               MultivaluedMap<String, String> headers, InputStream is)
+        throws IOException {
+
+        final DataSource ds;
+        if (cls == FileDataSource.class) {
+            File file = new BinaryDataProvider<File>().readFrom(File.class, File.class, annotations, type, headers, is);
+            ds = new FileDataSource(file);
+        } else if (cls == DataSource.class || cls == DataHandler.class) {
+            ds = new InputStreamDataSource(is, type.toString());
+        } else {
+            LOG.warning("Unsupported DataSource class: " + cls.getName());
+            throw ExceptionUtils.toWebApplicationException(null, null);
+        }
+        return cls.cast(DataSource.class.isAssignableFrom(cls) ? ds : new DataHandler(ds));
+    }
+
+    public long getSize(T t, Class<?> type, Type genericType, Annotation[] annotations,
+                        MediaType mt) {
+        return -1;
+    }
+
+    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mt) {
+        return isSupported(type);
+    }
+
+    private static boolean isSupported(Class<?> type) {
+        return DataSource.class.isAssignableFrom(type) || DataHandler.class.isAssignableFrom(type);
+    }
+
+    public void writeTo(T src, Class<?> cls, Type genericType, Annotation[] annotations,
+                        MediaType type, MultivaluedMap<String, Object> headers, OutputStream os)
+        throws IOException {
+        DataSource ds = DataSource.class.isAssignableFrom(cls)
+            ? (DataSource)src : ((DataHandler)src).getDataSource();
+        if (useDataSourceContentType) {
+            setContentTypeIfNeeded(type, headers, ds.getContentType());
+        }
+        IOUtils.copyAndCloseInput(ds.getInputStream(), os);
+    }
+
+    private void setContentTypeIfNeeded(MediaType type,
+        MultivaluedMap<String, Object> headers, String ct) {
+
+        if (!StringUtils.isEmpty(ct) && !type.equals(JAXRSUtils.toMediaType(ct))) {
+            headers.putSingle("Content-Type", ct);
+        }
+    }
+
+    public void setUseDataSourceContentType(boolean useDataSourceContentType) {
+        this.useDataSourceContentType = useDataSourceContentType;
+    }
+
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/FormEncodingProvider.java b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/FormEncodingProvider.java
new file mode 100644
index 0000000..0344592
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/FormEncodingProvider.java
@@ -0,0 +1,228 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.jaxrs.provider;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Encoded;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.cxf.jaxrs.ext.MessageContext;
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
+import org.apache.cxf.jaxrs.impl.MetadataMap;
+import org.apache.cxf.jaxrs.utils.AnnotationUtils;
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
+import org.apache.cxf.jaxrs.utils.FormUtils;
+import org.apache.cxf.jaxrs.utils.HttpUtils;
+import org.apache.cxf.jaxrs.utils.multipart.AttachmentUtils;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.PhaseInterceptorChain;
+
+@Produces({"application/x-www-form-urlencoded", "multipart/form-data" })
+@Consumes({"application/x-www-form-urlencoded", "multipart/form-data" })
+@Provider
+public class FormEncodingProvider<T> extends AbstractConfigurableProvider
+    implements MessageBodyReader<T>, MessageBodyWriter<T> {
+
+    private FormValidator validator;
+    @Context private MessageContext mc;
+    private String attachmentDir;
+    private String attachmentThreshold;
+    private String attachmentMaxSize;
+
+    private boolean expectEncoded;
+
+    public FormEncodingProvider() {
+
+    }
+
+    public FormEncodingProvider(boolean expectEncoded) {
+        this.expectEncoded = expectEncoded;
+    }
+
+    public void setExpectedEncoded(boolean expect) {
+        this.expectEncoded = expect;
+    }
+
+    public void setAttachmentDirectory(String dir) {
+        attachmentDir = dir;
+    }
+
+    public void setAttachmentThreshold(String threshold) {
+        attachmentThreshold = threshold;
+    }
+
+    public void setAttachmentMaxSize(String maxSize) {
+        attachmentMaxSize = maxSize;
+    }
+
+    public void setValidator(FormValidator formValidator) {
+        validator = formValidator;
+    }
+
+    public boolean isReadable(Class<?> type, Type genericType,
+                              Annotation[] annotations, MediaType mt) {
+        return isSupported(type, mt);
+    }
+
+    public T readFrom(
+        Class<T> clazz, Type genericType, Annotation[] annotations, MediaType mt,
+        MultivaluedMap<String, String> headers, InputStream is)
+        throws IOException {
+        if (is == null) {
+            return null;
+        }
+        try {
+            if (mt.isCompatible(MediaType.MULTIPART_FORM_DATA_TYPE)) {
+                MultipartBody body = AttachmentUtils.getMultipartBody(mc);
+                if (MultipartBody.class.isAssignableFrom(clazz)) {
+                    return clazz.cast(body);
+                } else if (Attachment.class.isAssignableFrom(clazz)) {
+                    return clazz.cast(body.getRootAttachment());
+                }
+            }
+
+            MultivaluedMap<String, String> params = createMap(clazz);
+            populateMap(params, annotations, is, mt, !keepEncoded(annotations));
+            validateMap(params);
+
+            persistParamsOnMessage(params);
+
+            return getFormObject(clazz, params);
+        } catch (WebApplicationException e) {
+            throw e;
+        } catch (Exception e) {
+            throw ExceptionUtils.toBadRequestException(e, null);
+        }
+    }
+
+    protected boolean keepEncoded(Annotation[] anns) {
+        return AnnotationUtils.getAnnotation(anns, Encoded.class) != null
+               || expectEncoded;
+    }
+
+    protected void persistParamsOnMessage(MultivaluedMap<String, String> params) {
+        Message message = PhaseInterceptorChain.getCurrentMessage();
+        if (message != null) {
+            message.put(FormUtils.FORM_PARAM_MAP, params);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    protected MultivaluedMap<String, String> createMap(Class<?> clazz) throws Exception {
+        if (clazz == MultivaluedMap.class || clazz == Form.class) {
+            return new MetadataMap<String, String>();
+        }
+        return (MultivaluedMap<String, String>)clazz.newInstance();
+    }
+
+    private T getFormObject(Class<T> clazz, MultivaluedMap<String, String> params) {
+        return clazz.cast(Form.class.isAssignableFrom(clazz) ? new Form(params) : params);
+    }
+
+    /**
+     * Retrieve map of parameters from the passed in message
+     */
+    protected void populateMap(MultivaluedMap<String, String> params,
+                               Annotation[] anns,
+                               InputStream is,
+                               MediaType mt,
+                               boolean decode) {
+        if (mt.isCompatible(MediaType.MULTIPART_FORM_DATA_TYPE)) {
+            MultipartBody body =
+                AttachmentUtils.getMultipartBody(mc, attachmentDir, attachmentThreshold, attachmentMaxSize);
+            FormUtils.populateMapFromMultipart(params, body, PhaseInterceptorChain.getCurrentMessage(),
+                                               decode);
+        } else {
+            String enc = HttpUtils.getEncoding(mt, StandardCharsets.UTF_8.name());
+
+            Object servletRequest = mc != null ? mc.getHttpServletRequest() : null;
+            if (servletRequest == null) {
+                FormUtils.populateMapFromString(params,
+                                                PhaseInterceptorChain.getCurrentMessage(),
+                                                FormUtils.readBody(is, enc),
+                                                enc,
+                                                decode);
+            } else {
+                FormUtils.populateMapFromString(params,
+                                                PhaseInterceptorChain.getCurrentMessage(),
+                                                FormUtils.readBody(is, enc),
+                                                enc,
+                                                decode,
+                                                (javax.servlet.http.HttpServletRequest)servletRequest);
+            }
+        }
+    }
+
+    protected void validateMap(MultivaluedMap<String, String> params) {
+        if (validator != null) {
+            validator.validate(params);
+        }
+    }
+
+    public long getSize(T t, Class<?> type,
+                        Type genericType, Annotation[] annotations,
+                        MediaType mediaType) {
+        return -1;
+    }
+
+    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations,
+                               MediaType mt) {
+        return isSupported(type, mt);
+    }
+
+    private static boolean isSupported(Class<?> type, MediaType mt) {
+        return (MultivaluedMap.class.isAssignableFrom(type) || Form.class.isAssignableFrom(type))
+            || ("multipart".equalsIgnoreCase(mt.getType())
+            && mt.isCompatible(MediaType.MULTIPART_FORM_DATA_TYPE)
+            && (MultivaluedMap.class.isAssignableFrom(type) || Form.class.isAssignableFrom(type)));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void writeTo(T obj, Class<?> c, Type t, Annotation[] anns,
+                        MediaType mt, MultivaluedMap<String, Object> headers, OutputStream os)
+        throws IOException, WebApplicationException {
+
+        MultivaluedMap<String, String> map =
+            (MultivaluedMap<String, String>)(obj instanceof Form ? ((Form)obj).asMap() : obj);
+        boolean encoded = keepEncoded(anns);
+
+        String enc = HttpUtils.getSetEncoding(mt, headers, StandardCharsets.UTF_8.name());
+
+        FormUtils.writeMapToOutputStream(map, os, enc, encoded);
+
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/JAXBElementProvider.java b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/JAXBElementProvider.java
new file mode 100644
index 0000000..ab27e02
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/JAXBElementProvider.java
@@ -0,0 +1,635 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.jaxrs.provider;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+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 javax.ws.rs.ext.Provider;
+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.adapters.XmlJavaTypeAdapter;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.Source;
+
+import org.w3c.dom.Document;
+
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.jaxrs.ext.MessageContext;
+import org.apache.cxf.jaxrs.ext.Nullable;
+import org.apache.cxf.jaxrs.ext.xml.XMLInstruction;
+import org.apache.cxf.jaxrs.ext.xml.XMLSource;
+import org.apache.cxf.jaxrs.ext.xml.XSISchemaLocation;
+import org.apache.cxf.jaxrs.ext.xml.XSLTTransform;
+import org.apache.cxf.jaxrs.utils.AnnotationUtils;
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
+import org.apache.cxf.jaxrs.utils.HttpUtils;
+import org.apache.cxf.jaxrs.utils.InjectionUtils;
+import org.apache.cxf.jaxrs.utils.JAXBUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.message.Attachment;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.staxutils.DepthExceededStaxException;
+import org.apache.cxf.staxutils.StaxUtils;
+import org.apache.cxf.staxutils.transform.TransformUtils;
+
+@Produces({"application/xml", "application/*+xml", "text/xml" })
+@Consumes({"application/xml", "application/*+xml", "text/xml" })
+@Provider
+public class JAXBElementProvider<T> extends AbstractJAXBProvider<T>  {
+    private static final String XML_PI_START = "<?xml version=\"1.0\" encoding=\"";
+    private static final String XML_PI_PROPERTY_RI = "com.sun.xml.bind.xmlHeaders";
+    private static final String XML_PI_PROPERTY_RI_INT = "com.sun.xml.internal.bind.xmlHeaders";
+
+    private static final String[] MARSHALLER_PROPERTIES = {
+        Marshaller.JAXB_ENCODING,
+        Marshaller.JAXB_FORMATTED_OUTPUT,
+        Marshaller.JAXB_FRAGMENT,
+        Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION,
+        Marshaller.JAXB_SCHEMA_LOCATION,
+        NS_MAPPER_PROPERTY_RI,
+        NS_MAPPER_PROPERTY_RI_INT,
+        XML_PI_PROPERTY_RI,
+        XML_PI_PROPERTY_RI_INT
+    };
+
+    private Map<String, Object> mProperties = Collections.emptyMap();
+    private Map<String, String> nsPrefixes = Collections.emptyMap();
+    private String xmlResourceOffset = "";
+    private String xmlPiPropertyName;
+
+    public JAXBElementProvider() {
+
+    }
+
+    protected boolean objectFactoryOrIndexAvailable(Class<?> type) {
+        return !Document.class.isAssignableFrom(type) && super.objectFactoryOrIndexAvailable(type);
+    }
+
+    public void setXmlResourceOffset(String value) {
+        xmlResourceOffset = value;
+    }
+
+    public void setNamespacePrefixes(Map<String, String> prefixes) {
+        nsPrefixes = prefixes;
+    }
+
+    protected void setXmlPiProperty(Marshaller ms, String value) throws Exception {
+        if (xmlPiPropertyName != null) {
+            setMarshallerProp(ms, value, xmlPiPropertyName, null);
+        } else {
+            setMarshallerProp(ms, value, XML_PI_PROPERTY_RI, XML_PI_PROPERTY_RI_INT);
+        }
+    }
+
+    @Override
+    protected boolean canBeReadAsJaxbElement(Class<?> type) {
+        return super.canBeReadAsJaxbElement(type)
+            && type != XMLSource.class && !Source.class.isAssignableFrom(type);
+    }
+
+    @Context
+    public void setMessageContext(MessageContext mc) {
+        super.setContext(mc);
+    }
+
+    public void setMarshallerProperties(Map<String, Object> marshallProperties) {
+        mProperties = marshallProperties;
+    }
+
+    public void setSchemaLocation(String schemaLocation) {
+        mProperties.put(Marshaller.JAXB_SCHEMA_LOCATION, schemaLocation);
+    }
+
+    public T readFrom(Class<T> type, Type genericType, Annotation[] anns, MediaType mt,
+        MultivaluedMap<String, String> headers, InputStream is)
+        throws IOException {
+        if (isPayloadEmpty(headers)) {
+            if (AnnotationUtils.getAnnotation(anns, Nullable.class) != null) {
+                return null;
+            }
+            reportEmptyContentLength();
+        }
+
+        XMLStreamReader reader = null;
+        Unmarshaller unmarshaller = null;
+        try {
+
+            boolean isCollection = InjectionUtils.isSupportedCollectionOrArray(type);
+            Class<?> theGenericType = isCollection ? InjectionUtils.getActualType(genericType) : type;
+            Class<?> theType = getActualType(theGenericType, genericType, anns);
+
+            unmarshaller = createUnmarshaller(theType, genericType, isCollection);
+            addAttachmentUnmarshaller(unmarshaller);
+            Object response = null;
+            if (JAXBElement.class.isAssignableFrom(type)
+                || !isCollection && (unmarshalAsJaxbElement
+                || jaxbElementClassMap != null && jaxbElementClassMap.containsKey(theType.getName()))) {
+                try (InputStream in = IOUtils.nullOrNotEmptyStream(is)) {
+                    // The return value might be "null" in case of empty stream, in this
+                    // case the unmarshaller fails with javax.xml.bind.UnmarshalException instead
+                    // of returning empty response.
+                    if (in != null) {
+                        reader = getStreamReader(in, type, mt);
+                        reader = TransformUtils.createNewReaderIfNeeded(reader, in);
+                        
+                        if (JAXBElement.class.isAssignableFrom(type) && type == theType) {
+                            response = unmarshaller.unmarshal(reader);
+                        } else {
+                            response = unmarshaller.unmarshal(reader, theType);
+                        }
+                    }
+                }
+            } else {
+                response = doUnmarshal(unmarshaller, type, is, anns, mt);
+            }
+            
+            if (response instanceof JAXBElement && !JAXBElement.class.isAssignableFrom(type)) {
+                response = ((JAXBElement<?>)response).getValue();
+            }
+            if (isCollection) {
+                response = ((CollectionWrapper)response).getCollectionOrArray(
+                                 unmarshaller, theType, type, genericType,
+                                 org.apache.cxf.jaxrs.utils.JAXBUtils.getAdapter(theGenericType, anns));
+            } else {
+                response = checkAdapter(response, type, anns, false);
+            }
+            return type.cast(response);
+
+        } catch (JAXBException e) {
+            handleJAXBException(e, true);
+        } catch (DepthExceededStaxException e) {
+            throw ExceptionUtils.toWebApplicationException(null, JAXRSUtils.toResponse(413));
+        } catch (WebApplicationException e) {
+            throw e;
+        } catch (Exception e) {
+            LOG.warning(ExceptionUtils.getStackTrace(e));
+            throw ExceptionUtils.toBadRequestException(e, null);
+        } finally {
+            try {
+                StaxUtils.close(reader);
+            } catch (XMLStreamException e) {
+                // Ignore
+            }
+            JAXBUtils.closeUnmarshaller(unmarshaller);
+        }
+        // unreachable
+        return null;
+    }
+
+    protected Object doUnmarshal(Unmarshaller unmarshaller, Class<?> type, InputStream is,
+                                 Annotation[] anns, MediaType mt)
+        throws JAXBException {
+        XMLStreamReader reader = getStreamReader(is, type, mt);
+        if (reader != null) {
+            try {
+                return unmarshalFromReader(unmarshaller, reader, anns, mt);
+            } catch (JAXBException e) {
+                throw e;
+            } finally {
+                try {
+                    StaxUtils.close(reader);
+                } catch (XMLStreamException e) {
+                    // Ignore
+                }
+            }
+        }
+        return unmarshalFromInputStream(unmarshaller, is, anns, mt);
+    }
+
+    protected XMLStreamReader getStreamReader(InputStream is, Class<?> type, MediaType mt) {
+        MessageContext mc = getContext();
+        XMLStreamReader reader = mc != null ? mc.getContent(XMLStreamReader.class) : null;
+        if (reader == null && mc != null) {
+            XMLInputFactory factory = (XMLInputFactory)mc.get(XMLInputFactory.class.getName());
+            if (factory != null) {
+                try {
+                    reader = factory.createXMLStreamReader(is);
+                } catch (XMLStreamException e) {
+                    throw ExceptionUtils.toInternalServerErrorException(
+                        new RuntimeException("Can not create XMLStreamReader", e), null);
+                }
+            }
+        }
+
+        if (reader == null && is == null) {
+            reader = getStreamHandlerFromCurrentMessage(XMLStreamReader.class);
+        }
+
+        reader = createTransformReaderIfNeeded(reader, is);
+        reader = createDepthReaderIfNeeded(reader, is);
+        if (InjectionUtils.isSupportedCollectionOrArray(type)) {
+            return new JAXBCollectionWrapperReader(TransformUtils.createNewReaderIfNeeded(reader, is));
+        }
+        return reader;
+
+    }
+
+    protected Object unmarshalFromInputStream(Unmarshaller unmarshaller, InputStream is,
+                                              Annotation[] anns, MediaType mt)
+        throws JAXBException {
+        // Try to create the read before unmarshalling the stream
+        XMLStreamReader xmlReader = null;
+        try {
+            if (is == null) {
+                Reader reader = getStreamHandlerFromCurrentMessage(Reader.class);
+                if (reader == null) {
+                    LOG.severe("No InputStream, Reader, or XMLStreamReader is available");
+                    throw ExceptionUtils.toInternalServerErrorException(null, null);
+                }
+                xmlReader = StaxUtils.createXMLStreamReader(reader);
+            } else {
+                xmlReader = StaxUtils.createXMLStreamReader(is);
+            }
+            configureReaderRestrictions(xmlReader);
+            return unmarshaller.unmarshal(xmlReader);
+        } finally {
+            try {
+                StaxUtils.close(xmlReader);
+            } catch (XMLStreamException e) {
+                // Ignore
+            }
+        }
+    }
+
+    protected Object unmarshalFromReader(Unmarshaller unmarshaller, XMLStreamReader reader,
+                                         Annotation[] anns, MediaType mt)
+        throws JAXBException {
+        return unmarshaller.unmarshal(reader);
+    }
+
+    public void writeTo(T obj, Class<?> cls, Type genericType, Annotation[] anns,
+        MediaType m, MultivaluedMap<String, Object> headers, OutputStream os)
+        throws IOException {
+        try {
+            String encoding = HttpUtils.getSetEncoding(m, headers, StandardCharsets.UTF_8.name());
+            if (InjectionUtils.isSupportedCollectionOrArray(cls)) {
+                marshalCollection(cls, obj, genericType, encoding, os, m, anns);
+            } else {
+                Object actualObject = checkAdapter(obj, cls, anns, true);
+                Class<?> actualClass = obj != actualObject || cls.isInterface()
+                    ? actualObject.getClass() : cls;
+                marshal(actualObject, actualClass, genericType, encoding, os, m, anns);
+            }
+        } catch (JAXBException e) {
+            handleJAXBException(e, false);
+        }  catch (WebApplicationException e) {
+            throw e;
+        } catch (Exception e) {
+            LOG.warning(ExceptionUtils.getStackTrace(e));
+            throw ExceptionUtils.toInternalServerErrorException(e, null);
+        }
+    }
+
+    protected void marshalCollection(Class<?> originalCls, Object collection,
+                                     Type genericType, String enc, OutputStream os,
+                                     MediaType m, Annotation[] anns)
+        throws Exception {
+
+        Class<?> actualClass = InjectionUtils.getActualType(genericType);
+        actualClass = getActualType(actualClass, genericType, anns);
+
+        Collection<?> c = originalCls.isArray() ? Arrays.asList((Object[]) collection)
+                                             : (Collection<?>) collection;
+
+        Iterator<?> it = c.iterator();
+
+        Object firstObj = it.hasNext() ? it.next() : null;
+
+        final QName qname;
+        if (firstObj instanceof JAXBElement) {
+            JAXBElement<?> el = (JAXBElement<?>)firstObj;
+            qname = el.getName();
+            actualClass = el.getDeclaredType();
+        } else {
+            qname = getCollectionWrapperQName(actualClass, genericType, firstObj, true);
+        }
+        if (qname == null) {
+            String message = new org.apache.cxf.common.i18n.Message("NO_COLLECTION_ROOT",
+                                                                    BUNDLE).toString();
+            throw new WebApplicationException(Response.serverError()
+                                              .entity(message).build());
+        }
+
+        os.write((XML_PI_START + (enc == null ? StandardCharsets.UTF_8.name() : enc) + "\"?>").getBytes());
+
+        final String startTag;
+        final String endTag;
+        if (!qname.getNamespaceURI().isEmpty()) {
+            String prefix = nsPrefixes.get(qname.getNamespaceURI());
+            if (prefix == null) {
+                prefix = "ns1";
+            }
+            startTag = "<" + prefix + ':' + qname.getLocalPart() + " xmlns:" + prefix + "=\""
+                + qname.getNamespaceURI() + "\">";
+            endTag = "</" + prefix + ':' + qname.getLocalPart() + ">";
+        } else {
+            startTag = "<" + qname.getLocalPart() + ">";
+            endTag = "</" + qname.getLocalPart() + ">";
+        }
+        os.write(startTag.getBytes());
+        if (firstObj != null) {
+            XmlJavaTypeAdapter adapter =
+                org.apache.cxf.jaxrs.utils.JAXBUtils.getAdapter(firstObj.getClass(), anns);
+            marshalCollectionMember(JAXBUtils.useAdapter(firstObj, adapter, true),
+                                    actualClass, genericType, enc, os, anns, m,
+                                    qname.getNamespaceURI());
+            while (it.hasNext()) {
+                marshalCollectionMember(JAXBUtils.useAdapter(it.next(), adapter, true), actualClass,
+                                        genericType, enc, os, anns, m,
+                                        qname.getNamespaceURI());
+            }
+        }
+        os.write(endTag.getBytes());
+    }
+    //CHECKSTYLE:OFF
+    protected void marshalCollectionMember(Object obj,
+                                           Class<?> cls,
+                                           Type genericType,
+                                           String enc,
+                                           OutputStream os,
+                                           Annotation[] anns,
+                                           MediaType mt,
+                                           String ns) throws Exception {
+    //CHECKSTYLE:ON
+        if (!(obj instanceof JAXBElement)) {
+            obj = convertToJaxbElementIfNeeded(obj, cls, genericType);
+        }
+
+        if (obj instanceof JAXBElement && cls != JAXBElement.class) {
+            cls = JAXBElement.class;
+        }
+
+        Marshaller ms = createMarshaller(obj, cls, genericType, enc);
+        ms.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
+        if (ns.length() > 0) {
+            Map<String, String> map = new HashMap<>();
+            // set the default just in case
+            if (!nsPrefixes.containsKey(ns)) {
+                map.put(ns, "ns1");
+            }
+            map.putAll(nsPrefixes);
+            setNamespaceMapper(ms, map);
+        }
+        marshal(obj, cls, genericType, enc, os, anns, mt, ms);
+    }
+
+    protected void marshal(Object obj, Class<?> cls, Type genericType,
+                           String enc, OutputStream os, MediaType mt) throws Exception {
+        marshal(obj, cls, genericType, enc, os, mt, new Annotation[]{});
+    }
+
+    protected void marshal(Object obj, Class<?> cls, Type genericType,
+                           String enc, OutputStream os, MediaType mt,
+                           Annotation[] anns) throws Exception {
+        obj = convertToJaxbElementIfNeeded(obj, cls, genericType);
+        if (obj instanceof JAXBElement && cls != JAXBElement.class) {
+            cls = JAXBElement.class;
+        }
+
+        Marshaller ms = createMarshaller(obj, cls, genericType, enc);
+        if (!nsPrefixes.isEmpty()) {
+            setNamespaceMapper(ms, nsPrefixes);
+        }
+        addAttachmentMarshaller(ms);
+        processXmlAnnotations(ms, mt, anns);
+        marshal(obj, cls, genericType, enc, os, anns, mt, ms);
+    }
+
+    private void processXmlAnnotations(Marshaller ms, MediaType mt, Annotation[] anns) throws Exception {
+        if (anns == null) {
+            return;
+        }
+        for (Annotation ann : anns) {
+            if (ann.annotationType() == XMLInstruction.class) {
+                addProcessingInstructions(ms, (XMLInstruction)ann);
+            } else if (ann.annotationType() == XSISchemaLocation.class) {
+                addSchemaLocation(ms, (XSISchemaLocation)ann);
+            } else if (ann.annotationType() == XSLTTransform.class) {
+                addXslProcessingInstruction(ms, mt, (XSLTTransform)ann);
+            }
+        }
+    }
+
+    private void addProcessingInstructions(Marshaller ms, XMLInstruction pi) throws Exception {
+        String value = pi.value();
+        int ind = value.indexOf("href='");
+        if (ind > 0) {
+            String relRef = value.substring(ind + 6);
+            relRef = relRef.substring(0, relRef.length() - 3).trim();
+            if (relRef.endsWith("'")) {
+                relRef = relRef.substring(0, relRef.length() - 1);
+            }
+            String absRef = resolveXMLResourceURI(relRef);
+            value = value.substring(0, ind + 6) + absRef + "'?>";
+        }
+        setXmlPiProperty(ms, value);
+    }
+
+    private void addXslProcessingInstruction(Marshaller ms, MediaType mt, XSLTTransform ann)
+        throws Exception {
+        if (ann.type() == XSLTTransform.TransformType.CLIENT
+            || ann.type() == XSLTTransform.TransformType.BOTH && ann.mediaTypes().length > 0) {
+            for (String s : ann.mediaTypes()) {
+                if (mt.isCompatible(JAXRSUtils.toMediaType(s))) {
+                    return;
+                }
+            }
+            String absRef = resolveXMLResourceURI(ann.value());
+            String xslPi = "<?xml-stylesheet type=\"text/xsl\" href=\"" + absRef + "\"?>";
+            setXmlPiProperty(ms, xslPi);
+        }
+    }
+
+    private void addSchemaLocation(Marshaller ms, XSISchemaLocation sl) throws Exception {
+        String value = sl.resolve() ? resolveXMLResourceURI(sl.value()) : sl.value();
+        String propName = !sl.noNamespace()
+            ? Marshaller.JAXB_SCHEMA_LOCATION : Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION;
+        ms.setProperty(propName, value);
+    }
+
+    protected String resolveXMLResourceURI(String path) {
+        MessageContext mc = getContext();
+        if (mc != null) {
+            String httpBasePath = (String)mc.get("http.base.path");
+            final UriBuilder builder;
+            if (httpBasePath != null) {
+                builder = UriBuilder.fromPath(httpBasePath);
+            } else {
+                builder = mc.getUriInfo().getBaseUriBuilder();
+            }
+            return builder.path(path).path(xmlResourceOffset).build().toString();
+        }
+        return path;
+    }
+
+
+
+    protected void addAttachmentMarshaller(Marshaller ms) {
+        Collection<Attachment> attachments = getAttachments(true);
+        if (attachments != null) {
+            Object value = getContext().getContextualProperty(Message.MTOM_THRESHOLD);
+            Integer threshold = value != null ? Integer.valueOf(value.toString()) : Integer.valueOf(0);
+            ms.setAttachmentMarshaller(new JAXBAttachmentMarshaller(
+                attachments, threshold));
+        }
+    }
+
+    protected void addAttachmentUnmarshaller(Unmarshaller um) {
+        Collection<Attachment> attachments = getAttachments(false);
+        if (attachments != null) {
+            um.setAttachmentUnmarshaller(new JAXBAttachmentUnmarshaller(
+                attachments));
+        }
+    }
+
+    private Collection<Attachment> getAttachments(boolean write) {
+        MessageContext mc = getContext();
+        if (mc != null) {
+            // TODO: there has to be a better fix
+            String propertyName = write ? "WRITE-" + Message.ATTACHMENTS : Message.ATTACHMENTS;
+            return CastUtils.cast((Collection<?>)mc.get(propertyName));
+        }
+        return null;
+    }
+    //CHECKSTYLE:OFF
+    protected final void marshal(Object obj, Class<?> cls, Type genericType,
+                           String enc, OutputStream os,
+                           Annotation[] anns, MediaType mt, Marshaller ms)
+        throws Exception {
+    //CHECKSTYLE:ON
+        for (Map.Entry<String, Object> entry : mProperties.entrySet()) {
+            ms.setProperty(entry.getKey(), entry.getValue());
+        }
+        MessageContext mc = getContext();
+        if (mc != null) {
+            // check Marshaller properties which might've been set earlier on,
+            // they'll overwrite statically configured ones
+            for (String key : MARSHALLER_PROPERTIES) {
+                Object value = mc.get(key);
+                if (value != null) {
+                    ms.setProperty(key, value);
+                }
+            }
+
+        }
+        XMLStreamWriter writer = getStreamWriter(obj, os, mt);
+        if (writer != null) {
+            if (os == null) {
+                ms.setProperty(Marshaller.JAXB_FRAGMENT, true);
+            } else if (mc != null) {
+                if (mc.getContent(XMLStreamWriter.class) != null) {
+                    ms.setProperty(Marshaller.JAXB_FRAGMENT, true);
+                }
+                mc.put(XMLStreamWriter.class.getName(), writer);
+            }
+            marshalToWriter(ms, obj, writer, anns, mt);
+            if (mc != null) {
+                writer.writeEndDocument();
+            }
+        } else {
+            marshalToOutputStream(ms, obj, os, anns, mt);
+        }
+    }
+
+    protected XMLStreamWriter getStreamWriter(Object obj, OutputStream os, MediaType mt) {
+        XMLStreamWriter writer = null;
+        MessageContext mc = getContext();
+        if (mc != null) {
+            writer = mc.getContent(XMLStreamWriter.class);
+            if (writer == null) {
+                XMLOutputFactory factory = (XMLOutputFactory)mc.get(XMLOutputFactory.class.getName());
+                if (factory != null) {
+                    try {
+                        writer = factory.createXMLStreamWriter(os);
+                    } catch (XMLStreamException e) {
+                        throw ExceptionUtils.toInternalServerErrorException(
+                            new RuntimeException("Cant' create XMLStreamWriter", e), null);
+                    }
+                }
+            }
+            if (writer == null && getEnableStreaming()) {
+                writer = StaxUtils.createXMLStreamWriter(os);
+            }
+        }
+
+        if (writer == null && os == null) {
+            writer = getStreamHandlerFromCurrentMessage(XMLStreamWriter.class);
+        }
+        return createTransformWriterIfNeeded(writer, os, true);
+    }
+
+    protected void marshalToOutputStream(Marshaller ms, Object obj, OutputStream os,
+                                         Annotation[] anns, MediaType mt)
+        throws Exception {
+        org.apache.cxf.common.jaxb.JAXBUtils.setMinimumEscapeHandler(ms);
+        if (os == null) {
+            Writer writer = getStreamHandlerFromCurrentMessage(Writer.class);
+            if (writer == null) {
+                LOG.severe("No OutputStream, Writer, or XMLStreamWriter is available");
+                throw ExceptionUtils.toInternalServerErrorException(null, null);
+            }
+            ms.marshal(obj, writer);
+            writer.flush();
+        } else {
+            ms.marshal(obj, os);
+        }
+    }
+
+    protected void marshalToWriter(Marshaller ms, Object obj, XMLStreamWriter writer,
+                                   Annotation[] anns, MediaType mt)
+        throws Exception {
+        org.apache.cxf.common.jaxb.JAXBUtils.setNoEscapeHandler(ms);
+        ms.marshal(obj, writer);
+    }
+
+    public void setXmlPiPropertyName(String xmlPiPropertyName) {
+        this.xmlPiPropertyName = xmlPiPropertyName;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java
new file mode 100644
index 0000000..e129eff
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/MultipartProvider.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.jaxrs.provider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.nio.file.Files;
+import java.util.ArrayList;
+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.ResourceBundle;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import javax.activation.DataHandler;
+import javax.activation.DataSource;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.cxf.attachment.AttachmentUtil;
+import org.apache.cxf.attachment.ByteDataSource;
+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.helpers.CastUtils;
+import org.apache.cxf.jaxrs.ext.MessageContext;
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition;
+import org.apache.cxf.jaxrs.ext.multipart.InputStreamDataSource;
+import org.apache.cxf.jaxrs.ext.multipart.Multipart;
+import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
+import org.apache.cxf.jaxrs.ext.multipart.MultipartOutputFilter;
+import org.apache.cxf.jaxrs.impl.MetadataMap;
+import org.apache.cxf.jaxrs.utils.AnnotationUtils;
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
+import org.apache.cxf.jaxrs.utils.InjectionUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.jaxrs.utils.multipart.AttachmentUtils;
+import org.apache.cxf.message.Message;
+
+@Provider
+@Consumes({"multipart/related", "multipart/mixed", "multipart/alternative", "multipart/form-data" })
+@Produces({"multipart/related", "multipart/mixed", "multipart/alternative", "multipart/form-data" })
+public class MultipartProvider extends AbstractConfigurableProvider
+    implements MessageBodyReader<Object>, MessageBodyWriter<Object> {
+
+    private static final String SUPPORT_TYPE_AS_MULTIPART = "support.type.as.multipart";
+    private static final String SINGLE_PART_IS_COLLECTION = "single.multipart.is.collection";
+    private static final Logger LOG = LogUtils.getL7dLogger(MultipartProvider.class);
+    private static final ResourceBundle BUNDLE = BundleUtils.getBundle(MultipartProvider.class);
+    private static final Set<Class<?>> WELL_KNOWN_MULTIPART_CLASSES = new HashSet<>();
+    private static final Set<String> MULTIPART_SUBTYPES = new HashSet<>();
+    static {
+        WELL_KNOWN_MULTIPART_CLASSES.add(MultipartBody.class);
+        WELL_KNOWN_MULTIPART_CLASSES.add(Attachment.class);
+
+        MULTIPART_SUBTYPES.add("form-data");
+        MULTIPART_SUBTYPES.add("mixed");
+        MULTIPART_SUBTYPES.add("related");
+        MULTIPART_SUBTYPES.add("alternative");
+    }
+
+    @Context
+    private MessageContext mc;
+    private String attachmentDir;
+    private String attachmentThreshold;
+    private String attachmentMaxSize;
+
+    public void setMessageContext(MessageContext context) {
+        this.mc = context;
+    }
+
+    public void setAttachmentDirectory(String dir) {
+        attachmentDir = dir;
+    }
+
+    public void setAttachmentThreshold(String threshold) {
+        attachmentThreshold = threshold;
+    }
+
+    public void setAttachmentMaxSize(String maxSize) {
+        attachmentMaxSize = maxSize;
+    }
+
+    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations,
+                              MediaType mt) {
+        return isSupported(type, annotations, mt);
+
+    }
+
+    private boolean isSupported(Class<?> type, Annotation[] anns, MediaType mt) {
+        return mediaTypeSupported(mt)
+            && (WELL_KNOWN_MULTIPART_CLASSES.contains(type)
+                || Collection.class.isAssignableFrom(type)
+                || Map.class.isAssignableFrom(type) && type != MultivaluedMap.class
+                || AnnotationUtils.getAnnotation(anns, Multipart.class) != null
+                || PropertyUtils.isTrue(mc.getContextualProperty(SUPPORT_TYPE_AS_MULTIPART)));
+    }
+
+    protected void checkContentLength() {
+        if (mc != null && isPayloadEmpty(mc.getHttpHeaders())) {
+            String message = new org.apache.cxf.common.i18n.Message("EMPTY_BODY", BUNDLE).toString();
+            LOG.warning(message);
+            throw new WebApplicationException(400);
+        }
+    }
+
+    public Object readFrom(Class<Object> c, Type t, Annotation[] anns, MediaType mt,
+                           MultivaluedMap<String, String> headers, InputStream is)
+        throws IOException, WebApplicationException {
+        checkContentLength();
+        List<Attachment> infos = AttachmentUtils.getAttachments(
+                mc, attachmentDir, attachmentThreshold, attachmentMaxSize);
+
+        boolean collectionExpected = Collection.class.isAssignableFrom(c);
+        if (collectionExpected
+            && AnnotationUtils.getAnnotation(anns, Multipart.class) == null) {
+            return getAttachmentCollection(t, infos, anns);
+        }
+        if (c.isAssignableFrom(Map.class)) {
+            Map<String, Object> map = new LinkedHashMap<>(infos.size());
+            Class<?> actual = getActualType(t, 1);
+            for (Attachment a : infos) {
+                map.put(a.getContentType().toString(), fromAttachment(a, actual, actual, anns));
+            }
+            return map;
+        }
+        if (MultipartBody.class.isAssignableFrom(c)) {
+            return new MultipartBody(infos);
+        }
+
+        Multipart id = AnnotationUtils.getAnnotation(anns, Multipart.class);
+        Attachment multipart = AttachmentUtils.getMultipart(id, mt, infos);
+        if (multipart != null) {
+            if (collectionExpected
+                && !mediaTypeSupported(multipart.getContentType())
+                && !PropertyUtils.isTrue(mc.getContextualProperty(SINGLE_PART_IS_COLLECTION))) {
+                List<Attachment> allMultiparts = AttachmentUtils.getMatchingAttachments(id, infos);
+                return getAttachmentCollection(t, allMultiparts, anns);
+            }
+            return fromAttachment(multipart, c, t, anns);
+        }
+
+        if (id != null && !id.required()) {
+            /*
+             * Return default value for a missing optional part
+             */
+            Object defaultValue = null;
+            if (c.isPrimitive()) {
+                defaultValue = PrimitiveUtils.read((Class<?>)c == boolean.class ? "false" : "0", c);
+            }
+            return defaultValue;
+        }
+
+        throw ExceptionUtils.toBadRequestException(null, null);
+
+    }
+
+    private Object getAttachmentCollection(Type t, List<Attachment> infos, Annotation[] anns) throws IOException {
+        Class<?> actual = getActualType(t, 0);
+        if (Attachment.class.isAssignableFrom(actual)) {
+            return infos;
+        }
+        Collection<Object> objects = new ArrayList<>();
+        for (Attachment a : infos) {
+            objects.add(fromAttachment(a, actual, actual, anns));
+        }
+        return objects;
+    }
+
+    private Class<?> getActualType(Type type, int pos) {
+        Class<?> actual = null;
+        try {
+            actual = InjectionUtils.getActualType(type, pos);
+        } catch (Exception ex) {
+            // ignore;
+        }
+        return actual != null && actual != Object.class ? actual : Attachment.class;
+    }
+
+    private <T> Object fromAttachment(Attachment multipart, Class<T> c, Type t, Annotation[] anns)
+        throws IOException {
+        if (DataHandler.class.isAssignableFrom(c)) {
+            return multipart.getDataHandler();
+        } else if (DataSource.class.isAssignableFrom(c)) {
+            return multipart.getDataHandler().getDataSource();
+        } else if (Attachment.class.isAssignableFrom(c)) {
+            return multipart;
+        } else {
+            if (mediaTypeSupported(multipart.getContentType())) {
+                mc.put("org.apache.cxf.multipart.embedded", true);
+                mc.put("org.apache.cxf.multipart.embedded.ctype", multipart.getContentType());
+                mc.put("org.apache.cxf.multipart.embedded.input",
+                       multipart.getDataHandler().getInputStream());
+                anns = new Annotation[]{};
+            }
+            MessageBodyReader<T> r =
+                mc.getProviders().getMessageBodyReader(c, t, anns, multipart.getContentType());
+            if (r != null) {
+                InputStream is = multipart.getDataHandler().getInputStream();
+                return r.readFrom(c, t, anns, multipart.getContentType(), multipart.getHeaders(),
+                                  is);
+            }
+        }
+        return null;
+    }
+
+    private boolean mediaTypeSupported(MediaType mt) {
+        return "multipart".equals(mt.getType()) && MULTIPART_SUBTYPES.contains(mt.getSubtype());
+    }
+
+    public long getSize(Object t, Class<?> type, Type genericType, Annotation[] annotations,
+                        MediaType mediaType) {
+        return -1;
+    }
+
+    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations,
+                               MediaType mt) {
+        return isSupported(type, annotations, mt);
+    }
+
+
+    public void writeTo(Object obj, Class<?> type, Type genericType, Annotation[] anns, MediaType mt,
+                        MultivaluedMap<String, Object> headers, OutputStream os)
+        throws IOException, WebApplicationException {
+
+        List<Attachment> handlers = convertToDataHandlers(obj, type, genericType, anns, mt);
+        if (mc.get(AttachmentUtils.OUT_FILTERS) != null) {
+            List<MultipartOutputFilter> filters = CastUtils.cast((List<?>)mc.get(AttachmentUtils.OUT_FILTERS));
+            for (MultipartOutputFilter filter : filters) {
+                filter.filter(handlers);
+            }
+        }
+        mc.put(MultipartBody.OUTBOUND_MESSAGE_ATTACHMENTS, handlers);
+        handlers.get(0).getDataHandler().writeTo(os);
+    }
+
+    private List<Attachment> convertToDataHandlers(Object obj,
+                                                   Class<?> type, Type genericType,
+                                                   Annotation[] anns, MediaType mt)  throws IOException {
+        if (Map.class.isAssignableFrom(obj.getClass())) {
+            Map<Object, Object> objects = CastUtils.cast((Map<?, ?>)obj);
+            List<Attachment> handlers = new ArrayList<>(objects.size());
+            int i = 0;
+            for (Iterator<Map.Entry<Object, Object>> iter = objects.entrySet().iterator();
+                iter.hasNext();) {
+                Map.Entry<Object, Object> entry = iter.next();
+                Object value = entry.getValue();
+                Attachment handler = createDataHandler(value, value.getClass(),
+                                                       new Annotation[]{},
+                                                       entry.getKey().toString(),
+                                                       mt.toString(),
+                                                       i++);
+                handlers.add(handler);
+            }
+            return handlers;
+        }
+        String rootMediaType = getRootMediaType(anns, mt);
+        if (List.class.isAssignableFrom(obj.getClass())) {
+            return getAttachments((List<?>)obj, rootMediaType);
+        }
+        if (MultipartBody.class.isAssignableFrom(type)) {
+            List<Attachment> atts = ((MultipartBody)obj).getAllAttachments();
+            // these attachments may have no DataHandlers, but objects only
+            return getAttachments(atts, rootMediaType);
+        }
+        Attachment handler = createDataHandler(obj,
+                                               genericType, anns,
+                                               rootMediaType, mt.toString(), 1);
+        return Collections.singletonList(handler);
+    }
+
+    private List<Attachment> getAttachments(List<?> objects, String rootMediaType) throws IOException {
+        List<Attachment> handlers = new ArrayList<>(objects.size());
+        for (int i = 0; i < objects.size(); i++) {
+            Object value = objects.get(i);
+            Attachment handler = createDataHandler(value,
+                                           value.getClass(), new Annotation[]{},
+                                           rootMediaType, rootMediaType, i);
+            handlers.add(handler);
+        }
+        return handlers;
+    }
+
+    private <T> Attachment createDataHandler(T obj,
+                                             Type genericType,
+                                             Annotation[] anns,
+                                             String mimeType,
+                                             String mainMediaType,
+                                             int id) throws IOException {
+        @SuppressWarnings("unchecked")
+        Class<T> cls = (Class<T>)obj.getClass();
+        return createDataHandler(obj, cls, genericType, anns, mimeType, mainMediaType, id);
+    }
+    private <T> Attachment createDataHandler(T obj,
+                                         Class<T> cls,
+                                         Type genericType,
+                                         Annotation[] anns,
+                                         String mimeType,
+                                         String mainMediaType,
+                                         int id) throws IOException {
+        final DataHandler dh;
+        if (InputStream.class.isAssignableFrom(obj.getClass())) {
+            dh = createInputStreamDH((InputStream)obj, mimeType);
+        } else if (DataHandler.class.isAssignableFrom(obj.getClass())) {
+            dh = (DataHandler)obj;
+        } else if (DataSource.class.isAssignableFrom(obj.getClass())) {
+            dh = new DataHandler((DataSource)obj);
+        } else if (File.class.isAssignableFrom(obj.getClass())) {
+            File f = (File)obj;
+            ContentDisposition cd = mainMediaType.startsWith(MediaType.MULTIPART_FORM_DATA)
+                ? new ContentDisposition("form-data;name=file;filename=" + f.getName()) :  null;
+            return new Attachment(AttachmentUtil.BODY_ATTACHMENT_ID, Files.newInputStream(f.toPath()), cd);
+        } else if (Attachment.class.isAssignableFrom(obj.getClass())) {
+            Attachment att = (Attachment)obj;
+            if (att.getObject() == null) {
+                return att;
+            }
+            dh = getHandlerForObject(att.getObject(),
+                                     att.getObject().getClass(), new Annotation[]{},
+                                     att.getContentType().toString(), id);
+            return new Attachment(att.getContentId(), dh, att.getHeaders());
+        } else if (byte[].class.isAssignableFrom(obj.getClass())) {
+            ByteDataSource source = new ByteDataSource((byte[])obj);
+            source.setContentType(mimeType);
+            dh = new DataHandler(source);
+        } else {
+            dh = getHandlerForObject(obj, cls, genericType, anns, mimeType);
+        }
+        String contentId = getContentId(anns, id);
+        MultivaluedMap<String, String> headers = new MetadataMap<>();
+        headers.putSingle("Content-Type", mimeType);
+        
+        return new Attachment(contentId, dh, headers);
+    }
+
+    private String getContentId(Annotation[] anns, int id) {
+        Multipart part = AnnotationUtils.getAnnotation(anns, Multipart.class);
+        if (part != null && !"".equals(part.value())) {
+            return part.value();
+        }
+        return id == 0 ? AttachmentUtil.BODY_ATTACHMENT_ID : Integer.toString(id);
+    }
+
+    private <T> DataHandler getHandlerForObject(T obj,
+                                            Class<T> cls, Type genericType,
+                                            Annotation[] anns,
+                                            String mimeType) {
+        MediaType mt = JAXRSUtils.toMediaType(mimeType);
+        mc.put(ProviderFactory.ACTIVE_JAXRS_PROVIDER_KEY, this);
+
+        final MessageBodyWriter<T> r;
+        try {
+            r = mc.getProviders().getMessageBodyWriter(cls, genericType, anns, mt);
+        } finally {
+            mc.put(ProviderFactory.ACTIVE_JAXRS_PROVIDER_KEY, null);
+        }
+        if (r == null) {
+            org.apache.cxf.common.i18n.Message message =
+                new org.apache.cxf.common.i18n.Message("NO_MSG_WRITER",
+                                                   BUNDLE,
+                                                   cls);
+            LOG.severe(message.toString());
+            throw ExceptionUtils.toInternalServerErrorException(null, null);
+        }
+
+        return new MessageBodyWriterDataHandler<T>(r, obj, cls, genericType, anns, mt);
+    }
+    private <T> DataHandler getHandlerForObject(T obj,
+                                            Type genericType,
+                                            Annotation[] anns,
+                                            String mimeType, int id) {
+        @SuppressWarnings("unchecked")
+        Class<T> cls = (Class<T>)obj.getClass();
+        return getHandlerForObject(obj, cls, genericType, anns, mimeType);
+    }
+
+    private DataHandler createInputStreamDH(InputStream is, String mimeType) {
+        return new DataHandler(new InputStreamDataSource(is, mimeType));
+    }
+
+    private String getRootMediaType(Annotation[] anns, MediaType mt) {
+        String mimeType = mt.getParameters().get("type");
+        if (mimeType != null) {
+            return mimeType;
+        }
+        Multipart id = AnnotationUtils.getAnnotation(anns, Multipart.class);
+        if (id != null && !MediaType.WILDCARD.equals(id.type())) {
+            mimeType = id.type();
+        }
+        if (mimeType == null) {
+            if (PropertyUtils.isTrue(mc.getContextualProperty(Message.MTOM_ENABLED))) {
+                mimeType = "text/xml";
+            } else {
+                mimeType = MediaType.APPLICATION_OCTET_STREAM;
+            }
+        }
+        return mimeType;
+    }
+
+    private static class MessageBodyWriterDataHandler<T> extends DataHandler {
+        private MessageBodyWriter<T> writer;
+        private T obj;
+        private Class<T> cls;
+        private Type genericType;
+        private Annotation[] anns;
+        private MediaType contentType;
+        MessageBodyWriterDataHandler(MessageBodyWriter<T> writer,
+                                     T obj,
+                                     Class<T> cls,
+                                     Type genericType,
+                                     Annotation[] anns,
+                                     MediaType contentType) {
+            super(new ByteDataSource("1".getBytes(), contentType.toString()));
+            this.writer = writer;
+            this.obj = obj;
+            this.cls = cls;
+            this.genericType = genericType;
+            this.anns = anns;
+            this.contentType = contentType;
+        }
+
+        @Override
+        public void writeTo(OutputStream os) {
+            try {
+                writer.writeTo(obj, cls, genericType, anns, contentType,
+                               new MetadataMap<String, Object>(), os);
+            } catch (IOException ex) {
+                throw ExceptionUtils.toInternalServerErrorException(ex, null);
+            }
+        }
+
+        @Override
+        public String getContentType() {
+            return contentType.toString();
+        }
+
+        // TODO : throw UnsupportedOperationException for all other DataHandler methods
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/SourceProvider.java b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/SourceProvider.java
new file mode 100644
index 0000000..76980b8
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/SourceProvider.java
@@ -0,0 +1,236 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.jaxrs.provider;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+import java.util.logging.Logger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamSource;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.io.CachedOutputStream;
+import org.apache.cxf.jaxrs.ext.MessageContext;
+import org.apache.cxf.jaxrs.ext.xml.XMLSource;
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
+import org.apache.cxf.jaxrs.utils.HttpUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.PhaseInterceptorChain;
+import org.apache.cxf.staxutils.DepthExceededStaxException;
+import org.apache.cxf.staxutils.StaxSource;
+import org.apache.cxf.staxutils.StaxUtils;
+
+@Provider
+@Produces({"application/xml", "application/*+xml", "text/xml" })
+@Consumes({"application/xml", "application/*+xml", "text/xml", "text/html" })
+public class SourceProvider<T> extends AbstractConfigurableProvider implements
+    MessageBodyReader<T>, MessageBodyWriter<T> {
+
+    private static final String PREFERRED_FORMAT = "source-preferred-format";
+    private static final Logger LOG = LogUtils.getL7dLogger(SourceProvider.class);
+    @Context
+    private MessageContext context;
+
+
+    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mt) {
+        return Source.class.isAssignableFrom(type)
+            || Node.class.isAssignableFrom(type);
+    }
+
+    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mt) {
+        return Source.class.isAssignableFrom(type)
+               || XMLSource.class.isAssignableFrom(type)
+               || Document.class.isAssignableFrom(type);
+    }
+
+    public T readFrom(Class<T> source, Type genericType, Annotation[] annotations, MediaType m,
+        MultivaluedMap<String, String> headers, InputStream is)
+        throws IOException {
+
+        Class<?> theSource = source;
+        if (theSource == Source.class) {
+            String s = getPreferredSource();
+            if ("sax".equalsIgnoreCase(s) || "cxf.stax".equalsIgnoreCase(s)) {
+                theSource = SAXSource.class;
+            }
+        }
+        try {
+            if (DOMSource.class.isAssignableFrom(theSource) || Document.class.isAssignableFrom(theSource)) {
+
+                boolean docRequired = Document.class.isAssignableFrom(theSource);
+                XMLStreamReader reader = getReader(is);
+                try {
+                    Document doc = StaxUtils.read(reader);
+                    return source.cast(docRequired ? doc : new DOMSource(doc));
+                } catch (DepthExceededStaxException e) {
+                    throw ExceptionUtils.toWebApplicationException(null, JAXRSUtils.toResponse(413));
+                } catch (XMLStreamException e) {
+                    if (e.getMessage() != null && e.getMessage().startsWith("Maximum Number")) {
+                        throw ExceptionUtils.toWebApplicationException(null, JAXRSUtils.toResponse(413));
+                    }
+                    throw ExceptionUtils.toBadRequestException(e, null);
+                } catch (Exception e) {
+                    IOException ioex = new IOException("Problem creating a Source object");
+                    ioex.setStackTrace(e.getStackTrace());
+                    throw ioex;
+                } finally {
+                    try {
+                        reader.close();
+                    } catch (XMLStreamException e) {
+                        //ignore
+                    }
+                }
+            } else if (SAXSource.class.isAssignableFrom(theSource)
+                      || StaxSource.class.isAssignableFrom(theSource)) {
+                return source.cast(new StaxSource(getReader(is)));
+            } else if (StreamSource.class.isAssignableFrom(theSource)
+                       || Source.class.isAssignableFrom(theSource)) {
+                return source.cast(new StreamSource(getRealStream(is)));
+            } else if (XMLSource.class.isAssignableFrom(theSource)) {
+                return source.cast(new XMLSource(getRealStream(is)));
+            }
+        } catch (ClassCastException e) {
+            String msg = "Unsupported class: " + source.getName();
+            LOG.warning(msg);
+            throw ExceptionUtils.toInternalServerErrorException(null, null);
+        }
+
+        throw new IOException("Unrecognized source");
+    }
+
+    protected XMLStreamReader getReader(InputStream is) {
+        XMLStreamReader reader = getReaderFromMessage();
+        if (reader == null) {
+            reader = StaxUtils.createXMLStreamReader(is);
+        }
+        return configureReaderRestrictions(reader);
+    }
+
+    protected XMLStreamReader configureReaderRestrictions(XMLStreamReader reader) {
+        Message message = PhaseInterceptorChain.getCurrentMessage();
+        if (message != null) {
+            try {
+                return StaxUtils.configureReader(reader, message);
+            } catch (XMLStreamException ex) {
+                throw ExceptionUtils.toInternalServerErrorException(ex, null);
+            }
+        }
+        return reader;
+    }
+
+    protected InputStream getRealStream(InputStream is) throws IOException {
+        XMLStreamReader reader = getReaderFromMessage();
+        return reader == null ? is : getStreamFromReader(reader);
+    }
+
+    private InputStream getStreamFromReader(XMLStreamReader input)
+        throws IOException {
+
+        try (CachedOutputStream out = new CachedOutputStream()) {
+            StaxUtils.copy(input, out);
+            return out.getInputStream();
+        } catch (XMLStreamException ex) {
+            throw new IOException("XMLStreamException:" + ex.getMessage());
+        }
+    }
+
+    protected XMLStreamReader getReaderFromMessage() {
+        MessageContext mc = getContext();
+        if (mc != null) {
+            return mc.getContent(XMLStreamReader.class);
+        }
+        return null;
+    }
+
+    public void writeTo(T source, Class<?> clazz, Type genericType, Annotation[] annotations,
+        MediaType mt, MultivaluedMap<String, Object> headers, OutputStream os)
+        throws IOException {
+
+        String encoding = HttpUtils.getSetEncoding(mt, headers, StandardCharsets.UTF_8.name());
+
+        final XMLStreamReader reader;
+        if (source instanceof Source) {
+            reader = StaxUtils.createXMLStreamReader((Source)source);
+        } else if (source instanceof Document) {
+            reader = StaxUtils.createXMLStreamReader((Document)source);
+        } else {
+            reader = StaxUtils.createXMLStreamReader(new DOMSource((Node)source));
+        }
+        XMLStreamWriter writer = StaxUtils.createXMLStreamWriter(os, encoding);
+        try {
+            StaxUtils.copy(reader, writer);
+        } catch (XMLStreamException e) {
+            throw ExceptionUtils.toInternalServerErrorException(e, null);
+        } finally {
+            try {
+                reader.close();
+            } catch (XMLStreamException e) {
+                //ignore
+            }
+            try {
+                writer.flush();
+                writer.close();
+            } catch (XMLStreamException e) {
+                //ignore
+            }
+        }
+    }
+
+    public long getSize(T source, Class<?> type, Type genericType, Annotation[] annotations,
+                        MediaType mt) {
+        return -1;
+    }
+
+    protected String getPreferredSource() {
+        MessageContext mc = getContext();
+        String source = null;
+        if (mc != null) {
+            source = (String)mc.getContextualProperty(PREFERRED_FORMAT);
+        }
+        return source != null ? source : "sax";
+
+    }
+
+    protected MessageContext getContext() {
+        return context;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/provider/XSLTJaxbProvider.java b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/XSLTJaxbProvider.java
new file mode 100644
index 0000000..4250963
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/provider/XSLTJaxbProvider.java
@@ -0,0 +1,588 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.jaxrs.provider;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.PathSegment;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.Provider;
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.io.CachedOutputStream;
+import org.apache.cxf.jaxrs.ext.MessageContext;
+import org.apache.cxf.jaxrs.ext.xml.XSLTTransform;
+import org.apache.cxf.jaxrs.utils.AnnotationUtils;
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
+import org.apache.cxf.jaxrs.utils.InjectionUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.jaxrs.utils.ResourceUtils;
+import org.apache.cxf.staxutils.StaxSource;
+import org.apache.cxf.staxutils.StaxUtils;
+
+@Produces({"application/xml", "application/*+xml", "text/xml", "text/html" })
+@Consumes({"application/xml", "application/*+xml", "text/xml", "text/html" })
+@Provider
+public class XSLTJaxbProvider<T> extends JAXBElementProvider<T> {
+
+    private static final Logger LOG = LogUtils.getL7dLogger(XSLTJaxbProvider.class);
+
+    private static final String ABSOLUTE_PATH_PARAMETER = "absolute.path";
+    private static final String BASE_PATH_PARAMETER = "base.path";
+    private static final String RELATIVE_PATH_PARAMETER = "relative.path";
+    private static final String XSLT_TEMPLATE_PROPERTY = "xslt.template";
+    private SAXTransformerFactory factory;
+    private Templates inTemplates;
+    private Templates outTemplates;
+    private Map<String, Templates> inMediaTemplates;
+    private Map<String, Templates> outMediaTemplates;
+    private ConcurrentHashMap<String, Templates> annotationTemplates =
+        new ConcurrentHashMap<>();
+
+    private List<String> inClassesToHandle;
+    private List<String> outClassesToHandle;
+    private Map<String, Object> inParamsMap;
+    private Map<String, Object> outParamsMap;
+    private Map<String, String> inProperties;
+    private Map<String, String> outProperties;
+    private URIResolver uriResolver;
+    private String systemId;
+
+    private boolean supportJaxbOnly;
+    private boolean refreshTemplates;
+    private boolean secureProcessing = true;
+
+    public void setSupportJaxbOnly(boolean support) {
+        this.supportJaxbOnly = support;
+    }
+
+    @Override
+    public boolean isReadable(Class<?> type, Type genericType, Annotation[] anns, MediaType mt) {
+        if (!super.isReadable(type, genericType, anns, mt)) {
+            return false;
+        }
+
+        if (InjectionUtils.isSupportedCollectionOrArray(type)) {
+            return supportJaxbOnly;
+        }
+
+        // if the user has set the list of in classes and a given class
+        // is in that list then it can only be handled by the template
+        if (inClassCanBeHandled(type.getName()) || inClassesToHandle == null && !supportJaxbOnly) {
+            return inTemplatesAvailable(type, anns, mt);
+        }
+        return supportJaxbOnly;
+    }
+
+    @Override
+    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] anns, MediaType mt) {
+        // JAXB support is required
+        if (!super.isWriteable(type, genericType, anns, mt)) {
+            return false;
+        }
+        if (InjectionUtils.isSupportedCollectionOrArray(type)) {
+            return supportJaxbOnly;
+        }
+
+        // if the user has set the list of out classes and a given class
+        // is in that list then it can only be handled by the template
+        if (outClassCanBeHandled(type.getName()) || outClassesToHandle == null && !supportJaxbOnly) {
+            return outTemplatesAvailable(type, anns, mt);
+        }
+        return supportJaxbOnly;
+    }
+
+    protected boolean inTemplatesAvailable(Class<?> cls, Annotation[] anns, MediaType mt) {
+        return inTemplates != null
+            || inMediaTemplates != null && inMediaTemplates.containsKey(mt.getType() + "/"
+                                                                        + mt.getSubtype())
+            || getTemplatesFromAnnotation(cls, anns, mt) != null;
+    }
+
+    protected boolean outTemplatesAvailable(Class<?> cls, Annotation[] anns, MediaType mt) {
+        return outTemplates != null
+            || outMediaTemplates != null && outMediaTemplates.containsKey(mt.getType()
+                                                                          + "/" + mt.getSubtype())
+            || getTemplatesFromAnnotation(cls, anns, mt) != null;
+    }
+
+    protected Templates getTemplatesFromAnnotation(Class<?> cls,
+                                                   Annotation[] anns,
+                                                   MediaType mt) {
+        Templates t = null;
+        XSLTTransform ann = getXsltTransformAnn(anns, mt);
+        if (ann != null) {
+            t = annotationTemplates.get(ann.value());
+            if (t == null || refreshTemplates) {
+                String path = ann.value();
+                final String cp = "classpath:";
+                if (!path.startsWith(cp)) {
+                    path = cp + path;
+                }
+                t = createTemplates(path);
+                if (t == null) {
+                    createTemplates(ClassLoaderUtils.getResource(ann.value(), cls));
+                }
+                if (t != null) {
+                    annotationTemplates.put(ann.value(), t);
+                }
+            }
+        }
+        return t;
+
+    }
+
+    protected Templates getAnnotationTemplates(Annotation[] anns) {
+        Templates t = null;
+        XSLTTransform ann = AnnotationUtils.getAnnotation(anns, XSLTTransform.class);
+        if (ann != null) {
+            t = annotationTemplates.get(ann.value());
+        }
+        return t;
+
+    }
+
+    protected XSLTTransform getXsltTransformAnn(Annotation[] anns, MediaType mt) {
+        XSLTTransform ann = AnnotationUtils.getAnnotation(anns, XSLTTransform.class);
+        if (ann != null && ann.type() != XSLTTransform.TransformType.CLIENT) {
+            if (ann.mediaTypes().length > 0) {
+                for (String s : ann.mediaTypes()) {
+                    if (mt.isCompatible(JAXRSUtils.toMediaType(s))) {
+                        return ann;
+                    }
+                }
+                return null;
+            }
+            return ann;
+        }
+        return null;
+    }
+
+
+
+    protected Templates getInTemplates(Annotation[] anns, MediaType mt) {
+        Templates t = createTemplatesFromContext();
+        if (t != null) {
+            return t;
+        }
+        t = inTemplates != null ? inTemplates
+            : inMediaTemplates != null ? inMediaTemplates.get(mt.getType() + "/" + mt.getSubtype()) : null;
+        if (t == null) {
+            t = getAnnotationTemplates(anns);
+        }
+        return t;
+    }
+
+    protected Templates getOutTemplates(Annotation[] anns, MediaType mt) {
+        Templates t = createTemplatesFromContext();
+        if (t != null) {
+            return t;
+        }
+        t = outTemplates != null ? outTemplates
+            : outMediaTemplates != null ? outMediaTemplates.get(mt.getType() + "/" + mt.getSubtype()) : null;
+        if (t == null) {
+            t = getAnnotationTemplates(anns);
+        }
+        return t;
+    }
+
+    @Override
+    protected Object unmarshalFromInputStream(Unmarshaller unmarshaller, InputStream is,
+                                              Annotation[] anns, MediaType mt)
+        throws JAXBException {
+        try {
+
+            Templates t = createTemplates(getInTemplates(anns, mt), inParamsMap, inProperties);
+            if (t == null && supportJaxbOnly) {
+                return super.unmarshalFromInputStream(unmarshaller, is, anns, mt);
+            }
+
+            if (unmarshaller.getClass().getName().contains("eclipse")) {
+                //eclipse MOXy doesn't work properly with the XMLFilter/Reader thing
+                //so we need to bounce through a DOM
+                Source reader = new StaxSource(StaxUtils.createXMLStreamReader(is));
+                DOMResult dom = new DOMResult();
+                t.newTransformer().transform(reader, dom);
+                return unmarshaller.unmarshal(dom.getNode());
+            }
+            XMLFilter filter;
+            try {
+                filter = factory.newXMLFilter(t);
+            } catch (TransformerConfigurationException ex) {
+                TemplatesImpl ti = (TemplatesImpl)t;
+                filter = factory.newXMLFilter(ti.getTemplates());
+                trySettingProperties(filter, ti);
+            }
+            XMLReader reader = new StaxSource(StaxUtils.createXMLStreamReader(is));
+            filter.setParent(reader);
+            SAXSource source = new SAXSource();
+            source.setXMLReader(filter);
+            if (systemId != null) {
+                source.setSystemId(systemId);
+            }
+            return unmarshaller.unmarshal(source);
+        } catch (TransformerException ex) {
+            LOG.warning("Transformation exception : " + ex.getMessage());
+            throw ExceptionUtils.toInternalServerErrorException(ex, null);
+        }
+    }
+
+    private void trySettingProperties(Object filter, TemplatesImpl ti) {
+        try {
+            //Saxon doesn't allow creating a Filter or Handler from anything other than it's original
+            //Templates.  That then requires setting the parameters after the fact, but there
+            //isn't a standard API for that, so we have to grab the Transformer via reflection to
+            //set the parameters.
+            Transformer tr = (Transformer)filter.getClass().getMethod("getTransformer").invoke(filter);
+            tr.setURIResolver(ti.resolver);
+            for (Map.Entry<String, Object> entry : ti.transformParameters.entrySet()) {
+                tr.setParameter(entry.getKey(), entry.getValue());
+            }
+            for (Map.Entry<String, String> entry : ti.outProps.entrySet()) {
+                tr.setOutputProperty(entry.getKey(), entry.getValue());
+            }
+        } catch (Exception e) {
+            LOG.log(Level.WARNING, "Could not set properties for transfomer", e);
+        }
+    }
+
+    @Override
+    protected Object unmarshalFromReader(Unmarshaller unmarshaller, XMLStreamReader reader,
+                                         Annotation[] anns, MediaType mt)
+        throws JAXBException {
+        CachedOutputStream out = new CachedOutputStream();
+        try {
+            XMLStreamWriter writer = StaxUtils.createXMLStreamWriter(out);
+            StaxUtils.copy(new StaxSource(reader), writer);
+            writer.writeEndDocument();
+            writer.flush();
+            writer.close();
+            return unmarshalFromInputStream(unmarshaller, out.getInputStream(), anns, mt);
+        } catch (Exception ex) {
+            throw ExceptionUtils.toBadRequestException(ex, null);
+        }
+    }
+
+    @Override
+    protected void marshalToWriter(Marshaller ms, Object obj, XMLStreamWriter writer,
+                                   Annotation[] anns, MediaType mt)
+        throws Exception {
+        CachedOutputStream out = new CachedOutputStream();
+        marshalToOutputStream(ms, obj, out, anns, mt);
+
+        StaxUtils.copy(new StreamSource(out.getInputStream()), writer);
+    }
+
+    @Override
+    protected void addAttachmentMarshaller(Marshaller ms) {
+        // complete
+    }
+
+    protected Result getStreamResult(OutputStream os, Annotation[] anns, MediaType mt) throws Exception {
+        return new StreamResult(os);
+    }
+
+    @Override
+    protected void marshalToOutputStream(Marshaller ms, Object obj, OutputStream os,
+                                         Annotation[] anns, MediaType mt)
+        throws Exception {
+
+        Templates t = createTemplates(getOutTemplates(anns, mt), outParamsMap, outProperties);
+        if (t == null && supportJaxbOnly) {
+            super.marshalToOutputStream(ms, obj, os, anns, mt);
+            return;
+        }
+        org.apache.cxf.common.jaxb.JAXBUtils.setMinimumEscapeHandler(ms);
+        TransformerHandler th;
+        try {
+            th = factory.newTransformerHandler(t);
+        } catch (TransformerConfigurationException ex) {
+            TemplatesImpl ti = (TemplatesImpl)t;
+            th = factory.newTransformerHandler(ti.getTemplates());
+            this.trySettingProperties(th, ti);
+        }
+        Result result = getStreamResult(os, anns, mt);
+        if (systemId != null) {
+            result.setSystemId(systemId);
+        }
+        th.setResult(result);
+
+        if (getContext() == null) {
+            th.startDocument();
+        }
+        ms.marshal(obj, th);
+        if (getContext() == null) {
+            th.endDocument();
+        }
+    }
+
+    public void setOutTemplate(String loc) {
+        outTemplates = createTemplates(loc);
+    }
+
+    public void setInTemplate(String loc) {
+        inTemplates = createTemplates(loc);
+    }
+
+    public void setInMediaTemplates(Map<String, String> map) {
+        inMediaTemplates = new HashMap<>();
+        for (Map.Entry<String, String> entry : map.entrySet()) {
+            inMediaTemplates.put(entry.getKey(), createTemplates(entry.getValue()));
+        }
+    }
+
+    public void setOutMediaTemplates(Map<String, String> map) {
+        outMediaTemplates = new HashMap<>();
+        for (Map.Entry<String, String> entry : map.entrySet()) {
+            outMediaTemplates.put(entry.getKey(), createTemplates(entry.getValue()));
+        }
+    }
+
+    public void setResolver(URIResolver resolver) {
+        uriResolver = resolver;
+        if (factory != null) {
+            factory.setURIResolver(uriResolver);
+        }
+    }
+
+    public void setSystemId(String system) {
+        systemId = system;
+    }
+
+    public void setInParameters(Map<String, Object> inParams) {
+        this.inParamsMap = inParams;
+    }
+
+    public void setOutParameters(Map<String, Object> outParams) {
+        this.outParamsMap = outParams;
+    }
+
+    public void setInProperties(Map<String, String> inProps) {
+        this.inProperties = inProps;
+    }
+
+    public void setOutProperties(Map<String, String> outProps) {
+        this.outProperties = outProps;
+    }
+
+    public void setInClassNames(List<String> classNames) {
+        inClassesToHandle = classNames;
+    }
+
+    public boolean inClassCanBeHandled(String className) {
+        return inClassesToHandle != null && inClassesToHandle.contains(className);
+    }
+
+    public void setOutClassNames(List<String> classNames) {
+        outClassesToHandle = classNames;
+    }
+
+    public boolean outClassCanBeHandled(String className) {
+        return outClassesToHandle != null && outClassesToHandle.contains(className);
+    }
+
+    protected Templates createTemplates(Templates templates,
+                                      Map<String, Object> configuredParams,
+                                      Map<String, String> outProps) {
+        if (templates == null) {
+            if (supportJaxbOnly) {
+                return null;
+            }
+            LOG.severe("No template is available");
+            throw ExceptionUtils.toInternalServerErrorException(null, null);
+        }
+
+        TemplatesImpl templ = new TemplatesImpl(templates, uriResolver);
+        MessageContext mc = getContext();
+        if (mc != null) {
+            UriInfo ui = mc.getUriInfo();
+            MultivaluedMap<String, String> params = ui.getPathParameters();
+            for (Map.Entry<String, List<String>> entry : params.entrySet()) {
+                String value = entry.getValue().get(0);
+                int ind = value.indexOf(';');
+                if (ind > 0) {
+                    value = value.substring(0, ind);
+                }
+                templ.setTransformerParameter(entry.getKey(), value);
+            }
+
+            List<PathSegment> segments = ui.getPathSegments();
+            if (!segments.isEmpty()) {
+                setTransformParameters(templ, segments.get(segments.size() - 1).getMatrixParameters());
+            }
+            setTransformParameters(templ, ui.getQueryParameters());
+            templ.setTransformerParameter(ABSOLUTE_PATH_PARAMETER, ui.getAbsolutePath().toString());
+            templ.setTransformerParameter(RELATIVE_PATH_PARAMETER, ui.getPath());
+            templ.setTransformerParameter(BASE_PATH_PARAMETER, ui.getBaseUri().toString());
+            if (configuredParams != null) {
+                for (Map.Entry<String, Object> entry : configuredParams.entrySet()) {
+                    templ.setTransformerParameter(entry.getKey(), entry.getValue());
+                }
+            }
+        }
+        if (outProps != null) {
+            templ.setOutProperties(outProps);
+        }
+
+        return templ;
+    }
+
+    private void setTransformParameters(TemplatesImpl templ, MultivaluedMap<String, String> params) {
+        for (Map.Entry<String, List<String>> entry : params.entrySet()) {
+            templ.setTransformerParameter(entry.getKey(), entry.getValue().get(0));
+        }
+    }
+
+    protected Templates createTemplates(String loc) {
+        try {
+            return createTemplates(ResourceUtils.getResourceURL(loc, this.getBus()));
+        } catch (Exception ex) {
+            LOG.warning("No template can be created : " + ex.getMessage());
+        }
+        return null;
+    }
+
+    protected Templates createTemplatesFromContext() {
+        MessageContext mc = getContext();
+        if (mc != null) {
+            String template = (String)mc.getContextualProperty(XSLT_TEMPLATE_PROPERTY);
+            if (template != null) {
+                return createTemplates(template);
+            }
+        }
+        return null;
+    }
+
+    protected Templates createTemplates(URL urlStream) {
+        if (urlStream == null) {
+            return null;
+        }
+
+        try (Reader r = new BufferedReader(
+                           new InputStreamReader(urlStream.openStream(), StandardCharsets.UTF_8))) {
+            Source source = new StreamSource(r);
+            source.setSystemId(urlStream.toExternalForm());
+            if (factory == null) {
+                factory = (SAXTransformerFactory)TransformerFactory.newInstance();
+                factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, secureProcessing);
+                if (uriResolver != null) {
+                    factory.setURIResolver(uriResolver);
+                }
+            }
+            return factory.newTemplates(source);
+
+        } catch (Exception ex) {
+            LOG.warning("No template can be created : " + ex.getMessage());
+        }
+        return null;
+    }
+
+    public void setRefreshTemplates(boolean refresh) {
+        this.refreshTemplates = refresh;
+    }
+
+    public void setSecureProcessing(boolean secureProcessing) {
+        this.secureProcessing = secureProcessing;
+    }
+
+    private static class TemplatesImpl implements Templates {
+
+        private Templates templates;
+        private URIResolver resolver;
+        private Map<String, Object> transformParameters = new HashMap<>();
+        private Map<String, String> outProps = new HashMap<>();
+
+        TemplatesImpl(Templates templates, URIResolver resolver) {
+            this.templates = templates;
+            this.resolver = resolver;
+        }
+
+        public Templates getTemplates() {
+            return templates;
+        }
+
+        public void setTransformerParameter(String name, Object value) {
+            transformParameters.put(name, value);
+        }
+
+        public void setOutProperties(Map<String, String> props) {
+            this.outProps = props;
+        }
+
+        public Properties getOutputProperties() {
+            return templates.getOutputProperties();
+        }
+
+        public Transformer newTransformer() throws TransformerConfigurationException {
+            Transformer tr = templates.newTransformer();
+            tr.setURIResolver(resolver);
+            for (Map.Entry<String, Object> entry : transformParameters.entrySet()) {
+                tr.setParameter(entry.getKey(), entry.getValue());
+            }
+            for (Map.Entry<String, String> entry : outProps.entrySet()) {
+                tr.setOutputProperty(entry.getKey(), entry.getValue());
+            }
+            return tr;
+        }
+
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/security/JAASAuthenticationFilter.java b/transform/src/patch/java/org/apache/cxf/jaxrs/security/JAASAuthenticationFilter.java
new file mode 100644
index 0000000..4dd05e3
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/security/JAASAuthenticationFilter.java
@@ -0,0 +1,170 @@
+/**
+ * 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.security;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.annotation.Priority;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.Configuration;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.UriBuilder;
+
+import org.apache.cxf.interceptor.security.JAASLoginInterceptor;
+import org.apache.cxf.interceptor.security.NamePasswordCallbackHandler;
+import org.apache.cxf.jaxrs.impl.HttpHeadersImpl;
+import org.apache.cxf.jaxrs.utils.HttpUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.message.Message;
+
+@PreMatching
+@Priority(Priorities.AUTHENTICATION)
+public class JAASAuthenticationFilter implements ContainerRequestFilter {
+
+    private static final List<MediaType> HTML_MEDIA_TYPES =
+        Arrays.asList(MediaType.APPLICATION_XHTML_XML_TYPE, MediaType.TEXT_HTML_TYPE);
+
+    private URI redirectURI;
+    private String realmName;
+    private boolean ignoreBasePath = true;
+
+    private JAASLoginInterceptor interceptor;
+
+    public JAASAuthenticationFilter() {
+        interceptor = new JAASLoginInterceptor() {
+            protected CallbackHandler getCallbackHandler(String name, String password) {
+                return JAASAuthenticationFilter.this.getCallbackHandler(name, password);
+            }
+        };
+        interceptor.setUseDoAs(false);
+    }
+
+    @Deprecated
+    public void setRolePrefix(String name) {
+        interceptor.setRolePrefix(name);
+    }
+
+
+    public void setIgnoreBasePath(boolean ignore) {
+        this.ignoreBasePath = ignore;
+    }
+
+    public void setContextName(String name) {
+        interceptor.setContextName(name);
+    }
+
+    public void setLoginConfig(Configuration config) {
+        interceptor.setLoginConfig(config);
+    }
+
+    public void setRoleClassifier(String rc) {
+        interceptor.setRoleClassifier(rc);
+    }
+
+    public void setRoleClassifierType(String rct) {
+        interceptor.setRoleClassifierType(rct);
+    }
+
+
+    public void setRedirectURI(String uri) {
+        this.redirectURI = URI.create(uri);
+    }
+
+    public void setRealmName(String name) {
+        this.realmName = name;
+    }
+
+    protected CallbackHandler getCallbackHandler(String name, String password) {
+        return new NamePasswordCallbackHandler(name, password);
+    }
+
+    @Override
+    public void filter(ContainerRequestContext context) {
+        Message m = JAXRSUtils.getCurrentMessage();
+        try {
+            interceptor.handleMessage(m);
+        } catch (SecurityException ex) {
+            context.abortWith(handleAuthenticationException(ex, m));
+        }
+    }
+
+    protected Response handleAuthenticationException(SecurityException ex, Message m) {
+        HttpHeaders headers = new HttpHeadersImpl(m);
+        if (redirectURI != null && isRedirectPossible(headers)) {
+
+            final URI finalRedirectURI;
+
+            if (!redirectURI.isAbsolute()) {
+                String endpointAddress = HttpUtils.getEndpointAddress(m);
+                Object basePathProperty = m.get(Message.BASE_PATH);
+                if (ignoreBasePath && basePathProperty != null && !"/".equals(basePathProperty)) {
+                    int index = endpointAddress.lastIndexOf(basePathProperty.toString());
+                    if (index != -1) {
+                        endpointAddress = endpointAddress.substring(0, index);
+                    }
+                }
+                finalRedirectURI = UriBuilder.fromUri(endpointAddress).path(redirectURI.toString()).build();
+            } else {
+                finalRedirectURI = redirectURI;
+            }
+
+            return Response.status(getRedirectStatus()).
+                    header(HttpHeaders.LOCATION, finalRedirectURI).build();
+        }
+        ResponseBuilder builder = Response.status(Response.Status.UNAUTHORIZED);
+
+        StringBuilder sb = new StringBuilder();
+
+        List<String> authHeader = headers.getRequestHeader(HttpHeaders.AUTHORIZATION);
+        if (authHeader != null && !authHeader.isEmpty()) {
+            // should HttpHeadersImpl do it ?
+            String[] authValues = authHeader.get(0).split(" ");
+            if (authValues.length > 0) {
+                sb.append(authValues[0]);
+            }
+        } else {
+            sb.append("Basic");
+        }
+        if (realmName != null) {
+            sb.append(" realm=\"").append(realmName).append('"');
+        }
+        builder.header(HttpHeaders.WWW_AUTHENTICATE, sb.toString());
+
+        return builder.build();
+    }
+
+    protected Response.Status getRedirectStatus() {
+        return Response.Status.TEMPORARY_REDIRECT;
+    }
+
+    protected boolean isRedirectPossible(HttpHeaders headers) {
+        List<MediaType> clientTypes = headers.getAcceptableMediaTypes();
+        return !JAXRSUtils.intersectMimeTypes(clientTypes, HTML_MEDIA_TYPES, false)
+                          .isEmpty();
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/security/KerberosAuthenticationFilter.java b/transform/src/patch/java/org/apache/cxf/jaxrs/security/KerberosAuthenticationFilter.java
new file mode 100644
index 0000000..1c7a164
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/security/KerberosAuthenticationFilter.java
@@ -0,0 +1,251 @@
+/**
+ * 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.security;
+
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.List;
+import java.util.logging.Logger;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.Configuration;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.security.SimplePrincipal;
+import org.apache.cxf.common.security.SimpleSecurityContext;
+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.jaxrs.ext.MessageContext;
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.security.SecurityContext;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+@PreMatching
+public class KerberosAuthenticationFilter implements ContainerRequestFilter {
+
+    private static final Logger LOG = LogUtils.getL7dLogger(KerberosAuthenticationFilter.class);
+
+    private static final String NEGOTIATE_SCHEME = "Negotiate";
+    private static final String PROPERTY_USE_KERBEROS_OID = "auth.spnego.useKerberosOid";
+    private static final String KERBEROS_OID = "1.2.840.113554.1.2.2";
+    private static final String SPNEGO_OID = "1.3.6.1.5.5.2";
+
+    private MessageContext messageContext;
+    private CallbackHandler callbackHandler;
+    private Configuration loginConfig;
+    private String loginContextName = "";
+    private String servicePrincipalName;
+    private String realm;
+
+    @Override
+    public void filter(ContainerRequestContext context) {
+
+        List<String> authHeaders = messageContext.getHttpHeaders()
+            .getRequestHeader(HttpHeaders.AUTHORIZATION);
+        if (authHeaders == null || authHeaders.size() != 1) {
+            LOG.fine("No Authorization header is available");
+            throw ExceptionUtils.toNotAuthorizedException(null, getFaultResponse());
+        }
+        String[] authPair = authHeaders.get(0).split(" ");
+        if (authPair.length != 2 || !NEGOTIATE_SCHEME.equalsIgnoreCase(authPair[0])) {
+            LOG.fine("Negotiate Authorization scheme is expected");
+            throw ExceptionUtils.toNotAuthorizedException(null, getFaultResponse());
+        }
+
+        byte[] serviceTicket = getServiceTicket(authPair[1]);
+
+        try {
+            Subject serviceSubject = loginAndGetSubject();
+
+            GSSContext gssContext = createGSSContext();
+
+            Subject.doAs(serviceSubject, new ValidateServiceTicketAction(gssContext, serviceTicket));
+
+            GSSName srcName = gssContext.getSrcName();
+            if (srcName == null) {
+                throw ExceptionUtils.toNotAuthorizedException(null, getFaultResponse());
+            }
+
+            String complexUserName = srcName.toString();
+
+            String simpleUserName = complexUserName;
+            int index = simpleUserName.lastIndexOf('@');
+            if (index > 0) {
+                simpleUserName = simpleUserName.substring(0, index);
+            }
+            Message m = JAXRSUtils.getCurrentMessage();
+            m.put(SecurityContext.class, createSecurityContext(simpleUserName, complexUserName, gssContext));
+
+            if (!gssContext.getCredDelegState()) {
+                gssContext.dispose();
+            }
+
+        } catch (LoginException e) {
+            LOG.fine("Unsuccessful JAAS login for the service principal: " + e.getMessage());
+            throw ExceptionUtils.toNotAuthorizedException(e, getFaultResponse());
+        } catch (GSSException e) {
+            LOG.fine("GSS API exception: " + e.getMessage());
+            throw ExceptionUtils.toNotAuthorizedException(e, getFaultResponse());
+        } catch (PrivilegedActionException e) {
+            LOG.fine("PrivilegedActionException: " + e.getMessage());
+            throw ExceptionUtils.toNotAuthorizedException(e, getFaultResponse());
+        }
+    }
+
+    protected SecurityContext createSecurityContext(String simpleUserName, String complexUserName,
+                                                    GSSContext gssContext) {
+        return new KerberosSecurityContext(new KerberosPrincipal(simpleUserName, complexUserName), gssContext);
+    }
+
+    protected GSSContext createGSSContext() throws GSSException {
+        boolean useKerberosOid = PropertyUtils.isTrue(
+            messageContext.getContextualProperty(PROPERTY_USE_KERBEROS_OID));
+        Oid oid = new Oid(useKerberosOid ? KERBEROS_OID : SPNEGO_OID);
+
+        GSSManager gssManager = GSSManager.getInstance();
+
+        String spn = getCompleteServicePrincipalName();
+        GSSName gssService = gssManager.createName(spn, null);
+
+        return gssManager.createContext(gssService.canonicalize(oid),
+                   oid, null, GSSContext.DEFAULT_LIFETIME);
+    }
+
+    protected Subject loginAndGetSubject() throws LoginException {
+
+        // The login without a callback can work if
+        // - Kerberos keytabs are used with a principal name set in the JAAS config
+        // - Kerberos is integrated into the OS logon process
+        //   meaning that a process which runs this code has the
+        //   user identity
+
+        final LoginContext lc;
+        if (!StringUtils.isEmpty(loginContextName) || loginConfig != null) {
+            lc = new LoginContext(loginContextName, null, callbackHandler, loginConfig);
+        } else {
+            LOG.fine("LoginContext can not be initialized");
+            throw new LoginException();
+        }
+        lc.login();
+        return lc.getSubject();
+    }
+
+    private byte[] getServiceTicket(String encodedServiceTicket) {
+        try {
+            return Base64Utility.decode(encodedServiceTicket);
+        } catch (Base64Exception ex) {
+            throw ExceptionUtils.toNotAuthorizedException(null, getFaultResponse());
+        }
+    }
+
+    private static Response getFaultResponse() {
+        return JAXRSUtils.toResponseBuilder(401).header(HttpHeaders.WWW_AUTHENTICATE, NEGOTIATE_SCHEME).build();
+    }
+
+    protected String getCompleteServicePrincipalName() {
+        String name = servicePrincipalName == null
+            ? "HTTP/" + messageContext.getUriInfo().getBaseUri().getHost() : servicePrincipalName;
+        if (realm != null) {
+            name += "@" + realm;
+        }
+        return name;
+
+
+    }
+
+    @Context
+    public void setMessageContext(MessageContext context) {
+        this.messageContext = context;
+    }
+
+    public void setLoginContextName(String contextName) {
+        this.loginContextName = contextName;
+    }
+
+    public void setServicePrincipalName(String servicePrincipalName) {
+        this.servicePrincipalName = servicePrincipalName;
+    }
+
+    public void setRealm(String realm) {
+        this.realm = realm;
+    }
+
+    public void setCallbackHandler(CallbackHandler callbackHandler) {
+        this.callbackHandler = callbackHandler;
+    }
+
+    private static final class ValidateServiceTicketAction implements PrivilegedExceptionAction<byte[]> {
+        private final GSSContext context;
+        private final byte[] token;
+
+        private ValidateServiceTicketAction(GSSContext context, byte[] token) {
+            this.context = context;
+            this.token = token;
+        }
+
+        public byte[] run() throws GSSException {
+            return context.acceptSecContext(token, 0, token.length);
+        }
+    }
+
+    public static class KerberosPrincipal extends SimplePrincipal {
+        private static final long serialVersionUID = 1L;
+        private String complexName;
+        public KerberosPrincipal(String simpleName, String complexName) {
+            super(simpleName);
+            this.complexName = complexName;
+        }
+
+        public String getKerberosName() {
+            return complexName;
+        }
+    }
+
+    public static class KerberosSecurityContext extends SimpleSecurityContext {
+        private GSSContext context;
+        public KerberosSecurityContext(KerberosPrincipal principal,
+                                       GSSContext context) {
+            super(principal);
+            this.context = context;
+        }
+
+        public GSSContext getGSSContext() {
+            return context;
+        }
+    }
+
+
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java b/transform/src/patch/java/org/apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java
new file mode 100644
index 0000000..290c223
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java
@@ -0,0 +1,640 @@
+/**
+ * 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.servlet;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.ws.rs.core.Application;
+
+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.PrimitiveUtils;
+import org.apache.cxf.common.util.PropertyUtils;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.feature.Feature;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.interceptor.Interceptor;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.lifecycle.PerRequestResourceProvider;
+import org.apache.cxf.jaxrs.lifecycle.ResourceProvider;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.jaxrs.model.ApplicationInfo;
+import org.apache.cxf.jaxrs.model.ProviderInfo;
+import org.apache.cxf.jaxrs.provider.ProviderFactory;
+import org.apache.cxf.jaxrs.utils.InjectionUtils;
+import org.apache.cxf.jaxrs.utils.ResourceUtils;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.service.invoker.Invoker;
+import org.apache.cxf.transport.http.DestinationRegistry;
+import org.apache.cxf.transport.servlet.CXFNonSpringServlet;
+
+public class CXFNonSpringJaxrsServlet extends CXFNonSpringServlet {
+
+    private static final long serialVersionUID = -8916352798780577499L;
+
+    private static final Logger LOG = LogUtils.getL7dLogger(CXFNonSpringJaxrsServlet.class);
+
+    private static final String USER_MODEL_PARAM = "user.model";
+    private static final String SERVICE_ADDRESS_PARAM = "jaxrs.address";
+    private static final String IGNORE_APP_PATH_PARAM = "jaxrs.application.address.ignore";
+    private static final String SERVICE_CLASSES_PARAM = "jaxrs.serviceClasses";
+    private static final String PROVIDERS_PARAM = "jaxrs.providers";
+    private static final String FEATURES_PARAM = "jaxrs.features";
+    private static final String OUT_INTERCEPTORS_PARAM = "jaxrs.outInterceptors";
+    private static final String OUT_FAULT_INTERCEPTORS_PARAM = "jaxrs.outFaultInterceptors";
+    private static final String IN_INTERCEPTORS_PARAM = "jaxrs.inInterceptors";
+    private static final String INVOKER_PARAM = "jaxrs.invoker";
+    private static final String SERVICE_SCOPE_PARAM = "jaxrs.scope";
+    private static final String EXTENSIONS_PARAM = "jaxrs.extensions";
+    private static final String LANGUAGES_PARAM = "jaxrs.languages";
+    private static final String PROPERTIES_PARAM = "jaxrs.properties";
+    private static final String SCHEMAS_PARAM = "jaxrs.schemaLocations";
+    private static final String DOC_LOCATION_PARAM = "jaxrs.documentLocation";
+    private static final String STATIC_SUB_RESOLUTION_PARAM = "jaxrs.static.subresources";
+    private static final String SERVICE_SCOPE_SINGLETON = "singleton";
+    private static final String SERVICE_SCOPE_REQUEST = "prototype";
+
+    private static final String PARAMETER_SPLIT_CHAR = "class.parameter.split.char";
+    private static final String DEFAULT_PARAMETER_SPLIT_CHAR = ",";
+    private static final String SPACE_PARAMETER_SPLIT_CHAR = "space";
+
+    private static final String JAXRS_APPLICATION_PARAM = "javax.ws.rs.Application";
+
+    private ClassLoader classLoader;
+    private Application application;
+
+    public CXFNonSpringJaxrsServlet() {
+
+    }
+
+    public CXFNonSpringJaxrsServlet(Application app) {
+        this.application = app;
+    }
+
+    public CXFNonSpringJaxrsServlet(Object singletonService) {
+        this(Collections.singleton(singletonService));
+    }
+    public CXFNonSpringJaxrsServlet(Set<Object> applicationSingletons) {
+        this(new ApplicationImpl(applicationSingletons));
+    }
+
+    public CXFNonSpringJaxrsServlet(Application app, DestinationRegistry destinationRegistry, Bus bus) {
+        super(destinationRegistry, bus);
+        this.application = app;
+    }
+
+    @Override
+    public void init(ServletConfig servletConfig) throws ServletException {
+        super.init(servletConfig);
+
+        if (getApplication() != null) {
+            createServerFromApplication(servletConfig);
+            return;
+        }
+
+        String applicationClass = servletConfig.getInitParameter(JAXRS_APPLICATION_PARAM);
+        if (applicationClass != null) {
+            createServerFromApplication(applicationClass, servletConfig);
+            return;
+        }
+
+        String splitChar = getParameterSplitChar(servletConfig);
+        JAXRSServerFactoryBean bean = new JAXRSServerFactoryBean();
+        bean.setBus(getBus());
+
+        String address = servletConfig.getInitParameter(SERVICE_ADDRESS_PARAM);
+        if (address == null) {
+            address = "/";
+        }
+        bean.setAddress(address);
+
+        bean.setStaticSubresourceResolution(getStaticSubResolutionValue(servletConfig));
+
+        String modelRef = servletConfig.getInitParameter(USER_MODEL_PARAM);
+        if (modelRef != null) {
+            bean.setModelRef(modelRef.trim());
+        }
+        setDocLocation(bean, servletConfig);
+        setSchemasLocations(bean, servletConfig);
+        setAllInterceptors(bean, servletConfig, splitChar);
+        setInvoker(bean, servletConfig);
+
+        Map<Class<?>, Map<String, List<String>>> resourceClasses =
+            getServiceClasses(servletConfig, modelRef != null, splitChar);
+        Map<Class<?>, ResourceProvider> resourceProviders =
+            getResourceProviders(servletConfig, resourceClasses);
+
+        List<?> providers = getProviders(servletConfig, splitChar);
+
+        bean.setResourceClasses(new ArrayList<Class<?>>(resourceClasses.keySet()));
+        bean.setProviders(providers);
+        for (Map.Entry<Class<?>, ResourceProvider> entry : resourceProviders.entrySet()) {
+            bean.setResourceProvider(entry.getKey(), entry.getValue());
+        }
+        setExtensions(bean, servletConfig);
+
+        List<? extends Feature> features = getFeatures(servletConfig, splitChar);
+        bean.getFeatures().addAll(features);
+
+        bean.create();
+    }
+
+    protected String getParameterSplitChar(ServletConfig servletConfig) {
+        String param = servletConfig.getInitParameter(PARAMETER_SPLIT_CHAR);
+        if (!StringUtils.isEmpty(param) && SPACE_PARAMETER_SPLIT_CHAR.equals(param.trim())) {
+            return " ";
+        }
+        return DEFAULT_PARAMETER_SPLIT_CHAR;
+    }
+    protected boolean getStaticSubResolutionValue(ServletConfig servletConfig) {
+        String param = servletConfig.getInitParameter(STATIC_SUB_RESOLUTION_PARAM);
+        if (param != null) {
+            return Boolean.valueOf(param.trim());
+        }
+        return false;
+    }
+
+    protected void setExtensions(JAXRSServerFactoryBean bean, ServletConfig servletConfig) {
+        bean.setExtensionMappings(
+             CastUtils.cast((Map<?, ?>)parseMapSequence(servletConfig.getInitParameter(EXTENSIONS_PARAM))));
+        bean.setLanguageMappings(
+             CastUtils.cast((Map<?, ?>)parseMapSequence(servletConfig.getInitParameter(LANGUAGES_PARAM))));
+        Map<String, Object> properties = CastUtils.cast(
+                parseMapSequence(servletConfig.getInitParameter(PROPERTIES_PARAM)),
+                String.class, Object.class);
+        if (properties != null) {
+            bean.getProperties(true).putAll(properties);
+        }
+    }
+
+    protected void setAllInterceptors(JAXRSServerFactoryBean bean, ServletConfig servletConfig,
+                                      String splitChar)
+        throws ServletException {
+        setInterceptors(bean, servletConfig, OUT_INTERCEPTORS_PARAM, splitChar);
+        setInterceptors(bean, servletConfig, OUT_FAULT_INTERCEPTORS_PARAM, splitChar);
+        setInterceptors(bean, servletConfig, IN_INTERCEPTORS_PARAM, splitChar);
+    }
+
+    protected void setSchemasLocations(JAXRSServerFactoryBean bean, ServletConfig servletConfig) {
+        String schemas = servletConfig.getInitParameter(SCHEMAS_PARAM);
+        if (schemas == null) {
+            return;
+        }
+        String[] locations = schemas.split(" ");
+        List<String> list = new ArrayList<>();
+        for (String loc : locations) {
+            String theLoc = loc.trim();
+            if (!theLoc.isEmpty()) {
+                list.add(theLoc);
+            }
+        }
+        if (!list.isEmpty()) {
+            bean.setSchemaLocations(list);
+        }
+    }
+
+    protected void setDocLocation(JAXRSServerFactoryBean bean, ServletConfig servletConfig) {
+        String wadlLoc = servletConfig.getInitParameter(DOC_LOCATION_PARAM);
+        if (wadlLoc != null) {
+            bean.setDocLocation(wadlLoc);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    protected void setInterceptors(JAXRSServerFactoryBean bean, ServletConfig servletConfig,
+                                   String paramName,
+                                   String splitChar) throws ServletException {
+        String value = servletConfig.getInitParameter(paramName);
+        if (value == null) {
+            return;
+        }
+        String[] values = value.split(splitChar);
+        List<Interceptor<? extends Message>> list = new ArrayList<>();
+        for (String interceptorVal : values) {
+            Map<String, List<String>> props = new HashMap<>();
+            String theValue = getClassNameAndProperties(interceptorVal, props);
+            if (!theValue.isEmpty()) {
+                try {
+                    Class<?> intClass = loadClass(theValue, "Interceptor");
+                    Object object = intClass.newInstance();
+                    injectProperties(object, props);
+                    list.add((Interceptor<? extends Message>)object);
+                } catch (ServletException ex) {
+                    throw ex;
+                } catch (Exception ex) {
+                    LOG.warning("Interceptor class " + theValue + " can not be created");
+                    throw new ServletException(ex);
+                }
+            }
+        }
+        if (!list.isEmpty()) {
+            if (OUT_INTERCEPTORS_PARAM.equals(paramName)) {
+                bean.setOutInterceptors(list);
+            } else if (OUT_FAULT_INTERCEPTORS_PARAM.equals(paramName)) {
+                bean.setOutFaultInterceptors(list);
+            } else {
+                bean.setInInterceptors(list);
+            }
+        }
+    }
+
+    protected void setInvoker(JAXRSServerFactoryBean bean, ServletConfig servletConfig)
+        throws ServletException {
+        String value = servletConfig.getInitParameter(INVOKER_PARAM);
+        if (value == null) {
+            return;
+        }
+        Map<String, List<String>> props = new HashMap<>();
+        String theValue = getClassNameAndProperties(value, props);
+        if (!theValue.isEmpty()) {
+            try {
+                Class<?> intClass = loadClass(theValue, "Invoker");
+                Object object = intClass.newInstance();
+                injectProperties(object, props);
+                bean.setInvoker((Invoker)object);
+            } catch (ServletException ex) {
+                throw ex;
+            } catch (Exception ex) {
+                LOG.warning("Invoker class " + theValue + " can not be created");
+                throw new ServletException(ex);
+            }
+        }
+
+
+    }
+
+    protected Map<Class<?>, Map<String, List<String>>> getServiceClasses(ServletConfig servletConfig,
+                                            boolean modelAvailable,
+                                            String splitChar) throws ServletException {
+        String serviceBeans = servletConfig.getInitParameter(SERVICE_CLASSES_PARAM);
+        if (serviceBeans == null) {
+            if (modelAvailable) {
+                return Collections.emptyMap();
+            }
+            throw new ServletException("At least one resource class should be specified");
+        }
+        String[] classNames = serviceBeans.split(splitChar);
+        Map<Class<?>, Map<String, List<String>>> map = new HashMap<>();
+        for (String cName : classNames) {
+            Map<String, List<String>> props = new HashMap<>();
+            String theName = getClassNameAndProperties(cName, props);
+            if (!theName.isEmpty()) {
+                Class<?> cls = loadClass(theName);
+                map.put(cls, props);
+            }
+        }
+        if (map.isEmpty()) {
+            throw new ServletException("At least one resource class should be specified");
+        }
+        return map;
+    }
+
+    protected List<? extends Feature> getFeatures(ServletConfig servletConfig, String splitChar)
+        throws ServletException {
+
+        String featuresList = servletConfig.getInitParameter(FEATURES_PARAM);
+        if (featuresList == null) {
+            return Collections.< Feature >emptyList();
+        }
+        String[] classNames = featuresList.split(splitChar);
+        List< Feature > features = new ArrayList<>();
+        for (String cName : classNames) {
+            Map<String, List<String>> props = new HashMap<>();
+            String theName = getClassNameAndProperties(cName, props);
+            if (!theName.isEmpty()) {
+                Class<?> cls = loadClass(theName);
+                if (Feature.class.isAssignableFrom(cls)) {
+                    features.add((Feature)createSingletonInstance(cls, props, servletConfig));
+                }
+            }
+        }
+        return features;
+    }
+
+    protected List<?> getProviders(ServletConfig servletConfig, String splitChar) throws ServletException {
+        String providersList = servletConfig.getInitParameter(PROVIDERS_PARAM);
+        if (providersList == null) {
+            return Collections.emptyList();
+        }
+        String[] classNames = providersList.split(splitChar);
+        List<Object> providers = new ArrayList<>();
+        for (String cName : classNames) {
+            Map<String, List<String>> props = new HashMap<>();
+            String theName = getClassNameAndProperties(cName, props);
+            if (!theName.isEmpty()) {
+                Class<?> cls = loadClass(theName);
+                providers.add(createSingletonInstance(cls, props, servletConfig));
+            }
+        }
+        return providers;
+    }
+
+    private String getClassNameAndProperties(String cName, Map<String, List<String>> props) {
+        String theName = cName.trim();
+        int ind = theName.indexOf('(');
+        if (ind != -1 && theName.endsWith(")")) {
+            props.putAll(parseMapListSequence(theName.substring(ind + 1, theName.length() - 1)));
+            theName = theName.substring(0, ind).trim();
+        }
+        return theName;
+    }
+
+    protected static Map<String, List<String>> parseMapListSequence(String sequence) {
+        if (sequence != null) {
+            sequence = sequence.trim();
+            Map<String, List<String>> map = new HashMap<>();
+            String[] pairs = sequence.split(" ");
+            for (String pair : pairs) {
+                String thePair = pair.trim();
+                if (thePair.length() == 0) {
+                    continue;
+                }
+                String[] values = thePair.split("=");
+                String key;
+                String value;
+                if (values.length == 2) {
+                    key = values[0].trim();
+                    value = values[1].trim();
+                } else {
+                    key = thePair;
+                    value = "";
+                }
+                List<String> list = map.get(key);
+                if (list == null) {
+                    list = new LinkedList<>();
+                    map.put(key, list);
+                }
+                list.add(value);
+            }
+            return map;
+        }
+        return Collections.emptyMap();
+    }
+
+    protected Map<Class<?>, ResourceProvider> getResourceProviders(ServletConfig servletConfig,
+            Map<Class<?>, Map<String, List<String>>> resourceClasses) throws ServletException {
+        String scope = servletConfig.getInitParameter(SERVICE_SCOPE_PARAM);
+        if (scope != null && !SERVICE_SCOPE_SINGLETON.equals(scope)
+            && !SERVICE_SCOPE_REQUEST.equals(scope)) {
+            throw new ServletException("Only singleton and prototype scopes are supported");
+        }
+        boolean isPrototype = SERVICE_SCOPE_REQUEST.equals(scope);
+        Map<Class<?>, ResourceProvider> map = new HashMap<>();
+        for (Map.Entry<Class<?>, Map<String, List<String>>> entry : resourceClasses.entrySet()) {
+            Class<?> c = entry.getKey();
+            map.put(c, isPrototype ? new PerRequestResourceProvider(c)
+                                   : new SingletonResourceProvider(
+                                         createSingletonInstance(c, entry.getValue(), servletConfig),
+                                         true));
+        }
+        return map;
+    }
+
+    protected boolean isAppResourceLifecycleASingleton(Application app, ServletConfig servletConfig) {
+        String scope = servletConfig.getInitParameter(SERVICE_SCOPE_PARAM);
+        if (scope == null) {
+            scope = (String)app.getProperties().get(SERVICE_SCOPE_PARAM);
+        }
+        return SERVICE_SCOPE_SINGLETON.equals(scope);
+    }
+
+    protected Object createSingletonInstance(Class<?> cls, Map<String, List<String>> props, ServletConfig sc)
+        throws ServletException {
+        Constructor<?> c = ResourceUtils.findResourceConstructor(cls, false);
+        if (c == null) {
+            throw new ServletException("No valid constructor found for " + cls.getName());
+        }
+        boolean isApplication = Application.class.isAssignableFrom(c.getDeclaringClass());
+        try {
+            final ProviderInfo<? extends Object> provider;
+            if (c.getParameterTypes().length == 0) {
+                if (isApplication) {
+                    provider = new ApplicationInfo((Application)c.newInstance(), getBus());
+                } else {
+                    provider = new ProviderInfo<>(c.newInstance(), getBus(), false, true);
+                }
+            } else {
+                Map<Class<?>, Object> values = new HashMap<>();
+                values.put(ServletContext.class, sc.getServletContext());
+                values.put(ServletConfig.class, sc);
+                provider = ProviderFactory.createProviderFromConstructor(c, values, getBus(), isApplication, true);
+            }
+            Object instance = provider.getProvider();
+            injectProperties(instance, props);
+            configureSingleton(instance);
+            return isApplication ? provider : instance;
+        } catch (InstantiationException ex) {
+            ex.printStackTrace();
+            throw new ServletException("Resource class " + cls.getName()
+                                       + " can not be instantiated");
+        } catch (IllegalAccessException ex) {
+            ex.printStackTrace();
+            throw new ServletException("Resource class " + cls.getName()
+                                       + " can not be instantiated due to IllegalAccessException");
+        } catch (InvocationTargetException ex) {
+            ex.printStackTrace();
+            throw new ServletException("Resource class " + cls.getName()
+                                       + " can not be instantiated due to InvocationTargetException");
+        }
+    }
+
+    private void injectProperties(Object instance, Map<String, List<String>> props) {
+        if (props == null || props.isEmpty()) {
+            return;
+        }
+        Method[] methods = instance.getClass().getMethods();
+        Map<String, Method> methodsMap = new HashMap<>();
+        for (Method m : methods) {
+            methodsMap.put(m.getName(), m);
+        }
+        for (Map.Entry<String, List<String>> entry : props.entrySet()) {
+            Method m = methodsMap.get("set" + StringUtils.capitalize(entry.getKey()));
+            if (m != null) {
+                Class<?> type = m.getParameterTypes()[0];
+                Object value;
+                if (InjectionUtils.isPrimitive(type)) {
+                    value = PrimitiveUtils.read(entry.getValue().get(0), type);
+                } else {
+                    value = entry.getValue();
+                }
+                InjectionUtils.injectThroughMethod(instance, m, value);
+            }
+        }
+    }
+
+    protected void configureSingleton(Object instance) {
+
+    }
+
+    protected void createServerFromApplication(String applicationNames, ServletConfig servletConfig)
+        throws ServletException {
+
+        boolean ignoreApplicationPath = isIgnoreApplicationPath(servletConfig);
+
+        String[] classNames = applicationNames.split(getParameterSplitChar(servletConfig));
+
+        if (classNames.length > 1 && ignoreApplicationPath) {
+            throw new ServletException("\"" + IGNORE_APP_PATH_PARAM
+                + "\" parameter must be set to false for multiple Applications be supported");
+        }
+
+        for (String cName : classNames) {
+            ApplicationInfo providerApp = createApplicationInfo(cName, servletConfig);
+
+            Application app = providerApp.getProvider();
+            JAXRSServerFactoryBean bean = ResourceUtils.createApplication(
+                                                app,
+                                                ignoreApplicationPath,
+                                                getStaticSubResolutionValue(servletConfig),
+                                                isAppResourceLifecycleASingleton(app, servletConfig),
+                                                getBus());
+            String splitChar = getParameterSplitChar(servletConfig);
+            setAllInterceptors(bean, servletConfig, splitChar);
+            setInvoker(bean, servletConfig);
+            setExtensions(bean, servletConfig);
+            setDocLocation(bean, servletConfig);
+            setSchemasLocations(bean, servletConfig);
+
+            List<?> providers = getProviders(servletConfig, splitChar);
+            bean.setProviders(providers);
+            List<? extends Feature> features = getFeatures(servletConfig, splitChar);
+            bean.getFeatures().addAll(features);
+
+            bean.setBus(getBus());
+            bean.setApplicationInfo(providerApp);
+            bean.create();
+        }
+    }
+
+    protected boolean isIgnoreApplicationPath(ServletConfig servletConfig) {
+        String ignoreParam = servletConfig.getInitParameter(IGNORE_APP_PATH_PARAM);
+        return ignoreParam == null || PropertyUtils.isTrue(ignoreParam);
+    }
+
+    protected void createServerFromApplication(ServletConfig servletConfig)
+        throws ServletException {
+
+        Application app = getApplication();
+        JAXRSServerFactoryBean bean = ResourceUtils.createApplication(
+                                          app,
+                                          isIgnoreApplicationPath(servletConfig),
+                                          getStaticSubResolutionValue(servletConfig),
+                                          isAppResourceLifecycleASingleton(app, servletConfig),
+                                          getBus());
+        String splitChar = getParameterSplitChar(servletConfig);
+        setAllInterceptors(bean, servletConfig, splitChar);
+        setInvoker(bean, servletConfig);
+        setExtensions(bean, servletConfig);
+        setDocLocation(bean, servletConfig);
+        setSchemasLocations(bean, servletConfig);
+
+        List<?> providers = getProviders(servletConfig, splitChar);
+        bean.setProviders(providers);
+        List<? extends Feature> features = getFeatures(servletConfig, splitChar);
+        bean.getFeatures().addAll(features);
+
+        bean.setBus(getBus());
+        bean.setApplication(getApplication());
+        bean.create();
+    }
+
+    protected Application createApplicationInstance(String appClassName, ServletConfig servletConfig)
+        throws ServletException {
+        return null;
+    }
+    protected ApplicationInfo createApplicationInfo(String appClassName, ServletConfig servletConfig)
+        throws ServletException {
+
+        Application customApp = createApplicationInstance(appClassName, servletConfig);
+        if (customApp != null) {
+            return new ApplicationInfo(customApp, getBus());
+        }
+        Map<String, List<String>> props = new HashMap<>();
+        appClassName = getClassNameAndProperties(appClassName, props);
+        Class<?> appClass = loadApplicationClass(appClassName);
+        ApplicationInfo appInfo = (ApplicationInfo)createSingletonInstance(appClass, props, servletConfig);
+        Map<String, Object> servletProps = new HashMap<>();
+        ServletContext servletContext = servletConfig.getServletContext();
+        for (Enumeration<String> names = servletContext.getInitParameterNames(); names.hasMoreElements();) {
+            String name = names.nextElement();
+            servletProps.put(name, servletContext.getInitParameter(name));
+        }
+        for (Enumeration<String> names = servletConfig.getInitParameterNames(); names.hasMoreElements();) {
+            String name = names.nextElement();
+            servletProps.put(name, servletConfig.getInitParameter(name));
+        }
+        appInfo.setOverridingProps(servletProps);
+        return appInfo;
+    }
+
+    protected Class<?> loadApplicationClass(String appClassName) throws ServletException {
+        return loadClass(appClassName, "Application");
+    }
+
+    protected Class<?> loadClass(String cName) throws ServletException {
+        return loadClass(cName, "Resource");
+    }
+
+    protected Class<?> loadClass(String cName, String classType) throws ServletException {
+        try {
+            final Class<?> cls;
+            if (classLoader == null) {
+                cls = ClassLoaderUtils.loadClass(cName, CXFNonSpringJaxrsServlet.class);
+            } else {
+                cls = classLoader.loadClass(cName);
+            }
+            return cls;
+        } catch (ClassNotFoundException ex) {
+            throw new ServletException("No " + classType + " class " + cName.trim() + " can be found", ex);
+        }
+    }
+
+    public void setClassLoader(ClassLoader loader) {
+        this.classLoader = loader;
+    }
+
+    protected Application getApplication() {
+        return application;
+    }
+
+    private static class ApplicationImpl extends Application {
+        private Set<Object> applicationSingletons;
+        ApplicationImpl(Set<Object> applicationSingletons) {
+            this.applicationSingletons = applicationSingletons;
+        }
+        public Set<Object> getSingletons() {
+            return applicationSingletons;
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/spring/JAXRSServerFactoryBeanDefinitionParser.java b/transform/src/patch/java/org/apache/cxf/jaxrs/spring/JAXRSServerFactoryBeanDefinitionParser.java
new file mode 100644
index 0000000..2115f6d
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/spring/JAXRSServerFactoryBeanDefinitionParser.java
@@ -0,0 +1,286 @@
+/**
+ * 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.spring;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
+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.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.ClasspathScanner;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.configuration.spring.AbstractBeanDefinitionParser;
+import org.apache.cxf.endpoint.Server;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.JAXRSServiceFactoryBean;
+import org.apache.cxf.jaxrs.lifecycle.ResourceProvider;
+import org.apache.cxf.jaxrs.model.UserResource;
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
+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.config.AutowireCapableBeanFactory;
+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 JAXRSServerFactoryBeanDefinitionParser extends AbstractBeanDefinitionParser {
+    private static final Logger LOG = LogUtils.getL7dLogger(JAXRSServerFactoryBeanDefinitionParser.class);
+
+    public JAXRSServerFactoryBeanDefinitionParser() {
+        super();
+        setBeanClass(SpringJAXRSServerFactoryBean.class);
+    }
+
+    @Override
+    protected void mapAttribute(BeanDefinitionBuilder bean, Element e, String name, String val) {
+        if ("beanNames".equals(name)) {
+            String[] values = val.split(" ");
+            List<SpringResourceFactory> tempFactories = new ArrayList<>(values.length);
+            for (String v : values) {
+                String theValue = v.trim();
+                if (theValue.length() > 0) {
+                    tempFactories.add(new SpringResourceFactory(theValue));
+                }
+            }
+            bean.addPropertyValue("tempResourceProviders", tempFactories);
+        } else 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 if ("serviceAnnotation".equals(name)) {
+            bean.addPropertyValue("serviceAnnotation", val);
+        } else if ("publish".equals(name)) {
+            mapToProperty(bean, "start", val);
+        } else {
+            mapToProperty(bean, name, val);
+        }
+    }
+
+    @Override
+    protected void mapElement(ParserContext ctx, BeanDefinitionBuilder bean, Element el, String name) {
+        if ("properties".equals(name)
+            || "extensionMappings".equals(name)
+            || "languageMappings".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 ("invoker".equals(name)) {
+            setFirstChildAsProperty(el, ctx, bean, "serviceFactory.invoker");
+        } 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) || "schemaLocations".equals(name)
+            || "providers".equals(name) || "serviceBeans".equals(name)
+            || "modelBeans".equals(name)) {
+            List<?> list = ctx.getDelegate().parseListElement(el, bean.getBeanDefinition());
+            bean.addPropertyValue(name, list);
+        }  else if ("serviceFactories".equals(name)) {
+            List<?> list = ctx.getDelegate().parseListElement(el, bean.getBeanDefinition());
+            bean.addPropertyValue("resourceProviders", list);
+        } else if ("model".equals(name)) {
+            List<UserResource> resources = ResourceUtils.getResourcesFromElement(el);
+            bean.addPropertyValue("modelBeans", resources);
+        } else {
+            setFirstChildAsProperty(el, ctx, bean, name);
+        }
+    }
+
+
+    @Override
+    protected void doParse(Element element, ParserContext ctx, BeanDefinitionBuilder bean) {
+        super.doParse(element, ctx, bean);
+
+        bean.setInitMethodName("create");
+        bean.setDestroyMethodName("destroy");
+
+        // We don't really want to delay the registration of our Server
+        bean.setLazyInit(false);
+    }
+
+    @Override
+    protected String resolveId(Element elem,
+                               AbstractBeanDefinition definition,
+                               ParserContext ctx)
+        throws BeanDefinitionStoreException {
+        String id = super.resolveId(elem, definition, ctx);
+        if (StringUtils.isEmpty(id)) {
+            id = getBeanClass().getName() + "--" + definition.hashCode();
+        }
+
+        return id;
+    }
+
+    @Override
+    protected boolean hasBusProperty() {
+        return true;
+    }
+
+    public static class SpringJAXRSServerFactoryBean extends JAXRSServerFactoryBean implements
+        ApplicationContextAware {
+
+        private List<SpringResourceFactory> tempFactories;
+        private List<String> basePackages;
+        private String serviceAnnotation;
+        private ApplicationContext context;
+        private boolean serviceBeansAvailable;
+        private boolean providerBeansAvailable;
+        private boolean resourceProvidersAvailable;
+
+        public SpringJAXRSServerFactoryBean() {
+            super();
+        }
+
+        public SpringJAXRSServerFactoryBean(JAXRSServiceFactoryBean sf) {
+            super(sf);
+        }
+
+        public void destroy() {
+            Server server = super.getServer();
+            if (server != null && server.isStarted()) {
+                server.destroy();
+            }
+        }
+        @Override
+        public void setServiceBeans(List<Object> beans) {
+            super.setServiceBeans(beans);
+            this.serviceBeansAvailable = true;
+        }
+        @Override
+        public void setProviders(List<? extends Object> beans) {
+            super.setProviders(beans);
+            this.providerBeansAvailable = true;
+        }
+        public void setResourceProviders(List<ResourceProvider> rps) {
+            super.setResourceProviders(rps);
+            this.resourceProvidersAvailable = true;
+        }
+        public void setBasePackages(List<String> basePackages) {
+            this.basePackages = basePackages;
+        }
+
+        public void setServiceAnnotation(String serviceAnnotation) {
+            this.serviceAnnotation = serviceAnnotation;
+        }
+
+        public void setTempResourceProviders(List<SpringResourceFactory> providers) {
+            tempFactories = providers;
+        }
+
+        public void setApplicationContext(ApplicationContext ctx) throws BeansException {
+            this.context = ctx;
+
+            if (tempFactories != null) {
+                List<ResourceProvider> factories = new ArrayList<>(
+                    tempFactories.size());
+                for (int i = 0; i < tempFactories.size(); i++) {
+                    SpringResourceFactory factory = tempFactories.get(i);
+                    factory.setApplicationContext(ctx);
+                    factories.add(factory);
+                }
+                tempFactories.clear();
+                setResourceProviders(factories);
+            }
+            Class<? extends Annotation> serviceAnnotationClass = loadServiceAnnotationClass();
+            if (basePackages != null) {
+                try {
+                    final Map< Class< ? extends Annotation >, Collection< Class< ? > > > classes =
+                        ClasspathScanner.findClasses(basePackages, Provider.class, Path.class);
+
+                    this.setServiceBeans(createBeansFromDiscoveredClasses(context,
+                                                                          classes.get(Path.class),
+                                                                          serviceAnnotationClass));
+                    this.setProviders(createBeansFromDiscoveredClasses(context,
+                                                                       classes.get(Provider.class),
+                                                                       serviceAnnotationClass));
+                } 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);
+                }
+            } else if (serviceAnnotationClass != null
+                && !serviceBeansAvailable && !providerBeansAvailable && !resourceProvidersAvailable) {
+                discoverContextResources(serviceAnnotationClass);
+            }
+            if (bus == null) {
+                setBus(BusWiringBeanFactoryPostProcessor.addDefaultBus(ctx));
+            }
+        }
+        private void discoverContextResources(Class<? extends Annotation> serviceAnnotationClass) {
+            AbstractSpringComponentScanServer scanServer =
+                new AbstractSpringComponentScanServer(serviceAnnotationClass) { };
+            scanServer.setApplicationContext(context);
+            scanServer.setJaxrsResources(this);
+        }
+        @SuppressWarnings("unchecked")
+        private Class<? extends Annotation> loadServiceAnnotationClass() {
+            if (serviceAnnotation != null) {
+                try {
+                    return (Class<? extends Annotation>)ClassLoaderUtils.loadClass(serviceAnnotation, this.getClass());
+                } catch (Exception ex) {
+                    throw new RuntimeException(ex);
+                }
+            }
+            return null;
+        }
+    }
+    static List<Object> createBeansFromDiscoveredClasses(ApplicationContext context,
+                                                         Collection<Class<?>> classes,
+                                                         Class<? extends Annotation> serviceClassAnnotation) {
+        AutowireCapableBeanFactory beanFactory = context.getAutowireCapableBeanFactory();
+        final List< Object > providers = new ArrayList<>();
+        for (final Class< ? > clazz: classes) {
+            if (serviceClassAnnotation != null && clazz.getAnnotation(serviceClassAnnotation) == null) {
+                continue;
+            }
+            Object bean;
+            try {
+                bean = beanFactory.createBean(clazz, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
+            } catch (Exception ex) {
+                String stackTrace = ExceptionUtils.getStackTrace(ex);
+                LOG.fine("Autowire failure for a " + clazz.getName() + " bean: " + stackTrace);
+                bean = beanFactory.createBean(clazz);
+            }
+            providers.add(bean);
+        }
+        return providers;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/utils/HttpUtils.java b/transform/src/patch/java/org/apache/cxf/jaxrs/utils/HttpUtils.java
new file mode 100644
index 0000000..aa42382
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/utils/HttpUtils.java
@@ -0,0 +1,704 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.jaxrs.utils;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+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.UriBuilder;
+import javax.ws.rs.ext.RuntimeDelegate;
+import javax.ws.rs.ext.RuntimeDelegate.HeaderDelegate;
+
+import org.apache.cxf.common.i18n.BundleUtils;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.PropertyUtils;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.common.util.UrlUtils;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.jaxrs.impl.HttpHeadersImpl;
+import org.apache.cxf.jaxrs.impl.HttpServletRequestFilter;
+import org.apache.cxf.jaxrs.impl.HttpServletResponseFilter;
+import org.apache.cxf.jaxrs.impl.MetadataMap;
+import org.apache.cxf.jaxrs.impl.PathSegmentImpl;
+import org.apache.cxf.jaxrs.impl.RuntimeDelegateImpl;
+import org.apache.cxf.jaxrs.model.ParameterType;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.service.model.EndpointInfo;
+import org.apache.cxf.transport.Destination;
+import org.apache.cxf.transport.http.AbstractHTTPDestination;
+import org.apache.cxf.transport.http.Headers;
+import org.apache.cxf.transport.servlet.BaseUrlHelper;
+
+public final class HttpUtils {
+
+    private static final ResourceBundle BUNDLE = BundleUtils.getBundle(HttpUtils.class);
+    private static final Logger LOG = LogUtils.getL7dLogger(HttpUtils.class);
+
+    private static final String REQUEST_PATH_TO_MATCH = "path_to_match";
+    private static final String REQUEST_PATH_TO_MATCH_SLASH = "path_to_match_slash";
+
+    private static final String HTTP_SCHEME = "http";
+    private static final String LOCAL_HOST_IP_ADDRESS = "127.0.0.1";
+    private static final String REPLACE_LOOPBACK_PROPERTY = "replace.loopback.address.with.localhost";
+    private static final String LOCAL_HOST_IP_ADDRESS_SCHEME = "://" + LOCAL_HOST_IP_ADDRESS;
+    private static final String ANY_IP_ADDRESS = "0.0.0.0";
+    private static final String ANY_IP_ADDRESS_SCHEME = "://" + ANY_IP_ADDRESS;
+    private static final int DEFAULT_HTTP_PORT = 80;
+
+    private static final Pattern ENCODE_PATTERN = Pattern.compile("%[0-9a-fA-F][0-9a-fA-F]");
+    private static final String CHARSET_PARAMETER = "charset";
+    private static final String DOUBLE_QUOTE = "\"";
+
+    // there are more of such characters, ex, '*' but '*' is not affected by UrlEncode
+    private static final String PATH_RESERVED_CHARACTERS = "=@/:!$&\'(),;~";
+    private static final String QUERY_RESERVED_CHARACTERS = "?/,";
+    
+    private static final Set<String> KNOWN_HTTP_VERBS_WITH_NO_REQUEST_CONTENT =
+        new HashSet<>(Arrays.asList(new String[]{"GET", "HEAD", "OPTIONS", "TRACE"}));
+    private static final Set<String> KNOWN_HTTP_VERBS_WITH_NO_RESPONSE_CONTENT =
+        new HashSet<>(Arrays.asList(new String[]{"HEAD", "OPTIONS"}));
+
+    private HttpUtils() {
+    }
+
+    public static String urlDecode(String value, String enc) {
+        return UrlUtils.urlDecode(value, enc);
+    }
+
+    public static String urlDecode(String value) {
+        return UrlUtils.urlDecode(value);
+    }
+
+    public static String pathDecode(String value) {
+        return UrlUtils.pathDecode(value);
+    }
+
+    private static String componentEncode(String reservedChars, String value) {
+
+        StringBuilder buffer = null;
+        int length = value.length();
+        int startingIndex = 0;
+        for (int i = 0; i < length; i++) {
+            char currentChar = value.charAt(i);
+            if (reservedChars.indexOf(currentChar) != -1) {
+                if (buffer == null) {
+                    buffer = new StringBuilder(length + 8);
+                }
+                // If it is going to be an empty string nothing to encode.
+                if (i != startingIndex) {
+                    buffer.append(urlEncode(value.substring(startingIndex, i)));
+                }
+                buffer.append(currentChar);
+                startingIndex = i + 1;
+            }
+        }
+
+        if (buffer == null) {
+            return urlEncode(value);
+        }
+        if (startingIndex < length) {
+            buffer.append(urlEncode(value.substring(startingIndex, length)));
+        }
+
+        return buffer.toString();
+    }
+
+    public static String queryEncode(String value) {
+
+        return componentEncode(QUERY_RESERVED_CHARACTERS, value);
+    }
+
+    public static String urlEncode(String value) {
+
+        return urlEncode(value, StandardCharsets.UTF_8.name());
+    }
+
+    public static String urlEncode(String value, String enc) {
+
+        return UrlUtils.urlEncode(value, enc);
+    }
+
+    public static String pathEncode(String value) {
+
+        String result = componentEncode(PATH_RESERVED_CHARACTERS, value);
+        // URLEncoder will encode '+' to %2B but will turn ' ' into '+'
+        // We need to retain '+' and encode ' ' as %20
+        if (result.indexOf('+') != -1) {
+            result = result.replace("+", "%20");
+        }
+        if (result.indexOf("%2B") != -1) {
+            result = result.replace("%2B", "+");
+        }
+
+        return result;
+    }
+
+    public static boolean isPartiallyEncoded(String value) {
+        return ENCODE_PATTERN.matcher(value).find();
+    }
+
+    /**
+     * Encodes partially encoded string. Encode all values but those matching pattern
+     * "percent char followed by two hexadecimal digits".
+     *
+     * @param encoded fully or partially encoded string.
+     * @return fully encoded string
+     */
+    public static String encodePartiallyEncoded(String encoded, boolean query) {
+        if (encoded.length() == 0) {
+            return encoded;
+        }
+        Matcher m = ENCODE_PATTERN.matcher(encoded);
+
+        if (!m.find()) {
+            return query ? HttpUtils.queryEncode(encoded) : HttpUtils.pathEncode(encoded);
+        }
+
+        int length = encoded.length();
+        StringBuilder sb = new StringBuilder(length + 8);
+        int i = 0;
+        do {
+            String before = encoded.substring(i, m.start());
+            sb.append(query ? HttpUtils.queryEncode(before) : HttpUtils.pathEncode(before));
+            sb.append(m.group());
+            i = m.end();
+        } while (m.find());
+        String tail = encoded.substring(i, length);
+        sb.append(query ? HttpUtils.queryEncode(tail) : HttpUtils.pathEncode(tail));
+        return sb.toString();
+    }
+
+    public static SimpleDateFormat getHttpDateFormat() {
+        return Headers.getHttpDateFormat();
+    }
+
+    public static String toHttpDate(Date date) {
+        return Headers.toHttpDate(date);
+    }
+
+    public static RuntimeDelegate getOtherRuntimeDelegate() {
+        try {
+            RuntimeDelegate rd = RuntimeDelegate.getInstance();
+            return rd instanceof RuntimeDelegateImpl ? null : rd;
+        } catch (Throwable t) {
+            return null;
+        }
+    }
+
+    public static HeaderDelegate<Object> getHeaderDelegate(Object o) {
+        return getHeaderDelegate(RuntimeDelegate.getInstance(), o);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static HeaderDelegate<Object> getHeaderDelegate(RuntimeDelegate rd, Object o) {
+        return rd == null ? null : (HeaderDelegate<Object>)rd.createHeaderDelegate(o.getClass());
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> MultivaluedMap<String, T> getModifiableStringHeaders(Message m) {
+        MultivaluedMap<String, Object> headers = getModifiableHeaders(m);
+        convertHeaderValuesToString(headers, false);
+        return (MultivaluedMap<String, T>)headers;
+    }
+
+    public static MultivaluedMap<String, Object> getModifiableHeaders(Message m) {
+        Map<String, List<Object>> headers = CastUtils.cast((Map<?, ?>)m.get(Message.PROTOCOL_HEADERS));
+        return new MetadataMap<String, Object>(headers, false, false, true);
+    }
+
+    public static void convertHeaderValuesToString(Map<String, List<Object>> headers, boolean delegateOnly) {
+        if (headers == null) {
+            return;
+        }
+        RuntimeDelegate rd = getOtherRuntimeDelegate();
+        if (rd == null && delegateOnly) {
+            return;
+        }
+        for (Map.Entry<String, List<Object>> entry : headers.entrySet()) {
+            List<Object> values = entry.getValue();
+            for (int i = 0; i < values.size(); i++) {
+                Object value = values.get(i);
+
+                if (value != null && !(value instanceof String)) {
+
+                    HeaderDelegate<Object> hd = getHeaderDelegate(rd, value);
+
+                    if (hd != null) {
+                        value = hd.toString(value);
+                    } else if (!delegateOnly) {
+                        value = value.toString();
+                    }
+
+                    try {
+                        values.set(i, value);
+                    } catch (UnsupportedOperationException ex) {
+                        // this may happen if an unmodifiable List was set via Map put
+                        List<Object> newList = new ArrayList<>(values);
+                        newList.set(i, value);
+                        // Won't help if the map is unmodifiable in which case it is a bug anyway
+                        headers.put(entry.getKey(), newList);
+                    }
+
+                }
+
+            }
+        }
+
+    }
+
+    public static Date getHttpDate(String value) {
+        if (value == null) {
+            return null;
+        }
+        try {
+            return Headers.getHttpDateFormat().parse(value);
+        } catch (ParseException ex) {
+            return null;
+        }
+    }
+
+    public static Locale getLocale(String value) {
+        if (value == null) {
+            return null;
+        }
+        final String language;
+        String locale = null;
+        int index = value.indexOf('-');
+        if (index == 0 || index == value.length() - 1) {
+            throw new IllegalArgumentException("Illegal locale value : " + value);
+        }
+
+        if (index > 0) {
+            language = value.substring(0, index);
+            locale = value.substring(index + 1);
+        } else {
+            language = value;
+        }
+
+        if (locale == null) {
+            return new Locale(language);
+        }
+        return new Locale(language, locale);
+
+    }
+
+    public static int getContentLength(String value) {
+        if (value == null) {
+            return -1;
+        }
+        try {
+            int len = Integer.parseInt(value);
+            return len >= 0 ? len : -1;
+        } catch (Exception ex) {
+            return -1;
+        }
+    }
+
+    public static String getHeaderString(List<String> values) {
+        if (values == null) {
+            return null;
+        }
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < values.size(); i++) {
+            String value = values.get(i);
+            if (StringUtils.isEmpty(value)) {
+                continue;
+            }
+            sb.append(value);
+            if (i + 1 < values.size()) {
+                sb.append(',');
+            }
+        }
+        return sb.toString();
+    }
+
+    public static boolean isDateRelatedHeader(String headerName) {
+        return HttpHeaders.DATE.equalsIgnoreCase(headerName)
+               || HttpHeaders.IF_MODIFIED_SINCE.equalsIgnoreCase(headerName)
+               || HttpHeaders.IF_UNMODIFIED_SINCE.equalsIgnoreCase(headerName)
+               || HttpHeaders.EXPIRES.equalsIgnoreCase(headerName)
+               || HttpHeaders.LAST_MODIFIED.equalsIgnoreCase(headerName);
+    }
+
+    public static boolean isHttpRequest(Message message) {
+        return message.get(AbstractHTTPDestination.HTTP_REQUEST) != null;
+    }
+
+    public static URI toAbsoluteUri(String relativePath, Message message) {
+        String base = BaseUrlHelper.getBaseURL(
+            (HttpServletRequest)message.get(AbstractHTTPDestination.HTTP_REQUEST));
+        return URI.create(base + relativePath);
+    }
+
+    public static URI toAbsoluteUri(URI u, Message message) {
+        HttpServletRequest request =
+            (HttpServletRequest)message.get(AbstractHTTPDestination.HTTP_REQUEST);
+        boolean absolute = u.isAbsolute();
+        StringBuilder uriBuf = new StringBuilder();
+        if (request != null && (!absolute || isLocalHostOrAnyIpAddress(u, uriBuf, message))) {
+            String serverAndPort = request.getServerName();
+            boolean localAddressUsed = false;
+            if (absolute) {
+                if (ANY_IP_ADDRESS.equals(serverAndPort)) {
+                    serverAndPort = request.getLocalAddr();
+                    localAddressUsed = true;
+                }
+                if (LOCAL_HOST_IP_ADDRESS.equals(serverAndPort)) {
+                    serverAndPort = "localhost";
+                    localAddressUsed = true;
+                }
+            }
+
+
+            int port = localAddressUsed ? request.getLocalPort() : request.getServerPort();
+            if (port != DEFAULT_HTTP_PORT) {
+                serverAndPort += ":" + port;
+            }
+            String base = request.getScheme() + "://" + serverAndPort;
+            if (!absolute) {
+                u = URI.create(base + u.toString());
+            } else {
+                int originalPort = u.getPort();
+                String hostValue = uriBuf.toString().contains(ANY_IP_ADDRESS_SCHEME)
+                    ? ANY_IP_ADDRESS : LOCAL_HOST_IP_ADDRESS;
+                String replaceValue = originalPort == -1 ? hostValue : hostValue + ":" + originalPort;
+                u = URI.create(u.toString().replace(replaceValue, serverAndPort));
+            }
+        }
+        return u;
+    }
+
+    private static boolean isLocalHostOrAnyIpAddress(URI u, StringBuilder uriStringBuffer, Message m) {
+        String uriString = u.toString();
+        boolean result = uriString.contains(LOCAL_HOST_IP_ADDRESS_SCHEME) && replaceLoopBackAddress(m)
+            || uriString.contains(ANY_IP_ADDRESS_SCHEME);
+        uriStringBuffer.append(uriString);
+        return result;
+    }
+
+    private static boolean replaceLoopBackAddress(Message m) {
+        Object prop = m.getContextualProperty(REPLACE_LOOPBACK_PROPERTY);
+        return prop == null || PropertyUtils.isTrue(prop);
+    }
+
+    public static void resetRequestURI(Message m, String requestURI) {
+        m.remove(REQUEST_PATH_TO_MATCH_SLASH);
+        m.remove(REQUEST_PATH_TO_MATCH);
+        m.put(Message.REQUEST_URI, requestURI);
+    }
+
+
+    public static String getPathToMatch(Message m, boolean addSlash) {
+        String var = addSlash ? REQUEST_PATH_TO_MATCH_SLASH : REQUEST_PATH_TO_MATCH;
+        String pathToMatch = (String)m.get(var);
+        if (pathToMatch != null) {
+            return pathToMatch;
+        }
+        String requestAddress = getProtocolHeader(m, Message.REQUEST_URI, "/");
+        if (m.get(Message.QUERY_STRING) == null) {
+            int index = requestAddress.lastIndexOf('?');
+            if (index > 0 && index < requestAddress.length()) {
+                m.put(Message.QUERY_STRING, requestAddress.substring(index + 1));
+                requestAddress = requestAddress.substring(0, index);
+            }
+        }
+        String baseAddress = getBaseAddress(m);
+        pathToMatch = getPathToMatch(requestAddress, baseAddress, addSlash);
+        m.put(var, pathToMatch);
+        return pathToMatch;
+    }
+
+    public static String getProtocolHeader(Message m, String name, String defaultValue) {
+        return getProtocolHeader(m, name, defaultValue, false);
+    }
+
+    public static String getProtocolHeader(Message m, String name, String defaultValue, boolean setOnMessage) {
+        String value = (String)m.get(name);
+        if (value == null) {
+            value = new HttpHeadersImpl(m).getRequestHeaders().getFirst(name);
+            if (value != null && setOnMessage) {
+                m.put(name, value);
+            }
+        }
+        return value == null ? defaultValue : value;
+    }
+
+    public static String getBaseAddress(Message m) {
+        String endpointAddress = getEndpointAddress(m);
+        try {
+            URI uri = new URI(endpointAddress);
+            String path = uri.getRawPath();
+            String scheme = uri.getScheme();
+            if (scheme != null && !scheme.startsWith(HttpUtils.HTTP_SCHEME)
+                && HttpUtils.isHttpRequest(m)) {
+                path = HttpUtils.toAbsoluteUri(path, m).getRawPath();
+            }
+            return (path == null || path.length() == 0) ? "/" : path;
+        } catch (URISyntaxException ex) {
+            return endpointAddress;
+        }
+    }
+
+    public static String getEndpointAddress(Message m) {
+        String address;
+        Destination d = m.getExchange().getDestination();
+        if (d != null) {
+            if (d instanceof AbstractHTTPDestination) {
+                EndpointInfo ei = ((AbstractHTTPDestination)d).getEndpointInfo();
+                HttpServletRequest request = (HttpServletRequest)m.get(AbstractHTTPDestination.HTTP_REQUEST);
+                Object property = request != null
+                    ? request.getAttribute("org.apache.cxf.transport.endpoint.address") : null;
+                address = property != null ? property.toString() : ei.getAddress();
+            } else {
+                address = m.containsKey(Message.BASE_PATH)
+                    ? (String)m.get(Message.BASE_PATH) : d.getAddress().getAddress().getValue();
+            }
+        } else {
+            address = (String)m.get(Message.ENDPOINT_ADDRESS);
+        }
+        if (address.startsWith("http") && address.endsWith("//")) {
+            address = address.substring(0, address.length() - 1);
+        }
+        return address;
+    }
+
+    public static void updatePath(Message m, String path) {
+        String baseAddress = getBaseAddress(m);
+        boolean pathSlash = path.startsWith("/");
+        boolean baseSlash = baseAddress.endsWith("/");
+        if (pathSlash && baseSlash) {
+            path = path.substring(1);
+        } else if (!pathSlash && !baseSlash) {
+            path = "/" + path;
+        }
+        m.put(Message.REQUEST_URI, baseAddress + path);
+        m.remove(REQUEST_PATH_TO_MATCH);
+        m.remove(REQUEST_PATH_TO_MATCH_SLASH);
+    }
+
+
+    public static String getPathToMatch(String path, String address, boolean addSlash) {
+
+        int ind = path.indexOf(address);
+        if (ind == -1 && address.equals(path + "/")) {
+            path += "/";
+            ind = 0;
+        }
+        if (ind == 0) {
+            path = path.substring(address.length());
+        }
+        if (addSlash && !path.startsWith("/")) {
+            path = "/" + path;
+        }
+
+        return path;
+    }
+
+    public static String getOriginalAddress(Message m) {
+        Destination d = m.getDestination();
+        return d == null ? "/" : d.getAddress().getAddress().getValue();
+    }
+
+    public static String fromPathSegment(PathSegment ps) {
+        if (PathSegmentImpl.class.isAssignableFrom(ps.getClass())) {
+            return ((PathSegmentImpl)ps).getOriginalPath();
+        }
+        StringBuilder sb = new StringBuilder();
+        sb.append(ps.getPath());
+        for (Map.Entry<String, List<String>> entry : ps.getMatrixParameters().entrySet()) {
+            for (String value : entry.getValue()) {
+                sb.append(';').append(entry.getKey());
+                if (value != null) {
+                    sb.append('=').append(value);
+                }
+            }
+        }
+        return sb.toString();
+    }
+
+    public static Response.Status getParameterFailureStatus(ParameterType pType) {
+        if (pType == ParameterType.MATRIX || pType == ParameterType.PATH
+            || pType == ParameterType.QUERY) {
+            return Response.Status.NOT_FOUND;
+        }
+        return Response.Status.BAD_REQUEST;
+    }
+
+    public static String getSetEncoding(MediaType mt, MultivaluedMap<String, Object> headers,
+                                        String defaultEncoding) {
+        String enc = getMediaTypeCharsetParameter(mt);
+        if (enc == null) {
+            return defaultEncoding;
+        }
+        try {
+            "0".getBytes(enc);
+            return enc;
+        } catch (UnsupportedEncodingException ex) {
+            String message = new org.apache.cxf.common.i18n.Message("UNSUPPORTED_ENCODING",
+                                 BUNDLE, enc, defaultEncoding).toString();
+            LOG.warning(message);
+            headers.putSingle(HttpHeaders.CONTENT_TYPE,
+                JAXRSUtils.mediaTypeToString(mt, CHARSET_PARAMETER)
+                + ';' + CHARSET_PARAMETER + "="
+                + (defaultEncoding == null ? StandardCharsets.UTF_8 : defaultEncoding));
+        }
+        return defaultEncoding;
+    }
+
+    public static String getEncoding(MediaType mt, String defaultEncoding) {
+        String charset = mt == null ? defaultEncoding : getMediaTypeCharsetParameter(mt);
+        return charset == null ? defaultEncoding : charset;
+    }
+
+    public static String getMediaTypeCharsetParameter(MediaType mt) {
+        String charset = mt.getParameters().get(CHARSET_PARAMETER);
+        if (charset != null && charset.startsWith(DOUBLE_QUOTE) 
+            && charset.endsWith(DOUBLE_QUOTE) && charset.length() > 1) {
+            charset = charset.substring(1,  charset.length() - 1);
+        }
+        return charset;
+    }
+
+    public static URI resolve(UriBuilder baseBuilder, URI uri) {
+        if (!uri.isAbsolute()) {
+            return baseBuilder.build().resolve(uri);
+        }
+        return uri;
+    }
+
+    public static URI relativize(URI base, URI uri) {
+        // quick bail-out
+        if (!(base.isAbsolute()) || !(uri.isAbsolute())) {
+            return uri;
+        }
+        if (base.isOpaque() || uri.isOpaque()) {
+            // Unlikely case of an URN which can't deal with
+            // relative path, such as urn:isbn:0451450523
+            return uri;
+        }
+        // Check for common root
+        URI root = base.resolve("/");
+        if (!(root.equals(uri.resolve("/")))) {
+            // Different protocol/auth/host/port, return as is
+            return uri;
+        }
+
+        // Ignore hostname bits for the following , but add "/" in the beginning
+        // so that in worst case we'll still return "/fred" rather than
+        // "http://example.com/fred".
+        URI baseRel = URI.create("/").resolve(root.relativize(base));
+        URI uriRel = URI.create("/").resolve(root.relativize(uri));
+
+        // Is it same path?
+        if (baseRel.getPath().equals(uriRel.getPath())) {
+            return baseRel.relativize(uriRel);
+        }
+
+        // Direct siblings? (ie. in same folder)
+        URI commonBase = baseRel.resolve("./");
+        if (commonBase.equals(uriRel.resolve("./"))) {
+            return commonBase.relativize(uriRel);
+        }
+
+        // No, then just keep climbing up until we find a common base.
+        URI relative = URI.create("");
+        while (!(uriRel.getPath().startsWith(commonBase.getPath())) && !"/".equals(commonBase.getPath())) {
+            commonBase = commonBase.resolve("../");
+            relative = relative.resolve("../");
+        }
+
+        // Now we can use URI.relativize
+        URI relToCommon = commonBase.relativize(uriRel);
+        // and prepend the needed ../
+        return relative.resolve(relToCommon);
+
+    }
+
+    public static String toHttpLanguage(Locale locale) {
+        return Headers.toHttpLanguage(locale);
+    }
+
+    public static boolean isPayloadEmpty(MultivaluedMap<String, String> headers) {
+        if (headers != null) {
+            String value = headers.getFirst(HttpHeaders.CONTENT_LENGTH);
+            if (value != null) {
+                try {
+                    Long len = Long.valueOf(value);
+                    return len <= 0;
+                } catch (NumberFormatException ex) {
+                    // ignore
+                }
+            }
+        }
+
+        return false;
+    }
+    
+    public static <T> T createServletResourceValue(Message m, Class<T> clazz) {
+
+        Object value = null;
+        if (clazz == HttpServletRequest.class) {
+            HttpServletRequest request = (HttpServletRequest)m.get(AbstractHTTPDestination.HTTP_REQUEST);
+            value = request != null ? new HttpServletRequestFilter(request, m) : null;
+        } else if (clazz == HttpServletResponse.class) {
+            HttpServletResponse response = (HttpServletResponse)m.get(AbstractHTTPDestination.HTTP_RESPONSE);
+            value = response != null ? new HttpServletResponseFilter(response, m) : null;
+        } else if (clazz == ServletContext.class) {
+            value = m.get(AbstractHTTPDestination.HTTP_CONTEXT);
+        } else if (clazz == ServletConfig.class) {
+            value = m.get(AbstractHTTPDestination.HTTP_CONFIG);
+        }
+
+        return clazz.cast(value);
+    }
+
+    public static boolean isMethodWithNoRequestContent(String method) {
+        return KNOWN_HTTP_VERBS_WITH_NO_REQUEST_CONTENT.contains(method);
+    }
+    
+    public static boolean isMethodWithNoResponseContent(String method) {
+        return KNOWN_HTTP_VERBS_WITH_NO_RESPONSE_CONTENT.contains(method);
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java b/transform/src/patch/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java
new file mode 100644
index 0000000..fb53bd6
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java
@@ -0,0 +1,1044 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.jaxrs.utils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.Encoded;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.MatrixParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+import javax.xml.namespace.QName;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.i18n.BundleUtils;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.feature.Feature;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.helpers.DOMUtils;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.ext.DefaultMethod;
+import org.apache.cxf.jaxrs.ext.xml.ElementClass;
+import org.apache.cxf.jaxrs.ext.xml.XMLName;
+import org.apache.cxf.jaxrs.lifecycle.PerRequestResourceProvider;
+import org.apache.cxf.jaxrs.lifecycle.ResourceProvider;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.jaxrs.model.ClassResourceInfo;
+import org.apache.cxf.jaxrs.model.MethodDispatcher;
+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.model.ResourceTypes;
+import org.apache.cxf.jaxrs.model.URITemplate;
+import org.apache.cxf.jaxrs.model.UserOperation;
+import org.apache.cxf.jaxrs.model.UserResource;
+import org.apache.cxf.jaxrs.provider.JAXBElementProvider;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageImpl;
+import org.apache.cxf.resource.ResourceManager;
+import org.apache.cxf.staxutils.StaxUtils;
+
+public final class ResourceUtils {
+
+    private static final Logger LOG = LogUtils.getL7dLogger(ResourceUtils.class);
+    private static final ResourceBundle BUNDLE = BundleUtils.getBundle(ResourceUtils.class);
+    private static final String CLASSPATH_PREFIX = "classpath:";
+    private static final String NOT_RESOURCE_METHOD_MESSAGE_ID = "NOT_RESOURCE_METHOD";
+    private static final String NOT_SUSPENDED_ASYNC_MESSAGE_ID = "NOT_SUSPENDED_ASYNC_METHOD";
+    private static final String NO_VOID_RETURN_ASYNC_MESSAGE_ID = "NO_VOID_RETURN_ASYNC_METHOD";
+    private static final Set<String> SERVER_PROVIDER_CLASS_NAMES;
+    static {
+        SERVER_PROVIDER_CLASS_NAMES = new HashSet<>();
+        SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.ext.MessageBodyWriter");
+        SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.ext.MessageBodyReader");
+        SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.ext.ExceptionMapper");
+        SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.ext.ContextResolver");
+        SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.ext.ReaderInterceptor");
+        SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.ext.WriterInterceptor");
+        SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.ext.ParamConverterProvider");
+        SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.container.ContainerRequestFilter");
+        SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.container.ContainerResponseFilter");
+        SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.container.DynamicFeature");
+        SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.core.Feature");
+        SERVER_PROVIDER_CLASS_NAMES.add("org.apache.cxf.jaxrs.ext.ContextProvider");
+
+    }
+
+    private ResourceUtils() {
+    }
+
+    private static Method[] getDeclaredMethods(final Class<?> c) {
+        return AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
+            @Override
+            public Method[] run() {
+                return c.getDeclaredMethods();
+            }
+        });
+    }
+    public static Method findPostConstructMethod(Class<?> c) {
+        return findPostConstructMethod(c, null);
+    }
+    public static Method findPostConstructMethod(Class<?> c, String name) {
+        if (Object.class == c || null == c) {
+            return null;
+        }
+        for (Method m : getDeclaredMethods(c)) {
+            if (name != null) {
+                if (m.getName().equals(name)) {
+                    return m;
+                }
+            } else if (m.getAnnotation(PostConstruct.class) != null) {
+                return m;
+            }
+        }
+        Method m = findPostConstructMethod(c.getSuperclass(), name);
+        if (m != null) {
+            return m;
+        }
+        for (Class<?> i : c.getInterfaces()) {
+            m = findPostConstructMethod(i, name);
+            if (m != null) {
+                return m;
+            }
+        }
+        return null;
+    }
+
+    public static Method findPreDestroyMethod(Class<?> c) {
+        return findPreDestroyMethod(c, null);
+    }
+
+    public static Method findPreDestroyMethod(Class<?> c, String name) {
+        if (Object.class == c || null == c) {
+            return null;
+        }
+        for (Method m : getDeclaredMethods(c)) {
+            if (name != null) {
+                if (m.getName().equals(name)) {
+                    return m;
+                }
+            } else if (m.getAnnotation(PreDestroy.class) != null) {
+                return m;
+            }
+        }
+        Method m = findPreDestroyMethod(c.getSuperclass(), name);
+        if (m != null) {
+            return m;
+        }
+        for (Class<?> i : c.getInterfaces()) {
+            m = findPreDestroyMethod(i, name);
+            if (m != null) {
+                return m;
+            }
+        }
+        return null;
+    }
+
+    public static ClassResourceInfo createClassResourceInfo(
+        Map<String, UserResource> resources, UserResource model,
+        Class<?> defaultClass,
+        boolean isRoot, boolean enableStatic,
+        Bus bus) {
+        final boolean isDefaultClass = defaultClass != null;
+        Class<?> sClass = !isDefaultClass  ? loadClass(model.getName()) : defaultClass;
+        return createServiceClassResourceInfo(resources, model, sClass, isRoot, enableStatic, bus);
+    }
+
+    public static ClassResourceInfo createServiceClassResourceInfo(
+        Map<String, UserResource> resources, UserResource model,
+        Class<?> sClass, boolean isRoot, boolean enableStatic, Bus bus) {
+        if (model == null) {
+            throw new RuntimeException("Resource class " + sClass.getName() + " has no model info");
+        }
+        ClassResourceInfo cri =
+            new ClassResourceInfo(sClass, sClass, isRoot, enableStatic, true,
+                                  model.getConsumes(), model.getProduces(), bus);
+        URITemplate t = URITemplate.createTemplate(model.getPath());
+        cri.setURITemplate(t);
+
+        MethodDispatcher md = new MethodDispatcher();
+        Map<String, UserOperation> ops = model.getOperationsAsMap();
+
+        Method defaultMethod = null;
+        Map<String, Method> methodNames = new HashMap<>();
+        for (Method m : cri.getServiceClass().getMethods()) {
+            if (m.getAnnotation(DefaultMethod.class) != null) {
+                // if needed we can also support multiple default methods
+                defaultMethod = m;
+            }
+            methodNames.put(m.getName(), m);
+        }
+
+        for (Map.Entry<String, UserOperation> entry : ops.entrySet()) {
+            UserOperation op = entry.getValue();
+            Method actualMethod = methodNames.get(op.getName());
+            if (actualMethod == null) {
+                actualMethod = defaultMethod;
+            }
+            if (actualMethod == null) {
+                continue;
+            }
+            OperationResourceInfo ori =
+                new OperationResourceInfo(actualMethod, cri, URITemplate.createTemplate(op.getPath()),
+                                          op.getVerb(), op.getConsumes(), op.getProduces(),
+                                          op.getParameters(),
+                                          op.isOneway());
+            String rClassName = actualMethod.getReturnType().getName();
+            if (op.getVerb() == null) {
+                if (resources.containsKey(rClassName)) {
+                    ClassResourceInfo subCri = rClassName.equals(model.getName()) ? cri
+                        : createServiceClassResourceInfo(resources, resources.get(rClassName),
+                                                         actualMethod.getReturnType(), false, enableStatic, bus);
+                    if (subCri != null) {
+                        cri.addSubClassResourceInfo(subCri);
+                        md.bind(ori, actualMethod);
+                    }
+                }
+            } else {
+                md.bind(ori, actualMethod);
+            }
+        }
+
+        cri.setMethodDispatcher(md);
+        return checkMethodDispatcher(cri) ? cri : null;
+
+    }
+
+    public static ClassResourceInfo createClassResourceInfo(final Class<?> rClass,
+                                                            final Class<?> sClass,
+                                                            boolean root,
+                                                            boolean enableStatic) {
+        return createClassResourceInfo(rClass, sClass, root, enableStatic, BusFactory.getThreadDefaultBus());
+
+    }
+
+    public static ClassResourceInfo createClassResourceInfo(final Class<?> rClass,
+                                                            final Class<?> sClass,
+                                                            boolean root,
+                                                            boolean enableStatic,
+                                                            Bus bus) {
+        return createClassResourceInfo(rClass, sClass, null, root, enableStatic, bus);
+    }
+
+    public static ClassResourceInfo createClassResourceInfo(final Class<?> rClass,
+                                                            final Class<?> sClass,
+                                                            ClassResourceInfo parent,
+                                                            boolean root,
+                                                            boolean enableStatic,
+                                                            Bus bus) {
+        return createClassResourceInfo(rClass, sClass, parent, root, enableStatic, bus, null, null);
+    }
+
+    //CHECKSTYLE:OFF
+    public static ClassResourceInfo createClassResourceInfo(final Class<?> rClass,
+                                                            final Class<?> sClass,
+                                                            ClassResourceInfo parent,
+                                                            boolean root,
+                                                            boolean enableStatic,
+                                                            Bus bus,
+                                                            List<MediaType> defaultConsumes,
+                                                            List<MediaType> defaultProduces) {
+    //CHECKSTYLE:ON
+        ClassResourceInfo cri = new ClassResourceInfo(rClass, sClass, root, enableStatic, bus,
+                                                      defaultConsumes, defaultProduces);
+        cri.setParent(parent);
+
+        if (root) {
+            URITemplate t = URITemplate.createTemplate(cri.getPath());
+            cri.setURITemplate(t);
+        }
+
+        evaluateResourceClass(cri, enableStatic);
+        return checkMethodDispatcher(cri) ? cri : null;
+    }
+
+    private static void evaluateResourceClass(ClassResourceInfo cri, boolean enableStatic) {
+        MethodDispatcher md = new MethodDispatcher();
+        Class<?> serviceClass = cri.getServiceClass();
+
+        final Set<Method> annotatedMethods = new HashSet<>();
+
+        for (Method m : serviceClass.getMethods()) {
+            if (!m.isBridge() && !m.isSynthetic()) {
+                //do real methods first
+                Method annotatedMethod = AnnotationUtils.getAnnotatedMethod(serviceClass, m);
+                if (!annotatedMethods.contains(annotatedMethod)) {
+                    evaluateResourceMethod(cri, enableStatic, md, m, annotatedMethod);
+                    annotatedMethods.add(annotatedMethod);
+                }
+            }
+        }
+        for (Method m : serviceClass.getMethods()) {
+            if (m.isBridge() || m.isSynthetic()) {
+                //if a bridge/synthetic method isn't already mapped to something, go ahead and do it
+                Method annotatedMethod = AnnotationUtils.getAnnotatedMethod(serviceClass, m);
+                if (!annotatedMethods.contains(annotatedMethod)) {
+                    evaluateResourceMethod(cri, enableStatic, md, m, annotatedMethod);
+                    annotatedMethods.add(annotatedMethod);
+                }
+            }
+        }
+        cri.setMethodDispatcher(md);
+    }
+
+    private static void evaluateResourceMethod(ClassResourceInfo cri, boolean enableStatic, MethodDispatcher md,
+                                               Method m, Method annotatedMethod) {
+        String httpMethod = AnnotationUtils.getHttpMethodValue(annotatedMethod);
+        Path path = AnnotationUtils.getMethodAnnotation(annotatedMethod, Path.class);
+
+        if (httpMethod != null || path != null) {
+            if (!checkAsyncResponse(annotatedMethod)) {
+                return;
+            }
+
+            md.bind(createOperationInfo(m, annotatedMethod, cri, path, httpMethod), m);
+            if (httpMethod == null) {
+                // subresource locator
+                Class<?> subClass = m.getReturnType();
+                if (subClass == Class.class) {
+                    subClass = InjectionUtils.getActualType(m.getGenericReturnType());
+                }
+                if (enableStatic) {
+                    ClassResourceInfo subCri = cri.findResource(subClass, subClass);
+                    if (subCri == null) {
+                        ClassResourceInfo ancestor = getAncestorWithSameServiceClass(cri, subClass);
+                        subCri = ancestor != null ? ancestor
+                                : createClassResourceInfo(subClass, subClass, cri, false, enableStatic,
+                                cri.getBus());
+                    }
+
+                    if (subCri != null) {
+                        cri.addSubClassResourceInfo(subCri);
+                    }
+                }
+            }
+        } else {
+            reportInvalidResourceMethod(m, NOT_RESOURCE_METHOD_MESSAGE_ID, Level.FINE);
+        }
+    }
+
+    private static void reportInvalidResourceMethod(Method m, String messageId, Level logLevel) {
+        if (LOG.isLoggable(logLevel)) {
+            LOG.log(logLevel, new org.apache.cxf.common.i18n.Message(messageId,
+                                                             BUNDLE,
+                                                             m.getDeclaringClass().getName(),
+                                                             m.getName()).toString());
+        }
+    }
+
+    private static boolean checkAsyncResponse(Method m) {
+        Class<?>[] types = m.getParameterTypes();
+        for (int i = 0; i < types.length; i++) {
+            if (types[i] == AsyncResponse.class) {
+                if (AnnotationUtils.getAnnotation(m.getParameterAnnotations()[i], Suspended.class) == null) {
+                    reportInvalidResourceMethod(m, NOT_SUSPENDED_ASYNC_MESSAGE_ID, Level.FINE);
+                    return false;
+                }
+                if (m.getReturnType() == Void.TYPE || m.getReturnType() == Void.class) {
+                    return true;
+                }
+                reportInvalidResourceMethod(m, NO_VOID_RETURN_ASYNC_MESSAGE_ID, Level.WARNING);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static ClassResourceInfo getAncestorWithSameServiceClass(ClassResourceInfo parent, Class<?> subClass) {
+        if (parent == null) {
+            return null;
+        }
+        if (parent.getServiceClass() == subClass) {
+            return parent;
+        }
+        return getAncestorWithSameServiceClass(parent.getParent(), subClass);
+    }
+
+    public static Constructor<?> findResourceConstructor(Class<?> resourceClass, boolean perRequest) {
+        List<Constructor<?>> cs = new LinkedList<>();
+        for (Constructor<?> c : resourceClass.getConstructors()) {
+            Class<?>[] params = c.getParameterTypes();
+            Annotation[][] anns = c.getParameterAnnotations();
+            boolean match = true;
+            for (int i = 0; i < params.length; i++) {
+                if (!perRequest) {
+                    if (AnnotationUtils.getAnnotation(anns[i], Context.class) == null) {
+                        match = false;
+                        break;
+                    }
+                } else if (!AnnotationUtils.isValidParamAnnotations(anns[i])) {
+                    match = false;
+                    break;
+                }
+            }
+            if (match) {
+                cs.add(c);
+            }
+        }
+        Collections.sort(cs, new Comparator<Constructor<?>>() {
+
+            public int compare(Constructor<?> c1, Constructor<?> c2) {
+                int p1 = c1.getParameterTypes().length;
+                int p2 = c2.getParameterTypes().length;
+                return p1 > p2 ? -1 : p1 < p2 ? 1 : 0;
+            }
+
+        });
+        return cs.isEmpty() ? null : cs.get(0);
+    }
+
+    public static List<Parameter> getParameters(Method resourceMethod) {
+        Annotation[][] paramAnns = resourceMethod.getParameterAnnotations();
+        if (paramAnns.length == 0) {
+            return CastUtils.cast(Collections.emptyList(), Parameter.class);
+        }
+        Class<?>[] types = resourceMethod.getParameterTypes();
+        List<Parameter> params = new ArrayList<>(paramAnns.length);
+        for (int i = 0; i < paramAnns.length; i++) {
+            Parameter p = getParameter(i, paramAnns[i], types[i]);
+            params.add(p);
+        }
+        return params;
+    }
+
+    //CHECKSTYLE:OFF
+    public static Parameter getParameter(int index, Annotation[] anns, Class<?> type) {
+
+        Context ctx = AnnotationUtils.getAnnotation(anns, Context.class);
+        if (ctx != null) {
+            return new Parameter(ParameterType.CONTEXT, index, null);
+        }
+
+        boolean isEncoded = AnnotationUtils.getAnnotation(anns, Encoded.class) != null;
+
+        BeanParam bp = AnnotationUtils.getAnnotation(anns, BeanParam.class);
+        if (bp != null) {
+            return new Parameter(ParameterType.BEAN, index, null, isEncoded, null);
+        }
+
+        String dValue = AnnotationUtils.getDefaultParameterValue(anns);
+
+        PathParam a = AnnotationUtils.getAnnotation(anns, PathParam.class);
+        if (a != null) {
+            return new Parameter(ParameterType.PATH, index, a.value(), isEncoded, dValue);
+        }
+        QueryParam q = AnnotationUtils.getAnnotation(anns, QueryParam.class);
+        if (q != null) {
+            return new Parameter(ParameterType.QUERY, index, q.value(), isEncoded, dValue);
+        }
+        MatrixParam m = AnnotationUtils.getAnnotation(anns, MatrixParam.class);
+        if (m != null) {
+            return new Parameter(ParameterType.MATRIX, index, m.value(), isEncoded, dValue);
+        }
+
+        FormParam f = AnnotationUtils.getAnnotation(anns, FormParam.class);
+        if (f != null) {
+            return new Parameter(ParameterType.FORM, index, f.value(), isEncoded, dValue);
+        }
+
+        HeaderParam h = AnnotationUtils.getAnnotation(anns, HeaderParam.class);
+        if (h != null) {
+            return new Parameter(ParameterType.HEADER, index, h.value(), isEncoded, dValue);
+        }
+
+        CookieParam c = AnnotationUtils.getAnnotation(anns, CookieParam.class);
+        if (c != null) {
+            return new Parameter(ParameterType.COOKIE, index, c.value(), isEncoded, dValue);
+        }
+
+        return new Parameter(ParameterType.REQUEST_BODY, index, null);
+
+    }
+    //CHECKSTYLE:ON
+
+    private static OperationResourceInfo createOperationInfo(Method m, Method annotatedMethod,
+                                                      ClassResourceInfo cri, Path path, String httpMethod) {
+        OperationResourceInfo ori = new OperationResourceInfo(m, annotatedMethod, cri);
+        URITemplate t = URITemplate.createTemplate(path);
+        ori.setURITemplate(t);
+        ori.setHttpMethod(httpMethod);
+        return ori;
+    }
+
+
+    private static boolean checkMethodDispatcher(ClassResourceInfo cr) {
+        if (cr.getMethodDispatcher().getOperationResourceInfos().isEmpty()) {
+            LOG.warning(new org.apache.cxf.common.i18n.Message("NO_RESOURCE_OP_EXC",
+                                                               BUNDLE,
+                                                               cr.getServiceClass().getName()).toString());
+            return false;
+        }
+        return true;
+    }
+
+
+    private static Class<?> loadClass(String cName) {
+        try {
+            return ClassLoaderUtils.loadClass(cName.trim(), ResourceUtils.class);
+        } catch (ClassNotFoundException ex) {
+            throw new RuntimeException("No class " + cName.trim() + " can be found", ex);
+        }
+    }
+
+
+    public static List<UserResource> getUserResources(String loc, Bus bus) {
+        try (InputStream is = ResourceUtils.getResourceStream(loc, bus)) {
+            if (is == null) {
+                return null;
+            }
+            return getUserResources(is);
+        } catch (Exception ex) {
+            LOG.warning("Problem with processing a user model at " + loc);
+        }
+
+        return null;
+    }
+
+    public static InputStream getResourceStream(String loc, Bus bus) throws IOException {
+        URL url = getResourceURL(loc, bus);
+        return url == null ? null : url.openStream();
+    }
+
+    public static URL getResourceURL(String loc, Bus bus) throws IOException {
+        URL url;
+        if (loc.startsWith(CLASSPATH_PREFIX)) {
+            String path = loc.substring(CLASSPATH_PREFIX.length());
+            url = ResourceUtils.getClasspathResourceURL(path, ResourceUtils.class, bus);
+        } else {
+            try {
+                url = new URL(loc);
+            } catch (Exception ex) {
+                // it can be either a classpath or file resource without a scheme
+                url = ResourceUtils.getClasspathResourceURL(loc, ResourceUtils.class, bus);
+                if (url == null) {
+                    File file = new File(loc);
+                    if (file.exists()) {
+                        url = file.toURI().toURL();
+                    }
+                }
+            }
+        }
+        if (url == null) {
+            LOG.warning("No resource " + loc + " is available");
+        }
+        return url;
+    }
+
+    public static InputStream getClasspathResourceStream(String path, Class<?> callingClass, Bus bus) {
+        InputStream is = ClassLoaderUtils.getResourceAsStream(path, callingClass);
+        return is == null ? getResource(path, InputStream.class, bus) : is;
+    }
+
+    public static URL getClasspathResourceURL(String path, Class<?> callingClass, Bus bus) {
+        URL url = ClassLoaderUtils.getResource(path, callingClass);
+        return url == null ? getResource(path, URL.class, bus) : url;
+    }
+
+    public static <T> T getResource(String path, Class<T> resourceClass, Bus bus) {
+        if (bus != null) {
+            ResourceManager rm = bus.getExtension(ResourceManager.class);
+            if (rm != null) {
+                return rm.resolveResource(path, resourceClass);
+            }
+        }
+        return null;
+    }
+
+    public static Properties loadProperties(String propertiesLocation, Bus bus) throws IOException {
+        Properties props = new Properties();
+        try (InputStream is = getResourceStream(propertiesLocation, bus)) {
+            props.load(is);
+        }
+        return props;
+    }
+
+    public static List<UserResource> getUserResources(String loc) {
+        return getUserResources(loc, BusFactory.getThreadDefaultBus());
+    }
+
+    public static List<UserResource> getUserResources(InputStream is) throws Exception {
+        Document doc = StaxUtils.read(new InputStreamReader(is, StandardCharsets.UTF_8));
+        return getResourcesFromElement(doc.getDocumentElement());
+    }
+
+    public static List<UserResource> getResourcesFromElement(Element modelEl) {
+        List<UserResource> resources = new ArrayList<>();
+        List<Element> resourceEls =
+            DOMUtils.findAllElementsByTagNameNS(modelEl,
+                                                "http://cxf.apache.org/jaxrs", "resource");
+        for (Element e : resourceEls) {
+            resources.add(getResourceFromElement(e));
+        }
+        return resources;
+    }
+
+
+    public static ResourceTypes getAllRequestResponseTypes(List<ClassResourceInfo> cris,
+                                                           boolean jaxbOnly) {
+        return getAllRequestResponseTypes(cris, jaxbOnly, null);
+    }
+
+    public static ResourceTypes getAllRequestResponseTypes(List<ClassResourceInfo> cris,
+                                                           boolean jaxbOnly,
+                                                           MessageBodyWriter<?> jaxbWriter) {
+        ResourceTypes types = new ResourceTypes();
+        for (ClassResourceInfo resource : cris) {
+            getAllTypesForResource(resource, types, jaxbOnly, jaxbWriter);
+        }
+        return types;
+    }
+
+    public static Class<?> getActualJaxbType(Class<?> type, Method resourceMethod, boolean inbound) {
+        ElementClass element = resourceMethod.getAnnotation(ElementClass.class);
+        if  (element != null) {
+            Class<?> cls = inbound ? element.request() : element.response();
+            if (cls != Object.class) {
+                return cls;
+            }
+        }
+        return type;
+    }
+
+    private static void getAllTypesForResource(ClassResourceInfo resource,
+                                               ResourceTypes types,
+                                               boolean jaxbOnly,
+                                               MessageBodyWriter<?> jaxbWriter) {
+        Class<?> jaxbElement = null;
+        try {
+            jaxbElement = ClassLoaderUtils.loadClass("javax.xml.bind.JAXBElement", ResourceUtils.class);
+        } catch (final ClassNotFoundException e) {
+            // no-op
+        }
+
+        for (OperationResourceInfo ori : resource.getMethodDispatcher().getOperationResourceInfos()) {
+            Method method = ori.getAnnotatedMethod() == null ? ori.getMethodToInvoke() : ori.getAnnotatedMethod();
+            Class<?> realReturnType = method.getReturnType();
+            Class<?> cls = realReturnType;
+            if (cls == Response.class || ori.isAsync()) {
+                cls = getActualJaxbType(cls, method, false);
+            }
+            Type type = method.getGenericReturnType();
+            if (jaxbOnly) {
+                checkJaxbType(resource.getServiceClass(), cls, realReturnType == Response.class || ori.isAsync()
+                    ? cls : type, types, method.getAnnotations(), jaxbWriter, jaxbElement);
+            } else {
+                types.getAllTypes().put(cls, type);
+            }
+
+            for (Parameter pm : ori.getParameters()) {
+                if (pm.getType() == ParameterType.REQUEST_BODY) {
+                    Class<?> inType = method.getParameterTypes()[pm.getIndex()];
+                    if (inType != AsyncResponse.class) {
+                        Type paramType = method.getGenericParameterTypes()[pm.getIndex()];
+                        if (jaxbOnly) {
+                            checkJaxbType(resource.getServiceClass(), inType, paramType, types,
+                                          method.getParameterAnnotations()[pm.getIndex()], jaxbWriter, jaxbElement);
+                        } else {
+                            types.getAllTypes().put(inType, paramType);
+                        }
+                    }
+                }
+            }
+
+        }
+
+        for (ClassResourceInfo sub : resource.getSubResources()) {
+            if (!isRecursiveSubResource(resource, sub)) {
+                getAllTypesForResource(sub, types, jaxbOnly, jaxbWriter);
+            }
+        }
+    }
+
+    private static boolean isRecursiveSubResource(ClassResourceInfo parent, ClassResourceInfo sub) {
+        if (parent == null) {
+            return false;
+        }
+        if (parent == sub) {
+            return true;
+        }
+        return isRecursiveSubResource(parent.getParent(), sub);
+    }
+
+    private static void checkJaxbType(Class<?> serviceClass,
+                                      Class<?> type,
+                                      Type genericType,
+                                      ResourceTypes types,
+                                      Annotation[] anns,
+                                      MessageBodyWriter<?> jaxbWriter,
+                                      Class<?> jaxbElement) {
+        boolean isCollection = false;
+        if (InjectionUtils.isSupportedCollectionOrArray(type)) {
+            type = InjectionUtils.getActualType(genericType);
+            isCollection = true;
+        }
+        if (type == Object.class && !(genericType instanceof Class)
+            || genericType instanceof TypeVariable) {
+            Type theType = InjectionUtils.processGenericTypeIfNeeded(serviceClass,
+                                                      Object.class,
+                                                      genericType);
+            type = InjectionUtils.getActualType(theType);
+        }
+        if (type == null
+            || InjectionUtils.isPrimitive(type)
+            || (jaxbElement != null && jaxbElement.isAssignableFrom(type))
+            || Response.class.isAssignableFrom(type)
+            || type.isInterface()) {
+            return;
+        }
+
+        MessageBodyWriter<?> writer = jaxbWriter;
+        if (writer == null) {
+            JAXBElementProvider<Object> defaultWriter = new JAXBElementProvider<>();
+            defaultWriter.setMarshallAsJaxbElement(true);
+            defaultWriter.setXmlTypeAsJaxbElementOnly(true);
+            writer = defaultWriter;
+        }
+        if (writer.isWriteable(type, type, anns, MediaType.APPLICATION_XML_TYPE)) {
+            types.getAllTypes().put(type, type);
+            Class<?> genCls = InjectionUtils.getActualType(genericType);
+            if (genCls != type && genCls != null && genCls != Object.class
+                && !InjectionUtils.isSupportedCollectionOrArray(genCls)) {
+                types.getAllTypes().put(genCls, genCls);
+            }
+
+            XMLName name = AnnotationUtils.getAnnotation(anns, XMLName.class);
+            QName qname = name != null ? JAXRSUtils.convertStringToQName(name.value()) : null;
+            if (isCollection) {
+                types.getCollectionMap().put(type, qname);
+            } else {
+                types.getXmlNameMap().put(type, qname);
+            }
+        }
+    }
+
+    private static UserResource getResourceFromElement(Element e) {
+        UserResource resource = new UserResource();
+        resource.setName(e.getAttribute("name"));
+        resource.setPath(e.getAttribute("path"));
+        resource.setConsumes(e.getAttribute("consumes"));
+        resource.setProduces(e.getAttribute("produces"));
+        List<Element> operEls =
+            DOMUtils.findAllElementsByTagNameNS(e,
+                 "http://cxf.apache.org/jaxrs", "operation");
+        List<UserOperation> opers = new ArrayList<>(operEls.size());
+        for (Element operEl : operEls) {
+            opers.add(getOperationFromElement(operEl));
+        }
+        resource.setOperations(opers);
+        return resource;
+    }
+
+    private static UserOperation getOperationFromElement(Element e) {
+        UserOperation op = new UserOperation();
+        op.setName(e.getAttribute("name"));
+        op.setVerb(e.getAttribute("verb"));
+        op.setPath(e.getAttribute("path"));
+        op.setOneway(Boolean.parseBoolean(e.getAttribute("oneway")));
+        op.setConsumes(e.getAttribute("consumes"));
+        op.setProduces(e.getAttribute("produces"));
+        List<Element> paramEls =
+            DOMUtils.findAllElementsByTagNameNS(e,
+                 "http://cxf.apache.org/jaxrs", "param");
+        List<Parameter> params = new ArrayList<>(paramEls.size());
+        for (int i = 0; i < paramEls.size(); i++) {
+            Element paramEl = paramEls.get(i);
+            Parameter p = new Parameter(paramEl.getAttribute("type"), i, paramEl.getAttribute("name"));
+            p.setEncoded(Boolean.valueOf(paramEl.getAttribute("encoded")));
+            p.setDefaultValue(paramEl.getAttribute("defaultValue"));
+            String pClass = paramEl.getAttribute("class");
+            if (!StringUtils.isEmpty(pClass)) {
+                try {
+                    p.setJavaType(ClassLoaderUtils.loadClass(pClass, ResourceUtils.class));
+                } catch (Exception ex) {
+                    throw new RuntimeException(ex);
+                }
+            }
+            params.add(p);
+        }
+        op.setParameters(params);
+        return op;
+    }
+
+    public static Object[] createConstructorArguments(Constructor<?> c,
+                                                      Message m,
+                                                      boolean perRequest) {
+        return createConstructorArguments(c, m, perRequest, null);
+    }
+
+    public static Object[] createConstructorArguments(Constructor<?> c,
+                                                      Message m,
+                                                      boolean perRequest,
+                                                      Map<Class<?>, Object> contextValues) {
+        Class<?>[] params = c.getParameterTypes();
+        Annotation[][] anns = c.getParameterAnnotations();
+        Type[] genericTypes = c.getGenericParameterTypes();
+        return createConstructorArguments(c, m, perRequest, contextValues, params, anns, genericTypes);
+    }
+
+    public static Object[] createConstructorArguments(Constructor<?> c,
+                                                      Message m,
+                                                      boolean perRequest,
+                                                      Map<Class<?>,
+                                                      Object> contextValues,
+                                                      Class<?>[] params,
+                                                      Annotation[][] anns,
+                                                      Type[] genericTypes) {
+        if (m == null) {
+            m = new MessageImpl();
+        }
+        @SuppressWarnings("unchecked")
+        MultivaluedMap<String, String> templateValues =
+            (MultivaluedMap<String, String>)m.get(URITemplate.TEMPLATE_PARAMETERS);
+        Object[] values = new Object[params.length];
+        for (int i = 0; i < params.length; i++) {
+            if (AnnotationUtils.getAnnotation(anns[i], Context.class) != null) {
+                Object contextValue = contextValues != null ? contextValues.get(params[i]) : null;
+                if (contextValue == null) {
+                    if (perRequest || InjectionUtils.VALUE_CONTEXTS.contains(params[i].getName())) {
+                        values[i] = JAXRSUtils.createContextValue(m, genericTypes[i], params[i]);
+                    } else {
+                        values[i] = InjectionUtils.createThreadLocalProxy(params[i]);
+                    }
+                } else {
+                    values[i] = contextValue;
+                }
+            } else {
+                // this branch won't execute for singletons given that the found constructor
+                // is guaranteed to have only Context parameters, if any, for singletons
+                Parameter p = ResourceUtils.getParameter(i, anns[i], params[i]);
+                values[i] = JAXRSUtils.createHttpParameterValue(
+                                p, params[i], genericTypes[i], anns[i], m, templateValues, null);
+            }
+        }
+        return values;
+    }
+
+    @SuppressWarnings("unchecked")
+    public static JAXRSServerFactoryBean createApplication(Application app,
+                                                           boolean ignoreAppPath,
+                                                           boolean staticSubresourceResolution,
+                                                           boolean useSingletonResourceProvider,
+                                                           Bus bus) {
+
+        Set<Object> singletons = app.getSingletons();
+        verifySingletons(singletons);
+
+        List<Class<?>> resourceClasses = new ArrayList<>();
+        List<Object> providers = new ArrayList<>();
+        List<Feature> features = new ArrayList<>();
+        Map<Class<?>, ResourceProvider> map = new HashMap<>();
+
+        // Note, app.getClasses() returns a list of per-request classes
+        // or singleton provider classes
+        for (Class<?> cls : app.getClasses()) {
+            if (isValidApplicationClass(cls, singletons)) {
+                if (isValidProvider(cls)) {
+                    providers.add(createProviderInstance(cls));
+                } else if (Feature.class.isAssignableFrom(cls)) {
+                    features.add(createFeatureInstance((Class<? extends Feature>) cls));
+                } else {
+                    resourceClasses.add(cls);
+                    if (useSingletonResourceProvider) {
+                        map.put(cls, new SingletonResourceProvider(createProviderInstance(cls)));
+                    } else {
+                        map.put(cls, new PerRequestResourceProvider(cls));
+                    }
+                }
+            }
+        }
+
+        // we can get either a provider or resource class here
+        for (Object o : singletons) {
+            if (isValidProvider(o.getClass())) {
+                providers.add(o);
+            } else if (o instanceof Feature) {
+                features.add((Feature) o);
+            } else {
+                resourceClasses.add(o.getClass());
+                map.put(o.getClass(), new SingletonResourceProvider(o));
+            }
+        }
+
+        JAXRSServerFactoryBean bean = new JAXRSServerFactoryBean();
+        if (bus != null) {
+            bean.setBus(bus);
+        }
+
+        String address = "/";
+        if (!ignoreAppPath) {
+            ApplicationPath appPath = locateApplicationPath(app.getClass());
+            if (appPath != null) {
+                address = appPath.value();
+            }
+        }
+        if (!address.startsWith("/")) {
+            address = "/" + address;
+        }
+        bean.setAddress(address);
+        bean.setStaticSubresourceResolution(staticSubresourceResolution);
+        bean.setResourceClasses(resourceClasses);
+        bean.setProviders(providers);
+        bean.getFeatures().addAll(features);
+        for (Map.Entry<Class<?>, ResourceProvider> entry : map.entrySet()) {
+            bean.setResourceProvider(entry.getKey(), entry.getValue());
+        }
+        Map<String, Object> appProps = app.getProperties();
+        if (appProps != null) {
+            bean.getProperties(true).putAll(appProps);
+        }
+        bean.setApplication(app);
+        return bean;
+    }
+
+    public static Object createProviderInstance(Class<?> cls) {
+        try {
+            Constructor<?> c = ResourceUtils.findResourceConstructor(cls, false);
+            if (c != null && c.getParameterTypes().length == 0) {
+                return c.newInstance();
+            }
+            return c;
+        } catch (Throwable ex) {
+            throw new RuntimeException("Provider " + cls.getName() + " can not be created", ex);
+        }
+    }
+
+    public static Feature createFeatureInstance(Class<? extends Feature> cls) {
+        try {
+            Constructor<?> c = ResourceUtils.findResourceConstructor(cls, false);
+
+            if (c == null) {
+                throw new RuntimeException("No valid constructor found for " + cls.getName());
+            }
+
+            return (Feature) c.newInstance();
+        } catch (Throwable ex) {
+            throw new RuntimeException("Feature " + cls.getName() + " can not be created", ex);
+        }
+    }
+
+    private static boolean isValidProvider(Class<?> c) {
+        if (c == null || c == Object.class) {
+            return false;
+        }
+        if (c.getAnnotation(Provider.class) != null) {
+            return true;
+        }
+        for (Class<?> itf : c.getInterfaces()) {
+            if (SERVER_PROVIDER_CLASS_NAMES.contains(itf.getName())) {
+                return true;
+            }
+        }
+        return isValidProvider(c.getSuperclass());
+    }
+
+    private static void verifySingletons(Set<Object> singletons) {
+        if (singletons.isEmpty()) {
+            return;
+        }
+        Set<String> map = new HashSet<>();
+        for (Object s : singletons) {
+            if (map.contains(s.getClass().getName())) {
+                throw new RuntimeException("More than one instance of the same singleton class "
+                                           + s.getClass().getName() + " is available");
+            }
+            map.add(s.getClass().getName());
+        }
+    }
+
+    public static boolean isValidResourceClass(Class<?> c) {
+        if (c.isInterface() || Modifier.isAbstract(c.getModifiers())) {
+            LOG.info("Ignoring invalid resource class " + c.getName());
+            return false;
+        }
+        return true;
+    }
+
+    public static ApplicationPath locateApplicationPath(Class<?> appClass) {
+        ApplicationPath appPath = appClass.getAnnotation(ApplicationPath.class);
+        if (appPath == null && appClass.getSuperclass() != Application.class) {
+            return locateApplicationPath(appClass.getSuperclass());
+        }
+        return appPath;
+    }
+
+    private static boolean isValidApplicationClass(Class<?> c, Set<Object> singletons) {
+        if (!isValidResourceClass(c)) {
+            return false;
+        }
+        for (Object s : singletons) {
+            if (c == s.getClass()) {
+                LOG.info("Ignoring per-request resource class " + c.getName()
+                         + " as it is also registered as singleton");
+                return false;
+            }
+        }
+        return true;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/message/MessageUtils.java b/transform/src/patch/java/org/apache/cxf/message/MessageUtils.java
new file mode 100644
index 0000000..e7f7523
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/message/MessageUtils.java
@@ -0,0 +1,261 @@
+/**
+ * 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.message;
+
+import java.lang.reflect.Method;
+import java.net.HttpURLConnection;
+import java.util.Optional;
+import java.util.logging.Logger;
+
+import org.w3c.dom.Node;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.PropertyUtils;
+import org.apache.cxf.service.invoker.MethodDispatcher;
+import org.apache.cxf.service.model.BindingOperationInfo;
+
+
+/**
+ * Holder for utility methods relating to messages.
+ */
+public final class MessageUtils {
+
+    private static final Logger LOG = LogUtils.getL7dLogger(MessageUtils.class);
+
+    /**
+     * Prevents instantiation.
+     */
+    private MessageUtils() {
+    }
+
+    /**
+     * Determine if message is outbound.
+     *
+     * @param message the current Message
+     * @return true if the message direction is outbound
+     */
+    public static boolean isOutbound(Message message) {
+        if (message == null) {
+            return false;
+        }
+
+        Exchange exchange = message.getExchange();
+        return exchange != null
+               && (message == exchange.getOutMessage() || message == exchange.getOutFaultMessage());
+    }
+
+    /**
+     * Determine if message is fault.
+     *
+     * @param message the current Message
+     * @return true if the message is a fault
+     */
+    public static boolean isFault(Message message) {
+        return message != null
+               && message.getExchange() != null
+               && (message == message.getExchange().getInFaultMessage() || message == message.getExchange()
+                   .getOutFaultMessage());
+    }
+
+    /**
+     * Determine the fault mode for the underlying (fault) message
+     * (for use on server side only).
+     *
+     * @param message the fault message
+     * @return the FaultMode
+     */
+    public static FaultMode getFaultMode(Message message) {
+        if (message != null
+            && message.getExchange() != null
+            && message == message.getExchange().getOutFaultMessage()) {
+            FaultMode mode = message.get(FaultMode.class);
+            if (null != mode) {
+                return mode;
+            }
+            return FaultMode.RUNTIME_FAULT;
+        }
+        return null;
+    }
+
+    /**
+     * Determine if current messaging role is that of requestor.
+     *
+     * @param message the current Message
+     * @return true if the current messaging role is that of requestor
+     */
+    public static boolean isRequestor(Message message) {
+        if (message != null) {
+            Boolean requestor = (Boolean) message.get(Message.REQUESTOR_ROLE);
+            return requestor != null && requestor;
+        }
+        return false;
+    }
+
+    /**
+     * Determine if the current message is a partial response.
+     *
+     * @param message the current message
+     * @return true if the current messags is a partial response
+     */
+    public static boolean isPartialResponse(Message message) {
+        return message != null && Boolean.TRUE.equals(message.get(Message.PARTIAL_RESPONSE_MESSAGE));
+    }
+
+    /**
+     * Determines if the current message is an empty partial response, which
+     * is a partial response with an empty content.
+     *
+     * @param message the current message
+     * @return true if the current messags is a partial empty response
+     */
+    public static boolean isEmptyPartialResponse(Message message) {
+        return message != null && Boolean.TRUE.equals(message.get(Message.EMPTY_PARTIAL_RESPONSE_MESSAGE));
+    }
+
+    /**
+     * Returns true if a value is either the String "true" (regardless of case)  or Boolean.TRUE.
+     * @param value
+     * @return true if value is either the String "true" or Boolean.TRUE
+     * @deprecated replaced by {@link #PropertyUtils#isTrue(Object)}
+     */
+    @Deprecated
+    public static boolean isTrue(Object value) {
+        return PropertyUtils.isTrue(value);
+    }
+
+    public static boolean getContextualBoolean(Message m, String key) {
+        return getContextualBoolean(m, key, false);
+    }
+    public static boolean getContextualBoolean(Message m, String key, boolean defaultValue) {
+        if (m != null) {
+            Object o = m.getContextualProperty(key);
+            if (o != null) {
+                return PropertyUtils.isTrue(o);
+            }
+        }
+        return defaultValue;
+    }
+
+    public static int getContextualInteger(Message m, String key, int defaultValue) {
+        if (m != null) {
+            Object o = m.getContextualProperty(key);
+            if (o instanceof String) {
+                try {
+                    int i = Integer.parseInt((String)o);
+                    if (i > 0) {
+                        return i;
+                    }
+                } catch (NumberFormatException ex) {
+                    LOG.warning("Incorrect integer value of " + o + " specified for: " + key);
+                }
+            }
+        }
+        return defaultValue;
+    }
+
+    public static Object getContextualProperty(Message m, String propPreferred, String propDefault) {
+        Object prop = null;
+        if (m != null) {
+            prop = m.getContextualProperty(propPreferred);
+            if (prop == null && propDefault != null) {
+                prop = m.getContextualProperty(propDefault);
+            }
+        }
+        return prop;
+    }
+
+    /**
+     * Returns true if the underlying content format is a W3C DOM or a SAAJ message.
+     */
+    public static boolean isDOMPresent(Message m) {
+        return m != null && m.getContent(Node.class) != null;
+        /*
+        for (Class c : m.getContentFormats()) {
+            if (c.equals(Node.class) || "javax.xml.soap.SOAPMessage".equals(c.getName())) {
+                return true;
+            }
+        }
+        return false;
+        */
+    }
+
+    public static Optional<Method> getTargetMethod(Message m) {
+        Method method;
+        BindingOperationInfo bop = m.getExchange().getBindingOperationInfo();
+        if (bop != null) {
+            MethodDispatcher md = (MethodDispatcher) m.getExchange().getService().get(MethodDispatcher.class.getName());
+            method = md.getMethod(bop);
+        } else {
+            // See please JAXRSInInterceptor#RESOURCE_METHOD for the reference
+            method = (Method) m.get("org.apache.cxf.resource.method");
+        }
+        return Optional.ofNullable(method);
+    }
+    
+    /**
+     * Gets the response code from the message and tries to deduct one if it 
+     * is not set yet. 
+     * @param message message to get response code from
+     * @return response code (or deducted value assuming success)
+     */
+    public static int getReponseCodeFromMessage(Message message) {
+        Integer i = (Integer)message.get(Message.RESPONSE_CODE);
+        if (i != null) {
+            return i.intValue();
+        }
+        int code = hasNoResponseContent(message) ? HttpURLConnection.HTTP_ACCEPTED : HttpURLConnection.HTTP_OK;
+        // put the code in the message so that others can get it
+        message.put(Message.RESPONSE_CODE, code);
+        return code;
+    }
+
+    /**
+     * Determines if the current message has no response content.
+     * The message has no response content if either:
+     *  - the request is oneway and the current message is no partial
+     *    response or an empty partial response.
+     *  - the request is not oneway but the current message is an empty partial
+     *    response.
+     * @param message
+     * @return
+     */
+    public static boolean hasNoResponseContent(Message message) {
+        final boolean ow = isOneWay(message);
+        final boolean pr = MessageUtils.isPartialResponse(message);
+        final boolean epr = MessageUtils.isEmptyPartialResponse(message);
+
+        //REVISIT may need to provide an option to choose other behavior?
+        // old behavior not suppressing any responses  => ow && !pr
+        // suppress empty responses for oneway calls   => ow && (!pr || epr)
+        // suppress additionally empty responses for decoupled twoway calls =>
+        return (ow && !pr) || epr;
+    }
+    
+    /**
+     * Checks if the message is oneway or not
+     * @param message the message under consideration
+     * @return true if the message has been marked as oneway
+     */
+    public static boolean isOneWay(Message message) {
+        final Exchange ex = message.getExchange();
+        return ex != null && ex.isOneWay();
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/phase/PhaseChainCache.java b/transform/src/patch/java/org/apache/cxf/phase/PhaseChainCache.java
new file mode 100644
index 0000000..ccd12d2
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/phase/PhaseChainCache.java
@@ -0,0 +1,137 @@
+/**
+ * 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.phase;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.SortedSet;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.cxf.common.util.ModCountCopyOnWriteArrayList;
+import org.apache.cxf.interceptor.Interceptor;
+import org.apache.cxf.message.Message;
+
+/**
+ * The PhaseChainCache provides default interceptor chains for SOAP requests
+ * and responses, both from the client and web service side.  The list of
+ * phases supplied in the get() methods of this class are defined by default
+ * within org.apache.cxf.phase.PhaseManagerImpl.  For an example of this class
+ * in use, check the sourcecode of org.apache.cxf.endpoint.ClientImpl.
+ */
+public final class PhaseChainCache {
+    AtomicReference<ChainHolder> lastData = new AtomicReference<>();
+
+    public PhaseInterceptorChain get(SortedSet<Phase> phaseList,
+                                     List<Interceptor<? extends Message>> p1) {
+        return getChain(lastData, phaseList, p1);
+    }
+
+    public PhaseInterceptorChain get(SortedSet<Phase> phaseList,
+                                     List<Interceptor<? extends Message>> p1,
+                                     List<Interceptor<? extends Message>> p2) {
+        return getChain(lastData, phaseList, p1, p2);
+    }
+    public PhaseInterceptorChain get(SortedSet<Phase> phaseList,
+                                     List<Interceptor<? extends Message>> p1,
+                                     List<Interceptor<? extends Message>> p2,
+                                     List<Interceptor<? extends Message>> p3) {
+        return getChain(lastData, phaseList, p1, p2, p3);
+    }
+    public PhaseInterceptorChain get(SortedSet<Phase> phaseList,
+                                     List<Interceptor<? extends Message>> p1,
+                                     List<Interceptor<? extends Message>> p2,
+                                     List<Interceptor<? extends Message>> p3,
+                                     List<Interceptor<? extends Message>> p4) {
+        return getChain(lastData, phaseList, p1, p2, p3, p4);
+    }
+    public PhaseInterceptorChain get(SortedSet<Phase> phaseList,
+                                     List<Interceptor<? extends Message>> p1,
+                                     List<Interceptor<? extends Message>> p2,
+                                     List<Interceptor<? extends Message>> p3,
+                                     List<Interceptor<? extends Message>> p4,
+                                     List<Interceptor<? extends Message>> p5) {
+        return getChain(lastData, phaseList, p1, p2, p3, p4, p5);
+    }
+
+    @SafeVarargs
+    static PhaseInterceptorChain getChain(AtomicReference<ChainHolder> lastData,
+                                          SortedSet<Phase> phaseList,
+                                         List<Interceptor<? extends Message>> ... providers) {
+        ChainHolder last = lastData.get();
+
+        if (last == null
+            || !last.matches(providers)) {
+
+            PhaseInterceptorChain chain = new PhaseInterceptorChain(phaseList);
+            List<ModCountCopyOnWriteArrayList<Interceptor<? extends Message>>> copy
+                = new ArrayList<>(providers.length);
+            for (List<Interceptor<? extends Message>> p : providers) {
+                copy.add(new ModCountCopyOnWriteArrayList<>(p));
+                chain.add(p);
+            }
+            last = new ChainHolder(chain, copy);
+            lastData.set(last);
+        }
+
+
+        return last.chain.cloneChain();
+    }
+
+    private static class ChainHolder {
+        List<ModCountCopyOnWriteArrayList<Interceptor<? extends Message>>> lists;
+        PhaseInterceptorChain chain;
+
+        ChainHolder(PhaseInterceptorChain c,
+                    List<ModCountCopyOnWriteArrayList<Interceptor<? extends Message>>> l) {
+            lists = l;
+            chain = c;
+        }
+
+        @SafeVarargs
+        final boolean matches(List<Interceptor<? extends Message>> ... providers) {
+            if (lists.size() == providers.length) {
+                for (int x = 0; x < providers.length; x++) {
+                    if (lists.get(x).size() != providers[x].size()) {
+                        return false;
+                    }
+
+                    if (providers[x].getClass() == ModCountCopyOnWriteArrayList.class) {
+                        if (((ModCountCopyOnWriteArrayList<?>)providers[x]).getModCount()
+                            != lists.get(x).getModCount()) {
+                            return false;
+                        }
+                    } else {
+                        ListIterator<Interceptor<? extends Message>> i1 = lists.get(x).listIterator();
+                        ListIterator<Interceptor<? extends Message>> i2 = providers[x].listIterator();
+
+                        while (i1.hasNext()) {
+                            if (i1.next() != i2.next()) {
+                                return false;
+                            }
+                        }
+                    }
+                }
+                return true;
+            }
+            return false;
+        }
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/phase/PhaseInterceptorChain.java b/transform/src/patch/java/org/apache/cxf/phase/PhaseInterceptorChain.java
new file mode 100644
index 0000000..c8a52a8
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/phase/PhaseInterceptorChain.java
@@ -0,0 +1,857 @@
+/**
+ * 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.phase;
+
+import java.lang.ref.WeakReference;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.PropertyUtils;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.continuations.SuspendedInvocationException;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.interceptor.Interceptor;
+import org.apache.cxf.interceptor.InterceptorChain;
+import org.apache.cxf.interceptor.ServiceInvokerInterceptor;
+import org.apache.cxf.logging.FaultListener;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.FaultMode;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageUtils;
+import org.apache.cxf.service.Service;
+import org.apache.cxf.service.model.BindingOperationInfo;
+import org.apache.cxf.service.model.OperationInfo;
+import org.apache.cxf.transport.MessageObserver;
+
+/**
+ * A PhaseInterceptorChain orders Interceptors according to the phase they
+ * participate in and also according to the before & after properties on an
+ * Interceptor.
+ * <p>
+ * A List of phases is supplied to the PhaseInterceptorChain in the constructor.
+ * This class is typically instantiated from the PhaseChainCache class in this
+ * package. Interceptors that are added to the chain are ordered by phase.
+ * Within a phase, interceptors can order themselves. Each PhaseInterceptor
+ * has an ID. PhaseInterceptors can supply a Collection of IDs which they
+ * should run before or after, supplying fine grained ordering.
+ * <p>
+ *
+ */
+public class PhaseInterceptorChain implements InterceptorChain {
+    public static final String PREVIOUS_MESSAGE = PhaseInterceptorChain.class.getName() + ".PREVIOUS_MESSAGE";
+
+    private static final Logger LOG = LogUtils.getL7dLogger(PhaseInterceptorChain.class);
+
+    private static final ThreadLocal<Message> CURRENT_MESSAGE = new ThreadLocal<>();
+
+    private final Map<String, Integer> nameMap;
+    private final Phase[] phases;
+
+    // heads[phase] refers to the first interceptor of the given phase
+    private InterceptorHolder[] heads;
+    // tails[phase] refers to the last interceptor of the given phase
+    private InterceptorHolder[] tails;
+    // hasAfters[phase] indicates that the given phase has already inserted
+    // interceptors that may need to be placed after future to-be-inserted
+    // interceptors.  This flag is used to activate ordering of interceptors
+    // when new ones are added to the list for this phase.
+    // Note no hasBefores[] is needed because implementation adds subsequent
+    // interceptors to the end of the list by default.
+    private boolean[] hasAfters;
+
+
+    private State state;
+    private Message pausedMessage;
+    private MessageObserver faultObserver;
+    private PhaseInterceptorIterator iterator;
+    private final boolean isFineLogging;
+
+    // currently one chain for one request/response, use below as signal
+    // to avoid duplicate fault processing on nested calling of
+    // doIntercept(), which will throw same fault multi-times
+    private boolean faultOccurred;
+    private boolean chainReleased;
+
+
+    private PhaseInterceptorChain(PhaseInterceptorChain src) {
+        isFineLogging = LOG.isLoggable(Level.FINE);
+
+        //only used for clone
+        state = State.EXECUTING;
+
+        //immutable, just repoint
+        nameMap = src.nameMap;
+        phases = src.phases;
+
+        int length = phases.length;
+        hasAfters = new boolean[length];
+        System.arraycopy(src.hasAfters, 0, hasAfters, 0, length);
+
+        heads = new InterceptorHolder[length];
+        tails = new InterceptorHolder[length];
+
+        InterceptorHolder last = null;
+        for (int x = 0; x < length; x++) {
+            InterceptorHolder ih = src.heads[x];
+            while (ih != null
+                && ih.phaseIdx == x) {
+                InterceptorHolder ih2 = new InterceptorHolder(ih);
+                ih2.prev = last;
+                if (last != null) {
+                    last.next = ih2;
+                }
+                if (heads[x] == null) {
+                    heads[x] = ih2;
+                }
+                tails[x] = ih2;
+                last = ih2;
+                ih = ih.next;
+            }
+        }
+    }
+
+    public PhaseInterceptorChain(SortedSet<Phase> ps) {
+        state = State.EXECUTING;
+        isFineLogging = LOG.isLoggable(Level.FINE);
+
+        int numPhases = ps.size();
+        phases = new Phase[numPhases];
+        nameMap = new HashMap<>();
+
+        heads = new InterceptorHolder[numPhases];
+        tails = new InterceptorHolder[numPhases];
+        hasAfters = new boolean[numPhases];
+
+        int idx = 0;
+        for (Phase phase : ps) {
+            phases[idx] = phase;
+            nameMap.put(phase.getName(), idx);
+            ++idx;
+        }
+    }
+
+    public static Message getCurrentMessage() {
+        return CURRENT_MESSAGE.get();
+    }
+
+    public static boolean setCurrentMessage(PhaseInterceptorChain chain, Message m) {
+        if (getCurrentMessage() == m) {
+            return false;
+        }
+        if (chain.iterator.hasPrevious()) {
+            chain.iterator.previous();
+            if (chain.iterator.next() instanceof ServiceInvokerInterceptor) {
+                CURRENT_MESSAGE.set(m);
+                return true;
+            }
+            String error = "Only ServiceInvokerInterceptor can update the current chain message";
+            LOG.warning(error);
+            throw new IllegalStateException(error);
+        }
+        return false;
+
+    }
+
+    public synchronized State getState() {
+        return state;
+    }
+
+    public synchronized void releaseAndAcquireChain() {
+        while (!chainReleased) {
+            try {
+                this.wait();
+            } catch (InterruptedException ex) {
+                // ignore
+            }
+        }
+        chainReleased = false;
+    }
+
+    public synchronized void releaseChain() {
+        this.chainReleased = true;
+        this.notifyAll();
+    }
+
+    public PhaseInterceptorChain cloneChain() {
+        return new PhaseInterceptorChain(this);
+    }
+
+    private void updateIterator() {
+        if (iterator == null) {
+            iterator = new PhaseInterceptorIterator(heads);
+            outputChainToLog(false);
+        }
+    }
+
+    public void add(Collection<Interceptor<? extends Message>> newhandlers) {
+        add(newhandlers, false);
+    }
+
+    public void add(Collection<Interceptor<? extends Message>> newhandlers, boolean force) {
+        if (newhandlers == null) {
+            return;
+        }
+
+        for (Interceptor<? extends Message> handler : newhandlers) {
+            add(handler, force);
+        }
+    }
+
+    public void add(Interceptor<? extends Message> i) {
+        add(i, false);
+    }
+
+    public void add(Interceptor<? extends Message> i, boolean force) {
+        PhaseInterceptor<? extends Message> pi = (PhaseInterceptor<? extends Message>)i;
+
+        String phaseName = pi.getPhase();
+        Integer phase = nameMap.get(phaseName);
+
+        if (phase == null) {
+            LOG.warning("Skipping interceptor " + i.getClass().getName()
+                + ((phaseName == null) ? ": Phase declaration is missing."
+                : ": Phase " + phaseName + " specified does not exist."));
+        } else {
+            if (isFineLogging) {
+                LOG.fine("Adding interceptor " + i + " to phase " + phaseName);
+            }
+
+            insertInterceptor(phase, pi, force);
+        }
+        Collection<PhaseInterceptor<? extends Message>> extras
+            = pi.getAdditionalInterceptors();
+        if (extras != null) {
+            for (PhaseInterceptor<? extends Message> p : extras) {
+                add(p, force);
+            }
+        }
+    }
+
+    public synchronized void pause() {
+        state = State.PAUSED;
+        pausedMessage = CURRENT_MESSAGE.get();
+    }
+    public synchronized void unpause() {
+        if (state == State.PAUSED || state == State.SUSPENDED) {
+            state = State.EXECUTING;
+            pausedMessage = null;
+        }
+    }
+
+    public synchronized void suspend() {
+        state = State.SUSPENDED;
+        pausedMessage = CURRENT_MESSAGE.get();
+    }
+
+    public synchronized void resume() {
+        if (state == State.PAUSED || state == State.SUSPENDED) {
+            state = State.EXECUTING;
+            Message m = pausedMessage;
+            pausedMessage = null;
+            doIntercept(m);
+        }
+    }
+
+    /**
+     * Intercept a message, invoking each phase's handlers in turn.
+     *
+     * @param message the message
+     * @throws Exception
+     */
+    @SuppressWarnings("unchecked")
+    public synchronized boolean doIntercept(Message message) {
+        updateIterator();
+
+        Message oldMessage = CURRENT_MESSAGE.get();
+        try {
+            CURRENT_MESSAGE.set(message);
+            if (oldMessage != null
+                && !message.containsKey(PREVIOUS_MESSAGE)
+                && message != oldMessage
+                && message.getExchange() != oldMessage.getExchange()) {
+                message.put(PREVIOUS_MESSAGE, new WeakReference<Message>(oldMessage));
+            }
+            while (state == State.EXECUTING && iterator.hasNext()) {
+                try {
+                    Interceptor<Message> currentInterceptor = (Interceptor<Message>)iterator.next();
+                    if (isFineLogging) {
+                        LOG.fine("Invoking handleMessage on interceptor " + currentInterceptor);
+                    }
+                    //System.out.println("-----------" + currentInterceptor);
+                    currentInterceptor.handleMessage(message);
+                    if (state == State.SUSPENDED) {
+                         // throw the exception to make sure thread exit without interrupt
+                        throw new SuspendedInvocationException();
+                    }
+
+                } catch (SuspendedInvocationException ex) {
+
+                    // Moving the chain iterator to the previous interceptor is needed
+                    // for the invocation to be resumed from the same interceptor which
+                    // suspended the invocation.
+                    // If "suspend.chain.on.current.interceptor" is set to true then
+                    // the chain will be resumed from the interceptor which follows
+                    // the interceptor which suspended the invocation.
+                    Object suspendProp = message.remove("suspend.chain.on.current.interceptor");
+                    if ((suspendProp == null || PropertyUtils.isFalse(suspendProp))
+                        && iterator.hasPrevious()) {
+                        iterator.previous();
+                    }
+                    pause();
+                    throw ex;
+                } catch (RuntimeException ex) {
+                    if (!faultOccurred) {
+                        faultOccurred = true;
+                        wrapExceptionAsFault(message, ex);
+                    }
+                    state = State.ABORTED;
+                }
+            }
+            if (state == State.EXECUTING) {
+                state = State.COMPLETE;
+            }
+            return state == State.COMPLETE;
+        } finally {
+            CURRENT_MESSAGE.set(oldMessage);
+        }
+    }
+
+    private void wrapExceptionAsFault(Message message, RuntimeException ex) {
+        String description = getServiceInfo(message);
+
+        message.setContent(Exception.class, ex);
+        unwind(message);
+        Exception ex2 = message.getContent(Exception.class);
+        if (ex2 == null) {
+            ex2 = ex;
+        }
+
+        FaultListener flogger = (FaultListener)
+                message.getContextualProperty(FaultListener.class.getName());
+        boolean useDefaultLogging = true;
+        if (flogger != null) {
+            useDefaultLogging = flogger.faultOccurred(ex2, description, message);
+        }
+        if (useDefaultLogging) {
+            doDefaultLogging(message, ex2, description);
+        }
+
+        if (message.getExchange() != null && message.getContent(Exception.class) != null) {
+            message.getExchange().put(Exception.class, ex2);
+        }
+
+        if (faultObserver != null && !isOneWay(message)) {
+            // CXF-5629. when exchange is one way and robust, it becomes req-resp in order to
+            // send the fault
+            message.getExchange().setOneWay(false);
+            faultObserver.onMessage(message);
+        }
+    }
+
+    private String getServiceInfo(Message message) {
+        StringBuilder description = new StringBuilder();
+        if (message.getExchange() != null) {
+            Exchange exchange = message.getExchange();
+            Service service = exchange.getService();
+            if (service != null) {
+                description.append('\'');
+                description.append(service.getName());
+                BindingOperationInfo boi = exchange.getBindingOperationInfo();
+                OperationInfo opInfo = boi != null ? boi.getOperationInfo() : null;
+                if (opInfo != null) {
+                    description.append('#').append(opInfo.getName());
+                }
+                description.append("\' ");
+            }
+        }
+        return description.toString();
+    }
+
+    private void doDefaultLogging(Message message, Exception ex, String description) {
+        FaultMode mode = message.get(FaultMode.class);
+        if (mode == FaultMode.CHECKED_APPLICATION_FAULT) {
+            if (isFineLogging) {
+                LogUtils.log(LOG, Level.FINE,
+                             "Application " + description
+                             + "has thrown exception, unwinding now", ex);
+            } else if (LOG.isLoggable(Level.INFO)) {
+                Throwable t = ex;
+                if (ex instanceof Fault
+                    && ex.getCause() != null) {
+                    t = ex.getCause();
+                }
+
+                LogUtils.log(LOG, Level.INFO,
+                             "Application " + description
+                             + "has thrown exception, unwinding now: "
+                             + t.getClass().getName()
+                             + ": " + ex.getMessage());
+            }
+        } else if (LOG.isLoggable(Level.WARNING)) {
+            if (mode == FaultMode.UNCHECKED_APPLICATION_FAULT) {
+                LogUtils.log(LOG, Level.WARNING,
+                             "Application " + description
+                             + "has thrown exception, unwinding now", ex);
+            } else {
+                LogUtils.log(LOG, Level.WARNING,
+                             "Interceptor for " + description
+                             + "has thrown exception, unwinding now", ex);
+            }
+        }
+    }
+
+    private boolean isOneWay(Message message) {
+        return (message.getExchange() != null) && message.getExchange().isOneWay() && !isRobustOneWay(message);
+    }
+
+    private boolean isRobustOneWay(Message message) {
+        return MessageUtils.getContextualBoolean(message, Message.ROBUST_ONEWAY, false);
+    }
+
+    /**
+     * Intercept a message, invoking each phase's handlers in turn,
+     * starting after the specified interceptor.
+     *
+     * @param message the message
+     * @param startingAfterInterceptorID the id of the interceptor
+     * @throws Exception
+     */
+    public synchronized boolean doInterceptStartingAfter(Message message,
+                                                         String startingAfterInterceptorID) {
+        updateIterator();
+        while (state == State.EXECUTING && iterator.hasNext()) {
+            PhaseInterceptor<? extends Message> currentInterceptor
+                = (PhaseInterceptor<? extends Message>)iterator.next();
+            if (currentInterceptor.getId().equals(startingAfterInterceptorID)) {
+                break;
+            }
+        }
+        return doIntercept(message);
+    }
+
+    /**
+     * Intercept a message, invoking each phase's handlers in turn,
+     * starting at the specified interceptor.
+     *
+     * @param message the message
+     * @param startingAtInterceptorID the id of the interceptor
+     * @throws Exception
+     */
+    public synchronized boolean doInterceptStartingAt(Message message,
+                                                         String startingAtInterceptorID) {
+        updateIterator();
+        while (state == State.EXECUTING && iterator.hasNext()) {
+            PhaseInterceptor<? extends Message> currentInterceptor
+                = (PhaseInterceptor<? extends Message>)iterator.next();
+            if (currentInterceptor.getId().equals(startingAtInterceptorID)) {
+                iterator.previous();
+                break;
+            }
+        }
+        return doIntercept(message);
+    }
+
+    public synchronized void reset() {
+        updateIterator();
+        if (state == State.COMPLETE) {
+            state = State.EXECUTING;
+            iterator.reset();
+        } else {
+            iterator.reset();
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void unwind(Message message) {
+        while (iterator.hasPrevious()) {
+            Interceptor<Message> currentInterceptor = (Interceptor<Message>)iterator.previous();
+            if (isFineLogging) {
+                LOG.fine("Invoking handleFault on interceptor " + currentInterceptor);
+            }
+            try {
+                currentInterceptor.handleFault(message);
+            } catch (RuntimeException e) {
+                LOG.log(Level.WARNING, "Exception in handleFault on interceptor " + currentInterceptor, e);
+                throw e;
+            } catch (Exception e) {
+                LOG.log(Level.WARNING, "Exception in handleFault on interceptor " + currentInterceptor, e);
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public void remove(Interceptor<? extends Message> i) {
+        PhaseInterceptorIterator it = new PhaseInterceptorIterator(heads);
+        while (it.hasNext()) {
+            InterceptorHolder holder = it.nextInterceptorHolder();
+            if (holder.interceptor == i) {
+                remove(holder);
+                return;
+            }
+        }
+    }
+
+    public synchronized void abort() {
+        this.state = InterceptorChain.State.ABORTED;
+    }
+
+    public Iterator<Interceptor<? extends Message>> iterator() {
+        return getIterator();
+    }
+    public ListIterator<Interceptor<? extends Message>> getIterator() {
+        return new PhaseInterceptorIterator(heads);
+    }
+
+    private void remove(InterceptorHolder i) {
+        if (i.prev != null) {
+            i.prev.next = i.next;
+        }
+        if (i.next != null) {
+            i.next.prev = i.prev;
+        }
+        int ph = i.phaseIdx;
+        if (heads[ph] == i) {
+            if (i.next != null
+                && i.next.phaseIdx == ph) {
+                heads[ph] = i.next;
+            } else {
+                heads[ph] = null;
+                tails[ph] = null;
+            }
+        }
+        if (tails[ph] == i) {
+            if (i.prev != null
+                && i.prev.phaseIdx == ph) {
+                tails[ph] = i.prev;
+            } else {
+                heads[ph] = null;
+                tails[ph] = null;
+            }
+        }
+    }
+
+    private void insertInterceptor(int phase, PhaseInterceptor<? extends Message> interc, boolean force) {
+        InterceptorHolder ih = new InterceptorHolder(interc, phase);
+        if (heads[phase] == null) {
+            // no interceptors yet in this phase
+            heads[phase] = ih;
+            tails[phase] = ih;
+            hasAfters[phase] = !interc.getAfter().isEmpty();
+
+            int idx = phase - 1;
+            while (idx >= 0) {
+                if (tails[idx] != null) {
+                    break;
+                }
+                --idx;
+            }
+            if (idx >= 0) {
+                //found something before us, in an earlier phase
+                ih.prev = tails[idx];
+                ih.next = tails[idx].next;
+                if (ih.next != null) {
+                    ih.next.prev = ih;
+                }
+                tails[idx].next = ih;
+            } else {
+                //did not find something before us, try after
+                idx = phase + 1;
+                while (idx < heads.length) {
+                    if (heads[idx] != null) {
+                        break;
+                    }
+                    ++idx;
+                }
+
+                if (idx != heads.length) {
+                    //found something after us
+                    ih.next = heads[idx];
+                    heads[idx].prev = ih;
+                }
+            }
+        } else { // this phase already has interceptors attached
+
+            // list of interceptors that the new interceptor should precede
+            Set<String> beforeList = interc.getBefore();
+
+            // list of interceptors that the new interceptor should be after
+            Set<String> afterList = interc.getAfter();
+
+            // firstBefore will hold the first interceptor of a given phase
+            // that the interceptor to be added must precede
+            InterceptorHolder firstBefore = null;
+
+            // lastAfter will hold the last interceptor of a given phase
+            // that the interceptor to be added must come after
+            InterceptorHolder lastAfter = null;
+
+            String id = interc.getId();
+            if (hasAfters[phase] || !beforeList.isEmpty()) {
+
+                InterceptorHolder ih2 = heads[phase];
+                while (ih2 != tails[phase].next) {
+                    PhaseInterceptor<? extends Message> cmp = ih2.interceptor;
+                    String cmpId = cmp.getId();
+                    if (cmpId != null && firstBefore == null
+                        && (beforeList.contains(cmpId)
+                            || cmp.getAfter().contains(id))) {
+                        firstBefore = ih2;
+                    }
+                    if (cmp.getBefore().contains(id)
+                        || (cmpId != null && afterList.contains(cmpId))) {
+                        lastAfter = ih2;
+                    }
+                    if (!force && cmpId.equals(id)) {
+                        // interceptor is already in chain
+                        return;
+                    }
+                    ih2 = ih2.next;
+                }
+                if (lastAfter == null && beforeList.contains("*")) {
+                    firstBefore = heads[phase];
+                }
+                
+            } else if (!force) {
+                // skip interceptor if already in chain
+                InterceptorHolder ih2 = heads[phase];
+                while (ih2 != tails[phase].next) {
+                    if (ih2.interceptor.getId().equals(id)) {
+                        return;
+                    }
+                    ih2 = ih2.next;
+                }
+
+            }
+            hasAfters[phase] |= !afterList.isEmpty();
+
+            if (firstBefore == null
+                && lastAfter == null
+                && !beforeList.isEmpty()
+                && afterList.isEmpty()) {
+                //if this interceptor has stuff it MUST be before,
+                //but nothing it must be after, just
+                //stick it at the beginning
+                firstBefore = heads[phase];
+            }
+
+            if (firstBefore == null) {
+                //just add new interceptor at the end
+                ih.prev = tails[phase];
+                ih.next = tails[phase].next;
+                tails[phase].next = ih;
+
+                if (ih.next != null) {
+                    ih.next.prev = ih;
+                }
+                tails[phase] = ih;
+            } else {
+                ih.prev = firstBefore.prev;
+                if (ih.prev != null) {
+                    ih.prev.next = ih;
+                }
+                ih.next = firstBefore;
+                firstBefore.prev = ih;
+
+                if (heads[phase] == firstBefore) {
+                    heads[phase] = ih;
+                }
+            }
+        }
+        if (iterator != null) {
+            outputChainToLog(true);
+        }
+    }
+
+    public String toString() {
+        return toString("");
+    }
+    private String toString(String message) {
+        StringBuilder chain = new StringBuilder(128);
+
+        chain.append("Chain ")
+            .append(super.toString())
+            .append(message)
+            .append(". Current flow:\n");
+
+        for (int x = 0; x < phases.length; x++) {
+            if (heads[x] != null) {
+                chain.append("  ");
+                printPhase(x, chain);
+            }
+        }
+        return chain.toString();
+    }
+    private void printPhase(int ph, StringBuilder chain) {
+
+        chain.append(phases[ph].getName())
+            .append(" [");
+        InterceptorHolder i = heads[ph];
+        boolean first = true;
+        while (i != tails[ph].next) {
+            if (first) {
+                first = false;
+            } else {
+                chain.append(", ");
+            }
+            String nm = i.interceptor.getClass().getSimpleName();
+            if (StringUtils.isEmpty(nm)) {
+                nm = i.interceptor.getId();
+            }
+            chain.append(nm);
+            i = i.next;
+        }
+        chain.append("]\n");
+    }
+
+    private void outputChainToLog(boolean modified) {
+        if (isFineLogging) {
+            if (modified) {
+                LOG.fine(toString(" was modified"));
+            } else {
+                LOG.fine(toString(" was created"));
+            }
+        }
+    }
+
+    public MessageObserver getFaultObserver() {
+        return faultObserver;
+    }
+
+    public void setFaultObserver(MessageObserver faultObserver) {
+        this.faultObserver = faultObserver;
+    }
+
+    static final class PhaseInterceptorIterator implements ListIterator<Interceptor<? extends Message>> {
+        InterceptorHolder[] heads;
+        InterceptorHolder prev;
+        InterceptorHolder first;
+
+        PhaseInterceptorIterator(InterceptorHolder[] h) {
+            heads = h;
+            first = findFirst();
+        }
+
+        public void reset() {
+            prev = null;
+            first = findFirst();
+        }
+
+        private InterceptorHolder findFirst() {
+            for (int x = 0; x < heads.length; x++) {
+                if (heads[x] != null) {
+                    return heads[x];
+                }
+            }
+            return null;
+        }
+
+
+        public boolean hasNext() {
+            if (prev == null) {
+                return first != null;
+            }
+            return prev.next != null;
+        }
+
+        public Interceptor<? extends Message> next() {
+            if (prev == null) {
+                if (first == null) {
+                    throw new NoSuchElementException();
+                }
+                prev = first;
+            } else {
+                if (prev.next == null) {
+                    throw new NoSuchElementException();
+                }
+                prev = prev.next;
+            }
+            return prev.interceptor;
+        }
+        public InterceptorHolder nextInterceptorHolder() {
+            if (prev == null) {
+                if (first == null) {
+                    throw new NoSuchElementException();
+                }
+                prev = first;
+            } else {
+                if (prev.next == null) {
+                    throw new NoSuchElementException();
+                }
+                prev = prev.next;
+            }
+            return prev;
+        }
+
+        public boolean hasPrevious() {
+            return prev != null;
+        }
+        public Interceptor<? extends Message> previous() {
+            if (prev == null) {
+                throw new NoSuchElementException();
+            }
+            InterceptorHolder tmp = prev;
+            prev = prev.prev;
+            return tmp.interceptor;
+        }
+
+        public int nextIndex() {
+            throw new UnsupportedOperationException();
+        }
+        public int previousIndex() {
+            throw new UnsupportedOperationException();
+        }
+        public void add(Interceptor<? extends Message> o) {
+            throw new UnsupportedOperationException();
+        }
+        public void set(Interceptor<? extends Message> o) {
+            throw new UnsupportedOperationException();
+        }
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+
+    static final class InterceptorHolder {
+        PhaseInterceptor<? extends Message> interceptor;
+        InterceptorHolder next;
+        InterceptorHolder prev;
+        int phaseIdx;
+
+        InterceptorHolder(PhaseInterceptor<? extends Message> i, int p) {
+            interceptor = i;
+            phaseIdx = p;
+        }
+        InterceptorHolder(InterceptorHolder p) {
+            interceptor = p.interceptor;
+            phaseIdx = p.phaseIdx;
+        }
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/service/factory/FactoryBeanListener.java b/transform/src/patch/java/org/apache/cxf/service/factory/FactoryBeanListener.java
new file mode 100644
index 0000000..687f211
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/service/factory/FactoryBeanListener.java
@@ -0,0 +1,145 @@
+/**
+ * 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.service.factory;
+
+/**
+ *
+ */
+public interface FactoryBeanListener {
+    enum Event {
+        /**
+         * Event fired at the very start of processing.  No parameters.  Useful
+         * for setting up any state the listener may need to maintain.
+         */
+        START_CREATE,
+
+        /**
+         * Event fired at the very end of processing.   One parameter is passed
+         * in which is the Service object that was created.
+         */
+        END_CREATE,
+
+        /**
+         * Called at the start of processing when it detects that the service
+         * is to be created based on a wsdl contract.   One String parameter
+         * of the URL of the wsdl.
+         */
+        CREATE_FROM_WSDL,
+
+        /**
+         * Called at the start of processing when it detects that the service
+         * is to be created based on a Java class.  One Class<?> parameter
+         * of the class that is being analyzed.
+         */
+        CREATE_FROM_CLASS,
+
+        /**
+         * Called after the wsdl is loaded/parsed.   Single parameter of the
+         * WSS4J Definition of the WSDL.
+         */
+        WSDL_LOADED,
+
+        /**
+         * Called after the Service is set into the Factory after which the getService()
+         * call will return a valid value.  One parameter of the Service object.
+         */
+        SERVICE_SET,
+
+
+        /**
+         * OperationInfo, Method
+         */
+        INTERFACE_OPERATION_BOUND,
+
+        /**
+         * OperationInfo, Method, MessageInfo
+         */
+        OPERATIONINFO_IN_MESSAGE_SET,
+        OPERATIONINFO_OUT_MESSAGE_SET,
+
+        /**
+         * OperationInfo, Class<? extends Throwable>, FaultInfo
+         */
+        OPERATIONINFO_FAULT,
+
+        /**
+         * InterfaceInfo, Class<?>
+         */
+        INTERFACE_CREATED,
+
+        /**
+         * DataBinding
+         */
+        DATABINDING_INITIALIZED,
+
+        /**
+         * EndpointInfo, Endpoint, Class
+         */
+        ENDPOINT_CREATED,
+
+        /**
+         * Server, targetObject, Class
+         */
+        PRE_SERVER_CREATE,
+
+        /**
+         * Server, targetObject, Class
+         */
+        SERVER_CREATED,
+
+        /**
+         * BindingInfo, BindingOperationInfo, Implementation Method
+         */
+        BINDING_OPERATION_CREATED,
+
+        /**
+         * BindingInfo
+         */
+        BINDING_CREATED,
+
+        /**
+         * Endpoint
+         */
+        PRE_CLIENT_CREATE,
+
+        /**
+         * Endpoint, Client
+         */
+        CLIENT_CREATED,
+
+        /**
+         * EndpointInfo, Endpoint, SEI Class, Class
+         */
+        ENDPOINT_SELECTED,
+
+        /**
+         * EndpointInfo
+         */
+        ENDPOINTINFO_CREATED,
+
+        /**
+         * Class[], InvokationHandler, Proxy
+         */
+        PROXY_CREATED,
+    }
+
+
+    void handleEvent(Event ev, AbstractServiceFactoryBean factory, Object ... args);
+}
diff --git a/transform/src/patch/java/org/apache/cxf/service/invoker/FactoryInvoker.java b/transform/src/patch/java/org/apache/cxf/service/invoker/FactoryInvoker.java
new file mode 100644
index 0000000..465046b
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/service/invoker/FactoryInvoker.java
@@ -0,0 +1,70 @@
+/**
+ * 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.service.invoker;
+
+import java.util.ResourceBundle;
+
+import org.apache.cxf.common.i18n.BundleUtils;
+import org.apache.cxf.common.i18n.Message;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.message.Exchange;
+
+/**
+ * This invoker implementation calls a Factory to create the service object.
+ *
+ */
+public class FactoryInvoker extends AbstractInvoker {
+    static final ResourceBundle BUNDLE = BundleUtils.getBundle(FactoryInvoker.class);
+
+    protected Factory factory;
+
+    /**
+     * Create a FactoryInvoker object.
+     *
+     * @param factory the factory used to create service object.
+     */
+    public FactoryInvoker(Factory factory) {
+        this.factory = factory;
+    }
+    public FactoryInvoker() {
+    }
+    public void setFactory(Factory f) {
+        this.factory = f;
+    }
+
+    public Object getServiceObject(Exchange ex) {
+        try {
+            return factory.create(ex);
+        } catch (Fault e) {
+            throw e;
+        } catch (Throwable e) {
+            throw new Fault(new Message("CREATE_SERVICE_OBJECT_EXC", BUNDLE), e);
+        }
+    }
+
+    @Override
+    public void releaseServiceObject(final Exchange ex, Object obj) {
+        factory.release(ex, obj);
+    }
+
+    public boolean isSingletonFactory() {
+        return factory instanceof SingletonFactory;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/service/model/AbstractPropertiesHolder.java b/transform/src/patch/java/org/apache/cxf/service/model/AbstractPropertiesHolder.java
new file mode 100644
index 0000000..d80e02d
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/service/model/AbstractPropertiesHolder.java
@@ -0,0 +1,271 @@
+/**
+ * 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.service.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.xml.namespace.QName;
+
+public abstract class AbstractPropertiesHolder implements Extensible {
+    private AbstractPropertiesHolder delegate;
+    private boolean delegateProperties;
+
+    private AtomicReference<Map<String, Object>> propertyMap = new AtomicReference<>();
+    private AtomicReference<Object[]> extensors = new AtomicReference<>();
+    private Map<QName, Object> extensionAttributes;
+    private String documentation;
+
+
+    public final void setDelegate(AbstractPropertiesHolder p, boolean props) {
+        delegate = p;
+        delegateProperties = props;
+        if (delegate == null) {
+            return;
+        }
+        if (documentation != null) {
+            delegate.setDocumentation(documentation);
+            documentation = null;
+        }
+        if (extensionAttributes != null) {
+            delegate.setExtensionAttributes(extensionAttributes);
+            extensionAttributes = null;
+        }
+        if (extensors.get() != null) {
+            for (Object el : extensors.get()) {
+                delegate.addExtensor(el);
+            }
+            extensors.set(null);
+        }
+        if (delegateProperties && propertyMap.get() != null) {
+            for (Map.Entry<String, Object> p2 : propertyMap.get().entrySet()) {
+                delegate.setProperty(p2.getKey(), p2.getValue());
+            }
+            propertyMap.set(null);
+        }
+    }
+
+    public String getDocumentation() {
+        if (delegate != null) {
+            return delegate.getDocumentation();
+        }
+        return documentation;
+    }
+    public void setDocumentation(String s) {
+        if (delegate != null) {
+            delegate.setDocumentation(s);
+        } else {
+            documentation = s;
+        }
+    }
+    public Map<String, Object> getProperties() {
+        if (delegate != null && delegateProperties) {
+            return delegate.getProperties();
+        }
+        return propertyMap.get();
+    }
+    public Object getProperty(String name) {
+        if (delegate != null && delegateProperties) {
+            return delegate.getProperty(name);
+        }
+        if (null == propertyMap.get()) {
+            return null;
+        }
+        return propertyMap.get().get(name);
+    }
+    public Object removeProperty(String name) {
+        if (delegate != null && delegateProperties) {
+            delegate.removeProperty(name);
+        }
+        if (null == propertyMap.get()) {
+            return null;
+        }
+        return propertyMap.get().remove(name);
+    }
+
+    public <T> T getProperty(String name, Class<T> cls) {
+        return cls.cast(getProperty(name));
+    }
+    public boolean hasProperty(String name) {
+        if (delegate != null && delegateProperties) {
+            return delegate.hasProperty(name);
+        }
+        Map<String, Object> map = propertyMap.get();
+        if (map != null) {
+            return map.containsKey(name);
+        }
+        return false;
+    }
+
+    public void setProperty(String name, Object v) {
+        if (delegate != null && delegateProperties) {
+            delegate.setProperty(name, v);
+            return;
+        }
+        if (null == propertyMap.get()) {
+            propertyMap.compareAndSet(null, new ConcurrentHashMap<>(4, 0.75f, 2));
+        }
+        if (v == null) {
+            propertyMap.get().remove(name);
+        } else {
+            propertyMap.get().put(name, v);
+        }
+    }
+
+    public boolean containsExtensor(Object el) {
+        if (delegate != null) {
+            return delegate.containsExtensor(el);
+        }
+
+        Object[] exts = extensors.get();
+        if (exts != null) {
+            for (Object o : exts) {
+                if (o == el) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+    public void addExtensor(Object el) {
+        if (delegate != null) {
+            delegate.addExtensor(el);
+            return;
+        }
+        Object[] exts = extensors.get();
+        Object[] exts2;
+        if (exts == null) {
+            exts2 = new Object[1];
+        } else {
+            exts2 = new Object[exts.length + 1];
+            System.arraycopy(exts, 0, exts2, 0, exts.length);
+        }
+        exts2[exts2.length - 1] = el;
+        if (!extensors.compareAndSet(exts, exts2)) {
+            //keep trying
+            addExtensor(el);
+        }
+    }
+
+    public <T> T getExtensor(Class<T> cls) {
+        if (delegate != null) {
+            return delegate.getExtensor(cls);
+        }
+        Object[] exts = extensors.get();
+        if (exts == null) {
+            return null;
+        }
+        for (int x = 0; x < exts.length; x++) {
+            if (cls.isInstance(exts[x])) {
+                return cls.cast(exts[x]);
+            }
+        }
+        return null;
+    }
+    public <T> List<T> getExtensors(Class<T> cls) {
+        if (delegate != null) {
+            return delegate.getExtensors(cls);
+        }
+
+        Object[] exts = extensors.get();
+        if (exts == null) {
+            return null;
+        }
+        List<T> list = new ArrayList<>(exts.length);
+        for (int x = 0; x < exts.length; x++) {
+            if (cls.isInstance(exts[x])) {
+                list.add(cls.cast(exts[x]));
+            }
+        }
+        return list;
+    }
+
+    public AtomicReference<Object[]> getExtensors() {
+        if (delegate != null) {
+            return delegate.getExtensors();
+        }
+        return extensors;
+    }
+
+
+    public Object getExtensionAttribute(QName name) {
+        if (delegate != null) {
+            return delegate.getExtensionAttribute(name);
+        }
+        return null == extensionAttributes ? null : extensionAttributes.get(name);
+    }
+
+    public Map<QName, Object> getExtensionAttributes() {
+        if (delegate != null) {
+            return delegate.getExtensionAttributes();
+        }
+        return extensionAttributes;
+    }
+
+    public void addExtensionAttribute(QName name, Object attr) {
+        if (delegate != null) {
+            delegate.addExtensionAttribute(name, attr);
+            return;
+        }
+        if (null == extensionAttributes) {
+            extensionAttributes = new HashMap<>();
+        }
+        extensionAttributes.put(name, attr);
+    }
+
+    public void setExtensionAttributes(Map<QName, Object> attrs) {
+        if (delegate != null) {
+            delegate.setExtensionAttributes(attrs);
+            return;
+        }
+        extensionAttributes = attrs;
+    }
+
+    /**
+     * Lookup a configuration value. This may be found in the properties holder supplied
+     * (i.e. an EndpointInfo or ServiceInfo), or it may be a property on the Bus itself.
+     * If no value is found, the defaultValue is returned.
+     *
+     * @param defaultValue the default value
+     * @param type the extensor type
+     * @return the configuration value or the default
+     */
+    public <T> T getTraversedExtensor(T defaultValue, Class<T> type) {
+        if (delegate != null) {
+            return delegate.getTraversedExtensor(defaultValue, type);
+        }
+        T extensor = getExtensor(type);
+        if (extensor == null) {
+            return defaultValue;
+        }
+        return extensor;
+    }
+
+
+    protected static final boolean equals(Object o1, Object o2) {
+        return Objects.equals(o1, o2);
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/service/model/FaultInfo.java b/transform/src/patch/java/org/apache/cxf/service/model/FaultInfo.java
new file mode 100644
index 0000000..f2f240f
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/service/model/FaultInfo.java
@@ -0,0 +1,61 @@
+/**
+ * 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.service.model;
+
+import javax.xml.namespace.QName;
+
+public class FaultInfo extends AbstractMessageContainer {
+    private QName faultName;
+
+    public FaultInfo(QName fname, QName mname, OperationInfo info) {
+        super(info, mname);
+        faultName = fname;
+    }
+
+    public QName getFaultName() {
+        return faultName;
+    }
+    public void setFaultName(QName fname) {
+        faultName = fname;
+    }
+
+
+
+    @Override
+    public int hashCode() {
+        return faultName == null ? -1 : faultName.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (!(o instanceof FaultInfo)) {
+            return false;
+        }
+        FaultInfo oi = (FaultInfo)o;
+        return equals(faultName, oi.faultName)
+            && super.equals(o);
+    }
+
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/service/model/InterfaceInfo.java b/transform/src/patch/java/org/apache/cxf/service/model/InterfaceInfo.java
new file mode 100644
index 0000000..a5c3f26
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/service/model/InterfaceInfo.java
@@ -0,0 +1,124 @@
+/**
+ * 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.service.model;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Logger;
+
+import javax.xml.namespace.QName;
+
+import org.apache.cxf.common.i18n.Message;
+import org.apache.cxf.common.logging.LogUtils;
+
+public class InterfaceInfo extends AbstractDescriptionElement implements NamedItem {
+    private static final Logger LOG = LogUtils.getL7dLogger(InterfaceInfo.class);
+
+    QName name;
+    ServiceInfo service;
+
+    Map<QName, OperationInfo> operations = new ConcurrentHashMap<>(4, 0.75f, 2);
+
+    public InterfaceInfo(ServiceInfo info, QName q) {
+        name = q;
+        service = info;
+        info.setInterface(this);
+    }
+    public DescriptionInfo getDescription() {
+        if (service == null) {
+            return null;
+        }
+        return service.getDescription();
+    }
+
+    public ServiceInfo getService() {
+        return service;
+    }
+
+    public void setName(QName n) {
+        name = n;
+    }
+    public QName getName() {
+        return name;
+    }
+
+
+    /**
+     * Adds an operation to this service.
+     *
+     * @param oname the qualified name of the operation.
+     * @return the operation.
+     */
+    public OperationInfo addOperation(QName oname) {
+        if (oname == null) {
+            throw new NullPointerException(
+                new Message("OPERATION.NAME.NOT.NULL", LOG).toString());
+        }
+        if (operations.containsKey(oname)) {
+            throw new IllegalArgumentException(
+                new Message("DUPLICATED.OPERATION.NAME", LOG, oname).toString());
+        }
+
+        OperationInfo operation = new OperationInfo(this, oname);
+        addOperation(operation);
+        return operation;
+    }
+
+    /**
+     * Adds an operation to this service.
+     *
+     * @param operation the operation.
+     */
+    void addOperation(OperationInfo operation) {
+        operations.put(operation.getName(), operation);
+    }
+
+    /**
+     * Removes an operation from this service.
+     *
+     * @param operation the operation.
+     */
+    public void removeOperation(OperationInfo operation) {
+        operations.remove(operation.getName());
+    }
+
+    /**
+     * Returns the operation info with the given name, if found.
+     *
+     * @param oname the name.
+     * @return the operation; or <code>null</code> if not found.
+     */
+    public OperationInfo getOperation(QName oname) {
+        return operations.get(oname);
+    }
+
+    /**
+     * Returns all operations for this service.
+     *
+     * @return all operations.
+     */
+    public Collection<OperationInfo> getOperations() {
+        return Collections.unmodifiableCollection(operations.values());
+    }
+
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/service/model/OperationInfo.java b/transform/src/patch/java/org/apache/cxf/service/model/OperationInfo.java
new file mode 100644
index 0000000..d5fe4d3
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/service/model/OperationInfo.java
@@ -0,0 +1,242 @@
+/**
+ * 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.service.model;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Logger;
+
+import javax.xml.namespace.QName;
+
+import org.apache.cxf.common.i18n.Message;
+import org.apache.cxf.common.logging.LogUtils;
+
+public class OperationInfo extends AbstractPropertiesHolder implements NamedItem {
+    private static final Logger LOG = LogUtils.getL7dLogger(OperationInfo.class);
+    InterfaceInfo intf;
+    QName opName;
+    String inName;
+    MessageInfo inputMessage;
+    String outName;
+    MessageInfo outputMessage;
+    Map<QName, FaultInfo> faults;
+    OperationInfo unwrappedOperation;
+    List<String> parameterOrdering;
+
+    public OperationInfo() {
+    }
+
+    OperationInfo(InterfaceInfo it, QName n) {
+        intf = it;
+        setName(n);
+    }
+    OperationInfo(OperationInfo op) {
+        intf = op.getInterface();
+        setName(op.getName());
+    }
+
+    /**
+     * Returns the name of the Operation.
+     * @return the name of the Operation
+     */
+    public QName getName() {
+        return opName;
+    }
+    /**
+     * Sets the name of the operation.
+     * @param name the new name of the operation
+     */
+    public final void setName(QName name) {
+        if (name == null) {
+            throw new NullPointerException("Operation Name cannot be null.");
+        }
+        opName = name;
+    }
+    public InterfaceInfo getInterface() {
+        return intf;
+    }
+
+
+    public MessageInfo createMessage(QName nm, MessageInfo.Type type) {
+        return new MessageInfo(this, type, nm);
+    }
+
+    public MessageInfo getOutput() {
+        return outputMessage;
+    }
+    public String getOutputName() {
+        return outName;
+    }
+    public void setOutput(String nm, MessageInfo out) {
+        outName = nm;
+        outputMessage = out;
+        if (unwrappedOperation != null && unwrappedOperation.getOutput() != null) {
+            unwrappedOperation.getOutput().setDelegate(out, false);
+        }
+    }
+    public boolean hasOutput() {
+        return outputMessage != null;
+    }
+
+    public MessageInfo getInput() {
+        return inputMessage;
+    }
+    public String getInputName() {
+        return inName;
+    }
+    public void setInput(String nm, MessageInfo in) {
+        inName = nm;
+        inputMessage = in;
+        if (unwrappedOperation != null && unwrappedOperation.getInput() != null) {
+            unwrappedOperation.getInput().setDelegate(in, false);
+        }
+    }
+    public boolean hasInput() {
+        return inputMessage != null;
+    }
+
+    public boolean isOneWay() {
+        return inputMessage != null && outputMessage == null;
+    }
+
+    public boolean isUnwrappedCapable() {
+        return unwrappedOperation != null;
+    }
+
+    public OperationInfo getUnwrappedOperation() {
+        return unwrappedOperation;
+    }
+    public void setUnwrappedOperation(OperationInfo op) {
+        unwrappedOperation = op;
+    }
+    public boolean isUnwrapped() {
+        return false;
+    }
+
+
+    /**
+     * Adds an fault to this operation.
+     *
+     * @param name the fault name.
+     */
+    public FaultInfo addFault(QName name, QName message) {
+        if (name == null) {
+            throw new NullPointerException(new Message("FAULT.NAME.NOT.NULL", LOG).toString());
+        }
+        if (faults != null && faults.containsKey(name)) {
+            throw new IllegalArgumentException(
+                new Message("DUPLICATED.FAULT.NAME", LOG, name).toString());
+        }
+        FaultInfo fault = new FaultInfo(name, message, this);
+        addFault(fault);
+        return fault;
+    }
+
+    /**
+     * Adds a fault to this operation.
+     *
+     * @param fault the fault.
+     */
+    public synchronized void addFault(FaultInfo fault) {
+        if (faults == null) {
+            faults = new ConcurrentHashMap<>(4, 0.75f, 2);
+        }
+        faults.put(fault.getFaultName(), fault);
+    }
+
+    /**
+     * Removes a fault from this operation.
+     *
+     * @param name the qualified fault name.
+     */
+    public void removeFault(QName name) {
+        if (faults != null) {
+            faults.remove(name);
+        }
+    }
+
+    /**
+     * Returns the fault with the given name, if found.
+     *
+     * @param name the name.
+     * @return the fault; or <code>null</code> if not found.
+     */
+    public FaultInfo getFault(QName name) {
+        if (faults != null) {
+            return faults.get(name);
+        }
+        return null;
+    }
+
+    public boolean hasFaults() {
+        return faults != null && !faults.isEmpty();
+    }
+
+    /**
+     * Returns all faults for this operation.
+     *
+     * @return all faults.
+     */
+    public Collection<FaultInfo> getFaults() {
+        if (faults == null) {
+            return Collections.emptyList();
+        }
+        return Collections.unmodifiableCollection(faults.values());
+    }
+
+    public void setParameterOrdering(List<String> o) {
+        this.parameterOrdering = o;
+    }
+
+    public List<String> getParameterOrdering() {
+        return parameterOrdering;
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder("[OperationInfo: ")
+            .append(opName)
+            .append(']').toString();
+    }
+
+    public int hashCode() {
+        return opName == null ? -1 : opName.hashCode();
+    }
+
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (!(o instanceof OperationInfo)) {
+            return false;
+        }
+        OperationInfo oi = (OperationInfo)o;
+        return equals(opName, oi.opName)
+            && equals(inputMessage, oi.inputMessage)
+            && equals(outputMessage, oi.outputMessage)
+            && equals(faults, oi.faults)
+            && equals(intf.getName(), oi.intf.getName());
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/service/model/UnwrappedOperationInfo.java b/transform/src/patch/java/org/apache/cxf/service/model/UnwrappedOperationInfo.java
new file mode 100644
index 0000000..31ed72c
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/service/model/UnwrappedOperationInfo.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.service.model;
+
+import java.util.Collection;
+
+import javax.xml.namespace.QName;
+
+public class UnwrappedOperationInfo extends OperationInfo {
+    OperationInfo wrappedOp;
+
+    public UnwrappedOperationInfo(OperationInfo op) {
+        super(op);
+        wrappedOp = op;
+        setDelegate(wrappedOp, true);
+    }
+
+    public OperationInfo getWrappedOperation() {
+        return wrappedOp;
+    }
+
+    @Override
+    public boolean isUnwrapped() {
+        return true;
+    }
+
+    @Override
+    public FaultInfo addFault(QName name, QName message) {
+        return wrappedOp.addFault(name, message);
+    }
+
+    @Override
+    public FaultInfo getFault(QName name) {
+        return wrappedOp.getFault(name);
+    }
+
+    @Override
+    public Collection<FaultInfo> getFaults() {
+        return wrappedOp.getFaults();
+    }
+
+
+    @Override
+    public void setOutput(String nm, MessageInfo out) {
+        super.setOutput(nm, out);
+        out.setDelegate(wrappedOp.getOutput(), false);
+    }
+
+    @Override
+    public void setInput(String nm, MessageInfo in) {
+        super.setInput(nm, in);
+        in.setDelegate(wrappedOp.getInput(), false);
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/staxutils/StaxUtils.java b/transform/src/patch/java/org/apache/cxf/staxutils/StaxUtils.java
new file mode 100644
index 0000000..440968c
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/staxutils/StaxUtils.java
@@ -0,0 +1,2222 @@
+/**
+ * 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.staxutils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.Location;
+import javax.xml.stream.StreamFilter;
+import javax.xml.stream.XMLEventFactory;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLResolver;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.Characters;
+import javax.xml.stream.events.DTD;
+import javax.xml.stream.events.Namespace;
+import javax.xml.stream.events.StartDocument;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stax.StAXSource;
+import javax.xml.transform.stream.StreamSource;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.Element;
+import org.w3c.dom.EntityReference;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+import org.w3c.dom.UserDataHandler;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.PropertyUtils;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.common.util.SystemPropertyAction;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.helpers.DOMUtils;
+import org.apache.cxf.message.Message;
+
+public final class StaxUtils {
+    // System properties for defaults, but also contextual properties usable
+    // for StaxInInterceptor
+    public static final String MAX_CHILD_ELEMENTS =
+        "org.apache.cxf.stax.maxChildElements";
+    public static final String MAX_ELEMENT_DEPTH =
+        "org.apache.cxf.stax.maxElementDepth";
+    public static final String MAX_ATTRIBUTE_COUNT =
+        "org.apache.cxf.stax.maxAttributeCount";
+    public static final String MAX_ATTRIBUTE_SIZE =
+        "org.apache.cxf.stax.maxAttributeSize";
+    public static final String MAX_TEXT_LENGTH =
+        "org.apache.cxf.stax.maxTextLength";
+    public static final String MIN_TEXT_SEGMENT =
+        "org.apache.cxf.stax.minTextSegment";
+    public static final String MAX_ELEMENT_COUNT =
+        "org.apache.cxf.stax.maxElementCount";
+    public static final String MAX_XML_CHARACTERS =
+        "org.apache.cxf.stax.maxXMLCharacters";
+
+    public static final String ALLOW_INSECURE_PARSER =
+        "org.apache.cxf.stax.allowInsecureParser";
+
+    private static final String INNER_ELEMENT_COUNT_SYSTEM_PROP =
+        "org.apache.cxf.staxutils.innerElementCountThreshold";
+    private static final String INNER_ELEMENT_LEVEL_SYSTEM_PROP =
+        "org.apache.cxf.staxutils.innerElementLevelThreshold";
+    private static final String AUTO_CLOSE_INPUT_SOURCE_PROP =
+        "org.apache.cxf.staxutils.autoCloseInputSource";
+
+    private static final Logger LOG = LogUtils.getL7dLogger(StaxUtils.class);
+
+    private static final Queue<XMLInputFactory> NS_AWARE_INPUT_FACTORY_POOL;
+    private static final XMLInputFactory SAFE_INPUT_FACTORY;
+    private static final Queue<XMLOutputFactory> OUTPUT_FACTORY_POOL;
+    private static final XMLOutputFactory SAFE_OUTPUT_FACTORY;
+
+    private static final String XML_NS = "http://www.w3.org/2000/xmlns/";
+    private static final String[] DEF_PREFIXES = new String[] {
+        "ns1".intern(), "ns2".intern(), "ns3".intern(),
+        "ns4".intern(), "ns5".intern(), "ns6".intern(),
+        "ns7".intern(), "ns8".intern(), "ns9".intern()
+    };
+
+    private static final int MAX_ATTR_COUNT_VAL =
+            getInteger(MAX_ATTRIBUTE_COUNT, 500);
+    private static final int MAX_ATTR_SIZE_VAL =
+            getInteger(MAX_ATTRIBUTE_SIZE, 64 * 1024); //64K per attribute, likely just "list" will hit
+    private static final int MAX_TEXT_LENGTH_VAL =
+            getInteger(MAX_TEXT_LENGTH, 128 * 1024 * 1024);  //128M - more than this should DEFINITELY use MTOM
+    private static final int MIN_TEXT_SEGMENT_VAL =
+            getInteger(MIN_TEXT_SEGMENT, 64); // Same default as woodstox
+    private static final long MAX_ELEMENT_COUNT_VAL =
+            getLong(MAX_ELEMENT_COUNT, Long.MAX_VALUE);
+    private static final long MAX_XML_CHARS_VAL =
+            getLong(MAX_XML_CHARACTERS, Long.MAX_VALUE);
+    private static final int PARSER_POOL_SIZE_VAL =
+            getInteger("org.apache.cxf.staxutils.pool-size", 20);
+    private static final boolean ALLOW_INSECURE_PARSER_VAL;
+    private static final boolean AUTO_CLOSE_INPUT_SOURCE;
+
+    // Here we check old names first and then new names for the threshold properties
+    private static final int MAX_ELEMENT_DEPTH_VAL =
+            getInteger(MAX_ELEMENT_DEPTH, getInteger(INNER_ELEMENT_LEVEL_SYSTEM_PROP, 100));
+    private static final int MAX_CHILD_ELEMENTS_VAL =
+            getInteger(MAX_CHILD_ELEMENTS, getInteger(INNER_ELEMENT_COUNT_SYSTEM_PROP, 50000));
+
+    // Variables from Woodstox
+    private static final String P_MAX_ATTRIBUTES_PER_ELEMENT = "com.ctc.wstx.maxAttributesPerElement";
+    private static final String P_MAX_ATTRIBUTE_SIZE = "com.ctc.wstx.maxAttributeSize";
+    private static final String P_MAX_TEXT_LENGTH = "com.ctc.wstx.maxTextLength";
+    private static final String P_MAX_ELEMENT_COUNT = "com.ctc.wstx.maxElementCount";
+    private static final String P_MAX_CHARACTERS = "com.ctc.wstx.maxCharacters";
+    private static final String P_MAX_ELEMENT_DEPTH = "com.ctc.wstx.maxElementDepth";
+    private static final String P_MAX_CHILDREN_PER_ELEMENT = "com.ctc.wstx.maxChildrenPerElement";
+    private static final String P_MIN_TEXT_SEGMENT = "com.ctc.wstx.minTextSegment";
+
+
+    static {
+        NS_AWARE_INPUT_FACTORY_POOL = new ArrayBlockingQueue<>(PARSER_POOL_SIZE_VAL);
+        OUTPUT_FACTORY_POOL = new ArrayBlockingQueue<>(PARSER_POOL_SIZE_VAL);
+
+        String allowInsecureParser = SystemPropertyAction.getPropertyOrNull(ALLOW_INSECURE_PARSER);
+        if (!StringUtils.isEmpty(allowInsecureParser)) {
+            ALLOW_INSECURE_PARSER_VAL = "1".equals(allowInsecureParser) || Boolean.parseBoolean(allowInsecureParser);
+        } else {
+            ALLOW_INSECURE_PARSER_VAL = false;
+        }
+        
+        String autoCloseInputSource = SystemPropertyAction.getPropertyOrNull(AUTO_CLOSE_INPUT_SOURCE_PROP);
+        if (!StringUtils.isEmpty(autoCloseInputSource)) {
+            AUTO_CLOSE_INPUT_SOURCE = "1".equals(autoCloseInputSource) || Boolean.parseBoolean(autoCloseInputSource);
+        } else {
+            AUTO_CLOSE_INPUT_SOURCE = false; /* set 'false' by default */
+        }
+
+        XMLInputFactory xif = null;
+        try {
+            xif = createXMLInputFactory(true);
+            String xifClassName = xif.getClass().getName();
+            if (!xifClassName.contains("ctc.wstx") && !xifClassName.contains("xml.xlxp")
+                    && !xifClassName.contains("xml.xlxp2") && !xifClassName.contains("bea.core")) {
+                xif = null;
+            }
+        } catch (Throwable t) {
+            //ignore, can always drop down to the pooled factories
+        }
+        SAFE_INPUT_FACTORY = xif;
+
+        XMLOutputFactory xof = null;
+        try {
+            xof = XMLOutputFactory.newInstance();
+            String xofClassName = xof.getClass().getName();
+            if (!xofClassName.contains("ctc.wstx") && !xofClassName.contains("xml.xlxp")
+                && !xofClassName.contains("xml.xlxp2") && !xofClassName.contains("bea.core")) {
+                xof = null;
+            }
+        } catch (Throwable t) {
+            //ignore, can always drop down to the pooled factories
+        }
+        SAFE_OUTPUT_FACTORY = xof;
+
+    }
+
+    private StaxUtils() {
+    }
+    private static int getInteger(String prop, int def) {
+        try {
+            String s = SystemPropertyAction.getPropertyOrNull(prop);
+            if (StringUtils.isEmpty(s)) {
+                return def;
+            }
+            int i = Integer.parseInt(s);
+            if (i < 0) {
+                i = def;
+            }
+            return i;
+        } catch (Throwable t) {
+            //ignore
+        }
+        return def;
+    }
+    private static long getLong(String prop, long def) {
+        try {
+            String s = SystemPropertyAction.getPropertyOrNull(prop);
+            if (StringUtils.isEmpty(s)) {
+                return def;
+            }
+            long i = Long.parseLong(s);
+            if (i < 0) {
+                i = def;
+            }
+            return i;
+        } catch (Throwable t) {
+            //ignore
+        }
+        return def;
+    }
+
+    /**
+     * Return a cached, namespace-aware, factory.
+     */
+    private static XMLInputFactory getXMLInputFactory() {
+        if (SAFE_INPUT_FACTORY != null) {
+            return SAFE_INPUT_FACTORY;
+        }
+        XMLInputFactory f = NS_AWARE_INPUT_FACTORY_POOL.poll();
+        if (f == null) {
+            f = createXMLInputFactory(true);
+        }
+        return f;
+    }
+
+    private static void returnXMLInputFactory(XMLInputFactory factory) {
+        if (SAFE_INPUT_FACTORY != factory) {
+            NS_AWARE_INPUT_FACTORY_POOL.offer(factory);
+        }
+    }
+
+    private static XMLOutputFactory getXMLOutputFactory() {
+        if (SAFE_OUTPUT_FACTORY != null) {
+            return SAFE_OUTPUT_FACTORY;
+        }
+        XMLOutputFactory f = OUTPUT_FACTORY_POOL.poll();
+        if (f == null) {
+            f = XMLOutputFactory.newInstance();
+        }
+        return f;
+    }
+
+    private static void returnXMLOutputFactory(XMLOutputFactory factory) {
+        if (SAFE_OUTPUT_FACTORY != factory) {
+            OUTPUT_FACTORY_POOL.offer(factory);
+        }
+    }
+
+    /**
+     * Return a new factory so that the caller can set sticky parameters.
+     * @param nsAware
+     * @throws XMLStreamException
+     */
+    public static XMLInputFactory createXMLInputFactory(boolean nsAware) {
+        XMLInputFactory factory = null;
+        try {
+            factory = XMLInputFactory.newInstance();
+        } catch (Throwable t) {
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.log(Level.FINE, "XMLInputFactory.newInstance() failed with: ", t);
+            }
+        }
+        if (factory == null || !setRestrictionProperties(factory)) {
+            try {
+                factory = createWoodstoxFactory();
+            } catch (Throwable t) {
+                if (LOG.isLoggable(Level.FINE)) {
+                    LOG.log(Level.FINE, "Cannot create Woodstox XMLInputFactory: ", t);
+                }
+            }
+
+            if (factory == null) {
+                throw new RuntimeException("Failed to create XMLInputFactory.");
+            }
+
+            if (!setRestrictionProperties(factory)) {
+                if (ALLOW_INSECURE_PARSER_VAL) {
+                    LOG.log(Level.WARNING, "INSECURE_PARSER_DETECTED", factory.getClass().getName());
+                } else {
+                    throw new RuntimeException("Cannot create a secure XMLInputFactory, "
+                        + "you should either add woodstox or set " + ALLOW_INSECURE_PARSER
+                        + " system property to true if an unsafe mode is acceptable.");
+                }
+            }
+        }
+        setProperty(factory, XMLInputFactory.IS_NAMESPACE_AWARE, nsAware);
+        setProperty(factory, XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
+        setProperty(factory, XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.FALSE);
+        setProperty(factory, XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
+        factory.setXMLResolver(new XMLResolver() {
+            public Object resolveEntity(String publicID, String systemID,
+                                        String baseURI, String namespace)
+                throws XMLStreamException {
+                throw new XMLStreamException("Reading external entities is disabled");
+            }
+        });
+
+        return factory;
+    }
+
+    private static XMLInputFactory createWoodstoxFactory() {
+        return WoodstoxHelper.createInputFactory();
+    }
+
+    public static XMLEventFactory createWoodstoxEventFactory() {
+        return WoodstoxHelper.createEventFactory();
+    }
+
+    private static boolean setRestrictionProperties(XMLInputFactory factory) {
+        //For now, we can only support Woodstox 4.2.x and newer as none of the other
+        //stax parsers support these settings
+        final boolean wstxMaxs = setProperty(factory, P_MAX_ATTRIBUTES_PER_ELEMENT, MAX_ATTR_COUNT_VAL)
+                    && setProperty(factory, P_MAX_ATTRIBUTE_SIZE, MAX_ATTR_SIZE_VAL)
+                    && setProperty(factory, P_MAX_CHILDREN_PER_ELEMENT, MAX_CHILD_ELEMENTS_VAL)
+                    && setProperty(factory, P_MAX_ELEMENT_COUNT, MAX_ELEMENT_COUNT_VAL)
+                    && setProperty(factory, P_MAX_ELEMENT_DEPTH, MAX_ELEMENT_DEPTH_VAL)
+                    && setProperty(factory, P_MAX_CHARACTERS, MAX_XML_CHARS_VAL)
+                    && setProperty(factory, P_MAX_TEXT_LENGTH, MAX_TEXT_LENGTH_VAL);
+        return wstxMaxs
+            && setProperty(factory, P_MIN_TEXT_SEGMENT, MIN_TEXT_SEGMENT_VAL);
+    }
+
+    private static boolean setProperty(XMLInputFactory f, String p, Object o) {
+        try {
+            f.setProperty(p,  o);
+            return true;
+        } catch (Throwable t) {
+            //ignore
+        }
+        return false;
+    }
+
+
+
+    public static XMLStreamWriter createXMLStreamWriter(Writer out) {
+        XMLOutputFactory factory = getXMLOutputFactory();
+        try {
+            return factory.createXMLStreamWriter(out);
+        } catch (XMLStreamException e) {
+            throw new RuntimeException("Cant' create XMLStreamWriter", e);
+        } finally {
+            returnXMLOutputFactory(factory);
+        }
+    }
+
+    public static XMLStreamWriter createXMLStreamWriter(OutputStream out) {
+        return createXMLStreamWriter(out, null);
+    }
+
+    public static XMLStreamWriter createXMLStreamWriter(OutputStream out, String encoding) {
+        XMLOutputFactory factory = getXMLOutputFactory();
+        try {
+            return factory.createXMLStreamWriter(out, encoding != null ? encoding : StandardCharsets.UTF_8.name());
+        } catch (XMLStreamException e) {
+            throw new RuntimeException("Cant' create XMLStreamWriter", e);
+        } finally {
+            returnXMLOutputFactory(factory);
+        }
+    }
+
+    public static XMLStreamWriter createXMLStreamWriter(Result r) {
+        if (r instanceof DOMResult) {
+            //use our own DOM writer to avoid issues with Sun's
+            //version that doesn't support getNamespaceContext
+            DOMResult dr = (DOMResult)r;
+            Node nd = dr.getNode();
+            if (nd instanceof Document) {
+                return new W3CDOMStreamWriter((Document)nd);
+            } else if (nd instanceof Element) {
+                return new W3CDOMStreamWriter((Element)nd);
+            } else if (nd instanceof DocumentFragment) {
+                return new W3CDOMStreamWriter((DocumentFragment)nd);
+            }
+        }
+        XMLOutputFactory factory = getXMLOutputFactory();
+        try {
+            return factory.createXMLStreamWriter(r);
+        } catch (XMLStreamException e) {
+            throw new RuntimeException("Cant' create XMLStreamWriter", e);
+        } finally {
+            returnXMLOutputFactory(factory);
+        }
+    }
+
+    public static XMLStreamReader createFilteredReader(XMLStreamReader reader, StreamFilter filter) {
+        XMLInputFactory factory = getXMLInputFactory();
+        try {
+            return factory.createFilteredReader(reader, filter);
+        } catch (XMLStreamException e) {
+            throw new RuntimeException("Cant' create XMLStreamReader", e);
+        } finally {
+            returnXMLInputFactory(factory);
+        }
+    }
+
+
+    public static void nextEvent(XMLStreamReader dr) {
+        try {
+            dr.next();
+        } catch (XMLStreamException e) {
+            throw new RuntimeException("Couldn't parse stream.", e);
+        }
+    }
+
+    public static boolean toNextText(DepthXMLStreamReader reader) {
+        if (reader.getEventType() == XMLStreamConstants.CHARACTERS) {
+            return true;
+        }
+
+        try {
+            int depth = reader.getDepth();
+            int event = reader.getEventType();
+            while (reader.getDepth() >= depth && reader.hasNext()) {
+                if (event == XMLStreamConstants.CHARACTERS && reader.getDepth() == depth + 1) {
+                    return true;
+                }
+                event = reader.next();
+            }
+            return false;
+        } catch (XMLStreamException e) {
+            throw new RuntimeException("Couldn't parse stream.", e);
+        }
+    }
+    public static boolean toNextTag(XMLStreamReader reader) {
+        try {
+            // advance to first tag.
+            int x = reader.getEventType();
+            while (x != XMLStreamConstants.START_ELEMENT
+                && x != XMLStreamConstants.END_ELEMENT
+                && reader.hasNext()) {
+                x = reader.next();
+            }
+        } catch (XMLStreamException e) {
+            throw new RuntimeException("Couldn't parse stream.", e);
+        }
+        return true;
+    }
+
+    public static boolean toNextTag(DepthXMLStreamReader reader, QName endTag) {
+        try {
+            int depth = reader.getDepth();
+            int event = reader.getEventType();
+            while (reader.getDepth() >= depth && reader.hasNext()) {
+                if (event == XMLStreamConstants.START_ELEMENT && reader.getName().equals(endTag)
+                    && reader.getDepth() == depth + 1) {
+                    return true;
+                }
+                event = reader.next();
+            }
+            return false;
+        } catch (XMLStreamException e) {
+            throw new RuntimeException("Couldn't parse stream.", e);
+        }
+    }
+
+    public static void writeStartElement(XMLStreamWriter writer, String prefix, String name, String namespace)
+        throws XMLStreamException {
+        if (prefix == null) {
+            prefix = "";
+        }
+
+        if (!namespace.isEmpty()) {
+            writer.writeStartElement(prefix, name, namespace);
+            if (!prefix.isEmpty()) {
+                writer.writeNamespace(prefix, namespace);
+                writer.setPrefix(prefix, namespace);
+            } else {
+                writer.writeDefaultNamespace(namespace);
+                writer.setDefaultNamespace(namespace);
+            }
+        } else {
+            writer.writeStartElement(name);
+            writer.writeDefaultNamespace("");
+            writer.setDefaultNamespace("");
+        }
+    }
+
+    /**
+     * Returns true if currently at the start of an element, otherwise move
+     * forwards to the next element start and return true, otherwise false is
+     * returned if the end of the stream is reached.
+     */
+    public static boolean skipToStartOfElement(XMLStreamReader in) throws XMLStreamException {
+        for (int code = in.getEventType(); code != XMLStreamConstants.END_DOCUMENT; code = in.next()) {
+            if (code == XMLStreamConstants.START_ELEMENT) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static boolean toNextElement(DepthXMLStreamReader dr) {
+        if (dr.getEventType() == XMLStreamConstants.START_ELEMENT) {
+            return true;
+        }
+        if (dr.getEventType() == XMLStreamConstants.END_ELEMENT) {
+            return false;
+        }
+        try {
+            int depth = dr.getDepth();
+
+            for (int event = dr.getEventType(); dr.getDepth() >= depth && dr.hasNext(); event = dr.next()) {
+                if (event == XMLStreamConstants.START_ELEMENT && dr.getDepth() == depth + 1) {
+                    return true;
+                } else if (event == XMLStreamConstants.END_ELEMENT) {
+                    depth--;
+                }
+            }
+
+            return false;
+        } catch (XMLStreamException e) {
+            throw new RuntimeException("Couldn't parse stream.", e);
+        }
+    }
+
+    public static boolean skipToStartOfElement(DepthXMLStreamReader in) throws XMLStreamException {
+        for (int code = in.getEventType(); code != XMLStreamConstants.END_DOCUMENT; code = in.next()) {
+            if (code == XMLStreamConstants.START_ELEMENT) {
+                return true;
+            }
+        }
+        return false;
+    }
+    public static void copy(Source source, OutputStream os) throws XMLStreamException {
+        XMLStreamWriter writer = createXMLStreamWriter(os);
+        try {
+            copy(source, writer);
+        } finally {
+            try {
+                writer.flush();
+            } catch (XMLStreamException ex) {
+                //ignore
+            }
+            StaxUtils.close(writer);
+        }
+    }
+    public static void copy(Source source, XMLStreamWriter writer) throws XMLStreamException {
+        if (source instanceof StaxSource) {
+            StaxSource ss = (StaxSource)source;
+            if (ss.getXMLStreamReader() == null) {
+                return;
+            }
+        } else if (source instanceof StAXSource) {
+            StAXSource ss = (StAXSource)source;
+            if (ss.getXMLStreamReader() == null) {
+                return;
+            }
+        } else if (source instanceof SAXSource) {
+            SAXSource ss = (SAXSource)source;
+            InputSource src = ss.getInputSource();
+            if (src == null || (src.getSystemId() == null && src.getPublicId() == null)) {
+                if (ss.getXMLReader() != null) {
+                    //OK - reader is OK.  We'll use that out
+                    StreamWriterContentHandler ch = new StreamWriterContentHandler(writer);
+                    XMLReader reader = ((SAXSource)source).getXMLReader();
+                    reader.setContentHandler(ch);
+                    try {
+                        try {
+                            reader.setFeature("http://xml.org/sax/features/namespaces", true);
+                        } catch (Throwable t) {
+                            //ignore
+                        }
+                        try {
+                            reader.setProperty("http://xml.org/sax/properties/lexical-handler", ch);
+                        } catch (Throwable t) {
+                            //ignore
+                        }
+                        reader.parse(((SAXSource)source).getInputSource());
+                        return;
+                    } catch (Exception e) {
+                        throw new XMLStreamException(e.getMessage(), e);
+                    }
+                } else if (ss.getInputSource() == null) {
+                    //nothing to copy, just return
+                    return;
+                }
+            }
+
+        } else if (source instanceof StreamSource) {
+            StreamSource ss = (StreamSource)source;
+            if (ss.getInputStream() == null
+                && ss.getReader() == null
+                && ss.getSystemId() == null) {
+                //nothing to copy, just return
+                return;
+            }
+        }
+        XMLStreamReader reader = createXMLStreamReader(source);
+        copy(reader, writer);
+        reader.close();
+    }
+
+    public static Document copy(Document doc)
+        throws XMLStreamException, ParserConfigurationException {
+
+        XMLStreamReader reader = createXMLStreamReader(doc);
+        W3CDOMStreamWriter writer = new W3CDOMStreamWriter();
+        copy(reader, writer);
+        Document d = writer.getDocument();
+        try {
+            d.setDocumentURI(doc.getDocumentURI());
+        } catch (Exception ex) {
+            //ignore - probably not DOM level 3
+        }
+        return d;
+    }
+    public static void copy(Document doc, XMLStreamWriter writer) throws XMLStreamException {
+        XMLStreamReader reader = createXMLStreamReader(doc);
+        copy(reader, writer);
+    }
+    public static void copy(Element node, XMLStreamWriter writer) throws XMLStreamException {
+        XMLStreamReader reader = createXMLStreamReader(node);
+        copy(reader, writer);
+    }
+
+    public static void copy(XMLStreamReader reader, OutputStream os)
+        throws XMLStreamException {
+        XMLStreamWriter xsw = StaxUtils.createXMLStreamWriter(os);
+        StaxUtils.copy(reader, xsw);
+        xsw.close();
+    }
+
+    public static void writeTo(Node node, OutputStream os) throws XMLStreamException {
+        copy(new DOMSource(node), os);
+    }
+    public static void writeTo(Node node, OutputStream os, int indent) throws XMLStreamException {
+        if (indent > 0) {
+            XMLStreamWriter writer = new PrettyPrintXMLStreamWriter(createXMLStreamWriter(os), indent);
+            try {
+                copy(new DOMSource(node), writer);
+            } finally {
+                writer.close();
+            }
+        } else {
+            copy(new DOMSource(node), os);
+        }
+    }
+    public static void writeTo(Node node, Writer os) throws XMLStreamException {
+        writeTo(node, os, 0);
+    }
+    public static void writeTo(Node node, Writer os, int indent) throws XMLStreamException {
+        XMLStreamWriter writer = createXMLStreamWriter(os);
+        if (indent > 0) {
+            writer = new PrettyPrintXMLStreamWriter(writer, indent);
+        }
+        try {
+            copy(new DOMSource(node), writer);
+        } finally {
+            writer.close();
+        }
+    }
+
+
+    /**
+     * Copies the reader to the writer. The start and end document methods must
+     * be handled on the writer manually.
+     *
+     * @param reader
+     * @param writer
+     * @throws XMLStreamException
+     */
+    public static void copy(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException {
+        copy(reader, writer, false, false);
+    }
+    public static void copy(XMLStreamReader reader, XMLStreamWriter writer, boolean fragment)
+        throws XMLStreamException {
+        copy(reader, writer, fragment, false);
+    }
+    public static void copy(XMLStreamReader reader,
+                            XMLStreamWriter writer,
+                            boolean fragment,
+                            boolean isThreshold) throws XMLStreamException {
+        // number of elements read in
+        int read = 0;
+        int elementCount = 0;
+        final Deque<Integer> countStack = new ArrayDeque<>();
+        int event = reader.getEventType();
+
+        while (reader.hasNext()) {
+            switch (event) {
+            case XMLStreamConstants.START_ELEMENT:
+                read++;
+                if (isThreshold) {
+                    elementCount++;
+
+                    if (MAX_ELEMENT_DEPTH_VAL != -1 && read >= MAX_ELEMENT_DEPTH_VAL) {
+                        throw new DepthExceededStaxException("reach the innerElementLevelThreshold:"
+                                                   + MAX_ELEMENT_DEPTH_VAL);
+                    }
+                    if (MAX_CHILD_ELEMENTS_VAL != -1 && elementCount >= MAX_CHILD_ELEMENTS_VAL) {
+                        throw new DepthExceededStaxException("reach the innerElementCountThreshold:"
+                                                   + MAX_CHILD_ELEMENTS_VAL);
+                    }
+                    countStack.push(elementCount);
+                    elementCount = 0;
+                }
+                writeStartElement(reader, writer);
+                break;
+            case XMLStreamConstants.END_ELEMENT:
+                if (read > 0) {
+                    writer.writeEndElement();
+                }
+                read--;
+                if (read < 0 && fragment) {
+                    return;
+                }
+                if (isThreshold && !countStack.isEmpty()) {
+                    elementCount = countStack.pop();
+                }
+                break;
+            case XMLStreamConstants.CHARACTERS:
+            case XMLStreamConstants.SPACE:
+                String s = reader.getText();
+                if (s != null) {
+                    writer.writeCharacters(s);
+                }
+                break;
+            case XMLStreamConstants.COMMENT:
+                writer.writeComment(reader.getText());
+                break;
+            case XMLStreamConstants.CDATA:
+                writer.writeCData(reader.getText());
+                break;
+            case XMLStreamConstants.START_DOCUMENT:
+            case XMLStreamConstants.END_DOCUMENT:
+            case XMLStreamConstants.ATTRIBUTE:
+            case XMLStreamConstants.NAMESPACE:
+                break;
+            default:
+                break;
+            }
+            event = reader.next();
+        }
+    }
+
+    private static void writeStartElement(XMLStreamReader reader, XMLStreamWriter writer)
+        throws XMLStreamException {
+        String uri = reader.getNamespaceURI();
+        String prefix = reader.getPrefix();
+        String local = reader.getLocalName();
+
+        if (prefix == null) {
+            prefix = "";
+        }
+
+        boolean writeElementNS = false;
+
+        if (uri != null) {
+            writeElementNS = true;
+            Iterator<String> it = CastUtils.cast(writer.getNamespaceContext().getPrefixes(uri));
+            if (!it.hasNext() && StringUtils.isEmpty(prefix) && StringUtils.isEmpty(uri)
+                && StringUtils.isEmpty(writer.getNamespaceContext().getNamespaceURI(""))) {
+                writeElementNS = false;
+            }
+            while (it.hasNext()) {
+                String s = it.next();
+                if (s == null) {
+                    s = "";
+                }
+                if (s.equals(prefix)) {
+                    writeElementNS = false;
+                }
+            }
+        }
+
+        // Write out the element name
+        if (uri != null) {
+            if (prefix.isEmpty() && StringUtils.isEmpty(uri)) {
+                writer.writeStartElement(local);
+            } else {
+                writer.writeStartElement(prefix, local, uri);
+            }
+        } else {
+            writer.writeStartElement(local);
+        }
+
+        // Write out the namespaces
+        for (int i = 0; i < reader.getNamespaceCount(); i++) {
+            String nsURI = reader.getNamespaceURI(i);
+            String nsPrefix = reader.getNamespacePrefix(i);
+            if (nsPrefix == null) {
+                nsPrefix = "";
+            }
+            if (nsURI == null) {
+                nsURI = "";
+            }
+            if (nsPrefix.isEmpty()) {
+                writer.writeDefaultNamespace(nsURI);
+                writer.setDefaultNamespace(nsURI);
+            } else {
+                writer.writeNamespace(nsPrefix, nsURI);
+                writer.setPrefix(nsPrefix, nsURI);
+            }
+
+            if (nsURI.equals(uri) && nsPrefix.equals(prefix)) {
+                writeElementNS = false;
+            }
+        }
+
+        // Check if the namespace still needs to be written.
+        // We need this check because namespace writing works
+        // different on Woodstox and the RI.
+        if (writeElementNS) {
+            if (prefix.isEmpty()) {
+                writer.writeDefaultNamespace(uri);
+                writer.setDefaultNamespace(uri);
+            } else {
+                writer.writeNamespace(prefix, uri);
+                writer.setPrefix(prefix, uri);
+            }
+        }
+
+        // Write out attributes
+        for (int i = 0; i < reader.getAttributeCount(); i++) {
+            String ns = reader.getAttributeNamespace(i);
+            String nsPrefix = reader.getAttributePrefix(i);
+            if (ns == null || ns.isEmpty()) {
+                writer.writeAttribute(reader.getAttributeLocalName(i), reader.getAttributeValue(i));
+            } else if (nsPrefix == null || nsPrefix.isEmpty()) {
+                writer.writeAttribute(reader.getAttributeNamespace(i), reader.getAttributeLocalName(i),
+                                      reader.getAttributeValue(i));
+            } else {
+                Iterator<String> it = CastUtils.cast(writer.getNamespaceContext().getPrefixes(ns));
+                boolean writeNs = true;
+                while (it != null && it.hasNext()) {
+                    String s = it.next();
+                    if (s == null) {
+                        s = "";
+                    }
+                    if (s.equals(nsPrefix)) {
+                        writeNs = false;
+                    }
+                }
+                if (writeNs) {
+                    writer.writeNamespace(nsPrefix, ns);
+                    writer.setPrefix(nsPrefix, ns);
+                }
+                writer.writeAttribute(reader.getAttributePrefix(i), reader.getAttributeNamespace(i), reader
+                    .getAttributeLocalName(i), reader.getAttributeValue(i));
+            }
+
+        }
+    }
+
+    public static void writeDocument(Document d, XMLStreamWriter writer, boolean repairing)
+        throws XMLStreamException {
+        writeDocument(d, writer, true, repairing);
+    }
+
+    public static void writeDocument(Document d, XMLStreamWriter writer, boolean writeProlog,
+                                     boolean repairing) throws XMLStreamException {
+        if (writeProlog) {
+            writer.writeStartDocument();
+        }
+
+        Node node = d.getFirstChild();
+        while (node != null) {
+            if (writeProlog || node.getNodeType() == Node.ELEMENT_NODE) {
+                writeNode(node, writer, repairing);
+            }
+            node = node.getNextSibling();
+        }
+
+        if (writeProlog) {
+            writer.writeEndDocument();
+        }
+    }
+
+    /**
+     * Writes an Element to an XMLStreamWriter. The writer must already have
+     * started the document (via writeStartDocument()). Also, this probably
+     * won't work with just a fragment of a document. The Element should be the
+     * root element of the document.
+     *
+     * @param e
+     * @param writer
+     * @throws XMLStreamException
+     */
+    public static void writeElement(Element e, XMLStreamWriter writer, boolean repairing)
+        throws XMLStreamException {
+        writeElement(e, writer, repairing, true);
+    }
+
+    /**
+     * Writes an Element to an XMLStreamWriter. The writer must already have
+     * started the document (via writeStartDocument()). Also, this probably
+     * won't work with just a fragment of a document. The Element should be the
+     * root element of the document.
+     *
+     * @param e
+     * @param writer
+     * @param endElement true if the element should be ended
+     * @throws XMLStreamException
+     */
+    public static void writeElement(Element e,
+                                    XMLStreamWriter writer,
+                                    boolean repairing,
+                                    boolean endElement)
+        throws XMLStreamException {
+        String prefix = e.getPrefix();
+        String ns = e.getNamespaceURI();
+        String localName = e.getLocalName();
+
+        if (prefix == null) {
+            prefix = "";
+        }
+        if (localName == null) {
+            localName = e.getNodeName();
+
+            if (localName == null) {
+                throw new IllegalStateException("Element's local name cannot be null!");
+            }
+        }
+
+        String decUri = writer.getNamespaceContext().getNamespaceURI(prefix);
+        boolean declareNamespace = decUri == null || !decUri.equals(ns);
+
+        if (ns == null || ns.isEmpty()) {
+            writer.writeStartElement(localName);
+            if (StringUtils.isEmpty(decUri)) {
+                declareNamespace = false;
+            }
+        } else {
+            writer.writeStartElement(prefix, localName, ns);
+        }
+
+        for (Node attr : sortElementAttributes(e.getAttributes())) {
+
+            String name = attr.getLocalName();
+            String attrPrefix = attr.getPrefix();
+            if (attrPrefix == null) {
+                attrPrefix = "";
+            }
+            if (name == null) {
+                name = attr.getNodeName();
+            }
+
+            if ("xmlns".equals(attrPrefix)) {
+                writer.writeNamespace(name, attr.getNodeValue());
+                writer.setPrefix(name, attr.getNodeValue());
+                if (name.equals(prefix) && attr.getNodeValue().equals(ns)) {
+                    declareNamespace = false;
+                }
+            } else {
+                if ("xmlns".equals(name) && "".equals(attrPrefix)) {
+                    writer.writeDefaultNamespace(attr.getNodeValue());
+                    writer.setDefaultNamespace(attr.getNodeValue());
+                    if (attr.getNodeValue().equals(ns)) {
+                        declareNamespace = false;
+                    } else if (StringUtils.isEmpty(attr.getNodeValue())
+                        && StringUtils.isEmpty(ns)) {
+                        declareNamespace = false;
+                    }
+                } else {
+                    String attns = attr.getNamespaceURI();
+                    String value = attr.getNodeValue();
+                    if (attns == null || attns.isEmpty()) {
+                        writer.writeAttribute(name, value);
+                    } else if (attrPrefix.isEmpty()) {
+                        writer.writeAttribute(attns, name, value);
+                    } else {
+                        if (repairing && writer.getNamespaceContext().getNamespaceURI(attrPrefix) == null) {
+                            writer.writeNamespace(attrPrefix, attns);
+                        }
+                        writer.writeAttribute(attrPrefix, attns, name, value);
+                    }
+                }
+            }
+        }
+
+        if (declareNamespace && repairing) {
+            if (ns == null) {
+                writer.writeNamespace(prefix, "");
+                writer.setPrefix(prefix, "");
+            } else {
+                writer.writeNamespace(prefix, ns);
+                writer.setPrefix(prefix, ns);
+            }
+        }
+
+        Node nd = e.getFirstChild();
+        while (nd != null) {
+            writeNode(nd, writer, repairing);
+            nd = nd.getNextSibling();
+        }
+
+        if (endElement) {
+            writer.writeEndElement();
+        }
+    }
+
+    private static List<Node> sortElementAttributes(NamedNodeMap attrs) {
+        if (attrs.getLength() == 0) {
+            return Collections.<Node> emptyList();
+        }
+        List<Node> sortedAttrs = new ArrayList<>(attrs.getLength());
+        for (int i = 0; i < attrs.getLength(); i++) {
+            Node attr = attrs.item(i);
+            String name = attr.getLocalName();
+            if (name == null) {
+                name = attr.getNodeName();
+            }
+            if ("xmlns".equals(attr.getPrefix()) || "xmlns".equals(name)) {
+                sortedAttrs.add(0, attr);
+            } else {
+                sortedAttrs.add(attr);
+            }
+        }
+
+        return sortedAttrs;
+    }
+
+    public static void writeNode(Node n, XMLStreamWriter writer, boolean repairing)
+        throws XMLStreamException {
+
+        switch (n.getNodeType()) {
+        case Node.ELEMENT_NODE:
+            writeElement((Element)n, writer, repairing);
+            break;
+        case Node.TEXT_NODE:
+            writer.writeCharacters(((Text)n).getNodeValue());
+            break;
+        case Node.COMMENT_NODE:
+            writer.writeComment(((Comment)n).getData());
+            break;
+        case Node.CDATA_SECTION_NODE:
+            writer.writeCData(((CDATASection)n).getData());
+            break;
+        case Node.ENTITY_REFERENCE_NODE:
+            writer.writeEntityRef(((EntityReference)n).getNodeValue());
+            break;
+        case Node.PROCESSING_INSTRUCTION_NODE:
+            ProcessingInstruction pi = (ProcessingInstruction)n;
+            writer.writeProcessingInstruction(pi.getTarget(), pi.getData());
+            break;
+        case Node.DOCUMENT_NODE:
+            writeDocument((Document)n, writer, repairing);
+            break;
+        case Node.DOCUMENT_FRAGMENT_NODE: {
+            DocumentFragment frag = (DocumentFragment)n;
+            Node child = frag.getFirstChild();
+            while (child != null) {
+                writeNode(child, writer, repairing);
+                child = child.getNextSibling();
+            }
+            break;
+        }
+        case Node.DOCUMENT_TYPE_NODE:
+            try {
+                if (((DocumentType)n).getTextContent() != null) {
+                    writer.writeDTD(((DocumentType)n).getTextContent());
+                }
+            } catch (UnsupportedOperationException ex) {
+                //can we ignore?  DOM writers really don't allow this
+                //as there isn't a way to write a DTD in dom
+            }
+            break;
+        default:
+            throw new IllegalStateException("Found type: " + n.getClass().getName());
+        }
+    }
+
+    public static Document read(Source s) throws XMLStreamException {
+        XMLStreamReader reader = createXMLStreamReader(s);
+        try {
+            return read(reader);
+        } finally {
+            try {
+                reader.close();
+            } catch (Exception ex) {
+                //ignore
+            }
+        }
+    }
+    public static Document read(InputStream s) throws XMLStreamException {
+        XMLStreamReader reader = createXMLStreamReader(s);
+        try {
+            return read(reader);
+        } finally {
+            try {
+                reader.close();
+            } catch (Exception ex) {
+                //ignore
+            }
+        }
+    }
+    public static Document read(Reader s) throws XMLStreamException {
+        XMLStreamReader reader = createXMLStreamReader(s);
+        try {
+            return read(reader);
+        } finally {
+            try {
+                reader.close();
+            } catch (Exception ex) {
+                //ignore
+            }
+        }
+    }
+    public static Document read(File is) throws XMLStreamException, IOException {
+        try (InputStream fin = Files.newInputStream(is.toPath())) {
+            return read(fin);
+        }
+    }
+    public static Document read(InputSource s) throws XMLStreamException {
+        XMLStreamReader reader = null;
+        try {
+            reader = createXMLStreamReader(s);
+            return read(reader);
+        } finally {
+            StaxUtils.close(reader);
+        }
+    }
+    public static Document read(XMLStreamReader reader) throws XMLStreamException {
+        return read(reader, false);
+    }
+    public static Document read(XMLStreamReader reader, boolean recordLoc) throws XMLStreamException {
+        Document doc = DOMUtils.createDocument();
+        if (reader.getLocation().getSystemId() != null) {
+            try {
+                doc.setDocumentURI(reader.getLocation().getSystemId());
+            } catch (Exception e) {
+                //ignore - probably not DOM level 3
+            }
+        }
+        readDocElements(doc, doc, reader, true, recordLoc);
+        return doc;
+    }
+
+    public static Document read(DocumentBuilder builder, XMLStreamReader reader, boolean repairing)
+        throws XMLStreamException {
+
+        Document doc = builder == null ? DOMUtils.createDocument() : builder.newDocument();
+        if (reader.getLocation().getSystemId() != null) {
+            try {
+                doc.setDocumentURI(reader.getLocation().getSystemId());
+            } catch (Exception e) {
+                //ignore - probably not DOM level 3
+            }
+        }
+        readDocElements(doc, reader, repairing);
+        return doc;
+    }
+
+    /**
+     * @param parent
+     */
+    private static Document getDocument(Node parent) {
+        return (parent instanceof Document) ? (Document)parent : parent.getOwnerDocument();
+    }
+
+    private static boolean isDeclared(Element e, String namespaceURI, String prefix) {
+        while (e != null) {
+            Attr att;
+            if (prefix != null && !prefix.isEmpty()) {
+                att = e.getAttributeNodeNS(XML_NS, prefix);
+            } else {
+                att = e.getAttributeNode("xmlns");
+            }
+
+            if (att != null && att.getNodeValue().equals(namespaceURI)) {
+                return true;
+            }
+
+            if (e.getParentNode() instanceof Element) {
+                e = (Element)e.getParentNode();
+            } else if (StringUtils.isEmpty(prefix) && StringUtils.isEmpty(namespaceURI)) {
+                //A document that probably doesn't have any namespace qualifies elements
+                return true;
+            } else {
+                break;
+            }
+        }
+        return false;
+    }
+
+    public static void readDocElements(Node parent, XMLStreamReader reader, boolean repairing)
+        throws XMLStreamException {
+        Document doc = getDocument(parent);
+        readDocElements(doc, parent, reader, repairing, false);
+    }
+
+    public static void readDocElements(Node parent, XMLStreamReader reader, boolean repairing,
+                                       boolean isThreshold)
+        throws XMLStreamException {
+        Document doc = getDocument(parent);
+        readDocElements(doc, parent, reader, repairing, false, isThreshold);
+    }
+
+    /**
+     * @param parent
+     * @param reader
+     * @throws XMLStreamException
+     */
+    public static void readDocElements(Document doc, Node parent,
+                                       XMLStreamReader reader, boolean repairing, boolean recordLoc)
+        throws XMLStreamException {
+        readDocElements(doc, parent, reader, repairing, recordLoc, false);
+    }
+
+    /**
+     * @param parent
+     * @param reader
+     * @throws XMLStreamException
+     */
+    public static void readDocElements(Document doc, Node parent,
+                                       XMLStreamReader reader, boolean repairing, boolean recordLoc,
+                                       boolean isThreshold)
+        throws XMLStreamException {
+        final Deque<Node> stack = new ArrayDeque<>();
+        int event = reader.getEventType();
+        int elementCount = 0;
+        while (reader.hasNext()) {
+            switch (event) {
+            case XMLStreamConstants.START_ELEMENT: {
+                elementCount++;
+                Element e;
+                if (!StringUtils.isEmpty(reader.getPrefix())) {
+                    e = doc.createElementNS(reader.getNamespaceURI(),
+                                            reader.getPrefix() + ':' + reader.getLocalName());
+                } else {
+                    e = doc.createElementNS(reader.getNamespaceURI(), reader.getLocalName());
+                }
+                e = (Element)parent.appendChild(e);
+                recordLoc = addLocation(doc, e, reader, recordLoc);
+
+                for (int ns = 0; ns < reader.getNamespaceCount(); ns++) {
+                    String uri = reader.getNamespaceURI(ns);
+                    String prefix = reader.getNamespacePrefix(ns);
+
+                    declare(e, uri, prefix);
+                }
+
+                for (int att = 0; att < reader.getAttributeCount(); att++) {
+                    String name = reader.getAttributeLocalName(att);
+                    String prefix = reader.getAttributePrefix(att);
+                    if (prefix != null && !prefix.isEmpty()) {
+                        name = prefix + ':' + name;
+                    }
+
+                    Attr attr = doc.createAttributeNS(reader.getAttributeNamespace(att), name);
+                    attr.setValue(reader.getAttributeValue(att));
+                    e.setAttributeNode(attr);
+                }
+
+                if (repairing && !isDeclared(e, reader.getNamespaceURI(), reader.getPrefix())) {
+                    declare(e, reader.getNamespaceURI(), reader.getPrefix());
+                }
+                stack.push(parent);
+                if (isThreshold && MAX_ELEMENT_DEPTH_VAL != -1
+                    && stack.size() >= MAX_ELEMENT_DEPTH_VAL) {
+                    throw new DepthExceededStaxException("reach the innerElementLevelThreshold:"
+                                               + MAX_ELEMENT_DEPTH_VAL);
+                }
+                if (isThreshold && MAX_CHILD_ELEMENTS_VAL != -1
+                    && elementCount >= MAX_CHILD_ELEMENTS_VAL) {
+                    throw new DepthExceededStaxException("reach the innerElementCountThreshold:"
+                                               + MAX_CHILD_ELEMENTS_VAL);
+                }
+                parent = e;
+                break;
+            }
+            case XMLStreamConstants.END_ELEMENT:
+                if (stack.isEmpty()) {
+                    return;
+                }
+                parent = stack.pop();
+                if (parent instanceof Document || parent instanceof DocumentFragment) {
+                    return;
+                }
+                break;
+            case XMLStreamConstants.NAMESPACE:
+                break;
+            case XMLStreamConstants.ATTRIBUTE:
+                break;
+            case XMLStreamConstants.CHARACTERS:
+                if (parent != null) {
+                    recordLoc = addLocation(doc,
+                                            parent.appendChild(doc.createTextNode(reader.getText())),
+                                            reader, recordLoc);
+                }
+                break;
+            case XMLStreamConstants.COMMENT:
+                if (parent != null) {
+                    parent.appendChild(doc.createComment(reader.getText()));
+                }
+                break;
+            case XMLStreamConstants.CDATA:
+                recordLoc = addLocation(doc,
+                                        parent.appendChild(doc.createCDATASection(reader.getText())),
+                                        reader, recordLoc);
+                break;
+            case XMLStreamConstants.PROCESSING_INSTRUCTION:
+                parent.appendChild(doc.createProcessingInstruction(reader.getPITarget(), reader.getPIData()));
+                break;
+            case XMLStreamConstants.ENTITY_REFERENCE:
+                parent.appendChild(doc.createProcessingInstruction(reader.getPITarget(), reader.getPIData()));
+                break;
+            default:
+                break;
+            }
+
+            if (reader.hasNext()) {
+                event = reader.next();
+            }
+        }
+    }
+
+    public static class StreamToDOMContext {
+        private final Deque<Node> stack = new ArrayDeque<>();
+        private int elementCount;
+        private boolean repairing;
+        private boolean recordLoc;
+        private boolean threshold;
+
+        public StreamToDOMContext(boolean repairing, boolean recordLoc, boolean threshold) {
+            this.repairing = repairing;
+            this.recordLoc = recordLoc;
+            this.threshold = threshold;
+        }
+
+        public void setRecordLoc(boolean recordLoc) {
+            this.recordLoc = recordLoc;
+        }
+
+        public boolean isRecordLoc() {
+            return this.recordLoc;
+        }
+
+        public boolean isRepairing() {
+            return this.repairing;
+        }
+
+        public boolean isThreshold() {
+            return this.threshold;
+        }
+
+        public int incrementCount() {
+            return ++elementCount;
+        }
+
+        public int decreaseCount() {
+            return --elementCount;
+        }
+
+        public int getCount() {
+            return elementCount;
+        }
+
+        public void pushToStack(Node node) {
+            stack.push(node);
+        }
+
+        public Node popFromStack() {
+            return stack.pop();
+        }
+
+        public int getStackSize() {
+            return stack.size();
+        }
+
+        public boolean isStackEmpty() {
+            return stack.isEmpty();
+        }
+    }
+
+    public static void readDocElements(Document doc, Node parent, XMLStreamReader reader, StreamToDOMContext context)
+        throws XMLStreamException {
+        int event = reader.getEventType();
+        while (reader.hasNext()) {
+            switch (event) {
+            case XMLStreamConstants.START_ELEMENT: {
+                context.incrementCount();
+                Element e;
+                if (!StringUtils.isEmpty(reader.getPrefix())) {
+                    e = doc.createElementNS(reader.getNamespaceURI(),
+                                            reader.getPrefix() + ":" + reader.getLocalName());
+                } else {
+                    e = doc.createElementNS(reader.getNamespaceURI(), reader.getLocalName());
+                }
+                e = (Element)parent.appendChild(e);
+                if (context.isRecordLoc()) {
+                    context.setRecordLoc(addLocation(doc, e, reader.getLocation(), context.isRecordLoc()));
+                }
+
+                for (int ns = 0; ns < reader.getNamespaceCount(); ns++) {
+                    String uri = reader.getNamespaceURI(ns);
+                    String prefix = reader.getNamespacePrefix(ns);
+
+                    declare(e, uri, prefix);
+                }
+
+                for (int att = 0; att < reader.getAttributeCount(); att++) {
+                    String name = reader.getAttributeLocalName(att);
+                    String prefix = reader.getAttributePrefix(att);
+                    if (prefix != null && prefix.length() > 0) {
+                        name = prefix + ":" + name;
+                    }
+
+                    Attr attr = doc.createAttributeNS(reader.getAttributeNamespace(att), name);
+                    attr.setValue(reader.getAttributeValue(att));
+                    e.setAttributeNode(attr);
+                }
+
+                if (context.isRepairing() && !isDeclared(e, reader.getNamespaceURI(), reader.getPrefix())) {
+                    declare(e, reader.getNamespaceURI(), reader.getPrefix());
+                }
+                context.pushToStack(parent);
+                if (context.isThreshold() && MAX_ELEMENT_DEPTH_VAL != -1
+                    && context.getStackSize() >= MAX_ELEMENT_DEPTH_VAL) {
+                    throw new DepthExceededStaxException("reach the innerElementLevelThreshold:"
+                                               + MAX_ELEMENT_DEPTH_VAL);
+                }
+                if (context.isThreshold() && MAX_CHILD_ELEMENTS_VAL != -1
+                    && context.getCount() >= MAX_CHILD_ELEMENTS_VAL) {
+                    throw new DepthExceededStaxException("reach the innerElementCountThreshold:"
+                                               + MAX_CHILD_ELEMENTS_VAL);
+                }
+                parent = e;
+                break;
+            }
+            case XMLStreamConstants.END_ELEMENT:
+                if (context.isStackEmpty()) {
+                    return;
+                }
+                parent = context.popFromStack();
+                if (parent instanceof Document || parent instanceof DocumentFragment) {
+                    return;
+                }
+                break;
+            case XMLStreamConstants.NAMESPACE:
+                break;
+            case XMLStreamConstants.ATTRIBUTE:
+                break;
+            case XMLStreamConstants.CHARACTERS:
+                if (parent != null) {
+                    context.setRecordLoc(addLocation(doc,
+                                                     parent.appendChild(doc.createTextNode(reader.getText())),
+                                                     reader.getLocation(), context.isRecordLoc()));
+                }
+                break;
+            case XMLStreamConstants.COMMENT:
+                if (parent != null) {
+                    parent.appendChild(doc.createComment(reader.getText()));
+                }
+                break;
+            case XMLStreamConstants.CDATA:
+                context.setRecordLoc(addLocation(doc,
+                                        parent.appendChild(doc.createCDATASection(reader.getText())),
+                                        reader.getLocation(), context.isRecordLoc()));
+                break;
+            case XMLStreamConstants.PROCESSING_INSTRUCTION:
+                parent.appendChild(doc.createProcessingInstruction(reader.getPITarget(), reader.getPIData()));
+                break;
+            case XMLStreamConstants.ENTITY_REFERENCE:
+                parent.appendChild(doc.createProcessingInstruction(reader.getPITarget(), reader.getPIData()));
+                break;
+            default:
+                break;
+            }
+
+            if (reader.hasNext()) {
+                event = reader.next();
+            }
+        }
+    }
+
+    public static Node readDocElement(Document doc, Node parent, XMLEvent ev, StreamToDOMContext context)
+        throws XMLStreamException {
+        switch (ev.getEventType()) {
+        case XMLStreamConstants.START_ELEMENT: {
+            context.incrementCount();
+            Element e;
+            StartElement startElem = ev.asStartElement();
+            QName name = startElem.getName();
+            if (!StringUtils.isEmpty(name.getPrefix())) {
+                e = doc.createElementNS(name.getNamespaceURI(),
+                                        name.getPrefix() + ":" + name.getLocalPart());
+            } else {
+                e = doc.createElementNS(name.getNamespaceURI(), name.getLocalPart());
+            }
+            e = (Element)parent.appendChild(e);
+            if (context.isRecordLoc()) {
+                context.setRecordLoc(addLocation(doc, e, startElem.getLocation(), context.isRecordLoc()));
+            }
+
+            if (context.isRepairing() && !isDeclared(e, name.getNamespaceURI(), name.getPrefix())) {
+                declare(e, name.getNamespaceURI(), name.getPrefix());
+            }
+            context.pushToStack(parent);
+            if (context.isThreshold() && MAX_ELEMENT_DEPTH_VAL != -1
+                && context.getStackSize() >= MAX_ELEMENT_DEPTH_VAL) {
+                throw new DepthExceededStaxException("reach the innerElementLevelThreshold:"
+                                           + MAX_ELEMENT_DEPTH_VAL);
+            }
+            if (context.isThreshold() && MAX_CHILD_ELEMENTS_VAL != -1
+                && context.getCount() >= MAX_CHILD_ELEMENTS_VAL) {
+                throw new DepthExceededStaxException("reach the innerElementCountThreshold:"
+                                           + MAX_CHILD_ELEMENTS_VAL);
+            }
+            parent = e;
+            break;
+        }
+        case XMLStreamConstants.END_ELEMENT:
+            if (context.isStackEmpty()) {
+                return parent;
+            }
+            parent = context.popFromStack();
+            if (parent instanceof Document || parent instanceof DocumentFragment) {
+                return parent;
+            }
+            break;
+        case XMLStreamConstants.NAMESPACE:
+            Namespace ns = (Namespace)ev;
+            declare((Element)parent, ns.getNamespaceURI(), ns.getPrefix());
+            break;
+        case XMLStreamConstants.ATTRIBUTE:
+            Attribute at = (Attribute)ev;
+            QName qname = at.getName();
+            String attName = qname.getLocalPart();
+            String attPrefix = qname.getPrefix();
+            if (attPrefix != null && attPrefix.length() > 0) {
+                attName = attPrefix + ":" + attName;
+            }
+            Attr attr = doc.createAttributeNS(qname.getNamespaceURI(), attName);
+            attr.setValue(at.getValue());
+            ((Element)parent).setAttributeNode(attr);
+            break;
+        case XMLStreamConstants.CHARACTERS:
+            Characters characters = ev.asCharacters();
+            context.setRecordLoc(addLocation(doc,
+                                             parent.appendChild(doc.createTextNode(characters.getData())),
+                                             characters.getLocation(), context.isRecordLoc()));
+            break;
+        case XMLStreamConstants.COMMENT:
+            parent.appendChild(doc.createComment(((javax.xml.stream.events.Comment)ev).getText()));
+            break;
+        case XMLStreamConstants.CDATA:
+            Characters cdata = ev.asCharacters();
+            context.setRecordLoc(addLocation(doc,
+                                             parent.appendChild(doc.createCDATASection(cdata.getData())),
+                                             cdata.getLocation(), context.isRecordLoc()));
+            break;
+        case XMLStreamConstants.PROCESSING_INSTRUCTION:
+            parent.appendChild(doc.createProcessingInstruction(((ProcessingInstruction)ev).getTarget(),
+                                                               ((ProcessingInstruction)ev).getData()));
+            break;
+        case XMLStreamConstants.ENTITY_REFERENCE:
+            javax.xml.stream.events.EntityReference er = (javax.xml.stream.events.EntityReference)ev;
+            parent.appendChild(doc.createEntityReference(er.getName()));
+            break;
+        default:
+            break;
+        }
+        return parent;
+    }
+
+    private static boolean addLocation(Document doc, Node node,
+                                       Location loc,
+                                       boolean recordLoc) {
+        if (recordLoc && loc != null && (loc.getColumnNumber() != 0 || loc.getLineNumber() != 0)) {
+            try {
+                final int charOffset = loc.getCharacterOffset();
+                final int colNum = loc.getColumnNumber();
+                final int linNum = loc.getLineNumber();
+                final String pubId = loc.getPublicId() == null ? doc.getDocumentURI() : loc.getPublicId();
+                final String sysId = loc.getSystemId() == null ? doc.getDocumentURI() : loc.getSystemId();
+                Location loc2 = new Location() {
+                    public int getCharacterOffset() {
+                        return charOffset;
+                    }
+                    public int getColumnNumber() {
+                        return colNum;
+                    }
+                    public int getLineNumber() {
+                        return linNum;
+                    }
+                    public String getPublicId() {
+                        return pubId;
+                    }
+                    public String getSystemId() {
+                        return sysId;
+                    }
+                };
+                node.setUserData("location", loc2, LocationUserDataHandler.INSTANCE);
+            } catch (Throwable ex) {
+                //possibly not DOM level 3, won't be able to record this then
+                return false;
+            }
+        }
+        return recordLoc;
+    }
+
+    private static boolean addLocation(Document doc, Node node,
+                                    XMLStreamReader reader,
+                                    boolean recordLoc) {
+        return addLocation(doc, node, reader.getLocation(), recordLoc);
+    }
+
+    private static class LocationUserDataHandler implements UserDataHandler {
+        public static final LocationUserDataHandler INSTANCE = new LocationUserDataHandler();
+
+        public void handle(short operation, String key, Object data, Node src, Node dst) {
+            if (operation == NODE_CLONED) {
+                dst.setUserData(key, data, this);
+            }
+        }
+    }
+
+    private static void declare(Element node, String uri, String prefix) {
+        String qualname;
+        if (prefix != null && prefix.length() > 0) {
+            qualname = "xmlns:" + prefix;
+        } else {
+            qualname = "xmlns";
+        }
+        Attr attr = node.getOwnerDocument().createAttributeNS(XML_NS, qualname);
+        attr.setValue(uri);
+        node.setAttributeNodeNS(attr);
+    }
+    public static XMLStreamReader createXMLStreamReader(InputSource src) {
+        String sysId = src.getSystemId() == null ? null : src.getSystemId();
+        String pubId = src.getPublicId() == null ? null : src.getPublicId();
+        if (src.getByteStream() != null) {
+            final InputStream is = src.getByteStream();
+
+            if (src.getEncoding() == null) {
+                final StreamSource ss = new StreamSource(is, sysId);
+                ss.setPublicId(pubId);
+                
+                final XMLStreamReader xmlStreamReader = createXMLStreamReader(ss);
+                if (AUTO_CLOSE_INPUT_SOURCE) {
+                    return new AutoCloseableXMLStreamReader(xmlStreamReader, is);
+                } else {
+                    return xmlStreamReader;
+                }
+            }
+            
+            return new AutoCloseableXMLStreamReader(createXMLStreamReader(is, src.getEncoding()), is);
+        } else if (src.getCharacterStream() != null) {
+            final Reader reader = src.getCharacterStream();
+            final StreamSource ss = new StreamSource(reader, sysId);
+            ss.setPublicId(pubId);
+            final XMLStreamReader xmlStreamReader = createXMLStreamReader(ss);
+            if (AUTO_CLOSE_INPUT_SOURCE) {
+                return new AutoCloseableXMLStreamReader(xmlStreamReader, reader);
+            } else {
+                return xmlStreamReader;
+            }
+        } else {
+            try {
+                final URL url = new URL(sysId);
+                final InputStream is = url.openStream();
+                final StreamSource ss = new StreamSource(is, sysId);
+                ss.setPublicId(pubId);
+                return new AutoCloseableXMLStreamReader(createXMLStreamReader(ss), is);
+            } catch (Exception ex) {
+                //ignore - not a valid URL
+            }
+        }
+        throw new IllegalArgumentException("InputSource must have a ByteStream or CharacterStream");
+    }
+    /**
+     * @param in
+     * @param encoding
+     */
+    public static XMLStreamReader createXMLStreamReader(InputStream in, String encoding) {
+        XMLInputFactory factory = getXMLInputFactory();
+        try {
+            return factory.createXMLStreamReader(in, encoding != null ? encoding : StandardCharsets.UTF_8.name());
+        } catch (XMLStreamException e) {
+            throw new RuntimeException("Couldn't parse stream.", e);
+        } finally {
+            returnXMLInputFactory(factory);
+        }
+    }
+
+    /**
+     * @param in
+     */
+    public static XMLStreamReader createXMLStreamReader(InputStream in) {
+        XMLInputFactory factory = getXMLInputFactory();
+        try {
+            return factory.createXMLStreamReader(in);
+        } catch (XMLStreamException e) {
+            throw new RuntimeException("Couldn't parse stream.", e);
+        } finally {
+            returnXMLInputFactory(factory);
+        }
+    }
+    public static XMLStreamReader createXMLStreamReader(String systemId, InputStream in) {
+        XMLInputFactory factory = getXMLInputFactory();
+        try {
+            return factory.createXMLStreamReader(systemId, in);
+        } catch (XMLStreamException e) {
+            throw new RuntimeException("Couldn't parse stream.", e);
+        } finally {
+            returnXMLInputFactory(factory);
+        }
+    }
+
+    public static XMLStreamReader createXMLStreamReader(Element el) {
+        return new W3CDOMStreamReader(el);
+    }
+    public static XMLStreamReader createXMLStreamReader(Document doc) {
+        return new W3CDOMStreamReader(doc.getDocumentElement());
+    }
+    public static XMLStreamReader createXMLStreamReader(Element el, String sysId) {
+        return new W3CDOMStreamReader(el, sysId);
+    }
+    public static XMLStreamReader createXMLStreamReader(Document doc, String sysId) {
+        return new W3CDOMStreamReader(doc.getDocumentElement(), sysId);
+    }
+
+    public static XMLStreamReader createXMLStreamReader(Source source) {
+        try {
+            if (source instanceof DOMSource) {
+                DOMSource ds = (DOMSource)source;
+                Node nd = ds.getNode();
+                Element el = null;
+                if (nd instanceof Document) {
+                    el = ((Document)nd).getDocumentElement();
+                } else if (nd instanceof Element) {
+                    el = (Element)nd;
+                }
+
+                if (null != el) {
+                    return new W3CDOMStreamReader(el, source.getSystemId());
+                }
+            } else if (source instanceof StAXSource) {
+                return ((StAXSource)source).getXMLStreamReader();
+            } else if (source instanceof StaxSource) {
+                return ((StaxSource)source).getXMLStreamReader();
+            } else if (source instanceof SAXSource) {
+                SAXSource ss = (SAXSource)source;
+                if (ss.getXMLReader() == null) {
+                    return createXMLStreamReader(((SAXSource)source).getInputSource());
+                }
+            }
+
+            XMLInputFactory factory = getXMLInputFactory();
+            try {
+                XMLStreamReader reader = null;
+
+                try {
+                    reader = factory.createXMLStreamReader(source);
+                } catch (UnsupportedOperationException e) {
+                    //ignore
+                }
+                if (reader == null && source instanceof StreamSource) {
+                    //createXMLStreamReader from Source is optional, we'll try and map it
+                    StreamSource ss = (StreamSource)source;
+                    if (ss.getInputStream() != null) {
+                        reader = factory.createXMLStreamReader(ss.getSystemId(),
+                                                               ss.getInputStream());
+                    } else {
+                        reader = factory.createXMLStreamReader(ss.getSystemId(),
+                                                               ss.getReader());
+                    }
+                }
+                return reader;
+            } finally {
+                returnXMLInputFactory(factory);
+            }
+        } catch (XMLStreamException e) {
+            throw new RuntimeException("Couldn't parse stream.", e);
+        }
+    }
+
+    /**
+     * @param reader
+     */
+    public static XMLStreamReader createXMLStreamReader(Reader reader) {
+        XMLInputFactory factory = getXMLInputFactory();
+        try {
+            return factory.createXMLStreamReader(reader);
+        } catch (XMLStreamException e) {
+            throw new RuntimeException("Couldn't parse stream.", e);
+        } finally {
+            returnXMLInputFactory(factory);
+        }
+    }
+
+    /**
+     * Reads a QName from the element text. Reader must be positioned at the
+     * start tag.
+     */
+    public static QName readQName(XMLStreamReader reader) throws XMLStreamException {
+        String value = reader.getElementText();
+        if (value == null) {
+            return null;
+        }
+        value = value.trim();
+
+        int index = value.indexOf(':');
+
+        if (index == -1) {
+            return new QName(value);
+        }
+
+        String prefix = value.substring(0, index);
+        String localName = value.substring(index + 1);
+        String ns = reader.getNamespaceURI(prefix);
+
+        if (!StringUtils.isEmpty(prefix) && ns == null) {
+            throw new RuntimeException("Invalid QName in mapping: " + value);
+        }
+
+        if (ns == null) {
+            return new QName(localName);
+        }
+
+        return new QName(ns, localName, prefix);
+    }
+
+    /**
+     * Create a unique namespace uri/prefix combination.
+     *
+     * @return The namespace with the specified URI. If one doesn't exist, one
+     *         is created.
+     * @throws XMLStreamException
+     */
+    public static String getUniquePrefix(XMLStreamWriter writer, String namespaceURI, boolean declare)
+        throws XMLStreamException {
+        String prefix = writer.getPrefix(namespaceURI);
+        if (prefix == null) {
+            prefix = getUniquePrefix(writer);
+
+            if (declare) {
+                writer.setPrefix(prefix, namespaceURI);
+                writer.writeNamespace(prefix, namespaceURI);
+            }
+        }
+        return prefix;
+    }
+    public static String getUniquePrefix(XMLStreamWriter writer, String namespaceURI)
+        throws XMLStreamException {
+        return getUniquePrefix(writer, namespaceURI, false);
+    }
+    public static String getUniquePrefix(XMLStreamWriter writer) {
+        NamespaceContext nc = writer.getNamespaceContext();
+        if (nc == null) {
+            return DEF_PREFIXES[0];
+        }
+        for (String t : DEF_PREFIXES) {
+            String uri = nc.getNamespaceURI(t);
+            if (StringUtils.isEmpty(uri)) {
+                return t;
+            }
+        }
+
+        int n = 10;
+        while (true) {
+            String nsPrefix = "ns" + n;
+            String uri = nc.getNamespaceURI(nsPrefix);
+            if (StringUtils.isEmpty(uri)) {
+                return nsPrefix;
+            }
+            n++;
+        }
+    }
+
+
+    public static void printXmlFragment(XMLStreamReader reader) {
+        try {
+            StringWriter sw = new StringWriter(1024);
+            XMLStreamWriter writer = null;
+            try {
+                writer = new PrettyPrintXMLStreamWriter(createXMLStreamWriter(sw), 4);
+                copy(reader, writer);
+                writer.flush();
+            } finally {
+                StaxUtils.close(writer);
+            }
+            LOG.info(sw.toString());
+        } catch (XMLStreamException e) {
+            LOG.severe(e.getMessage());
+        }
+    }
+
+
+    private static void writeStartElementEvent(XMLEvent event, XMLStreamWriter writer)
+        throws XMLStreamException {
+        StartElement start = event.asStartElement();
+        QName name = start.getName();
+        String nsURI = name.getNamespaceURI();
+        String localName = name.getLocalPart();
+        String prefix = name.getPrefix();
+
+        if (prefix != null) {
+            writer.writeStartElement(prefix, localName, nsURI);
+        } else if (nsURI != null) {
+            writer.writeStartElement(localName, nsURI);
+        } else {
+            writer.writeStartElement(localName);
+        }
+        Iterator<XMLEvent> it = CastUtils.cast(start.getNamespaces());
+        while (it != null && it.hasNext()) {
+            writeEvent(it.next(), writer);
+        }
+
+        it = CastUtils.cast(start.getAttributes());
+        while (it != null && it.hasNext()) {
+            writeAttributeEvent(it.next(), writer);
+        }
+    }
+    private static void writeAttributeEvent(XMLEvent event, XMLStreamWriter writer)
+        throws XMLStreamException {
+
+        Attribute attr = (Attribute)event;
+        QName name = attr.getName();
+        String nsURI = name.getNamespaceURI();
+        String localName = name.getLocalPart();
+        String prefix = name.getPrefix();
+        String value = attr.getValue();
+
+        if (prefix != null) {
+            writer.writeAttribute(prefix, nsURI, localName, value);
+        } else if (nsURI != null) {
+            writer.writeAttribute(nsURI, localName, value);
+        } else {
+            writer.writeAttribute(localName, value);
+        }
+    }
+
+    public static void writeEvent(XMLEvent event, XMLStreamWriter writer)
+        throws XMLStreamException {
+
+        switch (event.getEventType()) {
+        case XMLStreamConstants.START_ELEMENT:
+            writeStartElementEvent(event, writer);
+            break;
+        case XMLStreamConstants.END_ELEMENT:
+            writer.writeEndElement();
+            break;
+        case XMLStreamConstants.ATTRIBUTE:
+            writeAttributeEvent(event, writer);
+            break;
+        case XMLStreamConstants.ENTITY_REFERENCE:
+            writer.writeEntityRef(((javax.xml.stream.events.EntityReference)event).getName());
+            break;
+        case XMLStreamConstants.DTD:
+            writer.writeDTD(((DTD)event).getDocumentTypeDeclaration());
+            break;
+        case XMLStreamConstants.PROCESSING_INSTRUCTION:
+            if (((javax.xml.stream.events.ProcessingInstruction)event).getData() != null) {
+                writer.writeProcessingInstruction(
+                    ((javax.xml.stream.events.ProcessingInstruction)event).getTarget(),
+                    ((javax.xml.stream.events.ProcessingInstruction)event).getData());
+            } else {
+                writer.writeProcessingInstruction(
+                    ((javax.xml.stream.events.ProcessingInstruction)event).getTarget());
+            }
+            break;
+        case XMLStreamConstants.NAMESPACE:
+            if (((Namespace)event).isDefaultNamespaceDeclaration()) {
+                writer.writeDefaultNamespace(((Namespace)event).getNamespaceURI());
+                writer.setDefaultNamespace(((Namespace)event).getNamespaceURI());
+            } else {
+                writer.writeNamespace(((Namespace)event).getPrefix(),
+                                      ((Namespace)event).getNamespaceURI());
+                writer.setPrefix(((Namespace)event).getPrefix(),
+                                 ((Namespace)event).getNamespaceURI());
+            }
+            break;
+        case XMLStreamConstants.COMMENT:
+            writer.writeComment(((javax.xml.stream.events.Comment)event).getText());
+            break;
+        case XMLStreamConstants.CHARACTERS:
+        case XMLStreamConstants.SPACE:
+            writer.writeCharacters(event.asCharacters().getData());
+            break;
+        case XMLStreamConstants.CDATA:
+            writer.writeCData(event.asCharacters().getData());
+            break;
+        case XMLStreamConstants.START_DOCUMENT:
+            if (((StartDocument)event).encodingSet()) {
+                writer.writeStartDocument(((StartDocument)event).getCharacterEncodingScheme(),
+                                          ((StartDocument)event).getVersion());
+
+            } else {
+                writer.writeStartDocument(((StartDocument)event).getVersion());
+            }
+            break;
+        case XMLStreamConstants.END_DOCUMENT:
+            writer.writeEndDocument();
+            break;
+        default:
+            //shouldn't get here
+        }
+    }
+    public static void print(Node node) {
+        XMLStreamWriter writer = null;
+        try {
+            writer = createXMLStreamWriter(System.out);
+            copy(new DOMSource(node), writer);
+            writer.flush();
+        } catch (XMLStreamException e) {
+            throw new RuntimeException(e);
+        } finally {
+            StaxUtils.close(writer);
+        }
+    }
+
+    public static String toString(Source src) {
+        StringWriter sw = new StringWriter(1024);
+        XMLStreamWriter writer = null;
+        try {
+            writer = createXMLStreamWriter(sw);
+            copy(src, writer);
+            writer.flush();
+        } catch (XMLStreamException e) {
+            throw new RuntimeException(e);
+        } finally {
+            StaxUtils.close(writer);
+        }
+        return sw.toString();
+    }
+    public static String toString(Node src) {
+        return toString(new DOMSource(src));
+    }
+    public static String toString(Document doc) {
+        StringWriter sw = new StringWriter(1024);
+        XMLStreamWriter writer = null;
+        try {
+            writer = createXMLStreamWriter(sw);
+            copy(doc, writer);
+            writer.flush();
+        } catch (XMLStreamException e) {
+            throw new RuntimeException(e);
+        } finally {
+            StaxUtils.close(writer);
+        }
+        return sw.toString();
+    }
+    public static String toString(Element el) {
+        return toString(el, 0);
+    }
+    public static String toString(Element el, int indent) {
+        StringWriter sw = new StringWriter(1024);
+        XMLStreamWriter writer = null;
+        try {
+            writer = createXMLStreamWriter(sw);
+            if (indent > 0) {
+                writer = new PrettyPrintXMLStreamWriter(writer, indent);
+            }
+            copy(el, writer);
+            writer.flush();
+        } catch (XMLStreamException e) {
+            throw new RuntimeException(e);
+        } finally {
+            StaxUtils.close(writer);
+        }
+        return sw.toString();
+    }
+    public static void close(XMLStreamReader reader) throws XMLStreamException {
+        if (reader != null) {
+            reader.close();
+        }
+    }
+
+    public static void close(XMLStreamWriter writer) {
+        if (writer != null) {
+            try {
+                writer.close();
+            } catch (Exception e) {
+                //ignore
+            }
+        }
+    }
+
+    public static boolean isSecureReader(XMLStreamReader reader, Message message) {
+        if (reader instanceof DocumentDepthProperties) {
+            return true;
+        }
+        try {
+            if (reader.getProperty(P_MAX_CHILDREN_PER_ELEMENT) != null) {
+                return true;
+            }
+        } catch (Exception ex) {
+            //ignore
+        }
+        return false;
+    }
+
+    public static XMLStreamReader configureReader(XMLStreamReader xreader, Message message) throws XMLStreamException {
+        Integer messageMaxChildElements = PropertyUtils.getInteger(message, MAX_CHILD_ELEMENTS);
+        Integer messageMaxElementDepth = PropertyUtils.getInteger(message, MAX_ELEMENT_DEPTH);
+        Integer messageMaxAttributeCount = PropertyUtils.getInteger(message, MAX_ATTRIBUTE_COUNT);
+        Integer messageMaxAttributeSize = PropertyUtils.getInteger(message, MAX_ATTRIBUTE_SIZE);
+        Integer messageMaxTextLength = PropertyUtils.getInteger(message, MAX_TEXT_LENGTH);
+        Long messageMaxElementCount = PropertyUtils.getLong(message, MAX_ELEMENT_COUNT);
+        Long messageMaxXMLCharacters = PropertyUtils.getLong(message, MAX_XML_CHARACTERS);
+        return configureReader(xreader, messageMaxChildElements, messageMaxElementDepth,
+                               messageMaxAttributeCount, messageMaxAttributeSize, messageMaxTextLength,
+                               messageMaxElementCount, messageMaxXMLCharacters);
+    }
+
+    //CHECKSTYLE:OFF - lots of params to configure
+    public static XMLStreamReader configureReader(XMLStreamReader reader, Integer maxChildElements,
+                                       Integer maxElementDepth, Integer maxAttributeCount,
+                                       Integer maxAttributeSize, Integer maxTextLength,
+                                       Long maxElementCount, Long maxXMLCharacters)
+        throws XMLStreamException {
+        //CHECKSTYLE:ON
+
+        // We currently ONLY support Woodstox 4.2.x for most of this other than a few things
+        // that we can handle via a wrapper.
+        try {
+            DocumentDepthProperties p = null;
+            if (maxChildElements != null) {
+                try {
+                    setProperty(reader, P_MAX_CHILDREN_PER_ELEMENT, maxChildElements);
+                } catch (Throwable t) {
+                    //we can handle this via a wrapper
+                    p = new DocumentDepthProperties();
+                    p.setInnerElementCountThreshold(maxChildElements);
+                }
+            }
+            if (maxElementDepth != null) {
+                try {
+                    setProperty(reader, P_MAX_ELEMENT_DEPTH, maxElementDepth);
+                } catch (Throwable t) {
+                    //we can handle this via a wrapper
+                    if (p == null) {
+                        p = new DocumentDepthProperties();
+                    }
+                    p.setInnerElementLevelThreshold(maxElementDepth);
+                }
+            }
+            if (maxAttributeCount != null) {
+                setProperty(reader, P_MAX_ATTRIBUTES_PER_ELEMENT, maxAttributeCount);
+            }
+            if (maxAttributeSize != null) {
+                setProperty(reader, P_MAX_ATTRIBUTE_SIZE, maxAttributeSize);
+            }
+            if (maxTextLength != null) {
+                setProperty(reader, P_MAX_TEXT_LENGTH, maxTextLength);
+            }
+            if (maxElementCount != null) {
+                try {
+                    setProperty(reader, P_MAX_ELEMENT_COUNT, maxElementCount);
+                } catch (Throwable t) {
+                    //we can handle this via a wrapper
+                    if (p == null) {
+                        p = new DocumentDepthProperties();
+                    }
+                    p.setElementCountThreshold(maxElementCount.intValue());
+                }
+            }
+            if (maxXMLCharacters != null) {
+                setProperty(reader, P_MAX_CHARACTERS, maxXMLCharacters);
+            }
+            if (p != null) {
+                reader = new DepthRestrictingStreamReader(reader, p);
+            }
+        } catch (ClassCastException cce) {
+            //not an XMLStreamReader2
+            if (ALLOW_INSECURE_PARSER_VAL) {
+                LOG.warning("INSTANCE_NOT_XMLSTREAMREADER2");
+            } else {
+                throw new XMLStreamException(cce.getMessage(), cce);
+            }
+        } catch (IllegalArgumentException cce) {
+            //not a property supported by this version of woodstox
+            if (ALLOW_INSECURE_PARSER_VAL) {
+                LOG.log(Level.WARNING, "SECURE_PROPERTY_NOT_SUPPORTED", cce.getMessage());
+            } else {
+                throw new XMLStreamException(cce.getMessage(), cce);
+            }
+        }
+        return reader;
+    }
+    private static void setProperty(XMLStreamReader reader, String p, Object v) {
+        WoodstoxHelper.setProperty(reader, p, v);
+    }
+
+    
+}
diff --git a/transform/src/patch/java/org/apache/cxf/staxutils/W3CDOMStreamReader.java b/transform/src/patch/java/org/apache/cxf/staxutils/W3CDOMStreamReader.java
new file mode 100644
index 0000000..03b1992
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/staxutils/W3CDOMStreamReader.java
@@ -0,0 +1,429 @@
+/**
+ * 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.staxutils;
+
+import java.util.ArrayList;
+
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+import javax.xml.stream.Location;
+import javax.xml.stream.XMLStreamException;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+import org.w3c.dom.TypeInfo;
+
+import org.apache.cxf.helpers.DOMUtils;
+
+public class W3CDOMStreamReader extends AbstractDOMStreamReader<Node, Node> {
+    private Node content;
+
+    private Document document;
+
+    private W3CNamespaceContext context;
+
+    private String sysId;
+
+    /**
+     * @param element
+     */
+    public W3CDOMStreamReader(Element element) {
+        super(new ElementFrame<Node, Node>(element, null));
+        content = element;
+        newFrame(getCurrentFrame());
+
+        this.document = element.getOwnerDocument();
+    }
+    public W3CDOMStreamReader(Element element, String systemId) {
+        this(element);
+        sysId = systemId;
+    }
+    public W3CDOMStreamReader(Document doc) {
+        super(new ElementFrame<Node, Node>(doc, false) {
+            public boolean isDocument() {
+                return true;
+            }
+        });
+        this.document = doc;
+    }
+    public W3CDOMStreamReader(DocumentFragment docfrag) {
+        super(new ElementFrame<Node, Node>(docfrag, true) {
+            public boolean isDocumentFragment() {
+                return true;
+            }
+        });
+        this.document = docfrag.getOwnerDocument();
+    }
+
+    /**
+     * Get the document associated with this stream.
+     */
+    public Document getDocument() {
+        return document;
+    }
+    public String getSystemId() {
+        try {
+            return sysId == null ? document.getDocumentURI() : sysId;
+        } catch (Throwable ex) {
+            //ignore, probably not DOM level 3
+        }
+        return sysId;
+    }
+    /**
+     * Find name spaces declaration in atrributes and move them to separate
+     * collection.
+     */
+    @Override
+    protected final void newFrame(ElementFrame<Node, Node> frame) {
+        Node element = getCurrentNode();
+        frame.uris = new ArrayList<>();
+        frame.prefixes = new ArrayList<>();
+        frame.attributes = new ArrayList<>();
+
+        if (context == null) {
+            context = new W3CNamespaceContext();
+        }
+        if (element instanceof Element) {
+            context.setElement((Element)element);
+        }
+
+        NamedNodeMap nodes = element.getAttributes();
+
+        if (nodes != null) {
+            for (int i = 0; i < nodes.getLength(); i++) {
+                Node node = nodes.item(i);
+                String prefix = node.getPrefix();
+                String localName = node.getLocalName();
+                String value = node.getNodeValue();
+                String name = node.getNodeName();
+
+                if (prefix == null) {
+                    prefix = "";
+                }
+
+                if ("xmlns".equals(name)) {
+                    frame.uris.add(value);
+                    frame.prefixes.add("");
+                } else if (!prefix.isEmpty() && "xmlns".equals(prefix)) {
+                    frame.uris.add(value);
+                    frame.prefixes.add(localName);
+                } else if (name.startsWith("xmlns:")) {
+                    frame.uris.add(value);
+                    frame.prefixes.add(name.substring(6));
+                } else {
+                    frame.attributes.add(node);
+                }
+            }
+        }
+    }
+
+    public final Node getCurrentNode() {
+        return getCurrentFrame().element;
+    }
+    public final Element getCurrentElement() {
+        return (Element)getCurrentFrame().element;
+    }
+
+    @Override
+    protected ElementFrame<Node, Node> getChildFrame() {
+        return new ElementFrame<Node, Node>(getCurrentFrame().currentChild,
+                                getCurrentFrame());
+    }
+
+    @Override
+    protected boolean hasMoreChildren() {
+        if (getCurrentFrame().currentChild == null) {
+            return getCurrentNode().getFirstChild() != null;
+        }
+        return getCurrentFrame().currentChild.getNextSibling() != null;
+    }
+
+    @Override
+    protected int nextChild() {
+        ElementFrame<Node, Node> frame = getCurrentFrame();
+        if (frame.currentChild == null) {
+            content = getCurrentNode().getFirstChild();
+        } else {
+            content = frame.currentChild.getNextSibling();
+        }
+
+        frame.currentChild = content;
+        switch (content.getNodeType()) {
+        case Node.ELEMENT_NODE:
+            return START_ELEMENT;
+        case Node.TEXT_NODE:
+            return CHARACTERS;
+        case Node.COMMENT_NODE:
+            return COMMENT;
+        case Node.CDATA_SECTION_NODE:
+            return CDATA;
+        case Node.ENTITY_REFERENCE_NODE:
+            return ENTITY_REFERENCE;
+        case Node.PROCESSING_INSTRUCTION_NODE:
+            return PROCESSING_INSTRUCTION;
+        default:
+            throw new IllegalStateException("Found type: " + content.getClass().getName());
+        }
+    }
+
+    @Override
+    public String getElementText() throws XMLStreamException {
+        String result = DOMUtils.getRawContent(content);
+
+        ElementFrame<Node, Node> frame = getCurrentFrame();
+        frame.ended = true;
+        currentEvent = END_ELEMENT;
+        endElement();
+
+        // we should not return null according to the StAx API javadoc
+        return result != null ? result : "";
+    }
+
+    @Override
+    public String getNamespaceURI(String prefix) {
+        ElementFrame<Node, Node> frame = getCurrentFrame();
+
+        while (null != frame) {
+            int index = frame.prefixes.indexOf(prefix);
+            if (index != -1) {
+                return frame.uris.get(index);
+            }
+
+            if (frame.parent == null && frame.getElement() instanceof Element) {
+                return ((Element)frame.getElement()).lookupNamespaceURI(prefix);
+            }
+            frame = frame.parent;
+        }
+
+        return null;
+    }
+
+    public String getAttributeValue(String ns, String local) {
+        Attr at;
+        if (ns == null || ns.isEmpty()) {
+            at = getCurrentElement().getAttributeNode(local);
+        } else {
+            at = getCurrentElement().getAttributeNodeNS(ns, local);
+        }
+
+        if (at == null) {
+            return null;
+        }
+        return at.getNodeValue();
+    }
+
+    public int getAttributeCount() {
+        return getCurrentFrame().attributes.size();
+    }
+
+    Attr getAttribute(int i) {
+        return (Attr)getCurrentFrame().attributes.get(i);
+    }
+
+    private String getLocalName(Attr attr) {
+
+        String name = attr.getLocalName();
+        if (name == null) {
+            name = attr.getNodeName();
+        }
+        return name;
+    }
+
+    public QName getAttributeName(int i) {
+        Attr at = getAttribute(i);
+
+        String prefix = at.getPrefix();
+        String ln = getLocalName(at);
+        // at.getNodeName();
+        String ns = at.getNamespaceURI();
+
+        if (prefix == null) {
+            return new QName(ns, ln);
+        }
+        return new QName(ns, ln, prefix);
+    }
+
+    public String getAttributeNamespace(int i) {
+        return getAttribute(i).getNamespaceURI();
+    }
+
+    public String getAttributeLocalName(int i) {
+        Attr attr = getAttribute(i);
+        return getLocalName(attr);
+    }
+
+    public String getAttributePrefix(int i) {
+        return getAttribute(i).getPrefix();
+    }
+
+    public String getAttributeType(int i) {
+        Attr attr = getAttribute(i);
+        if (attr.isId()) {
+            return "ID";
+        }
+        TypeInfo schemaType = null;
+        try {
+            schemaType = attr.getSchemaTypeInfo();
+        } catch (Throwable t) {
+            //DOM level 2?
+        }
+        return (schemaType == null) ? "CDATA"
+            : schemaType.getTypeName() == null ? "CDATA" : schemaType.getTypeName();
+    }
+
+
+    public String getAttributeValue(int i) {
+        return getAttribute(i).getValue();
+    }
+
+    public boolean isAttributeSpecified(int i) {
+        return getAttribute(i).getValue() != null;
+    }
+
+    public int getNamespaceCount() {
+        return getCurrentFrame().prefixes.size();
+    }
+
+    public String getNamespacePrefix(int i) {
+        return getCurrentFrame().prefixes.get(i);
+    }
+
+    public String getNamespaceURI(int i) {
+        return getCurrentFrame().uris.get(i);
+    }
+
+    public NamespaceContext getNamespaceContext() {
+        return context;
+    }
+
+    public String getText() {
+        if (content instanceof Text) {
+            return ((Text)content).getData();
+        } else if (content instanceof Comment) {
+            return ((Comment)content).getData();
+        }
+        return DOMUtils.getRawContent(getCurrentNode());
+    }
+
+    public char[] getTextCharacters() {
+        return getText().toCharArray();
+    }
+
+    public int getTextStart() {
+        return 0;
+    }
+
+    public int getTextLength() {
+        return getText().length();
+    }
+
+    public String getEncoding() {
+        return null;
+    }
+
+    public QName getName() {
+        Node el = getCurrentNode();
+
+        String prefix = getPrefix();
+        String ln = getLocalName();
+
+        return new QName(el.getNamespaceURI(), ln, prefix);
+    }
+
+    public String getLocalName() {
+        String ln = getCurrentNode().getLocalName();
+        if (ln == null) {
+            ln = getCurrentNode().getNodeName();
+            if (ln.indexOf(':') != -1) {
+                ln = ln.substring(ln.indexOf(':') + 1);
+            }
+        }
+        return ln;
+    }
+
+    public String getNamespaceURI() {
+        String ln = getCurrentNode().getLocalName();
+        if (ln == null) {
+            ln = getCurrentNode().getNodeName();
+            if (ln.indexOf(':') == -1) {
+                ln = getNamespaceURI("");
+            } else {
+                ln = getNamespaceURI(ln.substring(0, ln.indexOf(':')));
+            }
+            return ln;
+        }
+        return getCurrentNode().getNamespaceURI();
+    }
+
+    public String getPrefix() {
+        String prefix = getCurrentNode().getPrefix();
+        if (prefix == null) {
+            String nodeName = getCurrentNode().getNodeName();
+            if (nodeName.indexOf(':') != -1) {
+                prefix = nodeName.substring(0, nodeName.indexOf(':'));
+            }  else {
+                prefix = "";
+            }
+        }
+        return prefix;
+    }
+
+    public String getPITarget() {
+        return ((ProcessingInstruction)content).getTarget();
+    }
+
+    public String getPIData() {
+        return ((ProcessingInstruction)content).getData();
+    }
+    public Location getLocation() {
+        try {
+            Object o = getCurrentNode().getUserData("location");
+            if (o instanceof Location) {
+                return (Location)o;
+            }
+        } catch (Throwable ex) {
+            //ignore, probably not DOM level 3
+        }
+        return super.getLocation();
+    }
+
+    public String toString() {
+        if (document == null) {
+            return "<null>";
+        }
+        if (document.getDocumentElement() == null) {
+            return "<null document element>";
+        }
+        try {
+            return StaxUtils.toString(document);
+        } catch (Throwable t) {
+            t.printStackTrace();
+            return super.toString();
+        }
+    }
+
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/transport/ChainInitiationObserver.java b/transform/src/patch/java/org/apache/cxf/transport/ChainInitiationObserver.java
new file mode 100644
index 0000000..a4b8aa3
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/transport/ChainInitiationObserver.java
@@ -0,0 +1,197 @@
+/**
+ * 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;
+
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collection;
+
+import javax.xml.namespace.QName;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.binding.Binding;
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.classloader.ClassLoaderUtils.ClassLoaderHolder;
+import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.interceptor.Interceptor;
+import org.apache.cxf.interceptor.InterceptorChain;
+import org.apache.cxf.interceptor.InterceptorProvider;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.ExchangeImpl;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.PhaseChainCache;
+import org.apache.cxf.phase.PhaseManager;
+import org.apache.cxf.service.Service;
+import org.apache.cxf.service.model.EndpointInfo;
+
+public class ChainInitiationObserver implements MessageObserver {
+    protected Endpoint endpoint;
+    protected Bus bus;
+    protected ClassLoader loader;
+
+    private PhaseChainCache chainCache = new PhaseChainCache();
+
+    public ChainInitiationObserver(Endpoint endpoint, Bus bus) {
+        super();
+        this.endpoint = endpoint;
+        this.bus = bus;
+        if (bus != null) {
+            loader = bus.getExtension(ClassLoader.class);
+        }
+    }
+
+    public void onMessage(Message m) {
+        Bus origBus = BusFactory.getAndSetThreadDefaultBus(bus);
+        ClassLoaderHolder origLoader = null;
+        try {
+            if (loader != null) {
+                origLoader = ClassLoaderUtils.setThreadContextClassloader(loader);
+            }
+            InterceptorChain phaseChain;
+
+            if (m.getInterceptorChain() != null) {
+                phaseChain = m.getInterceptorChain();
+                // To make sure the phase chain is run by one thread once
+                synchronized (phaseChain) {
+                    if (phaseChain.getState() == InterceptorChain.State.PAUSED
+                        || phaseChain.getState() == InterceptorChain.State.SUSPENDED) {
+                        phaseChain.resume();
+                        return;
+                    }
+                }
+            }
+
+            Message message = getBinding().createMessage(m);
+            Exchange exchange = message.getExchange();
+            if (exchange == null) {
+                exchange = new ExchangeImpl();
+                m.setExchange(exchange);
+            }
+            exchange.setInMessage(message);
+            setExchangeProperties(exchange, message);
+
+            InterceptorProvider dbp = null;
+            if (endpoint.getService().getDataBinding() instanceof InterceptorProvider) {
+                dbp = (InterceptorProvider)endpoint.getService().getDataBinding();
+            }
+            // setup chain
+            if (dbp == null) {
+                phaseChain = chainCache.get(bus.getExtension(PhaseManager.class).getInPhases(),
+                                                             bus.getInInterceptors(),
+                                                             endpoint.getService().getInInterceptors(),
+                                                             endpoint.getInInterceptors(),
+                                                             getBinding().getInInterceptors());
+            } else {
+                phaseChain = chainCache.get(bus.getExtension(PhaseManager.class).getInPhases(),
+                                            bus.getInInterceptors(),
+                                            endpoint.getService().getInInterceptors(),
+                                            endpoint.getInInterceptors(),
+                                            getBinding().getInInterceptors(),
+                                            dbp.getInInterceptors());
+            }
+
+
+
+            message.setInterceptorChain(phaseChain);
+
+            phaseChain.setFaultObserver(endpoint.getOutFaultObserver());
+
+            addToChain(phaseChain, message);
+
+            phaseChain.doIntercept(message);
+
+        } finally {
+            if (origBus != bus) {
+                BusFactory.setThreadDefaultBus(origBus);
+            }
+            if (origLoader != null) {
+                origLoader.reset();
+            }
+        }
+    }
+    private void addToChain(InterceptorChain chain, Message m) {
+        Collection<InterceptorProvider> providers
+            = CastUtils.cast((Collection<?>)m.get(Message.INTERCEPTOR_PROVIDERS));
+        if (providers != null) {
+            for (InterceptorProvider p : providers) {
+                chain.add(p.getInInterceptors());
+            }
+        }
+        Collection<Interceptor<? extends Message>> is
+            = CastUtils.cast((Collection<?>)m.get(Message.IN_INTERCEPTORS));
+        if (is != null) {
+            chain.add(is);
+        }
+        if (m.getDestination() instanceof InterceptorProvider) {
+            chain.add(((InterceptorProvider)m.getDestination()).getInInterceptors());
+        }
+    }
+
+    protected Binding getBinding() {
+        return endpoint.getBinding();
+    }
+
+    protected void setExchangeProperties(Exchange exchange, Message m) {
+        exchange.put(Endpoint.class, endpoint);
+        exchange.put(Binding.class, getBinding());
+        exchange.put(Bus.class, bus);
+        if (exchange.getDestination() == null) {
+            exchange.setDestination(m.getDestination());
+        }
+        if (endpoint != null && endpoint.getService() != null) {
+            exchange.put(Service.class, endpoint.getService());
+
+            EndpointInfo endpointInfo = endpoint.getEndpointInfo();
+
+            if (endpointInfo.getService() != null)  {
+                QName serviceQName = endpointInfo.getService().getName();
+                exchange.put(Message.WSDL_SERVICE, serviceQName);
+
+                QName interfaceQName = endpointInfo.getService().getInterface().getName();
+                exchange.put(Message.WSDL_INTERFACE, interfaceQName);
+
+
+                QName portQName = endpointInfo.getName();
+                exchange.put(Message.WSDL_PORT, portQName);
+                URI wsdlDescription = endpointInfo.getProperty("URI", URI.class);
+                if (wsdlDescription == null && !endpointInfo.hasProperty("URI")) {
+                    String address = endpointInfo.getAddress();
+                    try {
+                        wsdlDescription = new URI(address + "?wsdl");
+                    } catch (URISyntaxException e) {
+                        // do nothing
+                    }
+                    endpointInfo.setProperty("URI", wsdlDescription);
+                }
+                exchange.put(Message.WSDL_DESCRIPTION, wsdlDescription);
+            }
+        } else {
+            exchange.put(Service.class, null);
+        }
+    }
+
+    public Endpoint getEndpoint() {
+        return endpoint;
+    }
+
+}
diff --git a/transform/src/patch/java/org/apache/cxf/validation/AbstractBeanValidationInterceptor.java b/transform/src/patch/java/org/apache/cxf/validation/AbstractBeanValidationInterceptor.java
new file mode 100644
index 0000000..3fecf1a
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/validation/AbstractBeanValidationInterceptor.java
@@ -0,0 +1,65 @@
+/**
+ * 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.validation;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import javax.validation.ValidationException;
+
+import org.apache.cxf.message.Message;
+
+public abstract class AbstractBeanValidationInterceptor extends AbstractValidationInterceptor {
+    protected AbstractBeanValidationInterceptor(String phase) {
+        super(phase);
+    }
+
+    @Override
+    protected Object getServiceObject(Message message) {
+        return checkNotNull(super.getServiceObject(message), "SERVICE_OBJECT_NULL");
+    }
+
+    @Override
+    protected Method getServiceMethod(Message message) {
+        return (Method)checkNotNull(super.getServiceMethod(message), "SERVICE_METHOD_NULL");
+    }
+
+    private Object checkNotNull(Object object, String name) {
+        if (object == null) {
+            String message = new org.apache.cxf.common.i18n.Message(name, BUNDLE).toString();
+            LOG.severe(message);
+            throw new ValidationException(message);
+        }
+        return object;
+    }
+
+    @Override
+    protected void handleValidation(final Message message, final Object resourceInstance,
+                                    final Method method, final List<Object> arguments) {
+        if (!arguments.isEmpty()) {
+            BeanValidationProvider provider = getProvider(message);
+            provider.validateParameters(resourceInstance, method, unwrapArgs(arguments).toArray());
+            message.getExchange().put(BeanValidationProvider.class, provider);
+        }
+    }
+
+    protected List<Object> unwrapArgs(List<Object> arguments) {
+        return arguments;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/workqueue/AutomaticWorkQueueImpl.java b/transform/src/patch/java/org/apache/cxf/workqueue/AutomaticWorkQueueImpl.java
new file mode 100644
index 0000000..bff113e
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/workqueue/AutomaticWorkQueueImpl.java
@@ -0,0 +1,619 @@
+/**
+ * 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.workqueue;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.concurrent.DelayQueue;
+import java.util.concurrent.Delayed;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.classloader.ClassLoaderUtils.ClassLoaderHolder;
+import org.apache.cxf.common.injection.NoJSR250Annotations;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.ReflectionUtil;
+
+@NoJSR250Annotations
+public class AutomaticWorkQueueImpl implements AutomaticWorkQueue {
+    public static final String PROPERTY_NAME = "name";
+    static final int DEFAULT_MAX_QUEUE_SIZE = 256;
+    private static final Logger LOG =
+        LogUtils.getL7dLogger(AutomaticWorkQueueImpl.class);
+
+
+    String name = "default";
+    int maxQueueSize;
+    int initialThreads;
+    int lowWaterMark;
+    int highWaterMark;
+    long dequeueTimeout;
+    AtomicInteger approxThreadCount = new AtomicInteger();
+
+    ThreadPoolExecutor executor;
+    Method addWorkerMethod;
+    Object[] addWorkerArgs;
+
+    AWQThreadFactory threadFactory;
+    ReentrantLock mainLock;
+    final ReentrantLock addThreadLock = new ReentrantLock();
+
+    DelayQueue<DelayedTaskWrapper> delayQueue;
+    WatchDog watchDog;
+
+    boolean shared;
+    int sharedCount;
+
+    private List<PropertyChangeListener> changeListenerList;
+
+    public AutomaticWorkQueueImpl() {
+        this(DEFAULT_MAX_QUEUE_SIZE);
+    }
+    public AutomaticWorkQueueImpl(String name) {
+        this(DEFAULT_MAX_QUEUE_SIZE, name);
+    }
+    public AutomaticWorkQueueImpl(int max) {
+        this(max, "default");
+    }
+    public AutomaticWorkQueueImpl(int max, String name) {
+        this(max,
+             0,
+             25,
+             5,
+             2 * 60 * 1000L,
+             name);
+    }
+    public AutomaticWorkQueueImpl(int mqs,
+                                  int initialThreads,
+                                  int highWaterMark,
+                                  int lowWaterMark,
+                                  long dequeueTimeout) {
+        this(mqs, initialThreads, highWaterMark, lowWaterMark, dequeueTimeout, "default");
+    }
+    public AutomaticWorkQueueImpl(int mqs,
+                                  int initialThreads,
+                                  int highWaterMark,
+                                  int lowWaterMark,
+                                  long dequeueTimeout,
+                                  String name) {
+        this.maxQueueSize = mqs == -1 ? DEFAULT_MAX_QUEUE_SIZE : mqs;
+        this.initialThreads = initialThreads;
+        this.highWaterMark = -1 == highWaterMark ? Integer.MAX_VALUE : highWaterMark;
+        this.lowWaterMark = -1 == lowWaterMark ? Integer.MAX_VALUE : lowWaterMark;
+        this.dequeueTimeout = dequeueTimeout;
+        this.name = name;
+        this.changeListenerList = new ArrayList<>();
+    }
+
+    public void addChangeListener(PropertyChangeListener listener) {
+        this.changeListenerList.add(listener);
+    }
+
+    public void removeChangeListener(PropertyChangeListener listener) {
+        this.changeListenerList.remove(listener);
+    }
+
+    public void notifyChangeListeners(PropertyChangeEvent event) {
+        for (PropertyChangeListener listener : changeListenerList) {
+            listener.propertyChange(event);
+        }
+    }
+
+    public void setShared(boolean shared) {
+        this.shared = shared;
+    }
+    public boolean isShared() {
+        return shared;
+    }
+    public void addSharedUser() {
+        sharedCount++;
+    }
+    public void removeSharedUser() {
+        sharedCount--;
+    }
+    public int getShareCount() {
+        return sharedCount;
+    }
+
+    protected synchronized ThreadPoolExecutor getExecutor() {
+        if (executor == null) {
+            threadFactory = createThreadFactory(name);
+            executor = new ThreadPoolExecutor(lowWaterMark,
+                                              highWaterMark,
+                                              TimeUnit.MILLISECONDS.toMillis(dequeueTimeout),
+                                              TimeUnit.MILLISECONDS,
+                                              new LinkedBlockingQueue<Runnable>(maxQueueSize),
+                                              threadFactory) {
+                @Override
+                protected void terminated() {
+                    ThreadFactory f = executor.getThreadFactory();
+                    if (f instanceof AWQThreadFactory) {
+                        ((AWQThreadFactory)f).shutdown();
+                    }
+                    if (watchDog != null) {
+                        watchDog.shutdown();
+                    }
+                }
+            };
+
+
+            if (LOG.isLoggable(Level.FINE)) {
+                StringBuilder buf = new StringBuilder(128).append("Constructing automatic work queue with:\n")
+                        .append("max queue size: ").append(maxQueueSize).append('\n')
+                        .append("initialThreads: ").append(initialThreads).append('\n')
+                        .append("lowWaterMark: ").append(lowWaterMark).append('\n')
+                        .append("highWaterMark: ").append(highWaterMark).append('\n');
+                LOG.fine(buf.toString());
+            }
+
+            if (initialThreads > highWaterMark) {
+                initialThreads = highWaterMark;
+            }
+
+            // as we cannot prestart more core than corePoolSize initial threads, we temporarily
+            // change the corePoolSize to the number of initial threads
+            // this is important as otherwise these threads will be created only when
+            // the queue has filled up,
+            // potentially causing problems with starting up under heavy load
+            if (initialThreads < Integer.MAX_VALUE && initialThreads > 0) {
+                executor.setCorePoolSize(initialThreads);
+                int started = executor.prestartAllCoreThreads();
+                if (started < initialThreads) {
+                    LOG.log(Level.WARNING, "THREAD_START_FAILURE_MSG",
+                            new Object[] {started, initialThreads});
+                }
+                executor.setCorePoolSize(lowWaterMark);
+            }
+
+            ReentrantLock l;
+            try {
+                Field f = ThreadPoolExecutor.class.getDeclaredField("mainLock");
+                ReflectionUtil.setAccessible(f);
+                l = (ReentrantLock)f.get(executor);
+            } catch (Throwable t) {
+                l = new ReentrantLock();
+            }
+            mainLock = l;
+
+
+            try {
+                //java 7
+                addWorkerMethod = ThreadPoolExecutor.class.getDeclaredMethod("addWorker",
+                                                                             Runnable.class, Boolean.TYPE);
+                addWorkerArgs = new Object[] {null, Boolean.FALSE};
+            } catch (Throwable t2) {
+                //nothing we cando
+            }
+        }
+        return executor;
+    }
+    private AWQThreadFactory createThreadFactory(final String nm) {
+        ThreadGroup group;
+        try {
+            //Try and find the highest level ThreadGroup that we're allowed to use.
+            //That SHOULD allow the default classloader and thread locals and such
+            //to be the least likely to cause issues down the road.
+            group = AccessController.doPrivileged(
+                new PrivilegedAction<ThreadGroup>() {
+                    public ThreadGroup run() {
+                        ThreadGroup group = Thread.currentThread().getThreadGroup();
+                        ThreadGroup parent = group;
+                        try {
+                            while (parent != null) {
+                                group = parent;
+                                parent = parent.getParent();
+                            }
+                        } catch (SecurityException se) {
+                            //ignore - if we get here, the "group" is as high as
+                            //the security manager will allow us to go.   Use that one.
+                        }
+                        return new ThreadGroup(group, nm + "-workqueue");
+                    }
+                }
+            );
+        } catch (SecurityException e) {
+            group = new ThreadGroup(nm + "-workqueue");
+        }
+        return new AWQThreadFactory(group, nm);
+    }
+
+    static class DelayedTaskWrapper implements Delayed, Runnable {
+        long trigger;
+        Runnable work;
+
+        DelayedTaskWrapper(Runnable work, long delay) {
+            this.work = work;
+            trigger = System.currentTimeMillis() + delay;
+        }
+
+        public long getDelay(TimeUnit unit) {
+            long n = trigger - System.currentTimeMillis();
+            return unit.convert(n, TimeUnit.MILLISECONDS);
+        }
+
+        public int compareTo(Delayed delayed) {
+            long other = ((DelayedTaskWrapper)delayed).trigger;
+            int returnValue;
+            if (this.trigger < other) {
+                returnValue = -1;
+            } else if (this.trigger > other) {
+                returnValue = 1;
+            } else {
+                returnValue = 0;
+            }
+            return returnValue;
+        }
+
+        public void run() {
+            work.run();
+        }
+
+    }
+
+    class WatchDog extends Thread {
+        DelayQueue<DelayedTaskWrapper> delayQueue;
+        AtomicBoolean shutdown = new AtomicBoolean(false);
+
+        WatchDog(DelayQueue<DelayedTaskWrapper> queue) {
+            delayQueue = queue;
+        }
+
+        public void shutdown() {
+            shutdown.set(true);
+            // to exit the waiting thread
+            interrupt();
+        }
+
+        public void run() {
+            DelayedTaskWrapper task;
+            try {
+                while (!shutdown.get()) {
+                    task = delayQueue.take();
+                    if (task != null) {
+                        try {
+                            execute(task);
+                        } catch (Exception ex) {
+                            LOG.warning("Executing the task from DelayQueue with exception: " + ex);
+                        }
+                    }
+                }
+            } catch (InterruptedException e) {
+                if (LOG.isLoggable(Level.FINE)) {
+                    LOG.finer("The DelayQueue watchdog Task is stopping");
+                }
+            }
+
+        }
+
+    }
+    class AWQThreadFactory implements ThreadFactory {
+        final AtomicInteger threadNumber = new AtomicInteger(1);
+        ThreadGroup group;
+        String name;
+        ClassLoader loader;
+
+        AWQThreadFactory(ThreadGroup gp, String nm) {
+            group = gp;
+            name = nm;
+            //force the loader to be the loader of CXF, not the application loader
+            loader = AutomaticWorkQueueImpl.class.getClassLoader();
+        }
+
+        public Thread newThread(final Runnable r) {
+            if (group.isDestroyed()) {
+                group = new ThreadGroup(group.getParent(), name + "-workqueue");
+            }
+            Runnable wrapped = new Runnable() {
+                public void run() {
+                    approxThreadCount.incrementAndGet();
+                    try {
+                        r.run();
+                    } finally {
+                        approxThreadCount.decrementAndGet();
+                    }
+                }
+            };
+            final Thread t = new Thread(group,
+                                  wrapped,
+                                  name + "-workqueue-" + threadNumber.getAndIncrement(),
+                                  0);
+            AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
+                public Boolean run() {
+                    t.setContextClassLoader(loader);
+                    return true;
+                }
+            });
+            t.setDaemon(true);
+            if (t.getPriority() != Thread.NORM_PRIORITY) {
+                t.setPriority(Thread.NORM_PRIORITY);
+            }
+            return t;
+        }
+        public void setName(String s) {
+            name = s;
+        }
+        public void shutdown() {
+            if (!group.isDestroyed()) {
+                try {
+                    group.destroy();
+                    group.setDaemon(true);
+                } catch (Throwable t) {
+                    //ignore
+                }
+            }
+        }
+    }
+
+    public void setName(String s) {
+        name = s;
+        if (threadFactory != null) {
+            threadFactory.setName(s);
+        }
+    }
+    public String getName() {
+        return name;
+    }
+
+    public String toString() {
+        return new StringBuilder(super.toString())
+                .append(" [queue size: ").append(getSize())
+                .append(", max size: ").append(maxQueueSize)
+                .append(", threads: ").append(getPoolSize())
+                .append(", active threads: ").append(getActiveCount())
+                .append(", low water mark: ").append(getLowWaterMark())
+                .append(", high water mark: ").append(getHighWaterMark())
+                .append(']').toString();
+    }
+
+    public void execute(final Runnable command) {
+        //Grab the context classloader of this thread.   We'll make sure we use that
+        //on the thread the runnable actually runs on.
+
+        final ClassLoader loader = Thread.currentThread().getContextClassLoader();
+        Runnable r = new Runnable() {
+            public void run() {
+                ClassLoaderHolder orig = ClassLoaderUtils.setThreadContextClassloader(loader);
+                try {
+                    command.run();
+                } finally {
+                    if (orig != null) {
+                        orig.reset();
+                    }
+                }
+            }
+        };
+        //The ThreadPoolExecutor in the JDK doesn't expand the number
+        //of threads until the queue is full.   However, we would
+        //prefer the number of threads to expand immediately and
+        //only uses the queue if we've reached the maximum number
+        //of threads.
+        ThreadPoolExecutor ex = getExecutor();
+        ex.execute(r);
+        if (addWorkerMethod != null
+            && !ex.getQueue().isEmpty()
+            && this.approxThreadCount.get() < highWaterMark
+            && addThreadLock.tryLock()) {
+            try {
+                mainLock.lock();
+                try {
+                    int ps = this.getPoolSize();
+                    int sz = executor.getQueue().size();
+                    int sz2 = this.getActiveCount();
+
+                    if ((sz + sz2) > ps) {
+                        // Needs --add-opens java.base/java.util.concurrent=ALL-UNNAMED for JDK16+
+                        ReflectionUtil.setAccessible(addWorkerMethod).invoke(executor, addWorkerArgs);
+                    }
+                } catch (Exception exc) {
+                    //ignore
+                } finally {
+                    mainLock.unlock();
+                }
+            } finally {
+                addThreadLock.unlock();
+            }
+        }
+    }
+
+    // WorkQueue interface
+    public void execute(Runnable work, long timeout) {
+        try {
+            execute(work);
+        } catch (RejectedExecutionException ree) {
+            try {
+                if (!getExecutor().getQueue().offer(work, timeout, TimeUnit.MILLISECONDS)) {
+                    throw ree;
+                }
+            } catch (InterruptedException ie) {
+                throw ree;
+            }
+        }
+    }
+
+    public synchronized void schedule(final Runnable work, final long delay) {
+        if (delayQueue == null) {
+            delayQueue = new DelayQueue<>();
+            watchDog = new WatchDog(delayQueue);
+            watchDog.setDaemon(true);
+            watchDog.start();
+        }
+        delayQueue.put(new DelayedTaskWrapper(work, delay));
+    }
+
+    // AutomaticWorkQueue interface
+
+    public void shutdown(boolean processRemainingWorkItems) {
+        if (executor != null) {
+            if (!processRemainingWorkItems) {
+                executor.getQueue().clear();
+            }
+            executor.shutdown();
+        }
+    }
+
+
+    /**
+     * Gets the maximum size (capacity) of the backing queue.
+     * @return the maximum size (capacity) of the backing queue.
+     */
+    public long getMaxSize() {
+        return maxQueueSize;
+    }
+
+    /**
+     * Gets the current size of the backing queue.
+     * @return the current size of the backing queue.
+     */
+    public long getSize() {
+        return executor == null ? 0 : executor.getQueue().size();
+    }
+
+
+    public boolean isEmpty() {
+        return executor == null || executor.getQueue().isEmpty();
+    }
+
+    public boolean isFull() {
+        return executor != null && executor.getQueue().remainingCapacity() == 0;
+    }
+
+    public int getHighWaterMark() {
+        int hwm = executor == null ? highWaterMark : executor.getMaximumPoolSize();
+        return hwm == Integer.MAX_VALUE ? -1 : hwm;
+    }
+
+    public int getLowWaterMark() {
+        int lwm = executor == null ? lowWaterMark : executor.getCorePoolSize();
+        return lwm == Integer.MAX_VALUE ? -1 : lwm;
+    }
+
+    public int getInitialSize() {
+        return this.initialThreads;
+    }
+
+    public void setHighWaterMark(int hwm) {
+        highWaterMark = hwm < 0 ? Integer.MAX_VALUE : hwm;
+        if (executor != null) {
+            notifyChangeListeners(new PropertyChangeEvent(this, "highWaterMark",
+                                                          this.executor.getMaximumPoolSize(), hwm));
+            executor.setMaximumPoolSize(highWaterMark);
+        }
+    }
+
+    public void setLowWaterMark(int lwm) {
+        lowWaterMark = lwm < 0 ? 0 : lwm;
+        if (executor != null) {
+            notifyChangeListeners(new PropertyChangeEvent(this, "lowWaterMark",
+                                                          this.executor.getCorePoolSize(), lwm));
+            executor.setCorePoolSize(lowWaterMark);
+        }
+    }
+
+    public void setInitialSize(int initialSize) {
+        notifyChangeListeners(new PropertyChangeEvent(this, "initialSize", this.initialThreads, initialSize));
+        this.initialThreads = initialSize;
+    }
+
+    public void setQueueSize(int size) {
+        notifyChangeListeners(new PropertyChangeEvent(this, "queueSize", this.maxQueueSize, size));
+        this.maxQueueSize = size;
+    }
+
+    public void setDequeueTimeout(long l) {
+        notifyChangeListeners(new PropertyChangeEvent(this, "dequeueTimeout", this.dequeueTimeout, l));
+        this.dequeueTimeout = l;
+    }
+
+    public boolean isShutdown() {
+        if (executor == null) {
+            return false;
+        }
+        return executor.isShutdown();
+    }
+    public int getLargestPoolSize() {
+        if (executor == null) {
+            return 0;
+        }
+        return executor.getLargestPoolSize();
+    }
+    public int getPoolSize() {
+        if (executor == null) {
+            return 0;
+        }
+        return executor.getPoolSize();
+    }
+    public int getActiveCount() {
+        if (executor == null) {
+            return 0;
+        }
+        return executor.getActiveCount();
+    }
+    public void update(Dictionary<String, String> config) {
+        String s = config.get("highWaterMark");
+        if (s != null) {
+            this.highWaterMark = Integer.parseInt(s);
+        }
+        s = config.get("lowWaterMark");
+        if (s != null) {
+            this.lowWaterMark = Integer.parseInt(s);
+        }
+        s = config.get("initialSize");
+        if (s != null) {
+            this.initialThreads = Integer.parseInt(s);
+        }
+        s = config.get("dequeueTimeout");
+        if (s != null) {
+            this.dequeueTimeout = Long.parseLong(s);
+        }
+        s = config.get("queueSize");
+        if (s != null) {
+            this.maxQueueSize = Integer.parseInt(s);
+        }
+    }
+    public Dictionary<String, String> getProperties() {
+        Dictionary<String, String> properties = new Hashtable<>();
+        NumberFormat nf = NumberFormat.getIntegerInstance();
+        properties.put("name", nf.format(getName()));
+        properties.put("highWaterMark", nf.format(getHighWaterMark()));
+        properties.put("lowWaterMark", nf.format(getLowWaterMark()));
+        properties.put("initialSize", nf.format(getLowWaterMark()));
+        properties.put("dequeueTimeout", nf.format(getLowWaterMark()));
+        properties.put("queueSize", nf.format(getLowWaterMark()));
+        return properties;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/cxf/ws/addressing/MAPAggregator.java b/transform/src/patch/java/org/apache/cxf/ws/addressing/MAPAggregator.java
new file mode 100644
index 0000000..206494d
--- /dev/null
+++ b/transform/src/patch/java/org/apache/cxf/ws/addressing/MAPAggregator.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.ws.addressing;
+
+import java.util.Collection;
+
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.AbstractPhaseInterceptor;
+import org.apache.cxf.phase.Phase;
+import org.apache.cxf.phase.PhaseInterceptor;
+
+/**
+ * Logical Handler responsible for aggregating the Message Addressing
+ * Properties for outgoing messages.
+ */
+public class MAPAggregator extends AbstractPhaseInterceptor<Message> {
+    public static final String USING_ADDRESSING = MAPAggregator.class.getName() + ".usingAddressing";
+    public static final String ADDRESSING_DISABLED = MAPAggregator.class.getName() + ".addressingDisabled";
+    public static final String DECOUPLED_DESTINATION = MAPAggregator.class.getName()
+        + ".decoupledDestination";
+    public static final String ACTION_VERIFIED = MAPAggregator.class.getName() + ".actionVerified";
+    public static final String ADDRESSING_NAMESPACE = MAPAggregator.class.getName() + ".addressingNamespace";
+
+    public interface MAPAggregatorLoader {
+        MAPAggregator createImplementation(MAPAggregator mag);
+    }
+
+    protected MessageIdCache messageIdCache;
+    protected boolean usingAddressingAdvisory = true;
+    protected boolean addressingRequired;
+    protected boolean allowDuplicates = true;
+    protected WSAddressingFeature.AddressingResponses addressingResponses
+        = WSAddressingFeature.AddressingResponses.ALL;
+
+    /**
+     * The real implementation of the MAPAggregator interceptor
+     */
+    private MAPAggregator impl;
+
+    /**
+     * Constructor.
+     */
+    public MAPAggregator() {
+        super(MAPAggregator.class.getName(), Phase.PRE_LOGICAL);
+        addBefore("org.apache.cxf.interceptor.OneWayProcessorInterceptor");
+    }
+
+    /**
+     * Indicates if duplicate messageIDs are allowed.
+     * @return true if duplicate messageIDs are allowed
+     */
+    public boolean allowDuplicates() {
+        if (impl != null) {
+            return impl.allowDuplicates();
+        }
+        return allowDuplicates;
+    }
+
+    /**
+     * Allows/disallows duplicate messageIdDs.
+     * @param ad whether duplicate messageIDs are allowed
+     */
+    public void setAllowDuplicates(boolean ad) {
+        if (impl != null) {
+            impl.setAllowDuplicates(ad);
+        }
+        allowDuplicates = ad;
+    }
+
+    /**
+     * Whether the presence of the <wsaw:UsingAddressing> element
+     * in the WSDL is purely advisory, i.e. its absence doesn't prevent
+     * the encoding of WS-A headers.
+     *
+     * @return true if the presence of the <wsaw:UsingAddressing> element is
+     * advisory
+     */
+    public boolean isUsingAddressingAdvisory() {
+        if (impl != null) {
+            return impl.isUsingAddressingAdvisory();
+        }
+        return usingAddressingAdvisory;
+    }
+
+    /**
+     * Controls whether the presence of the <wsaw:UsingAddressing> element
+     * in the WSDL is purely advisory, i.e. its absence doesn't prevent
+     * the encoding of WS-A headers.
+     *
+     * @param advisory true if the presence of the <wsaw:UsingAddressing>
+     * element is to be advisory
+     */
+    public void setUsingAddressingAdvisory(boolean advisory) {
+        if (impl != null) {
+            impl.setUsingAddressingAdvisory(advisory);
+        }
+        usingAddressingAdvisory = advisory;
+    }
+
+    /**
+     * Whether the use of addressing is completely required for this endpoint
+     *
+     * @return true if addressing is required
+     */
+    public boolean isAddressingRequired() {
+        if (impl != null) {
+            return impl.addressingRequired;
+        }
+        return addressingRequired;
+    }
+    /**
+     * Sets whether the use of addressing is completely required for this endpoint
+     *
+     */
+    public void setAddressingRequired(boolean required) {
+        if (impl != null) {
+            impl.setAddressingRequired(required);
+        }
+        addressingRequired = required;
+    }
+
+    /**
+     * Sets Addresing Response
+     *
+     */
+    public void setAddressingResponses(WSAddressingFeature.AddressingResponses responses) {
+        if (impl != null) {
+            impl.setAddressingResponses(responses);
+        }
+        addressingResponses = responses;
+    }
+
+    /**
+     * Returns the cache used to enforce duplicate message IDs when
+     * {@link #allowDuplicates()} returns {@code false}.
+     *
+     * @return the cache used to enforce duplicate message IDs
+     */
+    public MessageIdCache getMessageIdCache() {
+        if (impl != null) {
+            return impl.getMessageIdCache();
+        }
+        return messageIdCache;
+    }
+
+    /**
+     * Sets the cache used to enforce duplicate message IDs when
+     * {@link #allowDuplicates()} returns {@code false}.
+     *
+     * @param messageIdCache the cache to use
+     *
+     * @throws NullPointerException if {@code messageIdCache} is {@code null}
+     */
+    public void setMessageIdCache(MessageIdCache messageIdCache) {
+        if (messageIdCache == null) {
+            throw new NullPointerException("messageIdCache cannot be null.");
+        }
+        if (impl != null) {
+            impl.setMessageIdCache(messageIdCache);
+        }
+        this.messageIdCache = messageIdCache;
+    }
+
+    /**
+     * Sets Addressing Response
+     *
+     */
+    public WSAddressingFeature.AddressingResponses getAddressingResponses() {
+        if (impl != null) {
+            return impl.getAddressingResponses();
+        }
+        return addressingResponses;
+    }
+
+    /**
+     * Invoked for normal processing of inbound and outbound messages.
+     *
+     * @param message the current message
+     */
+    public void handleMessage(Message message) {
+        if (impl == null) {
+            //load impl
+            MAPAggregatorLoader loader = message.getExchange().getBus()
+                .getExtension(MAPAggregatorLoader.class);
+            impl = loader.createImplementation(this);
+        }
+        impl.handleMessage(message);
+    }
+
+    @Override
+    public void handleFault(Message message) {
+        if (impl != null) {
+            impl.handleFault(message);
+        }
+    }
+
+
+    @Override
+    public Collection<PhaseInterceptor<? extends Message>> getAdditionalInterceptors() {
+        if (impl != null) {
+            return impl.getAdditionalInterceptors();
+        }
+        return super.getAdditionalInterceptors();
+    }
+
+
+}