View Javadoc

1   /*
2    * Copyright 2005 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.server;
18  
19  import java.util.ArrayList;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Locale;
23  import javax.xml.namespace.QName;
24  
25  import org.springframework.util.ObjectUtils;
26  import org.springframework.util.StringUtils;
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.MessageDispatcher;
31  import org.springframework.ws.soap.SoapBody;
32  import org.springframework.ws.soap.SoapFault;
33  import org.springframework.ws.soap.SoapHeader;
34  import org.springframework.ws.soap.SoapHeaderElement;
35  import org.springframework.ws.soap.SoapMessage;
36  import org.springframework.ws.soap.soap11.Soap11Header;
37  import org.springframework.ws.soap.soap12.Soap12Header;
38  
39  /**
40   * SOAP-specific subclass of the {@link MessageDispatcher}. Adds functionality for adding actor roles to a endpoint
41   * invocation chain, and endpoint interception using {@link SoapEndpointInterceptor} objects.
42   *
43   * @author Arjen Poutsma
44   * @see org.springframework.ws.soap.SoapMessage
45   * @see SoapEndpointInterceptor
46   * @since 1.0.0
47   */
48  public class SoapMessageDispatcher extends MessageDispatcher {
49  
50      /** Default message used when creating a SOAP MustUnderstand fault. */
51      public static final String DEFAULT_MUST_UNDERSTAND_FAULT_STRING =
52              "One or more mandatory SOAP header blocks not understood";
53  
54      private String mustUnderstandFaultString = DEFAULT_MUST_UNDERSTAND_FAULT_STRING;
55  
56      private Locale mustUnderstandFaultStringLocale = Locale.ENGLISH;
57  
58      /**
59       * Sets the message used for <code>MustUnderstand</code> fault. Default to {@link
60       * #DEFAULT_MUST_UNDERSTAND_FAULT_STRING}.
61       */
62      public void setMustUnderstandFaultString(String mustUnderstandFaultString) {
63          this.mustUnderstandFaultString = mustUnderstandFaultString;
64      }
65  
66      /** Sets the locale of the message used for <code>MustUnderstand</code> fault. Default to {@link Locale#ENGLISH}. */
67      public void setMustUnderstandFaultStringLocale(Locale mustUnderstandFaultStringLocale) {
68          this.mustUnderstandFaultStringLocale = mustUnderstandFaultStringLocale;
69      }
70  
71      /**
72       * Process the headers targeted at the actor or role fullfilled by the endpoint. Also processed the
73       * <code>MustUnderstand</code> headers in the incoming SOAP request message. Iterates over all SOAP headers which
74       * should be understood for this role, and determines whether these are supported. Generates a SOAP MustUnderstand
75       * fault if a header is not understood.
76       *
77       * @param mappedEndpoint the mapped EndpointInvocationChain
78       * @param messageContext the message context
79       * @return <code>true</code> if all necessary headers are understood; <code>false</code> otherwise
80       * @see SoapEndpointInvocationChain#getActorsOrRoles()
81       * @see org.springframework.ws.soap.SoapHeader#examineMustUnderstandHeaderElements(String)
82       */
83      protected boolean handleRequest(EndpointInvocationChain mappedEndpoint, MessageContext messageContext) {
84          if (messageContext.getRequest() instanceof SoapMessage) {
85              String[] actorsOrRoles = null;
86              boolean isUltimateReceiver = true;
87              if (mappedEndpoint instanceof SoapEndpointInvocationChain) {
88                  SoapEndpointInvocationChain soapChain = (SoapEndpointInvocationChain) mappedEndpoint;
89                  actorsOrRoles = soapChain.getActorsOrRoles();
90                  isUltimateReceiver = soapChain.isUltimateReceiver();
91              }
92              return handleHeaders(mappedEndpoint, messageContext, actorsOrRoles, isUltimateReceiver);
93          }
94          return true;
95      }
96  
97      private boolean handleHeaders(EndpointInvocationChain mappedEndpoint,
98                                    MessageContext messageContext,
99                                    String[] actorsOrRoles,
100                                   boolean isUltimateReceiver) {
101         SoapMessage soapRequest = (SoapMessage) messageContext.getRequest();
102         SoapHeader soapHeader = soapRequest.getSoapHeader();
103         if (soapHeader == null) {
104             return true;
105         }
106         Iterator headerIterator;
107         if (soapHeader instanceof Soap11Header) {
108             headerIterator = ((Soap11Header) soapHeader).examineHeaderElementsToProcess(actorsOrRoles);
109         }
110         else {
111             headerIterator =
112                     ((Soap12Header) soapHeader).examineHeaderElementsToProcess(actorsOrRoles, isUltimateReceiver);
113         }
114         List notUnderstoodHeaderNames = new ArrayList();
115         while (headerIterator.hasNext()) {
116             SoapHeaderElement headerElement = (SoapHeaderElement) headerIterator.next();
117             QName headerName = headerElement.getName();
118             if (headerElement.getMustUnderstand() && logger.isDebugEnabled()) {
119                 logger.debug("Handling MustUnderstand header " + headerName);
120             }
121             if (headerElement.getMustUnderstand() && !headerUnderstood(mappedEndpoint, headerElement)) {
122                 notUnderstoodHeaderNames.add(headerName);
123             }
124         }
125         if (notUnderstoodHeaderNames.isEmpty()) {
126             return true;
127         }
128         else {
129             SoapMessage response = (SoapMessage) messageContext.getResponse();
130             createMustUnderstandFault(response, notUnderstoodHeaderNames, actorsOrRoles);
131             return false;
132         }
133     }
134 
135     /**
136      * Handles the request for a single SOAP actor/role. Iterates over all <code>MustUnderstand</code> headers for a
137      * specific SOAP 1.1 actor or SOAP 1.2 role, and determines whether these are understood by any of the registered
138      * <code>SoapEndpointInterceptor</code>. If they are, returns <code>true</code>. If they are not, a SOAP fault is
139      * created, and false is returned.
140      *
141      * @see SoapEndpointInterceptor#understands(org.springframework.ws.soap.SoapHeaderElement)
142      */
143     private boolean headerUnderstood(EndpointInvocationChain mappedEndpoint, SoapHeaderElement headerElement) {
144         EndpointInterceptor[] interceptors = mappedEndpoint.getInterceptors();
145         if (ObjectUtils.isEmpty(interceptors)) {
146             return false;
147         }
148         for (int i = 0; i < interceptors.length; i++) {
149             EndpointInterceptor interceptor = interceptors[i];
150             if (interceptor instanceof SoapEndpointInterceptor &&
151                     ((SoapEndpointInterceptor) interceptor).understands(headerElement)) {
152                 return true;
153             }
154         }
155         return false;
156     }
157 
158     private void createMustUnderstandFault(SoapMessage soapResponse,
159                                            List notUnderstoodHeaderNames,
160                                            String[] actorsOrRoles) {
161         if (logger.isWarnEnabled()) {
162             logger.warn("Could not handle mustUnderstand headers: " +
163                     StringUtils.collectionToCommaDelimitedString(notUnderstoodHeaderNames) + ". Returning fault");
164         }
165         SoapBody responseBody = soapResponse.getSoapBody();
166         SoapFault fault =
167                 responseBody.addMustUnderstandFault(mustUnderstandFaultString, mustUnderstandFaultStringLocale);
168         if (!ObjectUtils.isEmpty(actorsOrRoles)) {
169             fault.setFaultActorOrRole(actorsOrRoles[0]);
170         }
171         SoapHeader header = soapResponse.getSoapHeader();
172         if (header instanceof Soap12Header) {
173             Soap12Header soap12Header = (Soap12Header) header;
174             for (Iterator iterator = notUnderstoodHeaderNames.iterator(); iterator.hasNext();) {
175                 QName headerName = (QName) iterator.next();
176                 soap12Header.addNotUnderstoodHeaderElement(headerName);
177             }
178         }
179     }
180 
181 }