¿Cómo usar el API Spring o Client4SpringBoot?

Introducción

La plataforma ofrece varios módulos que permiten la adquisición de información, fundamentalmente el API Manager y el DigitalBroker (también conocido como IoTBroker).

El API Manager permite de forma muy sencilla y visual publicar el acceso (queries, actualizaciones, inserciones) a las ontologías como APIs REST ofreciendo interfaces OpenAPI (swagger), de modo que pueda generar un cliente en cualquier tecnología para atacar a estas APIS.

Podéis ver cómo usar el API Manager en cualquiera de estos tutoriales:

(API Manager) Creación y Ciclo de vida de las APIs / (API Manager) Invocación a APIS vía Swagger y generación de cliente con Swagger Editor / (API Manager) Invocar APIs de gestión de la plataforma con Token OAuth2

Usar el API Manager tiene varias ventajas, como tener registradas y catalogadas las interfaces (los servicios) que publica el sistema para su consumo por los clientes.

Pero hay escenarios donde la aproximación del API Manager puede no ser la más adecuada, como pueden ser:

  • Si estoy trabajando en Java con Spring Boot y no quiero estar generando wrappers de las APIS publicadas.

  • Si estoy usando la plataforma como repositorio de datos y tengo un gran número de ontologías a manejar.

  • Si necesito mayor flexibilidad y reducir tiempos de desarrollo.

  • Si soy un dispositivo y quiero usar un protocolo más eficiente como MQTT.

En estos escenarios y en muchos más puede interesar más usar el DigitalBroker como punto de entrada a mi negocio.

¿Qué es Client4SpringBoot?

Client4SpringBoot es una librería Java preparada para funcionar sobre Spring Boot y que simplifica el acceso al DigitalBroker wrapeando las peticiones con un interfaz Repository que permite invocar al DigitalBroker a través de métodos Java.

Nota Importante: Debido a que spring.boot-dev-tools hace una recarga de clases con su propio classloader, no es recomentable utilizarlo en un proyecto que utilice la libreria client4SpringBoot, ya que no se podrán inyectar con @Autowired las clases anotadas como @IoTBrokerRepository, al haber sido cargadas con el classloader de la aplicación en vez de con el classloader de las clases Bean donde se inyectan, provocando un error de este tipo:

***************************
APPLICATION FAILED TO START
***************************

Description:

Field ticketRepository in com.minsait.onesait.platform.examples.iotclient4springboot.repository.CheckTicketRepository required a bean of type 'com.minsait.onesait.platform.examples.iotclient4springboot.repository.TicketsRepository' that could not be found.


Action:

Consider defining a bean of type 'com.minsait.onesait.platform.examples.iotclient4springboot.repository.TicketsRepository' in your configuration.





Veamos ahora el aspecto que tiene la libreria:

La forma de acceder a las ontologías es a través de un interfaz en el que se definen las consultas sobre el DigitalBroker:

luego configuraré la conexión con el DigitalBroker y a través de la inyección de dependencias de Spring podré usar este Repository para acceder al DigitalBroker. 



¿Cómo se usa?

Veamos cómo se usa Client4SpringBoot con un ejemplo. En el ejemplo harás lo siguiente:

  • Crearás una Ontología de ejemplo, por ejemplo una ontología para representar una Incidencia reportada por un ciudadano en una ciudad (CitizenTicket) y una conexión con la Plataforma (Token,...)

  • Crearás un proyecto Maven y lo configuraremos con la librería para poder comunicar con la plataforma insertando y recuperando datos de la ontología previamente creada.

¡¡¡Vamos allá!!!

