Trabajando con Mule ESB

8
20539

Trabajando con Mule ESB

Índice de contenidos.

1. Introducción

En este tutorial vamos a hacer un ejemplo de uso de Mule ESB. En otros tutoriales vimos unos primeros pasos con Mule ESB, también cómo montar un
proyecto de Mule ESB con Maven, y cómo crear un proyecto de Mule con Mule Studio.

En esta ocasión vamos a profundizar un poco más en esta tecnología creando una aplicación que levantará un web service de SOAP que recibirá peticiones para consultar un catálogo de vehículos por id de vehículo. El web service hará la consulta de vehículos almacenados en un mapa y enviará al usuario por correo electrónico la información del vehículo consultada. Para trabajar un poco más con esta tecnología haremos uso de los transformadores de Mule e introduciremos uno en el flujo que se encargará de componer el correo electrónico que se envía al usuario.

2. Entorno

  • MacBook Pro 15′ (2.4 GHz Intel Core i5, 8GB DDR3 SDRAM).
  • Sistema Operativo: Mac OS X Snow Leopard 10.6.8
  • JDK 1.6.0_29
  • Mule 3.2.0

3. Crear el Web Service de SOAP

Para empezar crearemos un nuevo proyecto utilizando el arquetipo de Mule ya que nos ahorra mucho tiempo.

A continuación crearemos el servicio web que se encargue de atender las peticiones HTTP y hacer la consulta de los vehículos en el catálogo. Primero crearemos el interfaz ‘SearchCar’ que expone el método ‘search’ el cual recibe dos parámetros: el id del vehículo y el mail del usuario que realiza la petición para enviarle por correo electrónico la información del vehículo consultado.

package com.autentia.tutoriales;

import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;

@WebService
public interface SearchCar {
	
    @WebResult(name="id") 
    Car search(@WebParam(name="id") Long id, 
    		   @WebParam(name="mail") String customer);
}

La forma mas sencilla de crear el servicio web es anotándolo mediante @WebService. Esta anotación pertenece al API de JAX-WS (Java API for XML Web Services) incluida en Java 6. El método ‘search’ recibe los parámetros id y mail en la petición.

Una vez creado el interfaz del web service creamos una implementación. Implementamos el interfaz creado anteriormente y también el interfaz de Initialisable del API de Mule. Mediante la implementación de este interfaz, Mule se encargará de invocar al método initialize durante el arranque de la aplicación el servidor. En este método creamos el catálogo de coches para la consulta.

package com.autentia.tutoriales;

import java.util.HashMap;
import java.util.Map;

import javax.jws.WebService;

import org.mule.api.lifecycle.Initialisable;
import org.mule.api.lifecycle.InitialisationException;

@WebService(serviceName="searchCarService", endpointInterface="com.autentia.tutoriales.SearchCar")
public class SearchCarService implements SearchCar, Initialisable {
	
	final Map<Long, Car> cars = new HashMap<Long, Car>();
	
	@Override
	public void initialise() throws InitialisationException {
		cars.put(1L, new Car(1L, "Renault", "Megane", 18500D));
		cars.put(2L, new Car(2L, "Ford", "Focus", 17500D));
		cars.put(3L, new Car(3L, "Alfa Romeo", "159", 24000D));
		cars.put(4L, new Car(4L, "BMW", "Serie 1", 38900D));
		cars.put(5L, new Car(5L, "Volkswagen", "Golf", 24200D));
	}

	@Override
	public Car search(Long id, String customerMail) {
		final Car car = cars.get(id);
		car.setCustomerMail(customerMail);
        return car;
    }
}

El método ‘initialise’ será invocado tras la instanciación del servicio. En este caso lo utilizaremos para crear el catálogo de vehículos del ejemplo.

El método ‘search’ es el encargado de hacer la búsqueda del vehículo en el catálogo a través del id. También se encargará de recoger el email del cliente al que enviarle la información del vehículo consultado. La clase Car es un simple POJO que almacena el id del vehículo, el fabricante, modelo, precio y mail del usuario que realiza la consulta.

4. Fichero mule-config.xml

En este fichero es donde se configura todo el flujo de Mule. Contiene el servicio web de búsqueda de vehículos donde se indica la dirección HTTP donde está desplegado nuestro servicio de consulta de vehículos en el catálogo.

