Développer un microservice qui interagit avec Flower

02/04/2017

Introduction

Cet article vise à décrire la mise en place d’un microservice basique effectuant une recherche de document auprès de Flower.

Thèmes abordés :

  • développment d’un service Rest avec Spring Boot et Maven
  • utilisation de l’API Web Services de Flower

Prérequis :

  • Maven
  • Utilisation d’un Artifactory Arondor (public ou interne)

Création du projet Maven

Cette partie fournit la configuration d’un projet Maven en incluant les dépendances nécessaires :

  • Spring Boot
  • Les APIs Flower
  • Jackson pour la désérialisation JSON
  • Slf4j pour la gestion des logs

Créer le projet Maven avec le fichier de configuration pom.xml suivant :

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.flower.docs.samples</groupId>
	<artifactId>basic-operation-hook-sample</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<spring.version>4.3.6.RELEASE</spring.version>
		<spring-security.version>4.2.1.RELEASE</spring-security.version>
		<spring.boot.version>1.5.1.RELEASE</spring.boot.version>
		<flower.version>2.3.3</flower.version>
	</properties>

	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.5.1</version>
				<configuration>
					<source>1.7</source>
					<target>1.7</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<version>${spring.boot.version}</version>
				<executions>
					<execution>
						<goals>
							<goal>repackage</goal>
							 <goal>build-info</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<executable>true</executable>
				</configuration>
			</plugin>
		</plugins>
	</build>

	<dependencies>
		<dependency>
			<groupId>com.flower.docs.core</groupId>
			<artifactId>flower-docs-ws-client</artifactId>
			<version>${flower.version}</version>
		</dependency>
		<dependency>
			<groupId>com.flower.docs.apis</groupId>
			<artifactId>flower-docs-operation-api</artifactId>
			<version>${flower.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<version>${spring.boot.version}</version>
			<exclusions>
				<exclusion>
					<groupId>org.slf4j</groupId>
					<artifactId>log4j-over-slf4j</artifactId>
				</exclusion>
				<exclusion>
					<groupId>ch.qos.logback</groupId>
					<artifactId>logback-classic</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
			<version>${spring.boot.version}</version>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.21</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.21</version>
		</dependency>
	</dependencies>
</project>

Création d’une application Spring Boot

Définition de l’application Spring Boot

Définir une application SpringBoot comme celle-ci dessous :

package com.flower.samples;

@SpringBootApplication
@ComponentScan(basePackages = { "com.flower.samples", "com.flower.docs.security.authentication" })
@ImportResource({ "classpath:flower-docs-services-webservices.xml", "classpath:flower-docs-security-token.xml" })
public class SampleApplication
{
    public static void main(String[] args)
    {
        SpringApplication.run(SampleApplication.class, args);
    }
}

Configuration minimale

Dans le répertoire src/main/resources du projet, ajouter les 2 fichiers suivant :

  • application.properties, la configuration des variables de l’application SpringBoot
  • log4j.xml, la configuration des logs

Configuration Spring Boot : application.properties

spring.application.name=flower-sample
security.basic.enabled=false
server.port=7777
server.contextPath=/sample
flower.password=okidoki

Configuration des logs : log4j.xml

<?xml version="1.0" encoding="UTF-8" ?>
	<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="false" xmlns:log4j='http://jakarta.apache.org/log4j/'>

	<appender name="consoleAppender" class="org.apache.log4j.ConsoleAppender">
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%d{ISO8601} [%t] %-5p %c - %m%n" />
		</layout>
	</appender>

	<appender name="operation-appender" class="org.apache.log4j.RollingFileAppender">
		<param name="append" value="true" />
		<param name="file" value="flower-sample.log" />
		<param name="maxFileSize" value="10MB" />
		<param name="maxBackupIndex" value="3" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%d{ISO8601} [%t] %-5p %c - %m%n" />
		</layout>
	</appender>

	<category name="com.flower">
		<priority value="INFO" />
		<appender-ref ref="operation-appender" />
	</category>

	<root>
		<level value="WARN" />
		<appender-ref ref="consoleAppender" />
	</root>
</log4j:configuration>

Ajout d’un service REST: PingService

Afin de tester l’application, nous vous proposons d’ajouter un service REST pour tester notre application Spring Boot.

package com.flower.samples;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PingService
{
    private static final Logger LOGGER = LoggerFactory.getLogger(PingService.class);

    @PostConstruct
    public void initialize() throws Exception
    {
        LOGGER.info("{} REST service has been initialized", this.getClass().getName());
    }

    @RequestMapping(value = "/")
    public String ping()
    {
        LOGGER.info("Received ping");
        return "Up!";
    }
}

Vous pouvez dès à présent passer à la partie Compilation & Lancement pour tester ce nouveau service.

Rechercher un document : SampleSearchService

Essayons maintenant de développer un service REST nous permettant de rechercher un document au sein de Flower.

Authentification

La classe com.flower.docs.security.authentication.Authenticator fournit un moyen facile d’initialiser l’authentification qui sera utilisée pour faire les appels aux web services Flower.

Grâce aux paramêtres flower.user et flower.password, il est possible de configurer ,grâce aux propriétés de la JVM, les informations d’authentification.

Utilisation du service de document

L’objet com.flower.docs.service.api.document.DocumentService nous permet d’obtenir une référence au service permet la gestion des documents au sein de Flower. Il nous permet notamment de réaliser des recherches de documents.

Pour exécuter la recherche, un object com.flower.docs.domain.search.SearchRequest est instancié à l’aide des builders :

  • com.flower.docs.common.search.SearchRequestBuilder
  • com.flower.docs.common.search.FilterClauseBuilder
  • com.flower.docs.common.search.CriterionBuilder

Mise en place du service REST

package com.flower.samples;

import static com.flower.docs.domain.FlowerFields.NAME;
import static com.flower.docs.domain.search.Operators.CONTAINS;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.flower.docs.common.search.CriterionBuilder;
import com.flower.docs.common.search.FilterClauseBuilder;
import com.flower.docs.common.search.SearchRequestBuilder;
import com.flower.docs.domain.exception.FunctionalException;
import com.flower.docs.domain.exception.TechnicalException;
import com.flower.docs.domain.search.Criterion;
import com.flower.docs.domain.search.SearchResponse;
import com.flower.docs.domain.search.SearchResult;
import com.flower.docs.security.authentication.Authenticator;
import com.flower.docs.service.api.document.DocumentService;

@RestController
public class SampleSearchService
{
    @Autowired
    private DocumentService documentService;

    @Autowired
    private Authenticator authenticator;

    @RequestMapping(value = "/{scope}/search/{name}", method = { RequestMethod.GET })
    public List<SearchResult> searchByName(@PathVariable final String scope, @PathVariable final String name)
            throws FunctionalException, TechnicalException
    {
        authenticator.authenticate(scope);

        SearchRequestBuilder requestBuilder = SearchRequestBuilder.init().max(10);
        Criterion criterion = CriterionBuilder.field(NAME).operator(CONTAINS).value(name).build();
        requestBuilder.filter(FilterClauseBuilder.init().criterion(criterion).build());

        SearchResponse response = documentService.search(requestBuilder.build());

        return response.getResults();
    }
}

Compilation & Lancement

Compilation Maven

  • Aller à la racine du projet, puis exécuter la commande Maven suivante :

    mvn clean install
    

Exécution du JAR

  • Pour démarrer le service, exécuter la commande suivante :

    java -jar target/basic-operation-hook-sample-0.0.1-SNAPSHOT.jar
    
  • Pour configurer ce microservice, il est possible de passer des paramètres grâce aux propriétés de la JVM, par exemple :

    java -flower.user=*** -Dflower.password=*** -Dws.url=http://localhost:8081/flower-docs-ws/services \
      -jar target/basic-operation-hook-sample-0.0.1-SNAPSHOT.jar 
    
  • Le JAR peut également être directement exécuté tel que

    ./basic-operation-hook-sample-0.0.1-SNAPSHOT.jar 
    

Tests

Ping : si l’application démarre sans erreur, vous pouvez la tester en faisant un GET sur http://localhost:7777/sample/ et vérifier que le retour est bien Up! avec un code de retour 200

Service de recherche : pour tester le service de recherche, vous pouvez :

Autres endpoints :

  • GET sur /health retourne le statut du service
  • GET sur /info retourne les informations de build du microservice

Problèmes possibles

Connexion à Flower

Le service de ping fonctionne (GET sur /sample/) mais un GET sur sample/<scope>/search/a retourne :

{
  "timestamp": 1493210821798,
  "status": 500,
  "error": "Internal Server Error",
  "exception": "javax.xml.ws.soap.SOAPFaultException",
  "message": "Could not send Message.",
  "path": "/sample/Syldavia/search/a"
}
  • Vérifier, dans vos logs (ou la console), si vous avez des exceptions type java.net.ConnectException
  • Si c’est le cas, vérifier l’URL des web services Flower utilisée
    • Vous pouvez la modifier avec le paramètre ws.url
    • La valeur de cette propriété doit permettre d’accéder à la servlet exposant l’ensemble des web services SOAP du instance Flower.

Authentification Flower

Le service de ping fonctionne (GET sur /sample/) mais un GET sur sample/<scope>/search/a retourne :

{
  "timestamp": 1493211603336,
  "status": 500,
  "error": "Internal Server Error",
  "exception": "javax.xml.ws.soap.SOAPFaultException",
  "message": "F00324: Les identifiants de l'utilisateur system ne sont pas valides",
  "path": "/sample/Syldavia/search/a"
}

Vérifier la configuration du compte utilisateur utilisé dans cet exemple (cf. partie Authentification)