MuleSoft 4 y sus tecnologías

Un ESB (Enterprise Services Bus) nos facilita interactuar entre dos o más sistemas informáticos y componentes de software de diversos proveedores. Un ESB es una tecnología para integraciones de software que gestiona datos y su comunicación entre diferentes servicios. En este contexto, encontramos que MuleSoft cuenta con su producto Mule ESB y complementarios orientado como plataforma de integraciones, el cual ha ganado popularidad y posicionamiento en el mercado en este tópico.

Para entender mejor el concepto, imagine que un gobierno publica un mecanismo o una API para gestionar la facturación electrónica. Cada empresa debe reportar al gobierno mediante su propio mecanismo o software (o adquirir solución de un tercero especializado). Ahora bien, tienes una aplicación que no cumple con los requisitos de la facturación electrónica y no ves necesario adquirir una solución de terceros porque existen características en tu sistema de gestión que ya hacen parte de la dinámica de tu organización, o prefieres ampliar las características de un producto propio. Así que cuentas con un sistema de gestión y el gobierno pide que la facturación electrónica se entrege bajo sus criterios y mecanismo, por tanto debes cubrir esto con una integración. Aquí aparece en el medio un ESB como el de MuleSoft, con el cual puedes implementar las transformaciones necesarias y comunicar los dos sistemas, reduciendo los tiempos de desarrollo del componente de software.

Mule ESB posee características tecnológicas como la estructura del mensaje (payload, attribute, vars) y flujos que se componen de conectores disponibles (HTTP Listener, HTTP Request, Select Database). Además, incorpora el uso de RAML para especificación de una API (basado en YAML), así como su propio lenguaje DataWeave para transformar datos (siendo un lenguaje sencillo y funcional, con semejanzas a Javascript).

Con la propuesta MuleSoft no sólo integras servicios, sino que también es posible construir tu propia API, y en una alta medida el desarrollo del componente de software se hace de modo visual (drag and drog).

En el presente texto veremos de modo ágil las características esenciales para comenzar con Mule ESB 4 y Anypoint Studio (7+).

Estructura de un Proyecto con Anypoint Studio

Una forma de comenzar con MuleSoft es decargar la herramienta Anypoint Studio (un IDE gráfico basado en Eclipse). Cuando inicias encontrarás en el proyecto los siguientes elementos:

Esta herramienta ya incorpora Mule ESB Server 4 en su edición empresarial (EE)

Ejemplo esencial de un Módulo XML

Anypoint Studio nos permite desarrollar aplicaciones o componentes que correrán en Mule ESB Server, y esto lo hace de un modo visual. Sin embargo, podemos encontrar que internamente se genera un archivo xml.

Para representar en xml lo que se hace con Anypoint Studio, veamos el contenido de un ejemplo esencial:

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

<mule xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns="http://www.mulesoft.org/schema/mule/core"
	xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd">

	<http:listener-config name="HTTP_Listener_config"
    doc:name="HTTP Listener config" doc:id="a2665ce3-9e1f-4a9a-b802-e8a531d1729a" >
		
    <http:listener-connection host="0.0.0.0" port="8081" />
	</http:listener-config>
	
  <flow name="mytestFlow" doc:id="6da5d990-6097-4bb1-9df7-709503c8b3fd" >
		
    <http:listener doc:name="Listener"
      doc:id="3b8a0a1f-482c-47f2-a504-2436de5037bb" config-ref="HTTP_Listener_config" path="/hello"/>
		
    <set-payload value="Hi there from Mule!" doc:name="Set Payload" doc:id="745f1b45-ae81-4730-b7bd-3a8a2d86872d" />
	</flow>
</mule>

Listener ~> Set Payload

En este ejemplo, se configura un Listener (para exponer un servicio en el puerto 8081) y se tiene un flujo con ese conector (Listener). Además, se usa el conector Set Payload (para establecer un valor).

Podemos consultar en un navegador la dirección localhost:8081/hello para ver el resultado, que correspondería al mensaje: Hi there from Mule!

Podríamos agregar luego un Logger para ver también el mensaje en consola, y así vamos definiendo nuestro flujo. Lo que se logra con este ejercicio es demostrar una funcionalidad sencilla pero operando sin escribir código alguno.

Estructura del Mensaje de Mule ESB

Cuando se asigna el valor al payload o a una variable se usa una cadena o una expresión. las expresiones e definen entre los siguientes caracteres: #[...]

Flujos de Mule ESB

Conectores de Mule ESB