Accediendo al Entorno y creando la Ontología y el DigitalClient para el ejemplo

  • Comienza por conectarte al ControlPanel de un Entorno de la Plataforma. En el ejemplo utilizarás el entorno de experimentación habilitado https://www.onesaitplatform.online/controlpanel/
    Si aún no tienes usuario, puedes crearlo directamente pulsando New Account.
    Una vez dentro del ControlPanel ya puedes comenzar a trabajar. Lógate y ve a la opción de Menú My Ontologies:

    Selecciona CREATE para crear una nueva Ontología. En este caso, selecciona Crear Ontología Paso a Paso:

    aunque también podrías elegir cargar datos de un fichero.

    En el ejemplo crearás la ontología con estos datos: 

  •  Recuerda que los nombres de las ontologías son únicos, por tanto tu Ontología debería incluir algo para hacerla única como: CitizenTicket_<iduser>
    Luego, selecciona una plantilla base para la creación de la ontología. En el ejemplo seleccionaremos EmptyBase de GENERAL:
    Esta plantilla no tiene ningún atributo base, por lo que podrás crear mi ontología como consideres.
    Ve pulsando en ADD NEW PROPERTY para ir añadiendo tus propiedades:

     
    En el ejemplo, crea estos atributos:

    • mail: String required (representa el email de contacto de la persona).

    • explanation: String required (representa la descripción de la incidencia).

    • status: String required (el status de la incidencia, al registrarse será PENDING).

    • coordinates: GeoJSON tipo Point optional (coordenadas en la que se registró la incidencia).

    • fullName: String optional (nombre completo del remitente de la incidencia).

En el ControlPanel se vería así:

lo siguiente es pulsar el botón  . Esto actualiza el JSON-Schema que representa internamente la ontología y puedes editarlo dentro:

En la ventana en la que aparece, selecciona para ver cómo es el JSON-Schema, y en tu caso cambiar el elemento raíz que ahora es EmptyBase por uno más descriptivo, por ejemplo CitizenTicket:

Puedes pulsar el botón  para hacerte una idea de cómo es la estructura en JSON que debes enviar a la plataforma desde tu cliente:

Guarda este ejemplo porque luego te será útil:

{"CitizenTicket":{ "mail":"string","explanation":"string","status":"string","coordinates":{"coordinates":[28.6,28.6],"type":"Point"},"fullName":"string"}}

Acaba la creación de mi Ontología pulsando el Botón :

Ten en cuenta que hemos generado la ontología con la configuración por defecto, esto es sin autorizaciones a otros usuarios y usando MongoDB como Base de datos de almacenamiento. Esto puede configurarse en las pestañas AUTHORIZATIONS Y ADVANCED SETTINGS



2. Una vez creada la Ontología, el Sistema te permite continuar con la creación de un API para esta ontología, un Device, un gadget o un Dashboard.

En este caso, selecciona Create new Device

Esto te llevará directamente a la pantalla de creación de Clientes:

En esta pantalla tienes que dar una Identificación a tu cliente, por ejemplo ClientCitizenTicket_lmgracia, e indicar que trabaja con tu ontología CitizenTicket_lmgracia con todos los permisos:

Pulsa NEW:

Esto te llevará al listado de Clientes Digitales:

Puedes entrar a ver el detalle con el botón   . Dentro verás que la plataforma te ha generado un Token para este Cliente Digital (Si pulsas modificar, puedes crear más Tokens).

Guarda este Token para la comunicación desde el cliente: dbea2f7877ed4712824a4856a62cae88

3. Puedes también introducir algunos datos de ejemplo para las pruebas. Para eso, vuelve al menú Development>My ontologies, busca tu ontología:

y pulsa el botón . Esto te permite crear instancias de mi ontología desde un editor. Pulsa NEW:

Rellena los datos y pulsa New.

Inmediatamente te habrá creado un registro.

Si vas a la opción de Menú TOOLS>Query Tool, puedes hacer una consulta sobre tu ontología CitizenTicket y te devolverá:

Puedes ver que, asociado a la inserción, se añade un elemento contextData que incluye información de contexto como el user de la inserción y la fecha.

Creando y configurando el proyecto Spring Boot base

