Working on SWS-84 (WS-Addressing)

This commit is contained in:
Arjen Poutsma
2008-02-22 18:46:45 +00:00
parent c026c56bb9
commit 5466353beb
13 changed files with 568 additions and 533 deletions

View File

@@ -0,0 +1,97 @@
/*
* 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.util.HashMap;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;
/**
* @author Arjen Poutsma
* @since 1.5.0
*/
public abstract class AbstractActionEndpointMapping extends AbstractAddressingEndpointMapping
implements ApplicationContextAware {
// keys are action URIs, values are endpoints
private final Map endpointMap = new HashMap();
private ApplicationContext applicationContext;
/**
* Looks up an endpoint instance for the given action. All keys are tried in order.
*
* @param action the action URI
* @return the associated endpoint instance, or <code>null</code> if not found
*/
protected Object lookupEndpoint(URI action) {
return endpointMap.get(action);
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* Register the specified endpoint for the given action URI.
*
* @param action the action the bean should be mapped to
* @param endpoint the endpoint instance or endpoint bean name String (a bean name will automatically be resolved
* into the corresponding endpoint bean)
* @throws org.springframework.beans.BeansException
* if the endpoint couldn't be registered
* @throws IllegalStateException if there is a conflicting endpoint registered
*/
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 + "]");
}
}
}
protected final Object getEndpointInternal(MessageAddressingProperties map) {
return getEndpointInternal(map.getTo(), map.getAction());
}
protected abstract Object getEndpointInternal(URI to, URI action);
}

View File

