Spring WS: Creación de Servicios Web con Spring

8
40776

Spring WS: Creación de Servicios Web con Spring

En el siguiente tutorial vamos a ver algunas de las aportaciones que nos ofrece Spring en relación a la construcción de Servicios Web.

Se presupone que el lector ya posee conocimientos de Servicios Web, Maven y Spring.

Indice de contenido:

  1. Introducción.
  2. Ejemplo de construcción de un Servicio Web con Spring.
    1. Entorno.
    2. Estructura del proyecto.
    3. Describiendo la petición al servicio web.
    4. Describiendo la respuesta del servicio web.
    5. Código fuente de las clases que componen el servicio Web.
    6. Archivo de configuración de Log4J: /WEB-ING/log4j.xml.
    7. Archivo de configuración de Spring 2 (\WEB-INF\bibliotecaWS-servlet.xml).
    8. Archivo de configuración y despliegue de la aplicación (/WEB-INF/web.xml).
    9. Archivo de configuración de Maven 2: pom.xml.
    10. Construcción y despliegue la aplicación.
  3. Ejemplo de construcción de un cliente (Axis2) para probar el servicio web.
  4. Ejemplo de construcción de un cliente (con Spring) para probar el servicio web.
  5. Referencias
  6. Conclusiones

Introducción

Todos estaremos de acuerdo en que las aplicaciones actuales no están aisladas, que hay necesidad (y deben) interoperar para resolver los problemas, es más, incluso aunque ahora no sea necesario, se debe diseñar una solución para que en caso de necesidad el impacto sea mínimo.

Un problema debería ser resuelto una vez y ser reutilizado por el resto de sistemas (con independencia de lenguajes de programación y arquitecturas) sin tener que volver que resolver el mismo problema implementando una y otra vez lo mismo en cada nueva aplicación…. (SOA).

En este tutorial vamos a ver un ejemplo de construcción de un servicio web usando el modelo contrato primero (Spring apuesta por este modelo).
Desde mi punto de vista la mejor forma de implementar un servicio Web, cuando de verdad se desea interoperabilidad… no quiero entrar en este tema si os interesa, podéis leer la siguientes webs que explican por que es mejor el modelo contrato primero frente a partir de una interfaz de programación:

Para terminar la introducción decir que Spring también tiene mucho que aportar en relación a otras técnicas de comunicación remota: RMI, Hessian y Burlap, HTTPInvoker. Pero se salen del ámbito de este tutorial.

Ejemplo de construcción de un Servicio Web con Spring:

A continuación vamos a ver un completo ejemplo en donde construiremos un Servicio Web que representa al motor de búsqueda de libros de una biblioteca. Las aplicaciones preguntarán sobre libros de una determinada categoría y nivel y el servicio devolverá aquellos libros disponibles que cumplen esos criterios de búsqueda.

Entorno

El siguiente ejemplo está construido en el siguiente entorno:

  • HP Pavilion.
  • Windows Vista Home Premium.
  • Eclipse Ganymede.
  • Java 6.
  • Maven 2.
  • Plugin Maven 4QE para Eclipse.

Estructura del proyecto:

Una de las muchas ventajas de Maven es que estandariza la estructura de los proyectos, es decir, cualquier persona con conocimientos de Maven tendría facilidad de comprender como se estructura y dónde está cada cosa dentro del proyecto.

Si nos fijamos hay un arquetipo Maven 2 para proyectos Spring WS: http://www.mavenrepository.com/artifact/org.springframework.ws/spring-ws-archetype

Yo cree el proyecto con el comando:

mvn archetype:create -DarchetypeGroupId=org.springframework.ws -DarchetypeArtifactId=spring-ws-archetype -DarchetypeVersion=1.5.6 -DgroupId=com.autentia.tutoriales -DartifactId=bibliotecaWS

Vosotros como ya lo tenéis hecho, simplemente tendréis que importarlo desde vuestro IDE favorito. Yo uso Eclipse Ganymede con el plugin Q4E para gestión de proyectos Maven.

Describiendo la petición al servicio web: booksInfoRequest.xml

¿Cómo nos gustaría que fueran los mensajes de solicitud o consulta de libros?… pues, por ejemplo así:

