From 6e9137e8603346155f46bd90f5843eca721ccc40 Mon Sep 17 00:00:00 2001
From: Gustavo Martin Morcuende <gu.martinm@gmail.com>
Date: Sat, 19 Dec 2015 22:43:11 +0100
Subject: [PATCH] jaxb2: many improvements about how Spring WS works

---
 HOWSPRINGWORKS.txt                                 | 138 ++++++++++++++++++
 .../src/main/resources/schemas/parent.xsd          |   6 +
 .../webservices/exceptions/BusinessException.java  |  22 +++
 .../impl/CustomBindingExampleServiceImpl.java      |   6 +
 .../src/main/resources/schemas/examples.xsd        |  13 +-
 .../resources/spring-configuration/ws/soap-ws.xml  | 155 +++++++++++++++++----
 6 files changed, 314 insertions(+), 26 deletions(-)
 create mode 100644 HOWSPRINGWORKS.txt
 create mode 100644 jaxb2/web-services-spring-jaxb2-server/src/main/java/de/spring/webservices/exceptions/BusinessException.java

diff --git a/HOWSPRINGWORKS.txt b/HOWSPRINGWORKS.txt
new file mode 100644
index 0000000..42f5276
--- /dev/null
+++ b/HOWSPRINGWORKS.txt
@@ -0,0 +1,138 @@
+
+
+*************************** Porque estoy usando sws:annotation-driven ***************************
+
+Porque estoy usando sws:annotation-driven y no declarando explícitamente los beans en XML existe un
+orden de búsqueda de por defecto de macheadores de endpoints y excepciones. Ver: org.springframework.ws.config.AnnotationDrivenBeanDefinitionParser
+Cuanto más bajo es el valor de order mayor es la prioridad.
+
+
+    	1. Manejadores de excepción, orden por defecto inicializado en AnnotationDrivenBeanDefinitionParser:
+    	a) SoapFaultAnnotationExceptionResolver será el primer manejador de excepciones que se intente usar por defecto. order = 0
+    	b) SimpleSoapExceptionResolver será el último manejador de excepciones que se intente usar por defecto. order = Ordered.LOWEST_PRECEDENCE
+	   Se usará si la excepción generada no pudo ser manejada por SoapFaultAnnotationExceptionResolver (porque la excepción no fue anotada con
+	   @SoapFault. Este manejador se traga cualquier excepción.
+    	
+    	2. Endpoints a buscar, orden por defecto inicializado en AnnotationDrivenBeanDefinitionParser:
+    	a) PayloadRootAnnotationMethodEndpointMapping será el primer tipo de endpoints que se buscará y que se intentará usar. order = 0
+	   Si el XML SOAP que llega no machea ningún metodo anotado con este EndPoint pasamos a b).
+    	b) SoapActionAnnotationMethodEndpointMapping será el segundo tipo de endpoints que se intentará usar. order = 1
+	   Si el XML SOAP que llega no machea ningún metodo anotado con este EndPoint pasamos a c).
+    	c) AnnotationActionEndpointMapping será el último tipo de endpoints que se buscará. order = 2
+	   Si el XML SOAP que llega no machea tampoco métodos anotado con este EndPoint se
+	   lanza NoEndpointFoundException desde org.springframework.ws.server.MessageDispatcher.dispatch()
+
+
+    	EN LUGAR DE USAR LA ANOTACIÓN PODRÍAMOS HABER DECLARADO EXPLÍCITAMENTE CADA BEAN TAL QUE ASÍ:
+    	
+    	<bean id="soapFaultMappingExceptionResolver"
+        	class="org.springframework.ws.soap.server.endpoint.SoapFaultMappingExceptionResolver">
+        	<property name="order" value="0" />
+    	</bean>
+    	
+    	<bean id="payloadRootAnnotationMethodEndpointMapping"
+        	class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
+        	<property name="order" value="0" />
+    	</bean>
+    	
+    	CON LA ANOTACIÓN ME AHORRO DECLARAR bean POR bean PERO LO MALO ES QUE INSTANCIO MANEJADORES QUE LUEGO NO USO :(
+
+
+
+
+*************************** org.springframework.ws.server.MessageDispatcher ***************************
+
+
+org.springframework.ws.server.MessageDispatcher ES LA CLASE QUE BUSCA EndPoints Y MANEJADORES DE EXCEPCIÓN
+
+ORDEN DE BUSQUEDA DE IMPLEMENTACIONES DE MANEJADORES DE EXCEPCION
+Busca en el ApplicationContext manejadores de excepción siguiendo este orden. Cuanto más bajo es el valor de order
+mayor es la prioridad.
+
+Por haber usado sws:annotation-driven el orden es el siguiente:
+
+
+1. Primero se busca por excepciones anotadas con @SoapFault. Si la excepcion generada
+está anotada son @SoapFault entonces se usa este manejador de excepcion.
+
+Implementation of the org.springframework.ws.server.EndpointExceptionResolver interface
+that uses the SoapFault annotation to map exceptions to SOAP Faults.
+org.springframework.ws.soap.server.endpoint.SoapFaultAnnotationExceptionResolver
+
+
+
+2. Segundo usa este manejador de excepción. Este manejador machea cualquier excepción así que si
+este manejador llega primero siempre resolverá la excepción.
+Simple, SOAP-specific EndpointExceptionResolver implementation that stores
+the exception's message as the fault string. 
+org.springframework.ws.soap.server.endpoint.SimpleSoapExceptionResolver
+
+
+3. Un manejador de excepciones inyectado por mi en un archivo XML de Spring. Si no se pone prioridad por defecto
+Spring le pone la prioridad más baja que es lo que me pasó a mí al principio y SimpleSoapExceptionResolver
+se meterá siempre por el medio :(
+org.springframework.ws.soap.server.endpoint.SoapFaultMappingExceptionResolver
+
+
+Si ninguno machea MessageDispatcher lanza la Excepción desde org.springframework.ws.server.MessageDispatcher.processEndpointException()
+
+
+
+ORDEN DE BUSQUEDA DE ENDPOINTS EN EL APPLICATION CONTEXT:
+Busca en el ApplicationContext metodos anotados siguiendo este orden. Si el XML SOAP que me llega
+machea con alguna de estas anotaciones, entonces ese método anotado será usado. Y NO SE CONTINUARÁ
+BUSCANDO MÁS MANEJADORES. EN CUANTO UNO MACHEA YA NO SE BUSCA POR MÁS.
+
+
+Por haber usado sws:annotation-driven el orden es el siguiente:
+
+
+1. Primero se busca por métodos de este modo. Si el XML SOAP machea con algún metodo anotado
+de este modo, entonces ese método será usado y no se continúa buscando matches.
+
+Implementation of the org.springframework.ws.server.EndpointMapping interface that uses the
+PayloadRoot annotation to map methods to request payload root elements. 
+org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping
+@Endpoint
+public class MyEndpoint {
+
+    @PayloadRoot(localPart = "Request", namespace = "http://springframework.org/spring-ws")
+    public Source doSomethingWithRequest() {
+         ...
+    }
+}
+
+2. Segundo se busca por métodos de este modo. Si el XML SOAP machea con algún metodo anotado
+de este modo, entonces ese método será usado y no se continúa buscando matches.
+
+Implementation of the org.springframework.ws.server.EndpointMapping interface that uses the
+SoapAction annotation to map methods to the request SOAPAction header. 
+org.springframework.ws.soap.server.endpoint.mapping.SoapActionAnnotationMethodEndpointMapping
+@Endpoint
+public class MyEndpoint{
+
+    @SoapAction("http://springframework.org/spring-ws/SoapAction")
+    public Source doSomethingWithRequest() {
+	 ...
+    }
+}
+
+
+3. Tercero se busca por métodos de este modo. Si el XML SOAP machea con algún metodo anotado
+de este modo, entonces ese método será usado.
+
+Implementation of the org.springframework.ws.server.EndpointMapping interface that uses the
+@Action annotation to map methods to a WS-Addressing Action header. 
+org.springframework.ws.soap.addressing.server.AnnotationActionEndpointMapping
+@Endpoint
+@Address("mailto:joe@fabrikam123.example")
+public class MyEndpoint{
+
+    @Action("http://fabrikam123.example/mail/Delete")
+    public Source doSomethingWithRequest() {
+	 ...
+    }
+}
+
+
+Si ninguno machea MessageDispatcher lanza NoEndpointFoundException desde org.springframework.ws.server.MessageDispatcher.dispatch()
diff --git a/jaxb2/web-services-spring-jaxb2-globalxsds/src/main/resources/schemas/parent.xsd b/jaxb2/web-services-spring-jaxb2-globalxsds/src/main/resources/schemas/parent.xsd
index 0ce508e..3c340a4 100644
--- a/jaxb2/web-services-spring-jaxb2-globalxsds/src/main/resources/schemas/parent.xsd
+++ b/jaxb2/web-services-spring-jaxb2-globalxsds/src/main/resources/schemas/parent.xsd
@@ -12,5 +12,11 @@
             <xs:enumeration value="FIVETH"/>
         </xs:restriction>
     </xs:simpleType>
+    
+    <xs:simpleType name="limitedString">
+    	<xs:restriction base="xs:string">
+      		<xs:maxLength value="20" />
+    	</xs:restriction>
+  	</xs:simpleType>
 
 </xs:schema>
diff --git a/jaxb2/web-services-spring-jaxb2-server/src/main/java/de/spring/webservices/exceptions/BusinessException.java b/jaxb2/web-services-spring-jaxb2-server/src/main/java/de/spring/webservices/exceptions/BusinessException.java
new file mode 100644
index 0000000..f13ae91
--- /dev/null
+++ b/jaxb2/web-services-spring-jaxb2-server/src/main/java/de/spring/webservices/exceptions/BusinessException.java
@@ -0,0 +1,22 @@
+package de.spring.webservices.exceptions;
+
+/**
+ * This exception will be caught by org.springframework.ws.soap.server.endpoint.SoapFaultMappingExceptionResolver
+ *
+ */
+public class BusinessException extends RuntimeException {
+
+	private static final long serialVersionUID = -4042139454770293299L;
+
+	public BusinessException() {
+        super();
+    }
+
+    public BusinessException(String message) {
+        super(message);
+    }
+
+    public BusinessException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/jaxb2/web-services-spring-jaxb2-server/src/main/java/de/spring/webservices/services/impl/CustomBindingExampleServiceImpl.java b/jaxb2/web-services-spring-jaxb2-server/src/main/java/de/spring/webservices/services/impl/CustomBindingExampleServiceImpl.java
index caa7e41..de65baa 100644
--- a/jaxb2/web-services-spring-jaxb2-server/src/main/java/de/spring/webservices/services/impl/CustomBindingExampleServiceImpl.java
+++ b/jaxb2/web-services-spring-jaxb2-server/src/main/java/de/spring/webservices/services/impl/CustomBindingExampleServiceImpl.java
@@ -2,6 +2,7 @@ package de.spring.webservices.services.impl;
 
 import org.springframework.stereotype.Service;
 
+//import de.spring.webservices.exceptions.BusinessException;
 import de.spring.webservices.auto.CustomBindingExampleRequest;
 import de.spring.webservices.auto.CustomBindingExampleResponse;
 import de.spring.webservices.auto.ParentEnumType;
@@ -16,6 +17,11 @@ public class CustomBindingExampleServiceImpl implements
 	@Override
     public CustomBindingExampleResponse requestResponse(final CustomBindingExampleRequest request) {
 		
+		
+		// Example about how works org.springframework.ws.soap.server.endpoint.SoapFaultMappingExceptionResolver
+		// see soap-ws.xml Spring configuration file.
+//		throw new BusinessException("This feature has not been implemented yet.");
+		
         CustomBindingExampleResponse response = new CustomBindingExampleResponse();
 
         response.setData("CUSTOM BINDING SNAKE EYES AND " + request.getData());
diff --git a/jaxb2/web-services-spring-jaxb2-server/src/main/resources/schemas/examples.xsd b/jaxb2/web-services-spring-jaxb2-server/src/main/resources/schemas/examples.xsd
index 96c3737..8b4bb95 100644
--- a/jaxb2/web-services-spring-jaxb2-server/src/main/resources/schemas/examples.xsd
+++ b/jaxb2/web-services-spring-jaxb2-server/src/main/resources/schemas/examples.xsd
@@ -12,10 +12,21 @@
 
     <!--
         We are going to use catalog.cat in order to avoid downloading parent.xsd from remote server
+        when creating Java objects from examples.xsd.
     -->
     <xs:import namespace="http://gumartinm.name/spring-ws/parent" schemaLocation="http://gumartinm.name/spring-ws/parent/parent.xsd" />
 
 
+	<!-- Spring requires the following:
+	     1. XSD elements being used as request must end with Request name.
+	     2. XSD elements being used as response must end with Response name.
+
+		 IN THIS WAY SPRING FINDS OUT HOW TO CREATE THE wsdl:operation IN THE AUTOGENERATED WSDL.
+		 
+		 ExampleRequest and ExampleResponse will be associated to the wsdl:operation Example in the autogenerated wsdl and
+		 the wsdl:operation Example will have the wsdl:request ExampleRequest and wsdl:response ExampleResponse  elements.
+		 The same for CustomBindingExample.
+	-->
 	<!-- Using inheritance and annox plugin -->
 	<xs:element name="ExampleRequest">
 	   <xs:complexType>
@@ -26,7 +37,7 @@
                 </xs:appinfo>
             </xs:annotation>   
             <xs:all>
-                <xs:element name="data" type="xs:string" />
+                <xs:element name="data" type="parent:limitedString" />
             </xs:all>
         </xs:complexType>
 	</xs:element>
diff --git a/jaxb2/web-services-spring-jaxb2-server/src/main/resources/spring-configuration/ws/soap-ws.xml b/jaxb2/web-services-spring-jaxb2-server/src/main/resources/spring-configuration/ws/soap-ws.xml
index a59c214..e70b466 100644
--- a/jaxb2/web-services-spring-jaxb2-server/src/main/resources/spring-configuration/ws/soap-ws.xml
+++ b/jaxb2/web-services-spring-jaxb2-server/src/main/resources/spring-configuration/ws/soap-ws.xml
@@ -14,38 +14,81 @@
                       http://www.springframework.org/schema/util 
                       http://www.springframework.org/schema/util/spring-util.xsd">
 
-	<!-- Searches for @Endpoint  --> 
+	<!-- Searches for beans in packages (instead of XML configuration we can use in this way annotations like @Service, @Endpoint, etc, etc)  -->
     <context:component-scan base-package="de.spring.webservices"/>
 
     <!--
-        Aqui se podría especificar un unmarshaller (para la request) o un
-        marshaller (para la response) especifico. Por ejemplo Castor.
+        Three ways of using a marshallers/unmarshallers.
         
-        Por la anotacion que uso para el EndPoint y porque tengo JAXB2 en el
-        classpath, Spring lo que está haciendo es el equivalente a si se 
-        escribiera lo siguiente:
+        1. No declarar nada en el XML y dejar que Spring lo haga internamente todo por nosotros.
+        Esto equivale a esta configuracion en XML
+        
+        <oxm:jaxb2-marshaller id="marshaller" context-path="de.spring.webservices"/>
+        El context-path Spring supongo que lo rellena automáticamente en base al component-scan declarado arriba.
+        
+        2. Especificando el context-path para ser escaneado por Spring usando anotaciones. Esto
+        se hace de este modo:
         
         <oxm:jaxb2-marshaller id="marshaller" context-path="de.spring.webservices.auto"/>
+        Esto es lo mismo que haría Spring si no declaramos nada en el XML pero así tenemos opción de
+        de especificar un context-path en concreto.
         
-         Searches for @PayloadRoot
-        <sws:annotation-driven marshaller="marshaller" unmarshaller="marshaller"/>
+        3. Especificando la implementación concreta del marshaller.
+        Con esta opción además puedo usar packagesToScan, contest-path si no recuerdo mal tenía problemas
+        cuando había dos ObjectFactory con el mismo package. Uno está en globalxsds y otro en este proyecto.
+        De todos modos, probablemente habría que usar un package distinto para lo que hay
+     	en globalxsds (quizás incluso basado en el namespace del xsd) y así podría evitar esta configuración.
      -->
-     
-     <!-- 
-     	VOY A INSTANCIAR EXPLÍCITAMENTE EL Marshaller Y VOY A USAR packagesToScan
-     	PORQUE TENGO DOS ObjectFactory CON EL MISMO package. UNO ESTÁ EN globalxsds Y OTRO
-     	ESTÁ EN ESTE PROYECTO server.
-     	
-     	SI NO RECUERDO MAL HABÍA PROBLEMAS CUANDO SE TENÍA DOS ObjectFactory IGUALES Y
-     	SE USABA contest-path.
-     	
-     	DE TODOS MODOS, PROBABLEMENTE HABRÍA QUE USAR UN PACKAGE DISTINTO PARA LO QUE HAY
-     	EN globalxsds Y ASÍ PODRÍA EVITAR ESTA CONFIGURACIÓN :(
-      -->
     <bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
     	<property name="packagesToScan" value="de.spring.webservices.auto"/>
   	</bean>
+  	<!-- Searches for @PayloadRoot -->
     <sws:annotation-driven marshaller="marshaller" unmarshaller="marshaller" />
+    
+    
+    <!-- 
+    	CONSECUENCIAS DE USAR sws:annotation-driven VER: org.springframework.ws.config.AnnotationDrivenBeanDefinitionParser
+    	Cuanto más bajo es el valor de order mayor es la prioridad.
+    	
+    	1. Manejadores de excepción, orden por defecto inicializado en AnnotationDrivenBeanDefinitionParser:
+    	a) SoapFaultAnnotationExceptionResolver será el primer manejador de excepciones que se intente usar por defecto. order = 0
+    	b) SimpleSoapExceptionResolver será el último manejador de excepciones que se intente usar por defecto. order = Ordered.LOWEST_PRECEDENCE
+		   Se usará si la excepción generada no pudo ser manejada por SoapFaultAnnotationExceptionResolver (porque la excepción no fue anotada con
+		   @SoapFault. Este manejador se traga cualquier excepción.
+    	
+    	2. Endpoints a buscar, orden por defecto inicializado en AnnotationDrivenBeanDefinitionParser:
+    	a) PayloadRootAnnotationMethodEndpointMapping será el primer tipo de endpoints que se buscará y que se intentará usar. order = 0
+		   Si el XML SOAP que llega no machea ningún metodo anotado con este EndPoint pasamos a b).
+    	b) SoapActionAnnotationMethodEndpointMapping será el segundo tipo de endpoints que se intentará usar. order = 1
+		   Si el XML SOAP que llega no machea ningún metodo anotado con este EndPoint pasamos a c).
+    	c) AnnotationActionEndpointMapping será el último tipo de endpoints que se buscará. order = 2
+		   Si el XML SOAP que llega no machea tampoco métodos anotado con este EndPoint se
+		   lanza NoEndpointFoundException desde org.springframework.ws.server.MessageDispatcher.dispatch()
+    	
+    	
+    	EN LUGAR DE USAR LA ANOTACIÓN PODRÍAMOS HABER DECLARADO EXPLÍCITAMENTE CADA BEAN TAL QUE ASÍ:
+    	
+    	<bean id="soapFaultMappingExceptionResolver"
+        	class="org.springframework.ws.soap.server.endpoint.SoapFaultMappingExceptionResolver">
+        	<property name="order" value="0" />
+    	</bean>
+    	
+    	<bean id="payloadRootAnnotationMethodEndpointMapping"
+        	class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
+        	<property name="order" value="0" />
+    	</bean>
+    	
+    	CON LA ANOTACIÓN ME AHORRO DECLARAR bean POR bean PERO LO MALO ES QUE INSTANCIO MANEJADORES QUE LUEGO NO USO :(
+    	
+    	
+    	ATENCION:
+    	Solo se usa una instancia de org.springframework.oxm.jaxb.Jaxb2Marshaller que será usada por todas las llamadas (todos los hilos).
+    	Es hilo seguro, por debajo usa javax.xml.bind.JAXBContext que es hilo seguro.
+    	JAXBContext debe ser siempre un singleton porque la primera vez tarda mucho en inicializarse. Cada vez que se haga marshalling y unmarshalling
+    	se deben crear objetos ligeros y no seguros mediante los métodos JAXBContext.createMarshaller() y JAXBContext.createUnmarshaller()
+    	(esto es lo que hace Jaxb2Marshaller por nosotros) Los objetos ligeros creados son javax.xml.bind.Marshaller y javax.xml.bind.Unmarshaller
+    	que no son hilo seguro y por tanto serán creados por cada petición (todo esto lo está haciendo Spring ya, así que genial)
+     -->
 
     <!--
         Spring makes the WSDL file for us from the XSD file.
@@ -75,10 +118,16 @@
         </property>
     </bean>
     
+    <!-- The interceptors in this set are automatically configured on each registered EndpointMapping
+    	 What means, every class annotated with @Endpoint will be using these interceptors.
+    	 
+    	 This configuration enables us to log the SOAP XML Request (received from client), Response (sent by this server) and Fault (sent by this server).
+     -->
     <sws:interceptors>
         <bean class="org.springframework.ws.soap.server.endpoint.interceptor.SoapEnvelopeLoggingInterceptor">
             <property name="logRequest" value="true"/>
             <property name="logResponse" value="true"/>
+            <property name="logFault" value="true"/>
         </bean>
 
         <!-- 
@@ -102,15 +151,71 @@
         </bean>
     </sws:interceptors>
     
-   
-    <bean id="exceptionResolver"
+    
+    <!-- 
+	    PARA METER MAS COSAS A LA RESPUESTA CON ERROR, por ejemplo si quisiéramos enviar en Reason Text la pila de excepción tengo que
+	    implementar mi propio manejador de excepción que extienda de AbstractSoapFaultDefinitionExceptionResolver y añada todo aquello
+	    que yo necesite.
+	    
+	    A mí me parecen muy pobres las implementaciones que trae Spring así que implementaré el mío propio que devuelva la pila
+	    de excepción si la hay.
+     -->
+    <bean id="soapFaultMappingExceptionResolver"
         class="org.springframework.ws.soap.server.endpoint.SoapFaultMappingExceptionResolver">
+        <!--
+        	No quiero usar un valor por defecto porque si no, si mi manejador no machea ninguna excepción
+        	devolvera este valor por defecto.
+        	
+        	Cuando se escribe value="SERVER" Spring está seteando SoapFaultDefinition.SERVER en defaultValue
+        	de org.springframework.ws.soap.server.endpoint.AbstractSoapFaultDefinitionExceptionResolver Esto parece
+        	magia pero Spring lo logra usando org.springframework.ws.soap.server.endpoint.SoapFaultDefinitionEditor.setAsText("SERVER")
+        	
         <property name="defaultFault" value="SERVER"/>
+        -->
         <property name="exceptionMappings">
-            <value>
-                org.springframework.oxm.ValidationFailureException=CLIENT,Invalid request
-            </value>
+            <props>
+            	<!--
+            		<prop key="de.spring.webservices.exceptions.BusinessException">SERVER,something went wrong in server side,en_US</prop>
+            		
+            		Si hago esto en lugar de devolver al cliente el mensaje que va dentro de la excepcion devuelvo siempre:
+            		
+					<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
+					   <env:Header/>
+					   <env:Body>
+					      <env:Fault>
+					         <env:Code>
+					            <env:Value>env:Receiver</env:Value>    Receiver y Server significa lo mismo. Cuando pongo "SERVER" en primer token aquí aparece Receiver.
+					         </env:Code>
+					         <env:Reason>
+					            <env:Text xml:lang="en-US">something went wrong in server side</env:Text>   en-US porque puse como Locale en_US en el tercer token.
+					         </env:Reason>
+					      </env:Fault>
+					   </env:Body>
+					</env:Envelope>
+            		
+            		El primer token es el Value. Si es SERVER o RECEIVER se muestra Receiver. Si es CLIENT o SENDER se muestra Sender.
+            		
+            		El segundo token es el mensaje que SIEMPRE se mostrará.
+            		
+            		El tercer token mapea a un Locale en SoapFaultDefinition (por defecto Locale.ENGLISH). El mapeo se hace con
+            		org.springframework.util.StringUtils.parseLocaleString("en_US")
+            		
+            		
+            		
+            		
+            		
+            		Yo prefiero que se devuelva el mensaje que va dentro de la excepción. Para eso SOLO puede haber un token y
+            		el Locale siempre será entonces Locale.ENGLISH.
+            		
+            		Uso SERVER porque de.spring.webservices.exceptions.BusinessException es una excepción generada en el lado servidor.
+            	 -->
+      			<prop key="de.spring.webservices.exceptions.BusinessException">SERVER</prop>
+    		</props>
         </property>
+        <!-- Así mi manejador de excepciones entra antes que  SimpleSoapExceptionResolver pero después que
+        	 SoapFaultAnnotationExceptionResolver
+        -->
+        <property name="order" value="1" />
     </bean>
     
 </beans>
-- 
2.1.4