@@ -0,0 +1,78 @@
/*
* 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 org.springframework.aop.support.AopUtils;
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 AbstractActionMethodEndpointMapping extends AbstractActionEndpointMapping {
/**
* Helper method that registers the methods of the given bean. This method iterates over the methods of the bean,
* and calls {@link #getActionForMethod(Method)} for each. If this returns a URI, the method is registered using
* {@link #registerEndpoint(URI, Object)}.
*
* @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

@@ -45,7 +45,7 @@ import org.springframework.xml.transform.TransformerObjectSupport;
* @author Arjen Poutsma
* @since 1.5.0
*/
public abstract class AbstractWsAddressingEndpointMapping extends TransformerObjectSupport
public abstract class AbstractAddressingEndpointMapping extends TransformerObjectSupport
implements SoapEndpointMapping, InitializingBean {
private String[] actorsOrRoles;
@@ -63,7 +63,7 @@ public abstract class AbstractWsAddressingEndpointMapping extends TransformerObj
private EndpointInterceptor[] postInterceptors = new EndpointInterceptor[0];
/** Protected constructor. Initializes the default settings. */
protected AbstractWsAddressingEndpointMapping() {
protected AbstractAddressingEndpointMapping() {
initDefaultStrategies();
}

View File

@@ -1,208 +0,0 @@
/*
* 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.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 AbstractWsAddressingMapping extends TransformerObjectSupport
implements SoapEndpointMapping, InitializingBean {
private String[] actorsOrRoles;
private boolean isUltimateReceiver = true;
private MessageIdStrategy messageIdStrategy;
private WebServiceMessageSender[] messageSenders;
private WsAddressingVersion[] versions;
private EndpointInterceptor[] preInterceptors;
private EndpointInterceptor[] postInterceptors;
private int wsAddressingInterceptorIdx = -1;
private EndpointInterceptor[] allInterceptors;
/** Protected constructor. Initializes the default settings. */
protected AbstractWsAddressingMapping() {
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) {
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) {
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 WS-Addressing " + 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 new SoapEndpointInvocationChain(endpoint, getAllEndpointInterceptors(versions[i]), actorsOrRoles,
isUltimateReceiver);
}
}
return null;
}
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;
}
private EndpointInterceptor[] getAllEndpointInterceptors(WsAddressingVersion version) {
// lazy init
if (allInterceptors == null) {
if (preInterceptors == null) {
preInterceptors = new EndpointInterceptor[0];
}
if (postInterceptors == null) {
postInterceptors = new EndpointInterceptor[0];
}
allInterceptors = new EndpointInterceptor[preInterceptors.length + postInterceptors.length + 1];
System.arraycopy(preInterceptors, 0, allInterceptors, 0, preInterceptors.length);
System.arraycopy(postInterceptors, 0, allInterceptors, preInterceptors.length + 1, postInterceptors.length);
}
allInterceptors[preInterceptors.length] =
new WsAddressingEndpointInterceptor(version, messageIdStrategy, messageSenders);
return allInterceptors;
}
/**
* 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);
}

View File

@@ -33,7 +33,7 @@ import org.springframework.ws.transport.WebServiceMessageSender;
/**
* {@link SoapEndpointInterceptor} implementation that deals with WS-Addressing headers. Stateful, and instatiated by
* the {@link AbstractWsAddressingEndpointMapping}.
* the {@link AbstractAddressingEndpointMapping}.
*
* @author Arjen Poutsma
* @since 1.5.0

View File

@@ -0,0 +1,94 @@
/*
* 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.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.StringUtils;
import org.springframework.ws.server.endpoint.MethodEndpoint;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.soap.addressing.annotation.Action;
import org.springframework.ws.soap.addressing.annotation.Address;
/**
* @author Arjen Poutsma
* @since 1.5.0
*/
public class AnnotationActionMethodEndpointMapping extends AbstractActionMethodEndpointMapping
implements BeanPostProcessor {
/** Returns the 'endpoint' annotation type. Default is {@link Endpoint}. */
protected Class<? extends Annotation> getEndpointAnnotationType() {
return Endpoint.class;
}
public final Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public final Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (getEndpointClass(bean).getAnnotation(getEndpointAnnotationType()) != null) {
registerMethods(bean);
}
return bean;
}
protected URI getActionForMethod(Method method) {
Action action = method.getAnnotation(Action.class);
if (action != null) {
try {
return new URI(action.value());
}
catch (URISyntaxException e) {
// ignore
}
}
return null;
}
protected Object getEndpointInternal(URI to, URI action) {
MethodEndpoint methodEndpoint = (MethodEndpoint) lookupEndpoint(action);
if (methodEndpoint != null) {
// respect the Address annotation, if set
Class endpointClass = methodEndpoint.getMethod().getDeclaringClass();
Address address = AnnotationUtils.findAnnotation(endpointClass, Address.class);
if (address != null && StringUtils.hasText(address.value())) {
try {
URI addressUri = new URI(address.value());
if (to.equals(addressUri)) {
return methodEndpoint;
}
}
catch (URISyntaxException e) {
throw new IllegalArgumentException(
"Invalid Address annotation [" + address.value() + "] on [" + endpointClass + "]");
}
}
else {
// address not set
return methodEndpoint;
}
}
return null;
}
}

View File