<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:vm="http://www.mulesoft.org/schema/mule/vm" xmlns:cxf="http://www.mulesoft.org/schema/mule/cxf"
    xmlns:smtp="http://www.mulesoft.org/schema/mule/smtp" xmlns:smtps="http://www.mulesoft.org/schema/mule/smtps"
    xmlns:email="http://www.mulesoft.org/schema/mule/email"
    xmlns:servlet="http://www.mulesoft.org/schema/mule/servlet"
    xsi:schemaLocation="
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/3.2/mule.xsd
        http://www.mulesoft.org/schema/mule/vm http://www.mulesoft.org/schema/mule/vm/3.2/mule-vm.xsd
        http://www.mulesoft.org/schema/mule/cxf http://www.mulesoft.org/schema/mule/cxf/3.2/mule-cxf.xsd
        http://www.mulesoft.org/schema/mule/smtp http://www.mulesoft.org/schema/mule/smtp/3.2/mule-smtp.xsd
        http://www.mulesoft.org/schema/mule/smtps http://www.mulesoft.org/schema/mule/smtps/3.2/mule-smtps.xsd
        http://www.mulesoft.org/schema/mule/email http://www.mulesoft.org/schema/mule/email/3.2/mule-email.xsd
        http://www.mulesoft.org/schema/mule/servlet http://www.mulesoft.org/schema/mule/servlet/3.2/mule-servlet.xsd">
    
    <flow name="SearchCarService">
        <composite-source>
            <inbound-endpoint address="http://localhost:8081/cars" exchange-pattern="request-response">
                <cxf:jaxws-service serviceClass="com.autentia.tutoriales.SearchCar" />
            </inbound-endpoint>
        </composite-source>
        
        <component>
            <singleton-object class="com.autentia.tutoriales.SearchCarService" />
        </component>
    </flow>
</mule>

Como endpoint de entrada configuramos el SearchCar a través de la etiqueta ‘jaxws-service’.

Indicamos a mule a través de la etiqueta ‘singleton-object’ que cree un Singleton del servicio de búsqueda de vehículos.

5. Envio de correos

Para configurar el servicio de envío de correos utilizaremos el servicio smtps que nos proporciona Mule. Para incluirlo en nuestro flujo añadimos al fichero config-mule.xml, después del flujo anterior, lo siguiente:

	<context:property-placeholder location="mail.properties" />
	
	<flow name="EmailService">
        <vm:inbound-endpoint path="emailService" exchange-pattern="one-way" />
        <smtps:outbound-endpoint user="${user}" 
        						 password="${password}" 
        						 host="${host}" 
        						 from="${from}">
            <custom-transformer class="com.autentia.tutoriales.EmailTransformer" />
            <email:string-to-email-transformer />
        </smtps:outbound-endpoint>
    </flow>

Configuramos un nuevo flujo llamado EmailService que contienen un endpoint de entrada y un servicio de envío de correos que proporciona Mule. En el servicio SMTP debemos indicar la configuración de la cuenta con la que se envían los correos. Estas propiedades se externalizan en el fichero mail.properties que dejaremos en src/main/resources junto al mule-config.xml.

Para completar el servicio de envío de correos hemos introducido un transformer que será invocado antes del envío del correo y se encargará de componer el mensaje que se enviará al cliente. Para crearnos este transformer debemos crear una clase que extienda de org.mule.transformer.AbstractMessageTransformer y que implemente el método transformMessage:

	package com.autentia.tutoriales;
	
	import org.mule.api.MuleMessage;
	import org.mule.api.transformer.TransformerException;
	import org.mule.transformer.AbstractMessageTransformer;
	import org.mule.transport.email.MailProperties;
	
	public class EmailTransformer extends AbstractMessageTransformer {
	
	    @Override
	    public Object transformMessage(MuleMessage message, String outputEncoding) throws TransformerException {
	        final Car car = (Car) message.getPayload();
	
	        final StringBuilder mailMessage =  new StringBuilder("A continuación le enviamos los datos del vehiculo que nos ha solicitado:").append("\n");
	        mailMessage.append("Fabricante:  " + car.getManufacturer()).append("\n");
	        mailMessage.append("Modelo: " + car.getModel()).append("\n");
	        mailMessage.append("Precio: " + car.getPrice()).append(" euros \n");
	        mailMessage.append("Recibe un cordial saludo. ");
	        
	        message.setOutboundProperty(MailProperties.SUBJECT_PROPERTY, "Información del vehículo que nos ha solicitado");
	        message.setOutboundProperty(MailProperties.TO_ADDRESSES_PROPERTY, car.getCustomerMail());
	
	        return mailMessage.toString();
	    }
	}

Lo más destacado de esta clase es el parámetro ‘message’ de la clase MuleMessage que contiene el parámetro devuelto por el método search del servicio web, en nuestro caso un objeto de la clase Car. A través de estos mensajes es la manera que tenemos de comunicar los distintos elementos dentro del flujo de Mule.