Los conectores son componentes o módulos que hacen parte del flujo de una aplicación con Mule y pueden servir para acceder a un recurso o sistema, transformar un mensaje, hacer una petición web, acceder a la base de datos, o interactuar con alguna integración disponible (ej. AWS, Salesforce). A continuación se describen una serie de conectores.

RAML es YAML para diseñar API’s

Si conoces acerca de YAML (un lenguaje de marcado para datos) entonces ya tienes las bases para RAML, el cual busca especificar una API bajo su estándar. Esta especificación se puede usar en Anypoint Studio (MuleSoft) para generar automáticamente el flujo con los conectores iniciales, además de generar documentación sobre la API y lograr algo funcional.

Recomiendo acceder a una introducción sobre RAML

Como noción general, es importante tener presente las características esenciales de un archivo RAML:

Tendríamos que agregar que el archivo inicial (o root) debe tener el atributo title y la primera línea debe contener la expresión #%RAML 1.0 (que se refiere a la versión de RAML)

Un archivo sencillo, pensando en una API para gestionar posts de un Blog, puede comprender lo siguiente:

#%RAML 1.0
title: myapi

/posts:
  get:
    responses:
      200:
        body:
          application/json:
            example:
              { "id": "1", "title": "My Article" }
      400:
        body:
          application/json:
            example:
              { "message": "Not found" }

Este archivo se conoce como root y puede tener referencias a otros archivos para configurar tipos de datos (objetos) y ejemplos de datos.

Veamos un ejemplo completo asociando otros archivos por medio de !include

#%RAML 1.0
title: myapi

types:
  mytype: !include plus/dataType.raml

/posts:
  get:
    responses:
      200:
        body:
          application/json:
            example: !include plus/example.raml
      400:
        body:
          application/json:
            example:
              { "message": "Not found" }
  post:
    body:
      application/json:
        type: mytype
    responses:
      200:
        body:
          application/json:
            type: mytype
      400:
        body:
          application/json:
            example:
              { "message": "Something wrong" }
  /{id}:
    get:
      responses:
        200:
          body:
            application/json:
              type: mytype
        400:
          body:
            application/json:
              example:
                { "message": "Not found" }
    delete:
      responses:
        200:
          body:
            application/json:
              example:
                { "message": "Deleted" }
        400:
          body:
            application/json:
              example:
                { "message": "Something wrong" }
    put:
      body:
        application/json:
          type: mytype
      responses:
        200:
          body:
            application/json:
              type: mytype
        400:
          body:
            application/json:
              example:
                { "message": "Not found" }
#%RAML 1.0 DataType

type: object
properties:
  id: string
  author: string
  title: string
  content: string

Para datos numéricos se usa number

#%RAML 1.0 NamedExample

value:
  -
    "id": "1",
    "title": "Art",
    "author": "John",
    "content": "",
  -
    "id": "2",
    "title": "Music",
    "author": "Jane",
    "content": "",

Con la combinación de estos archivos tendremos una especificación sencilla de nuestra API. RAML es una alternativa a OpenAPI (Swagger) y que en lo personal prefiero RAML por su sencillez. Además, es posible convertir RAML a OpenAPI.

DataWeave para transformar datos

En la versión comercial, MuleSoft cuenta con su propio lenguaje de expresiones para transformar datos, denominado DataWeave. Se trata de un lenguaje funcional y de script orientado a resolver el mapeo de datos del payload de manera sencilla y potente. Tiene algunas similitudes a Javascript pero no encuentras bucles for (aunque en su lugar se usa map).

Recomiendo acceder a la introducción oficial a DataWeave
Alternativamente, encuentras este tutorial o su respectivo video

Veamos un ejemplo:

%dw 2.0
output application/json
---
payload map ( payload01 , indexOfPayload01 ) -> {
	id: payload01.id,
	name: payload01.title
}

Aquí se procesa cada elemento del payload (con map) aplicando una concordancia entre los datos recibidos y los que se van a mostrar, por ejemplo, name se obtiene a partir de payload01.title. El output indica que se mostrará en formato json.

En el segundo ejemplo buscaremos filtrar los datos…

%dw 2.0
output application/json
---
payload.data.children
filter (not $.data.ups < 200 or $.data.downs > 0)
map ( child , indexOfChild ) -> {
	id: child.data.id,
	title: child.data.title,
	author: child.data.author,
  content: child.data.selftext
}

En este caso, se elige un elemento para ser filtrado (con filter) y se mapea cada dato (con map)