<?xml version="1.0" encoding="UTF-8"?>

	Servicios Web
	avanzado

Una categoría y un nivel, en donde el nivel está restringido a ser: básico o medio o avanzado

Ahora lo definimos con un XML Schema: booksInfoRequest.xsd

<?xml version="1.0" encoding="UTF-8"?>

	
	
		
			
				
				
			
		
	
	
	
		
			
			
			
		
	

¿Qué nó sabes hacerlo o no te apetece?, pues mira esté tutorial: Ver tutorial.

Describiendo la respuesta del servicio web: booksInfoResponse.xml

¿Cómo nos gustaría que fueran los mensajes de respuesta?… pues, por ejemplo así (con N libros):

<?xml version="1.0" encoding="UTF-8"?>

	
		Editorial Guay
		Java en ejemplos
		520
		40
	
	
		Editorial XYZ
		Aprenda Java en 50 dias
		700
		80
	

N libros en donde cada uno está definido por una editorial, un título (ambos texto libre), un número de páginas y un precio (ambos números positivos).

Ahora lo definimos con un XML Schema: booksInfoResponse.xsd

<?xml version="1.0" encoding="UTF-8"?>

	
  
    
      
        
      
    
  
  
  
    
      
        
        
        
        
      
    
  

Definir el Web Service: /WEB-INF/bibliotecaWS.xsd

Sencillo, unimos los XML Schema anteriores y el resto lo configuramos en Spring. Es decir, se podría decir que en conjunto es como el WSDL del servicio Web, es más, de hecho se generará automáticamente.

<?xml version="1.0" encoding="UTF-8"?>

	
	
		
			
				
				
			
		
	
	
	
		
			
			
			
		
	
	
  
    
      
        
      
    
  
  
  
    
      
        
        
        
        
      
    
  	

Código fuente de las clases que componen el servicio Web.

A continuación iremos viendo las clases que componen el proyecto.

NOTA: Lo voy a hacer a mano, pero las clases Libro, BookInfoRequest y BookInfoResponse podrían de muchas formas ser generadas automáticamente desde los XSD.
Por ejemplo, en el siguiente enlace http://www.adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=xmlBeans mi compañero Alejandro García os explica una de ellas.

com.autentia.tutoriales.spring.ws.entity.Libro:

package com.autentia.tutoriales.spring.ws.entity;

/**
 * Representación de un libro
 * @author Carlos García. Autentia
 * @see http://www.mobiletest.es
 */
public class Libro {
	private String editorial;
	private String titulo;
	private int	   paginas;
	private int	   precio;
		
	public String getEditorial() {
		return editorial;
	}
	public void setEditorial(String editorial) {
		this.editorial = editorial;
	}
	public String getTitulo() {
		return titulo;
	}
	public void setTitulo(String titulo) {
		this.titulo = titulo;
	}
	public int getPaginas() {
		return paginas;
	}
	public void setPaginas(int paginas) {
		this.paginas = paginas;
	}
	public int getPrecio() {
		return precio;
	}
	public void setPrecio(int precio) {
		this.precio = precio;
	}
}

com.autentia.tutoriales.spring.ws.entity.BooksInfoRequest:

package com.autentia.tutoriales.spring.ws.entity;

/**
 * Representación de una petición.
 * @author Carlos García. Autentia
 * @see http://www.mobiletest.es
 */
public class BooksInfoRequest {
	private String categoria;
	private String nivel;
	
	public String getCategoria() {
		return categoria;
	}
	public void setCategoria(String categoria) {
		this.categoria = categoria;
	}
	public String getNivel() {
		return nivel;
	}
	public void setNivel(String nivel) {
		this.nivel = nivel;
	}
}

com.autentia.tutoriales.spring.ws.entity.BooksInfoResponse:

package com.autentia.tutoriales.spring.ws.entity;

/**
 * Representación la respuesta a una petición BooksInforRequest.
 * @author Carlos García. Autentia
 * @see http://www.mobiletest.es
 */
public class BooksInfoResponse {
	private java.util.ArrayList libros;
	
	public BooksInfoResponse(){
		this.libros = new java.util.ArrayList();
	}

	public void addLibro(Libro libro){
		this.libros.add(libro);
	}
	
