You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by jl...@apache.org on 2022/11/17 22:40:58 UTC
[tomee] branch main updated: Restore all CXF files and do some tweaks for now
This is an automated email from the ASF dual-hosted git repository.
jlmonteiro pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomee.git
The following commit(s) were added to refs/heads/main by this push:
new 48d51f7b7f Restore all CXF files and do some tweaks for now
48d51f7b7f is described below
commit 48d51f7b7f7f6baa017da66d540554c3240b2ea2
Author: Jean-Louis Monteiro <jl...@tomitribe.com>
AuthorDate: Thu Nov 17 23:40:48 2022 +0100
Restore all CXF files and do some tweaks for now
---
.../java/org/apache/cxf/annotations/Policy.java | 80 +
.../apache/cxf/annotations/WSDLDocumentation.java | 88 +
.../cxf/attachment/AttachmentDataSource.java | 107 +
.../cxf/attachment/AttachmentDeserializer.java | 462 ++++
.../cxf/attachment/AttachmentSerializer.java | 345 +++
.../org/apache/cxf/attachment/AttachmentUtil.java | 572 +++++
.../apache/cxf/attachment/Base64DecoderStream.java | 196 ++
.../apache/cxf/attachment/ContentDisposition.java | 144 ++
.../cxf/attachment/LazyAttachmentCollection.java | 362 ++++
.../cxf/attachment/MimeBodyPartInputStream.java | 275 +++
.../java/org/apache/cxf/bus/CXFBusFactory.java | 47 +
.../bus/blueprint/BundleDelegatingClassLoader.java | 134 ++
.../cxf/bus/blueprint/BusDefinitionParser.java | 72 +
.../apache/cxf/bus/blueprint/ConfigurerImpl.java | 180 ++
.../org/apache/cxf/bus/extension/Extension.java | 291 +++
.../cxf/bus/extension/ExtensionManagerImpl.java | 381 ++++
.../ServiceContractResolverRegistryImpl.java | 113 +
.../java/org/apache/cxf/bus/osgi/CXFActivator.java | 134 ++
.../cxf/bus/osgi/CXFExtensionBundleListener.java | 181 ++
.../org/apache/cxf/bus/osgi/OSGIBusListener.java | 224 ++
.../cxf/bus/resource/ResourceManagerImpl.java | 85 +
.../apache/cxf/bus/spring/BusDefinitionParser.java | 310 +++
.../apache/cxf/bus/spring/BusEntityResolver.java | 94 +
.../cxf/bus/spring/BusExtensionPostProcessor.java | 71 +
.../cxf/bus/spring/Jsr250BeanPostProcessor.java | 164 ++
.../apache/cxf/bus/spring/NamespaceHandler.java | 57 +
.../java/org/apache/cxf/bus/spring/SpringBus.java | 142 ++
.../cxf/catalog/CatalogXmlSchemaURIResolver.java | 102 +
.../jsse/MultiKeyPasswordKeyManager.java | 84 +
.../configuration/jsse/TLSClientParameters.java | 258 +++
.../configuration/jsse/TLSParameterJaxBUtils.java | 420 ++++
.../cxf/configuration/spring/ConfigurerImpl.java | 288 +++
.../cxf/databinding/source/SourceDataBinding.java | 104 +
.../cxf/databinding/stax/StaxDataBinding.java | 187 ++
.../cxf/endpoint/AbstractConduitSelector.java | 308 +++
.../org/apache/cxf/endpoint/ClientCallback.java | 166 ++
.../java/org/apache/cxf/endpoint/ClientImpl.java | 1192 +++++++++++
.../java/org/apache/cxf/endpoint/EndpointImpl.java | 220 ++
.../java/org/apache/cxf/endpoint/ServerImpl.java | 221 ++
.../org/apache/cxf/feature/FastInfosetFeature.java | 110 +
.../org/apache/cxf/feature/WrappedFeature.java | 61 +
.../cxf/feature/transform/XSLTOutInterceptor.java | 210 ++
.../patch/java/org/apache/cxf/headers/Header.java | 78 +
.../java/org/apache/cxf/helpers/DOMUtils.java | 895 ++++++++
.../patch/java/org/apache/cxf/helpers/IOUtils.java | 428 ++++
.../java/org/apache/cxf/helpers/ServiceUtils.java | 214 ++
.../AbstractFaultChainInitiatorObserver.java | 142 ++
.../interceptor/AbstractLoggingInterceptor.java | 298 +++
.../cxf/interceptor/AnnotationInterceptors.java | 151 ++
.../cxf/interceptor/AttachmentInInterceptor.java | 72 +
.../cxf/interceptor/ClientOutFaultObserver.java | 68 +
.../interceptor/InFaultChainInitiatorObserver.java | 84 +
.../apache/cxf/interceptor/InterceptorChain.java | 109 +
.../org/apache/cxf/interceptor/LoggingMessage.java | 133 ++
.../interceptor/OneWayProcessorInterceptor.java | 177 ++
.../OutFaultChainInitiatorObserver.java | 84 +
.../security/DefaultSecurityContext.java | 188 ++
.../interceptor/security/JAASLoginInterceptor.java | 233 ++
.../cxf/internal/CXFAPINamespaceHandler.java | 127 ++
.../apache/cxf/io/CacheAndWriteOutputStream.java | 96 +
.../patch/java/org/apache/cxf/io/CachedWriter.java | 665 ++++++
.../java/org/apache/cxf/io/ReaderInputStream.java | 294 +++
.../apache/cxf/io/WriteOnCloseOutputStream.java | 46 +
.../apache/cxf/jaxrs/provider/ProviderFactory.java | 132 +-
.../java/org/apache/cxf/message/MessageUtils.java | 302 +++
.../java/org/apache/cxf/phase/PhaseChainCache.java | 137 ++
.../apache/cxf/phase/PhaseInterceptorChain.java | 857 ++++++++
.../cxf/service/factory/FactoryBeanListener.java | 145 ++
.../apache/cxf/service/invoker/FactoryInvoker.java | 70 +
.../service/model/AbstractPropertiesHolder.java | 271 +++
.../org/apache/cxf/service/model/FaultInfo.java | 61 +
.../apache/cxf/service/model/InterfaceInfo.java | 124 ++
.../apache/cxf/service/model/OperationInfo.java | 242 +++
.../cxf/service/model/UnwrappedOperationInfo.java | 72 +
.../java/org/apache/cxf/staxutils/StaxUtils.java | 2222 ++++++++++++++++++++
.../apache/cxf/staxutils/W3CDOMStreamReader.java | 429 ++++
pom.xml | 2 +-
77 files changed, 18809 insertions(+), 81 deletions(-)
diff --git a/deps/cxf-shade/src/patch/java/org/apache/cxf/annotations/Policy.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/annotations/Policy.java
new file mode 100644
index 0000000000..a0d4a75ea8
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/annotations/WSDLDocumentation.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/annotations/WSDLDocumentation.java
new file mode 100644
index 0000000000..9116f1c566
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/attachment/AttachmentDataSource.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/attachment/AttachmentDataSource.java
new file mode 100644
index 0000000000..d443832732
--- /dev/null
+++ b/deps/cxf-shade/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 jakarta.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/deps/cxf-shade/src/patch/java/org/apache/cxf/attachment/AttachmentDeserializer.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/attachment/AttachmentDeserializer.java
new file mode 100644
index 0000000000..4bd6b27d61
--- /dev/null
+++ b/deps/cxf-shade/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 jakarta.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/deps/cxf-shade/src/patch/java/org/apache/cxf/attachment/AttachmentSerializer.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/attachment/AttachmentSerializer.java
new file mode 100644
index 0000000000..e1fc0abd3d
--- /dev/null
+++ b/deps/cxf-shade/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 jakarta.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/deps/cxf-shade/src/patch/java/org/apache/cxf/attachment/AttachmentUtil.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/attachment/AttachmentUtil.java
new file mode 100644
index 0000000000..bb53347dcd
--- /dev/null
+++ b/deps/cxf-shade/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 jakarta.activation.CommandInfo;
+import jakarta.activation.CommandMap;
+import jakarta.activation.DataContentHandler;
+import jakarta.activation.DataHandler;
+import jakarta.activation.DataSource;
+import jakarta.activation.FileDataSource;
+import jakarta.activation.MailcapCommandMap;
+import jakarta.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/deps/cxf-shade/src/patch/java/org/apache/cxf/attachment/Base64DecoderStream.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/attachment/Base64DecoderStream.java
new file mode 100644
index 0000000000..134553fa27
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/attachment/ContentDisposition.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/attachment/ContentDisposition.java
new file mode 100644
index 0000000000..f9d4524a00
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/attachment/LazyAttachmentCollection.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/attachment/LazyAttachmentCollection.java
new file mode 100644
index 0000000000..339625bf18
--- /dev/null
+++ b/deps/cxf-shade/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 jakarta.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/deps/cxf-shade/src/patch/java/org/apache/cxf/attachment/MimeBodyPartInputStream.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/attachment/MimeBodyPartInputStream.java
new file mode 100644
index 0000000000..ab80b89e8b
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/CXFBusFactory.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/CXFBusFactory.java
new file mode 100644
index 0000000000..fb3a833002
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/blueprint/BundleDelegatingClassLoader.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/blueprint/BundleDelegatingClassLoader.java
new file mode 100644
index 0000000000..ff957c2545
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/blueprint/BusDefinitionParser.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/blueprint/BusDefinitionParser.java
new file mode 100644
index 0000000000..baf83b7671
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/blueprint/ConfigurerImpl.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/blueprint/ConfigurerImpl.java
new file mode 100644
index 0000000000..37f82fedaa
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/extension/Extension.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/extension/Extension.java
new file mode 100644
index 0000000000..03f0dc6192
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/extension/ExtensionManagerImpl.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/extension/ExtensionManagerImpl.java
new file mode 100644
index 0000000000..d4ce0be177
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/managers/ServiceContractResolverRegistryImpl.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/managers/ServiceContractResolverRegistryImpl.java
new file mode 100644
index 0000000000..a0f7af0c8e
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/osgi/CXFActivator.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/osgi/CXFActivator.java
new file mode 100644
index 0000000000..65042764a3
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/osgi/CXFExtensionBundleListener.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/osgi/CXFExtensionBundleListener.java
new file mode 100644
index 0000000000..ee9bb15ca1
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/osgi/OSGIBusListener.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/osgi/OSGIBusListener.java
new file mode 100644
index 0000000000..aad270d042
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/resource/ResourceManagerImpl.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/resource/ResourceManagerImpl.java
new file mode 100644
index 0000000000..7203e5886b
--- /dev/null
+++ b/deps/cxf-shade/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 jakarta.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/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/spring/BusDefinitionParser.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/spring/BusDefinitionParser.java
new file mode 100644
index 0000000000..9d508b62d0
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/spring/BusEntityResolver.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/spring/BusEntityResolver.java
new file mode 100644
index 0000000000..1e95e286b7
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/spring/BusExtensionPostProcessor.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/spring/BusExtensionPostProcessor.java
new file mode 100644
index 0000000000..9389be6b49
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/spring/Jsr250BeanPostProcessor.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/spring/Jsr250BeanPostProcessor.java
new file mode 100644
index 0000000000..1fb0746eb8
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/spring/NamespaceHandler.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/spring/NamespaceHandler.java
new file mode 100644
index 0000000000..9211ea8411
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/spring/SpringBus.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/bus/spring/SpringBus.java
new file mode 100644
index 0000000000..6616c8ed85
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/catalog/CatalogXmlSchemaURIResolver.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/catalog/CatalogXmlSchemaURIResolver.java
new file mode 100644
index 0000000000..15a3ce2e39
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/configuration/jsse/MultiKeyPasswordKeyManager.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/configuration/jsse/MultiKeyPasswordKeyManager.java
new file mode 100644
index 0000000000..6a1aee5fb5
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/configuration/jsse/TLSClientParameters.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/configuration/jsse/TLSClientParameters.java
new file mode 100644
index 0000000000..3e0099a126
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/configuration/jsse/TLSParameterJaxBUtils.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/configuration/jsse/TLSParameterJaxBUtils.java
new file mode 100644
index 0000000000..bd16aa436a
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/configuration/spring/ConfigurerImpl.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/configuration/spring/ConfigurerImpl.java
new file mode 100644
index 0000000000..d920936f81
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/databinding/source/SourceDataBinding.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/databinding/source/SourceDataBinding.java
new file mode 100644
index 0000000000..5321779f80
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/databinding/stax/StaxDataBinding.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/databinding/stax/StaxDataBinding.java
new file mode 100644
index 0000000000..3cb836b8b0
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/endpoint/AbstractConduitSelector.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/endpoint/AbstractConduitSelector.java
new file mode 100644
index 0000000000..250e523b11
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/endpoint/ClientCallback.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/endpoint/ClientCallback.java
new file mode 100644
index 0000000000..467b5a800d
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/endpoint/ClientImpl.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/endpoint/ClientImpl.java
new file mode 100644
index 0000000000..ffc07cc983
--- /dev/null
+++ b/deps/cxf-shade/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 jakarta.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/deps/cxf-shade/src/patch/java/org/apache/cxf/endpoint/EndpointImpl.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/endpoint/EndpointImpl.java
new file mode 100644
index 0000000000..340211ba27
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/endpoint/ServerImpl.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/endpoint/ServerImpl.java
new file mode 100644
index 0000000000..c417cb6fe6
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/feature/FastInfosetFeature.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/feature/FastInfosetFeature.java
new file mode 100644
index 0000000000..daa4bd38ca
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/feature/WrappedFeature.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/feature/WrappedFeature.java
new file mode 100644
index 0000000000..2236a38e4d
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/feature/transform/XSLTOutInterceptor.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/feature/transform/XSLTOutInterceptor.java
new file mode 100644
index 0000000000..53605ccace
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/headers/Header.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/headers/Header.java
new file mode 100644
index 0000000000..f933c14dba
--- /dev/null
+++ b/deps/cxf-shade/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/deps/cxf-shade/src/patch/java/org/apache/cxf/helpers/DOMUtils.java b/deps/cxf-shade/src/patch/java/org/apache/cxf/helpers/DOMUtils.java
new file mode 100644
index 0000000000..37bde6779d
--- /dev/null
+++ b/deps/cxf-shade/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) {
... 9547 lines suppressed ...