Nótese que las líneas superiores que se encuentran antes de los caracteres --- indican la versión del lenguaje a usar y el tipo de salida (json), allí mismo podrían definirse funciones y variables para preperar los datos. En la región bajo los caracteres --- definimos la manera en que se transforman los datos, es decir, el tratamiento de la salida. Generalmente, los datos se encuentran en el payload o vars, también se pueden aplicar funciones. El signo $ puede ser usado en un ámbito para referirse al elemento procesado en una iteración.

Veamos un ejemplo en XML

%dw 2.0
output application/xml
---
data:

item: payload map ( payload01 , indexOfPayload01 ) -> {
	id: payload01.id,
	name: payload01.title
}

Nótese que en el output se indica xml (application/xml según el mimeType respectivo). Además, se inicia con un elmento raíz (ej. data).
De modo semejante a json y xml, podríamos retornar datos en formato csv (Common Separate Version).

Finalmente, si se hace un request y se desea que el body se envíe como json, se usa también DataWeave (de lo contrario, se procesa como si se tratara de un formulario). Por ejemplo:

%dw 2.0
output application/json
---
{
	"name": "John",
	"age": 25
}

Preparando la base de datos para el ejercicio

Usando MySQL

Para comprender el concepto del uso de base de datos, usaremos el motor MySQL (o MariaDB), el cual debe estar instalado debidamente con su servicio iniciado.

Si cuentas con sistema Windows existen herramientas como Laragon (u otras) que facilitan su instalación y gestión de modo gráfico.

En Linux se inicia el servicio de MySQL ejecutando: sudo systemctl start mysqld
En macOS puede iniciarse ejecutando: sudo /usr/local/mysql/support-files/mysql.server start

Para acceder a la consola de MySQL desde Linux ejecutamos:

mysql -u root -p

En Debian se ejecuta: sudo mysql
En macOS se ejecuta: /usr/local/mysql/bin/mysql -u root -p

Desde allí podemos crear nuestra base de datos, por ejemplo myapi, incluso podemos agregar un usuario y establecer sus privelegios salvo que se desee simplificar el ejercicio utiliando el usuario root (aunque es conveniente tener un usuario específico). Veamos un ejemplo de las sentencia a utilizar:

CREATE DATABASE IF NOT EXISTS myapi;
CREATE USER 'myapi'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON mytest.* TO 'myapi'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;

CREATE TABLE posts (
	id varchar,
	author varchar,
	title varchar,
	content varchar,
	constraint pk_posts primary key (id)
);

De este modo, tendríamos nuestra base de datos configurada y nuestra primera tabla (posts). A continuación se ilustra el esqueleto de las sentencias SQL que usaremos posteriormente en Mule.

SELECT * FROM posts;

SELECT * FROM posts WHERE id = :id;

INSERT INTO posts (id, author, title, content)
VALUE (:id, :author, :title, :content);

DELETE FROM posts WHERE id = :id;

UPDATE posts
SET title = :title, content = :content
WHERE id = :id;

Alternativa de conexión con H2 Database

Además de bases de datos como Oracle y MySQL, podemos configurar alternativamente una base de datos embebida como lo es H2 Database usando un tipo de conexion denominado Generic Connection (aplicando el conector Database o creando un elemento global). Para ello se debe especificar el artefacto de Maven respectivo, así:

<dependency>
	<groupId>com.h2database</groupId>
	<artifactId>h2</artifactId>
	<version>2.1.210</version>
</dependency>

En la configuración del conector Database, debemos suministrar la url, por ejemplo jdbc:h2:/home/mydb. Además, Driver class name, que correspondería a org.h2.Driver, el usuario y la clave si aplican.

Para representar esto en xml tendriamos el siguiente contenido…

	<db:config name="Database_Config" doc:name="Database Config"
    doc:id="9fb0e9b2-2276-4f6a-b129-729a278543b8" >
		
    <db:generic-connection url="jdbc:h2:/home/mydb" driverClassName="org.h2.Driver" user="sa" password="" />

	</db:config>

Proyecto de inicio rápido

Retomaremos el ejemplo inicial para hacer un proyecto combinando algunas características. Comenzaremo usando el conector Request

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