@@ -0,0 +1,142 @@
/*
* 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;
/**
* Implementation of the <code>EndpointMapping</code> interface to map from WS-Addressing <code>Action</code> Message
* Addressing Property 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 {@link #setAddress(URI) address} property should be equal to the {@link
* MessageAddressingProperties#getTo() destination} property of the incominging message. As such, it can be used to
* create multiple Endpoint References, by defining multiple <code>SimpleActionEndpointMapping</code> bean definitions
* with different <code>address</code>property values.
*
* @author Arjen Poutsma
* @see MessageAddressingProperties#getAction()
* @since 1.5.0
*/
public class SimpleActionEndpointMapping extends AbstractActionEndpointMapping {
// contents will be copied over to endpointMap
private final Map actionMap = new HashMap();
private URI address;
/**
* Map action URIs to endpoint bean names. This is the typical way of configuring this EndpointMapping.
*
* @param mappings properties with URLs as keys and bean names as values
* @see #setActionMap(java.util.Map)
*/
public void setMappings(Properties mappings) throws URISyntaxException {
setActionMap(mappings);
}
/**
* Set a Map with action URIs as keys and handler beans (or handler bean names) as values. Convenient for population
* with bean references.
*
* @param actionMap map with action URIs as keys and beans as values
* @see #setMappings
*/
public void setActionMap(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.actionMap.put(action, entry.getValue());
}
}
/**
* Set the address property. If set, value of this property is compared to the {@link
* MessageAddressingProperties#getTo() destination} property of the incominging message.
*
* @param address the address URI
*/
public void setAddress(URI address) {
this.address = address;
}
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
registerEndpoints(actionMap);
}
protected Object getEndpointInternal(URI to, URI action) {
// MAP address much match the defined EPR address
if (address != null && !address.equals(to)) {
return null;
}
return lookupEndpoint(action);
}
/**
* Register all endpoints specified in the action map.
*
* @param actionMap Map with action URIs as keys and endppint beans or bean names as values
* @throws BeansException if an endpoint couldn't be registered
* @throws IllegalStateException if there is a conflicting endpoint registered
*/
protected void registerEndpoints(Map actionMap) throws BeansException {
if (actionMap.isEmpty()) {
logger.warn("Neither 'actionMap' nor 'mappings' set on SimpleActionEndpointMapping");
}
else {
for (Iterator iterator = actionMap.entrySet().iterator(); iterator.hasNext();) {
Map.Entry entry = (Map.Entry) iterator.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);
}
}
}
}

View File

@@ -1,148 +0,0 @@
/*
* 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

@@ -1,170 +0,0 @@
/*
* 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 AbstractWsAddressingMapping}.
*
* @author Arjen Poutsma
* @since 1.5.0
*/
class WsAddressingEndpointInterceptor implements SoapEndpointInterceptor {
private static final Log logger = LogFactory.getLog(WsAddressingEndpointInterceptor.class);
private final WsAddressingVersion version;
private final MessageIdStrategy messageIdStrategy;
private final WebServiceMessageSender[] messageSenders;
WsAddressingEndpointInterceptor(WsAddressingVersion version,
MessageIdStrategy messageIdStrategy,
WebServiceMessageSender[] messageSenders) {
Assert.notNull(version, "version must not be null");
Assert.notNull(messageIdStrategy, "messageIdStrategy must not be null");
Assert.notNull(messageSenders, "messageSenders must not be null");
this.version = version;
this.messageIdStrategy = messageIdStrategy;
this.messageSenders = messageSenders;
}
public 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 boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
return handleResponseOrFault(messageContext, false);
}
public 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());
SoapMessage request = (SoapMessage) messageContext.getRequest();
MessageAddressingProperties requestMap = version.getMessageAddressingProperties(request);
EndpointReference replyEpr = isFault ? requestMap.getFaultTo() : requestMap.getReplyTo();
if (handleNoneAddress(messageContext, replyEpr)) {
return false;
}
URI responseMessageId = getMessageId(messageContext);
MessageAddressingProperties replyMap = requestMap.getReplyProperties(replyEpr, null, 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

@@ -23,6 +23,9 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks an endpoint method as the handler for an incoming request. The annotation value signifies the value for the
* request WS-Addressing <code>Action</code> header that is handled by the method.
*
* @author Arjen Poutsma
* @since 1.5.0
*/
@@ -31,4 +34,13 @@ import java.lang.annotation.Target;
@Target(ElementType.METHOD)
public @interface Action {
/** Signifies the value for the request WS-Addressing <code>Action</code> header that is handled by the method. */
String value();
/**
* Explicit value of the WS-Addressing <code>Action</code> message addressing property for the <code>output</code>
* message of the operation.
*/
String output() default "";
}

View File

@@ -0,0 +1,49 @@
/*
* 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;
/**
* Marks an endpoint with a WS-Addressing <code>Address</code>. If this annotation is applied, the {@link #value()} is
* compared to the {@link org.springframework.ws.soap.addressing.MessageAddressingProperties#getTo() destination}
* property of the incominging message.
* <p/>
* as the handler for an incoming request. The annotation value signifies the value for the request WS-Addressing
* <code>Action</code> header that is handled by the method.
*
* @author Arjen Poutsma
* @since 1.5.0
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Address {
/**
* The value may indicate a suggestion for a logical component name, to be turned into a Spring bean in case of an
* autodetected component.
*
* @return the suggested component name, if any
*/
String value();
}