	public java.util.List getLibros() {
		return this.libros;
	}
}

com.autentia.tutoriales.spring.ws.IRequestProcessor:

Cuando el servicio web reciba una petición, la lógica de negorio real de tratamiento
de la misma será realizada por alguna clase que implemente esta interfaz.

package com.autentia.tutoriales.spring.ws;

import com.autentia.tutoriales.spring.ws.entity.BooksInfoRequest;
import com.autentia.tutoriales.spring.ws.entity.BooksInfoResponse;

/**
 * Tratamiento de una petición de búsqueda de libros
 * @author Carlos García. Autentia.
 * @see http://www.mobiletest.es 
 */
public interface IRequestProcessor {
	/**
	 * Procesa la petición de consulta
	 * @param request Datos de la consulta
	 * @return Devuelve la respuesta
	 */
	public BooksInfoResponse process(BooksInfoRequest request);
}

com.autentia.tutoriales.spring.ws.DummyRequestProcessor:

Implementación sencilla de la interfaz IRequestProcessor para este este ejemplo.

package com.autentia.tutoriales.spring.ws;

import com.autentia.tutoriales.spring.ws.entity.BooksInfoRequest;
import com.autentia.tutoriales.spring.ws.entity.BooksInfoResponse;
import com.autentia.tutoriales.spring.ws.entity.Libro;

/**
 * Implementación dummy de IRequestProcesor
 * @author Carlos García. Autentia.
 * @see http://www.mobiletest.es 
 */
public class DummyRequestProcessor implements IRequestProcessor {

	/* 
	 * @see com.autentia.tutoriales.spring.ws.IRequestProcessor#process(com.autentia.tutoriales.spring.ws.entity.BooksInfoRequest)
	 */
	public BooksInfoResponse process(BooksInfoRequest request) {
		BooksInfoResponse response = new BooksInfoResponse();
		Libro			  libro    = null;
		
		for (int i = 0; i < 5; i++){
			libro = new Libro();
			
			libro.setTitulo("Titulo libro " + i);
			libro.setEditorial("Editorial libro " + i);
			libro.setPaginas(100 + i);
			libro.setPrecio(50 + i);
			
			response.addLibro(libro);			
		}
		
		return response;
	}
}

com.autentia.tutoriales.spring.ws.BookInfoEndPoint:

EndPoint del WS, recibe las peticiones de consulta de libros (peticiones XML), las convierte en objetos
y delega su procesamiento a un IRequestProcessor.

Yo he elegido hacerlo con DOM (mensajes completos en memoria...), pero hay otras muchas implementaciones que nos permiten implementarlo con Stax, SAX, JDOM...
Simplemente deberemos leer, procesar y constuir en base a DOM los XML.

package com.autentia.tutoriales.spring.ws;

import java.util.Iterator;
import java.util.List;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.ws.server.endpoint.AbstractDomPayloadEndpoint;
import com.autentia.tutoriales.spring.ws.entity.*;

/**
 * EndPoint del WS, recibe las peticiones de consulta de libros (peticiones XML),
 * las convierte en objetos y delega su procesamiento a un IRequestProcessor.
 * @author Carlos García. Autentia
 * @see http://www.mobiletest.es
 */
public class BookInfoEndPoint extends AbstractDomPayloadEndpoint {
	private Log		  logger = LogFactory.getLog(BookInfoEndPoint.class);
	
	private IRequestProcessor procesor;

	/**
	 * Será inyectado por Spring
	 */
	public void setProcesor(IRequestProcessor procesor) {
		this.procesor = procesor;
		
	}

	/* 
	 * @see org.springframework.ws.server.endpoint.AbstractDomPayloadEndpoint#invokeInternal(org.w3c.dom.Element, org.w3c.dom.Document)
	 */
	protected Element invokeInternal(Element domRequest, Document document) throws Exception {
		if (logger.isDebugEnabled()){
			logger.debug("Petición de consulta de libros");
		}
		
		BooksInfoRequest  request     = this.xmlToInfoRequest(domRequest);
		
		if (logger.isDebugEnabled()){
			logger.debug("PETICION: categoria=" + request.getCategoria() + ", nivel=" + request.getNivel());
		}
		
		BooksInfoResponse response    = procesor.process(request);
		
		if (logger.isDebugEnabled()){
			int numLibros = 0;
			if (response.getLibros() != null){
				numLibros = response.getLibros().size();
			}
			logger.debug("RESPUESTA: Número de libros: " + numLibros);
		}
		
		Element			  domResponse = this.responseToXml(response, document);
		
		return domResponse;
	}
	
