Skip to content

Technical documentation

Bastien Fiorito edited this page Dec 17, 2015 · 8 revisions

Technical documentation is available here

You will find here

  • how to extend SITools2 REST API with applications, or dataset services, developping in java
  • how to integrate your own GUI components, to view your data differently
  • how to customize UI for portal, projects and others visual components
Table des matières

1 - Développement des extensions du serveur
_ 1.1 - Développement d'un filtre en entrée
_ 1.2 - Développement d’un convertisseur en sortie
_ 1.3 - Développement d’une application plugin
_ 1.4 - Développement d’une ressource plugin
____ 1.4.1 - Introduction
____ 1.4.2 - Création du modèle
____ 1.4.3 - Notions communes aux « resource plugin »
____ 1.4.4 - L’implémentation d’une ressource synchrone
____ 1.4.5 - Implémentation d’une « ressource plugin » avec gestion des tâches
____ 1.4.6 - Création d’une ressource de commande
____ 1.4.7 - Validation de la configuration
____ 1.4.8 - Ajout des ressources dans SITools2
_ 1.5 - Développement de filtres de sécurité

## Développement des extensions du serveur

SITools2 possède un ensemble d’API permettant de construire différents plugins. Ces plugins permettent d’ajouter de nouvelles fonctionnalités ou bien de modifier certains comportements du serveur :

  • Le plugin « Filter » permet d’ajouter un filtre de requête qui va lire une signature dans une URL et la convertir en une partie de la requête SQL interrogeant le jeu de données,

  • Le plugin « Converter » permet d’appliquer une fonction de transfert sur des données requêtées et de fournir une réponse qui sera affichée dans la réponse du serveur,

  • Le plugin « Application » permet d’ajouter un nouveau comportement au serveur qui peut être indépendant d’un jeu de données ou d’un projet,

  • Le plugin « Resource » est une ressource synchrone ou asynchrone liée à un jeu de données. Elle permet de récupérer les données requêtées et d’effectuer diverses opérations ainsi que de retourner une réponse qui sera affichée par le serveur,

  • Le plugin « Security Filter » permet d’ajouter son propre mécanisme d’autorisation pour l’accès à la fonction « stockage » de l’OAIS.

  • Le plugin « Units » permet d’ajouter ses propres mécanismes de conversion d’unités. Ces mécanismes de conversions d’unités peuvent être ensuite utilisés dans les formulaires de requête de SITools2.


### Développement d’un filtre de requête en entrée

Les filtres de requête permettent d’ajouter des contraintes lors d’une requête sur un jeu de données. Chaque filtre de requête peut ajouter des prédicats à la requête SQL qui va ensuite être exécuté par la base de données. Un filtre dispose d’un ensemble de paramètres en entrée, de paramètres internes (constantes) et de paramètres en sortie.

Introduction

La création d’un filtre se résume à l’implémentation d’une classe Java retournant un ensemble de prédicats. Cette classe doit hériter de la classe AbstractFilter et implémenter la méthode createPredicats(Request request, ArrayList<Predicat> predicats) ainsi que getValidator(). Le squelette d’une telle classe est le suivant :

public class MyFilter extends AbstractFilter {
	public BasicFilter() {
		// méta données du filtre
		// déclaration des paramètres de configuration
	}
	@ Override
	public final ArrayList < Predicat > createPredicats(Request request, ArrayList < Predicat > predicats)throws Exception {
		// récupération des paramètres de requêtes et de configuration
		// création des prédicats
		// retour de la liste des prédicats
	}
	@ Override
	public Validator < AbstractFilter > getValidator() {
		return new Validator < AbstractFilter > () {
			 @ Override
			public Set < ConstraintViolation > validate(AbstractFilter item) {
				// validation des paramètres de configuration
			}
		}
	}
}

####Le constructeur

  • Métadonnées du filtre

Le Constructeur possède un ensemble de métadonnées relatif au filtre, à savoir :

Métadonnées Description Méthodes à employer
L’auteur du filtre L’auteur du filtre apparaît dans l’interface d’administration setClassAuthor
Le propriétaire du filtre Le propriétaire du filtre apparaît dans l’interface d’administration setClassOwner
Le nom du filtre Le nom du filtre apparaît dans l’interface d’administration setName
La description du filtre La description du filtre apparaît dans l’interface d’administration setDescription
La version du filtre La version du filtre apparaît dans l’interface d’administration. Une vérification entre la version du filtre configuré et la version du filtre provenant du JAR est effectuée. Un warning est affichée si les versions diffèrent. setClassVersion
  • Paramétrage de configuration du filtre

Lors du développement d’un filtre, le développeur peut créer des paramètres de configuration qui seront saisis par l’administrateur du système. Pour cela il suffit d’instancier des objets FilterParameter(String name, String description, FilterParameterType type) et de les ajouter au filtre par l’utilisation de la méthode addParam(final FilterParameter param).

Lors de l’instanciation d’un objet FilterParameter, les paramètres suivants doivent être spécifiés :

Paramètre Description
name Nom du paramètre qui sera affiché dans l’interface d’administration pour la configuration du filtre
description Description du paramètre qui sera affiché dans l’interface d’administration
filterParameterType Type du paramètre entrainant un comportement différent dans l’IHM. Le type peut être soit INTERN ou IN. Un type INTERN est une constante que l’administrateur devra saisir dans l’IHM d’administration. Un type IN est un attribut du jeu de données que l’utilisateur devra choisir parmi la liste exposée dans l’IHM d’administration.
  • Notion de filtres par défaut

Les filtres sont actuellement le seul moyen d’enrichir l’API de recherche sur un jeu de données, et donc, de générer des prédicats à partir de paramètres d’une requête.

Les composants de formulaires, le filtrage sur les données dans le composant graphique tabulaire (« liveGrid ») utilisent des filtres pour générer des prédicats.

Afin de ne pas alourdir la gestion de l’administrateur, on a ajouté à un filtre la notion « par défaut ». Lors de la création d’un jeu de données, tous les filtres par défaut seront attachés automatiquement au nouveau jeu de données via un mécanisme de Trigger.

Un Filtre peut avoir deux constructeurs. Un par défaut, sans paramètre et obligatoire. Un autre avec un objet de type Context en paramètre. Dans cet objet Context, on peut retrouver le jeu de données en utilisant l’attribut « DATASET ». Ce constructeur permet d’avoir une liste de paramètres qui dépend du jeu de données et rend le filtre encore plus souple.

La méthode createPredicats

Cette méthode est exécutée lors de l’exécution du filtre.

Il faut tout d’abord récupérer l’ensemble des paramètres du filtre. Pour ce faire il faut utiliser les méthodes getParameterMap(), getInternParam(final String name).

Une fois les paramètres récupérés, il suffit de créer les prédicats voulus en fonction des attributs du jeu de données paramétrés et des constantes.

Dans cette méthode, on peut également récupérer certaines variables depuis le contexte. Ces variables sont SitoolsSettings (l’ensemble des propriétés du fichier sitools.properties) et la DataSetApplication.

Pour ce faire, il suffit de les récupérer dans les paramètres du contexte avec les clés : « DataSetApplication» et « ContextAttributes.SETTINGS » :

datasetApp = (DataSetApplication) getContext().getAttributes().get("DataSetApplication") ; 
sitoolsSettings = (SitoolsSettings) getContext().getAttributes().get(ContextAttributes.SETTINGS ) ;

Depuis la DataSetApplication, il est possible d’obtenir une connexion à la base de données :

// recuperation complete du dataset

DataSet ds = datasetApp.getDataSet();  
// recuperation d’une connexion à la source de données                                                                      
 Connection con = SitoolsDataSourceFactory.getInstance()
 .getDataSource(ds.getDatasource().getName()).getConnection();

Où datasetApp est la DataSetApplication récupéré depuis le contexte.

  • Prévention de l’injection SQL

Chaque filtre doit se prémunir contre l’injection SQL. Si les valeurs attendues sont numériques, il faut « caster » les valeurs attendues en double. Si les valeurs attendues sont des chaines alphanumériques, utiliser la méthode suivante :

String value = SQLUtils.quote(parameters\[VALUES\]);

Chaque développeur peut donc dans l’implémentation de son filtre définir le caractère par défaut de celui ci.

Utilisation de la conversion d’unité

Les classes qui héritent de la classe abstraite AbstractFilter ont à leur disposition la méthode :

Double convert(String unitFromName, String unitToName, String valueFrom, String dimension);

qui renvoie la conversion d’une valeur (valueFrom) donnée dans une certaine unité (unitFromName) vers une autre unité (unitToName) en utilisant une dimension physique Sitools2 dont le nom est indiqué (dimension). Cette méthode peut donc être utilisée dans la méthode createPredicats(Request request, ArrayList<Predicat> predicats) afin de créer une requête cohérente en fonction de l’unité associée à un attribut d’un jeu de données. Cette méthode renvoie une exception ResourceException lorsque l’unité ou la dimension physique envoyée n’est pas reconnue. Un exemple de l’utilisation de cette méthode est visible dans la méthode checkValues du NumericBetweenFilter.

Validation de la configuration

Les paramètres que l’administrateur a saisis peuvent être vérifiés par ce mécanisme de validation. Le squelette de la méthode est la suivante :

/**
 * Gets the validator for this Filter
 *
 * @return the validator for the filter
 */
@ Override
public Validator < AbstractFilter > getValidator() {
	return new Validator < AbstractFilter > () {
		 @ Override
		public Set < ConstraintViolation > validate(AbstractFilter item) {
			// TODO Auto-generated method stub
			return null;
		}
	};
}

