View Javadoc

1   /*
2    * Copyright 2005-2011 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.ws.soap.addressing.server;
18  
19  import java.net.URI;
20  import java.util.Arrays;
21  import java.util.Iterator;
22  import javax.xml.transform.TransformerException;
23  
24  import org.springframework.beans.factory.InitializingBean;
25  import org.springframework.core.Ordered;
26  import org.springframework.util.Assert;
27  import org.springframework.ws.context.MessageContext;
28  import org.springframework.ws.server.EndpointInterceptor;
29  import org.springframework.ws.server.EndpointInvocationChain;
30  import org.springframework.ws.server.EndpointMapping;
31  import org.springframework.ws.soap.SoapHeader;
32  import org.springframework.ws.soap.SoapHeaderElement;
33  import org.springframework.ws.soap.SoapMessage;
34  import org.springframework.ws.soap.addressing.core.MessageAddressingProperties;
35  import org.springframework.ws.soap.addressing.messageid.MessageIdStrategy;
36  import org.springframework.ws.soap.addressing.messageid.UuidMessageIdStrategy;
37  import org.springframework.ws.soap.addressing.version.Addressing10;
38  import org.springframework.ws.soap.addressing.version.Addressing200408;
39  import org.springframework.ws.soap.addressing.version.AddressingVersion;
40  import org.springframework.ws.soap.server.SoapEndpointInvocationChain;
41  import org.springframework.ws.soap.server.SoapEndpointMapping;
42  import org.springframework.ws.transport.WebServiceMessageSender;
43  import org.springframework.xml.transform.TransformerObjectSupport;
44  
45  /**
46   * Abstract base class for {@link EndpointMapping} implementations that handle WS-Addressing. Besides the normal {@link
47   * SoapEndpointMapping} properties, this mapping has a {@link #setVersions(org.springframework.ws.soap.addressing.version.AddressingVersion[])
48   * versions} property, which defines the WS-Addressing specifications supported. By default, these are {@link
49   * org.springframework.ws.soap.addressing.version.Addressing200408} and {@link org.springframework.ws.soap.addressing.version.Addressing10}.
50   * <p/>
51   * The {@link #setMessageIdStrategy(MessageIdStrategy) messageIdStrategy} property defines the strategy to use for
52   * creating reply <code>MessageIDs</code>. By default, this is the {@link UuidMessageIdStrategy}.
53   * <p/>
54   * The {@link #setMessageSenders(WebServiceMessageSender[]) messageSenders} are used to send out-of-band reply messages.
55   * If a request messages defines a non-anonymous reply address, these senders will be used to send the message.
56   * <p/>
57   * This mapping (and all subclasses) uses an implicit WS-Addressing {@link EndpointInterceptor}, which is added in every
58   * {@link EndpointInvocationChain} produced. As such, this mapping does not have the standard <code>interceptors</code>
59   * property, but rather a {@link #setPreInterceptors(EndpointInterceptor[]) preInterceptors} and {@link
60   * #setPostInterceptors(EndpointInterceptor[]) postInterceptors} property, which are added before and after the implicit
61   * WS-Addressing interceptor, respectively.
62   *
63   * @author Arjen Poutsma
64   * @since 1.5.0
65   */
66  public abstract class AbstractAddressingEndpointMapping extends TransformerObjectSupport
67          implements SoapEndpointMapping, InitializingBean, Ordered {
68  
69      private String[] actorsOrRoles;
70  
71      private boolean isUltimateReceiver = true;
72  
73      private MessageIdStrategy messageIdStrategy;
74  
75      private WebServiceMessageSender[] messageSenders = new WebServiceMessageSender[0];
76  
77      private AddressingVersion[] versions;
78  
79      private EndpointInterceptor[] preInterceptors = new EndpointInterceptor[0];
80  
81      private EndpointInterceptor[] postInterceptors = new EndpointInterceptor[0];
82  
83      private int order = Integer.MAX_VALUE;  // default: same as non-Ordered
84  
85  
86      /** Protected constructor. Initializes the default settings. */
87      protected AbstractAddressingEndpointMapping() {
88          initDefaultStrategies();
89      }
90  
91      /**
92       * Initializes the default implementation for this mapping's strategies: the {@link
93       * org.springframework.ws.soap.addressing.version.Addressing200408} and {@link org.springframework.ws.soap.addressing.version.Addressing10}
94       * versions of the specification, and the {@link UuidMessageIdStrategy}.
95       */
96      protected void initDefaultStrategies() {
97          this.versions = new AddressingVersion[]{new Addressing200408(), new Addressing10()};
98          messageIdStrategy = new UuidMessageIdStrategy();
99      }
100 
101     public final void setActorOrRole(String actorOrRole) {
102         Assert.notNull(actorOrRole, "actorOrRole must not be null");
103         actorsOrRoles = new String[]{actorOrRole};
104     }
105 
106     public final void setActorsOrRoles(String[] actorsOrRoles) {
107         Assert.notEmpty(actorsOrRoles, "actorsOrRoles must not be empty");
108         this.actorsOrRoles = actorsOrRoles;
109     }
110 
111     public final void setUltimateReceiver(boolean ultimateReceiver) {
112         this.isUltimateReceiver = ultimateReceiver;
113     }
114 
115     public final int getOrder() {
116         return order;
117     }
118 
119     /**
120      * Specify the order value for this mapping.
121      * <p/>
122      * Default value is {@link Integer#MAX_VALUE}, meaning that it's non-ordered.
123      *
124      * @see org.springframework.core.Ordered#getOrder()
125      */
126     public final void setOrder(int order) {
127         this.order = order;
128     }
129 
130 	/**
131 	 * Set additional interceptors to be applied before the implicit WS-Addressing interceptor, e.g.
132 	 * <code>XwsSecurityInterceptor</code>.
133      */
134     public final void setPreInterceptors(EndpointInterceptor[] preInterceptors) {
135         Assert.notNull(preInterceptors, "'preInterceptors' must not be null");
136         this.preInterceptors = preInterceptors;
137     }
138 
139     /**
140      * Set additional interceptors to be applied after the implicit WS-Addressing interceptor, e.g.
141      * <code>PayloadLoggingInterceptor</code>.
142      */
143     public final void setPostInterceptors(EndpointInterceptor[] postInterceptors) {
144         Assert.notNull(postInterceptors, "'postInterceptors' must not be null");
145         this.postInterceptors = postInterceptors;
146     }
147 
148     /**
149      * Sets the message id strategy used for creating WS-Addressing MessageIds.
150      * <p/>
151      * By default, the {@link UuidMessageIdStrategy} is used.
152      */
153     public final void setMessageIdStrategy(MessageIdStrategy messageIdStrategy) {
154         Assert.notNull(messageIdStrategy, "'messageIdStrategy' must not be null");
155         this.messageIdStrategy = messageIdStrategy;
156     }
157 
158 	/**
159 	 * Returns the message id strategy used for creating WS-Addressing MessageIds.
160 	 */
161 	public MessageIdStrategy getMessageIdStrategy() {
162 		return messageIdStrategy;
163 	}
164 
165 	/**
166 	 * Sets a single message senders, which is used to send out-of-band reply messages. If a
167 	 * request messages defines a non-anonymous reply address, this senders will be used to
168 	 * send the message.
169 	 *
170 	 * @param messageSender the message sender
171 	 */
172 	public final void setMessageSender(WebServiceMessageSender messageSender) {
173 		Assert.notNull(messageSender, "'messageSender' must not be null");
174 		setMessageSenders(new WebServiceMessageSender[]{messageSender});
175 	}
176 
177 	/**
178 	 * Sets the message senders, which are used to send out-of-band reply messages.
179 	 * If a request messages defines a non-anonymous reply address, these senders will be
180 	 * used to send the message.
181 	 *
182 	 * @param messageSenders the message senders
183 	 */
184 	public final void setMessageSenders(WebServiceMessageSender[] messageSenders) {
185 		Assert.notNull(messageSenders, "'messageSenders' must not be null");
186 		this.messageSenders = messageSenders;
187     }
188 
189 	/**
190 	 * Returns the message senders, which are used to send out-of-band reply messages.
191 	 *
192 	 * @return the message sender
193 	 */
194 	public final WebServiceMessageSender[] getMessageSenders() {
195 		return this.messageSenders;
196 	}
197 
198 	/**
199 	 * Sets the WS-Addressing versions to be supported by this mapping.
200 	 * <p/>
201 	 * By default, this array is set to support {@link org.springframework.ws.soap.addressing.version.Addressing200408
202      * the August 2004} and the {@link org.springframework.ws.soap.addressing.version.Addressing10 May 2006} versions of
203      * the specification.
204      */
205     public final void setVersions(AddressingVersion[] versions) {
206         this.versions = versions;
207     }
208 
209     public void afterPropertiesSet() throws Exception {
210         if (logger.isInfoEnabled()) {
211             logger.info("Supporting " + Arrays.asList(versions));
212         }
213     }
214 
215     public final EndpointInvocationChain getEndpoint(MessageContext messageContext) throws TransformerException {
216         Assert.isInstanceOf(SoapMessage.class, messageContext.getRequest());
217         SoapMessage request = (SoapMessage) messageContext.getRequest();
218         for (AddressingVersion version : versions) {
219             if (supports(version, request)) {
220                 if (logger.isDebugEnabled()) {
221                     logger.debug("Request [" + request + "] uses [" + version + "]");
222                 }
223                 MessageAddressingProperties requestMap = version.getMessageAddressingProperties(request);
224                 if (requestMap == null) {
225                     return null;
226                 }
227                 Object endpoint = getEndpointInternal(requestMap);
228                 if (endpoint == null) {
229                     return null;
230                 }
231                 return getEndpointInvocationChain(endpoint, version, requestMap);
232             }
233         }
234         return null;
235     }
236 
237     /**
238      * Creates a {@link SoapEndpointInvocationChain} based on the given endpoint and {@link
239      * org.springframework.ws.soap.addressing.version.AddressingVersion}.
240      */
241     private EndpointInvocationChain getEndpointInvocationChain(Object endpoint,
242                                                                AddressingVersion version,
243                                                                MessageAddressingProperties requestMap) {
244         URI responseAction = getResponseAction(endpoint, requestMap);
245         URI faultAction = getFaultAction(endpoint, requestMap);
246 
247 	    WebServiceMessageSender[] messageSenders = getMessageSenders(endpoint);
248 	    MessageIdStrategy messageIdStrategy = getMessageIdStrategy(endpoint);
249 
250 	    EndpointInterceptor[] interceptors =
251 			    new EndpointInterceptor[preInterceptors.length + postInterceptors.length +
252 					    1];
253 	    System.arraycopy(preInterceptors, 0, interceptors, 0, preInterceptors.length);
254         AddressingEndpointInterceptor interceptor = new AddressingEndpointInterceptor(version, messageIdStrategy,
255                 messageSenders, responseAction, faultAction);
256         interceptors[preInterceptors.length] = interceptor;
257         System.arraycopy(postInterceptors, 0, interceptors, preInterceptors.length + 1, postInterceptors.length);
258         return new SoapEndpointInvocationChain(endpoint, interceptors, actorsOrRoles, isUltimateReceiver);
259     }
260 
261     private boolean supports(AddressingVersion version, SoapMessage request) {
262         SoapHeader header = request.getSoapHeader();
263         if (header != null) {
264             for (Iterator<SoapHeaderElement> iterator = header.examineAllHeaderElements(); iterator.hasNext();) {
265                 SoapHeaderElement headerElement = iterator.next();
266                 if (version.understands(headerElement)) {
267                     return true;
268                 }
269             }
270         }
271         return false;
272     }
273 
274 	/**
275 	 * Returns the message senders for the given endpoint. Default implementation returns
276 	 * {@link #getMessageSenders()}
277 	 *
278 	 * @param endpoint the endpoint
279 	 * @return the message senders for the given endpoint
280 	 */
281 	protected WebServiceMessageSender[] getMessageSenders(Object endpoint) {
282 		return getMessageSenders();
283 	}
284 
285 	/**
286 	 * Returns the message ID strategy for the given endpoint. Default implementation
287 	 * returns {@link #getMessageIdStrategy()}
288 	 *
289 	 * @param endpoint the endpoint
290 	 * @return the message ID strategy for the given endpoint
291 	 */
292 	protected MessageIdStrategy getMessageIdStrategy(Object endpoint) {
293 		return getMessageIdStrategy();
294 	}
295 
296 	/**
297 	 * Lookup an endpoint for the given  {@link MessageAddressingProperties}, returning <code>null</code> if no specific
298 	 * one is found. This template method is called by {@link #getEndpoint(MessageContext)}.
299 	 *
300      * @param map the message addressing properties
301      * @return the endpoint, or <code>null</code>
302      */
303     protected abstract Object getEndpointInternal(MessageAddressingProperties map);
304 
305     /**
306      * Provides the WS-Addressing Action for response messages, given the endpoint, and request Message Addressing
307      * Properties.
308      *
309      * @param endpoint   the mapped endpoint
310      * @param requestMap the MAP for the request
311      * @return the response Action
312      */
313     protected abstract URI getResponseAction(Object endpoint, MessageAddressingProperties requestMap);
314 
315     /**
316      * Provides the WS-Addressing Action for response fault messages, given the endpoint, and request Message Addressing
317      * Properties.
318      *
319      * @param endpoint   the mapped endpoint
320      * @param requestMap the MAP for the request
321      * @return the response Action
322      */
323     protected abstract URI getFaultAction(Object endpoint, MessageAddressingProperties requestMap);
324 
325 }