Una vez tengo la ontología y el DigitalClient creados podré ponerme a desarrollar el cliente con la librería Client4SpringBoot:

  1. Antes de empezar, si aún no tienes el Entorno de Desarrollo de la Plataforma te recomendamos que lo instales, puedes ver cómo hacerlo en este tutorial:/wiki/spaces/PT/pages/7897242 (concretamente en el Step 1: How to install Development Environment in Windows)
    Una vez tengo mi entorno con Java, Maven, Eclipse,.. configurado puedo seguir.

  2. Lo primero es crear una aplicación Spring Boot, por ejemplo puedes ir a la web de Sprint Initializr en https://start.spring.io y crear un proyecto, en nuestro caso con estos parámetros:

    1. Seleccionaremos Maven Project, con Java y la última versión estable de Spring Boot (en el momento la 2.0.4)

    2. Como group pondremos com.minsait.onesait.platform.examples

    3. como artifact example-crud-Client4SpringBoot

    4. En dependencies pondré Web y Actuator

    Me quedará algo como:

  3. Pulsaré Generate Project esto me generará un ZIP que dejaré en una carpeta local (si estoy usando el entorno se recomienda dejarlo en S:\sources\examples\)
    Me quedará así:

    Si uso el Entorno de Desarrollo puedo borrar el fichero mvnw.cmd y la carpeta .mvn para asegurarme que tiro del Repo de Maven configurado en el entorno.

  4. Ahora iré al descriptor de Maven pom.xml para configurar un par de cosas:

    1. Añadiré la dependencia a la librería Client4SpringBoot y a su versión concreta (en el momento actual la 1.0.0-rc14):

      <dependency>
      <groupId>com.minsait.onesait.platform</groupId>
      <artifactId>onesaitplatform-iotclient4springboot</artifactId>
      <version>1.0.0-rc14</version>
      </dependency>

    2. Configuraré los repositorios,  incluyendo el repositorio desde el que se pueden descargar las librerías de la onesait Platform:

      <repositories>
      <repository>
      <id>onesait platform releases</id>
      <url>https://nexus.onesaitplatform.com/nexus/content/repositories/releases/</url>
      </repository>
      <repository>
      <id>maven central</id>
      <url>https://repo1.maven.org</url>
      </repository>
      </repositories>

Configurando la comunicación con la plataforma

  1. Comenzaremos por abrir un IDE para desarrollar, en el Entorno de Desarrollo se incluye un Eclipse configurado que podemos lanzar desde S:\scripts\eclipse.bat


  2. Desde Eclipse importaré mi proyecto Maven desde Import>Existing Maven Projects:


En la carpeta src/main/resources se habrá generado un fichero application.properties que renombraré a application.yml

y en el que incluiré estas entradas:

  1. En las entradas debo poner la información que registré antes desde el ControlPanel:

    • onesaitplatform.iotclient.token=dbea2f7877ed4712824a4856a62cae88

    • onesaitplatform.iotclient.deviceTemplate=ClientCitizenTicket_lmgracia

    • onesaitplatform.iotclient.device=instance1




spring: ## General Config 

   application.name: Example IoTClient4SpringBoot

 

onesaitplatform:

  iotclient:

    urlRestIoTBroker: https://s4citiespro.westeurope.cloudapp.azure.com/iot-broker

    sslverify: true

    token: dbea2f7877ed4712824a4856a62cae88

    deviceTemplate: ClientCitizenTicket_lmgracia

    device: instance1

    connectTimeoutInSec: 10

    writeTimeoutInSec: 30

    readTimeoutInSec: 30

 

## LOGGING CONF

logging:

   path: ./target/

   file: ${spring.application.name}

   level:

      org.springframework: INFO

      com.minsait: INFO



Creando la clase Java que representa mi ontología