<mule xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns="http://www.mulesoft.org/schema/mule/core"
	xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd">

	<http:listener-config name="HTTP_Listener_config"
    doc:name="HTTP Listener config" doc:id="a2665ce3-9e1f-4a9a-b802-e8a531d1729a" >

		<http:listener-connection host="0.0.0.0" port="8081" />
	</http:listener-config>

	<http:request-config name="HTTP_Request_configuration"
    doc:name="HTTP Request configuration" doc:id="6a110cd6-efe6-47c8-97bd-d8a57dd2007c" >

		<http:request-connection protocol="HTTPS" host="www.reddit.com" />
	</http:request-config>

	<flow name="mytestFlow" doc:id="6da5d990-6097-4bb1-9df7-709503c8b3fd" >

		<http:listener doc:name="Listener"
      doc:id="3b8a0a1f-482c-47f2-a504-2436de5037bb" config-ref="HTTP_Listener_config" path="/load"/>

		<http:request method="GET" doc:name="Request"
      doc:id="a68732da-0663-454f-86e5-38ed6b7a9c51" config-ref="HTTP_Request_configuration" path="/r/aww.json"/>
	</flow>
</mule>

En el siguiente paso se complementa el anterior y se busca representar el uso de Transform Message. Veamos como queda el flow

	<flow name="mytestFlow" doc:id="6da5d990-6097-4bb1-9df7-709503c8b3fd" >

		<http:listener doc:name="Listener"
      doc:id="3b8a0a1f-482c-47f2-a504-2436de5037bb" config-ref="HTTP_Listener_config" path="/load"/>

		<http:request method="GET" doc:name="Request"
      doc:id="a68732da-0663-454f-86e5-38ed6b7a9c51" config-ref="HTTP_Request_configuration" path="/r/aww.json"/>
		
    <ee:transform doc:name="Transform Message"
      doc:id="6f9b945a-ba68-4f29-8b21-f08f5dbea08d" >
			<ee:message >
				<ee:set-payload ><![CDATA[%dw 2.0
output application/json
---
payload.data.children map ( child , indexOfChild ) -> {
	id: child.data.id,
	title: child.data.title,
	author: child.data.author,
	content: child.data.selftext,
}]]></ee:set-payload>
			</ee:message>
		</ee:transform>
	</flow>

Agregemos ahora una conexión a la base de datos, lo cual quedaría representado como el siguiente contenido:

	<db:config name="Database_Config"
    doc:name="Database Config" doc:id="18df2f9d-6743-462b-a691-65a71e80f47d" >
		
    <db:my-sql-connection host="localhost" port="3306" user="root" password="password" />
	</db:config>

Y dentro de flow tendríamos un conector de base de datos (Database), por ejemplo así:

		<db:bulk-insert doc:name="Bulk insert"
      doc:id="8337459c-84c5-4a01-9de2-ffb3ed911589" config-ref="Database_Config">
			
      <db:sql ><![CDATA[insert ignore into posts (id, author, content)
values (:id, :author, :title, :content);]]></db:sql>
		</db:bulk-insert>

Para completar lo anterior, debemos aplicar otra transformación (Trannsform Message), por ejemplo:

		<ee:transform doc:name="Transform Message"
      doc:id="36f5cac9-54eb-4c50-9663-5f96c1a6777e" >
			
      <ee:message >
				<ee:set-payload ><![CDATA[%dw 2.0
output application/json
---
{
	success: true,
	message: "OK",
	total: sum(payload)
}]]></ee:set-payload>
			</ee:message>
		</ee:transform>

Ahora podemos consultar en el navegador en la ruta respectiva: localhost:8080/load

Podríamos cambiar el Listener inicial por Scheduler para lograr una tarea programada. Es decir, cada cierto tiempo (1 hora) se consulta una API para cargar y sincronizar la información en una tabla de la base de datos. Su representación sería la siguiente:

Scheduler ~> Request ~> Transform Message ~> Bulk Insert ~> Transform Message

		<scheduler doc:name="Scheduler"
      doc:id="57883d72-b14b-4de3-a04b-deffad51574c" >
			
      <scheduling-strategy >
				<fixed-frequency frequency="1" timeUnit="HOURS"/>
			</scheduling-strategy>
		</scheduler>

Haciendo una API con MuleSoft

A continuación encontraremos algunos trozos del archivo xml generado por la herramienta Anypoint Studio para representar un ejercicio que consiste en una API de posts de un Blog. Es posible agilizar nuestro ejercicio ya que contamos con la especificación en RAML (que es lo primero que debe hacerse en este caso).

Debe seleccionarse el proyecto y abrir el menú desplegable, se busca la opción Manage Dependencies. En la ventana que aparece se da clic en el icono del signo más y luego elegimos la opción from Exchange. En la nueva ventana se filtra con el nombre asignado a la especificación. En caso de que no aparezca es posible que se haya definido el proyecto como privado y debas conectarte. Se seleccióna la línea que corresponde, se agrega el módulo y se finaliza la operación.

