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