	/**
	 * @param request Elemento XML 
	 * @return Genera una instancia de BooksInfoRequest a partir de un elemento xml
	 */
	private BooksInfoRequest xmlToInfoRequest(Element request){
		String categoria = request.getElementsByTagName("categoria").item(0).getFirstChild().getNodeValue();
		String nivel	 = request.getElementsByTagName("nivel").item(0).getFirstChild().getNodeValue();
		
		BooksInfoRequest bookInfoRequest = new BooksInfoRequest();
		
		bookInfoRequest.setCategoria(categoria);
		bookInfoRequest.setNivel(nivel);
		
		return bookInfoRequest;
	}

	/**
	 * @param request Elemento XML 
	 * @return Genera una instancia de BooksInfoRequest a partir de un elemento xml
	 */
	private Element responseToXml(BooksInfoResponse response, Document document){
		Element		root	= document.createElementNS("http://www.adictosaltrabajo.com/spring/ws/schemas", "BooksInfoResponse");
		List libros  = response.getLibros();
		
		if (libros != null){
			Iterator iteLibros = null;
			Libro			libro	  = null;
			Element			domLibro  = null;
			
			iteLibros = libros.iterator();
			while (iteLibros.hasNext()){
				libro	 = iteLibros.next();
				domLibro = this.bookToXml(document, libro);
				root.appendChild(domLibro);
			}
		}
		
		return root;
	}

	/**
	 * 
	 * @param document Document para construir el DOM
	 * @param libro	   Objeto a convertir a XML
	 * @return Devuelve el objeto libro en XML.
	 */
	private Element bookToXml(Document document, Libro libro){
		Element domLibro = document.createElement("libro");
		
		this.addHijo(document, domLibro, "editorial", libro.getEditorial());
		this.addHijo(document, domLibro, "titulo",	libro.getTitulo());
		this.addHijo(document, domLibro, "paginas",	String.valueOf(libro.getPaginas()));
		this.addHijo(document, domLibro, "precio",	String.valueOf(libro.getPrecio()));
		
		return domLibro;
	}
	
	/**
	 * Añade una nueva propiedad valor al elemento .
	 * @param document Para crear elementos
	 * @param padre	   La nueva propiedad será agregada a este elemento
	 * @param tag      Nombre de la propiedad
	 * @param valor	   Valor de la propiedad
	 */
	private void addHijo(Document document, Element padre, String tag, String valor){
		Element domNombre = document.createElement(tag);
		Text	domValor  = document.createTextNode(valor);
		
		domNombre.appendChild(domValor);
		padre.appendChild(domNombre);
	}
}

Archivo de configuración de Log4J: /WEB-ING/log4j.xml:

A continuación exponemos el archivo de configuración de Log4J. Los mensajes con nivel WARN o superior irán a parar a un archivo y el resto (de cualquier nivel DEBUG, INFO, etc.) a otro.

<?xml version="1.0" encoding="UTF-8"?>


	
	
	
		
		
		
		

		
			
		
	

	
	
		
		
		

		
			
		
	

	
	
		
		
	
	
	
	
		
		
	

Archivo de configuración de Spring 2 (\WEB-INF\bibliotecaWS-servlet.xml):

Atención, el nombre del archivo es importante, debe ser igual que el nombre del servlet que verémos más adelante y luego "-servlet.xml".

El archivo está autocomentado.

<?xml version="1.0" encoding="UTF-8"?>

	
	
	

	
	
    	
	

	
    
        
            
        
    

	
	 
	 	
	 		
	 			
	 			
	 		
	 	
	 	
	 	
	 	
	 	   
	 	       
	 	   
	 	
	 
	
	  
	 
	     
	     
	     
	 	

	
	
	
	  
	  
	    
	      SENDER,Invalid request
	      SENDER,Invalid request
	    
	  
	
  	
	
	
	
	  
	    
	      
	    
	  
	  
	  
	  
	  
	