Deux états sont disponibles quand un problème de validation est détecté :

  • WARNING : un warning apparaît lors de la configuration du filtre par l’administrateur,

  • CRITICAL : un erreur critique apparaît lors de la configuration du filtre par l’administration. Dans ce cas, la configuration du filtre n’est pas sauvegardée

Description de l’API

Il est important que l’API du filtre soit décrite pour permettre à des clients, autre que l’IHM de SITools2, de comprendre comment utiliser les filtres. Voici un exemple montrant comme décrire les paramètres du filtre pour un WADL. Ce code est à utiliser dans le constructeur :

HashMap < String, ParameterInfo > rpd = new HashMap < String, ParameterInfo > ();
ParameterInfo paramInfo;
paramInfo = new ParameterInfo("p[#]", false, "xs:string", ParameterStyle.QUERY, "CONE_SEARCH_CARTESIEN|columnAlias1,columnAlias2,columnAlias3|RA_value|DEC_value|SR_Value");
rpd.put("0", paramInfo);
paramInfo = new ParameterInfo("c[#]", false, "xs:string", ParameterStyle.QUERY, "CONE_SEARCH_CARTESIEN|dictionaryName,conceptName1,conceptName2,conceptName3|RA_value|DEC_value|SR_Value");
rpd.put("1", paramInfo);
this.setRequestParamsDescription(rpd);
NOTE : Il est aussi possible de décrire l’API en surchargeant la méthode *getRequestParamsDescription()*.

Ajout d’un Filtre dans Sitools2

  • Méthode avec Eclipse

Si vous utilisez Eclipse, et que vous avez récupérer l’ensemble des sources, l’ajout d’un convertisseur est très simple.

En effet, il suffit d’ajouter cette classe dans le projet extensions et d’ajouter le nom complet de la classe dans le fichier fr.cnes.sitools.converter.FilterHelper.

Il faut ensuite exécuter la tache Ant présente dans le projet extensions afin d’inclure la nouvelle classe dans la liste des extensions. Il faut également redémarrer Sitools2 pour prendre en compte le nouveau Jar.

  • Méthode sans Eclipse

Dans le cas ou vous n’utilisez pas Eclipse, et que le projet extensions n’existe pas, il faut créer la bonne arborescence « à la main ».

Cette arborescence est tout de même assez simple. Il suffit d’avoir à la racine les dossiers contenant les sources ainsi qu’un dossier META-INF/services qui contient un fichier nommé fr.cnes.sitools.converter.FilterHelper dans lequel on place le nom des classes.

Il faut ensuite

  • Compiler les sources en ayant le jar de Sitools2 et de Restlet dans le classpath.

  • Créer un jar en incluant les classes compilées et le dossier META-INF/services.

  • Ajouter ce jar au classpath de Sitools2

####Gestion des logs

Il existe plusieurs méthodes pour récupérer un Logger dans Sitools2.

Via la classe Application :

Application application = getApplication();                                              
Logger logger = application.getLogger();

Directement via la classe Logger :

Logger logger = Logger.getLoggernom de la classe courante »);

Via le Context :

Logger logger = Context.getLogger();

Ensuite pour utiliser le Logger :

logger.log(Level.INFO, "Message to log");

***
<a name="dev_convertisseur_sortie"/>
### Développement dun convertisseur en sortie


Les convertisseurs peuvent être utilisés pour effectuer une conversion après lexécution de la requête par la base de données. Il dispose dun ensemble de paramètres en entrée, de paramètres internes (constantes) et de paramètres en sortie. Les attributs du jeu de données qui possèdent des convertisseurs doivent être sélectionnés comme non filtrable et non ordonnable puisque le convertisseur ne propose pas la conversion inverse.

#### Introduction

La création dun convertisseur en sortie se résume à limplémentation dune classe Java effectuant un traitement sur les données en sortie.

Cette classe doit hériter de la classe *AbstractConverter* et implémenter la méthode *getConversionOf*

Le squelette dune telle classe est le suivant :

