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 }