Archivo de configuración y despliegue de la aplicación (/WEB-INF/web.xml):

Las peticiones SOAP serán atendidas por el servlet org.springframework.ws.transport.http.MessageDispatcherServlet

<?xml version="1.0" encoding="UTF-8"?>

	Autentia. Ejemplo de Spring-WS

	
	
		log4jConfigLocation
		/WEB-INF/log4j.xml
	
	
	
	
		org.springframework.web.util.Log4jConfigListener
	

	
	
		bibliotecaWS
		org.springframework.ws.transport.http.MessageDispatcherServlet

		
		
			transformWsdlLocations
			true
		
		1
	

	
		bibliotecaWS
		/services/*
	

	
	
		bibliotecaWS
		*.wsdl
	


Archivo de configuración de Maven 2: pom.xml:

A continuación exponemos el archivo de configuración de Maven, se presupone que el lector ya tiene nociones de Maven.

<?xml version="1.0" encoding="UTF-8"?>

    4.0.0
    com.autentia.tutoriales
    bibliotecaWS
    war
    1.0-SNAPSHOT
    BibliotecaWS con Spring-WS
    http://www.adictosaltrabajo.com

    
       
           
               
                 maven-compiler-plugin  
                   
                     1.5  
                     1.5  
                     UTF-8  
                   
               
               
           
     
     
    
    	
        
            org.springframework.ws
            spring-ws-core
            1.5.6
        
    

Construcción y despliegue la aplicación:

A continuación ejecutamos el siguiente comando Maven: mvn package y desplegamos el archivo generado target\bibliotecaWS-1.0-SNAPSHOT.war en
nuestro servidor preferido (JBoss, Tomcat, WebLogic, WebSphere, Jetty, etc..).

Ejemplo de construcción de un cliente (Axis2) para probar el servicio web.

Este apartado da por sentado que el usuario ya sabe algo de Axis2, así que no voy a explicar con sumo detalle que es cada cosa (puedes consultar otros tutoriales en http://www.adictosaltrabajo.com de como se instala, configura, etc).

Como dije anteriormente, el servicio Web está desplegado y es capaz de autodescribirse a generando su propio WSDL, pues bien vamos a crear un cliente automáticamente desde su WDSL.

El código fuente de este tutorial puede ser descargado desde aquí (proyecto Eclipse).


%AXIS2_HOME%/bin/wsdl2java -sp -s -p com.autentia.tutoriales.spring.ws.cliente -uri http://localhost:8080/bibliotecaWS/bibliotecaWS.wsdl

En donde:

  • -sp: Por el namespace axis2 por defecto pone un namespace ns1 pero nuestro en el WS el Payload no está qualificado (no tiene namespace). Puedes verlo tu mismo copiando y pegando la URL en tu navegador...
  • -s: No queremos ni necesitamos que nos genere funcionalidad de invocación asíncrona.
  • -p: En que paquete deseamos que nos genere las clases.

Invocando el servicio Web. com.autentia.tutoriales.spring.ws.cliente.BibliotecaWSApp:

package com.autentia.tutoriales.spring.ws.cliente;

import com.autentia.tutoriales.spring.ws.cliente.BibliotecaWSServiceStub.Libro_type0;

/**
 * Ejemplo de invocación del WS de consulta de libros
 * @author Carlos García. Autentia
 * @see http://www.mobiletest.es
 */
public class BibliotecaWSApp {
	public static void main(String[] args) throws Exception {
		BibliotecaWSServiceStub stub = new BibliotecaWSServiceStub();
		BibliotecaWSServiceStub.BooksInfoRequest  peticion	= new BibliotecaWSServiceStub.BooksInfoRequest();
		
		peticion.setCategoria("java");
		peticion.setNivel(BibliotecaWSServiceStub.NivelType.avanzado);
		
		BibliotecaWSServiceStub.BooksInfoResponse respuesta = stub.BooksInfo(peticion);
		
		Libro_type0[] libros = respuesta.getLibro();
		if (libros != null){
			 for (int i = 0, lcount = libros.length; i < lcount; i++){
				 System.out.println(libros[i].getEditorial() + " " + libros[i].getTitulo() + " "  +  libros[i].getPaginas() + " " +  libros[i].getPrecio());		 
			 }
		} else {
			System.out.println("No hay libros");
		}		
	}
}

