diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractMethodEndpointMapping.java b/core/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractMethodEndpointMapping.java
index 8f08d5d5..cd6a3754 100644
--- a/core/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractMethodEndpointMapping.java
+++ b/core/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractMethodEndpointMapping.java
@@ -112,7 +112,7 @@ public abstract class AbstractMethodEndpointMapping extends AbstractEndpointMapp
Assert.notNull(endpoint, "'endpoint' must not be null");
Method[] methods = getEndpointClass(endpoint).getMethods();
for (int i = 0; i < methods.length; i++) {
- if (JdkVersion.getMajorJavaVersion() >= JdkVersion.JAVA_15 && methods[i].isSynthetic() ||
+ if (JdkVersion.isAtLeastJava15() && methods[i].isSynthetic() ||
methods[i].getDeclaringClass().equals(Object.class)) {
continue;
}
diff --git a/sandbox/src/main/java/org/springframework/ws/soap/addressing/AbstractWsAddressingEndpointMapping.java b/sandbox/src/main/java/org/springframework/ws/soap/addressing/AbstractWsAddressingEndpointMapping.java
new file mode 100644
index 00000000..886e1194
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/soap/addressing/AbstractWsAddressingEndpointMapping.java
@@ -0,0 +1,210 @@
+/*
+ * 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.addressing;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Iterator;
+import javax.xml.transform.TransformerException;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.core.JdkVersion;
+import org.springframework.util.Assert;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.server.EndpointInterceptor;
+import org.springframework.ws.server.EndpointInvocationChain;
+import org.springframework.ws.server.EndpointMapping;
+import org.springframework.ws.soap.SoapHeader;
+import org.springframework.ws.soap.SoapHeaderElement;
+import org.springframework.ws.soap.SoapMessage;
+import org.springframework.ws.soap.addressing.messageid.MessageIdStrategy;
+import org.springframework.ws.soap.addressing.messageid.RandomGuidMessageIdStrategy;
+import org.springframework.ws.soap.addressing.messageid.UuidMessageIdStrategy;
+import org.springframework.ws.soap.server.SoapEndpointInvocationChain;
+import org.springframework.ws.soap.server.SoapEndpointMapping;
+import org.springframework.ws.transport.WebServiceMessageSender;
+import org.springframework.xml.transform.TransformerObjectSupport;
+
+/**
+ * Abstract base class for {@link EndpointMapping} implementations that implement WS-Addressing.
+ *
+ * @author Arjen Poutsma
+ * @since 1.5.0
+ */
+public abstract class AbstractWsAddressingEndpointMapping extends TransformerObjectSupport
+ implements SoapEndpointMapping, InitializingBean {
+
+ private String[] actorsOrRoles;
+
+ private boolean isUltimateReceiver = true;
+
+ private MessageIdStrategy messageIdStrategy;
+
+ private WebServiceMessageSender[] messageSenders;
+
+ private WsAddressingVersion[] versions;
+
+ private EndpointInterceptor[] preInterceptors = new EndpointInterceptor[0];
+
+ private EndpointInterceptor[] postInterceptors = new EndpointInterceptor[0];
+
+ /** Protected constructor. Initializes the default settings. */
+ protected AbstractWsAddressingEndpointMapping() {
+ initDefaultStrategies();
+ }
+
+ /**
+ * Initializes the default implementation for this mapping's strategies: the {@link WsAddressing200408} and {@link
+ * WsAddressing200605} versions of the specication, and the {@link UuidMessageIdStrategy} on Java 5 and higher; the
+ * {@link RandomGuidMessageIdStrategy} on Java 1.4.
+ */
+ protected void initDefaultStrategies() {
+ this.versions = new WsAddressingVersion[]{new WsAddressing200408(), new WsAddressing200605()};
+ if (JdkVersion.isAtLeastJava15()) {
+ messageIdStrategy = new UuidMessageIdStrategy();
+ }
+ else {
+ messageIdStrategy = new RandomGuidMessageIdStrategy();
+ }
+ }
+
+ 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;
+ }
+
+ public final void setUltimateReceiver(boolean ultimateReceiver) {
+ this.isUltimateReceiver = ultimateReceiver;
+ }
+
+ /**
+ * Set additional interceptors to be applied before the implicit WS-Addressing interceptor, e.g.
+ * XwsSecurityInterceptor.
+ */
+ public final void setPreInterceptors(EndpointInterceptor[] preInterceptors) {
+ Assert.notNull(preInterceptors, "'preInterceptors' must not be null");
+ this.preInterceptors = preInterceptors;
+ }
+
+ /**
+ * Set additional interceptors to be applied after the implicit WS-Addressing interceptor, e.g.
+ * PayloadLoggingInterceptor.
+ */
+ public final void setPostInterceptors(EndpointInterceptor[] postInterceptors) {
+ Assert.notNull(postInterceptors, "'postInterceptors' must not be null");
+ this.postInterceptors = postInterceptors;
+ }
+
+ /**
+ * Sets the message id provider used for creating WS-Addressing MessageIds.
+ *
null if no specific
+ * one is found. This template method is called by {@link #getEndpoint(MessageContext)}.
+ *
+ * @param map the message addressing properties
+ * @return the endpoint, or null
+ */
+ protected abstract Object getEndpointInternal(MessageAddressingProperties map);
+
+ protected URI getResponseAction(Object endpoint) {
+ return null;
+ }
+
+ protected URI getFaultAction(Object endpoint) {
+ return null;
+ }
+
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/soap/addressing/AbstractWsAddressingMethodEndpointMapping.java b/sandbox/src/main/java/org/springframework/ws/soap/addressing/AbstractWsAddressingMethodEndpointMapping.java
new file mode 100644
index 00000000..2518854d
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/soap/addressing/AbstractWsAddressingMethodEndpointMapping.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2008 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.addressing;
+
+import java.lang.reflect.Method;
+import java.net.URI;
+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.Assert;
+import org.springframework.ws.server.endpoint.MethodEndpoint;
+
+/**
+ * @author Arjen Poutsma
+ * @since 1.5.0
+ */
+public abstract class AbstractWsAddressingMethodEndpointMapping extends AbstractWsAddressingEndpointMapping {
+
+ /** Keys are URI Actions, values are {@link MethodEndpoint}s. */
+ private final Map endpointMap = new HashMap();
+
+ protected Object getEndpointInternal(MessageAddressingProperties map) {
+ URI action = map.getAction();
+ if (logger.isDebugEnabled()) {
+ logger.debug("Looking up endpoint for [" + key + "]");
+ }
+ return lookupEndpoint(key);
+
+ //TODO implement
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ /**
+ * Looks up an endpoint instance for the given Action URI.
+ *
+ * @param action the Addressing action being mapped to
+ * @return the associated endpoint instance, or null if not found
+ */
+ protected MethodEndpoint lookupEndpoint(URI action) {
+ return (MethodEndpoint) endpointMap.get(action);
+ }
+
+ /**
+ * Register the given endpoint instance under the action URI.
+ *
+ * @param action the lookup key
+ * @param endpoint the method endpoint instance
+ * @throws BeansException if the endpoint could not be registered
+ */
+ protected void registerEndpoint(URI action, MethodEndpoint endpoint) throws BeansException {
+ Object mappedEndpoint = endpointMap.get(action);
+ if (mappedEndpoint != null) {
+ throw new ApplicationContextException("Cannot map endpoint [" + endpoint + "] on action [" + action +
+ "]: there's already endpoint [" + mappedEndpoint + "] mapped");
+ }
+ if (endpoint == null) {
+ throw new ApplicationContextException("Could not find endpoint for action [" + action + "]");
+ }
+ endpointMap.put(action, endpoint);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Mapped action [" + action + "] 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 #getActionForMethod(java.lang.reflect.Method)} for each. If this returns a string, the method is
+ * registered using {@link #registerEndpoint(URI, MethodEndpoint)}.
+ *
+ * @see #getActionForMethod (java.lang.reflect.Method)
+ */
+ protected void registerMethods(Object endpoint) {
+ Assert.notNull(endpoint, "'endpoint' must not be null");
+ 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;
+ }
+ URI action = getActionForMethod(methods[i]);
+ if (action != null) {
+ registerEndpoint(action, new MethodEndpoint(endpoint, methods[i]));
+ }
+ }
+ }
+
+ /**
+ * Returns the the action URI for the given method. Returns null if the method is not to be registered,
+ * which is the default.
+ *
+ * @param method the method
+ * @return the action URI, or null if the method is not to be registered
+ */
+ protected URI getActionForMethod(Method method) {
+ return null;
+ }
+
+ /**
+ * Return the class or interface to use for method reflection.
+ *
+ * Default implementation delegates to {@link AopUtils#getTargetClass(Object)}.
+ *
+ * @param endpoint the bean instance (might be an AOP proxy)
+ * @return the bean class to expose
+ */
+ protected Class getEndpointClass(Object endpoint) {
+ return AopUtils.getTargetClass(endpoint);
+ }
+
+
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/soap/addressing/AbstractWsAddressingVersion.java b/sandbox/src/main/java/org/springframework/ws/soap/addressing/AbstractWsAddressingVersion.java
index 179d694b..0af7bd2d 100644
--- a/sandbox/src/main/java/org/springframework/ws/soap/addressing/AbstractWsAddressingVersion.java
+++ b/sandbox/src/main/java/org/springframework/ws/soap/addressing/AbstractWsAddressingVersion.java
@@ -190,6 +190,10 @@ public abstract class AbstractWsAddressingVersion extends TransformerObjectSuppo
SoapHeaderElement to = header.addHeaderElement(getToName());
to.setText(map.getTo().toString());
to.setMustUnderstand(true);
+ if (map.getAction() != null) {
+ SoapHeaderElement action = header.addHeaderElement(getActionName());
+ action.setText(map.getAction().toString());
+ }
try {
Transformer transformer = createTransformer();
for (Iterator iterator = map.getReferenceParameters().iterator(); iterator.hasNext();) {
diff --git a/sandbox/src/main/java/org/springframework/ws/soap/addressing/AddressingEndpointInterceptor.java b/sandbox/src/main/java/org/springframework/ws/soap/addressing/AddressingEndpointInterceptor.java
new file mode 100644
index 00000000..91556904
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/soap/addressing/AddressingEndpointInterceptor.java
@@ -0,0 +1,181 @@
+/*
+ * 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.addressing;
+
+import java.io.IOException;
+import java.net.URI;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.util.Assert;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.soap.SoapHeaderElement;
+import org.springframework.ws.soap.SoapMessage;
+import org.springframework.ws.soap.addressing.messageid.MessageIdStrategy;
+import org.springframework.ws.soap.server.SoapEndpointInterceptor;
+import org.springframework.ws.transport.WebServiceConnection;
+import org.springframework.ws.transport.WebServiceMessageSender;
+
+/**
+ * {@link SoapEndpointInterceptor} implementation that deals with WS-Addressing headers. Stateful, and instatiated by
+ * the {@link AbstractWsAddressingEndpointMapping}.
+ *
+ * @author Arjen Poutsma
+ * @since 1.5.0
+ */
+class AddressingEndpointInterceptor implements SoapEndpointInterceptor {
+
+ private static final Log logger = LogFactory.getLog(AddressingEndpointInterceptor.class);
+
+ private final WsAddressingVersion version;
+
+ private final MessageIdStrategy messageIdStrategy;
+
+ private final WebServiceMessageSender[] messageSenders;
+
+ private URI replyAction;
+
+ private URI faultAction;
+
+ AddressingEndpointInterceptor(WsAddressingVersion version,
+ MessageIdStrategy messageIdStrategy,
+ WebServiceMessageSender[] messageSenders,
+ URI replyAction,
+ URI faultAction) {
+ Assert.notNull(version, "version must not be null");
+ Assert.notNull(messageIdStrategy, "messageIdStrategy must not be null");
+ if (messageSenders == null) {
+ messageSenders = new WebServiceMessageSender[0];
+ }
+ this.version = version;
+ this.messageIdStrategy = messageIdStrategy;
+ this.messageSenders = messageSenders;
+ this.replyAction = replyAction;
+ this.faultAction = faultAction;
+ }
+
+ public final boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {
+ Assert.isInstanceOf(SoapMessage.class, messageContext.getRequest());
+ SoapMessage request = (SoapMessage) messageContext.getRequest();
+ MessageAddressingProperties requestMap = version.getMessageAddressingProperties(request);
+ if (!requestMap.hasRequiredProperties()) {
+ version.addMessageAddressingHeaderRequiredFault((SoapMessage) messageContext.getResponse());
+ return false;
+ }
+ if (!requestMap.isValid() || messageIdStrategy.isDuplicate(requestMap.getMessageId())) {
+ version.addInvalidAddressingHeaderFault((SoapMessage) messageContext.getResponse());
+ return false;
+ }
+ return true;
+ }
+
+ public final boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
+ return handleResponseOrFault(messageContext, false);
+ }
+
+ public final boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception {
+ return handleResponseOrFault(messageContext, true);
+ }
+
+ private boolean handleResponseOrFault(MessageContext messageContext, boolean isFault) throws Exception {
+ Assert.isInstanceOf(SoapMessage.class, messageContext.getRequest());
+ Assert.isInstanceOf(SoapMessage.class, messageContext.getResponse());
+ MessageAddressingProperties requestMap =
+ version.getMessageAddressingProperties((SoapMessage) messageContext.getRequest());
+ EndpointReference replyEpr = !isFault ? requestMap.getReplyTo() : requestMap.getFaultTo();
+ if (handleNoneAddress(messageContext, replyEpr)) {
+ return false;
+ }
+ URI responseMessageId = getMessageId(messageContext);
+ URI action = !isFault ? replyAction : faultAction;
+ MessageAddressingProperties replyMap = requestMap.getReplyProperties(replyEpr, action, responseMessageId);
+ version.addAddressingHeaders((SoapMessage) messageContext.getResponse(), replyMap);
+ if (handleAnonymousAddress(messageContext, replyEpr)) {
+ return true;
+ }
+ else {
+ sendOutOfBand(messageContext, replyEpr);
+ return false;
+ }
+ }
+
+ private boolean handleNoneAddress(MessageContext messageContext, EndpointReference replyEpr) {
+ if (replyEpr == null || version.hasNoneAddress(replyEpr)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Request " + messageContext.getRequest() + "] has [" + replyEpr +
+ "] reply address; reply [" + messageContext.getResponse() + "] discarded");
+ }
+ messageContext.clearResponse();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean handleAnonymousAddress(MessageContext messageContext, EndpointReference replyEpr) {
+ if (version.hasAnonymousAddress(replyEpr)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Request " + messageContext.getRequest() + "] has [" + replyEpr +
+ "] reply address; sending in-band reply [" + messageContext.getResponse() + "]");
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void sendOutOfBand(MessageContext messageContext, EndpointReference replyEpr) throws IOException {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Request " + messageContext.getRequest() + "] has [" + replyEpr +
+ "] reply address; sending out-of-band reply [" + messageContext.getResponse() + "]");
+ }
+
+ boolean supported = false;
+ for (int i = 0; i < messageSenders.length; i++) {
+ if (messageSenders[i].supports(replyEpr.getAddress())) {
+ supported = true;
+ WebServiceConnection connection = null;
+ try {
+ connection = messageSenders[i].createConnection(replyEpr.getAddress());
+ connection.send(messageContext.getResponse());
+ break;
+ }
+ finally {
+ messageContext.clearResponse();
+ if (connection != null) {
+ connection.close();
+ }
+ }
+ }
+ }
+ if (!supported) {
+ logger.warn("Could not send out-of-band response to [" + replyEpr.getAddress() + "]. " +
+ "Configure WebServiceMessageSenders which support this uri.");
+ }
+ }
+
+ private URI getMessageId(MessageContext messageContext) {
+ URI responseMessageId = messageIdStrategy.newMessageId(messageContext);
+ if (logger.isTraceEnabled()) {
+ logger.trace("Generated reply MessageID [" + responseMessageId + "] for [" + messageContext + "]");
+ }
+ return responseMessageId;
+ }
+
+ public boolean understands(SoapHeaderElement header) {
+ return version.understands(header);
+ }
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/soap/addressing/EndpointReferenceEditor.java b/sandbox/src/main/java/org/springframework/ws/soap/addressing/EndpointReferenceEditor.java
new file mode 100644
index 00000000..9cf18d4e
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/soap/addressing/EndpointReferenceEditor.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2008 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.addressing;
+
+import java.beans.PropertyEditorSupport;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.springframework.util.StringUtils;
+
+/**
+ * Editor for EndpointReference, to directly populate a EPR property instead of using a String property as
+ * bridge.
+ *
+ * @author Arjen Poutsma
+ * @see EndpointReference
+ * @since 1.5.0
+ */
+public class EndpointReferenceEditor extends PropertyEditorSupport {
+
+ public void setAsText(String text) throws IllegalArgumentException {
+ if (StringUtils.hasText(text)) {
+ String uri = text.trim();
+ try {
+ URI address = new URI(uri);
+ setValue(new EndpointReference(address));
+ }
+ catch (URISyntaxException ex) {
+ throw new IllegalArgumentException("Invalid URI syntax: " + ex);
+ }
+ }
+ else {
+ setValue(null);
+ }
+ }
+
+ public String getAsText() {
+ EndpointReference value = (EndpointReference) getValue();
+ return (value != null ? value.toString() : "");
+ }
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/soap/addressing/WsAddressingActionEndpointMapping.java b/sandbox/src/main/java/org/springframework/ws/soap/addressing/WsAddressingActionEndpointMapping.java
new file mode 100644
index 00000000..f45c0fe2
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/soap/addressing/WsAddressingActionEndpointMapping.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2008 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.addressing;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.util.Assert;
+
+/**
+ * Implementation of the EndpointMapping interface to map from WS-Addressing Action headers to
+ * endpoint beans. Supports both mapping to bean instances and mapping to bean names.
+ *
+ * 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 WS_ADDRESSING_ACTION=ENDPOINT_BEAN_NAME. + * + * If set, the
destination property is used suitable for further endpoint determination. can be used to set
+ * an EndpointReference destination (i.e. the To element). If this propert
+ *
+ * @author Arjen Poutsma
+ * @since 1.5.0
+ */
+public class WsAddressingActionEndpointMapping extends AbstractWsAddressingEndpointMapping
+ implements ApplicationContextAware {
+
+ // keys are action URIs, values are endpoints
+ private final Map endpointMap = new HashMap();
+
+ // contents will be copied over to endpointMap
+ private final Map temporaryEndpointMap = new HashMap();
+
+ private URI destination;
+
+ private ApplicationContext applicationContext;
+
+ public void setMappings(Properties mappings) throws URISyntaxException {
+ setEndpointMap(mappings);
+ }
+
+ public void setEndpointMap(Map actionMap) throws URISyntaxException {
+ Iterator it = actionMap.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry entry = (Map.Entry) it.next();
+ URI action;
+ if (entry.getKey() instanceof String) {
+ action = new URI((String) entry.getKey());
+ }
+ else if (entry.getKey() instanceof URI) {
+ action = (URI) entry.getKey();
+ }
+ else {
+ throw new IllegalArgumentException("Invalid key [" + entry.getKey() + "]; expected String or URI");
+ }
+ this.temporaryEndpointMap.put(action, entry.getValue());
+ }
+ }
+
+ public void setDestination(URI destination) {
+ this.destination = destination;
+ }
+
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.applicationContext = applicationContext;
+ registerEndpoints(temporaryEndpointMap);
+ }
+
+ protected Object getEndpointInternal(MessageAddressingProperties map) {
+ // MAP address much match the defined EPR address
+ if (destination != null && !destination.equals(map.getTo())) {
+ return null;
+ }
+ return endpointMap.get(map.getAction());
+ }
+
+ protected void registerEndpoints(Map endpointMap) throws BeansException {
+ if (endpointMap.isEmpty()) {
+ logger.warn("Neither 'actionMap' nor 'mappings' set on WsAddressingActionEndpointMapping");
+ }
+ else {
+ Iterator it = endpointMap.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry entry = (Map.Entry) it.next();
+ URI action = (URI) entry.getKey();
+ Object endpoint = entry.getValue();
+ // Remove whitespace from endpoint bean name.
+ if (endpoint instanceof String) {
+ endpoint = ((String) endpoint).trim();
+ }
+ registerEndpoint(action, endpoint);
+ }
+ }
+ }
+
+ protected void registerEndpoint(URI action, Object endpoint) throws BeansException, IllegalStateException {
+ Assert.notNull(action, "Action must not be null");
+ Assert.notNull(endpoint, "Endpoint object must not be null");
+ Object resolvedEndpoint = endpoint;
+
+ if (endpoint instanceof String) {
+ String endpointName = (String) endpoint;
+ if (applicationContext.isSingleton(endpointName)) {
+ resolvedEndpoint = applicationContext.getBean(endpointName);
+ }
+ }
+ Object mappedEndpoint = this.endpointMap.get(action);
+ if (mappedEndpoint != null) {
+ if (mappedEndpoint != resolvedEndpoint) {
+ throw new IllegalStateException("Cannot map endpoint [" + endpoint + "] to action [" + action +
+ "]: There is already endpoint [" + resolvedEndpoint + "] mapped.");
+ }
+ }
+ else {
+ this.endpointMap.put(action, resolvedEndpoint);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Mapped Action [" + action + "] onto endpoint [" + resolvedEndpoint + "]");
+ }
+ }
+ }
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/soap/addressing/annotation/Action.java b/sandbox/src/main/java/org/springframework/ws/soap/addressing/annotation/Action.java
new file mode 100644
index 00000000..dd1420f1
--- /dev/null
+++ b/sandbox/src/main/java/org/springframework/ws/soap/addressing/annotation/Action.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2008 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.addressing.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;
+
+/**
+ * @author Arjen Poutsma
+ * @since 1.5.0
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Action {
+
+}
diff --git a/sandbox/src/test/java/org/springframework/ws/soap/addressing/AbstractWsAddressingInterceptorTestCase.java b/sandbox/src/test/java/org/springframework/ws/soap/addressing/AbstractAddressingInterceptorTestCase.java
similarity index 91%
rename from sandbox/src/test/java/org/springframework/ws/soap/addressing/AbstractWsAddressingInterceptorTestCase.java
rename to sandbox/src/test/java/org/springframework/ws/soap/addressing/AbstractAddressingInterceptorTestCase.java
index 02e422b8..83b3cd48 100644
--- a/sandbox/src/test/java/org/springframework/ws/soap/addressing/AbstractWsAddressingInterceptorTestCase.java
+++ b/sandbox/src/test/java/org/springframework/ws/soap/addressing/AbstractAddressingInterceptorTestCase.java
@@ -19,9 +19,9 @@ import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
import org.springframework.ws.transport.WebServiceConnection;
import org.springframework.ws.transport.WebServiceMessageSender;
-public abstract class AbstractWsAddressingInterceptorTestCase extends AbstractWsAddressingTestCase {
+public abstract class AbstractAddressingInterceptorTestCase extends AbstractWsAddressingTestCase {
- private WsAddressingEndpointInterceptor interceptor;
+ private AddressingEndpointInterceptor interceptor;
private MockControl strategyControl;
@@ -31,7 +31,10 @@ public abstract class AbstractWsAddressingInterceptorTestCase extends AbstractWs
strategyControl = MockControl.createControl(MessageIdStrategy.class);
strategyMock = (MessageIdStrategy) strategyControl.getMock();
strategyControl.expectAndDefaultReturn(strategyMock.isDuplicate(null), false);
- interceptor = new WsAddressingEndpointInterceptor(getVersion(), strategyMock, new WebServiceMessageSender[0]);
+ URI replyAction = new URI("urn:replyAction");
+ URI faultAction = new URI("urn:faultAction");
+ interceptor = new AddressingEndpointInterceptor(getVersion(), strategyMock, new WebServiceMessageSender[0],
+ replyAction, faultAction);
}
public void testUnderstands() throws Exception {
@@ -128,8 +131,10 @@ public abstract class AbstractWsAddressingInterceptorTestCase extends AbstractWs
MockControl senderControl = MockControl.createControl(WebServiceMessageSender.class);
WebServiceMessageSender senderMock = (WebServiceMessageSender) senderControl.getMock();
- interceptor = new WsAddressingEndpointInterceptor(getVersion(), strategyMock,
- new WebServiceMessageSender[]{senderMock});
+ URI replyAction = new URI("urn:replyAction");
+ URI faultAction = new URI("urn:replyAction");
+ interceptor = new AddressingEndpointInterceptor(getVersion(), strategyMock,
+ new WebServiceMessageSender[]{senderMock}, replyAction, faultAction);
MockControl connectionControl = MockControl.createControl(WebServiceConnection.class);
WebServiceConnection connectionMock = (WebServiceConnection) connectionControl.getMock();
diff --git a/sandbox/src/test/java/org/springframework/ws/soap/addressing/WsAddressingInterceptor200408Test.java b/sandbox/src/test/java/org/springframework/ws/soap/addressing/AddressingInterceptor200408Test.java
similarity index 80%
rename from sandbox/src/test/java/org/springframework/ws/soap/addressing/WsAddressingInterceptor200408Test.java
rename to sandbox/src/test/java/org/springframework/ws/soap/addressing/AddressingInterceptor200408Test.java
index c300b9cf..0a5da3ea 100644
--- a/sandbox/src/test/java/org/springframework/ws/soap/addressing/WsAddressingInterceptor200408Test.java
+++ b/sandbox/src/test/java/org/springframework/ws/soap/addressing/AddressingInterceptor200408Test.java
@@ -4,7 +4,7 @@
package org.springframework.ws.soap.addressing;
-public class WsAddressingInterceptor200408Test extends AbstractWsAddressingInterceptorTestCase {
+public class AddressingInterceptor200408Test extends AbstractAddressingInterceptorTestCase {
protected WsAddressingVersion getVersion() {
return new WsAddressing200408();
diff --git a/sandbox/src/test/java/org/springframework/ws/soap/addressing/WsAddressingInterceptor200605Test.java b/sandbox/src/test/java/org/springframework/ws/soap/addressing/AddressingInterceptor200605Test.java
similarity index 74%
rename from sandbox/src/test/java/org/springframework/ws/soap/addressing/WsAddressingInterceptor200605Test.java
rename to sandbox/src/test/java/org/springframework/ws/soap/addressing/AddressingInterceptor200605Test.java
index db468030..47169f44 100644
--- a/sandbox/src/test/java/org/springframework/ws/soap/addressing/WsAddressingInterceptor200605Test.java
+++ b/sandbox/src/test/java/org/springframework/ws/soap/addressing/AddressingInterceptor200605Test.java
@@ -4,7 +4,7 @@
package org.springframework.ws.soap.addressing;
-public class WsAddressingInterceptor200605Test extends AbstractWsAddressingInterceptorTestCase {
+public class AddressingInterceptor200605Test extends AbstractAddressingInterceptorTestCase {
protected WsAddressingVersion getVersion() {
return new WsAddressing200605();
diff --git a/sandbox/src/test/java/org/springframework/ws/soap/addressing/WsAddressingActionEndpointMappingTest.java b/sandbox/src/test/java/org/springframework/ws/soap/addressing/WsAddressingActionEndpointMappingTest.java
new file mode 100644
index 00000000..cad9585d
--- /dev/null
+++ b/sandbox/src/test/java/org/springframework/ws/soap/addressing/WsAddressingActionEndpointMappingTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright ${YEAR} 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.addressing;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.ws.context.DefaultMessageContext;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.server.EndpointInterceptor;
+import org.springframework.ws.server.EndpointInvocationChain;
+import org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor;
+import org.springframework.ws.soap.saaj.SaajSoapMessage;
+import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
+import org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor;
+
+public class WsAddressingActionEndpointMappingTest extends AbstractWsAddressingTestCase {
+
+ private WsAddressingActionEndpointMapping mapping;
+
+ private Endpoint1 endpoint1;
+
+ private Endpoint2 endpoint2;
+
+ protected void onSetUp() throws Exception {
+ mapping = new WsAddressingActionEndpointMapping();
+ Map map = new HashMap();
+ endpoint1 = new Endpoint1();
+ endpoint2 = new Endpoint2();
+ map.put("http://fabrikam123.example/mail/Delete", endpoint1);
+ map.put("http://fabrikam123.example/mail/Add", endpoint2);
+ mapping.setPreInterceptors(new EndpointInterceptor[]{new PayloadLoggingInterceptor()});
+ mapping.setPostInterceptors(new EndpointInterceptor[]{new PayloadValidatingInterceptor()});
+ mapping.setActionMap(map);
+ mapping.setApplicationContext(null);
+ mapping.afterPropertiesSet();
+ }
+
+ public void testMatch() throws Exception {
+ SaajSoapMessage message = loadSaajMessage("200408/valid.xml");
+ MessageContext messageContext = new DefaultMessageContext(message, new SaajSoapMessageFactory(messageFactory));
+
+ EndpointInvocationChain endpoint = mapping.getEndpoint(messageContext);
+ assertNotNull("No endpoint returned", endpoint);
+ assertEquals("Invalid endpoint returned", endpoint1, endpoint.getEndpoint());
+ EndpointInterceptor[] interceptors = endpoint.getInterceptors();
+ assertEquals("Invalid amount of interceptors returned", 3, interceptors.length);
+ assertTrue("Invalid first interceptor", interceptors[0] instanceof PayloadLoggingInterceptor);
+ assertTrue("Invalid first interceptor", interceptors[1] instanceof AddressingEndpointInterceptor);
+ assertTrue("Invalid first interceptor", interceptors[2] instanceof PayloadValidatingInterceptor);
+ }
+
+ public void testNoMatch() throws Exception {
+ SaajSoapMessage message = loadSaajMessage("200408/response-no-message-id.xml");
+ MessageContext messageContext = new DefaultMessageContext(message, new SaajSoapMessageFactory(messageFactory));
+
+ EndpointInvocationChain endpoint = mapping.getEndpoint(messageContext);
+ assertNull("Endpoint returned", endpoint);
+ }
+
+ private static class Endpoint1 {
+
+ }
+
+ private static class Endpoint2 {
+
+ }
+}
\ No newline at end of file
diff --git a/sandbox/src/test/resources/org/springframework/ws/soap/addressing/200408/response-anonymous.xml b/sandbox/src/test/resources/org/springframework/ws/soap/addressing/200408/response-anonymous.xml
index 8dde1531..99276263 100644
--- a/sandbox/src/test/resources/org/springframework/ws/soap/addressing/200408/response-anonymous.xml
+++ b/sandbox/src/test/resources/org/springframework/ws/soap/addressing/200408/response-anonymous.xml
@@ -4,6 +4,7 @@