Se ilustra a continuación el esquema que se genera automaticamente a partir de la especificación…

Listener ~> APIKit Router

<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:apikit="http://www.mulesoft.org/schema/mule/mule-apikit" xmlns:db="http://www.mulesoft.org/schema/mule/db" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation" xmlns:ee="http://www.mulesoft.org/schema/mule/ee/core" xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd http://www.mulesoft.org/schema/mule/mule-apikit http://www.mulesoft.org/schema/mule/mule-apikit/current/mule-apikit.xsd http://www.mulesoft.org/schema/mule/ee/core http://www.mulesoft.org/schema/mule/ee/core/current/mule-ee.xsd  http://www.mulesoft.org/schema/mule/db http://www.mulesoft.org/schema/mule/db/current/mule-db.xsd">
    <http:listener-config name="myapi-httpListenerConfig">
        <http:listener-connection host="0.0.0.0" port="8081" />
    </http:listener-config>

    <apikit:config name="myapi-config"
      api="resource::f7bc2de9-104f-46b7-a071-9f2829f6fd08:myapi:1.0.2:raml:zip:myapi.raml"
      outboundHeadersMapName="outboundHeaders" httpStatusVarName="httpStatus" />
</mule>

Si se trata de un proyecto local, la propiedad api de apikit:config podría ser, por ejemplo, myapi.raml

Podríamos agregar una variable dentro de este flow para determinar algún criterio, por ejemplo con la siguiente representación:

        <set-variable value="json"
          doc:name="Set Variable" doc:id="f5a2e314-04ff-4593-abfc-90c65decbd3f" variableName="kind"/>

Adicionalmente, encontraremos flujos privados para la API de consulta con el siguiente esquema…

Select ~> Transform Message

    <flow name="get:\posts:myapi-config">
      <db:select doc:name="Select"
        doc:id="2c3ae8a9-0161-4b44-a847-123919e36280" config-ref="Database_Config">
        
        <db:sql ><![CDATA[select * from posts;]]></db:sql>
      </db:select>

      <ee:transform doc:name="Transform Message"
        doc:id="2e3ce23f-7af8-497b-b2e1-338ec8dfc836" >
        
        <ee:message >
          <ee:set-payload ><![CDATA[%dw 2.0
  output application/json
  ---
  payload map ( payload01 , indexOfPayload01 ) -> {
    id: payload01.id,
    name: payload01.name
  }]]></ee:set-payload>
        </ee:message>
      </ee:transform>
    </flow>

Suponiendo que agregamos la ruta apixml para indicar que el resultado será en formato xml (duplicando el flujo que incluye ÀPIKit Router y asignando en ese flujo el valor de la variable kind en xml), tendríamos un flujo de consulta con el conector Choice de la siguiente manera:

    <flow name="get:\posts:myapi-config">
      <db:select doc:name="Select"
        doc:id="2c3ae8a9-0161-4b44-a847-123919e36280" config-ref="Database_Config">
        
        <db:sql ><![CDATA[select * from posts;]]></db:sql>
      </db:select>

		  <choice doc:name="Choice"
        doc:id="456bd214-47ba-4cfc-86f0-49fd89eda88c" >
			
        <when expression='#[vars.kind == "json"]'>
				  <ee:transform doc:name="Transform Message"
            doc:id="2e3ce23f-7af8-497b-b2e1-338ec8dfc836">
			    
            <ee:message>
				      <ee:set-payload><![CDATA[%dw 2.0
output application/json
---
payload map ( payload01 , indexOfPayload01 ) -> {
	id: payload01.id,
	name: payload01.title
}]]></ee:set-payload>
			    </ee:message>
		    </ee:transform>
			</when>
			<otherwise >
				<ee:transform doc:name="Transform Message"
          doc:id="b241159b-694d-4c1c-95ab-833fd14a4b37" >
					
          <ee:message >
						<ee:set-payload ><![CDATA[%dw 2.0
output application/xml
---
data:

item: payload map ( payload01 , indexOfPayload01 ) -> {
	id: payload01.id,
	name: payload01.title
}]]></ee:set-payload>
					</ee:message>
				</ee:transform>
			</otherwise>
		</choice>
  </flow>

Así podríamos representar las demás acciones de la API.

Invocando un componente en Java con Mule 4

Con el connector extra para Java, es posible invocar un método de una clase de este lenguaje. Por ejemplo, planteemos el siguiente código:

package mypackage;

public class MyComponent {
	public static String getResult(String name) {
		return "Hello " + name + " from Java!";
	}
}