Por último debemos comunicar el servicio web anterior con el servicio de envío de correo por lo que añadimos al mule-config.xml, dentro del flujo SearchCarService un outbound-endpoint invocando al servicio de correo. El fichero mule-config.xml al completo quedaría así:

...    
    <flow name="SearchCarService">
        <composite-source>
            <inbound-endpoint address="http://localhost:8081/cars" exchange-pattern="request-response">
                <cxf:jaxws-service serviceClass="com.autentia.tutoriales.SearchCar" />
            </inbound-endpoint>
        </composite-source>
        
        <component>
            <singleton-object class="com.autentia.tutoriales.SearchCarService" />
        </component>
        
        <async>
            <vm:outbound-endpoint path="emailService" exchange-pattern="one-way" />
        </async>
    </flow>
    
    <context:property-placeholder location="mail.properties" />
    
    <flow name="EmailService">
        <vm:inbound-endpoint path="emailService" exchange-pattern="one-way" />
        <smtps:outbound-endpoint user="${user}" 
        						 password="${password}" 
        						 host="${host}" 
        						 from="${from}">
            <custom-transformer class="com.autentia.tutoriales.EmailTransformer" />
            <email:string-to-email-transformer />
        </smtps:outbound-endpoint>
    </flow>
...

Para el servicio de envío de correos hemos configurado la tarea como asíncrona ya que no nos importa que no sea realice de forma inmediata tras la petición del cliente.

Para desplegar la aplicación en el servidor de Mule, podemos hacerlo seleccionando el fichero mule-config.xml desde Eclipse (si tenemos instalado el plugin de Mule) y con botón derecho Run As > Mule Server.

Para probar la aplicación invocaremos al servicio web a través de la URL http://localhost:8081/cars/search/id/2/mail/jalonso@autentia.com donde indicaremos un id del vehículo a buscar y el email donde queremos que nos envíe la respuesta. Si todo ha ido bien en la cuenta de correo indicada habrá llegado un correo con la información que le hemos solicitado.


6.Conclusiones

Con este caso práctico de un flujo de Mule hemos podido ver que con poco código y un poco de configuración obtenemos mucha funcionalidad. Es por ello que consideramos esta tecnología, al igual que otras similares, de gran ayuda para un gran número de aplicaciones de negocio que necesiten comunicar diferentes servicios.

Espero que te haya servido de ayuda.

Un saludo. Juan.

8 COMENTARIOS

  1. Hola, ps nada el ejemplo es de gran ayuda pero tengo problemas con:

    Puesto que esas variables son de un archivo properties \\\»email.properties\\\» sino estoy mal, pero quisiera saber cuales son los valores para estos parámetros q espera el componente Mule smtps. Ya que yo le doy algunos varoles y se genera una excepcion.

  2. oye tengo un par de preguntas, lo que sucede es que en este tema soy prácticamente nuevo, @WebResult y @WebParam para que sirven o que funcionalidad tienen??
    como puedo recibir la información que me envien en un WS (Web Service) transformarla en xml y enviarla a un adaptador?

    te agradeceria si me ayudaras o me dieras alguna informacion sobre eso, gracias 🙂

  3. Hola JUAN, estoy intentando seguir tu tutorial pero tengo algunos problemas con \\\»mail.properties\\\», resulta error, no encuentra éste archivo…..puedes ayudarnos con ésto?

  4. Ho9la Juan Alonso. Estoy estudianto la Herramienta para iniciar un proyecto y monte este proyecto del tutorial, el cuel esta muy buevno.

    Cuando pruebo el service desde la: http://localhost:8081/cars/SearchCar/id/2/mail/johnnyvi@hotmail.com, me sale el siguiete error:

    soap:Server
    No binding operation info while invoking unknown method with params unknown.

    Y asi sale el error en la consola:
    ERROR 2019-02-13 16:52:11,745 [[car_search].connector.http.mule.default.receiver.11] org.apache.cxf.service.invoker.AbstractInvoker: Invocation without a binding operation.
    WARN 2019-02-13 16:52:11,746 [[car_search].connector.http.mule.default.receiver.11] org.apache.cxf.phase.PhaseInterceptorChain: Interceptor for {http://tutoriales.autentia.com/}SearchCarService has thrown exception, unwinding now
    org.apache.cxf.interceptor.Fault: No binding operation info while invoking unknown method with params unknown.
    at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:59) ~[cxf-api-2.7.15.jar:2.7.15]……………….

    Me podrias indicar que podria haber faltado en la prueba.

    Gracias

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