Skip to content

Commit 82bf6ee

Browse files
committed
SWS-81
1 parent 7841ed6 commit 82bf6ee

File tree

5 files changed

+507
-31
lines changed

5 files changed

+507
-31
lines changed

core/src/main/java/org/springframework/ws/server/endpoint/AbstractMarshallingPayloadEndpoint.java

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import org.springframework.oxm.Marshaller;
2525
import org.springframework.oxm.Unmarshaller;
2626
import org.springframework.util.Assert;
27-
import org.springframework.validation.Validator;
2827
import org.springframework.ws.WebServiceMessage;
2928
import org.springframework.ws.context.MessageContext;
3029
import org.springframework.ws.support.MarshallingUtils;
@@ -51,8 +50,6 @@ public abstract class AbstractMarshallingPayloadEndpoint implements MessageEndpo
5150

5251
private Unmarshaller unmarshaller;
5352

54-
private Validator[] validators;
55-
5653
/**
5754
* Creates a new <code>AbstractMarshallingPayloadEndpoint</code>. The {@link Marshaller} and {@link Unmarshaller}
5855
* must be injected using properties.
@@ -122,30 +119,6 @@ public final void setUnmarshaller(Unmarshaller unmarshaller) {
122119
this.unmarshaller = unmarshaller;
123120
}
124121

125-
/**
126-
* Set the primary {@link Validator} for this endpoint. The {@link Validator} is must support the specified command
127-
* class. If there are one or more existing validators set already when this method is called, only the specified
128-
* validator will be kept. Use {@link #setValidators(Validator[])} to set multiple validators.
129-
*/
130-
public final void setValidator(Validator validator) {
131-
this.validators = new Validator[]{validator};
132-
}
133-
134-
/** Return the primary Validator for this controller. */
135-
public final Validator getValidator() {
136-
return (this.validators != null && this.validators.length > 0 ? this.validators[0] : null);
137-
}
138-
139-
/** Set the Validators for this controller. The Validator must support the specified command class. */
140-
public final void setValidators(Validator[] validators) {
141-
this.validators = validators;
142-
}
143-
144-
/** Return the Validators for this controller. */
145-
public final Validator[] getValidators() {
146-
return validators;
147-
}
148-
149122
public final void afterPropertiesSet() throws Exception {
150123
Assert.notNull(getMarshaller(), "marshaller is required");
151124
Assert.notNull(getUnmarshaller(), "unmarshaller is required");
@@ -155,10 +128,13 @@ public final void afterPropertiesSet() throws Exception {
155128
public final void invoke(MessageContext messageContext) throws Exception {
156129
WebServiceMessage request = messageContext.getRequest();
157130
Object requestObject = unmarshalRequest(request);
158-
Object responseObject = invokeInternal(requestObject);
159-
if (responseObject != null) {
160-
WebServiceMessage response = messageContext.getResponse();
161-
marshalResponse(responseObject, response);
131+
if (onUnmarshalRequest(messageContext, requestObject)) {
132+
Object responseObject = invokeInternal(requestObject);
133+
if (responseObject != null) {
134+
WebServiceMessage response = messageContext.getResponse();
135+
marshalResponse(responseObject, response);
136+
onMarshalResponse(messageContext, requestObject, responseObject);
137+
}
162138
}
163139
}
164140

@@ -170,13 +146,40 @@ private Object unmarshalRequest(WebServiceMessage request) throws IOException {
170146
return requestObject;
171147
}
172148

149+
/**
150+
* Callback for post-processing in terms of unmarshalling. Called on each message request, after standard
151+
* unmarshalling.
152+
* <p/>
153+
* Default implementation returns <code>true</code>.
154+
*
155+
* @param messageContext the message context
156+
* @param requestObject the object unmarshalled from the {@link MessageContext#getRequest() request}
157+
* @return <code>true</code> to continue and call {@link #invokeInternal(Object)}; <code>false</code> otherwise
158+
*/
159+
protected boolean onUnmarshalRequest(MessageContext messageContext, Object requestObject) throws Exception {
160+
return true;
161+
}
162+
173163
private void marshalResponse(Object responseObject, WebServiceMessage response) throws IOException {
174164
if (logger.isDebugEnabled()) {
175165
logger.debug("Marshalling [" + responseObject + "] to response payload");
176166
}
177167
MarshallingUtils.marshal(getMarshaller(), responseObject, response);
178168
}
179169

170+
/**
171+
* Callback for post-processing in terms of marshalling. Called on each message request, after standard marshalling
172+
* of the response. Only invoked when {@link #invokeInternal(Object)} returns an object.
173+
* <p/>
174+
* Default implementation is empty.
175+
*
176+
* @param messageContext the message context
177+
* @param requestObject the object unmarshalled from the {@link MessageContext#getRequest() request}
178+
* @param responseObject the object marshalled to the {@link MessageContext#getResponse()} request}
179+
*/
180+
protected void onMarshalResponse(MessageContext messageContext, Object requestObject, Object responseObject) {
181+
}
182+
180183
/**
181184
* Template method that gets called after the marshaller and unmarshaller have been set.
182185
* <p/>
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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.server.endpoint;
18+
19+
import org.springframework.validation.BeanPropertyBindingResult;
20+
import org.springframework.validation.Errors;
21+
import org.springframework.validation.ValidationUtils;
22+
import org.springframework.validation.Validator;
23+
import org.springframework.ws.context.MessageContext;
24+
25+
/**
26+
* Extension of the {@link AbstractMarshallingPayloadEndpoint} which validates the request payload with {@link
27+
* Validator}(s). The desired validators can be set using properties, and <strong>must</strong> {@link
28+
* Validator#supports(Class) support} the request object.
29+
*
30+
* @author Arjen Poutsma
31+
* @since 1.0.2
32+
*/
33+
public abstract class AbstractValidatingMarshallingPayloadEndpoint extends AbstractMarshallingPayloadEndpoint {
34+
35+
/** Default request object name used for validating request objects. */
36+
public static final String DEFAULT_REQUEST_NAME = "request";
37+
38+
private String requestName = DEFAULT_REQUEST_NAME;
39+
40+
private Validator[] validators;
41+
42+
/** Return the name of the request object for validation error codes. */
43+
public final String getRequestName() {
44+
return requestName;
45+
}
46+
47+
/** Set the name of the request object user for validation errors. */
48+
public final void setRequestName(String requestName) {
49+
this.requestName = requestName;
50+
}
51+
52+
/** Return the primary Validator for this controller. */
53+
public final Validator getValidator() {
54+
return (this.validators != null && this.validators.length > 0 ? this.validators[0] : null);
55+
}
56+
57+
/**
58+
* Set the primary {@link Validator} for this endpoint. The {@link Validator} is must support the unmarshalled
59+
* class. If there are one or more existing validators set already when this method is called, only the specified
60+
* validator will be kept. Use {@link #setValidators(Validator[])} to set multiple validators.
61+
*/
62+
public final void setValidator(Validator validator) {
63+
this.validators = new Validator[]{validator};
64+
}
65+
66+
/** Return the Validators for this controller. */
67+
public final Validator[] getValidators() {
68+
return validators;
69+
}
70+
71+
/** Set the Validators for this controller. The Validator must support the specified command class. */
72+
public final void setValidators(Validator[] validators) {
73+
this.validators = validators;
74+
}
75+
76+
protected final boolean onUnmarshalRequest(MessageContext messageContext, Object requestObject) throws Exception {
77+
if (validators != null) {
78+
Errors errors = new BeanPropertyBindingResult(requestObject, getRequestName());
79+
for (int i = 0; i < validators.length; i++) {
80+
ValidationUtils.invokeValidator(validators[i], requestObject, errors);
81+
}
82+
if (errors.hasErrors()) {
83+
return onValidationErrors(messageContext, requestObject, errors);
84+
}
85+
}
86+
return true;
87+
}
88+
89+
/**
90+
* Callback for post-processing validation errors. Called when validator(s) have been specified, and validation
91+
* fails.
92+
*
93+
* @param messageContext the message context
94+
* @param requestObject the object unmarshalled from the {@link MessageContext#getRequest() request}
95+
* @param errors validation errors holder
96+
* @return <code>true</code> to continue and call {@link #invokeInternal(Object)}; <code>false</code> otherwise
97+
*/
98+
protected abstract boolean onValidationErrors(MessageContext messageContext, Object requestObject, Errors errors);
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
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+
}

0 commit comments

Comments
 (0)