Plugin IHM

01/08/2018

Introduction

Cet article décrit comment développer un plugin permettant de fournir des valeurs, issues d’un référentiel, au sein d’un formulaire d’indexation.

Prérequis :

  • Notion de Javascript, Java, Spring Boot et Maven
  • Formulaire d’indexation
  • Un tag Family modifiable de type CHOICELIST avec deux valeurs f1 et f2
  • Un tag Nature modifiable de type FREELIST

Développement du plugin

Ouvrez votre IDE préféré et commencez par créer un projet Maven.

Maven

Afin de configurer correctement notre plugin basé sur Spring Boot, modifiez le fichier pom.xml généré tel que :

<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>

	<parent>
		<groupId>com.flower.docs.samples</groupId>
		<artifactId>flower-docs-samples</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>
	<artifactId>gui-plugin-sample</artifactId>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<maven.compiler.source>1.7</maven.compiler.source>
		<maven.compiler.target>1.7</maven.compiler.target>

		<maven.build.timestamp.format>yyMMdd-HHmmss</maven.build.timestamp.format>
		<buildNumber>${maven.build.timestamp}</buildNumber>

		<flower.version>2.3.7</flower.version>
		<swagger.version>2.6.1</swagger.version>

		<spring.version>4.3.18.RELEASE</spring.version>
		<spring-security.version>4.2.1.RELEASE</spring-security.version>
		<spring.boot.version>1.5.14.RELEASE</spring.boot.version>
		<spring.boot.admin.version>1.4.6</spring.boot.admin.version>
	</properties>
	
	<build>
		<plugins>
			<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>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<version>${spring.boot.version}</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>${swagger.version}</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>${swagger.version}</version>
		</dependency>
	</dependencies>
</project>

Spring Boot Application

Ce plugin est basé sur le framework Spring Boot et packagé sous la forme d’un JAR exécutable. Pour que l’exécution du JAR expose le web service de notre plugin, il est nécessaire de définir l’application suivante.

@SpringBootApplication
@EnableSwagger2
public class SampleApplication
{
    public static void main(String[] args)
    {
        SpringApplication.run(SampleApplication.class, args);
    }

    @Bean
    public Docket api()
    {
        return new Docket(DocumentationType.SWAGGER_2).select().apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any()).build();
    }
}

Nota : L’annotation @EnableSwagger2 permet de générer un Swagger automatiquement


Ensuite pour configurer cette application Spring Boot ajouter dans le répertoire src/main/resources, le fichier application.properties suivant :

spring.application.name=gui-plugin-sample
security.basic.enabled=false
server.port=3006
server.contextPath=/sample

Web service REST

Le web service suivant permet de gérer les GET faits sur /sample/families/{famille} et ainsi de retourner des natures correspondantes.

@RestController
@RequestMapping("/families")
public class FamiliesService
{
    @RequestMapping(value = "/{family}", method = { RequestMethod.GET })
    public Map<String, String> resolveNatures(@PathVariable final String family)
    {
        switch (family)
        {
        case "f1":
            Map<String, String> natures = new HashMap<>();
            natures.put("n1", "Nature1");
            natures.put("n2", "Nature2");
            return natures;
        default:
            Map<String, String> defaultNatures = new HashMap<>();
            defaultNatures.put("unknown", "Unknown");
            return defaultNatures;
        }
    }
}

Lancement du plugin

Après avoir ouvert un terminal et être allé : dans le dossier du projet, exécuter les commandes suivantes :

mvn clean install
java -jar target/gui-plugin-sample-0.0.1-SNAPSHOT.jar

Intégration du plugin dans l’IHM

Maintenant que vous êtes en possession du plugin, nous allons l’intégrer dans un formulaire d’indexation afin que nous puissions profiter des valeurs qu’il renvoie.

Consommons le plugin

A l’aide de la librairie jQuery, nous pouvons désormais consommer le web service exposé avec le script suivant :

$.ajax({
	url: "./plugins/sample/families/"+family
}).then(function(data) {
	console.log("Natures have been resolved for family="+family);
	$.each(data, function(key, label) { 
		console.log("key="+key+", label="+label); 
	});
});

Intégration dans un formulaire

Afin d’intégrer le plugin, ouvrez la console Javascript de votre navigateur et ajoutez le script suivant :

var inputTagName = "Family"; var outputTagName = "Nature";

JSAPI.get().registerForComponentChange(function(api, component, phase) {
	if(api.hasField(inputTagName) && api.hasField(outputTagName)){
		bindOnFamilyChange(api);
	}
});

function bindOnFamilyChange(api){
	api.registerForFieldChange(inputTagName, function(fieldName, fieldValue) {
		resolveNatures(fieldValue, api);
	});
}

function resolveNatures(family, api){
	$.ajax({
		url: "./plugins/sample/families/"+family
	}).then(function(data) {
		console.log("Natures have been resolved for family="+family);
		
		var allowedValues = new Array();
		$.each(data, function(key, label) { 
			allowedValues.push(buildAllowedValue(key, label));
		});
		api.setAllowedValues(outputTagName, allowedValues);
	});
}

function buildAllowedValue(symbolicName, label) {
	var language = new Language("EN");

	var allowedValue = new AllowedValueDefinition();
	allowedValue.setSymbolicName(symbolicName);

	var displayNames = new I18NLabel()
	displayNames.setLabel(language, label);
	allowedValue.setDisplayNames(displayNames);
	return allowedValue;
}

Le plugin est maintenant intégré. Pour le tester, il vous suffit de lancer la création d’un document d’une classe référençant les deux tags Famille et Nature.

Pour aller plus loin

Un plugin peut également être utilisé pour interagir avec Flower à travers les web services exposés. Pour cela, il est nécessaire de fournir un token utilisateur à Flower.

  • Commençons par ajouter la Ajouter la dépendance aux API Flower :
<dependency>
	<groupId>com.flower.docs.core</groupId>
	<artifactId>flower-docs-ws-client</artifactId>
	<version>${flower.version}</version>
	<exclusions>
		<exclusion>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
		</exclusion>
	</exclusions>
</dependency> 
  • Ensuite nous ajoutons un filtre HTTP permettant d’initialiser le contexte utilisteur à partir du jeton fourni par Flower :
@Configuration
@ImportResource(locations = { "classpath:flower-docs-security-token.xml",
        "classpath:flower-docs-services-webservices.xml" })
@ComponentScan("com.flower.docs.security.token")
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = false)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        http.addFilterBefore(tokenFilter(), BasicAuthenticationFilter.class);
    }

    @Bean
    TokenAuthenticationFilter tokenFilter() throws Exception
    {
        TokenAuthenticationFilter filter = new TokenAuthenticationFilter();
        filter.setAuthenticationManager(authenticationManagerBean());
        return filter;
    }
}
  • Il ne nous reste plus qu’à interagir avec les services Flower. Dans l’exemple ci-dessous, nous comptons le nombre total de documents :
@RestController
public class FlowerRestController
{
    @Autowired
    private DocumentService documentService;

    @RequestMapping(value = "/count", method = RequestMethod.GET)
    public String generateFromTags() throws TechnicalException, FunctionalException
    {
        return "ok: " + documentService.search(new SearchRequest()).getFound();
    }
}