Seleccionar página

Muchos no lo conoceréis pero Apache Karaf es un contenedor ligero y moderno basado en la tecnología OSGI que permite desplegar módulos, componentes y aplicaciones de forma aislada e independiente. A nivel conceptual, podemos imaginarnos que representa un servidor mínimo y personalizable capaz de soportar en caliente el ‘plug-and-play’ del software sin tropezar con los sufridos ‘Out Of Memory’.

En muchas ocasiones, se utiliza para la creación de integraciones basadas en arquitecturas ESB (incorporando el soporte para Apache Camel) e incluso la implementación de microservicios.

En este post vamos a realizar un ejemplo de un proyecto Camel desplegado en Apache Karaf que utiliza un datasource configurado externamente al proyecto y accesible de forma general a través de JNDI.

Nota: En muchas ocasiones, el datasource está configurado dentro del mismo proyecto por lo que no puede reutilizarse en otros recursos. 

Este ejemplo está realizado utilizando Ubuntu 20.04 LTS, OpenJDK 11 y Apache Karaf 4.3.3 atacando contra la base de datos MySQL 8.0.27.

Preparación de la base de datos

Antes de continuar, asegúrate que tienes instalado el servidor y el cliente de la base de datos MySQL.

A continuación, creamos un esquema test, un usuario y una tabla con datos insertados :

mysql> create database test;
Query OK, 1 row affected (0,15 sec)

mysql> create user 'neodoo'@'localhost' identified by 'neodoo';
Query OK, 0 rows affected (0,14 sec)

mysql> grant all privileges on test.* to 'neodoo'@'localhost';
Query OK, 0 rows affected (0,08 sec)

mysql> use test;
Database changed

mysql> create table Task (id int, title VARCHAR(50), description VARCHAR(250), dueDate DATETIME, finished BOOLEAN);
Query OK, 0 rows affected, 1 warning (0,54 sec)

mysql> insert into Task values (1, "Title", "Description", now(), true);
Query OK, 1 row affected (0,16 sec)

mysql> select * from Task;
+------+-------+-------------+---------------------+----------+
| id   | title | description | dueDate             | finished |
+------+-------+-------------+---------------------+----------+
|    1 | Title | Description | 2021-11-20 17:01:21 |        1 |
+------+-------+-------------+---------------------+----------+
1 row in set (0,00 sec)

Desde la consola del sistema operativo, verifica que puedes acceder a la base de datos con el nuevo usuario creado :

fsolans@fsolans-Lenovo-ideapad-330-15IKB:~$ mysql -u neodoo -pneodoo -h localhost test 
mysql: [Warning] Using a password on the command line interface can be insecure.
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 10
Server version: 8.0.27-0ubuntu0.21.10.1 (Ubuntu)

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

Si no puedes acceder al esquema a la base de datos con el usuario, ¡ no sigas y verifica los pasos anteriores !

Instalación de OpenJDK

Recuerda que Apache Karaf 4.3.x requiere Java SE 8 o superior.

En nuestro caso, instalamos la versión de OpenJDK 11 por lo que ejecutamos la siguiente sentencia desde la consola del sistema operativo :

fsolans@fsolans-Lenovo-ideapad-330-15IKB:~$ sudo apt install openjdk-11-jdk

A continuación, comprobamos la versión de Java :

fsolans@fsolans-Lenovo-ideapad-330-15IKB:~$ java --version
openjdk 11.0.12 2021-07-20
OpenJDK Runtime Environment (build 11.0.12+7-Ubuntu-0ubuntu3)
OpenJDK 64-Bit Server VM (build 11.0.12+7-Ubuntu-0ubuntu3, mixed mode, sharing)

Instalación de Apache Karaf

Descárgate la distribución binaria Karaf Runtime aquí (en el momento de la redacción de este artículo, la última versión es la 4.3.3) y descomprímela en tu máquina :

fsolans@fsolans-Lenovo-ideapad-330-15IKB:~$ sudo wget https://dlcdn.apache.org/karaf/4.3.3/apache-karaf-4.3.3.tar.gz
fsolans@fsolans-Lenovo-ideapad-330-15IKB:~$ sudo tar zxvf apache-karaf-4.3.3.tar.gz -C /usr/java/
Si lo prefieres, puedes utilizar la máquina AMI "Apache Karaf on Ubuntu 20.04 LTS by Neodoo" disponible en AWS Marketplace  y ahorrarte el proceso de instalación del servidor.

Arrancar Apache Karaf

Para arrancar el servidor, basta con ejecutar el siguiente comando :

fsolans@fsolans-Lenovo-ideapad-330-15IKB:~$ sudo -i
root@fsolans-Lenovo-ideapad-330-15IKB:~# cd /usr/java/apache-karaf-4.3.3/bin/
root@fsolans-Lenovo-ideapad-330-15IKB:/usr/java/apache-karaf-4.3.3/bin# ./karaf 
karaf: JAVA_HOME not set; results may vary
        __ __                  ____      
       / //_/____ __________ _/ __/      
      / ,<  / __ `/ ___/ __ `/ /_        
     / /| |/ /_/ / /  / /_/ / __/        
    /_/ |_|\__,_/_/   \__,_/_/         

  Apache Karaf (4.3.3)

