Fix split package between spring-ws-support and spring-ws-core
This commit moves the classes from the o.s.ws.transport.http and o.s.ws.transport.support packages from spring-ws-support to spring-ws-core. It turns out these classes don't bring extra dependencies, which make the move quite straightforward. Closes gh-1202
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright 2005-2025 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.ws.transport.http;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import jakarta.xml.soap.SOAPConstants;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.ws.WebServiceMessage;
|
||||
import org.springframework.ws.transport.AbstractReceiverConnection;
|
||||
import org.springframework.ws.transport.EndpointAwareWebServiceConnection;
|
||||
import org.springframework.ws.transport.FaultAwareWebServiceConnection;
|
||||
import org.springframework.ws.transport.WebServiceConnection;
|
||||
|
||||
/**
|
||||
* Implementation of {@link WebServiceConnection} that is based on the Java 6 HttpServer
|
||||
* {@link HttpExchange}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Greg Turnquist
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class HttpExchangeConnection extends AbstractReceiverConnection
|
||||
implements EndpointAwareWebServiceConnection, FaultAwareWebServiceConnection {
|
||||
|
||||
private final HttpExchange httpExchange;
|
||||
|
||||
private ByteArrayOutputStream responseBuffer;
|
||||
|
||||
private int responseStatusCode = HttpTransportConstants.STATUS_ACCEPTED;
|
||||
|
||||
private boolean chunkedEncoding;
|
||||
|
||||
/** Constructs a new exchange connection with the given {@code HttpExchange}. */
|
||||
protected HttpExchangeConnection(HttpExchange httpExchange) {
|
||||
Assert.notNull(httpExchange, "'httpExchange' must not be null");
|
||||
this.httpExchange = httpExchange;
|
||||
}
|
||||
|
||||
/** Returns the {@code HttpExchange} for this connection. */
|
||||
public HttpExchange getHttpExchange() {
|
||||
return this.httpExchange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUri() throws URISyntaxException {
|
||||
return this.httpExchange.getRequestURI();
|
||||
}
|
||||
|
||||
void setChunkedEncoding(boolean chunkedEncoding) {
|
||||
this.chunkedEncoding = chunkedEncoding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endpointNotFound() {
|
||||
this.responseStatusCode = HttpTransportConstants.STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
/*
|
||||
* Errors
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean hasError() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorMessage() throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Receiving request
|
||||
*/
|
||||
|
||||
@Override
|
||||
public Iterator<String> getRequestHeaderNames() throws IOException {
|
||||
return this.httpExchange.getRequestHeaders().keySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> getRequestHeaders(String name) throws IOException {
|
||||
List<String> headers = this.httpExchange.getRequestHeaders().get(name);
|
||||
return (headers != null) ? headers.iterator() : Collections.emptyIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputStream getRequestInputStream() throws IOException {
|
||||
return this.httpExchange.getRequestBody();
|
||||
}
|
||||
|
||||
/*
|
||||
* Sending response
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void addResponseHeader(String name, String value) throws IOException {
|
||||
this.httpExchange.getResponseHeaders().add(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OutputStream getResponseOutputStream() throws IOException {
|
||||
if (this.chunkedEncoding) {
|
||||
this.httpExchange.sendResponseHeaders(this.responseStatusCode, 0);
|
||||
return this.httpExchange.getResponseBody();
|
||||
}
|
||||
else {
|
||||
if (this.responseBuffer == null) {
|
||||
this.responseBuffer = new ByteArrayOutputStream();
|
||||
}
|
||||
return this.responseBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSendAfterWrite(WebServiceMessage message) throws IOException {
|
||||
if (!this.chunkedEncoding) {
|
||||
byte[] buf = this.responseBuffer.toByteArray();
|
||||
this.httpExchange.sendResponseHeaders(this.responseStatusCode, buf.length);
|
||||
OutputStream responseBody = this.httpExchange.getResponseBody();
|
||||
FileCopyUtils.copy(buf, responseBody);
|
||||
}
|
||||
this.responseBuffer = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose() throws IOException {
|
||||
if (this.responseStatusCode == HttpTransportConstants.STATUS_ACCEPTED
|
||||
|| this.responseStatusCode == HttpTransportConstants.STATUS_NOT_FOUND) {
|
||||
this.httpExchange.sendResponseHeaders(this.responseStatusCode, -1);
|
||||
}
|
||||
this.httpExchange.close();
|
||||
}
|
||||
|
||||
/*
|
||||
* Faults
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean hasFault() throws IOException {
|
||||
return this.responseStatusCode == HttpTransportConstants.STATUS_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFaultCode(QName faultCode) throws IOException {
|
||||
if (faultCode != null) {
|
||||
if (SOAPConstants.SOAP_SENDER_FAULT.equals(faultCode)) {
|
||||
this.responseStatusCode = HttpTransportConstants.STATUS_BAD_REQUEST;
|
||||
}
|
||||
else {
|
||||
this.responseStatusCode = HttpTransportConstants.STATUS_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.responseStatusCode = HttpTransportConstants.STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2005-2025 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.ws.transport.http;
|
||||
|
||||
/**
|
||||
* Exception that is thrown when an error occurs in the HTTP transport.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 1.5.8
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class HttpsTransportException extends HttpTransportException {
|
||||
|
||||
public HttpsTransportException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public HttpsTransportException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright 2005-2025 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.ws.transport.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Extension of {@link HttpUrlConnectionMessageSender} that adds support for (self-signed)
|
||||
* HTTPS certificates.
|
||||
*
|
||||
* @author Alex Marshall
|
||||
* @author Arjen Poutsma
|
||||
* @since 1.5.8
|
||||
*/
|
||||
public class HttpsUrlConnectionMessageSender extends HttpUrlConnectionMessageSender implements InitializingBean {
|
||||
|
||||
/** The default SSL protocol. */
|
||||
public static final String DEFAULT_SSL_PROTOCOL = "ssl";
|
||||
|
||||
private String sslProtocol = DEFAULT_SSL_PROTOCOL;
|
||||
|
||||
private String sslProvider;
|
||||
|
||||
private KeyManager[] keyManagers;
|
||||
|
||||
private TrustManager[] trustManagers;
|
||||
|
||||
private HostnameVerifier hostnameVerifier;
|
||||
|
||||
private SecureRandom rnd;
|
||||
|
||||
private SSLSocketFactory sslSocketFactory;
|
||||
|
||||
/**
|
||||
* Sets the SSL protocol to use. Default is {@code ssl}.
|
||||
* @see SSLContext#getInstance(String, String)
|
||||
*/
|
||||
public void setSslProtocol(String sslProtocol) {
|
||||
Assert.hasLength(sslProtocol, "'sslProtocol' must not be empty");
|
||||
this.sslProtocol = sslProtocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the SSL provider to use. Default is empty, to use the default provider.
|
||||
* @see SSLContext#getInstance(String, String)
|
||||
*/
|
||||
public void setSslProvider(String sslProvider) {
|
||||
this.sslProvider = sslProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the key managers to use for this message sender.
|
||||
* <p>
|
||||
* Setting either this property or {@link #setTrustManagers(TrustManager[])
|
||||
* trustManagers} is required.
|
||||
* @see SSLContext#init(KeyManager[], TrustManager[], SecureRandom)
|
||||
*/
|
||||
public void setKeyManagers(KeyManager[] keyManagers) {
|
||||
this.keyManagers = keyManagers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the trust managers to use for this message sender.
|
||||
* <p>
|
||||
* Setting either this property or {@link #setKeyManagers(KeyManager[]) keyManagers}
|
||||
* is required.
|
||||
* @see SSLContext#init(KeyManager[], TrustManager[], SecureRandom)
|
||||
*/
|
||||
public void setTrustManagers(TrustManager[] trustManagers) {
|
||||
this.trustManagers = trustManagers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the host name verifier to use for this message sender.
|
||||
* @see HttpsURLConnection#setHostnameVerifier(HostnameVerifier)
|
||||
*/
|
||||
public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {
|
||||
this.hostnameVerifier = hostnameVerifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the secure random to use for this message sender.
|
||||
* @see SSLContext#init(KeyManager[], TrustManager[], SecureRandom)
|
||||
*/
|
||||
public void setSecureRandom(SecureRandom rnd) {
|
||||
this.rnd = rnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the SSLSocketFactory to use for this message sender.
|
||||
* @see HttpsURLConnection#setSSLSocketFactory(SSLSocketFactory sf)
|
||||
*/
|
||||
public void setSslSocketFactory(SSLSocketFactory sslSocketFactory) {
|
||||
this.sslSocketFactory = sslSocketFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.isTrue(
|
||||
!(ObjectUtils.isEmpty(this.keyManagers) && ObjectUtils.isEmpty(this.trustManagers)
|
||||
&& (this.sslSocketFactory == null)),
|
||||
"Setting either 'keyManagers', 'trustManagers' or 'sslSocketFactory' is required");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareConnection(HttpURLConnection connection) throws IOException {
|
||||
super.prepareConnection(connection);
|
||||
if (connection instanceof HttpsURLConnection httpsConnection) {
|
||||
httpsConnection.setSSLSocketFactory(createSslSocketFactory());
|
||||
|
||||
if (this.hostnameVerifier != null) {
|
||||
httpsConnection.setHostnameVerifier(this.hostnameVerifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SSLSocketFactory createSslSocketFactory() throws HttpsTransportException {
|
||||
if (this.sslSocketFactory != null) {
|
||||
return this.sslSocketFactory;
|
||||
}
|
||||
try {
|
||||
SSLContext sslContext = StringUtils.hasLength(this.sslProvider)
|
||||
? SSLContext.getInstance(this.sslProtocol, this.sslProvider)
|
||||
: SSLContext.getInstance(this.sslProtocol);
|
||||
sslContext.init(this.keyManagers, this.trustManagers, this.rnd);
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Initialized SSL Context with key managers ["
|
||||
+ StringUtils.arrayToCommaDelimitedString(this.keyManagers) + "] trust managers ["
|
||||
+ StringUtils.arrayToCommaDelimitedString(this.trustManagers) + "] secure random [" + this.rnd
|
||||
+ "]");
|
||||
}
|
||||
return sslContext.getSocketFactory();
|
||||
}
|
||||
catch (NoSuchAlgorithmException | NoSuchProviderException ex) {
|
||||
throw new HttpsTransportException("Could not create SSLContext: " + ex.getMessage(), ex);
|
||||
}
|
||||
catch (KeyManagementException ex) {
|
||||
throw new HttpsTransportException("Could not initialize SSLContext: " + ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2005-2025 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.ws.transport.http;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
|
||||
import org.springframework.ws.transport.support.SimpleWebServiceMessageReceiverObjectSupport;
|
||||
|
||||
/**
|
||||
* {@link HttpHandler} that can be used to handle incoming {@link HttpExchange} service
|
||||
* requests. Designed for Sun's JRE 1.6 HTTP server.
|
||||
* <p>
|
||||
* Requires a {@link org.springframework.ws.WebServiceMessageFactory} which is used to
|
||||
* convert the incoming {@link HttpExchange} into a
|
||||
* {@link org.springframework.ws.WebServiceMessage}, and passes that to the
|
||||
* {@link org.springframework.ws.transport.WebServiceMessageReceiver}
|
||||
* {@link #setMessageReceiver(org.springframework.ws.transport.WebServiceMessageReceiver)
|
||||
* registered}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class WebServiceMessageReceiverHttpHandler extends SimpleWebServiceMessageReceiverObjectSupport
|
||||
implements HttpHandler {
|
||||
|
||||
private boolean chunkedEncoding = false;
|
||||
|
||||
/** Enables chunked encoding on response bodies. Defaults to {@code false}. */
|
||||
public void setChunkedEncoding(boolean chunkedEncoding) {
|
||||
this.chunkedEncoding = chunkedEncoding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange httpExchange) throws IOException {
|
||||
if (HttpTransportConstants.METHOD_POST.equals(httpExchange.getRequestMethod())) {
|
||||
HttpExchangeConnection connection = new HttpExchangeConnection(httpExchange);
|
||||
connection.setChunkedEncoding(this.chunkedEncoding);
|
||||
try {
|
||||
handleConnection(connection);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
this.logger.error(ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
httpExchange.sendResponseHeaders(HttpTransportConstants.STATUS_METHOD_NOT_ALLOWED, -1);
|
||||
httpExchange.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2005-2025 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.ws.transport.http;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
|
||||
import com.sun.net.httpserver.Headers;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.ws.wsdl.WsdlDefinition;
|
||||
import org.springframework.xml.transform.TransformerObjectSupport;
|
||||
|
||||
/**
|
||||
* {@link HttpHandler} implementation for WSDL documents.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class WsdlDefinitionHttpHandler extends TransformerObjectSupport implements HttpHandler, InitializingBean {
|
||||
|
||||
private static final String CONTENT_TYPE = "text/xml";
|
||||
|
||||
private WsdlDefinition definition;
|
||||
|
||||
public WsdlDefinitionHttpHandler() {
|
||||
}
|
||||
|
||||
public WsdlDefinitionHttpHandler(WsdlDefinition definition) {
|
||||
this.definition = definition;
|
||||
}
|
||||
|
||||
public void setDefinition(WsdlDefinition definition) {
|
||||
this.definition = definition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(this.definition, "'definition' is required");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange httpExchange) throws IOException {
|
||||
try (httpExchange) {
|
||||
if (HttpTransportConstants.METHOD_GET.equals(httpExchange.getRequestMethod())) {
|
||||
Headers headers = httpExchange.getResponseHeaders();
|
||||
headers.set(HttpTransportConstants.HEADER_CONTENT_TYPE, CONTENT_TYPE);
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
transform(this.definition.getSource(), new StreamResult(os));
|
||||
byte[] buf = os.toByteArray();
|
||||
httpExchange.sendResponseHeaders(HttpTransportConstants.STATUS_OK, buf.length);
|
||||
FileCopyUtils.copy(buf, httpExchange.getResponseBody());
|
||||
}
|
||||
else {
|
||||
httpExchange.sendResponseHeaders(HttpTransportConstants.STATUS_METHOD_NOT_ALLOWED, -1);
|
||||
}
|
||||
}
|
||||
catch (TransformerException ex) {
|
||||
this.logger.error(ex, ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2005-2025 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.ws.transport.support;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Abstract base class for asynchronous standalone, server-side transport objects.
|
||||
* Contains a Spring {@link TaskExecutor}, and various lifecycle callbacks.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public abstract class AbstractAsyncStandaloneMessageReceiver extends AbstractStandaloneMessageReceiver
|
||||
implements BeanNameAware {
|
||||
|
||||
/** Default thread name prefix. */
|
||||
public final String DEFAULT_THREAD_NAME_PREFIX = ClassUtils.getShortName(getClass()) + "-";
|
||||
|
||||
private TaskExecutor taskExecutor;
|
||||
|
||||
private String beanName;
|
||||
|
||||
/**
|
||||
* Set the Spring {@link TaskExecutor} to use for running the listener threads.
|
||||
* Default is {@link SimpleAsyncTaskExecutor}, starting up a number of new threads.
|
||||
* <p>
|
||||
* Specify an alternative task executor for integration with an existing thread pool,
|
||||
* such as the
|
||||
* {@link org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor}.
|
||||
*/
|
||||
public void setTaskExecutor(TaskExecutor taskExecutor) {
|
||||
this.taskExecutor = taskExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (this.taskExecutor == null) {
|
||||
this.taskExecutor = createDefaultTaskExecutor();
|
||||
}
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a default TaskExecutor. Called if no explicit TaskExecutor has been
|
||||
* specified.
|
||||
* <p>
|
||||
* The default implementation builds a
|
||||
* {@link org.springframework.core.task.SimpleAsyncTaskExecutor} with the specified
|
||||
* bean name (or the class name, if no bean name specified) as thread name prefix.
|
||||
* @see org.springframework.core.task.SimpleAsyncTaskExecutor#SimpleAsyncTaskExecutor(String)
|
||||
*/
|
||||
protected TaskExecutor createDefaultTaskExecutor() {
|
||||
String threadNamePrefix = (this.beanName != null) ? this.beanName + "-" : this.DEFAULT_THREAD_NAME_PREFIX;
|
||||
return new SimpleAsyncTaskExecutor(threadNamePrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given {@link Runnable} via this receiver's {@link TaskExecutor}.
|
||||
* @see #setTaskExecutor(TaskExecutor)
|
||||
*/
|
||||
protected void execute(Runnable runnable) {
|
||||
this.taskExecutor.execute(runnable);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 2005-2025 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.ws.transport.support;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.context.Lifecycle;
|
||||
|
||||
/**
|
||||
* Abstract base class for standalone, server-side transport objects. Provides a basic,
|
||||
* thread-safe implementation of the {@link Lifecycle} interface, and various template
|
||||
* methods to be implemented by concrete sub classes.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public abstract class AbstractStandaloneMessageReceiver extends SimpleWebServiceMessageReceiverObjectSupport
|
||||
implements Lifecycle, DisposableBean {
|
||||
|
||||
private volatile boolean active = false;
|
||||
|
||||
private boolean autoStartup = true;
|
||||
|
||||
private boolean running = false;
|
||||
|
||||
private final Object lifecycleMonitor = new Object();
|
||||
|
||||
/**
|
||||
* Return whether this server is currently active, that is, whether it has been set up
|
||||
* but not shut down yet.
|
||||
*/
|
||||
public final boolean isActive() {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
return this.active;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this server is currently running, that is, whether it has been
|
||||
* started and not stopped yet.
|
||||
*/
|
||||
@Override
|
||||
public final boolean isRunning() {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
return this.running;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to automatically start the receiver after initialization.
|
||||
* <p>
|
||||
* Default is {@code true}; set this to {@code false} to allow for manual startup.
|
||||
*/
|
||||
public void setAutoStartup(boolean autoStartup) {
|
||||
this.autoStartup = autoStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #activate()} when the BeanFactory initializes the receiver instance.
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
activate();
|
||||
}
|
||||
|
||||
/** Calls {@link #shutdown()} when the BeanFactory destroys the receiver instance. */
|
||||
@Override
|
||||
public void destroy() {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this server. Starts the server if {@link #setAutoStartup(boolean)
|
||||
* autoStartup} hasn't been turned off.
|
||||
*/
|
||||
public final void activate() throws Exception {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
this.active = true;
|
||||
}
|
||||
onActivate();
|
||||
if (this.autoStartup) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
/** Start this server. */
|
||||
@Override
|
||||
public final void start() {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
this.running = true;
|
||||
}
|
||||
onStart();
|
||||
}
|
||||
|
||||
/** Stop this server. */
|
||||
@Override
|
||||
public final void stop() {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
this.running = false;
|
||||
}
|
||||
onStop();
|
||||
}
|
||||
|
||||
/** Shut down this server. */
|
||||
public final void shutdown() {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
this.running = false;
|
||||
this.active = false;
|
||||
}
|
||||
onShutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method invoked when {@link #activate()} is invoked.
|
||||
* @throws Exception in case of errors
|
||||
*/
|
||||
protected abstract void onActivate() throws Exception;
|
||||
|
||||
/** Template method invoked when {@link #start()} is invoked. */
|
||||
protected abstract void onStart();
|
||||
|
||||
/** Template method invoked when {@link #stop()} is invoked. */
|
||||
protected abstract void onStop();
|
||||
|
||||
/** Template method invoked when {@link #shutdown()} is invoked. */
|
||||
protected abstract void onShutdown();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2005-2025 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.ws.transport.support;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.ws.transport.WebServiceConnection;
|
||||
import org.springframework.ws.transport.WebServiceMessageReceiver;
|
||||
|
||||
/**
|
||||
* Base class for server-side transport objects which have a predefined
|
||||
* {@link WebServiceMessageReceiver}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 1.5.0
|
||||
* @see #handleConnection(WebServiceConnection)
|
||||
*/
|
||||
public abstract class SimpleWebServiceMessageReceiverObjectSupport extends WebServiceMessageReceiverObjectSupport
|
||||
implements InitializingBean {
|
||||
|
||||
private WebServiceMessageReceiver messageReceiver;
|
||||
|
||||
/**
|
||||
* Returns the {@code WebServiceMessageReceiver} used by this listener.
|
||||
*/
|
||||
public WebServiceMessageReceiver getMessageReceiver() {
|
||||
return this.messageReceiver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code WebServiceMessageReceiver} used by this listener.
|
||||
*/
|
||||
public void setMessageReceiver(WebServiceMessageReceiver messageReceiver) {
|
||||
this.messageReceiver = messageReceiver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
super.afterPropertiesSet();
|
||||
Assert.notNull(getMessageReceiver(), "messageReceiver must not be null");
|
||||
}
|
||||
|
||||
protected final void handleConnection(WebServiceConnection connection) throws Exception {
|
||||
handleConnection(connection, getMessageReceiver());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2005-2025 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.ws.transport.http;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.ws.context.MessageContext;
|
||||
import org.springframework.ws.server.endpoint.MessageEndpoint;
|
||||
import org.springframework.ws.soap.SoapMessage;
|
||||
|
||||
public class FaultEndpoint implements MessageEndpoint {
|
||||
|
||||
@Override
|
||||
public void invoke(MessageContext messageContext) {
|
||||
|
||||
SoapMessage response = (SoapMessage) messageContext.getResponse();
|
||||
response.getSoapBody().addServerOrReceiverFault("Something went wrong", Locale.ENGLISH);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2005-2025 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.ws.transport.http;
|
||||
|
||||
import org.springframework.ws.context.MessageContext;
|
||||
import org.springframework.ws.server.endpoint.MessageEndpoint;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class NoResponseEndpoint implements MessageEndpoint {
|
||||
|
||||
@Override
|
||||
public void invoke(MessageContext messageContext) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2005-2025 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.ws.transport.http;
|
||||
|
||||
import org.springframework.ws.context.MessageContext;
|
||||
import org.springframework.ws.server.endpoint.MessageEndpoint;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class ResponseEndpoint implements MessageEndpoint {
|
||||
|
||||
@Override
|
||||
public void invoke(MessageContext messageContext) {
|
||||
messageContext.getResponse();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2005-2025 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.ws.transport.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import com.sun.net.httpserver.Authenticator;
|
||||
import com.sun.net.httpserver.Filter;
|
||||
import com.sun.net.httpserver.HttpContext;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
/**
|
||||
* Utility class ONLY used for testing SOAP interactions through the JRE's built-in
|
||||
* {@link HttpServer}.
|
||||
*/
|
||||
class SimpleHttpServerFactoryBean implements FactoryBean<HttpServer>, InitializingBean, DisposableBean {
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private int port = 8080;
|
||||
|
||||
private String hostname;
|
||||
|
||||
private int backlog = -1;
|
||||
|
||||
private int shutdownDelay = 0;
|
||||
|
||||
private Executor executor;
|
||||
|
||||
private Map<String, HttpHandler> contexts;
|
||||
|
||||
private List<Filter> filters;
|
||||
|
||||
private Authenticator authenticator;
|
||||
|
||||
private HttpServer server;
|
||||
|
||||
/**
|
||||
* Specify the HTTP server's port. Default is 8080.
|
||||
*/
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the HTTP server's hostname to bind to. Default is localhost; can be
|
||||
* overridden with a specific network address to bind to.
|
||||
*/
|
||||
public void setHostname(String hostname) {
|
||||
this.hostname = hostname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the HTTP server's TCP backlog. Default is -1, indicating the system's
|
||||
* default value.
|
||||
*/
|
||||
public void setBacklog(int backlog) {
|
||||
this.backlog = backlog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the number of seconds to wait until HTTP exchanges have completed when
|
||||
* shutting down the HTTP server. Default is 0.
|
||||
*/
|
||||
public void setShutdownDelay(int shutdownDelay) {
|
||||
this.shutdownDelay = shutdownDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JDK concurrent executor to use for dispatching incoming requests.
|
||||
* @see HttpServer#setExecutor
|
||||
*/
|
||||
public void setExecutor(Executor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register {@link HttpHandler HttpHandlers} for specific context paths.
|
||||
* @param contexts a Map with context paths as keys and HttpHandler objects as values
|
||||
*/
|
||||
public void setContexts(Map<String, HttpHandler> contexts) {
|
||||
this.contexts = contexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register common {@link Filter Filters} to be applied to all locally registered
|
||||
* {@link #setContexts contexts}.
|
||||
*/
|
||||
public void setFilters(List<Filter> filters) {
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a common {@link Authenticator} to be applied to all locally registered
|
||||
* {@link #setContexts contexts}.
|
||||
*/
|
||||
public void setAuthenticator(Authenticator authenticator) {
|
||||
this.authenticator = authenticator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws IOException {
|
||||
InetSocketAddress address = (this.hostname != null ? new InetSocketAddress(this.hostname, this.port)
|
||||
: new InetSocketAddress(this.port));
|
||||
this.server = HttpServer.create(address, this.backlog);
|
||||
if (this.executor != null) {
|
||||
this.server.setExecutor(this.executor);
|
||||
}
|
||||
if (this.contexts != null) {
|
||||
this.contexts.forEach((key, context) -> {
|
||||
HttpContext httpContext = this.server.createContext(key, context);
|
||||
if (this.filters != null) {
|
||||
httpContext.getFilters().addAll(this.filters);
|
||||
}
|
||||
if (this.authenticator != null) {
|
||||
httpContext.setAuthenticator(this.authenticator);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
this.logger.info("Starting HttpServer at address " + address);
|
||||
}
|
||||
this.server.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpServer getObject() {
|
||||
return this.server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends HttpServer> getObjectType() {
|
||||
return (this.server != null ? this.server.getClass() : HttpServer.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
this.logger.info("Stopping HttpServer");
|
||||
this.server.stop(this.shutdownDelay);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2005-2025 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.ws.transport.http;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.hc.client5.http.classic.methods.HttpGet;
|
||||
import org.apache.hc.client5.http.classic.methods.HttpPost;
|
||||
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
|
||||
import org.apache.hc.client5.http.impl.classic.HttpClients;
|
||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
||||
import org.apache.hc.core5.http.ContentType;
|
||||
import org.apache.hc.core5.http.HttpHeaders;
|
||||
import org.apache.hc.core5.http.io.HttpClientResponseHandler;
|
||||
import org.apache.hc.core5.http.io.entity.InputStreamEntity;
|
||||
import org.assertj.core.api.ThrowingConsumer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.ws.transport.TransportConstants;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration("httpserver-applicationContext.xml")
|
||||
public class WebServiceHttpHandlerIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private int port;
|
||||
|
||||
@Test
|
||||
public void testInvalidMethod() {
|
||||
HttpGet httpRequest = new HttpGet(serviceUrl());
|
||||
execute(httpRequest, response -> {
|
||||
assertThat(response.getCode()).isEqualTo(HttpTransportConstants.STATUS_METHOD_NOT_ALLOWED);
|
||||
assertThat(response.containsHeader(HttpHeaders.CONTENT_LENGTH)).isTrue();
|
||||
assertThat(response.getHeader(HttpHeaders.CONTENT_LENGTH).getValue()).isEqualTo("0");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoResponse() throws IOException {
|
||||
HttpPost httpRequest = new HttpPost(serviceUrl());
|
||||
httpRequest.addHeader(TransportConstants.HEADER_SOAP_ACTION, "http://springframework.org/spring-ws/NoResponse");
|
||||
Resource soapRequest = new ClassPathResource("soapRequest.xml", WebServiceHttpHandlerIntegrationTest.class);
|
||||
httpRequest.setEntity(new InputStreamEntity(soapRequest.getInputStream(), ContentType.TEXT_XML));
|
||||
execute(httpRequest, response -> {
|
||||
assertThat(response.getCode()).isEqualTo(HttpTransportConstants.STATUS_ACCEPTED);
|
||||
assertThat(response.containsHeader(HttpHeaders.CONTENT_LENGTH)).isTrue();
|
||||
assertThat(response.getHeader(HttpHeaders.CONTENT_LENGTH).getValue()).isEqualTo("0");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponse() throws IOException {
|
||||
HttpPost httpRequest = new HttpPost(serviceUrl());
|
||||
httpRequest.addHeader(TransportConstants.HEADER_SOAP_ACTION, "http://springframework.org/spring-ws/Response");
|
||||
Resource soapRequest = new ClassPathResource("soapRequest.xml", WebServiceHttpHandlerIntegrationTest.class);
|
||||
httpRequest.setEntity(new InputStreamEntity(soapRequest.getInputStream(), ContentType.TEXT_XML));
|
||||
execute(httpRequest, response -> {
|
||||
assertThat(response.getCode()).isEqualTo(HttpTransportConstants.STATUS_OK);
|
||||
assertThat(response.containsHeader(HttpHeaders.CONTENT_LENGTH)).isTrue();
|
||||
assertThat(response.getHeader(HttpHeaders.CONTENT_LENGTH).getValue()).asInt().isGreaterThan(0);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoEndpoint() throws IOException {
|
||||
HttpPost httpRequest = new HttpPost(serviceUrl());
|
||||
httpRequest.addHeader(TransportConstants.HEADER_SOAP_ACTION, "http://springframework.org/spring-ws/NoEndpoint");
|
||||
Resource soapRequest = new ClassPathResource("soapRequest.xml", WebServiceHttpHandlerIntegrationTest.class);
|
||||
httpRequest.setEntity(new InputStreamEntity(soapRequest.getInputStream(), ContentType.TEXT_XML));
|
||||
execute(httpRequest, response -> {
|
||||
assertThat(response.getCode()).isEqualTo(HttpTransportConstants.STATUS_NOT_FOUND);
|
||||
assertThat(response.containsHeader(HttpHeaders.CONTENT_LENGTH)).isTrue();
|
||||
assertThat(response.getHeader(HttpHeaders.CONTENT_LENGTH).getValue()).isEqualTo("0");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFault() throws IOException {
|
||||
HttpPost httpRequest = new HttpPost(serviceUrl());
|
||||
httpRequest.addHeader(TransportConstants.HEADER_SOAP_ACTION, "http://springframework.org/spring-ws/Fault");
|
||||
Resource soapRequest = new ClassPathResource("soapRequest.xml", WebServiceHttpHandlerIntegrationTest.class);
|
||||
httpRequest.setEntity(new InputStreamEntity(soapRequest.getInputStream(), ContentType.TEXT_XML));
|
||||
execute(httpRequest, response -> assertThat(response.getCode())
|
||||
.isEqualTo(HttpTransportConstants.STATUS_INTERNAL_SERVER_ERROR));
|
||||
}
|
||||
|
||||
private String serviceUrl() {
|
||||
return "http://localhost:%s/service".formatted(this.port);
|
||||
}
|
||||
|
||||
private void execute(ClassicHttpRequest request, ThrowingConsumer<ClassicHttpResponse> responseHandler) {
|
||||
try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
|
||||
HttpClientResponseHandler<Object> rh = httpResponse -> {
|
||||
responseHandler.accept(httpResponse);
|
||||
return null;
|
||||
};
|
||||
httpclient.execute(request, rh);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2005-2025 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.ws.transport.http.test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.util.Random;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility class that finds free BSD ports for use in testing scenario's.
|
||||
*
|
||||
* @author Ben Hale
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public abstract class FreePortScanner {
|
||||
|
||||
private static final int MIN_SAFE_PORT = 1024;
|
||||
|
||||
private static final int MAX_PORT = 65535;
|
||||
|
||||
private static final Random random = new Random();
|
||||
|
||||
/**
|
||||
* Returns the number of a free port in the default range.
|
||||
*/
|
||||
public static int getFreePort() {
|
||||
return getFreePort(MIN_SAFE_PORT, MAX_PORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of a free port in the given range.
|
||||
*/
|
||||
public static int getFreePort(int minPort, int maxPort) {
|
||||
|
||||
Assert.isTrue(minPort > 0, "'minPort' must be larger than 0");
|
||||
Assert.isTrue(maxPort > minPort, "'maxPort' must be larger than minPort");
|
||||
|
||||
int portRange = maxPort - minPort;
|
||||
int candidatePort;
|
||||
int searchCounter = 0;
|
||||
|
||||
do {
|
||||
if (++searchCounter > portRange) {
|
||||
throw new IllegalStateException(
|
||||
String.format("There were no ports available in the range %d to %d", minPort, maxPort));
|
||||
}
|
||||
candidatePort = getRandomPort(minPort, portRange);
|
||||
}
|
||||
while (!isPortAvailable(candidatePort));
|
||||
|
||||
return candidatePort;
|
||||
}
|
||||
|
||||
private static int getRandomPort(int minPort, int portRange) {
|
||||
return minPort + random.nextInt(portRange);
|
||||
}
|
||||
|
||||
private static boolean isPortAvailable(int port) {
|
||||
|
||||
ServerSocket serverSocket;
|
||||
|
||||
try {
|
||||
serverSocket = new ServerSocket();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Unable to create ServerSocket.", ex);
|
||||
}
|
||||
|
||||
try {
|
||||
InetSocketAddress sa = new InetSocketAddress(port);
|
||||
serverSocket.bind(sa);
|
||||
return true;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
serverSocket.close();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
|
||||
|
||||
<bean id="port"
|
||||
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
|
||||
<property name="targetClass"
|
||||
value="org.springframework.ws.transport.http.test.FreePortScanner"/>
|
||||
<property name="targetMethod" value="getFreePort"/>
|
||||
</bean>
|
||||
|
||||
<bean id="httpServer"
|
||||
class="org.springframework.ws.transport.http.SimpleHttpServerFactoryBean">
|
||||
<property name="port" ref="port"/>
|
||||
<property name="contexts">
|
||||
<map>
|
||||
<entry key="/service" value-ref="webServiceHandler"/>
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="webServiceHandler"
|
||||
class="org.springframework.ws.transport.http.WebServiceMessageReceiverHttpHandler">
|
||||
<property name="chunkedEncoding" value="true"/>
|
||||
<property name="messageFactory" ref="messageFactory"/>
|
||||
<property name="messageReceiver" ref="messageDispatcher"/>
|
||||
</bean>
|
||||
|
||||
<bean id="messageFactory"
|
||||
class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
|
||||
|
||||
<bean id="messageDispatcher"
|
||||
class="org.springframework.ws.soap.server.SoapMessageDispatcher">
|
||||
<property name="endpointMappings" ref="payloadMapping"/>
|
||||
</bean>
|
||||
|
||||
<bean id="payloadMapping"
|
||||
class="org.springframework.ws.soap.server.endpoint.mapping.SoapActionEndpointMapping">
|
||||
<property name="mappings">
|
||||
<props>
|
||||
<prop key="http://springframework.org/spring-ws/NoResponse">
|
||||
noResponseEndpoint
|
||||
</prop>
|
||||
<prop key="http://springframework.org/spring-ws/Response">
|
||||
responseEndpoint
|
||||
</prop>
|
||||
<prop key="http://springframework.org/spring-ws/Fault">faultEndpoint
|
||||
</prop>
|
||||
</props>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="noResponseEndpoint"
|
||||
class="org.springframework.ws.transport.http.NoResponseEndpoint"/>
|
||||
|
||||
<bean id="responseEndpoint"
|
||||
class="org.springframework.ws.transport.http.ResponseEndpoint"/>
|
||||
|
||||
<bean id="faultEndpoint"
|
||||
class="org.springframework.ws.transport.http.FaultEndpoint"/>
|
||||
|
||||
|
||||
</beans>
|
||||
Reference in New Issue
Block a user