Documentos de Académico
Documentos de Profesional
Documentos de Cultura
SPRING-WS –Session 5 1
Av. Canaval y Moreyra 380, 6to.Piso, San Isidro
Tel: +51(1) 987500273 www.joedayz.org
CUS
Descripción breve del CUS: El CUS permitirá al usuario del web service calcular
operaciones matemáticas de suma, resta, multiplicación y división, la información
enviada al servicio web podrá estar en formato XML o en un archivo de texto
comprimido en formato zip que el web service procesará y generará la información
en formato XML.
Spring WS
Como voy a usar spring para implementar mi web service lo primero que tengo que
hacer es definir el formato de los mensajes aceptados por mi web service así como
el formato de los mensajes de respuesta de mi web service. Esta definición la
realizo en un schema XSD:
SPRING-WS –Session 5 2
Av. Canaval y Moreyra 380, 6to.Piso, San Isidro
Tel: +51(1) 987500273 www.joedayz.org
SPRING-WS –Session 5 3
Av. Canaval y Moreyra 380, 6to.Piso, San Isidro
Tel: +51(1) 987500273 www.joedayz.org
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- Si el envío lo realizan mediante un archivo zip. El
archivo zip comprime
un archivo de texto operaciones.txt con el
siguiente formato:
operador,operando1,operando2,operandoN
-->
<xs:element name="operacionesZip"
type="calc:operacionesZipType"/>
</xs:choice>
</xs:complexType>
</xs:element>
<xs:simpleType name="operadorType">
<xs:restriction base="xs:string">
<xs:enumeration value="+" />
<xs:enumeration value="-" />
<xs:enumeration value="*" />
<xs:enumeration value="/" />
</xs:restriction>
</xs:simpleType>
<xs:complexType name="operacionType">
<xs:sequence>
<xs:element name="operandos">
<xs:complexType>
<xs:sequence>
<xs:element name="operando" minOccurs="2"
maxOccurs="unbounded"
type="xs:decimal" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="id" />
<xs:attribute name="operador" type="calc:operadorType" />
</xs:complexType>
<xs:complexType name="resultadoType">
<xs:simpleContent>
<xs:extension base="xs:decimal">
<xs:attribute name="id" use="required" type="xs:anySimpleType"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="operacionesZipType">
<xs:sequence>
<xs:element name="archivoZip" type="xs:base64Binary"
xmime:expectedContentTypes="application/zip"/>
SPRING-WS –Session 5 4
Av. Canaval y Moreyra 380, 6to.Piso, San Isidro
Tel: +51(1) 987500273 www.joedayz.org
</xs:sequence>
<xs:attribute name="nombreArchivoZip" />
</xs:complexType>
</xs:schema>
<!-- Mapeo del punto al cual llegarán las solicitudes del servicio web, se define el punto final
del servicio web por defecto así como los interceptores de validación y de logging.
Detecta los métodos anotados con @PayloadRoot sobre las clases anotadas con @Endpoint. -->
SPRING-WS –Session 5 5
Av. Canaval y Moreyra 380, 6to.Piso, San Isidro
Tel: +51(1) 987500273 www.joedayz.org
<bean id="annotationMapping"
class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<property name="interceptors">
<list>
<ref local="validatingInterceptor"/>
<ref local="loggingInterceptor"/>
</list>
</property>
<property name="order" value="1"/>
</bean>
<!-- Resolvedor de excepciones que genera el servicio web implementado mediante anotaciones
@SoapFault -->
<bean class="org.springframework.ws.soap.server.endpoint.SoapFaultAnnotationExceptionResolver">
<property name="order" value="1"/>
</bean>
<!-- Interceptor de validación de mensajes, valida que los mensajes de entrada y salida del
servicio web
se correspondan con el XSD -->
<bean id="validatingInterceptor"
class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
<property name="xsdSchema" ref="schema"/>
<property name="validateRequest" value="true"/>
<property name="validateResponse" value="true"/>
</bean>
<!-- Este bean define al descriptor del servicio web(WSDL) que es generado en tiempo de
ejecución
El descriptor de despliegue del servicio web(WSDL) puede ser recibido ingresando a
http://server:port/contexto/servlet/locationUri/nombreBean.wsdl, para mi caso es:
http://192.168.1.33:7001/springws/serviciosWeb/calculadoraDescriptor.wsdl
En este caso el nombre del bean se corresponde con el nombre del archivo -->
<bean id="calculadoraDescriptor"
class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
<property name="schema" ref="schema"/>
<property name="portTypeName" value="Calculadora"/>
<property name="locationUri" value="/serviciosWeb"/>
</bean>
<!-- Este bean define el schema XSD que soportará el servicio web -->
<bean id="schema" class="org.springframework.xml.xsd.SimpleXsdSchema">
<property name="xsd" value="/WEB-INF/calculadora.xsd"/>
</bean>
</beans>
Operación.java
package pe.joedayz.cus.domain;
import java.math.BigDecimal;
SPRING-WS –Session 5 6
Av. Canaval y Moreyra 380, 6to.Piso, San Isidro
Tel: +51(1) 987500273 www.joedayz.org
import java.util.List;
public Operacion(){
}
OperandoInvalidoException.java
UnzipException.java
SPRING-WS –Session 5 7
Av. Canaval y Moreyra 380, 6to.Piso, San Isidro
Tel: +51(1) 987500273 www.joedayz.org
CalculadoraService.java
CalculadoraServiceImpl.java
package pe.joedayz.cus.service.impl;
import java.math.BigDecimal;
import java.util.List;
import pe.joedayz.cus.service.CalculadoraService;
SPRING-WS –Session 5 8
Av. Canaval y Moreyra 380, 6to.Piso, San Isidro
Tel: +51(1) 987500273 www.joedayz.org
Como siempre para que el desarrollo sea más eficiente, generar fuentes con JAXB,
usamos MAVEN, el descriptor del proyecto es el siguiente:
SPRING-WS –Session 5 9
Av. Canaval y Moreyra 380, 6to.Piso, San Isidro
Tel: +51(1) 987500273 www.joedayz.org
CalculadoraEndpointWithAnnotation.java
package pe.joedayz.cus.ws;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipInputStream;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.xml.datatype.DatatypeConfigurationException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import pe.joedayz.cus.domain.Operacion;
import pe.joedayz.cus.exception.OperandoInvalidoException;
import pe.joedayz.cus.exception.UnzipException;
import pe.joedayz.cus.schema.MensajeRequest;
import pe.joedayz.cus.schema.MensajeResponse;
import pe.joedayz.cus.schema.MensajeResponse.Resultados;
import pe.joedayz.cus.schema.ObjectFactory;
import pe.joedayz.cus.schema.OperacionType;
import pe.joedayz.cus.schema.OperacionesZipType;
import pe.joedayz.cus.schema.ResultadoType;
import pe.joedayz.cus.service.CalculadoraService;
@Endpoint
public class CalculadoraEndpointWithAnnotation {
/**
* Antes de la ejecución de este método la declaración:
* <sws:marshalling-endpoints marshaller="formador" unmarshaller="formador"/>
* se a encargado de crear el arbol de objetos desde un archivo xml, hay
* que recordar que el servicio web recibe XML y que es spring integrado con
* JAXB quién realiza el proceso de unmarshalling y marshalling para crear el
* arbol de objetos(MensajeRequest) y el formato de respuesta respectivamente.
* */
@PayloadRoot(localPart = REQUEST_LOCAL_NAME, namespace = NAMESPACE_URI)
public MensajeResponse calcular(MensajeRequest request)
throws DatatypeConfigurationException,OperandoInvalidoException,UnzipException {
SPRING-WS –Session 5 10
Av. Canaval y Moreyra 380, 6to.Piso, San Isidro
Tel: +51(1) 987500273 www.joedayz.org
logger.info("Mensaje recibido.");
//Si el cliente del servicio web envió las operaciones en formato XML
if(request.getOperaciones()!=null){
List<OperacionType> operaciones =
request.getOperaciones().getOperacion();
for(OperacionType operacion:operaciones){
if( operacion.getOperador().equals("/") &&
operacion.getOperandos().getOperando().get(1).equals(BigDecimal.ZERO))
throw new OperandoInvalidoException("No se puede dividir
por cero.");
BigDecimal calculo =
calculadoraService.calcular(operacion.getOperandos().getOperando(), operacion.getOperador());
ResultadoType resultado = objectFactory.createResultadoType();
resultado.setId(operacion.getId());//Copio los identificadores.
resultado.setValue(calculo);
resultados.getResultado().add(resultado);
logger.info("Respuesta calculada en el
server:"+calculo.toPlainString());
}
}
else{
//Si no me enviaron las operaciones en formato XML, entonces me las
//enviaron en un archivo comprimido en formato zip:
OperacionesZipType operacionesZip = request.getOperacionesZip();
String archivoAsString = unzipOperaciones(operacionesZip.getArchivoZip());
List<Operacion> operacionesDomain = crearOperaciones(archivoAsString);
int idSecuencial = 1;
for(Operacion operacion:operacionesDomain){
BigDecimal calculo =
calculadoraService.calcular(operacion.getOperandos(), operacion.getOperador());
ResultadoType resultado = objectFactory.createResultadoType();
resultado.setId(String.valueOf(idSecuencial++));
resultado.setValue(calculo);
resultados.getResultado().add(resultado);
logger.info("Respuesta calculada en el
server:"+calculo.toPlainString());
}
}
return response;
}
/**
* Descomprime el archivo zip en una cadena
* */
private String unzipOperaciones(DataHandler handlerZip)throws UnzipException{
DataSource dataSource = null;
ZipInputStream zis = null;
String archivoAsString = null;
try{
dataSource = handlerZip.getDataSource();
zis = new ZipInputStream(dataSource.getInputStream());
if(zis.getNextEntry() != null){
SPRING-WS –Session 5 11
Av. Canaval y Moreyra 380, 6to.Piso, San Isidro
Tel: +51(1) 987500273 www.joedayz.org
int bytesLeidos = 0;
int BUFFER_SIZE = 1024*10;
byte buffer[] = new byte[BUFFER_SIZE];
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while( (bytesLeidos = zis.read(buffer,0,BUFFER_SIZE)) != -1 ){
bos.write( buffer, 0, bytesLeidos);
}
archivoAsString = new String(bos.toByteArray());
}
zis.close();
}catch(Exception e){
logger.info("Error al leer el zip.",e);
throw new UnzipException("Error al leer el zip.");
}finally{
}
return archivoAsString;
}
/**
* Crea una lista de operaciones a partir de un cadena de texto.
* */
private List<Operacion> crearOperaciones(String archivoAsString){
Operacion operacion = null;
List<Operacion> operaciones = new ArrayList<Operacion>();
String[] lineas = archivoAsString.split("\n");
for(int i=0;i<lineas.length;i++){
logger.info("Linea a analizar:"+lineas[i]);
String[] operacionToken = lineas[i].split(",");
List<BigDecimal> operandos = new ArrayList<BigDecimal>();
for(int j=1;j<operacionToken.length;j++){
logger.info("Operador a procesar:"+operacionToken[j]);
operandos.add(new BigDecimal(operacionToken[j].trim()));
}
operacion = new Operacion(operacionToken[0],operandos);
operaciones.add(operacion);
}
return operaciones;
}
SPRING-WS –Session 5 12
Av. Canaval y Moreyra 380, 6to.Piso, San Isidro
Tel: +51(1) 987500273 www.joedayz.org
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-
oxm-1.5.xsd
http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-
services/web-services-1.5.xsd">
<!-- Mapeo del punto al cual llegarán las solicitudes del servicio web, se define el punto final
del servicio web por defecto así como los interceptores de validación y de logging.
Detecta los métodos anotados con @PayloadRoot sobre las clases anotadas con @Endpoint. -->
<bean id="annotationMapping"
class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<property name="interceptors">
<list>
<ref local="validatingInterceptor"/>
<ref local="loggingInterceptor"/>
</list>
</property>
<property name="order" value="1"/>
</bean>
<!-- Resolvedor de excepciones que genera el servicio web implementado mediante anotaciones
@SoapFault -->
<bean class="org.springframework.ws.soap.server.endpoint.SoapFaultAnnotationExceptionResolver">
<property name="order" value="1"/>
</bean>
<!-- Interceptor de validación de mensajes, valida que los mensajes de entrada y salida del
servicio web
se correspondan con el XSD -->
<bean id="validatingInterceptor"
class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
<property name="xsdSchema" ref="schema"/>
<property name="validateRequest" value="true"/>
<property name="validateResponse" value="true"/>
</bean>
<!-- El punto al cual todas las solicitudes que ingresen por medio del servicio web llegarán y
procesarán -->
<bean id="calculadoraEndpoint" class="pe.joedayz.cus.ws.CalculadoraEndpointWithAnnotation">
<property name="calculadoraService" ref="calculadoraService"/>
</bean>
SPRING-WS –Session 5 13
Av. Canaval y Moreyra 380, 6to.Piso, San Isidro
Tel: +51(1) 987500273 www.joedayz.org
<!-- Indica que un unmarshaller debe ser usado para convertir un mensaje xml enviado al servicio
web a un árbol de objetos y una vez convertido setearlo en los parámetros de un método
tambien es usado para retornar valores marshalled(de objetos a java) en los mensajes de
respuesta -->
<sws:marshalling-endpoints marshaller="formador" unmarshaller="formador"/>
<!-- Este bean define al descriptor del servicio web(WSDL) que es generado en tiempo de
ejecución
El descriptor de despliegue del servicio web(WSDL) puede ser recibido ingresando a
http://server:port/contexto/servlet/locationUri/nombreBean.wsdl, para mi caso es:
http://localhost:28080/cus/serviciosWeb/calculadoraDescriptor.wsdl
En este caso el nombre del bean se corresponde con el nombre del archivo -->
<bean id="calculadoraDescriptor"
class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
<property name="schema" ref="schema"/>
<property name="portTypeName" value="Calculadora"/>
<property name="locationUri" value="/serviciosWeb"/>
</bean>
<!-- Este bean define el schema XSD que soportará el servicio web -->
<bean id="schema" class="org.springframework.xml.xsd.SimpleXsdSchema">
<property name="xsd" value="/WEB-INF/calculadora.xsd"/>
</bean>
SPRING-WS –Session 5 14