View File

@@ -0,0 +1,90 @@
/*
* 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.io.IOException;
import javax.xml.soap.SOAPException;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.ws.context.DefaultMessageContext;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.EndpointInvocationChain;
import org.springframework.ws.server.endpoint.MethodEndpoint;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.soap.addressing.annotation.Action;
import org.springframework.ws.soap.addressing.annotation.Address;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
public class AnnotationActionMethodEndpointMappingTest extends AbstractWsAddressingTestCase {
private StaticApplicationContext applicationContext;
private AnnotationActionMethodEndpointMapping mapping;
protected void onSetUp() throws Exception {
applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("mapping", AnnotationActionMethodEndpointMapping.class);
mapping = (AnnotationActionMethodEndpointMapping) applicationContext.getBean("mapping");
}
public void testNoAddress() throws Exception {
applicationContext.registerSingleton("endpoint", Endpoint1.class);
applicationContext.refresh();
MessageContext messageContext = createMessageContext();
EndpointInvocationChain chain = mapping.getEndpoint(messageContext);
assertNotNull("MethodEndpoint not registered", chain);
MethodEndpoint expected = new MethodEndpoint(applicationContext.getBean("endpoint"), "doIt", new Class[0]);
assertEquals("Invalid endpoint registered", expected, chain.getEndpoint());
}
public void testAddress() throws Exception {
applicationContext.registerSingleton("endpoint", Endpoint2.class);
applicationContext.refresh();
MessageContext messageContext = createMessageContext();
EndpointInvocationChain chain = mapping.getEndpoint(messageContext);
assertNotNull("MethodEndpoint not registered", chain);
MethodEndpoint expected = new MethodEndpoint(applicationContext.getBean("endpoint"), "doIt", new Class[0]);
assertEquals("Invalid endpoint registered", expected, chain.getEndpoint());
}
private MessageContext createMessageContext() throws SOAPException, IOException {
SaajSoapMessage message = loadSaajMessage("200408/valid.xml");
return new DefaultMessageContext(message, new SaajSoapMessageFactory(messageFactory));
}
@Endpoint
private static class Endpoint1 {
@Action("http://fabrikam123.example/mail/Delete")
public void doIt() {
}
}
@Endpoint
@Address("mailto:joe@fabrikam123.example")
private static class Endpoint2 {
@Action("http://fabrikam123.example/mail/Delete")
public void doIt() {
}
}
}

View File

@@ -28,16 +28,16 @@ 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 {
public class SimpleActionEndpointMappingTest extends AbstractWsAddressingTestCase {
private WsAddressingActionEndpointMapping mapping;
private SimpleActionEndpointMapping mapping;
private Endpoint1 endpoint1;
private Endpoint2 endpoint2;
protected void onSetUp() throws Exception {
mapping = new WsAddressingActionEndpointMapping();
mapping = new SimpleActionEndpointMapping();
Map map = new HashMap();
endpoint1 = new Endpoint1();
endpoint2 = new Endpoint2();
@@ -46,7 +46,6 @@ public class WsAddressingActionEndpointMappingTest extends AbstractWsAddressingT
mapping.setPreInterceptors(new EndpointInterceptor[]{new PayloadLoggingInterceptor()});
mapping.setPostInterceptors(new EndpointInterceptor[]{new PayloadValidatingInterceptor()});
mapping.setActionMap(map);
mapping.setApplicationContext(null);
mapping.afterPropertiesSet();
}