```java
public class MyConverter extends AbstractConverter {
	public LinearConverter() {
		//initialisation des détails du convertisseur
		//déclaration des paramètres
	}
	 @ Override
	public final Record getConversionOf(final Record rec)throws Exception {
		//récupération des paramètres
		//exécution du convertisseur
		return rec.
	}
	 @ Override
	public Validator < AbstractConverter > getValidator() {
		return new Validator < AbstractConverter > () {
			 @ Override
			public Set < ConstraintViolation > validate(AbstractConverter item) {
				// validation des paramètres
			}
		}
	}
}

Constructeur

  • Métadonnées du convertisseur

Le Constructeur possède un ensemble de métadonnées relatif au convertisseur, à savoir :

Métadonnées Description Méthodes à employer
L’auteur du convertisseur L’auteur du convertisseur apparaît dans l’interface d’administration setClassAuthor
Le propriétaire du convertisseur Le propriétaire du convertisseur apparaît dans l’interface d’administration setClassOwner
Le nom du convertisseur Le nom du convertisseur apparaît dans l’interface d’administration setName
La description du convertisseur La description du convertisseur apparaît dans l’interface d’administration setDescription
La version du convertisseur La version du convertisseur apparaît dans l’interface d’administration. Une vérification entre la version du filtre configuré et la version du filtre provenant du JAR est effectuée. Un warning est affichée si les versions diffèrent. setClassVersion
  • Paramétrage de configuration du convertisseur

Lors du développement d’un convertisseur, le développeur peut créer des paramètres de configuration qui seront saisis par l’administrateur du système. Pour cela il suffit d’instancier des objets ConverterParameter(String name, String description, ConverterParameterType type) et de les ajouter au convertisseur par l’utilisation de la méthode addParam(final ConverterParameter param).

Lors de l’instanciation d’un objet ConverterParameter, les paramètres suivants doivent être spécifiés :

Paramètre Description
name Nom du paramètre qui sera affiché dans l’interface d’administration pour la configuration du filtre
description Description du paramètre qui sera affiché dans l’interface d’administration
converterParameterType Type du paramètre entrainant un comportement différent dans l’IHM. Le type peut être soit INTERN, IN, OUT ou IN_OUT. Un type INTERN est une constante que l’administrateur devra saisir dans l’IHM d’administration. Un type IN est un attribut du jeu de données que l’utilisateur devra choisir parmi la liste exposée dans l’IHM d’administration. Un type OUT est un attribut du jeu de données (l’attribut pouvant être réel ou virtuel) dans lequel la réponse sera écrite. Un type IN_OUT est une fonction de transfert qui sera appliquée sur un attribut du jeu de données.

Un Convertisseur peut avoir deux constructeurs :

  • Un par défaut, sans paramètre et obligatoire

  • Un autre avec un objet de type Context en paramètre. Dans cet objet Context, on peut retrouver le jeu de données en utilisant l’attribut « DATASET » dans l’objet Context. Ce constructeur permet d’avoir une liste de paramètres qui dépend du jeu de données et rend le filtre encore plus souple.

La méthode getConvertionOf

Cette méthode est exécutée lors de l’exécution du convertisseur.

Il faut tout d’abord récupérer l’ensemble des paramètres du convertisseur. Pour ce faire il faut utiliser les méthodes getInParam, getInternParam, getInOutParam et getOutParam de la classe AbstractConverter.

Une fois les paramètres récupérés il suffit d’effectuer les traitements voulus sur les données

Dans cette méthode, on peut également récupérer certaines variables depuis le contexte. Ces variables sont SitoolsSettings, DataSetApplication ainsi que la requête HTTP effectuée précédemment.

Pour ce faire, il suffit de les récupérer dans les paramètres du contexte avec les clés : « DataSetApplication» et « ContextAttributes.SETTINGS » et « REQUEST ».

Depuis la DataSetApplication, il est possible d’obtenir une connexion à la base de données :

// recuperation complete du dataset
DataSet ds = datasetApp.getDataSet();
// recuperation d’une connexion à la source de données
Connection con = SitoolsDataSourceFactory.getInstance()
	.getDataSource(ds.getDatasource().getName()).getConnection();

Où datasetApp est la DataSetApplication récupéré depuis le contexte.

Dans le cas où une valeur nulle est retournée par le convertisseur, aucune conversion n’est réalisée.

Validation de la configuration

Les paramètres que l’administrateur a saisis peuvent être vérifiés par ce mécanisme de validation. Le squelette de la méthode est la suivante :

/**
 * Gets the validator for this Converter
 *
 * @return the validator for the converter
 */
 @ Override
public Validator < AbstractConverter > getValidator() {
	return new Validator < AbstractConverter > () {
		 @ Override
		public Set < ConstraintViolation > validate(AbstractConverter item) {
			// TODO Auto-generated method stub
			return null;
		}
	};
}

Deux états sont disponibles quand un problème de validation est détecté :

  • WARNING : un warning apparaît lors de la configuration du filtre par l’administrateur,

  • CRITICAL : une erreur critique apparaît lors de la configuration du filtre par l’administration. Dans ce cas, la configuration du filtre n’est pas sauvegardée

Ajout d’un convertisseur dans Sitools2

  • Méthode avec Eclipse

Si vous utilisez Eclipse, et que vous avez récupéré l’ensemble des sources, l’ajout d’un convertisseur est très simple.

En effet, il suffit d’ajouter cette classe dans le projet extensions et d’ajouter le nom complet de la classe dans le fichier fr.cnes.sitools.converter.ConverterHelper.

Il faut ensuite exécuter la tache Ant présente dans le projet extensions afin d’inclure la nouvelle classe dans la liste des extensions. Il faut également redémarrer Sitools2 pour prendre en compte le nouveau Jar.

  • Méthode sans Eclipse

Dans le cas où vous n’utilisez pas Eclipse, et que le projet extensions n’existe pas, il faut créer la bonne arborescence « à la main ».

Cette arborescence est tout de même assez simple. Il suffit d’avoir à la racine les dossiers contenant les sources ainsi qu’un dossier META-INF/services qui contient un fichier nommé fr.cnes.sitools.converter.ConverterHelper dans lequel on place le nom des classes.

L’arborescence ressemble donc à ceci :

Il faut ensuite

  • Compiler les sources en ayant le jar de Sitools2 et de Restlet dans le classpath.

  • Créer un jar en incluant les classes compilées et le dossier META-INF/services.

  • Ajouter ce jar au classpath de Sitools2

Gestion des logs

Il existe plusieurs méthodes pour récupérer un Logger dans Sitools2.

Via la classe Application :

Application application = getApplication();                                              
Logger logger = application.getLogger();

Directement via la classe Logger

Logger logger = Logger.getLoggernom de la classe courante »);

Via le Context :

Logger logger = Context.getLogger();

Ensuite pour utiliser le Logger :

logger.log(Level.INFO, "Message to log");

### Développement d’une application plugin

Une application permet d’exposer des ressources relatives à une même entité métier (gestion des projets, datasets…). Chaque application à une définition de sécurité qui lui est propre.

Introduction

Le système permet l’ajout d’une application à la manière d’un plugin en utilisant le même mécanisme que les convertisseurs.

Pour ajouter une application if suffit :

  • D’écrire sa classe, elle doit hériter de « AbstractApplicationPlugin »

  • De l’ajouter dans le projet « extensions »

  • D’ajouter le nom complet de la classe dans le fichier « fr.cnes.sitools.application.ApplicationPluginHelper» dans le dossier META-INF

  • De compiler le projet « extensions » et de redémarrer le Sitools2.

Il y a deux étapes importantes dans le codage d’une application, le constructeur qui permet de définir les paramètres et la méthode createInboundRoot qui définit le comportement de l’application et attache les ressources. Le codage n’est pas différent de celui d’une application Restlet classique.

Le constructeur

  • Métadonnées de l’application

Le Constructeur possède un ensemble de métadonnées relatif à l’application, à savoir :

Métadonnées Description Méthodes à employer
L’auteur de l’application L’auteur du convertisseur apparaît dans l’interface d’administration getModel().setClassAuthor
Le propriétaire de l’application Le propriétaire du convertisseur apparaît dans l’interface d’administration getModel().setClassOwner
La version du filtre La version du convertisseur apparaît dans l’interface d’administration. Une vérification entre la version du filtre configuré et la version du filtre provenant du JAR est effectuée. Un warning est affichée si les versions diffèrent. getModel().setClassVersion
La catégorie Catégorie dans laquelle l’application doit être classée (ADMIN, USER, ….) getModel().setCategory
  • Paramétrage de configuration de l’application

Lors du développement d’une application, le développeur peut créer des paramètres de configuration qui seront saisis par l’administrateur du système. Pour cela il suffit d’instancier des objets ConverterParameter(), d’utiliser ses setteurs et de les ajouter à l’application par la méthode addParameter(final ApplicationPluginrParameter param).

//création du paramètre
ApplicationPluginParameter param1 = new ApplicationPluginParameter();
param1.setName("param1");
param1.setDescription("Description de param1");
//ajout du paramètre
this.addParameter(param1);

Lors de l’instanciation d’un objet ApplicationPluginParameter, les paramètres suivants doivent être spécifiés :

Paramètre Description
name Nom du paramètre qui sera affiché dans l’interface d’administration pour la configuration du filtre
description Description du paramètre qui sera affiché dans l’interface d’administration

Une application peut avoir deux constructeurs :

  • Un par défaut, sans paramètre et obligatoire

  • Un autre avec un objet de type Context en paramètre ainsi que le modèle de l’application. Dans cet objet Context, on peut retrouver le jeu de données en utilisant l’attribut « DATASET » dans l’objet Context. Ce constructeur permet d’avoir une liste de paramètres qui dépend du jeu de données et rend le filtre encore plus souple.

La méthode createInboundRoot

Cette méthode définit le fonctionnement de l’application et permet d’attacher différentes ressources. C’est dans cette méthode que l’on peut utiliser les valeurs des paramètres définit dans le constructeur. On récupère un paramètre de la façon suivante :

//récupération du paramètre, « param1 » est le nom du paramètre 
 ApplicationPluginParameter param = this.getParameter("param1");

Validation de la configuration

Les paramètres que l’administrateur a saisis peuvent être vérifiés par ce mécanisme de validation. Le squelette de la méthode est la suivante :

/**
 * Gets the validator for this Converter
 *
 * @return the validator for the converter
 */
 @ Override
public Validator < AbstractApplicationPlugin > getValidator() {
	return new Validator < AbstractApplicationPlugin > () {
		 @ Override
		public Set < ConstraintViolation > validate(AbstractApplicationPlugin item) {
			// TODO Auto-generated method stub
			return null;
		}
	};
}

Deux états sont disponibles quand un problème de validation est détecté :

  • WARNING : un warning apparaît lors de la configuration du filtre par l’administrateur,

  • CRITICAL : un erreur critique apparaît lors de la configuration du filtre par l’administration. Dans ce cas, la configuration du filtre n’est pas sauvegardée

Description de l’API

Il est important que l’API de l’application soit décrite pour permettre de la documenter. Voici un exemple montrant comme décrire une application.

@ override
Public void sitoolsDescribe() {
	this.setName(“ProxyApp”);
	this.setAuthor(“AKKA Technologies”);
	this.setOwner(“CNES”);
	this.setDescription(“Proxy Application Plugin”);
}

Attachement des ressources plugins

Il est possible d’attacher des ressources plugin à une application plugin. Pour que cela soit possible il faut ajouter une ligne dans le constructeur de l’application

attachParameterizedResources(router);

Ajout d’une application dans SITools2

  • Méthode avec Eclipse

Si vous utilisez Eclipse, et que vous avez récupérer l’ensemble des sources, l’ajout d’une application est très simple.

En effet, il suffit d’ajouter cette classe dans le projet extensions et d’ajouter le nom complet de la classe dans le fichier fr.cnes.sitools.applications.ApplicationHelper.

Il faut ensuite exécuter la tache Ant présente dans le projet extensions afin d’inclure la nouvelle classe dans la liste des extensions. Il faut également redémarrer Sitools2 pour prendre en compte le nouveau Jar.

  • Méthode sans Eclipse

Dans le cas ou vous n’utilisez pas Eclipse, et que le projet extensions n’existe pas, il faut créer la bonne arborescence « à la main ».

Cette arborescence est tout de même assez simple. Il suffit d’avoir à la racine les dossiers contenant les sources ainsi qu’un dossier META-INF/services qui contient un fichier nommé fr.cnes.sitools.applications.ApplicationHelper dans lequel on place le nom des classes.

L’arborescence ressemble donc à ceci :

Il faut ensuite

  • Compiler les sources en ayant le jar de Sitools2 et de Restlet dans le classpath.

  • Créer un jar en incluant les classes compilées et le dossier META-INF/services.

  • Ajouter ce jar au classpath de Sitools2

Gestion des logs

Il existe plusieurs méthodes pour récupérer un Logger dans Sitools2.

Via la classe Application :

Application application = getApplication();                                             
Logger logger = application.getLogger();

Directement via la classe Logger

Logger logger = Logger.getLoggernom de la classe courante »);

Via le Context :

Logger logger = Context.getLogger();

Ensuite pour utiliser le Logger :

logger.log(Level.INFO, "Message to log");
### Développement d’une ressource plugin

Une ressource plugin est un service qui permet d’être attachée à une application. Dans la plupart des cas, cette ressource est attachée à un jeu de données et permet donc de le requêter afin de réaliser un traitement sur l’information contenue dans celui-ci.

NOTE : à l’heure actuelle, les applications d’administration et d’exposition de projets et de jeux de données, les applications plugins et l’administratonApplication sont les seules à pouvoir accueillir de tels plugins. Le design du système autorise cependant l’extension de ce comportement aux autres applications de SITools2 si le besoin se présentait.

#### Introduction

Il existe deux types de « resource plugin » :

  • L’un est simple et s’exécute d’une façon synchrone, deux classes sont nécessaires : une classe représentant le modèle et une autre représentant la ressource (le service à affectuer)

  • L’autre est plus complexe et peut s’exécuter d’une façon synchrone ou asynchrone. Une classe supplémentaire est nécessaire : la façade

#### Création du modèle

La classe modèle, interagissant avec l’IHM d’administration, doit hériter de la classe ResourceModel.Le squelette d’une telle classe est le suivant :

public class BasicParametrizedResourceModel extends ResourceModel {
	/**
	 * Constructor
	 */
	public BasicParametrizedResourceModel() {
		super();
		setClassAuthor("AKKA");
		setClassVersion("0.1");
		setName("BasicParametrizedResourceModel");
		setDescription("Resource model");
		setClassOwner(“CNES”);
		setResourceClassName(fr.cnes.sitools.resources.basic.BasicParametrizedResource.class.getName());
		ResourceParameter textToSend = new ResourceParameter("text", "text to send", ResourcesParameterType.PARAMETER_INTERN);
		textToSend.setValue("FooBar"); // default value
		addParam(textToSend);
		this.completeAttachUrlWith("basicresource/{yourtext}");
	}
}

Métadonnées de la ressource

Le Constructeur possède un ensemble de métadonnées relatif à la ressource, à savoir :

Métadonnées Description Méthodes à employer
L’auteur de la ressource L’auteur de la ressource apparaît dans l’interface d’administration setClassAuthor
Le propriétaire de la ressource Le propriétaire de la ressource apparaît dans l’interface d’administration setClassOwner
Le nom de la ressource Le nom de la ressource apparaît dans l’interface d’administration setName
La description de la ressource La description de la ressource apparaît dans l’interface d’administration setDescription
La version de la ressource La version de la ressource apparaît dans l’interface d’administration. Une vérification entre la version du filtre configuré et la version du filtre provenant du JAR est effectuée. Un warning est affichée si les versions diffèrent. setClassVersion

Paramétrage de configuration de la ressource

Lors du développement d’une ressource, le développeur peut créer des paramètres de configuration qui seront saisis par l’administrateur du système. Pour cela il suffit d’instancier des objets ResourceParameter(String name, String description, ResourceParameterType type) et de les ajouter au convertisseur par l’utilisation de la méthode addParam(final ResourceParameter param).

Lors de l’instanciation d’un objet ResourceParameter, les paramètres suivants doivent être spécifiés :

Paramètre Description
name Nom du paramètre qui sera affiché dans l’interface d’administration pour la configuration du filtre
description Description du paramètre qui sera affiché dans l’interface d’administration
ResourceParameterType Type du paramètre entrainant un comportement spécifique dans l’IHM. Le type peut être soit INTERN, USER_INPUT ou USER_GUI. Un type INTERN est une constante que l’administrateur devra saisir dans l’IHM d’administration. Un type USER_INPUT est un attribut qui apparait à l’utilisateur dans l’IHM cliente lorsque la ressource est appelée. Un type USER_GUI est utilisé pour afficher une information à l’utilisateur (ex : souhaitez-vous continuer l’opération ?).

Une fois l’objet ResourceParameter créé, il est possible d’ajouter des comportements plus fins au niveau de l’IHM. Pour cela, il suffit d’utiliser la méthode setValueType de l’objet ResourceParameter. Le tableau ci-dessous définit les valeurs du setteur qui sont supportées.

Valeur du setteur Description
xs:dataset.columnAlias Liste les colonnes visibles du jeu de données (uniquement compatible si la ressource est attachée à un jeu de données) dans l’IHM.
xs:enum[val1,val2,val3] Liste dans l’IHM les choix parmi les valeurs val1, val2 et val3 (une seule valeur possible). La méthode setValue permet ensuite de positionner une valeur par défaut dans l’IHM.
xs:enum-mulitple[val1,val2,val3] Liste dans l’IHM les choix parmi les valeurs val1, val2 et val3 (plusieurs valeurs possibles). La méthode setValue permet ensuite de positionner une valeur par défaut dans l’IHM
xs:enum-editable[val1,val2,val3] Liste dans l’IHM les choix parmi les valeurs val1, val2 et val3 (éditable par l’administrateur). La méthode setValue permet ensuite de positionner une valeur par défaut dans l’IHM
xs:enum-editable-multiple[val1,val2,val3] Liste dans l’IHM les choix parmi les valeurs val1, val2 et val3 (éditable par l’administrateur, et plusieurs choix possibles). La méthode setValue permet ensuite de positionner une valeur par défaut dans l’IHM
xs:boolean Liste dans l’IHM les choix parmi true ou false. La méthode setValue permet ensuite de positionner une valeur par défaut dans l’IHM
xs:image Liste dans l’IHM les choix parmi la liste des images uploadées sur le serveur, possibilité de choisir une image.
xs :dictionary Liste dans l’IHM les choix parmi la liste des dictionnaires (l’attribut « name » du dictionnaire est utilisé lors de la sauvegarde du paramètre).
Note : Il est tout à fait possible d’ajouter d’autres « xs » (ex : « xs :interger ») qui ne sont pas définis dans le tableau ci-dessous. Cependant ces « xs » n’étant pas implémentés dans l’IHM, ces « xs » auront le même comportement que un paramètre INTERN.

Une ressource peut avoir deux constructeurs :

  • Un par défaut, sans paramètre et obligatoire

  • Un autre avec un objet de type Context en paramètre. Dans cet objet Context, on peut retrouver le jeu de données en utilisant l’attribut « DATASET » dans l’objet Context. Ce constructeur permet d’avoir une liste de paramètres qui dépend du jeu de données et rend le filtre encore plus souple.

Spécification d’une partie de l’URI de la ressource

La ressource étant attachée à la DataSetApplication, l’URI de la ressource contient donc l’URI de la dataSetApplication. Pour configurer la partie de l’URI située après celle de la DataSetApplication, il suffit d’appeler la méthode completeAttachUrlWith.

L’implémentation du service

Le modèle fournit également la classe d’implémentation dans lequel est contenu le code métier : setResourceClassName(fr.cnes.sitools.resources.basic.BasicParametrizedResource.class.getName())

Pour que le client Sitools2 sache quelles sont les méthodes qu’il peut invoquer, il faut renseigner la liste des méthodes autorisées. Il s’agit de la liste des méthodes en majuscule séparée par le caractère « | » :

this.getParameterByName("methods").setValue("POST|GET");

Spécifier le type de sélection dans l’interface utilisateur

Pour que le client Sitools2 sache le type de sélection qu’il peut appliquer, il convient de renseigner le paramètre « dataSetSelection » :

this.setDataSetSelection(DataSetSelectionType.ALL);

L’énumération DataSetSelectionType contient les différentes possibilités :

/** No selection authorized */                                                     
 NONE,                                                                                  
 /** Single selection */                                             
 SINGLE,                                    
 /** Muliple selection */                                            
 MULTIPLE,                                                                     
 /** All the dataset can be selected */                                   
 ALL 

Ajouts de paramètres de façon dynamique

On peut ajouter dans un modèle de ressources des paramètres de façon dynamique en fonction du contexte de l’application. Cela permet par exemple d’avoir un paramètre par colonne de dataset ou de proposer la liste des dictionnaires en xs :enum à l’administrateur.

Il s’agit uniquement de rajouter des paramètres qui seront ensuite rempli par l’administrateur. Une fois le modèle sauvegardé, il n’est plus possible d’en ajouter avec cette méthode.

Il faut surcharger la méthode « initParametersForAdmin » dans l’implémentation du modèle. Cette méthode prend en paramètre un objet « Context » ce qui permet de récupérer certains paramètres :

  • appClassName : le nom de la classe d’application parent

  • parent : l’identifiant le la classe d’application parent

Compatibilité des ressources avec des applications

Il est également possible de spécifier le type d’application avec laquelle cette ressource est compatible. Par exemple, cela permet d’être sûr qu’une application qui gère des jeux de données ne sera pas attachée à une application de gestion des projets.

Pour spécifier le type de l’application, il suffit d’appeler, dans le modèle de la ressource, la méthode setApplicationClassName(className) avec en paramètre le nom complet de la classe d’application :

this.setApplicationClassName("fr.cnes.sitools.dataset.DataSetApplication");

On peut également saisir le nom d’une super classe, toutes les classes qui hérite de celle-ci seront donc compatibles. Dans le cas ou rien n’est spécifié, toutes les applications sont compatibles.

Au niveau de l’interface d’administration, seules les ressources compatibles avec une application donnée sont affichées ce qui permet d’éviter les erreurs.

Dans le cas ou une ressource serait attachée à une application non compatible, cette ressource est tout de même attachée mais un Warning est affiché dans le LOG. Lorsque l’on appelle cette ressource, elle retourne une erreur 503 pour spécifier que le service n’est pas disponible.

Chaque ressource peut être rattachée à une application qui étend ParamterizedApplication. Aujourd’hui, seule les ¨ProjectAdministration, ProjectApplication, DatasetAdministration et DatasetApplication sont concernées.

#### Notions communes aux « resource plugin »

Dans un Resource plugin, il est possible de faire quasiment n’importe quel traitement. Elle peut également répondre à l’API de consultation des données d’un jeu de données. On peut donc :

Accéder à la requête sur les données du jeu de données

// On récupère le context
Context context = getContext();
// On récupère la DataSetApplication
DataSetApplication datasetApp = (DataSetApplication)getApplication();
// On génère un nouveau DataSetExplorerUtil
DataSetExplorerUtil dsExplorerUtil = new DataSetExplorerUtil(datasetApp, getRequest(), getContext());
//On récupère les paramètres de connexion
DatabaseRequestParameters params = dsExplorerUtil.getDatabaseParams();
//On instancie une requête à l’aide d’une factory
DatabaseRequest databaseRequest = DatabaseRequestFactory.getDatabaseRequest(params);
//On crée une requête en fonction du type de la requête (distinct ou normale)
if (params.getDistinct()) {
	databaseRequest.createDistinctRequest();
} else {
	databaseRequest.createRequest();
}

On peut ensuite boucler sur les enregistrements avec la méthode boolean :nextResult() et récupérer un enregistrement avec la méthode Record :getRecord().

try {
	while (databaseRequest.nextResult()) {
		Record rec = databaseRequest.getRecord();
		A la fin de lexécution,
		il faut fermer la connexion à la source de données :
	}
	finally {
		if (databaseRequest != null) {
			databaseRequest.close();
		}
	}

Attention à bien fermer la connexion à la base de données. De plus, dans le cas ou la connexion est utilisée dans le code d’une représentation, il faut créer la requête et la relâcher dans la méthode write (ne pas prendre la connexion dans le constructeur et la relâcher dans le write).

Appliquer les convertisseurs

On peut également appliquer les convertisseurs définis sur le jeu de données. Pour ce faire, on doit récupérer la DatasetApplication et appliquer les convertisseurs.

//On récupère la DatasetApplication                                                                                                           
DataSetApplication datasetApp = (DataSetApplication)getApplication();                                                                          
//récupération des convertisseurs                                                                                                             
ConverterChained converterChained = datasetApp.getConverterChained();

Pour appliquer un convertisseur, on appelle la méthode Record :getConversionOf(Record) de la classe ConverterChained sur chacun des enregistrements. Dans le cas ou le convertisseur utilise la requête HTTP, il convient de l’ajouter dans le contexte du convertisseur avant son exécution. Il s’agit d’un traitement assez simple :

if (converterChained != null) {                                                                                                                 
 converterChained.getContext().getAttributes().put("REQUEST", request);                                                                           
 }

Ensuite on applique les convertisseurs sur chacun des enregistrements :

if (Util.isSet(converterChained)) {                                                        
 rec = converterChained.getConversionOf(rec);                                              
}

On peut également accéder au jeu de données en le récupérant dans la DatasetApplication.

Obtenir une connexion directe à la source de données

// recuperation complete du dataset
DataSet ds = datasetApp.getDataSet();
// recuperation d’une connexion à la source de données
Connection con = SitoolsDataSourceFactory.getInstance()
	.getDataSource(ds.getDatasource().getName()).getConnection();

Accès aux paramètres de la requête HTTP

Une resource plugin peut enrichir l’API de consultation des enregistrements d’un jeu de données en récupérant d’autres paramètres dans la requête HTTP. Il suffit de récupérer leur valeur dans l’objet Request.

getRequest().getResourceRef().getQueryAsForm().getFirst("limit");

Ajouter un prédicat

Il est possible d’ajouter un prédicat pour enrichir la requête à la base de données avant son exécution. Il suffit d’utiliser le code suivant :

Predicat pred = new Predicat();
pred.setLogicOperator("AND");
pred.setLeftAttribute(primaryKey);
//primaryKey est un objet de type Column
pred.setCompareOperator("=");
pred.setRightValue("?");
// params est un Objet de type DatabaseRequest
params.getPredicats().add(pred);

Créer un fichier

Il est possible de créer des fichiers lors de l’exécution d’une ressource. Pour ce faire, il existe une méthode dans la classe OrderResourceUtils (disponible dans le projet des extensions):

public static String addFile(Representation repr, String urlDest, ClientInfo clientInfo, Context context) throws SitoolsException

Cette méthode prend une Representation en paramètre, ainsi qu’une Url où sera créé le fichier. Cette Url doit contenir le nom du fichier. Elle prend également un ClientInfo pour prendre en compte les droits sur le répertoire de destination ainsi que le Context pour récupérer certaines variables.

Il faut spécifier le média type de la représentation en fonction du type de fichier. Il faut également que l’extension du fichier soit compatible avec ce média type. (Par exemple si on veut créer un fichier nommé « test.html », il faut que le media type de la représentation soit « MediaType.TEXT_HTML »).

Afin d’obtenir une Url de destination disponible, il existe une méthode :

public static String getUserAvailableFolderPath(SvaTask svaTask, Context context)

Cette Url de destination pointe dans l’espace utilisateur de l’utilisateur ayant demandé l’exécution de la ressource ou dans le dossier temporaire si la ressource est demandé sans utilisateur.

Copier un fichier existant

De la même manière que précédemment, il est possible de copier un fichier présent à une Url donnée dans une autre Url.

public static String copyFile(String fileUrl, String destUrl, ClientInfo clientInfo, Context context) throws SitoolsException

Zipper un ensemble de fichier

Il est possible de faire un fichier zip contenant un ensemble de fichier. Il suffit de passer une liste de nom de fichier accessible et le chemin local d’un fichier zip sur le serveur.

public final void zipFiles(ArrayList < String > listOfFiles, String destFilePath, ClientInfo clientInfo, Context context)throws SitoolsException

Le fichier zip ne peut être créé qu’en local sur le serveur. Il peut ensuite être copié dans un autre répertoire puis supprimé. Par exemple, dans le cas de la ressource de commande (OrderResource), le fichier zip est créé dans le dossier temporaire (SitoolsSettings.getInstance().getTmpFolderUrl()) puis recopié dans l’espace utilisateur.

Initialiser et utiliser un objet partagé par plusieurs ressources

Il peut être nécessaire dans certains cas d’initialiser un objet et de vouloir l’utiliser dans des ressources différentes.

Pour ce faire, il faut stocker cet objet dans le « Context » du Composant (Component) Restlet. Sitools2 est construit autour d’un seul composant auquel on attache toutes les applications, il est donc connu de toutes les applications et ressources.

Lors de l’exécution d’une ressource, dans la méthode doInit, on vérifie que l’objet existe dans le Context du composant. S’il existe on le récupère sinon on l’initialise et on le place dans le Context. On peut utiliser ce code comme exemple :

//Récupération du SitoolsSettings, il contient un pointeur vers le composant
SitoolsSettings settings = ((SitoolsSettings)getContext().getAttributes().get(ContextAttributes.SETTINGS));
/Récupération du Context du composant
Context contextComposant = settings.getComponent().getContext();
Object object = contextComposant.getAttributes().get(<SOME KEY>);
if (object == null) {
    // Initialisation de l 'objet
    object = new <SOME OBJECT>();
    // Ajout de l'objet initialisé
    contextComposant.getAttributes().put( < SOME KEY > , object);
}

Accès à des fichiers distants

Pour que les ressources puissent accéder à des fichiers ou des services distants, il peut être nécessaire de configurer un proxy. Cette configuration s’effectue dans le fichier « sitools.properties ». Il faut donc saisir les propriétés suivantes :

• Starter.WITH_PROXY=true ou false

• Starter.PROXY_HOST=Proxy host

• Starter.PROXY_PORT=Proxy port

• Starter.PROXY_USER=Proxy user

• Starter.PROXY_PASSWORD=Proxy Password

• Starter.NONPROXY_HOST=Liste des domaines (séparés par un |) qui ne seront pas envoyé via le proxy pour les appels de Sitools2 en tant que client.

Pour accéder à un fichier, il suffit d’utiliser la méthode getFile défini précédemment. Pour accéder à un service distant en utilisant le proxy, il faut ajouter quelques instructions pour prendre en compte l’authentification du proxy :

//création de la requête
request = new Request(Method.GET, url);
//application de l'authentification pour le proxy
if ((ProxySettings.getProxyAuthentication() != null) && reqGET.getProxyChallengeResponse() == null) {
	request.setProxyChallengeResponse(ProxySettings.getProxyAuthentication());
}
//execution de la requête
org.restlet.Response r = context.getClientDispatcher().handle(request);
On peut également utiliser une classe utilitaire ajouté récemment, lauthentification pour le proxy sera ajouté automatiquement. :
ClientResourceProxy clientResourceProxy = new ClientResourceProxy(url, Method.GET);
ClientResource clientResource = clientResourceProxy.getClientResource();
Representation representation = clientResource.handle();
#### L’implémentation d’une ressource synchrone

Un plugin de ressource hérite obligatoirement de la classe abstraite SitoolsParametrizedResource.

Elle peut implémenter l’ensemble des méthodes HTTP, par exemple pour GET :

package fr.cnes.sitools.resources.basic;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import fr.cnes.sitools.common.resource.SitoolsParametrizedResource;
public class BasicParametrizedResource extends SitoolsParametrizedResource {
	 @ Override
	public void doInit() {
		super.doInit();
		this.textSent = (String)this.getRequestAttributes().get("yourtext");
	}
	 @ Override
	public Representation get() {
		return new StringRepresentation("This is a dynamic resource sending the text : "
			 + getModel().getParametersMap().get("text").getValue());
	}
	 @ Override
	public void sitoolsDescribe() {
		setName(this.getClass().getName());
		setDescription("Test class for dynamic resource");
	}
}

Dans cet exemple, il est possible de récupérer la valeur de « yourtext » à partir de l’url, via la méthode :

String) this.getRequestAttributes().get("yourtext");

Pour récupérer la valeur des autres paramètres on utilise les méthodes classiques :

getModel().getParametersMap().get("text").getValue();

Gestion des logs

Il existe plusieurs méthodes pour récupérer un Logger dans Sitools2.

Via la classe Application :

Application application = getApplication();  
Logger logger = application.getLogger();

Directement via la classe Logger

Logger logger = Logger.getLoggernom de la classe courante »);

Via le Context :

Logger logger = Context.getLogger();

Ensuite pour utiliser le Logger :

logger.log(Level.INFO, "Message to log");
#### Implémentation d’une « ressource plugin » avec gestion des tâches

Il est désormais possible d’exécuter la ressource de manière synchrone ou asynchrone avec une gestion des tâches d’exécutions. Il est donc possible d’utiliser les ressources de la même manière que les ressources synchrones.

Les filtres définis sur un jeu de données peuvent être appliqué à cette ressource ce qui permet d’exécuter une requête similaire à l’API records dans une ressource.

Introduction

Il existe deux fonctionnements pour une ressource, le développeur devra choisir l’un ou l’autre en fonction de ses besoins :

  • Le résultat est un objet « Representation » : dans ce fonctionnement, il faut retourner une représentation qui sera stockée sur le serveur. Elle pourra être consommée une fois l’exécution terminée mais ne pourra l’être qu’une seule fois.

  • Le résultat est un fichier : dans ce fonctionnement, le résultat de l’exécution de la ressource doit être stocké dans un fichier et on doit retourner « null ». Il faut également renseigner l’url du fichier résultat dans la tâche courante pour pouvoir y accéder plusieurs fois à la fin de l’exécution.

Il existe deux moyens de passer des paramètres à une ressource :

  • En utilisant l’API de consultation des enregistrements d’un jeu de données

  • En passant l’url d’un fichier dans le corps de la requête. Ce fichier doit contenir une liste d’enregistrements contenant au moins la clé de chaque enregistrement. Ce fichier doit être en format JSON et être de la forme : {"orderRecord":{"records":[ liste des records ]}}. L’utilisation de ce dernier moyen demande de traiter le corps de la requête dans la « façade » dans le cas d’un appel POST, PUT ou DELETE

Il faut implémenter 3 classes pour mettre en place une ressource plugin avec gestion des tâches. Comme pour une ressource plugin classique il faut un modèle et une ressource. Il faut également une ressource « façade » qui servira d’entrée dans la ressource et définira l’interface du service.

Création du modèle

Le modèle s’implémente comme un modèle de ressource classique, cependant il faut que ce modèle hérite de la classe TaskResourceModel. Cette classe met en place 4 nouveaux paramètres : l’url d’une image (image), le nom de la classe d’implémentation de la ressource (resourceImplClassName) , le type d’exécution que pourra spécifier l’administrateur (runTypeAdministration), et le type d’exécution choisi par le client (runTypeClient). .

On peut spécifier ces paramètres avec les méthodes setResourceImplClassName(), setRunTypeAdministration(), setRunTypeClient() et setImage().

Exemple pour une ressource de commande de fichiers :

public class OrderResourceModel extends TaskResourceModel {
	/**
	 * Constructor
	 */
	public OrderResourceModel() {
		super();
		setClassAuthor("AKKA");
		setClassVersion("0.1");
		setName("OrderResourceModel");
		setDescription("Resource model for Order Resource");
		/** Resource facade */
		setResourceClassName("fr.cnes.sitools.resources.tasks.order.OrderResourceFacade");
		/** Resource d'implémentation */
		setResourceImplClassName("fr.cnes.sitools.resources.tasks.order.OrderResource");
		setRunTypeAdministration(TaskRunType.TASK_DEFAULT_RUN_SYNC);
		ResourceParameter paramColUrl = new ResourceParameter("colUrl", "Colum containing data url for order",
				ResourceParameterType.PARAMETER_INTERN);
		/** Type de paramètre pour lister les colonnes du dataset */
		paramColUrl.setValueType("xs:dataset.columnAlias");
		ResourceParameter param2 = new ResourceParameter("zip",
				"(true or false) If the data needs to be zipped at the end", ResourceParameterType.PARAMETER_USER_INPUT);
		param2.setValue("false");
		/** Type de colonne booléen */
		param2.setValueType("xs:boolean");
		this.addParam(paramColUrl);
		this.addParam(param2);
		this.setApplicationClassName(DataSetApplication.class.getName());
		this.getParameterByName("methods").setValue("POST");
		this.setDataSetSelection(DataSetSelectionType.ALL);
	}
}

L’implémentation du service

Le model fournit également :

  • la classe d’implémentation dans lequel est contenu la façade : setResourceClassName(fr.cnes.sitools.resources.basic.BasicParametrizedResource.class.getName())

  • la classe d’implémentation dans lequel est contenu le code métier: setResourceImplClassName("fr.cnes.sitools.resources.tasks.order.OrderResource")

  • le mode par défaut de la classe d’implémentation : setRunTypeAdministration(TaskRunType.TASK_DEFAULT_RUN_SYNC)

    La façade

Il s’agit de la ressource qui sera attachée à l’application, c’est donc celle qui servira d’entrée. Elle doit donc définir les différentes méthodes autorisées. Elle doit ensuite faire appel à la classe TaskUtils qui gère les appels synchrones ou asynchrones et les tâches d’exécutions.

Exemple pour une ressource de commande de fichiers :

public class OrderResourceFacade extends SitoolsParameterizedResource {
	/**
	 * Description de la ressource
	 */
	 @ Override
	public void sitoolsDescribe() {
		setName("OrderResourceFacade");
		setDescription("Resource to order data");
	}
	/**
	 * Description WADL de la methode POST
	 *
	 * @param info
	 *          The method description to update.
	 */
	 @ Override
	public void describePost(MethodInfo info) {
		info.setDocumentation("Method to order data from a dataset");
		info.setIdentifier("order");
		addStandardPostOrPutRequestInfo(info);
		DataSetExplorerUtil.addDatasetExplorerGetRequestInfo(info);
		DataSetApplication application = (DataSetApplication)getApplication();
		DataSetExplorerUtil.addDatasetExplorerGetFilterInfo(info, application.getFilterChained());
		addStandardResponseInfo(info);
		addStandardInternalServerErrorInfo(info);
	}
	/**
	 * Create the order
	 *
	 * @param represent
	 *          the {@link Representation} entity
	 * @param variant
	 *          The {@link Variant} needed
	 * @return a representation
	 */
	 @ Post
	public Representation createOrder(Representation represent, Variant variant) {
		processBody();
		return TaskUtils.execute(this, variant);
	}
	/**
	 * process the body and save the request entity {@link Representation}
	 */
	public void processBody() {
		Representation body = this.getRequest().getEntity();
		if (body != null && body.isAvailable() && body.getSize() > 0) {
			Form bodyForm = new Form(body);
			getContext().getAttributes().put(TaskUtils.BODY_CONTENT, bodyForm);
		}
	}
}

Cette façade doit également traiter le corps de la requête dans le cas ou la ressource serait appelée de manière asynchrone. En effet lors d’un appel asynchrone, la réponse est retournée avant la fin de l’exécution effective de la requête (dans la classe OrderResource). Lorsque la réponse est retourné, le corps de la requête est supprimé par Restlet et ne peut plus être utilisé, il faut donc le traiter avant l’appel au TaskUtils, par exemple en utilisant la méthode processBody.

Figure 26 : Diagramme de séquence très simplifié illustrant le déroulement d'un appel à OrderResource

Attention : Dans le cas d’une ressource acceptant une sélection d’enregistrement de dataset, il est impératif de recopier et d’appeler la méthode processBody comme dans l’exemple ci-dessus sinon lors d’un appel POST ou PUT, les sélections ne seront pas appliquées.

La ressource implémentation

Liste des objets passés dans le contexte

Certains objets sont passés dans le contexte aux ressources, pour chacun d’entre eux il existe une constante définie dans la classe TaskUtils :

  • PARENT_APPLICATION : l’application parente de la ressource

  • LOG_FOLDER : le répertoire de log de la ressource

  • BODY_CONTENT : le contenu du corps de la requête (doit précédemment avoir été rempli au niveau de la façade et mis dans le contexte de la ressource avec la même clé)

Précaution d’usage

  • Il faut éviter de prendre les connexions à la base de données à la création du Resource (dans le code d’implémentation de la ressource, ou le doInit) et il faut les relâcher lors de la récupération du résultat (dans le code de la Representation associée).

  • Il faut éviter les traitements trop longs ou bien prévenir l’administrateur de forcer le mode d’exécution à asynchrone.

  • Dans une ressource, il est possible de faire quasiment n’importe quel traitement il convient donc de faire attention aux traitements effectués.

Gestion des logs

Chaque exécution de ressource (tâche) possède son propre fichier de log. Ce log est présent dans le dossier data/resources_logs. (Configurable dans le fichier de properties avec la clé : Starter.APP_RESOURCE_LOGS_DIR)

Pour ajouter quelque chose au log, il faut récupérer le logger et utiliser la méthode log :

// on récupère le logger de la tache                                                                      
java.util.logging.Logger logger = task.getLogger();                                                     
// on log                                                                                                  
logger.log(Level.INFO, "log");

A noter que le système d’analyse de log (paragraphe 10.3) n’utilise pas ces fichiers pour l’analyse.

#### Création d’une ressource de commande

Une des ressources particulières offerte par Sitools2 est la ressource de commande. Elle permet d’effectuer une commande de fichiers, d’en garder une trace au niveau du serveur et de notifier l’administrateur à chaque commande utilisateur.

Architecture et modèle de classe

Figure 27 : Modèle de classe général OrderResource

Ce diagramme de classe très simplifié (pas d’affichage des méthodes ni des attributs) présente les différentes classes utilisées (hors classes utilitaires) pour la ressource de commande.

OrderResourceFacade est la façade de cette ressource,

OrderResourceModel est le modèle de cette ressource. On peut surcharger cette classe pour ajouter d’autres paramètres et effectuer des validations supplémentaires.

OrderResource est l’implémentation de cette ressource, on peut, comme on peut le voir dans le schéma suivant, hériter de cette classe ou directement de AbstractOrderResource pour spécialiser le processus de commande.

Figure 28 : Modèle de classe détaillé ResourceOrder

Sitools2 fournit une classe abstraite qui réalise le processus de commande moins certaines actions à implémenter (AbstractOrderResource).

Il fournit également une implémentation par défaut, qui réalise la commande de fichier disponible à une URL et les copie ou les Zip dans l’espace utilisateur de l’utilisateur qui a demandé la commande (OrderResource). L’URL de chaque fichier est récupérée dans une colonne du Dataset.

Il est donc possible de réaliser sa propre implémentation de commande en implémentant la classe AbstractOrderResource ou en surchargeant OrderResource.

Processus de commande

Cette partie présente les différentes étapes du processus de commande avec le nom des méthodes réalisant chacune des étapes :

  • Initialisation : initialiseOrder()

    • Vérification de l’utilisateur : checkUser()

    • Préparation de la requête à la base de données : prepareRequest()

    • Initialisation de la commande Sitools : doInitialiseOrder()

  • Exécution de la commande : executeOrder()

    • Activation de la commande Sitools

    • Exécution de la requête à la base de données : executeRequest()

    • Création de la liste de fichier à commander : listFilesToOrder() (A surcharger)

    • Copie de la liste des fichiers à commander dans l’espace administrateur

    • Exécution de la commande : processOrder() (A surcharger)

  • Finalisation de la commande : terminateOrder()

    • Finalisation de la commande Sitools : doTerminateOrder()

    • Notification de l’administrateur : notifyAdminEnd()

Pour simplifier, les 2 méthodes principales à surcharger sont listFilesToOrder() et processOrder().

Les classes utilitaires disponibles

Sitools2 fournit un ensemble de classes utilitaires pour simplifier l’implémentation d’un processus de commande.

Elles sont situées dans le package fr.cnes.sitools.resources.order.utils et sont au nombre de 3 :

ListReferencesAPI

Figure 29 : Classe ListReferenceAPI

Cette classe permet de gérer des listes de fichier à commander et de générer des fichiers d’index avec des liens vers les fichiers commandés.

Plus précisément elle permet de gérer 2 listes de Reference (Objet Restlet qui représente un lien vers une URI), une pour les fichiers sources (liste des fichiers à commander avec leurs URI sources) et une pour les fichiers destinations (liste des fichiers commandé avec leurs URI cibles).

Elle permet également de générer et de copier des fichiers d’index à partir de chacune des listes de Reference.

La méthode copyToAdminStorage génère la liste des fichiers sources et la copie dans l’espace administrateur des commandes (permet d’avoir une trace des fichiers commandés).

La méthode copyToUserStorage génère la liste des fichiers cible et la copie à une Reference donnée, en principe dans l’espace utilisateur où la commande est copiée. L’appel à cette méthode est à la charge du développeur.

OrderAPI

Figure 30 : Classe OrderAPI

Cette classe permet de gérer un objet commande au sens de Sitools2 afin de garder une trace visible depuis l’interface administrateur et utilisateur de chacune des commandes. Les différentes méthodes de cette classe permettent de créer, de modifier et de modifier le statut d’une commande. Elles permettent également d’ajouter un événement à cette commande pour permettre à l’utilisateur de suivre l’avancement de sa commande.

OrderResourceUtils

Figure 31 : Classe OrderResourceUtils

Cette classe offre un ensemble de méthodes utiles pour l’implémentation d’une ressource de commande. Elle permet de gérer des fichiers, d’en créer, d’en modifier, d’en copier ou d’en supprimer. Elle permet également de créer un ZIP à partir d’un ensemble de Reference.

Exemple d’implémentation

La classe OrderResource du projet fr.cnes.sitools.extension peut servir d’exemple pour une implémentation de ressource de commande.

#### Validation de la configuration

Les paramètres que l’administrateur a saisis peuvent être vérifiés par ce mécanisme de validation. Le squelette de la méthode est la suivante :

/**
 * Gets the validator for this Converter
 *
 * @return the validator for the converter
 */
@ Override
public Validator < ReosurceModel > getValidator() {
	return new Validator < ResourceModel > () {
		 @ Override
		public Set < ConstraintViolation > validate(ResourceModel item) {
			// TODO Auto-generated method stub
			return null;
		}
	};
}

Deux états sont disponibles quand un problème de validation est détecté :

  • WARNING : un warning apparaît lors de la configuration du filtre par l’administrateur,

  • CRITICAL : une erreur critique apparaît lors de la configuration du filtre par l’administration. Dans ce cas, la configuration du filtre n’est pas sauvegardée

#### Ajout des ressources dans SITools2

Il faut enfin modifier le fichier fr.cnes.sitools.plugins.ResourceHelper en y ajoutant la classe Model précédemment créée.

##Développement de filtres de sécurité

Le système permet l’ajout de filtres de sécurité. La finalité de ces filtres est de personnaliser l’accès à certaines ressources de SITools2.

###Introduction

L’implémentation d’un filtre de sécurité se résume à l’implémentation :

  • D’un modèle héritant de FilterModel

  • D’une ressource héritant de org.restlet.routing.Filter

Création du modèle

Le squelette d’une telle classe est le suivant :

public class DataStorageAuthorizerModel extends FilterModel {
	public DataStorageAuthorizerModel() {
		super();
		setName("DataStorageAuthorizer");
		setDescription("Customizable datastorage directory authorizer");
		setClassAuthor("AKKA Technologies");
		setClassOwner("CNES");
		setClassVersion("0.1");
		setClassName("fr.cnes.sitools.filter.authorizer.DataStorageAuthorizerModel");
		setFilterClassName("fr.cnes.sitools.filter.authorizer.DataStorageAuthorizer");

		/**                                                                                                                                                                                                                                                      
		 * Parameter for the log directory                                                                                                                                                                                                                           
		 */
		FilterParameter logDir = new FilterParameter("logdir", "Storage logging directory", FilterParameterType.PARAMETER\_INTERN);
		addParam(logDir);

		/**                                                                                                                                                                                                                                                       
		 * Parameter for authorize or block                                                                                                                                                                                                                       
		 */
		FilterParameter authorize = new FilterParameter("authorize", "Authorize true|false", FilterParameterType.PARAMETER\_INTERN);
		authorize.setValue("true"); // default true                                                                                                                                                                                                                  authorize.setValueType("xs:boolean");                                                                                                                                                                                                                        addParam(authorize);                                                                                                                                                                                                                                         }                                                                                                                             
		@Override
		public Validator & lt;
		FilterModel & gt;
		getValidator() { // TODO validator for DataStorageAuthorizerModel                                                                                                                                                                                                           return null;                                                                                                                  
		}
	}
}
  • Métadonnées du filtre de sécurité

Le Constructeur possède un ensemble de métadonnées relatif au filtre de sécurité, à savoir :

Métadonnées Description Méthodes à employer
L’auteur du filtre de sécurité L’auteur de la ressource apparaît dans l’interface d’administration setClassAuthor
Le propriétaire du filtre de sécurité Le propriétaire de la ressource apparaît dans l’interface d’administration setClassOwner
Le nom du filtre de sécurité Le nom de la ressource apparaît dans l’interface d’administration setName
La description du filtre de sécurité La description de la ressource apparaît dans l’interface d’administration setDescription
La version du filtre de sécurité La version de la ressource apparaît dans l’interface d’administration. Une vérification entre la version du filtre configuré et la version du filtre provenant du JAR est effectuée. Un warning est affichée si les versions diffèrent. setClassVersion
  • Paramétrage de configuration du filtre de sécurité

Lors du développement d’une ressource, le développeur peut créer des paramètres de configuration qui seront saisis par l’administrateur du système. Pour cela il suffit d’instancier des objets FilterParameter(String name, String description, FilterParameterType type) et de les ajouter au convertisseur par l’utilisation de la méthode addParam(final FilterParameter param).

Lors de l’instanciation d’un objet FilterParameter, les paramètres suivants doivent être spécifiés :

Paramètre Description
name Nom du paramètre qui sera affiché dans l’interface d’administration pour la configuration du filtre
description Description du paramètre qui sera affiché dans l’interface d’administration
filterParameterType Type du paramètre entrainant un comportement différent dans l’IHM. Le type peut être soit INTERN ou IN. Un type INTERN est une constante que l’administrateur devra saisir dans l’IHM d’administration. Un type IN est un attribut du jeu de données que l’utilisateur devra choisir parmi la liste exposée dans l’IHM d’administration.

Cette classe doit étendre de FilterModel.

Dans le constructeur, on peut ajouter des paramètres, comme pour d’autres plugins, qui seront utilisés ultérieurement. Dans ce modèle on définit également le nom de la classe Modèle et le nom de la classe de filtre associée.

setFilterClassName("fr.cnes.sitools.filter.authorizer.DataStorageAuthorizer");

####Création du filtre

  • Filtres génériques

    Il s’agit d’un filtre Restlet, cette classe doit donc étendre la classe org.restlet.routing.Filter.

Elle doit définir son propre constructeur avec le contexte en paramètre :

public DataStorageAuthorizer(Context context) {

	this.context = context;

	filterId = (String) this.context.getAttributes().get("FILTER\_ID");
	filterModel = (FilterModel) this.context.getAttributes().get("FILTER\_MODEL");
	FilterParameter authorizeParameter = filterModel.getParameterByName("authorize");
	if (authorizeParameter != null) {

		try {
			bauthorize = Boolean.parseBoolean(authorizeParameter.getValue());
		} catch (Exception e) {
			// TODO Auto-generated catch block                                                                                                                                     
			e.printStackTrace();
		}
	}
}
  • Cas particulier : filtre sur les datastorages

Le filtre DataStorageAuthorizer est une implémentation de filtre qui étend org.restlet.security.Authorizer . Il faut donc surcharger la méthode suivante :

public boolean authorize(Request arg0, Response arg1) {}                                                        

####Validation de la configuration

Les paramètres que l’administrateur a saisis peuvent être vérifiés par ce mécanisme de validation. Le squelette de la méthode est la suivante :

/**                                                                                                                               
 * Gets the validator for this Converter                                                                                                                                                                                                                                      
 * @return the validator for the converter                                                                                            
 */

@Override

public Validator & lt;
FilterModel & gt;
getValidator() {

	return new Validator & lt;
	FilterModel & gt;
	() {
		@Override
		public Set & lt;
		ConstraintViolation & gt;
		validate(FilterModel item) {
			// TODO Auto-generated method stub                                  
			return null;
		}
	};
}                                                                  

Deux états sont disponibles quand un problème de validation est détecté :

  • WARNING : un warning apparaît lors de la configuration du filtre par l’administrateur,

  • CRITICAL : une erreur critique apparaît lors de la configuration du filtre par l’administration. Dans ce cas, la configuration du filtre n’est pas sauvegardée

####Ajout d’un filtre de sécurité dans SITools2

Il faut enfin modifier le fichier fr.cnes.sitools.plugins.FilterPluginHelper en y ajoutant la classe Modèle précédemment créée.

Gestion des logs

Il existe plusieurs méthodes pour récupérer un Logger dans Sitools2.

Via la classe Application :

Application application = getApplication();
Logger logger = application.getLogger();

Directement via la classe Logger

Logger logger = Logger.getLoggernom de la classe courante »)

Via le Context :

Logger logger = Context.getLogger();

Ensuite pour utiliser le Logger :

logger.log(Level.INFO, "Message to log");

##Développement de convertisseurs d’unité

Le système permet le développement de systèmes d’unité et de convertisseurs spécifiques entre unités n’ayant pas les mêmes dimensions physiques.

###Création d’un nouveau système d’unités

Le développement majeur consiste en la création d’un nouveau système d’unité par l’extension de la classe SystemOfUnits du framework javax.measure (intégré au jar fr.cnes.sitools.core.jar).

Cette classe, qui doit suivre le design pattern d’un singleton, sera alors constituée de plusieurs attributs de classe (mot-clé « static » en Java) :

  • un champ général UNITS (type Set<Unit< ?>>) qui contient l’ensemble des unités du système

  • d’un champ pour chaque unité créée (cf. exemple ci-après).

De plus, pour assigner un symbole à l’unité, chacune des unités instanciées doit s’enregistrer au près du singleton du framework javax.measure LocalFormat, qui gère le mapping entre symbole et unités. Cet enregistrement est possible via la méthode LocalFormat.getInstance().getSymbolMap().label(unit, symbol).

Lors de l’instanciation de chaque unité, celle-ci doit également être ajouté à l’attribut statique UNITS décrit précédemment. Comme chaque unité est elle-même statique, cette instanciation doit donc s’effectuer via une méthode intermédiaire (méthode addUnit dans l’exemple donné ci-après).

public final class AstronomicSystem extends SystemOfUnits {
  /** Set of units in the system */
  public static final Set<Unit<?>> UNITS = new HashSet<Unit<?>>();
  /** Add Angstrom unit, equivalent to a tenth of a nanometer */
  public static final Unit<Length> ANGSTROM = addUnit(new AstronomicUnit<Length>("" + (char) 143, METRE, new MultiplyConverter(1e-10)));
… others units defined the same way/** Instance for singleton */
  private static AstronomicSystem instance = null;
  /** Singleton private constructor */
  private AstronomicSystem() {}
  /** Get singleton
   * @return the instance
   */
  public static synchronized AstronomicSystem getInstance() {
    if (instance == null) {
      instance = new AstronomicSystem();
    }
    return instance;
  }
  @Override
  public Set<Unit<?>> getUnits() {
    return Collections.unmodifiableSet(UNITS);
  }
  /** Add a unit to the system and return it
   * @param <U> the unit type to instantiate
   * @param unit the unit to add
   * @return the unit added
   */
  private static <U extends Unit<?>> U addUnit(U unit) {
    UNITS.add(unit);
    return unit;
  }

Voici également un exemple de spécialisation des unités :

public class AstronomicUnit < Q extends Quantity < Q >> extends TransformedUnit < Q > {

	/** Long for serialization */
	private static final long serialVersionUID = 1L;

	/** Holds the symbol. */
	private final transient String symbol;

/**
* Constructor with symbol
* @param symbol the symbol used for the unit
* @param parent the base unit definition
* @param converter the converter to the parent
*/

	public AstronomicUnit(String symbol, Unit < Q > parent, UnitConverter converter) {
		super(parent, converter);
		this.symbol = symbol;
		LocalFormat.getInstance().getSymbolMap().label(this, symbol);
	}
}

Création d’un convertisseur d’unité spécifique

La conversion entre deux unités n’ayant pas la même dimension physique (d’une longueur vers une fréquence par exemple, en utilisant la constante universelle c), n’est pas automatique dans le framework javax.measure.

Afin de rendre possible la conversion, SITools2 permet la création de convertisseurs spécialisés à partir de la classe mère SitoolsUnitConverter.

Ce convertisseur spécialisé a deux attributs principaux : l’unité de départ et celle d’arrivée, initialisées dans le constructeur du convertisseur. Ces deux unités peuvent être simple, le framework étant par ailleurs capable de gérer la conversion entre les multiples de ces deux unités. Dans l’exemple donné ci après, on se contente donc de prendre comme unité de départ l’Hertz (fréquence) et comme unité d’arrivée le mètre (longueur). Il est à noter que ce convertisseur sera également capable d’effectuer automatiquement la conversion inverse (arrivée vers départ) sans développement supplémentaire.

La suite du développement consiste à implémenter cinq méthodes :

  • getBaseToTargetConverter : doit retourner un convertisseur de l’unité de base vers l’unité cible, en passant généralement vers le système métrique (les convertisseurs vers le système métrique sont automatiquement générés par le framework javax.measure)

  • inverse : doit retourner le convertisseur inverse du précédent,

  • convert(double x) : renvoie la valeur x convertie, de l’unité de base vers l’unité cible,

  • convert(Number x, MathContext ctx) : renvoie la valeur convertie dans un certain contexte mathématique,

  • isLinear() : renvoie true si la conversion est linéaire.

L’héritage nécessite l’implémentation d’autres méthodes, mais qui n’ont aucun impact sur la conversion (equals et hashCode). Ci-après nous montrons l’exemple d’un convertisseur permettant la conversion entre le domaine des fréquences et celui des longueurs d’onde :

public class FrequencyWavelengthConverter extends SitoolsUnitConverter {

	/** Serial number */
	private static final long serialVersionUID = 1L;

	/** Speed of light */
	private static final Velocity C = QuantityFactory.getInstance(Velocity.class).create(2.99792e8, MetricSystem.METRES_PER_SECOND);

	/** Constructor */
	public FrequencyWavelengthConverter() {
		super();
		this.setStartUnit(MetricSystem.HERTZ);
		this.setTargetUnit(MetricSystem.METRE);
	}

	@Override
	public UnitConverter getBaseToTargetConverter() {
		UnitConverter startConverter = this.getStartUnit().getConverterToMetric();
		UnitConverter targetConverter = this.getTargetUnit().getConverterToMetric().inverse();
		return targetConverter.concatenate(this).concatenate(startConverter);
	}

	@Override
	public UnitConverter inverse() {
		UnitConverter startConverter = this.getStartUnit().getConverterToMetric().inverse();
		UnitConverter targetConverter = this.getTargetUnit().getConverterToMetric();
		return targetConverter.concatenate(this).concatenate(startConverter);
	}

	@Override
	public double convert(double value) {
		return C.doubleValue(MetricSystem.METRES_PER_SECOND) / value;
	}

	@Override
	public Number convert(Number value, MathContext ctx) {
		return convert(value.doubleValue());
	}

	@Override
	public boolean equals(Object cvtr) {
		return (cvtr instanceof FrequencyWavelengthConverter);
	}

	@Override
	public int hashCode() {
		return 0;
	}

	@Override
	public boolean isLinear() {
		return true;
	}
}

###Découverte des systèmes d’unité et des convertisseurs par SITOOLS2

Afin que les systèmes d’unité et les convertisseurs spécifiques soient découverts par SITools2, une dernière classe doit être crée puis déclarée dans un fichier Helper correspondant. Cette classe a donc pour unique but d’initialiser les nouveaux systèmes d’unités et les convertisseurs afin de les rendre disponibles.

Cette classe hérite de DimensionHelper, et consiste donc en l’enregistrement des convertisseurs d’unité (méthode registerUnitConverter(SitoolsUnitConverter)) et l’enregistrement des systèmes d’unité disponibles (méthode registerSystem(SystemOfUnit)).

Nous montrons ci-après un exemple d’Helper associé :

  • au convertisseur dont on a montré l’exemple précédemment,

  • au système d’unité dont on a montré l’exemple précédemment,

  • aux systèmes d’unité présents dans le framework.

public class SitoolsUnitConverterHelper extends DimensionHelper {

	/**                                                              
	 * Constructor
	 */
	public SitoolsUnitConverterHelper() {
		super();

		/**                                                            
		 * Registering all converters and all systems here
		 */

		this.registerUnitConverter(new FrequencyWavelengthConverter());
		this.registerSystem(AstronomicSystem.getInstance());
		this.registerSystem(MetricSystem.getInstance());
		this.registerSystem(USCustomarySystem.getInstance());
	}
}    

Pour finir, cette classe doit être répertoriée – comme pour les autres plugins – dans un fichier nommé fr.cnes.sitools.units.UnitsHelper, présent dans le répertoire META-INF/services. Ce référencement consiste uniquement à la présente du nom de la classe dans le fichier :

Contenu de fr.cnes.sitools.units.UnitsHelper

fr.cnes.sitools.units.helper.SitoolsUnitConverterHelper

Gestion des logs

Il existe plusieurs méthodes pour récupérer un Logger dans Sitools2.

Via la classe Application :

Application application = getApplication();
Logger logger = application.getLogger();

Directement via la classe Logger

Logger logger = Logger.getLoggernom de la classe courante »);

Via le Context :

Logger logger = Context.getLogger();

Ensuite pour utiliser le Logger :

logger.log(Level.INFO, "Message to log");