Lo siguiente es crear la clase Java que representa mi ontología, en un futuro se generará de forma automática, aunque el proceso es muy sencillo, veamos como funciona.

  1. Para tenerlo organizado todo en el paquete base ( com.minsait.onesait.platform.examples.examplecrudiotclient4springboot) crearé un paquete domain y dentro una clase CitizenTicketOntology y otra CitizenTicket

  2. Lo primero que haré es añadir la anotación @Data en las 2 clases, esta anotación de Lombok me genera automáticamente los get y set de todos los atributos y un método hashCode y toString()  https://projectlombok.org/features/Data

  3. Luego editaré la clase CitizenTicketOntology para añadirle esto:

  4. Veamos que representa cada elemento:
    - El campo _id representa el identificador interno de la Base de datos en la que se insertó el elemento, en nuestro caso MongoDB, si no lo necesitamos podemos no incluirlo en la clase, aunque nosotros lo hemos incluido.


    -El  campo contextData representa el contexto de inserción que automáticamente nos provee la plataforma, incluye usuario, fecha  y hora de inserción. También puedo no incluirlo si no voy a usarlo. La clase ContextData la proporciona la plataforma:


    -El campo citizenTicket realmente representa la instancia de la Ontología, esa que veíamos antes. Se mapea con el nombre de property CitizenTicket (el mismo que en el JSON):


     Si a la hora de crear la ontología no hubiera usado elemento raíz (en este caso es CitizenTicket) entonces no sería necesario crear la clase contenedora. Se recomienda crear un elemento raíz siempre que dote de semántica a la instancia (viendo esta instancia sé automáticamente su tipo).

  5. Lo siguiente será crear los atributos que componen mi ontología, recordemos cuales son:

    • mail: String required (representa el email de contacto de la persona)

    • explanation: String required (representa la descripción de la incidencia)

    • status: String required (el status de la incidencia, al registrarse será PENDING)

    • coordinates: GeoJSON Tipo Point optional (coordenadas en la que se registró la incidencia)

    • fullName: String optional (nombre completo del remitente de la incidencia)

    que en la clase quedarán así:


    Fijaros que en los atributos opcionales pongo un JsonIgnore para que si están a nulo no se envíen. Además las coordenadas mapean con el objeto GeometryPoint (Punto GeoJson).

  6. Para acabar volveré a la clase CitizenTicketOntology y añadiré una nueva anotación @JsonInclude(Include.NON_NULL) que sirve para que los atributos sólo se devuelvan si no son nulos (si el ContextData es nulo no me lo devolverá):

Creando el Repository básico para conectar con la Plataforma

Una vez configurada la conexión con la plataforma en el fichero application,yml y la clase que representa la ontología (CitizenTicket) puedo crear un Repository (patrón Repository)

Veamos cómo:

  1. En el paquete .repository creo un interfaz CitizenTickerRepository al que anotaré con la anotación @IoTBrokerRepository("CitizenTicket_lmgracia") indicando la ontología (principal) sobre la que trabaja el Repository:


     recuerda sustituir el nombre de la ontología por la tuya

  2. Luego iré añadiendo los métodos que maneja mi Repository, veamos los básicos (más adelante veremos algunos más avanzados)



    1. La anotación @IoTBrokerQuery se usa para consultas, como para recuperar todos los tickets con getAllTickets como para contarlos,...además se pueden pasar parámetros como podemos ver en el método getTicketsByUser.
      A partir de la release 1.0.0-rc19, la query se puede pasar de forma dinámica, como argumento e incluir parámetros, que sustitirán en la query introducida el valor indicado en la anotación @IotBrokerParam



      La anotación @IoTBrokerInsert se puede usar para insertar una o una lista de CitizenTicket. Como resultado devuelve un String con su ID.

    2. La anoación @IotBrokerUpdate permite actualizar un Ticket pasando de parámetros su id (el de BaseDatos) y la instancia

    3. La anotación @IoTBrokerDelete permite borrar un Ticket por su ID.

    4. A partir de la release 1.1.0-rc01, la anotación @IoTBrokerDynamicRepository permite a nivel de parámetro en un método, hacer consultas sobre un repositorio (ontología) distinto al indicado a nivel de clase con la anotación @IoTBrokerRepository. De este modo se permite parametrizar la ontologia sobre la que actua el método, permitiendo incluso crear Repositorys donde no se indique una ontologia a nivel de anotación @IoTBrokerRepository.

Comunicando con la Plataforma