Hit '<tab>' for a list of available commands
and '[cmd] --help' for help on a specific command.
Hit '<ctrl-d>' or type 'system:shutdown' or 'logout' to shutdown Karaf.

karaf@root()>                           

Como podemos observar, el servidor se arranca y al finalizar mantiene el acceso a la consola de Karaf.

No obstante, ésto no ocurre si se arranca como servicio como es el caso de nuestra máquina AMI y aunque el servidor también está arrancado, tendremos que conectarnos únicamente a la consola:

root@fsolans-Lenovo-ideapad-330-15IKB:/usr/java/apache-karaf-4.3.3/bin# ./client

Instalación del datasource

Provisionamos los features necesarios en Apache Karaf :

karaf@root()> feature:install jdbc jndi deployer aries-blueprint

A continuación, descargamos el driver JDBC de MySQL (versión 8.0.x), descomprimimos el fichero y copiamos la librería mysql-connector-java-8.0.x.jar en el directorio $KARAF_HOME/deploy.

Podemos ver cómo aparece la carga del driver consultando los logs de Karaf :

2021-11-21T15:02:36,248 | DEBUG | fileinstall-/usr/java/apache-karaf-4.3.3/deploy | configurator                     | 13 - org.apache.felix.configurator - 1.0.14 | Adding bundle com.mysql.cj:8.0.27 (55) : active
2021-11-21T15:02:36,249 | INFO  | fileinstall-/usr/java/apache-karaf-4.3.3/deploy | fileinstall                      | 17 - org.apache.felix.fileinstall - 3.7.0 | Started bundle: file:/usr/java/apache-karaf-4.3.3/deploy/mysql-connector-java-8.0.27.jar

La carga del driver se puede verificar también así :

karaf@root()> jdbc:ds-list
Name │ Service Id │ Product │ Version │ URL │ Status
─────┼────────────┼─────────┼─────────┼─────┼───────
karaf@root()> jdbc:ds-factories 
Name                │ Class                    │ Version │ Registration bundle
────────────────────┼──────────────────────────┼─────────┼────────────────────
com.mysql.cj-native │ com.mysql.cj.jdbc.Driver │ 8.0.27  │ com.mysql.cj [55]
karaf@root()> 

Creamos el fichero de configuración del datasource mysql8-with-mysql-connector-java-ds.xml :

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">

    <!-- This datasource is not a connection pool. It is just a concrete implementation of the javax.sql.DataSource interface
    <bean class="com.mysql.cj.jdbc.MysqlDataSource" id="mysql8Datasource">
    -->

    <!-- This datasource XA (a standard for working with distributed transaction) that extends MysqlDataSource
    <bean class="com.mysql.cj.jdbc.MysqlXADataSource" id="mysql8Datasource">
    -->

    <!-- This datasource is a connection pool that extends MysqlDatasource -->
    <bean class="com.mysql.cj.jdbc.MysqlConnectionPoolDataSource" id="mysql8Datasource">

        <property name="url" value="jdbc:mysql://localhost:3306/test"></property>

        <property name="user" value="neodoo"></property>
        <property name="password" value="neodoo"></property>

   </bean>

    <service interface="javax.sql.DataSource" ref="mysql8Datasource">

        <service-properties>
            <entry key="osgi.jndi.service.name" value="jdbc/mysql8"/>
        </service-properties>

    </service>

</blueprint>

Como podemos observar, hemos habilitado la clase compilada MysqlConnectionPoolDataSource del driver jdbc descargado que es una clase que extiende la clase MySqlDatasource y le agrega soporte para disponer de un pool de conexiones. Además, se han dejado comentado las dos clases MySqlDataSource y MySQLXADataSource que también puede emplearse.

Nota: Acuérdate de probar si eres capaz de acceder al esquema de la base de datos desde línea de comando.

Copiamos el fichero en el directorio $KARAF_HOME/deploy.

Podemos comprobar que se ha creado el datasource consultando el fichero $KARAF_HOME/data/log/karaf.log :

2021-11-21T15:10:23,875 | DEBUG | fileinstall-/usr/java/apache-karaf-4.3.3/deploy | configurator                     | 13 - org.apache.felix.configurator - 1.0.14 | Adding bundle mysql8-with-mysql-connector-java-ds.xml:0.0.0 (91) : active
2021-11-21T15:10:23,875 | INFO  | fileinstall-/usr/java/apache-karaf-4.3.3/deploy | fileinstall                      | 17 - org.apache.felix.fileinstall - 3.7.0 | Started bundle: blueprint:file:/usr/java/apache-karaf-4.3.3/deploy/mysql8-with-mysql-connector-java-ds.xml

Otra forma de consultar si el datasource se ha creado, es consultando el enlace jdbc y la creación del recurso jndi :

