View Javadoc

1   /*
2    * Copyright 2005-2010 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      @Override
84      protected boolean handleRequest(EndpointInvocationChain mappedEndpoint, MessageContext messageContext) {
85          if (messageContext.getRequest() instanceof SoapMessage) {
86              String[] actorsOrRoles = null;
87              boolean isUltimateReceiver = true;
88              if (mappedEndpoint instanceof SoapEndpointInvocationChain) {
89                  SoapEndpointInvocationChain soapChain = (SoapEndpointInvocationChain) mappedEndpoint;
90                  actorsOrRoles = soapChain.getActorsOrRoles();
91                  isUltimateReceiver = soapChain.isUltimateReceiver();
92              }
93              return handleHeaders(mappedEndpoint, messageContext, actorsOrRoles, isUltimateReceiver);
94          }
95          return true;
96      }
97  
98      private boolean handleHeaders(EndpointInvocationChain mappedEndpoint,
99                                    MessageContext messageContext,
100                                   String[] actorsOrRoles,
101                                   boolean isUltimateReceiver) {
102         SoapMessage soapRequest = (SoapMessage) messageContext.getRequest();
103         SoapHeader soapHeader = soapRequest.getSoapHeader();
104         if (soapHeader == null) {
105             return true;
106         }
107         Iterator<SoapHeaderElement> headerIterator;
108         if (soapHeader instanceof Soap11Header) {
109             headerIterator = ((Soap11Header) soapHeader).examineHeaderElementsToProcess(actorsOrRoles);
110         }
111         else {
112             headerIterator =
113                     ((Soap12Header) soapHeader).examineHeaderElementsToProcess(actorsOrRoles, isUltimateReceiver);
114         }
115         List<QName> notUnderstoodHeaderNames = new ArrayList<QName>();
116         while (headerIterator.hasNext()) {
117             SoapHeaderElement headerElement = headerIterator.next();
118             QName headerName = headerElement.getName();
119             if (headerElement.getMustUnderstand() && logger.isDebugEnabled()) {
120                 logger.debug("Handling MustUnderstand header " + headerName);
121             }
122             if (headerElement.getMustUnderstand() && !headerUnderstood(mappedEndpoint, headerElement)) {
123                 notUnderstoodHeaderNames.add(headerName);
124             }
125         }
126         if (notUnderstoodHeaderNames.isEmpty()) {
127             return true;
128         }
129         else {
130             SoapMessage response = (SoapMessage) messageContext.getResponse();
131             createMustUnderstandFault(response, notUnderstoodHeaderNames, actorsOrRoles);
132             return false;
133         }
134     }
135 
136     /**
137      * Handles the request for a single SOAP actor/role. Iterates over all <code>MustUnderstand</code> headers for a
138      * specific SOAP 1.1 actor or SOAP 1.2 role, and determines whether these are understood by any of the registered
139      * <code>SoapEndpointInterceptor</code>. If they are, returns <code>true</code>. If they are not, a SOAP fault is
140      * created, and false is returned.
141      *
142      * @see SoapEndpointInterceptor#understands(org.springframework.ws.soap.SoapHeaderElement)
143      */
144     private boolean headerUnderstood(EndpointInvocationChain mappedEndpoint, SoapHeaderElement headerElement) {
145         EndpointInterceptor[] interceptors = mappedEndpoint.getInterceptors();
146         if (ObjectUtils.isEmpty(interceptors)) {
147             return false;
148         }
149         for (EndpointInterceptor interceptor : interceptors) {
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<QName> 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 (QName headerName : notUnderstoodHeaderNames) {
175                 soap12Header.addNotUnderstoodHeaderElement(headerName);
176             }
177         }
178     }
179 
180 }