Teniéndo el código anterior, pensemos en un flujo como el siguiente:

Listener ~> Set Variable ~> Invoke static (Java) ~> Set Payload

Para representar esto tendriamos el siguiente contenido…

	<flow name="mysyncFlow1" doc:id="7328424d-904f-436b-a3bf-4faf7f55e8bf" >

		<http:listener doc:name="Listener"
      doc:id="37c0330b-9304-4d6d-9d16-7db563efd22d" config-ref="myapi-httpListenerConfig" path="/java"/>
		
    <set-variable value="Andrey" doc:name="Set Variable"
      doc:id="9f580932-d67a-4070-89c0-2c1848676abe" variableName="mytext"/>
		
    <java:invoke-static method="getResult(java.lang.String)"
      doc:name="Invoke static" doc:id="f2b67bc1-17ac-4692-98ea-a74789e13a27" class="mypackage.MyComponent">
			
      <java:args ><![CDATA[#[arg0: vars.mytext]]]></java:args>

		</java:invoke-static>
		
    <set-payload value="#[payload]" doc:name="Set Payload"
      doc:id="6dd4f299-aee3-4a4e-87ef-49e4764ae863" mimeType="text/plain"/>
    
	</flow>

Se puede observar que se realiza una operación de transformación con el conector Java.
Además de algún requisito particular de usar código Java, se puede ver como una alternativa cuando no cuentas con la versión empresarial de MuleSoft debido a que la versión comunitaria no incluye DataWeave.

Scripting con Mule 4 (alternativas a DataWeave)

Jython & Mule 4

Jython es la implementación de Python 2.7 para la JVM y también es posible usarlo en Mule gracias al conector Scripting. Lo primero que hacemos es incorporar la configuración de la librería respectiva en Maven a través del archivo pom.xml de nuestro proyecto, es decir, copiar y pegar el siguiente bloque dentro de las dependencias (<dependencies>):

<dependency>
  <groupId>org.python</groupId>
  <artifactId>jython-slim</artifactId>
  <version>2.7.2</version>
</dependency>

Asegúrese de encontrar groupid y artifactId también bajo la etiqueta <sharedLibraries> del pom.xml. Por ejemplo:

<sharedLibrary>
  <groupId>org.python</groupId>
  <artifactId>jython-slim</artifactId>
</sharedLibrary>

Pensemos que el script tendrá el siguiente contenido:

result = "Hi " + arg0 + " from Jython!"

Ahora pensemos en un flujo como el siguiente:

Listener ~> Set Variable ~> Scripting ~> Set Payload

Para representar esto tendriamos el siguiente contenido…

	<flow name="mysyncFlow2" doc:id="7f5b31f1-96b4-417a-bc95-003c948aef00" >

		<http:listener doc:name="Listener"
      doc:id="b7337c7e-9bb3-4e98-8de0-2ef7e1eaf1f4" config-ref="myapi-httpListenerConfig" path="/jython"/>
		
    <set-variable value="Andrey" doc:name="Set Variable"
      doc:id="0d25efb2-d7cf-401a-9362-8df11cd87813" variableName="mytext"/>
		
    <scripting:execute engine="python" doc:name="Execute"
      doc:id="db72e411-c730-4e49-bd90-fbb6c007c6c9">
			
			<scripting:code><![CDATA[result = "Hi " + arg0 + " from Jython!"]]></scripting:code>
			
      <scripting:parameters><![CDATA[#[arg0: vars.mytext]]]></scripting:parameters>

		</scripting:execute>
		
    <set-payload value="#[payload]" doc:name="Set Payload"
      doc:id="cf89d67d-3f6c-44e8-98df-0474bc6f4e03" mimeType="text/plain"/>
    
	</flow>

Se puede observar que se realiza una operación de transformación con el conector Scripting (scripting:code>).
Esto también se puede ver como una alternativa cuando no cuentas con la versión empresarial de MuleSoft debido a que la versión comunitaria no incluye DataWeave.

LuaJ & Mule 4

LuaJ es la implementación de Lua 5.2 para la JVM y es también posible usarlo en Mule gracias al conector Scripting.

Basados en lo visto anteriormente para Jython, veamos el escenario para LuaJ, empezando con el archivo pom.xml.

<dependency>
  <groupId>org.luaj</groupId>
  <artifactId>luaj-jse</artifactId>
  <version>3.0.1</version>
</dependency>

Asegúrese de encontrar groupid y artifactId también bajo la etiqueta <sharedLibraries> del pom.xml.

Pensemos que el script tendrá el siguiente contenido:

result = "Hi " .. arg0 .. " from LuaJ!"

Y la etiqueta xml del Mule podría verse así:

		<scripting:execute engine="lua" doc:name="Execute"
      doc:id="db72e411-c730-4e49-bd90-fbb6c007c6c9">
			
			<scripting:code><![CDATA[result = "Hi " .. arg0 .. " from LuaJ!"]]></scripting:code>
			
      <scripting:parameters><![CDATA[#[arg0: vars.mytext]]]></scripting:parameters>

		</scripting:execute>

JRuby & Mule 4

JRuby es la implementación de Ruby 2.6 para la JVM y es también posible usarlo en Mule gracias al conector Scripting.

Basados en lo visto anteriormente para Jython, veamos el escenario para JRuby, empezando con el archivo pom.xml.

<dependency>
  <groupId>org.jruby</groupId>
  <artifactId>jruby-core</artifactId>
  <version>9.3.3.0</version>
</dependency>
<dependency>
  <groupId>org.jruby</groupId>
  <artifactId>jruby-stdlib</artifactId>
  <version>9.3.3.0</version>
</dependency>

Asegúrese de encontrar groupid y artifactId también bajo la etiqueta <sharedLibraries> del pom.xml.

Pensemos que el script tendrá el siguiente contenido:

"Hi from JRuby!"

Y la etiqueta xml del Mule podría verse así:

		<scripting:execute engine="ruby" doc:name="Execute"
      doc:id="db72e411-c730-4e49-bd90-fbb6c007c6c9">
			
			<scripting:code><![CDATA["Hi from JRuby!"]]></scripting:code>
			
      <scripting:parameters><![CDATA[#[arg0: vars.mytext]]]></scripting:parameters>

		</scripting:execute>

Invocando un componente en Kotlin con Mule 4

Un camino muy estrecho

Dado que Kotlin proporciona interoperabilidad con Java, podría llegar a pensarse en usar este lenguaje con MuleSoft. Un ejemplo potencial del código anteriormente visto para Java podría ser el siguiente:

package mypackage;

class MyKotlinComponent {
  companion object {
    @JvmStatic
    fun getResult(name: String): String {
      return "Hello " + name + " from Kotlin!";
    }
  }
}

Sin embargo, actualmente con Mule 4 al configurar Maven no es posible integrar directamente el proceso de compilación para Kotlin. Por ello, lo que se debe hacer es un proyecto independiente de Mule y, mediante una clase de Java, importar las clases de una librería .jar. Las librerías se agregan danndo clic derecho sobre el elemento de nuestro proyecto (dentro del Package Explorer) para desplegar un menú, allí elegimos la opción Build Path y luego Add External Archives.

La otra alternativa consistiría en usar Anypoint Studio para crear un projecto bajo Kotlin que funcione como dependencia de nuestro proyecto en Mule.

Anypoint Studio se basa en Eclipse pero no viene con la opción de Marketplace, por lo que debemos agregar primero tal opción y luego el complemento para Kotlin. El Marketplace puede habilitarse mediante el menú Help / Install new software, agregando el repositorio Oxygen con el enlace https://download.eclipse.org/releases/oxygen. Cuando nos aparezca la lista, dentro del software disponible, buscamos la opción General Purpose Tools / Marketplace Client.

Una vez instalado el Marketpalce, reiniciamos Anypoint Studio y encontraremos la opción respectiva en el menu Help. De este modo, podremos buscar el complemento para Kotlin e instalarlo (y reniciamos la herramienta de nuevo).

Si se resuelve seguir este camino, debe recordarse que, por compatibilidad con el JDK de Anypoint Studio, en el archivo de configuración del proyecto (ej. pom.xml) se usaría la versión de JDK 8,

Instalacion y despliegue de Mule Runtime Standalone

Caundo deseamos ejecutar una instancia local de Mule, lo primero que debemos hacer es descargar la versión del Runtime (o Kernel) desde el sitio oficial

Una vez descargado simplemente se descomprime y se deja en una ubicación deseable. La carpeta descargada suele tener una estrucutra como la siguiente:

De modo esencial, podemos tener presente que en bin se encuentra el ejecutable y en apps se ubican los despliegues.

Para iniciar el servicio nos ubicamos en bin y podemos ejecutar, por ejemplo:

./mule console
./mule start
./mule stop

Mule utiliza Java Service Wrapper para su instalación y lanzamiento, en caso de inconvenientes con dicho componente encuentras en Internet la version apropiada para tu sistema (por ejemplo: wrapper-macosx-universal-64).

Se debe tener presente que una aplicación se despliega en principio usando Anypoint Studio, por lo que debe accedcer a la herramienta y utilizar del menú File la opción Export. Allí se abre la carpeta o categoría Mule y encuentras la opción Anypoint Studio Project to Mule Deployable Archive. Luego podemos seleccionar el proyecto. Esto nos genera una archivo .jar que se ubicaría en la carpeta apps de nuestra instalación de Mule. De hecho, podemos indicarlo esta ruta en la generación del .jar.

También es poisble usar la plataforma en la nube para configurar con MuleSoft Runtime servidores Standalone y gestionar su despliegue.

Proyecto de ejemplo usando Maven (sin Anypoint Studio)

Una alternativa al uso de Anypoint Studio es usar Maven (y su archivo pom.xml). De esta manera podríamos configurar nuestro proyecto y desplegarlo como vimos en un punto anterior. Evidentemente, no tendremos un entorno visual por lo que tendremos que usar Mule con archivos xml y quizás un poco de Java.

Sin embargo, la última versión disponible como artefacto de Maven, para construir un proyecto de Mule, No es basada en la 4 sino en la 3 (3.9.0) cuya actualización se lanzó el 17 de febrero de 2021 usando Java versión 8.

Basados en lo anterior, como requisito debe estar instalado Maven y debidamente configurado. Maven suele configurarse en la ruta $HOME/.m2. En .m2 puede establecerse un archivo de configuración con nombre settings.xml y con un contenido como el siguiente:

<settings>
    <pluginGroups>
        <pluginGroup>org.mule.tools</pluginGroup>
    </pluginGroups>

  <profile>
      <id>Mule Org</id>
      <activation>
          <activeByDefault>true</activeByDefault>
      </activation>
      <repositories>
          <repository>
              <id>mulesoft-releases</id>
              <name>MuleSoft Repository</name>
              <url>https://repository-master.mulesoft.org/releases/</url>
              <layout>default</layout>
          </repository>
          <repository>
              <id>mulesoft-snapshots</id>
              <name>MuleSoft Snapshot Repository</name>
              <url>https://repository-master.mulesoft.org/snapshots/</url>
              <layout>default</layout>
          </repository>
      </repositories>
  </profile>
</settings>

Si ya exsite un archivo .m2/settings.xml, debe interpretarse loa anterior de modo que se copie el contenido necesario dentro del bloque respectivos (ejemplo: profile).

Para iniciar el proyecto usando Maven, nos ubicamos en una carpeta destinada como espacio de trabajo para proyectos de código de software, y ejecutamos el comando a continuación:

mvn mule-project-archetype:create -DartifactId=myproject -DmuleVersion=3.9.0

myproject corresponde al nombre de la nueva carpeta para nuestro proyecto. 3.9.0 es la última versión admitida usando Maven.

Si se ejecuta bien nos presentará unas preguntas sobre la descripcion, la versión (3.9.0), el package del proyecto representado expresado como un path (es decir, con /), los transportadores (por defecto: file,http,jdbc,jms,vm), y los modulos que se deseen incluir (por ejemplo, json,client,cxf,management,scripting,sxc,xml), los cuales se pueden dejar por defecto. Finalmente, obtendremos una nueva carpeta correspondiente a nuestro proyecto.

Para implementar nuestra aplicación usaremos el archivo src/main/app/mule-config.xml, es decir, lo abrimos y colocamos un nuevo flujo con un contenido como el siguiente:

<?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"
    xsi:schemaLocation="
       http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd">

  <flow name="myMuleFlow">
      <http:inbound-endpoint exchange-pattern="request-response"
        host="localhost" port="8082" />

      <set-payload value="Hi there!"/>
  </flow>

</mule>

Después de guardar y cerrar el archivo, podemos ejecutar el comando de Maven siguiente:

mvn package -DskipTests

Para el desplieuge ejecutaríamos un comando como el siguiente:

mvn package deploy -DmuleDeploy -Dmule.version=3.9.0 -Dmule.home=$HOME/mule-standalone-3.9.0 -DskipTests

Lo que se busca es reemplazar el archivo mule-config.xml en la ruta apps/default de Mule 3.9.0. Si lo anterior no funciona, es preciso revisar los conceptos asociados a las carpetas para establecer un despliegue manual. Por ejemplo, crear la carpeta del proyecto bajo la carpeta apps y mover nuestro archivo xml, aunque quizás se requiera configurar un dominio (domains).