Y para terminar, al ejecutar la aplicación anterior, nos produce la siguiente salida:

Editorial libro 0 Titulo libro 0 100 50
Editorial libro 1 Titulo libro 1 101 51
Editorial libro 2 Titulo libro 2 102 52
Editorial libro 3 Titulo libro 3 103 53
Editorial libro 4 Titulo libro 4 104 54

Ejemplo de construcción de un cliente (con Spring) para probar el servicio web.

Este apartado está descrito en el siguiente tutorial http://www.adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=spring_ws_client_example1.

Referencias

Conclusiones

Bueno, como veis Spring no deja de sorprendernos en cuanto a su potencia y ventajas en el desarrollo de software de calidad (bajo acomplamiento, alta cohesión, etc.)

Resaltar que Spring también proporciona mucha funcionalidad para consumir servicios Web (hacer clientes), pero prefiero dejar esto para otro tutorial o para que el lector investige al respecto, haga un tutorial y nos lo mande para que lo publiquemos :-).

Carlos García Pérez. Creador de MobileTest, un complemento educativo para los profesores y sus alumnos.

cgpcosmad@gmail.com

8 COMENTARIOS

  1. Hola, efectivamente me faltaba el espacio de nombres.
    El servicio lo probé con un cliente java y un .Net y con el cliente java no tuve problemas pero con el .net no me reconoce el tipo de retorno y lo que hice fue cambiarle al archivo bibliotecaWS.xsd en la línea 27 ref por type y lo reconoció sin problemas.

  2. Buenas.

    Tengo una aplicación web con spring-mvc que funciona correctamente.
    Ahora querría que la aplicación sirviera unos cuantos métodos de lógica de negocio a través de servicios web.

    He seguido este tutorial al pie de la letra y al arrancar Spring me muestra la siguiente excepción por consola:

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name \\\’partidaArancelariaWS\\\’ defined in ServletContext resource [/WEB-INF/partidaArancelariaWS-servlet.xml]: Invocation of init method failed; nested exception is WSDLException: faultCode=CONFIGURATION_ERROR: No Java extensionType found to represent a \\\'{http://schemas.xmlsoap.org/wsdl/soap12/}binding\\\’ element in the context of a \\\’javax.wsdl.Binding\\\’.:

    He descubierto que si en vez de soap 12 utilizo soap 11 arranca correctamente, el problema es que cuando pido la WSDL la consola me muestra el siguiente error:

    Could not complete request
    java.lang.AbstractMethodError: org.apache.xerces.dom.DeferredDocumentImpl.getXmlStandalone()Z
    at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2TO.setDocumentInfo(DOM2TO.java:373)
    at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2TO.parse(DOM2TO.java:127)

    Mi XSD es igual que la del ejemplo del tutorial.

    ¿Alguién podrá arrojar algo de luz a mi problema?

    MUCHISIMAS GRACIAS¡

  3. He ejecutado correctamente el aplicativo y funciona correctamente.
    En mi caso el web service que he construido solo permitirá que ciertos usuarios puedan consumir el servicio, una forma de restringir el acceso es identificando la IP del equipo que esta solicitando consumir el web service.
    Como identifico de quien consume el servicio. En otras palabras como identifico la IP de la maquina que esta solicitando el servicio.

    Gracias por tu apoyo.

  4. Hola LuigiBuilder

    Por ejemplo podrías hacer que dentro de un javax.servlet.Filter pregunta por la propiedad request.getRemoteAddr() para ver si es una IP válida o no.

    Si no lo lanzas una excepción para que la petición sea anulada, es decir no invocas el chain.doFilter(request, response)

    Saludos

  5. Gracias Carlos,
    Seguí tu sugerencia y logre identificar las IPs permitidas.
    Pero tengo otra duda, como puedo recuperar esa IP desde el mismo webservice para luego grabar en una tabla tanto los datos que enviaron en la consulta como la IP de la maquina que hizo la consulta.

    Gracias por todo.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad