Despliegue y monitorización de Aplicaciones Spring Boot desde Control Panel de Sofia2

Introducción

Se ha integrado en el componente Contenedor de ThinKPs la posibilidad de desplegar aplicaciones basadas en Spring Boot. Para simplificar la integración de estas aplicaciones con Sofia2 se ha avanzado con la funcionalidad del módulo Sofia4Boot descrito aqui.

Las aplicaciones desplegadas dentro del contenedor de Sofia2 pueden ofrecer cualquier tipo de funcionalidad, incluso si no requiere de Sofia2 más que el contenedor y el sistema de gestión (en entornos desplegados tras un Nginx puede requerir cierta configuración, como abrir puertos).

Autorización

Las aplicaciones Spring Boot que usen en contenedor de ThinKPs necesitan proporcionar ciertos credenciales a la plataforma Sofia2. Para esto es necesario disponer de un ThinKP asociado a una ontología, y proporcionar como propiedad el token de acceso (ver sección de propiedades más adelante).

Creando una aplicación nueva

Para crear una aplicación basada en Spring Boot para Sofia2 se debe empezar con incluir en el POM las dependencias necesarias. Sofia2 dispone de un repositorio Nexus con las dependencias requeridas.

Atención! Sofia4Boot requiere de una versión de Java igual o superior a 1.8

Repositorio Nexus de Sofia2

<repositories>
     <repository>
    <id>SOFIA2</id>
    <url>http://sofia2.org/nexus/content/groups/public/</url>
    </repository>
</repositories>

Dependencias de Sofia4Boot

<dependency>
    <groupId>com.indra.sofia2.boot</groupId>
    <artifactId>sofia4boot</artifactId>
    <version>1.3</version>
</dependency>

Además hay que incluir las dependencias propias de Spring Boot (en esto no hay diferencias respecto a una aplicación SpringBoot típica). Es particularmente importante asegurarse que la aplicación que generamos es autocontenida (y ejecutable). Para esto lo más normal es usar el plugin de Spring Boot:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Durante el diseño de la aplicación hay que tener en cuenta que a la hora de arrancarla, el contenedor añadirá elementos adicionales a la configuración, como por ejemplo, limitación de memoria y propiedades de configuración. Un ejemplo de comando de arranque puede ser: -Xmx256m -Dspring.boot.admin.url=http://127.0.0.1:8101 -Dspring.boot.admin.prefer-ip=true. Esto incluye por ejemplo la configuración interna necesaria para registrar la aplicación en la instancia spring boot admin de Sofia2.

Clase principal

Para la clase principal usaremos una plantilla habitual para Spring Boot, a la que añadiremos el soporte para que se integre con el sistema de administración (esto se consigue añadiendo la anotación @EnableSpringBootAdminRegister).

@SpringBootApplication
@EnableSpringBootAdminRegister
public class S4BApp 
{
    public static void main (String [] args) {
        new SpringApplicationBuilder(S4BApp.class).run(args);
    }
}

La anotación @EnableSpringBootAdminRegister se encargará de registrar la aplicación en la consola, con lo que se podrá gestionar en una ventana de la consola de Sofia2:

Modelado de una ontología

La forma más fácil de modelar una ontología es descargarse las clases generadas desde el Control Panel de Sofia2. Para esto hay que ir a la opción de menú ‘Mis Ontologías’, buscar la ontología que se quiera, pulsar sobre el botón ‘Ver Ontología’ (con un icono representando un ojo), y al final de la ventana deberemos ver un botón con el texto ‘Clase Java de la Ontología’. Esto nos descargará un fichero comprimido con las clases de la ontología.

Esto también puede hacerse a mano. Un ejemplo de modelado de una ontología sencilla sería la siguiente:

Modelo de la ontología de ejemplo

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "level Schema",
    "type": "object",
    "required": [
        "Level"
    ],
    "properties": {
        "Level": {
            "type": "string",
            "$ref": "#/datos"
        }
    },
    "datos": {
        "description": "descripcion",
        "type": "object",
        "required": [
            "date"
        ],
        "properties": {
            "value": {
                "type": "integer"
            },
            "date": {
                "type": "string"
            }
        }
    }
}

Y un ejemplo de instancia:

{
    "Level": {
        "value": 1,
        "date": "string"
    }
}

Las clases Java de modelado serían:

Modelo de datos

public class Level {

    private String date;
    private Integer value;

    public Level() {

    }

    public Level(String date, Integer value) {
        this.date = date;
        this.value = value;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public Integer getValue() {
        return value;
    }

    public void setValue(Integer value) {
        this.value = value;
    }
}

Y como la ontología tiene un nivel adicional para controlar siempre los valores, tendremos que incluir un wrapper

Envoltorio de ontología

public class LevelOntology extends Ontology<Level> {

    @JsonProperty("Level")
    private Level level;

    public LevelOntology () {}

    public LevelOntology (String date, Integer value) {
        level = new Level(date, value);
    }

    @Override
    public void setData(Level level) {
        this.level = level;
    }

    @Override
    public Level getData() {
        return level;
    }
}

Acceso al repositorio de la ontología

Para operar con la ontología disponemos de un conjunto de anotaciones que nos autogeneran el código de operación necesario. Un ejemplo de repositorio para la sencilla ontología del ejemplo anterior:

@Sofia2Repository("level")
public interface LevelRepository {

    @Sofia2Query("select * from level")
    List<LevelOntology> findAll ();

    @Sofia2Query("select * from level where Level.value=0")
    List<LevelOntology> findAllZeros ();

    @Sofia2Query("select * from level where Level.value=$value")
    List<LevelOntology> findAllWhereValueMatches (@Param("$value") Integer value);

    @Sofia2Insert
    SofiaId save(LevelOntology levelOntology);

    @Sofia2Query("db.level.remove({\"Level.value\":$value})")
    SofiaIds deleteWhereValueMatches(@Param(value = "$value") Integer value);

    @Sofia2Delete
    List<SofiaId> delete(String id);
}

Este repositorio no tiene código, es sólo un interfaz, siendo la librería de Sofia4Boot la encargada de generar y registrar en el contexto de spring la clase de implementación necesaria, con lo cual ya podremos disponer de ella de la forma habitual en spring:

@Autowired private LevelRepository levelRepository;

Añadiendo funcionalidad

Para este ejemplo, simplemente voy a hacer que cada n minutos se genere un nuevo valor aleatorio para la ontología. Para esto implementamos un servicio proveedor de valores que se encargue de hacer inserciones en la ontología:

@Service
public class Provider {

    private static final Logger log = LoggerFactory.getLogger(Provider.class);
    private Random random = new Random();

    @Autowired
    private LevelRepository levelRepository;

    public void sendRandomMessage () {

        try {
            // --- create new level value
            Instant now = Instant.now();
            Integer value = random.nextInt(10);
            LevelOntology levelOntology = new LevelOntology(now.toString(), value);
            log.info("Inserting data ontology: {}, ", levelOntology);
            // --- insert a random value
            SofiaId id = levelRepository.save(new LevelOntology(now.toString(), value));
            log.info("Created new record with value '{}' and id '{}'", value, id.getId().getOid());
        } catch (Exception e) {
            log.warn("Captured exception running app", e);
        }
    }
}

Y para que se ejecute cada intervalo genero un job con un planificador (un scheduler) que arranque cuando la aplicación haya terminado de cargarlo todo:

@Configuration
public class JobConfig implements ApplicationListener<ContextRefreshedEvent> {

    private final Logger log = LoggerFactory.getLogger(JobConfig.class);
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    @Autowired
    private Config config;
    @Autowired
    private Provider provider;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {

        log.info("Scheduling tasks ({} minutes)", config.getIntervalInMinutes());
        Runnable task = new Runnable() {
            @Override
            public void run() {
                provider.sendRandomMessage();
            }
        };

        Integer interval = config.getIntervalInMinutes();
        scheduler.scheduleAtFixedRate(task, interval, interval, TimeUnit.MINUTES);
    }
}

Para cargar la configuración estamos usando una clase Java mapeada por Spring con un fichero de configuración:

@Configuration
@ConfigurationProperties(prefix = "s4b")
public class Config {

