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.server.endpoint;
18  
19  import java.util.Iterator;
20  import java.util.Locale;
21  import javax.xml.namespace.QName;
22  
23  import org.springframework.context.MessageSource;
24  import org.springframework.context.MessageSourceAware;
25  import org.springframework.validation.Errors;
26  import org.springframework.validation.ObjectError;
27  import org.springframework.validation.Validator;
28  import org.springframework.ws.context.MessageContext;
29  import org.springframework.ws.server.endpoint.AbstractValidatingMarshallingPayloadEndpoint;
30  import org.springframework.ws.soap.SoapBody;
31  import org.springframework.ws.soap.SoapFault;
32  import org.springframework.ws.soap.SoapFaultDetail;
33  import org.springframework.ws.soap.SoapFaultDetailElement;
34  import org.springframework.ws.soap.SoapMessage;
35  import org.springframework.xml.namespace.QNameUtils;
36  
37  /**
38   * Extension of the {@link AbstractValidatingMarshallingPayloadEndpoint} which validates the request payload with {@link
39   * Validator}(s), and creates a SOAP Fault whenever the request message cannot be validated. The desired validators can
40   * be set using properties, and <strong>must</strong> {@link Validator#supports(Class) support} the request object.
41   * <p/>
42   * The contents of the SOAP Fault can be specified by setting the {@link #setAddValidationErrorDetail(boolean)
43   * addValidationErrorDetail}, {@link #setFaultStringOrReason(String) faultStringOrReason}, or  {@link
44   * #setDetailElementName(QName) detailElementName} properties.
45   *
46   * @author Arjen Poutsma
47   * @since 1.0.2
48   */
49  public abstract class AbstractFaultCreatingValidatingMarshallingPayloadEndpoint
50          extends AbstractValidatingMarshallingPayloadEndpoint implements MessageSourceAware {
51  
52      /**
53       * Default SOAP Fault Detail name used when a global validation error occur on the request.
54       *
55       * @see #setDetailElementName(javax.xml.namespace.QName)
56       */
57      public static final QName DEFAULT_DETAIL_ELEMENT_NAME =
58              QNameUtils.createQName("http://springframework.org/spring-ws", "ValidationError", "spring-ws");
59  
60      /**
61       * Default SOAP Fault string used when a validation errors occur on the request.
62       *
63       * @see #setFaultStringOrReason(String)
64       */
65      public static final String DEFAULT_FAULTSTRING_OR_REASON = "Validation error";
66  
67      private boolean addValidationErrorDetail = true;
68  
69      private QName detailElementName = DEFAULT_DETAIL_ELEMENT_NAME;
70  
71      private String faultStringOrReason = DEFAULT_FAULTSTRING_OR_REASON;
72  
73      private Locale faultStringOrReasonLocale = Locale.ENGLISH;
74  
75      private MessageSource messageSource;
76  
77      /**
78       * Returns whether a SOAP Fault detail element should be created when a validation error occurs. This detail element
79       * will contain the exact validation errors. It is only added when the underlying message is a
80       * <code>SoapMessage</code>. Defaults to <code>true</code>.
81       *
82       * @see org.springframework.ws.soap.SoapFault#addFaultDetail()
83       */
84      public boolean getAddValidationErrorDetail() {
85          return addValidationErrorDetail;
86      }
87  
88      /**
89       * Indicates whether a SOAP Fault detail element should be created when a validation error occurs. This detail
90       * element will contain the exact validation errors. It is only added when the underlying message is a
91       * <code>SoapMessage</code>. Defaults to <code>true</code>.
92       *
93       * @see org.springframework.ws.soap.SoapFault#addFaultDetail()
94       */
95      public void setAddValidationErrorDetail(boolean addValidationErrorDetail) {
96          this.addValidationErrorDetail = addValidationErrorDetail;
97      }
98  
99      /** Returns the fault detail element name when validation errors occur on the request. */
100     public QName getDetailElementName() {
101         return detailElementName;
102     }
103 
104     /**
105      * Sets the fault detail element name when validation errors occur on the request. Defaults to
106      * <code>DEFAULT_DETAIL_ELEMENT_NAME</code>.
107      *
108      * @see #DEFAULT_DETAIL_ELEMENT_NAME
109      */
110     public void setDetailElementName(QName detailElementName) {
111         this.detailElementName = detailElementName;
112     }
113 
114     /** Sets the SOAP <code>faultstring</code> or <code>Reason</code> used when validation errors occur on the request. */
115     public String getFaultStringOrReason() {
116         return faultStringOrReason;
117     }
118 
119     /**
120      * Sets the SOAP <code>faultstring</code> or <code>Reason</code> used when validation errors occur on the request.
121      * It is only added when the underlying message is a <code>SoapMessage</code>. Defaults to
122      * <code>DEFAULT_FAULTSTRING_OR_REASON</code>.
123      *
124      * @see #DEFAULT_FAULTSTRING_OR_REASON
125      */
126     public void setFaultStringOrReason(String faultStringOrReason) {
127         this.faultStringOrReason = faultStringOrReason;
128     }
129 
130     /** Returns the locale for SOAP fault reason and validation message resolution. */
131     public Locale getFaultLocale() {
132         return faultStringOrReasonLocale;
133     }
134 
135     /**
136      * Sets the locale for SOAP fault reason and validation messages.  It is only added when the underlying message is a
137      * <code>SoapMessage</code>. Defaults to English.
138      *
139      * @see java.util.Locale#ENGLISH
140      */
141     public void setFaultStringOrReasonLocale(Locale faultStringOrReasonLocale) {
142         this.faultStringOrReasonLocale = faultStringOrReasonLocale;
143     }
144 
145     public final void setMessageSource(MessageSource messageSource) {
146         this.messageSource = messageSource;
147     }
148 
149     /**
150      * This implementation logs all errors, returns <code>false</code>, and creates a {@link
151      * SoapBody#addClientOrSenderFault(String,Locale) client or sender} {@link SoapFault}, adding a {@link
152      * SoapFaultDetail} with all errors if the <code>addValidationErrorDetail</code> property is <code>true</code>.
153      *
154      * @param messageContext the message context
155      * @param errors         the validation errors
156      * @return <code>true</code> to continue processing the request, <code>false</code> (the default) otherwise
157      * @see Errors#getAllErrors()
158      */
159     protected final boolean onValidationErrors(MessageContext messageContext, Object requestObject, Errors errors) {
160         for (Iterator iterator = errors.getAllErrors().iterator(); iterator.hasNext();) {
161             ObjectError objectError = (ObjectError) iterator.next();
162             String msg = messageSource.getMessage(objectError, getFaultLocale());
163             logger.warn("Validation error on request object[" + requestObject + "]: " + msg);
164         }
165         if (messageContext.getResponse() instanceof SoapMessage) {
166             SoapMessage response = (SoapMessage) messageContext.getResponse();
167             SoapBody body = response.getSoapBody();
168             SoapFault fault = body.addClientOrSenderFault(getFaultStringOrReason(), getFaultLocale());
169             if (getAddValidationErrorDetail()) {
170                 SoapFaultDetail detail = fault.addFaultDetail();
171                 for (Iterator iterator = errors.getAllErrors().iterator(); iterator.hasNext();) {
172                     ObjectError objectError = (ObjectError) iterator.next();
173                     String msg = messageSource.getMessage(objectError, getFaultLocale());
174                     SoapFaultDetailElement detailElement = detail.addFaultDetailElement(getDetailElementName());
175                     detailElement.addText(msg);
176                 }
177             }
178         }
179         return false;
180     }
181 }