karaf@root()> jdbc:ds-list 
Name        │ Service Id │ Product │ Version                 │ URL                              │ Status
────────────┼────────────┼─────────┼─────────────────────────┼──────────────────────────────────┼───────
jdbc/mysql8 │ 141        │ MySQL   │ 8.0.27-0ubuntu0.21.10.1 │ jdbc:mysql://localhost:3306/test │ OK
karaf@root()> jdbc:ds-info jdbc/mysql8
Property       │ Value
───────────────┼─────────────────────────────────────────────────────────────────────────────────
driver.version │ mysql-connector-java-8.0.27 (Revision: e920b979015ae7117d60d72bcc8f077a839cd791)
service.id     │ 141
db.version     │ 8.0.27-0ubuntu0.21.10.1
name           │ jdbc/mysql8
db.product     │ MySQL
url            │ jdbc:mysql://localhost:3306/test
driver.name    │ MySQL Connector/J
username       │ root@localhost
karaf@root()> jndi:names 
JNDI Name                │ Class Name
─────────────────────────┼────────────────────────────────────────────────
osgi:service/jndi        │ org.apache.karaf.jndi.internal.JndiServiceImpl
osgi:service/jdbc/mysql8 │ com.mysql.cj.jdbc.MysqlConnectionPoolDataSource
karaf@root()> 

Una vez obtenido el enlace JNDI al datasource de MySQL, ya podemos utilizar este recurso desde nuestro proyecto Camel:

Proyecto Camel con Blueprint

Provisionamos las features de camel y los conectores utilizados en las rutas del proyecto :

karaf@root()> feature:repo-add camel                                                    
Adding feature url mvn:org.apache.camel.karaf/apache-camel/RELEASE/xml/features
karaf@root()> feature:install camel camel-sql

El proyecto es un ejemplo muy básico con un único fichero routes-with-datasources.xml que define las rutas de Camel aunque en desarrollos más avanzados, suele ir empaquetado en un jar o kar con otros tipo de recursos (clases compiladas, ficheros de configuración …).

Creamos el fichero routes-with-datasources.xml en el que se ha configurado una ruta de Camel que se activa una sola vez y realiza una consulta SQL :

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" default-activation="lazy">

    <!-- Reference to external datasource using jndi -->
    <reference id="dataSource" interface="javax.sql.DataSource"
        filter="(osgi.jndi.service.name=jdbc/mysql8)">
    </reference>

    <bean id="sql" class="org.apache.camel.component.sql.SqlComponent">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <camelContext id="routes-with-datasource" xmlns="http://camel.apache.org/schema/blueprint">

       <route id="route-with-sql-component">
           <from uri="timer://foo?repeatCount=1" />
           <to uri="sql:select * from Task" />
          <log message="[SQL] Received: ${body}"/>
       </route>

    </camelContext>

</blueprint>

Nota: Como puedes observar, este proyecto utiliza el datasource generado externamente que está disponible vía jndi (osgi.jndi.service.name=jdbc/mysql8).

Copiamos el fichero en $KARAF_HOME/deploy :

fsolans@fsolans-Lenovo-ideapad-330-15IKB:/usr/java/apache-karaf-4.3.3/deploy$ sudo cp ../routes-with-datasource.xml .

Para comprobar que se ha ejecutado la ruta de Camel, podemos consultar el fichero de logs $KARAF_HOME/data/logs//karaf.log :

2021-11-20T17:04:20,200 | TRACE | Camel (routes-with-datasource) thread #13 - timer://foo | CamelInternalProcessor           | 92 - org.apache.camel.camel-base-engine - 3.13.0 | Exchange processed and is continued routed asynchronously for exchangeId: 9DE199B766E526F-0000000000000000 -> Exchange[9DE199B766E526F-0000000000000000]
2021-11-20T17:04:21,150 | DEBUG | Camel (routes-with-datasource) thread #13 - timer://foo | TimerConsumer                    | 126 - org.apache.camel.camel-timer - 3.13.0 | Cancelling foo timer as repeat count limit reached after 1 counts.

Por defecto, no aparece el resultado de la consulta pero si queremos aumentar las trazas, podemos variarlo desde la consola de Karaf :

karaf@root()> log:set DEBUG

Si volvemos a acceder al fichero $KARAF_HOME/data/log/karaf.log, vemos que el número de mensajes ha aumentado y en este caso podemos ver incluso la salida de la consulta a la base de datos.

Conclusión

Como podemos observar, hemos realizado una integración muy simple utilizando la implementación de los diseños de patrones EIP (ver patrones de mensajería aquí) con Apache Camel sobre el servidor Apache Karaf, un servidor moderno cuyo motor se basa en la tecnología OSGI.

En el siguiente post «Crear un datasource en Apache Karaf (2 de 2)«, haremos un ejemplo utilizando la librería Apache DBCP2 que se utiliza por ejemplo en la definnición del pool en Apache Tomcat en vez de utilizar esta implementación de pool de conexiones MySQLConnectionPoolDataSource .

Pin It on Pinterest