    // el valor por defecto es 1, y la property se mapea contra 's4b.intervalInMinutes'
    private Integer intervalInMinutes = 1;

    public Integer getIntervalInMinutes() {
        return intervalInMinutes;
    }

    public void setIntervalInMinutes(Integer intervalInMinutes) {
        this.intervalInMinutes = intervalInMinutes;
    }
}

Configuración de la aplicación

A parte de la configuración habitual de Spring, existen propiedades adicionales referentes a Sofia4Boot, y además están las propias de nuestra aplicación.

La configuración específica de Sofia4Boot que nos aplica está referida a continuación:

Propiedad Valor de ejemplo Descripción
sofia2.host sofia2.com El host del servicio MQTT
sofia2.port 1884 El puerto para MQTT
sofia2.kp levelkp El kp al que se asocia la aplicación
sofia2.instance levelkp-demo El nombre de esta instancia de thinkp
sofia2.token 5118bt0acc2e464782dfwb52af582h9z El token de autorización del thinkp
sofia2.mqtt false Decide si se utiliza rest (false) o mqtt (true)
sofia2.endpoint http://sofia2.com/sib/services/ssap La URL del endpoint SSAP de Sofia2

Las propiedades propias de este ejemplo, junto con las de Spring Boot:

Propiedad Valor de ejemplo Descripción
logging.file ${spring.application.name}.log El nombre del archivo de logs a generar
server.port 0 Si fuera necesario, el puerto en el que se quiere arrancar el servicio. Esto requiere actualizar la configuración de Nginx
management.security.enabled false Esta property se asegura que se utiliza el sistema de autenticación de sofía, y no el que tiene spring boot admin por defecto
s4b.intervalInMinutes 5 inserta un valor nuevo a la ontología cada 5 minutos

Desplegando en el Contenedor de ThinKPs

Para desplegar nuestra aplicación dentro del contenedor de ThinKPs de Sofia2 deberemos haber completado los siguientes pasos:

  1. Generar una ontología en la plataforma, o seleccionar una que ya esté disponible (puede estar vacía si no se necesitan datos).
  2. Crear un KP, y generar un token, que se usarán para autorizar a nuestra aplicación (como cualquier otro dispositivo).
  3. Implementar nuestra aplicación y disponer del archivo ‘jar’ para desplegarlo.

Hecho esto, accedemos a la consola de Sofia2, menú ‘Contenedor ThinKPs’, y una vez allí haremos click en el botón con el texto ‘Nuevo ThinKP en contenedor’. Rellenamos la ventana que nos muestra la consola asegurandonos de:

  • Marcar el tipo de aplicacion como ‘Aplicación Spring Boot’
  • Seleccionar un token de autenticación (que coincida con el que incluimos en la configuración de nuestra aplicación!).
  • Cargamos nuestro archivo jar
  • Seleccionamos un nombre para nuestro contenedor, asegurandonos de incluir la extensión ‘.jar’

Un ejemplo:

Cuando termine (según nuestra conexión y el tamaño del JAR puede tardar un poco), deberíamos ver una ventana con la descripción parecida a la siguiente:

Aquí tenemos el botón que nos permite arrancar nuestra aplicación (por defecto están paradas nada más crearlas).

Spring Boot Admin

Una vez nuestra aplicación está arrancada y si está configurada correctamente, pasado un rato deberíamos poder verla registrada en el servicio de administración de Spring Boot Admin, en el menú ‘ThinKPs -> Administración de Spring Boot’, como puede verse a continuación:

Pulsando sobre el botón ‘Details’ podemos ver más detalles acerca de nuestra aplicación, como por ejemplo:

Despliegue y monitorización de Aplicaciones Spring Boot desde Control Panel de Sofia2

Un comentario en “Despliegue y monitorización de Aplicaciones Spring Boot desde Control Panel de Sofia2

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

w

Conectando a %s