View Javadoc

1   /*
2    * Copyright 2007 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.JdkVersion;
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.RandomGuidMessageIdStrategy;
37  import org.springframework.ws.soap.addressing.messageid.UuidMessageIdStrategy;
38  import org.springframework.ws.soap.addressing.version.Addressing10;
39  import org.springframework.ws.soap.addressing.version.Addressing200408;
40  import org.springframework.ws.soap.addressing.version.AddressingVersion;
41  import org.springframework.ws.soap.server.SoapEndpointInvocationChain;
42  import org.springframework.ws.soap.server.SoapEndpointMapping;
43  import org.springframework.ws.transport.WebServiceMessageSender;
44  import org.springframework.xml.transform.TransformerObjectSupport;
45  
46  /**
47   * Abstract base class for {@link EndpointMapping} implementations that handle WS-Addressing. Besides the normal {@link
48   * SoapEndpointMapping} properties, this mapping has a {@link #setVersions(org.springframework.ws.soap.addressing.version.AddressingVersion[])
49   * versions} property, which defines the WS-Addressing specifications supported. By default, these are {@link
50   * org.springframework.ws.soap.addressing.version.Addressing200408} and {@link org.springframework.ws.soap.addressing.version.Addressing10}.
51   * <p/>
52   * The {@link #setMessageIdStrategy(MessageIdStrategy) messageIdStrategy} property defines the strategy to use for
53   * creating reply <code>MessageIDs</code>. By default, this is the {@link UuidMessageIdStrategy} on Java 5 and higher,
54   * and the {@link RandomGuidMessageIdStrategy} on Java 1.4.
55   * <p/>
56   * The {@link #setMessageSenders(WebServiceMessageSender[]) messageSenders} are used to send out-of-band reply messages.
57   * If a request messages defines a non-anonymous reply address, these senders will be used to send the message.
58   * <p/>
59   * This mapping (and all subclasses) uses an implicit WS-Addressing {@link EndpointInterceptor}, which is added in every
60   * {@link EndpointInvocationChain} produced. As such, this mapping does not have the standard <code>interceptors</code>
61   * property, but rather a {@link #setPreInterceptors(EndpointInterceptor[]) preInterceptors} and {@link
62   * #setPostInterceptors(EndpointInterceptor[]) postInterceptors} property, which are added before and after the implicit
63   * WS-Addressing interceptor, respectively.
64   *
65   * @author Arjen Poutsma
66   * @since 1.5.0
67   */
68  public abstract class AbstractAddressingEndpointMapping extends TransformerObjectSupport
69          implements SoapEndpointMapping, InitializingBean {
70  
71      private String[] actorsOrRoles;
72  
73      private boolean isUltimateReceiver = true;
74  
75      private MessageIdStrategy messageIdStrategy;
76  
77      private WebServiceMessageSender[] messageSenders = new WebServiceMessageSender[0];
78  
79      private AddressingVersion[] versions;
80  
81      private EndpointInterceptor[] preInterceptors = new EndpointInterceptor[0];
82  
83      private EndpointInterceptor[] postInterceptors = new EndpointInterceptor[0];
84  
85      /** Protected constructor. Initializes the default settings. */
86      protected AbstractAddressingEndpointMapping() {
87          initDefaultStrategies();
88      }
89  
90      /**
91       * Initializes the default implementation for this mapping's strategies: the {@link
92       * org.springframework.ws.soap.addressing.version.Addressing200408} and {@link org.springframework.ws.soap.addressing.version.Addressing10}
93       * versions of the specication, and the {@link UuidMessageIdStrategy} on Java 5 and higher; the {@link
94       * RandomGuidMessageIdStrategy} on Java 1.4.
95       */
96      protected void initDefaultStrategies() {
97          this.versions = new AddressingVersion[]{new Addressing200408(), new Addressing10()};
98          if (JdkVersion.isAtLeastJava15()) {
99              messageIdStrategy = new UuidMessageIdStrategy();
100         }
101         else {
102             messageIdStrategy = new RandomGuidMessageIdStrategy();
103         }
104     }
105 
106     public final void setActorOrRole(String actorOrRole) {
107         Assert.notNull(actorOrRole, "actorOrRole must not be null");
108         actorsOrRoles = new String[]{actorOrRole};
109     }
110 
111     public final void setActorsOrRoles(String[] actorsOrRoles) {
112         Assert.notEmpty(actorsOrRoles, "actorsOrRoles must not be empty");
113         this.actorsOrRoles = actorsOrRoles;
114     }
115 
116     public final void setUltimateReceiver(boolean ultimateReceiver) {
117         this.isUltimateReceiver = ultimateReceiver;
118     }
119 
120     /**
121      * Set additional interceptors to be applied before the implicit WS-Addressing interceptor, e.g.
122      * <code>XwsSecurityInterceptor</code>.
123      */
124     public final void setPreInterceptors(EndpointInterceptor[] preInterceptors) {
125         Assert.notNull(preInterceptors, "'preInterceptors' must not be null");
126         this.preInterceptors = preInterceptors;
127     }
128 
129     /**
130      * Set additional interceptors to be applied after the implicit WS-Addressing interceptor, e.g.
131      * <code>PayloadLoggingInterceptor</code>.
132      */
133     public final void setPostInterceptors(EndpointInterceptor[] postInterceptors) {
134         Assert.notNull(postInterceptors, "'postInterceptors' must not be null");
135         this.postInterceptors = postInterceptors;
136     }
137 
138     /**
139      * Sets the message id strategy used for creating WS-Addressing MessageIds.
140      * <p/>
141      * By default, the {@link UuidMessageIdStrategy} is used on Java 5 and higher, and the {@link
142      * RandomGuidMessageIdStrategy} on Java 1.4.
143      */
144     public final void setMessageIdStrategy(MessageIdStrategy messageIdStrategy) {
145         Assert.notNull(messageIdStrategy, "'messageIdStrategy' must not be null");
146         this.messageIdStrategy = messageIdStrategy;
147     }
148 
149     public final void setMessageSenders(WebServiceMessageSender[] messageSenders) {
150         Assert.notNull(messageSenders, "'messageSenders' must not be null");
151         this.messageSenders = messageSenders;
152     }
153 
154     /**
155      * Sets the WS-Addressing versions to be supported by this mapping.
156      * <p/>
157      * By default, this array is set to support {@link org.springframework.ws.soap.addressing.version.Addressing200408
158      * the August 2004} and the {@link org.springframework.ws.soap.addressing.version.Addressing10 May 2006} versions of
159      * the specification.
160      */
161     public final void setVersions(AddressingVersion[] versions) {
162         this.versions = versions;
163     }
164 
165     public void afterPropertiesSet() throws Exception {
166         if (logger.isInfoEnabled()) {
167             logger.info("Supporting " + Arrays.asList(versions));
168         }
169     }
170 
171     public final EndpointInvocationChain getEndpoint(MessageContext messageContext) throws TransformerException {
172         Assert.isInstanceOf(SoapMessage.class, messageContext.getRequest());
173         SoapMessage request = (SoapMessage) messageContext.getRequest();
174         for (int i = 0; i < versions.length; i++) {
175             if (supports(versions[i], request)) {
176                 if (logger.isDebugEnabled()) {
177                     logger.debug("Request [" + request + "] uses [" + versions[i] + "]");
178                 }
179                 MessageAddressingProperties requestMap = versions[i].getMessageAddressingProperties(request);
180                 if (requestMap == null) {
181                     return null;
182                 }
183                 Object endpoint = getEndpointInternal(requestMap);
184                 if (endpoint == null) {
185                     return null;
186                 }
187                 return getEndpointInvocationChain(endpoint, versions[i], requestMap);
188             }
189         }
190         return null;
191     }
192 
193     /**
194      * Creates a {@link SoapEndpointInvocationChain} based on the given endpoint and {@link
195      * org.springframework.ws.soap.addressing.version.AddressingVersion}.
196      */
197     private EndpointInvocationChain getEndpointInvocationChain(Object endpoint,
198                                                                AddressingVersion version,
199                                                                MessageAddressingProperties requestMap) {
200         URI responseAction = getResponseAction(endpoint, requestMap);
201         URI faultAction = getFaultAction(endpoint, requestMap);
202         EndpointInterceptor[] interceptors =
203                 new EndpointInterceptor[preInterceptors.length + postInterceptors.length + 1];
204         System.arraycopy(preInterceptors, 0, interceptors, 0, preInterceptors.length);
205         AddressingEndpointInterceptor interceptor = new AddressingEndpointInterceptor(version, messageIdStrategy,
206                 messageSenders, responseAction, faultAction);
207         interceptors[preInterceptors.length] = interceptor;
208         System.arraycopy(postInterceptors, 0, interceptors, preInterceptors.length + 1, postInterceptors.length);
209         return new SoapEndpointInvocationChain(endpoint, interceptors, actorsOrRoles, isUltimateReceiver);
210     }
211 
212     private boolean supports(AddressingVersion version, SoapMessage request) {
213         SoapHeader header = request.getSoapHeader();
214         if (header != null) {
215             for (Iterator iterator = header.examineAllHeaderElements(); iterator.hasNext();) {
216                 SoapHeaderElement headerElement = (SoapHeaderElement) iterator.next();
217                 if (version.understands(headerElement)) {
218                     return true;
219                 }
220             }
221         }
222         return false;
223     }
224 
225     /**
226      * Lookup an endpoint for the given  {@link MessageAddressingProperties}, returning <code>null</code> if no specific
227      * one is found. This template method is called by {@link #getEndpoint(MessageContext)}.
228      *
229      * @param map the message addressing properties
230      * @return the endpoint, or <code>null</code>
231      */
232     protected abstract Object getEndpointInternal(MessageAddressingProperties map);
233 
234     /**
235      * Provides the WS-Addressing Action for response messages, given the endpoint, and request Message Addressing
236      * Properties.
237      *
238      * @param endpoint   the mapped endpoint
239      * @param requestMap the MAP for the request
240      * @return the response Action
241      */
242     protected abstract URI getResponseAction(Object endpoint, MessageAddressingProperties requestMap);
243 
244     /**
245      * Provides the WS-Addressing Action for response fault messages, given the endpoint, and request Message Addressing
246      * Properties.
247      *
248      * @param endpoint   the mapped endpoint
249      * @param requestMap the MAP for the request
250      * @return the response Action
251      */
252     protected abstract URI getFaultAction(Object endpoint, MessageAddressingProperties requestMap);
253 
254 }