Ya tenemos todo preparado para comunicar con la plataforma a través de la clase que haga la comunicación:

  1. Crearé una clase CitizenTicketController en el paquete .control y la anotaré con la anotación @Component para que sea un Bean de Spring y con la anotación @Slf4j que instancia un log de SL4J (configurado con logback en plataforma):

  2. Añadiré la inyección a nuestro Repository:

  3. Para probar la comunicación haré un método check que anotaré con @PostConstruct (de modo que se invoque automáticamente una vez cargado el contexto de Spring Boot:

  4. Sólo me queda rellenar el método, hagamos algo sencillo:


    (pon un punto de interrupción 

  5. Para probarlo sólo me falta añadir un par de anotaciones a la clase Application de mi aplicación SpringBoot, voy a ella y le añado las anotaciones @EnableAutoConfiguration que es necesaria para que funcione la librería y @ComponentScan  con el paquete base para que sea capaz de de encontrar los Beans definidos:



    @EnableAutoConfiguration
    @ComponentScan("com.minsait.onesait.platform.examples.examplecrudiotclient4springboot")

  6. Desde Eclipse puedo lanzar ya mi proyecto Eclipse simplemente poniéndome sobre la clase Application y:


    Si he puesto en depuración todo podré ver cómo se detiene:




  7. Y probemos que funciona:


           

  8. Recomendamos que sigas completo este manual, pero también puedes descargarte el ejemplo desde aquí y probarlo:example-crud-IoTClient4SpringBoot.zip



Operaciones con gestión de Id de elementos:

Operaciones como Inserts, Bulk, Update y Delete pueden necesitar recibir el Id en BDTR de los elementos afectados (insertados, actualizados, Borrados).

En operaciones UPDATE y DELETE se desaconseja utilizar este modo y optar por no recuperar los Ids de los elementos afectados, salvo que sea estrictamente necesario, ya que desaprovecha capacidades nativas de la BDTR para actualizar y borrar en bloque incurriendo en un menor rendimiento.

Esto es posible del siguente modo:

  • Inserts: Devuelven el Id como un String en la operación en el Repository

  • BULK: Devuelven un objeto de tipo BulkResult, que permite obtener el número de elementos Insertados y la lista de Ids en BDTR:

  Donde BulkResult se define mediante la siguiente clase:

  • UPDATE: Se realiza mediante la operación de tipo Query, indicando que la respuesta es un objeto de tipo UpdateResult:

Donde UpdateResult se define mediante la siguiente clase:



  • DELETE: Se realiza mediante la operación de tipo Query, indicando que la respuesta es un objeto de tipo DeleteResult:

Donde DeleteResult se define mediante la siguiente clase:





Soporte para operaciones de tipo count:

Se permite realizar operaciones de tipo count tanto con criterios de query como sin criterios:



Soporte para Update y Delete SQL

A partir de la version 1.3.0 y compatible a partir de la versión 1.3.0-Barbarian de plataforma, se ha añadido soporte para operaciones SQL de tipo UPDATE y DELETE



Soporte para tipos Date y Timestamp

A partir de la versión 1.3.2, se han añadido a la libreria serialziadores para trabajar en las clases del modelo de objetos (Repositorios) con objetos de tipo Date:

A la hora de trabajar con fechas hay dos alternativas a la hora de declarar una ontologia:



  • Campos timestamp-mongo (las que llevan el $date): Se soportan en las clases del modelo de objetos, tanto en serialización como en deserialización con la clase com.minsait.onesait.platform.client.springboot.fromjson.Timestamp de la librería iotclient4boot:

  • Campos de tipo timestamp (String con el formato date-format): Se soportan en las clases del modelo, tanto en serialización como en deserialización con tipos java.util.Date, que hay que anotar con los siguientes serializadores proporcionados por la libreria iotclient4boot:

@JsonDeserialize(using = DateDeserializer.class)

@JsonSerialize(using = DateSerializer.class)

Soporte para tipos Geometry

La libreria iotclient4boot proporciona clases para trabajar con tipos Geometry definidos en la ontologia. En concreto proporciona una clase abstracta Geometry, de la que heredan GeometryPoint (geometrias de tipo Punto), GeometryLinestring (geometrias de tipo línea), GeometryMiltiLinestring (geometrias de tipo multilinea), GeometryPoligon (geometrias de tipo poligono). Cuando en una ontologia se definine un campo de tipo geometry, la correspondiente clase Java del modelo se tiene que definir como tipo Geometry y contener el tipo concreto (Punto, linea...).