diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/AbstractMarshallingPayloadEndpoint.java b/core/src/main/java/org/springframework/ws/server/endpoint/AbstractMarshallingPayloadEndpoint.java
index e164a090..fa36de9a 100644
--- a/core/src/main/java/org/springframework/ws/server/endpoint/AbstractMarshallingPayloadEndpoint.java
+++ b/core/src/main/java/org/springframework/ws/server/endpoint/AbstractMarshallingPayloadEndpoint.java
@@ -24,7 +24,6 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.Unmarshaller;
import org.springframework.util.Assert;
-import org.springframework.validation.Validator;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.support.MarshallingUtils;
@@ -51,8 +50,6 @@ public abstract class AbstractMarshallingPayloadEndpoint implements MessageEndpo
private Unmarshaller unmarshaller;
- private Validator[] validators;
-
/**
* Creates a new AbstractMarshallingPayloadEndpoint. The {@link Marshaller} and {@link Unmarshaller}
* must be injected using properties.
@@ -122,30 +119,6 @@ public abstract class AbstractMarshallingPayloadEndpoint implements MessageEndpo
this.unmarshaller = unmarshaller;
}
- /**
- * Set the primary {@link Validator} for this endpoint. The {@link Validator} is must support the specified command
- * class. If there are one or more existing validators set already when this method is called, only the specified
- * validator will be kept. Use {@link #setValidators(Validator[])} to set multiple validators.
- */
- public final void setValidator(Validator validator) {
- this.validators = new Validator[]{validator};
- }
-
- /** Return the primary Validator for this controller. */
- public final Validator getValidator() {
- return (this.validators != null && this.validators.length > 0 ? this.validators[0] : null);
- }
-
- /** Set the Validators for this controller. The Validator must support the specified command class. */
- public final void setValidators(Validator[] validators) {
- this.validators = validators;
- }
-
- /** Return the Validators for this controller. */
- public final Validator[] getValidators() {
- return validators;
- }
-
public final void afterPropertiesSet() throws Exception {
Assert.notNull(getMarshaller(), "marshaller is required");
Assert.notNull(getUnmarshaller(), "unmarshaller is required");
@@ -155,10 +128,13 @@ public abstract class AbstractMarshallingPayloadEndpoint implements MessageEndpo
public final void invoke(MessageContext messageContext) throws Exception {
WebServiceMessage request = messageContext.getRequest();
Object requestObject = unmarshalRequest(request);
- Object responseObject = invokeInternal(requestObject);
- if (responseObject != null) {
- WebServiceMessage response = messageContext.getResponse();
- marshalResponse(responseObject, response);
+ if (onUnmarshalRequest(messageContext, requestObject)) {
+ Object responseObject = invokeInternal(requestObject);
+ if (responseObject != null) {
+ WebServiceMessage response = messageContext.getResponse();
+ marshalResponse(responseObject, response);
+ onMarshalResponse(messageContext, requestObject, responseObject);
+ }
}
}
@@ -170,6 +146,20 @@ public abstract class AbstractMarshallingPayloadEndpoint implements MessageEndpo
return requestObject;
}
+ /**
+ * Callback for post-processing in terms of unmarshalling. Called on each message request, after standard
+ * unmarshalling.
+ *
true.
+ *
+ * @param messageContext the message context
+ * @param requestObject the object unmarshalled from the {@link MessageContext#getRequest() request}
+ * @return true to continue and call {@link #invokeInternal(Object)}; false otherwise
+ */
+ protected boolean onUnmarshalRequest(MessageContext messageContext, Object requestObject) throws Exception {
+ return true;
+ }
+
private void marshalResponse(Object responseObject, WebServiceMessage response) throws IOException {
if (logger.isDebugEnabled()) {
logger.debug("Marshalling [" + responseObject + "] to response payload");
@@ -177,6 +167,19 @@ public abstract class AbstractMarshallingPayloadEndpoint implements MessageEndpo
MarshallingUtils.marshal(getMarshaller(), responseObject, response);
}
+ /**
+ * Callback for post-processing in terms of marshalling. Called on each message request, after standard marshalling
+ * of the response. Only invoked when {@link #invokeInternal(Object)} returns an object.
+ *
+ * Default implementation is empty.
+ *
+ * @param messageContext the message context
+ * @param requestObject the object unmarshalled from the {@link MessageContext#getRequest() request}
+ * @param responseObject the object marshalled to the {@link MessageContext#getResponse()} request}
+ */
+ protected void onMarshalResponse(MessageContext messageContext, Object requestObject, Object responseObject) {
+ }
+
/**
* Template method that gets called after the marshaller and unmarshaller have been set.
*
diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/AbstractValidatingMarshallingPayloadEndpoint.java b/core/src/main/java/org/springframework/ws/server/endpoint/AbstractValidatingMarshallingPayloadEndpoint.java
new file mode 100644
index 00000000..fcde758a
--- /dev/null
+++ b/core/src/main/java/org/springframework/ws/server/endpoint/AbstractValidatingMarshallingPayloadEndpoint.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ws.server.endpoint;
+
+import org.springframework.validation.BeanPropertyBindingResult;
+import org.springframework.validation.Errors;
+import org.springframework.validation.ValidationUtils;
+import org.springframework.validation.Validator;
+import org.springframework.ws.context.MessageContext;
+
+/**
+ * Extension of the {@link AbstractMarshallingPayloadEndpoint} which validates the request payload with {@link
+ * Validator}(s). The desired validators can be set using properties, and must {@link
+ * Validator#supports(Class) support} the request object.
+ *
+ * @author Arjen Poutsma
+ * @since 1.0.2
+ */
+public abstract class AbstractValidatingMarshallingPayloadEndpoint extends AbstractMarshallingPayloadEndpoint {
+
+ /** Default request object name used for validating request objects. */
+ public static final String DEFAULT_REQUEST_NAME = "request";
+
+ private String requestName = DEFAULT_REQUEST_NAME;
+
+ private Validator[] validators;
+
+ /** Return the name of the request object for validation error codes. */
+ public final String getRequestName() {
+ return requestName;
+ }
+
+ /** Set the name of the request object user for validation errors. */
+ public final void setRequestName(String requestName) {
+ this.requestName = requestName;
+ }
+
+ /** Return the primary Validator for this controller. */
+ public final Validator getValidator() {
+ return (this.validators != null && this.validators.length > 0 ? this.validators[0] : null);
+ }
+
+ /**
+ * Set the primary {@link Validator} for this endpoint. The {@link Validator} is must support the unmarshalled
+ * class. If there are one or more existing validators set already when this method is called, only the specified
+ * validator will be kept. Use {@link #setValidators(Validator[])} to set multiple validators.
+ */
+ public final void setValidator(Validator validator) {
+ this.validators = new Validator[]{validator};
+ }
+
+ /** Return the Validators for this controller. */
+ public final Validator[] getValidators() {
+ return validators;
+ }
+
+ /** Set the Validators for this controller. The Validator must support the specified command class. */
+ public final void setValidators(Validator[] validators) {
+ this.validators = validators;
+ }
+
+ protected final boolean onUnmarshalRequest(MessageContext messageContext, Object requestObject) throws Exception {
+ if (validators != null) {
+ Errors errors = new BeanPropertyBindingResult(requestObject, getRequestName());
+ for (int i = 0; i < validators.length; i++) {
+ ValidationUtils.invokeValidator(validators[i], requestObject, errors);
+ }
+ if (errors.hasErrors()) {
+ return onValidationErrors(messageContext, requestObject, errors);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Callback for post-processing validation errors. Called when validator(s) have been specified, and validation
+ * fails.
+ *
+ * @param messageContext the message context
+ * @param requestObject the object unmarshalled from the {@link MessageContext#getRequest() request}
+ * @param errors validation errors holder
+ * @return true to continue and call {@link #invokeInternal(Object)}; false otherwise
+ */
+ protected abstract boolean onValidationErrors(MessageContext messageContext, Object requestObject, Errors errors);
+}
diff --git a/core/src/main/java/org/springframework/ws/soap/server/endpoint/AbstractFaultCreatingValidatingMarshallingPayloadEndpoint.java b/core/src/main/java/org/springframework/ws/soap/server/endpoint/AbstractFaultCreatingValidatingMarshallingPayloadEndpoint.java
new file mode 100644
index 00000000..3e77ae0e
--- /dev/null
+++ b/core/src/main/java/org/springframework/ws/soap/server/endpoint/AbstractFaultCreatingValidatingMarshallingPayloadEndpoint.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ws.soap.server.endpoint;
+
+import java.util.Iterator;
+import java.util.Locale;
+import javax.xml.namespace.QName;
+
+import org.springframework.context.MessageSource;
+import org.springframework.context.MessageSourceAware;
+import org.springframework.validation.Errors;
+import org.springframework.validation.ObjectError;
+import org.springframework.validation.Validator;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.server.endpoint.AbstractValidatingMarshallingPayloadEndpoint;
+import org.springframework.ws.soap.SoapBody;
+import org.springframework.ws.soap.SoapFault;
+import org.springframework.ws.soap.SoapFaultDetail;
+import org.springframework.ws.soap.SoapFaultDetailElement;
+import org.springframework.ws.soap.SoapMessage;
+import org.springframework.xml.namespace.QNameUtils;
+
+/**
+ * Extension of the {@link AbstractValidatingMarshallingPayloadEndpoint} which validates the request payload with {@link
+ * Validator}(s), and creates a SOAP Fault whenever the request message cannot be validated. The desired validators can
+ * be set using properties, and must {@link Validator#supports(Class) support} the request object.
+ *
+ * The contents of the SOAP Fault can be specified by setting the {@link #setAddValidationErrorDetail(boolean)
+ * addValidationErrorDetail}, {@link #setFaultStringOrReason(String) faultStringOrReason}, or {@link
+ * #setDetailElementName(QName) detailElementName} properties.
+ *
+ * @author Arjen Poutsma
+ * @since 1.0.2
+ */
+public abstract class AbstractFaultCreatingValidatingMarshallingPayloadEndpoint
+ extends AbstractValidatingMarshallingPayloadEndpoint implements MessageSourceAware {
+
+ /**
+ * Default SOAP Fault Detail name used when a global validation error occur on the request.
+ *
+ * @see #setDetailElementName(javax.xml.namespace.QName)
+ */
+ public static final QName DEFAULT_DETAIL_ELEMENT_NAME =
+ QNameUtils.createQName("http://springframework.org/spring-ws", "ValidationError", "spring-ws");
+
+ /**
+ * Default SOAP Fault string used when a validation errors occur on the request.
+ *
+ * @see #setFaultStringOrReason(String)
+ */
+ public static final String DEFAULT_FAULTSTRING_OR_REASON = "Validation error";
+
+ private boolean addValidationErrorDetail = true;
+
+ private QName detailElementName = DEFAULT_DETAIL_ELEMENT_NAME;
+
+ private String faultStringOrReason = DEFAULT_FAULTSTRING_OR_REASON;
+
+ private Locale faultStringOrReasonLocale = Locale.ENGLISH;
+
+ private MessageSource messageSource;
+
+ /**
+ * Returns whether a SOAP Fault detail element should be created when a validation error occurs. This detail element
+ * will contain the exact validation errors. It is only added when the underlying message is a
+ * SoapMessage. Defaults to true.
+ *
+ * @see org.springframework.ws.soap.SoapFault#addFaultDetail()
+ */
+ public boolean getAddValidationErrorDetail() {
+ return addValidationErrorDetail;
+ }
+
+ /**
+ * Indicates whether a SOAP Fault detail element should be created when a validation error occurs. This detail
+ * element will contain the exact validation errors. It is only added when the underlying message is a
+ * SoapMessage. Defaults to true.
+ *
+ * @see org.springframework.ws.soap.SoapFault#addFaultDetail()
+ */
+ public void setAddValidationErrorDetail(boolean addValidationErrorDetail) {
+ this.addValidationErrorDetail = addValidationErrorDetail;
+ }
+
+ /** Returns the fault detail element name when validation errors occur on the request. */
+ public QName getDetailElementName() {
+ return detailElementName;
+ }
+
+ /**
+ * Sets the fault detail element name when validation errors occur on the request. Defaults to
+ * DEFAULT_DETAIL_ELEMENT_NAME.
+ *
+ * @see #DEFAULT_DETAIL_ELEMENT_NAME
+ */
+ public void setDetailElementName(QName detailElementName) {
+ this.detailElementName = detailElementName;
+ }
+
+ /** Sets the SOAP faultstring or Reason used when validation errors occur on the request. */
+ public String getFaultStringOrReason() {
+ return faultStringOrReason;
+ }
+
+ /**
+ * Sets the SOAP faultstring or Reason used when validation errors occur on the request.
+ * It is only added when the underlying message is a SoapMessage. Defaults to
+ * DEFAULT_FAULTSTRING_OR_REASON.
+ *
+ * @see #DEFAULT_FAULTSTRING_OR_REASON
+ */
+ public void setFaultStringOrReason(String faultStringOrReason) {
+ this.faultStringOrReason = faultStringOrReason;
+ }
+
+ /** Returns the locale for SOAP fault reason and validation message resolution. */
+ public Locale getFaultLocale() {
+ return faultStringOrReasonLocale;
+ }
+
+ /**
+ * Sets the locale for SOAP fault reason and validation messages. It is only added when the underlying message is a
+ * SoapMessage. Defaults to English.
+ *
+ * @see java.util.Locale#ENGLISH
+ */
+ public void setFaultStringOrReasonLocale(Locale faultStringOrReasonLocale) {
+ this.faultStringOrReasonLocale = faultStringOrReasonLocale;
+ }
+
+ public final void setMessageSource(MessageSource messageSource) {
+ this.messageSource = messageSource;
+ }
+
+ /**
+ * This implementation logs all errors, returns false, and creates a {@link
+ * SoapBody#addClientOrSenderFault(String,Locale) client or sender} {@link SoapFault}, adding a {@link
+ * SoapFaultDetail} with all errors if the addValidationErrorDetail property is true.
+ *
+ * @param messageContext the message context
+ * @param errors the validation errors
+ * @return true to continue processing the request, false (the default) otherwise
+ * @see Errors#getAllErrors()
+ */
+ protected final boolean onValidationErrors(MessageContext messageContext, Object requestObject, Errors errors) {
+ for (Iterator iterator = errors.getAllErrors().iterator(); iterator.hasNext();) {
+ ObjectError objectError = (ObjectError) iterator.next();
+ String msg = messageSource.getMessage(objectError, getFaultLocale());
+ logger.warn("Validation error on request object[" + requestObject + "]: " + msg);
+ }
+ if (messageContext.getResponse() instanceof SoapMessage) {
+ SoapMessage response = (SoapMessage) messageContext.getResponse();
+ SoapBody body = response.getSoapBody();
+ SoapFault fault = body.addClientOrSenderFault(getFaultStringOrReason(), getFaultLocale());
+ if (getAddValidationErrorDetail()) {
+ SoapFaultDetail detail = fault.addFaultDetail();
+ for (Iterator iterator = errors.getAllErrors().iterator(); iterator.hasNext();) {
+ ObjectError objectError = (ObjectError) iterator.next();
+ String msg = messageSource.getMessage(objectError, getFaultLocale());
+ SoapFaultDetailElement detailElement = detail.addFaultDetailElement(getDetailElementName());
+ detailElement.addText(msg);
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/core/src/test/java/org/springframework/ws/soap/server/endpoint/FaultCreatingValidatingMarshallingPayloadEndpointTest.java b/core/src/test/java/org/springframework/ws/soap/server/endpoint/FaultCreatingValidatingMarshallingPayloadEndpointTest.java
new file mode 100644
index 00000000..efd6892d
--- /dev/null
+++ b/core/src/test/java/org/springframework/ws/soap/server/endpoint/FaultCreatingValidatingMarshallingPayloadEndpointTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright ${YEAR} the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ws.soap.server.endpoint;
+
+import java.io.IOException;
+import java.util.Iterator;
+import javax.xml.namespace.QName;
+import javax.xml.soap.Detail;
+import javax.xml.soap.DetailEntry;
+import javax.xml.soap.MessageFactory;
+import javax.xml.soap.SOAPFault;
+import javax.xml.soap.SOAPMessage;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+
+import junit.framework.TestCase;
+import org.springframework.context.support.ResourceBundleMessageSource;
+import org.springframework.oxm.Marshaller;
+import org.springframework.oxm.Unmarshaller;
+import org.springframework.oxm.XmlMappingException;
+import org.springframework.validation.Errors;
+import org.springframework.validation.ValidationUtils;
+import org.springframework.validation.Validator;
+import org.springframework.ws.context.DefaultMessageContext;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.soap.saaj.SaajSoapMessage;
+import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
+
+public class FaultCreatingValidatingMarshallingPayloadEndpointTest extends TestCase {
+
+ private MessageContext messageContext;
+
+ private ResourceBundleMessageSource messageSource;
+
+ protected void setUp() throws Exception {
+ this.messageSource = new ResourceBundleMessageSource();
+ this.messageSource.setBasename("org.springframework.ws.soap.server.endpoint.messages");
+ MessageFactory messageFactory = MessageFactory.newInstance();
+ SOAPMessage request = messageFactory.createMessage();
+ request.getSOAPBody().addBodyElement(new QName("http://www.springframework.org/spring-ws", "request"));
+ messageContext =
+ new DefaultMessageContext(new SaajSoapMessage(request), new SaajSoapMessageFactory(messageFactory));
+ }
+
+ public void testValidationIncorrect() throws Exception {
+ Person p = new Person("", -1);
+ PersonMarshaller marshaller = new PersonMarshaller(p);
+
+ AbstractFaultCreatingValidatingMarshallingPayloadEndpoint endpoint =
+ new AbstractFaultCreatingValidatingMarshallingPayloadEndpoint() {
+
+ protected Object invokeInternal(Object requestObject) throws Exception {
+ fail("No expected");
+ return null;
+ }
+ };
+ endpoint.setValidator(new PersonValidator());
+ endpoint.setMessageSource(messageSource);
+ endpoint.setMarshaller(marshaller);
+ endpoint.setUnmarshaller(marshaller);
+
+ endpoint.invoke(messageContext);
+
+ SOAPMessage response = ((SaajSoapMessage) messageContext.getResponse()).getSaajMessage();
+ assertTrue("Response has no fault", response.getSOAPBody().hasFault());
+ SOAPFault fault = response.getSOAPBody().getFault();
+ assertEquals("Invalid fault code", new QName("http://schemas.xmlsoap.org/soap/envelope/", "Client"),
+ fault.getFaultCodeAsQName());
+ assertEquals("Invalid fault string", endpoint.getFaultStringOrReason(), fault.getFaultString());
+ Detail detail = fault.getDetail();
+ assertNotNull("No detail", detail);
+ Iterator iterator = detail.getDetailEntries();
+ assertTrue("No detail entry", iterator.hasNext());
+ DetailEntry detailEntry = (DetailEntry) iterator.next();
+ assertEquals("Invalid detail entry name", new QName("http://springframework.org/spring-ws", "ValidationError"),
+ detailEntry.getElementQName());
+ assertEquals("Invalid detail entry text", "Name is required", detailEntry.getTextContent());
+ assertTrue("No detail entry", iterator.hasNext());
+ detailEntry = (DetailEntry) iterator.next();
+ assertEquals("Invalid detail entry name", new QName("http://springframework.org/spring-ws", "ValidationError"),
+ detailEntry.getElementQName());
+ assertEquals("Invalid detail entry text", "Age Cannot be negative", detailEntry.getTextContent());
+ assertFalse("Too many detail entries", iterator.hasNext());
+ }
+
+ public void testValidationCorrect() throws Exception {
+ Person p = new Person("John", 42);
+ PersonMarshaller marshaller = new PersonMarshaller(p);
+ AbstractFaultCreatingValidatingMarshallingPayloadEndpoint endpoint =
+ new AbstractFaultCreatingValidatingMarshallingPayloadEndpoint() {
+
+ protected Object invokeInternal(Object requestObject) throws Exception {
+ return null;
+ }
+ };
+ endpoint.setValidator(new PersonValidator());
+ endpoint.setMessageSource(messageSource);
+ endpoint.setMarshaller(marshaller);
+ endpoint.setUnmarshaller(marshaller);
+
+ endpoint.invoke(messageContext);
+
+ SOAPMessage response = ((SaajSoapMessage) messageContext.getResponse()).getSaajMessage();
+ assertFalse("Response has fault", response.getSOAPBody().hasFault());
+ }
+
+ private static class PersonValidator implements Validator {
+
+ public boolean supports(Class clazz) {
+ return Person.class.equals(clazz);
+ }
+
+ public void validate(Object obj, Errors e) {
+ ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
+ Person p = (Person) obj;
+ if (p.getAge() < 0) {
+ e.rejectValue("age", "age.negativevalue");
+ }
+ else if (p.getAge() > 110) {
+ e.rejectValue("age", "too.darn.old");
+ }
+ }
+ }
+
+ private static class Person {
+
+ private String name;
+
+ private int age;
+
+ private Person(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public String toString() {
+ return "Person{" + name + "," + age + "}";
+ }
+ }
+
+ private static class PersonMarshaller implements Unmarshaller, Marshaller {
+
+ private final Person person;
+
+ private PersonMarshaller(Person person) {
+ this.person = person;
+ }
+
+ public Object unmarshal(Source source) throws XmlMappingException, IOException {
+ return person;
+ }
+
+ public boolean supports(Class clazz) {
+ return Person.class.equals(clazz);
+ }
+
+ public void marshal(Object graph, Result result) throws XmlMappingException, IOException {
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/test/resources/org/springframework/ws/soap/server/endpoint/messages.properties b/core/src/test/resources/org/springframework/ws/soap/server/endpoint/messages.properties
new file mode 100644
index 00000000..c5ebafc7
--- /dev/null
+++ b/core/src/test/resources/org/springframework/ws/soap/server/endpoint/messages.properties
@@ -0,0 +1,2 @@
+name.empty=Name is required
+age.negativevalue=Age Cannot be negative
\ No newline at end of file