javax.xml.soap
diff --git a/sandbox/src/main/java/org/springframework/ws/server/endpoint/adapter/XPathParamAnnotationEndpointAdapter.java b/sandbox/src/main/java/org/springframework/ws/server/endpoint/adapter/XPathParamAnnotationEndpointAdapter.java
index c1722bc2..707ba938 100644
--- a/sandbox/src/main/java/org/springframework/ws/server/endpoint/adapter/XPathParamAnnotationEndpointAdapter.java
+++ b/sandbox/src/main/java/org/springframework/ws/server/endpoint/adapter/XPathParamAnnotationEndpointAdapter.java
@@ -17,6 +17,7 @@
package org.springframework.ws.server.endpoint.adapter;
import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
import javax.xml.namespace.QName;
@@ -104,14 +105,24 @@ public class XPathParamAnnotationEndpointAdapter extends AbstractMethodEndpointA
}
protected void invokeInternal(MessageContext messageContext, MethodEndpoint methodEndpoint) throws Exception {
- Element payloadElement = getMessagePayloadElement(messageContext.getRequest());
- Object[] args = getMethodArguments(payloadElement, methodEndpoint.getMethod());
- Object result = methodEndpoint.invoke(args);
- if (result != null && result instanceof Source) {
- Source responseSource = (Source) result;
- WebServiceMessage response = messageContext.getResponse();
- Transformer transformer = createTransformer();
- transformer.transform(responseSource, response.getPayloadResult());
+ try {
+ Element payloadElement = getMessagePayloadElement(messageContext.getRequest());
+ Object[] args = getMethodArguments(payloadElement, methodEndpoint.getMethod());
+ Object result = methodEndpoint.invoke(args);
+ if (result != null && result instanceof Source) {
+ Source responseSource = (Source) result;
+ WebServiceMessage response = messageContext.getResponse();
+ Transformer transformer = createTransformer();
+ transformer.transform(responseSource, response.getPayloadResult());
+ }
+ }
+ catch (InvocationTargetException ex) {
+ if (ex.getTargetException() instanceof Exception) {
+ throw (Exception) ex.getTargetException();
+ }
+ else {
+ throw ex;
+ }
}
}
diff --git a/sandbox/src/main/java/org/springframework/ws/server/endpoint/adapter/package.html b/sandbox/src/main/java/org/springframework/ws/server/endpoint/adapter/package.html
deleted file mode 100644
index febadcb4..00000000
--- a/sandbox/src/main/java/org/springframework/ws/server/endpoint/adapter/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-Provides miscellaneous endpoints EndpointAdapter implementations.
-
-
diff --git a/sandbox/src/main/java/org/springframework/ws/server/endpoint/annotation/Endpoint.java b/sandbox/src/main/java/org/springframework/ws/server/endpoint/annotation/Endpoint.java
new file mode 100644
index 00000000..9aecfbd1
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/server/endpoint/annotation/Endpoint.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.server.endpoint.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.ws.server.endpoint.mapping.AbstractAnnotationEndpointMapping;
+import org.springframework.ws.soap.server.endpoint.mapping.SoapActionAnnotationEndpointMapping;
+
+/**
+ * Marks a class as an endpoint.
+ *
+ * Instances of this class are typically picked up by an {@link AbstractAnnotationEndpointMapping} implementation, such
+ * as {@link SoapActionAnnotationEndpointMapping}.
+ *
+ * @author Arjen Poutsma
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Endpoint {
+
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/server/endpoint/annotation/PayloadRootQName.java b/sandbox/src/main/java/org/springframework/ws/server/endpoint/annotation/PayloadRootQName.java
new file mode 100644
index 00000000..e6c55b8d
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/server/endpoint/annotation/PayloadRootQName.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.server.endpoint.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface PayloadRootQName {
+
+ String value();
+
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractAnnotationEndpointMapping.java b/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractAnnotationEndpointMapping.java
new file mode 100644
index 00000000..54f2c3a8
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractAnnotationEndpointMapping.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.server.endpoint.mapping;
+
+import java.lang.annotation.Annotation;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.ws.server.endpoint.annotation.Endpoint;
+
+/**
+ * Abstract base for {@link org.springframework.ws.server.EndpointMapping} implementations that map classes tagged with
+ * an annotation. By default the annotation is {@link Endpoint}, but this can be overriden in subclasses.
+ *
+ * Each bean
+ *
+ * @author Arjen Poutsma
+ */
+public abstract class AbstractAnnotationEndpointMapping extends AbstractMethodEndpointMapping
+ implements BeanPostProcessor {
+
+ public final Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ registerMethods(bean);
+ return bean;
+ }
+
+ /** Returns the 'endpoint' annotation type. Default is {@link Endpoint}. */
+ protected Class extends Annotation> getEndpointAnnotationType() {
+ return Endpoint.class;
+ }
+
+ public final Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ return bean;
+ }
+
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractMethodEndpointMapping.java b/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractMethodEndpointMapping.java
new file mode 100644
index 00000000..74dda141
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractMethodEndpointMapping.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.server.endpoint.mapping;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.aop.support.AopUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContextException;
+import org.springframework.core.JdkVersion;
+import org.springframework.util.StringUtils;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.server.endpoint.MethodEndpoint;
+
+/**
+ * Abstract base class for {@link MethodEndpoint} mappings.
+ *
+ * Subclasses typically implement {@link org.springframework.beans.factory.config.BeanPostProcessor} to look for beans
+ * that qualify as enpoint. The methods of this bean are then registered under a specific key with {@link
+ * #registerEndpoint(String,MethodEndpoint)}.
+ *
+ * @author Arjen Poutsma
+ */
+public abstract class AbstractMethodEndpointMapping extends AbstractEndpointMapping {
+
+ /** Keys are Strings, values are {@link MethodEndpoint}s. */
+ private final Map endpointMap = new HashMap();
+
+ /**
+ * Lookup an endpoint for the given message. The extraction of the endpoint key is delegated to the concrete
+ * subclass.
+ *
+ * @return the looked up endpoint, or null
+ * @see #getLookupKeyForMessage(MessageContext)
+ */
+ protected Object getEndpointInternal(MessageContext messageContext) throws Exception {
+ String key = getLookupKeyForMessage(messageContext);
+ if (!StringUtils.hasLength(key)) {
+ return null;
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("Looking up endpoint for [" + key + "]");
+ }
+ return lookupEndpoint(key);
+ }
+
+ /**
+ * Returns the the endpoint keys for the given message context.
+ *
+ * @return the registration keys
+ */
+ protected abstract String getLookupKeyForMessage(MessageContext messageContext) throws Exception;
+
+ /**
+ * Looks up an endpoint instance for the given keys. All keys are tried in order.
+ *
+ * @param key key the beans are mapped to
+ * @return the associated endpoint instance, or null if not found
+ */
+ protected MethodEndpoint lookupEndpoint(String key) {
+ return (MethodEndpoint) endpointMap.get(key);
+ }
+
+ /**
+ * Register the given endpoint instance under the key.
+ *
+ * @param key the lookup key
+ * @param endpoint the method endpoint instance
+ * @throws BeansException if the endpoint could not be registered
+ */
+ protected void registerEndpoint(String key, MethodEndpoint endpoint) throws BeansException {
+ Object mappedEndpoint = endpointMap.get(key);
+ if (mappedEndpoint != null) {
+ throw new ApplicationContextException("Cannot map endpoint [" + endpoint + "] on registration key [" + key +
+ "]: there's already endpoint [" + mappedEndpoint + "] mapped");
+ }
+ if (endpoint == null) {
+ throw new ApplicationContextException("Could not find endpoint for key [" + key + "]");
+ }
+ endpointMap.put(key, endpoint);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Mapped key [" + key + "] onto endpoint [" + endpoint + "]");
+ }
+ }
+
+ /**
+ * Helper method that registers the methods of the given bean. This method iterates over the methods of the bean,
+ * and calls {@link #getLookupKeyForMethod(Method)} for each. If this returns a string, the method is registered
+ * using {@link #registerEndpoint(String,MethodEndpoint)}.
+ *
+ * @see #getLookupKeyForMethod(Method)
+ */
+ protected void registerMethods(Object endpoint) {
+ Method[] methods = getEndpointClass(endpoint).getMethods();
+ for (int i = 0; i < methods.length; i++) {
+ if (JdkVersion.isAtLeastJava15() && methods[i].isSynthetic() ||
+ methods[i].getDeclaringClass().equals(Object.class)) {
+ continue;
+ }
+ String key = getLookupKeyForMethod(methods[i]);
+ if (StringUtils.hasLength(key)) {
+ registerEndpoint(key, new MethodEndpoint(endpoint, methods[i]));
+ }
+ }
+ }
+
+ /**
+ * Returns the the endpoint keys for the given method. Returns null if the method is not to be
+ * registered, which is the default.
+ *
+ * @param method the method
+ * @return a registration key, or null if the method is not to be registered
+ */
+ protected String getLookupKeyForMethod(Method method) {
+ return null;
+ }
+
+ /**
+ * Return the class or interface to use for method reflection.
+ *
+ * Default implementation returns the target class for a CGLIB proxy, and the class of the given bean else (for a
+ * JDK proxy or a plain bean class).
+ *
+ * @param endpoint the bean instance (might be an AOP proxy)
+ * @return the bean class to expose
+ */
+ protected Class getEndpointClass(Object endpoint) {
+ if (AopUtils.isCglibProxy(endpoint)) {
+ return endpoint.getClass().getSuperclass();
+ }
+ return endpoint.getClass();
+ }
+
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootQNameMethodEndpointMapping.java b/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootQNameMethodEndpointMapping.java
new file mode 100644
index 00000000..02e09601
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootQNameMethodEndpointMapping.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.server.endpoint.mapping;
+
+import java.lang.reflect.Method;
+import javax.xml.namespace.QName;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMResult;
+
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.ws.WebServiceMessage;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.server.endpoint.annotation.PayloadRootQName;
+import org.springframework.xml.namespace.QNameUtils;
+import org.w3c.dom.Element;
+
+/** @author Arjen Poutsma */
+public class PayloadRootQNameMethodEndpointMapping extends AbstractAnnotationEndpointMapping {
+
+ private static TransformerFactory transformerFactory;
+
+ static {
+ transformerFactory = TransformerFactory.newInstance();
+ }
+
+ protected String getLookupKeyForMessage(MessageContext messageContext) throws Exception {
+ Element payloadElement = getMessagePayloadElement(messageContext.getRequest());
+ QName qName = QNameUtils.getQNameForNode(payloadElement);
+ return qName != null ? qName.toString() : null;
+ }
+
+ protected String getLookupKeyForMethod(Method method) {
+ PayloadRootQName annotation = AnnotationUtils.getAnnotation(method, PayloadRootQName.class);
+ return annotation != null ? annotation.value() : null;
+ }
+
+ private Element getMessagePayloadElement(WebServiceMessage message) throws TransformerException {
+ Transformer transformer = transformerFactory.newTransformer();
+ DOMResult domResult = new DOMResult();
+ transformer.transform(message.getPayloadSource(), domResult);
+ return (Element) domResult.getNode().getFirstChild();
+ }
+
+
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/SimpleMethodEndpointMapping.java b/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/SimpleMethodEndpointMapping.java
new file mode 100644
index 00000000..18fac8dc
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/server/endpoint/mapping/SimpleMethodEndpointMapping.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.server.endpoint.mapping;
+
+import java.lang.reflect.Method;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+import org.springframework.ws.WebServiceMessage;
+import org.springframework.ws.context.MessageContext;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * Simple subclass of {@link AbstractMethodEndpointMapping} that maps to all methods that start with a prefix, and end
+ * with a suffix. Endpoint beans are registered using the endpoints property.
+ *
+ * @author Arjen Poutsma
+ */
+public class SimpleMethodEndpointMapping extends AbstractMethodEndpointMapping implements InitializingBean {
+
+ public static final String DEFAULT_METHOD_PREFIX = "handle";
+
+ public static final String DEFAULT_METHOD_SUFFIX = "";
+
+ private Object[] endpoints;
+
+ private String methodPrefix = DEFAULT_METHOD_PREFIX;
+
+ private String methodSuffix = DEFAULT_METHOD_SUFFIX;
+
+ private TransformerFactory transformerFactory;
+
+ /** Sets the endpoints */
+ public void setEndpoints(Object[] endpoints) {
+ this.endpoints = endpoints;
+ }
+
+ /**
+ * Sets the method prefix. All methods with names starting with this string will be registered. Default is
+ * "handle".
+ *
+ * @see #DEFAULT_METHOD_PREFIX
+ */
+ public void setMethodPrefix(String methodPrefix) {
+ this.methodPrefix = methodPrefix;
+ }
+
+ /**
+ * Sets the method suffix. All methods with names ending with this string will be registered. Default is "" (i.e. no
+ * suffix).
+ *
+ * @see #DEFAULT_METHOD_SUFFIX
+ */
+ public void setMethodSuffix(String methodSuffix) {
+ this.methodSuffix = methodSuffix;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ Assert.notEmpty(endpoints, "endpoints is required");
+ transformerFactory = TransformerFactory.newInstance();
+ for (int i = 0; i < endpoints.length; i++) {
+ registerMethods(endpoints[i]);
+ }
+ }
+
+ /** Returns the name of the given method, with the prefix and suffix stripped off. */
+ protected String getLookupKeyForMethod(Method method) {
+ String methodName = method.getName();
+ if (methodName.startsWith(methodPrefix) && methodName.endsWith(methodSuffix)) {
+ return methodName.substring(methodPrefix.length(), methodName.length() - methodSuffix.length());
+ }
+ else {
+ return null;
+ }
+ }
+
+ protected String getLookupKeyForMessage(MessageContext messageContext) throws TransformerException {
+ Element payloadElement = getMessagePayloadElement(messageContext.getRequest());
+ return payloadElement.getLocalName();
+ }
+
+ private Element getMessagePayloadElement(WebServiceMessage message) throws TransformerException {
+ if (message.getPayloadSource() instanceof DOMSource) {
+ DOMSource domSource = (DOMSource) message.getPayloadSource();
+ if (domSource.getNode().getNodeType() == Node.ELEMENT_NODE) {
+ return (Element) domSource.getNode();
+ }
+ }
+ Transformer transformer = transformerFactory.newTransformer();
+ DOMResult domResult = new DOMResult();
+ transformer.transform(message.getPayloadSource(), domResult);
+ return (Element) domResult.getNode().getFirstChild();
+ }
+
+
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/SoapAction.java b/sandbox/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/SoapAction.java
new file mode 100644
index 00000000..3857ce45
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/SoapAction.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.soap.server.endpoint.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** @author Arjen Poutsma */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SoapAction {
+
+ String value();
+
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationEndpointMapping.java b/sandbox/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationEndpointMapping.java
new file mode 100644
index 00000000..0fc27a53
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationEndpointMapping.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.soap.server.endpoint.mapping;
+
+import java.lang.reflect.Method;
+
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.server.EndpointInterceptor;
+import org.springframework.ws.server.EndpointInvocationChain;
+import org.springframework.ws.server.endpoint.mapping.AbstractAnnotationEndpointMapping;
+import org.springframework.ws.soap.SoapEndpointMapping;
+import org.springframework.ws.soap.SoapMessage;
+import org.springframework.ws.soap.server.SoapEndpointInvocationChain;
+import org.springframework.ws.soap.server.endpoint.annotation.SoapAction;
+
+/**
+ * Implementation of the EndpointMapping interface to map from SOAPAction headers to endpoint
+ * beans. Supports both mapping to bean instances and mapping to bean names: the latter is required for prototype
+ * handlers.
+ *
+ * The endpointMap property is suitable for populating the endpoint map with bean references, e.g. via the
+ * map element in XML bean definitions.
+ *
+ * Mappings to bean names can be set via the mappings property, in a form accepted by the
+ * java.util.Properties class, like as follows:
+ *
+ * http://www.springframework.org/spring-ws/samples/airline/BookFlight=bookFlightEndpoint
+ * http://www.springframework.org/spring-ws/samples/airline/GetFlights=getFlightsEndpoint
+ *
+ * The syntax is SOAP_ACTION=ENDPOINT_BEAN_NAME.
+ *
+ * This endpoint mapping does not read from the request message, and therefore is more suitable for message contexts
+ * which directly read from the transport request (such as the AxiomSoapMessageContextFactory with the
+ * payloadCaching disabled).
+ *
+ * @author Arjen Poutsma
+ */
+public class SoapActionAnnotationEndpointMapping extends AbstractAnnotationEndpointMapping
+ implements SoapEndpointMapping {
+
+ private String[] actorsOrRoles;
+
+ public final void setActorOrRole(String actorOrRole) {
+ Assert.notNull(actorOrRole, "actorOrRole must not be null");
+ actorsOrRoles = new String[]{actorOrRole};
+ }
+
+ public final void setActorsOrRoles(String[] actorsOrRoles) {
+ Assert.notEmpty(actorsOrRoles, "actorsOrRoles must not be empty");
+ this.actorsOrRoles = actorsOrRoles;
+ }
+
+ /**
+ * Creates a new SoapEndpointInvocationChain based on the given endpoint, and the set interceptors, and
+ * actors/roles.
+ *
+ * @param endpoint the endpoint
+ * @param interceptors the endpoint interceptors
+ * @return the created invocation chain
+ * @see #setInterceptors(org.springframework.ws.server.EndpointInterceptor[])
+ * @see #setActorsOrRoles(String[])
+ */
+ protected final EndpointInvocationChain createEndpointInvocationChain(MessageContext messageContext,
+ Object endpoint,
+ EndpointInterceptor[] interceptors) {
+ return new SoapEndpointInvocationChain(endpoint, interceptors, actorsOrRoles);
+ }
+
+ protected String getLookupKeyForMessage(MessageContext messageContext) throws Exception {
+ if (messageContext.getRequest() instanceof SoapMessage) {
+ SoapMessage request = (SoapMessage) messageContext.getRequest();
+ String soapAction = request.getSoapAction();
+ if (StringUtils.hasLength(soapAction) && soapAction.charAt(0) == '"' &&
+ soapAction.charAt(soapAction.length() - 1) == '"') {
+ return soapAction.substring(1, soapAction.length() - 1);
+ }
+ else {
+ return soapAction;
+ }
+ }
+ else {
+ return null;
+ }
+ }
+
+ protected String getLookupKeyForMethod(Method method) {
+ SoapAction soapAction = AnnotationUtils.getAnnotation(method, SoapAction.class);
+ return soapAction != null ? soapAction.value() : null;
+ }
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsMessageSender.java b/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsMessageSender.java
index 7cb8f8e1..02f7fbc4 100644
--- a/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsMessageSender.java
+++ b/sandbox/src/main/java/org/springframework/ws/transport/jms/JmsMessageSender.java
@@ -17,6 +17,7 @@
package org.springframework.ws.transport.jms;
import java.io.IOException;
+import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueConnection;
@@ -30,7 +31,15 @@ import org.springframework.util.Assert;
import org.springframework.ws.transport.WebServiceConnection;
import org.springframework.ws.transport.WebServiceMessageSender;
-/** @author Arjen Poutsma */
+/**
+ * WebServiceMessageSender implementation that uses JMS {@link Queue}.
+ *
+ * This message sender sends the request message of the queue configured with either the queue or
+ * queueName property. It creates a temporary queue for the response message. For both request and response
+ * {@link BytesMessage}s are used.
+ *
+ * @author Arjen Poutsma
+ */
public class JmsMessageSender implements WebServiceMessageSender {
/** Default timeout for receive operations. */
diff --git a/sandbox/src/main/java/org/springframework/ws/transport/jms/WebServiceMessageReceiverMessageListener.java b/sandbox/src/main/java/org/springframework/ws/transport/jms/WebServiceMessageReceiverMessageListener.java
index f08bc295..77c39fea 100644
--- a/sandbox/src/main/java/org/springframework/ws/transport/jms/WebServiceMessageReceiverMessageListener.java
+++ b/sandbox/src/main/java/org/springframework/ws/transport/jms/WebServiceMessageReceiverMessageListener.java
@@ -30,7 +30,8 @@ import org.springframework.ws.transport.WebServiceMessageReceiver;
* Spring-2.0 {@link SessionAwareMessageListener} that can be used to handleMessage incoming JMS messages. Requires a
* {@link WebServiceMessageFactory} which is used to convert the incoming JMS {@link BytesMessage}s into a {@link
* WebServiceMessage}, and passes that context to the {@link WebServiceMessageReceiver} set with the property
- * messageReceiver. If a response is created, it is sent using a response JMS message.
+ * messageReceiver. If a response is created, it is sent using the {@link BytesMessage#getJMSReplyTo()
+ * reply to header} of the request message.
*
* @author Arjen Poutsma
* @see #setMessageFactory(org.springframework.ws.WebServiceMessageFactory)
diff --git a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportException.java b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportException.java
new file mode 100644
index 00000000..412455ec
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.transport.mail;
+
+import javax.jms.JMSException;
+import javax.mail.MessagingException;
+
+import org.springframework.ws.transport.TransportException;
+
+/** @author Arjen Poutsma */
+public class MailTransportException extends TransportException {
+
+ public MailTransportException(String msg) {
+ super(msg);
+ }
+
+ public MailTransportException(String msg, JMSException ex) {
+ super(msg + ": " + ex.getMessage());
+ }
+
+ public MailTransportException(MessagingException ex) {
+ super(ex.getMessage());
+ }
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportOutputStream.java b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportOutputStream.java
index 07a5468e..c72c26a2 100644
--- a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportOutputStream.java
+++ b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportOutputStream.java
@@ -23,9 +23,7 @@ import javax.mail.MessagingException;
import org.springframework.ws.transport.TransportOutputStream;
-/**
- * @author Arjen Poutsma
- */
+/** @author Arjen Poutsma */
public class MailTransportOutputStream extends TransportOutputStream {
private final Message message;
@@ -51,4 +49,5 @@ public class MailTransportOutputStream extends TransportOutputStream {
throw new IOException(ex.getMessage());
}
}
+
}
diff --git a/sandbox/src/main/java/org/springframework/ws/transport/support/AbstractMessagingContainer.java b/sandbox/src/main/java/org/springframework/ws/transport/support/AbstractMessagingContainer.java
new file mode 100644
index 00000000..557131f6
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/transport/support/AbstractMessagingContainer.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.transport.support;
+
+import org.springframework.beans.factory.BeanNameAware;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.context.Lifecycle;
+import org.springframework.core.task.SimpleAsyncTaskExecutor;
+import org.springframework.core.task.TaskExecutor;
+import org.springframework.util.ClassUtils;
+
+/**
+ * Abstract base class for standalone, server-side transport objects. Contains a Spring {@link TaskExecutor}, and
+ * various lifecycle callbacks.
+ *
+ * @author Arjen Poutsma
+ */
+public abstract class AbstractMessagingContainer extends SimpleWebServiceMessageReceiverObjectSupport
+ implements Lifecycle, DisposableBean, BeanNameAware {
+
+ /** Default thread name prefix. */
+ public final String DEFAULT_THREAD_NAME_PREFIX = ClassUtils.getShortName(getClass()) + "-";
+
+ private volatile boolean active = false;
+
+ private boolean autoStartup = true;
+
+ private boolean running = false;
+
+ private final Object lifecycleMonitor = new Object();
+
+ private TaskExecutor taskExecutor;
+
+ private String beanName;
+
+ /**
+ * Set whether to automatically start the listener after initialization.
+ *
+ * Default is true; set this to false to allow for manual startup.
+ */
+ public void setAutoStartup(boolean autoStartup) {
+ this.autoStartup = autoStartup;
+ }
+
+ /**
+ * Set the Spring {@link TaskExecutor} to use for running the listener threads. Default is {@link
+ * SimpleAsyncTaskExecutor}, starting up a number of new threads.
+ *
+ * Specify an alternative task executor for integration with an existing thread pool, such as the {@link
+ * org.springframework.scheduling.commonj.WorkManagerTaskExecutor} to integrate with WebSphere or WebLogic.
+ */
+ public void setTaskExecutor(TaskExecutor taskExecutor) {
+ this.taskExecutor = taskExecutor;
+ }
+
+ /** Returns the task executor. */
+ public TaskExecutor getTaskExecutor() {
+ return taskExecutor;
+ }
+
+ public void setBeanName(String beanName) {
+ this.beanName = beanName;
+ }
+
+ /** 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 (lifecycleMonitor) {
+ return active;
+ }
+ }
+
+ /** Return whether this server is currently running, that is, whether it has been started and not stopped yet. */
+ public final boolean isRunning() {
+ synchronized (lifecycleMonitor) {
+ return running;
+ }
+ }
+
+ /**
+ * Create a default TaskExecutor. Called if no explicit TaskExecutor has been specified.
+ *
+ * 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 = beanName != null ? beanName + "-" : DEFAULT_THREAD_NAME_PREFIX;
+ return new SimpleAsyncTaskExecutor(threadNamePrefix);
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ super.afterPropertiesSet();
+ if (taskExecutor == null) {
+ taskExecutor = createDefaultTaskExecutor();
+ }
+ activate();
+ }
+
+ /**
+ * Calls shutdown when the BeanFactory destroys the server instance.
+ *
+ * @see #shutdown()
+ */
+ public void destroy() {
+ shutdown();
+ }
+
+ /** Initialize this server. Starts the server if autoStartup hasn't been turned off. */
+ public final void activate() throws Exception {
+ synchronized (lifecycleMonitor) {
+ active = true;
+ lifecycleMonitor.notifyAll();
+ }
+ onActivate();
+ if (autoStartup) {
+ start();
+ }
+ }
+
+ /** Start this server. */
+ public final void start() {
+ synchronized (lifecycleMonitor) {
+ running = true;
+ lifecycleMonitor.notifyAll();
+ }
+ onStart();
+ }
+
+ /** Stop this server. */
+ public final void stop() {
+ synchronized (lifecycleMonitor) {
+ running = false;
+ lifecycleMonitor.notifyAll();
+ }
+ onStop();
+ }
+
+ /** Shut down the registered listeners and close this listener container. */
+ public final void shutdown() {
+ synchronized (lifecycleMonitor) {
+ running = false;
+ active = false;
+ lifecycleMonitor.notifyAll();
+ }
+ onShutdown();
+ }
+
+ protected abstract void onActivate() throws Exception;
+
+ protected abstract void onStart();
+
+ protected abstract void onStop();
+
+ protected abstract void onShutdown();
+
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessageSender.java b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessageSender.java
new file mode 100644
index 00000000..637dc5a2
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessageSender.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.transport.tcp;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+import org.springframework.ws.transport.WebServiceConnection;
+import org.springframework.ws.transport.WebServiceMessageSender;
+
+/** @author Arjen Poutsma */
+public class TcpMessageSender implements WebServiceMessageSender, InitializingBean {
+
+ private InetAddress address;
+
+ private int port = -1;
+
+ private int timeOut = 1000;
+
+ /** Sets the port the sender will connect to. */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ /** Sets the amount of milliseconds before the tcp connection will timeout. */
+ public void setTimeOut(int timeOut) {
+ this.timeOut = timeOut;
+ }
+
+ /**
+ * Sets the internet address the client will connect to.
+ *
+ * @throws java.net.UnknownHostException when the given address is not known
+ */
+ public void setAddress(String address) throws UnknownHostException {
+ this.address = InetAddress.getByName(address);
+ }
+
+ public WebServiceConnection createConnection() throws IOException {
+ Socket socket = new Socket();
+ SocketAddress socketAddress = new InetSocketAddress(address, port);
+ socket.connect(socketAddress, timeOut);
+ return new TcpSendingWebServiceConnection(socket);
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ if (port == -1) {
+ throw new IllegalArgumentException("port is required");
+ }
+ Assert.notNull(address, "address is required");
+
+ }
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessagingContainer.java b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessagingContainer.java
new file mode 100644
index 00000000..9f05db5d
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpMessagingContainer.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.transport.tcp;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import org.springframework.ws.transport.WebServiceConnection;
+import org.springframework.ws.transport.support.AbstractMessagingContainer;
+
+/** @author Arjen Poutsma */
+public class TcpMessagingContainer extends AbstractMessagingContainer {
+
+ private ServerSocket serverSocket;
+
+ private InetAddress bindAddress;
+
+ private int backlog = -1;
+
+ private int port = -1;
+
+ /** Sets the port the server will bind to. */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ /** Sets the server back log. */
+ public void setBacklog(int backlog) {
+ this.backlog = backlog;
+ }
+
+ /**
+ * Sets the local internet address the server will bind to. By default, it will accept connections on any/all local
+ * addresses.
+ *
+ * @throws java.net.UnknownHostException when the given address is not known
+ * @see java.net.ServerSocket#ServerSocket(int,int,java.net.InetAddress)
+ */
+ public void setBindAddress(String bindAddress) throws UnknownHostException {
+ this.bindAddress = InetAddress.getByName(bindAddress);
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ super.afterPropertiesSet();
+ if (port == -1) {
+ throw new IllegalArgumentException("port is required");
+ }
+ openServerSocket();
+ }
+
+ protected void onActivate() throws IOException {
+ openServerSocket();
+ }
+
+ protected void onStart() {
+ if (logger.isInfoEnabled()) {
+ logger.info("Starting tcp server [" + serverSocket.getLocalSocketAddress() + "]");
+ }
+ getTaskExecutor().execute(new SocketAcceptingRunnable());
+ }
+
+ protected void onStop() {
+ if (logger.isInfoEnabled()) {
+ logger.info("Stopping tcp server [" + serverSocket.getLocalSocketAddress() + "]");
+ }
+ }
+
+ protected void onShutdown() {
+ if (logger.isInfoEnabled()) {
+ logger.info("Shutting down tcp server [" + serverSocket.getLocalSocketAddress() + "]");
+ }
+ closeServerSocket();
+ }
+
+ /**
+ * Establish a shared ServerSocket for this server.
+ *
+ * The default implementation delegates to refreshSharedConnection, which does one immediate attempt
+ * and throws an exception if it fails. Can be overridden to have a recovery proces in place, retrying until a
+ * ServerSocket can be successfully established.
+ *
+ * @see #refreshServerSocket()
+ */
+ protected void openServerSocket() throws IOException {
+ refreshServerSocket();
+ }
+
+ /**
+ * Refresh the shared ServerSocket that this server holds.
+ *
+ * Called on startup and also after an infrastructure exception that occured during listener setup and/or
+ * execution.
+ */
+ protected final void refreshServerSocket() throws IOException {
+ closeServerSocket();
+ serverSocket = new ServerSocket(port, backlog, bindAddress);
+ }
+
+ protected void closeServerSocket() {
+ if (serverSocket == null) {
+ return;
+ }
+ try {
+ serverSocket.close();
+ }
+ catch (IOException ex) {
+ logger.debug("Could not close ServerSocket", ex);
+ }
+ }
+
+ private class SocketAcceptingRunnable implements Runnable {
+
+ public void run() {
+ while (isRunning()) {
+ try {
+ Socket socket = serverSocket.accept();
+ TcpRequestHandler handler = new TcpRequestHandler(socket);
+ getTaskExecutor().execute(handler);
+ }
+ catch (InterruptedIOException ex) {
+ logger.warn(ex);
+ }
+ catch (IOException ex) {
+ logger.warn("Could not accept incoming connection: " + ex.getMessage());
+ }
+ }
+ }
+ }
+
+ private class TcpRequestHandler implements Runnable {
+
+ private final Socket socket;
+
+ public TcpRequestHandler(Socket socket) {
+ this.socket = socket;
+ }
+
+ public void run() {
+ WebServiceConnection connection = new TcpReceivingWebServiceConnection(socket);
+ try {
+ handleConnection(connection);
+ }
+ catch (Exception ex) {
+ logger.warn("Could not handle request", ex);
+ }
+ }
+ }
+
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpReceivingWebServiceConnection.java b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpReceivingWebServiceConnection.java
new file mode 100644
index 00000000..b57babe7
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpReceivingWebServiceConnection.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.transport.tcp;
+
+import java.io.FilterInputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.springframework.util.Assert;
+import org.springframework.ws.transport.AbstractReceivingWebServiceConnection;
+
+/** @author Arjen Poutsma */
+public class TcpReceivingWebServiceConnection extends AbstractReceivingWebServiceConnection {
+
+ private final Socket socket;
+
+ public TcpReceivingWebServiceConnection(Socket socket) {
+ Assert.notNull(socket, "socket must not be null");
+ this.socket = socket;
+ }
+
+ public void close() throws IOException {
+ socket.close();
+ }
+
+ protected Iterator getRequestHeaderNames() throws IOException {
+ return Collections.EMPTY_LIST.iterator();
+ }
+
+ protected Iterator getRequestHeaders(String name) throws IOException {
+ return Collections.EMPTY_LIST.iterator();
+ }
+
+ protected InputStream getRequestInputStream() throws IOException {
+ return new FilterInputStream(socket.getInputStream()) {
+
+ public void close() throws IOException {
+ // don't close the socket
+ socket.shutdownInput();
+ }
+ };
+ }
+
+ protected void addResponseHeader(String name, String value) throws IOException {
+ }
+
+ protected OutputStream getResponseOutputStream() throws IOException {
+ return new FilterOutputStream(socket.getOutputStream()) {
+
+ public void close() throws IOException {
+ // don't close the socket
+ socket.shutdownOutput();
+ }
+ };
+ }
+
+ protected void sendResponse() throws IOException {
+ }
+
+
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpSendingWebServiceConnection.java b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpSendingWebServiceConnection.java
new file mode 100644
index 00000000..005ec35c
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpSendingWebServiceConnection.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.transport.tcp;
+
+import java.io.FilterInputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.springframework.util.Assert;
+import org.springframework.ws.transport.AbstractSendingWebServiceConnection;
+
+/** @author Arjen Poutsma */
+public class TcpSendingWebServiceConnection extends AbstractSendingWebServiceConnection {
+
+ private final Socket socket;
+
+ public TcpSendingWebServiceConnection(Socket socket) {
+ Assert.notNull(socket, "socket must not be null");
+ this.socket = socket;
+ }
+
+ public void close() throws IOException {
+ socket.close();
+ }
+
+ protected void addRequestHeader(String name, String value) throws IOException {
+ }
+
+ protected OutputStream getRequestOutputStream() throws IOException {
+ return new FilterOutputStream(socket.getOutputStream()) {
+
+ public void close() throws IOException {
+ // don't close the socket
+ socket.shutdownOutput();
+ }
+ };
+ }
+
+ protected void sendRequest() throws IOException {
+ }
+
+ protected boolean hasResponse() throws IOException {
+ return true;
+ }
+
+ protected Iterator getResponseHeaderNames() throws IOException {
+ return Collections.EMPTY_LIST.iterator();
+ }
+
+ protected Iterator getResponseHeaders(String name) throws IOException {
+ return Collections.EMPTY_LIST.iterator();
+ }
+
+ protected InputStream getResponseInputStream() throws IOException {
+ return new FilterInputStream(socket.getInputStream()) {
+
+ public void close() throws IOException {
+ // don't close the socket
+ socket.shutdownInput();
+ }
+ };
+ }
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpTransportException.java b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpTransportException.java
new file mode 100644
index 00000000..a1797367
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/transport/tcp/TcpTransportException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.transport.tcp;
+
+import java.io.IOException;
+
+import org.springframework.ws.transport.TransportException;
+
+/** @author Arjen Poutsma */
+public class TcpTransportException extends TransportException {
+
+ public TcpTransportException(String msg) {
+ super(msg);
+ }
+
+ public TcpTransportException(String msg, IOException ex) {
+ super(msg + ": " + ex.getMessage());
+ }
+
+ public TcpTransportException(IOException ex) {
+ super(ex.getMessage());
+ }
+}
diff --git a/sandbox/src/test/java/org/springframework/ws/server/endpoint/mapping/AnnotationEndpointMappingTest.java b/sandbox/src/test/java/org/springframework/ws/server/endpoint/mapping/AnnotationEndpointMappingTest.java
new file mode 100644
index 00000000..934210d3
--- /dev/null
+++ b/sandbox/src/test/java/org/springframework/ws/server/endpoint/mapping/AnnotationEndpointMappingTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.server.endpoint.mapping;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Method;
+
+import junit.framework.TestCase;
+import org.springframework.context.support.StaticApplicationContext;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.server.endpoint.MethodEndpoint;
+import org.springframework.ws.server.endpoint.annotation.Endpoint;
+
+/** TestCase for {@link AbstractAnnotationEndpointMapping} */
+public class AnnotationEndpointMappingTest extends TestCase {
+
+ private AbstractAnnotationEndpointMapping mapping;
+
+ private StaticApplicationContext applicationContext;
+
+ protected void setUp() throws Exception {
+ applicationContext = new StaticApplicationContext();
+ applicationContext.registerSingleton("mapping", MyAnnotationEndpointMapping.class);
+ applicationContext.registerSingleton("endpoint", MyEndpoint.class);
+ applicationContext.registerSingleton("other", OtherBean.class);
+ applicationContext.refresh();
+ mapping = (AbstractAnnotationEndpointMapping) applicationContext.getBean("mapping");
+ }
+
+ public void testRegistration() throws NoSuchMethodException {
+ MethodEndpoint endpoint = mapping.lookupEndpoint("arg");
+ assertNotNull("MethodEndpoint not registered", endpoint);
+ MethodEndpoint expected = new MethodEndpoint(applicationContext.getBean("endpoint"), "doIt", new Class[0]);
+ assertEquals("Invalid endpoint registered", expected, endpoint);
+
+ assertNull("Invalid endpoint registered", mapping.lookupEndpoint("arg2"));
+ }
+
+ private static class MyAnnotationEndpointMapping extends AbstractAnnotationEndpointMapping {
+
+ protected String getLookupKeyForMethod(Method method) {
+ MyAnnotation annotation = AnnotationUtils.getAnnotation(method, MyAnnotation.class);
+ if (annotation != null) {
+ return annotation.value();
+ }
+ else {
+ return null;
+ }
+ }
+
+ protected String getLookupKeyForMessage(MessageContext messageContext) throws Exception {
+ return "arg";
+ }
+ }
+
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface MyAnnotation {
+
+ String value();
+
+ }
+
+ @Endpoint
+ private static class MyEndpoint {
+
+ @MyAnnotation("arg")
+ public void doIt() {
+
+ }
+
+ }
+
+ private static class OtherBean {
+
+ @MyAnnotation("arg2")
+ public void doIt() {
+
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/sandbox/src/test/java/org/springframework/ws/server/endpoint/mapping/SimpleMethodEndpointMappingTest.java b/sandbox/src/test/java/org/springframework/ws/server/endpoint/mapping/SimpleMethodEndpointMappingTest.java
new file mode 100644
index 00000000..b029b4ba
--- /dev/null
+++ b/sandbox/src/test/java/org/springframework/ws/server/endpoint/mapping/SimpleMethodEndpointMappingTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.server.endpoint.mapping;
+
+import junit.framework.TestCase;
+import org.springframework.ws.MockWebServiceMessage;
+import org.springframework.ws.MockWebServiceMessageFactory;
+import org.springframework.ws.context.DefaultMessageContext;
+import org.springframework.ws.context.MessageContext;
+
+public class SimpleMethodEndpointMappingTest extends TestCase {
+
+ private SimpleMethodEndpointMapping mapping;
+
+ protected void setUp() throws Exception {
+ mapping = new SimpleMethodEndpointMapping();
+ mapping.setMethodPrefix("prefix");
+ mapping.setMethodSuffix("Suffix");
+ MyBean bean = new MyBean();
+ mapping.setEndpoints(new Object[]{bean});
+ mapping.afterPropertiesSet();
+ }
+
+ public void testRegistration() throws Exception {
+ assertNotNull("Endpoint not registered", mapping.lookupEndpoint("MyRequest"));
+ }
+
+ public void testGetLookupKeyForMessageNoNamespace() throws Exception {
+ MockWebServiceMessage request = new MockWebServiceMessage("");
+ MessageContext messageContext = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
+ String result = mapping.getLookupKeyForMessage(messageContext);
+ assertEquals("Invalid lookup key", "MyRequest", result);
+ }
+
+ public void testGetLookupKeyForMessageNamespace() throws Exception {
+ MockWebServiceMessage request =
+ new MockWebServiceMessage("");
+ MessageContext messageContext = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
+ String result = mapping.getLookupKeyForMessage(messageContext);
+ assertEquals("Invalid lookup key", "MyRequest", result);
+ }
+
+ private static class MyBean {
+
+ public void prefixMyRequestSuffix() {
+
+ }
+
+ public void request() {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/sandbox/src/test/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationEndpointMappingTest.java b/sandbox/src/test/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationEndpointMappingTest.java
new file mode 100644
index 00000000..806baf17
--- /dev/null
+++ b/sandbox/src/test/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationEndpointMappingTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.soap.server.endpoint.mapping;
+
+import junit.framework.TestCase;
+import org.springframework.context.support.StaticApplicationContext;
+import org.springframework.ws.server.endpoint.annotation.Endpoint;
+import org.springframework.ws.soap.server.endpoint.annotation.SoapAction;
+
+public class SoapActionAnnotationEndpointMappingTest extends TestCase {
+
+ private StaticApplicationContext applicationContext;
+
+ private SoapActionAnnotationEndpointMapping mapping;
+
+ protected void setUp() throws Exception {
+ applicationContext = new StaticApplicationContext();
+ applicationContext.registerSingleton("mapping", SoapActionAnnotationEndpointMapping.class);
+ applicationContext.registerSingleton("endpoint", MyEndpoint.class);
+ applicationContext.refresh();
+ mapping = (SoapActionAnnotationEndpointMapping) applicationContext.getBean("mapping");
+ }
+
+ public void testIt() {
+
+ }
+
+ @Endpoint
+ private static class MyEndpoint {
+
+ @SoapAction("http://springframework.org/spring-ws/action")
+ public void handleMessage() {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/sandbox/src/test/java/org/springframework/ws/transport/jms/SimpleTestingMessageReceiver.java b/sandbox/src/test/java/org/springframework/ws/transport/SimpleTestingMessageReceiver.java
similarity index 91%
rename from sandbox/src/test/java/org/springframework/ws/transport/jms/SimpleTestingMessageReceiver.java
rename to sandbox/src/test/java/org/springframework/ws/transport/SimpleTestingMessageReceiver.java
index 7597d253..b40d0948 100644
--- a/sandbox/src/test/java/org/springframework/ws/transport/jms/SimpleTestingMessageReceiver.java
+++ b/sandbox/src/test/java/org/springframework/ws/transport/SimpleTestingMessageReceiver.java
@@ -14,19 +14,19 @@
* limitations under the License.
*/
-package org.springframework.ws.transport.jms;
+package org.springframework.ws.transport;
import javax.xml.transform.Transformer;
import junit.framework.Assert;
import org.springframework.ws.context.MessageContext;
-import org.springframework.ws.transport.WebServiceMessageReceiver;
import org.springframework.xml.transform.TransformerObjectSupport;
public class SimpleTestingMessageReceiver extends TransformerObjectSupport implements WebServiceMessageReceiver {
public void receive(MessageContext messageContext) throws Exception {
Assert.assertNotNull("MessageContext is null", messageContext);
+ logger.info("Received message");
Transformer transformer = createTransformer();
transformer.transform(messageContext.getRequest().getPayloadSource(),
messageContext.getResponse().getPayloadResult());
diff --git a/sandbox/src/test/java/org/springframework/ws/transport/tcp/TcpServerIntegrationTest.java b/sandbox/src/test/java/org/springframework/ws/transport/tcp/TcpServerIntegrationTest.java
new file mode 100644
index 00000000..9e957a04
--- /dev/null
+++ b/sandbox/src/test/java/org/springframework/ws/transport/tcp/TcpServerIntegrationTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2007 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
+ *
+ * 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.springframework.ws.transport.tcp;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.Socket;
+import javax.xml.transform.stream.StreamResult;
+
+import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
+import org.springframework.ws.WebServiceMessageFactory;
+import org.springframework.ws.client.core.WebServiceTemplate;
+import org.springframework.ws.transport.WebServiceMessageSender;
+import org.springframework.xml.transform.StringSource;
+
+public class TcpServerIntegrationTest extends AbstractDependencyInjectionSpringContextTests {
+
+ private WebServiceMessageFactory messageFactory;
+
+ private WebServiceMessageSender messageSender;
+
+ public void setMessageFactory(WebServiceMessageFactory messageFactory) {
+ this.messageFactory = messageFactory;
+ }
+
+ public void setMessageSender(WebServiceMessageSender messageSender) {
+ this.messageSender = messageSender;
+ }
+
+ public static final String REQUEST =
+ "\n" +
+ " \n" +
+ " \n" +
+ " DIS\n" + " \n" +
+ " \n" + "";
+
+ public void testServer() throws IOException, InterruptedException {
+ Socket socket = new Socket("localhost", 9999);
+ Writer writer = null;
+ BufferedReader reader = null;
+ try {
+ writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
+ writer.write(REQUEST);
+ writer.flush();
+ socket.shutdownOutput();
+ reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ System.out.println(line);
+ }
+ }
+ finally {
+ socket.close();
+ }
+ }
+
+ public void testTemplate() throws Exception {
+ WebServiceTemplate template = new WebServiceTemplate(messageFactory, messageSender);
+ template.sendAndReceive(new StringSource(REQUEST), new StreamResult(System.out));
+ }
+
+ protected String[] getConfigLocations() {
+ return new String[]{"classpath:/org/springframework/ws/transport/tcp/applicationContext.xml"};
+ }
+
+}
\ No newline at end of file
diff --git a/sandbox/src/test/resources/org/springframework/ws/transport/jms/jms-receiver-applicationContext.xml b/sandbox/src/test/resources/org/springframework/ws/transport/jms/jms-receiver-applicationContext.xml
index e0ee3660..b65ff699 100644
--- a/sandbox/src/test/resources/org/springframework/ws/transport/jms/jms-receiver-applicationContext.xml
+++ b/sandbox/src/test/resources/org/springframework/ws/transport/jms/jms-receiver-applicationContext.xml
@@ -28,7 +28,7 @@
-
+
\ No newline at end of file
diff --git a/sandbox/src/test/resources/org/springframework/ws/transport/tcp/applicationContext.xml b/sandbox/src/test/resources/org/springframework/ws/transport/tcp/applicationContext.xml
new file mode 100644
index 00000000..449cb071
--- /dev/null
+++ b/sandbox/src/test/resources/org/springframework/ws/transport/tcp/applicationContext.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.springframework.context.Lifecycle
+
+
+
+
+
+
+
+