Working on SWS-84 (WS-Addressing)

This commit is contained in:
Arjen Poutsma
2008-02-21 22:31:59 +00:00
parent 8927fb064b
commit 9712ebb290
16 changed files with 860 additions and 8 deletions

View File

@@ -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;
}

View File

@@ -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.
* <code>XwsSecurityInterceptor</code>.
*/
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.
* <code>PayloadLoggingInterceptor</code>.
*/
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.
* <p/>
* By default, the {@link UuidMessageIdStrategy} is used on Java 5 and higher, and the {@link
* RandomGuidMessageIdStrategy} on Java 1.4.
*/
public final void setMessageIdProvider(MessageIdStrategy messageIdStrategy) {
this.messageIdStrategy = messageIdStrategy;
}
public final void setMessageSenders(WebServiceMessageSender[] messageSenders) {
this.messageSenders = messageSenders;
}
/**
* Sets the WS-Addressing versions to be supported by this mapping.
* <p/>
* By default, this array is set to support {@link WsAddressing200408 the August 2004} and the {@link
* WsAddressing200605 May 2006} versions of the specification.
*/
public final void setVersions(WsAddressingVersion[] versions) {
this.versions = versions;
}
public void afterPropertiesSet() throws Exception {
if (logger.isInfoEnabled()) {
logger.info("Supporting versions " + Arrays.asList(versions));
}
}
public final EndpointInvocationChain getEndpoint(MessageContext messageContext) throws TransformerException {
Assert.isInstanceOf(SoapMessage.class, messageContext.getRequest());
SoapMessage request = (SoapMessage) messageContext.getRequest();
for (int i = 0; i < versions.length; i++) {
if (supports(versions[i], request)) {
MessageAddressingProperties requestMap = versions[i].getMessageAddressingProperties(request);
if (requestMap == null) {
return null;
}
Object endpoint = getEndpointInternal(requestMap);
if (endpoint == null) {
return null;
}
return getEndpointInvocationChain(endpoint, versions[i]);
}
}
return null;
}
/** Creates a {@link SoapEndpointInvocationChain} based on the given endpoint and {@link WsAddressingVersion}. */
private EndpointInvocationChain getEndpointInvocationChain(Object endpoint, WsAddressingVersion version) {
URI responseAction = getResponseAction(endpoint);
URI faultAction = getFaultAction(endpoint);
EndpointInterceptor[] interceptors =
new EndpointInterceptor[preInterceptors.length + postInterceptors.length + 1];
System.arraycopy(preInterceptors, 0, interceptors, 0, preInterceptors.length);
AddressingEndpointInterceptor interceptor = new AddressingEndpointInterceptor(version, messageIdStrategy,
messageSenders, responseAction, faultAction);
interceptors[preInterceptors.length] = interceptor;
System.arraycopy(postInterceptors, 0, interceptors, preInterceptors.length + 1, postInterceptors.length);
return new SoapEndpointInvocationChain(endpoint, interceptors, actorsOrRoles, isUltimateReceiver);
}
private boolean supports(WsAddressingVersion version, SoapMessage request) {
SoapHeader header = request.getSoapHeader();
if (header != null) {
for (Iterator iterator = header.examineAllHeaderElements(); iterator.hasNext();) {
SoapHeaderElement headerElement = (SoapHeaderElement) iterator.next();
if (version.understands(headerElement)) {
return true;
}
}
}
return false;
}
/**
* Lookup an endpoint for the given {@link MessageAddressingProperties}, returning <code>null</code> 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 <code>null</code>
*/
protected abstract Object getEndpointInternal(MessageAddressingProperties map);
protected URI getResponseAction(Object endpoint) {
return null;
}
protected URI getFaultAction(Object endpoint) {
return null;
}
}

View File

@@ -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 <code>null</code> 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 <code>null</code> if the method is not to be registered,
* which is the default.
*
* @param method the method
* @return the action URI, or <code>null</code> 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.
* <p/>
* 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);
}
}

View File

@@ -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();) {

View File

@@ -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);
}
}

View File

@@ -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 <code>EndpointReference</code>, 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() : "");
}
}

View File

@@ -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 <code>EndpointMapping</code> interface to map from WS-Addressing <code>Action</code> headers to
* endpoint beans. Supports both mapping to bean instances and mapping to bean names.
* <p/>
* The <code>endpointMap</code> property is suitable for populating the endpoint map with bean references, e.g. via the
* map element in XML bean definitions.
* <p/>
* Mappings to bean names can be set via the <code>mappings</code> property, in a form accepted by the
* <code>java.util.Properties</code> class, like as follows:
* <pre>
* http://www.springframework.org/spring-ws/samples/airline/BookFlight=bookFlightEndpoint
* http://www.springframework.org/spring-ws/samples/airline/GetFlights=getFlightsEndpoint
* </pre>
* The syntax is WS_ADDRESSING_ACTION=ENDPOINT_BEAN_NAME.
* <p/>
* If set, the <code>destination</code> property is used suitable for further endpoint determination. can be used to set
* an EndpointReference destination (i.e. the <code>To</code> 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 + "]");
}
}
}
}

View File

@@ -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 {
}

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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 {
}
}

View File

@@ -4,6 +4,7 @@
<wsa:MessageID>uid:1234</wsa:MessageID>
<wsa:RelatesTo>uuid:aaaabbbb-cccc-dddd-eeee-ffffffffffff</wsa:RelatesTo>
<wsa:To env:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
<wsa:Action>urn:replyAction</wsa:Action>
</env:Header>
<env:Body/>
</env:Envelope>

View File

@@ -4,6 +4,7 @@
<wsa:MessageID>uid:1234</wsa:MessageID>
<wsa:RelatesTo>uuid:aaaabbbb-cccc-dddd-eeee-ffffffffffff</wsa:RelatesTo>
<wsa:To env:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
<wsa:Action>urn:faultAction</wsa:Action>
</env:Header>
<env:Body>
<env:Fault>

View File

@@ -3,6 +3,7 @@
<wsa:MessageID>uid:1234</wsa:MessageID>
<wsa:RelatesTo>http://example.com/someuniquestring</wsa:RelatesTo>
<wsa:To env:mustUnderstand="true">http://www.w3.org/2005/08/addressing/anonymous</wsa:To>
<wsa:Action>urn:replyAction</wsa:Action>
</env:Header>
<env:Body/>
</env:Envelope>

View File

@@ -3,6 +3,7 @@
<wsa:MessageID>uid:1234</wsa:MessageID>
<wsa:RelatesTo>http://example.com/someuniquestring</wsa:RelatesTo>
<wsa:To env:mustUnderstand="true">http://www.w3.org/2005/08/addressing/anonymous</wsa:To>
<wsa:Action>urn:faultAction</wsa:Action>
</env:Header>
<env:Body>
<env:Fault>