Skip to content

WebDav

Introduction

Ewt intègre un servlet WebDav au travers duquel une application Ewt peut offrir des fonctionnalités WebDav, notamment :

  • la navigation au sein d'un système de fichier
  • l'édition inline de documents (l'édition inline permet d'éditer un document via un client local (Word, LibreOffice, etc.) sans nécessité de download/upload manuel)

Principe de fonctionnement

Le servlet WebDav en lui-même n'intègre aucune logique métier. Il sert uniquement de relai entre un client WebDav et un script Ewt chargé de traiter la requête. De ce point de vue, le servlet WebDav de Ewt fonctionne de façon analogue au servlet Rest : il reçoit une requête, analyse l'URL pour en extraire le nom d'application, le nom de script et les éventuels éléments supplémentaires. Il passe ensuite ces éléments à un script chargé de faire le traitement, puis construit une réponse conforme à la spécification WebDav. L'analogie avec Rest s'arrête là car l'implémentation d'un script Ewt destiné à WebDav est très différente de celle d'un script destiné à Rest.

En effet, le script invoqué par WebDav doit respecter certaines règles afin d'implémenter la logique d'un magasin de données WebDav. On appelle ici "magasin de données" la couche qui se charge de traiter les commandes passées via le servlet WebDav (p.ex: "récupérer la ressource X", "créer un dossier", "énumérer le contenu de Y", etc.). Ces commandes sont implémentées sous la forme de fonctions dans un script Ewt.

flowchart LR
    C[Client] <-->|request| A[WebDav Servlet]
    A <--> S[Script]
    S <--> F[Database]

Voici un exemple fonction implémentant une méthode d'un magasin de donnée :

1
2
3
4
@webdav(method = "getStoredObject")
function fetch(uri) {
    return $file.load(uri);
}

Toutes les fonctions du magasin de données doivent satisfaire deux conditions:

  1. Elles doivent se trouver à la racine du script
  2. Elles doivent être annotées avec l'annotation @webdav

La fonction en soi peut avoir n'importe quel nom. C'est l'annotation @webdav qui indique à Ewt quel rôle (où plutôt quelle méthode) joue la fonction dans le magasin de donnée implémenté par le script. L'annotation doit avoir la forme suivante:

@webdav(method = NOM_COMMANDE [ , independent = BOOLEEN ])

Une même fonction peut jouer plusieurs rôles et peut par conséquent recevoir plusieurs annotations. Un exemple est donné plus bas.

L'annotation attend obligatoirement un paramètre method qui indique le nom de la méthode qu'implémente la fonction. Les noms de méthodes reconnues sont donnés plus bas. La valeur NOM_COMMANDE est une chaîne de caractères délimitée par ", ' ou `.

Un second paramètre independent est optionnel et vaut false par défaut. Il indique au servlet WebDav comment la fonction doit être invoquée. Nous revenons plus bas sur ce paramètre.

L'exemple donnée plus haut montre une fonction implémentant la méthode getStoredObject. Cette méthode a pour rôle de fournir au servlet des informations concernant une ressource. Ainsi l'annotation permet d'attribuer un rôle (une méthode) à une fonction du script.

L'annotation @webdav permet de déclarer les méthodes suivantes:

Servlet

Le servlet ch.epilogic.ewt.webdav.WebdavServlet doit être déclaré et mappé dans le fichier web.xml de la webapp Ewt. Le servlet reconnaît un certain nombre de paramètres d'initialisation:

enabled-methods

Liste des méthodes WebDav HTTP acceptées par le servlet. Les valeurs doivent être séparées par une virgule, sans espace.

Les méthodes reconnues sont les méthodes standard de la norme WebDav:

GET,HEAD,DELETE,COPY,LOCK,UNLOCK,MOVE,MKCOL,OPTIONS,PUT,PROPFIND,PROPPATCH

Si le paramètre n'est pas spécifié ou s'il vaut "*", toutes les méthodes sont activées.

Notez que certaines méthodes dépendent des autres. Par exemple la méthode MOVE dépend de COPY et DELETE. La méthode COPY dépend de DELETE. Certaines fonctionnalités nécessitent plusieurs méthodes. L'édition en ligne par exemple nécessite les méthodes OPTIONS,PROPFIND, HEAD,GET, LOCK,UNLOCK et PUT`.

instead-of-404 URI d'une ressource à retourner à la place de la ressource demandée lorsque cette dernière n'exite pas. Si non définie, le servlet retourne la page 404 standard.

no-content-length-headers Valeur du header Content-Length à retourner dans le cas où une ressource demandée n'existe pas. La valeur par défaut est -1.

lazy-folder-creation-on-put Flag true/false indiquant si le dépôt d'une ressource dans un dossier qui n'existe pas via la méthode PUT est autorisé à créer automatiquement le dossier en question.

La valeur par défaut est `false`.

Paramètre independent

Par défaut le paramètre independent est false. Dans ce cas, lorsque le servlet doit invoquer une méthode, il procède en deux temps :

  1. Il évalue tout le script : cette étape a pour but d'évaluer toutes les variables ou déclarations de fonctions qu'offre le script. Le servlet est obligé de passer par cette étape pour construire un scope qui sera réutilisé par la méthode.
  2. Il invoque la méthode dans le scope construit à l'étape précédente

Lorsque le paramètre independent est activé, cela indique au servlet que la fonction peut être invoquée indépendamment du reste du script, c'est-à-dire sans tenir compte des éventuelles autres fonctions ou déclarations de variables. Dans ce cas, l'étape 1 mentionnée ci-dessus est ignorée. L'étape 2 est alors réalisé avec un scope vide.

En clair, lorsqu'une fonction est annotée avec le paramètre independent = true, cela indique au servlet qu'il peut invoquer la méthode, et seulement la méthode. Le fait de déclarer une fonction indépendante offre deux avantages:

  • une sécurité accrue : seule la méthode est invoquée et non le reste
  • une meilleure performance : le script n'a pas besoin d'être intégralement évalué, seule la fonction l'est

En contrepartie, une fonction indépendante ne peut pas bénéficier des variables ou fonctions voisines. Le fait d'y faire référence provoquerait une erreur à l'exécution. Pour illustrer cela, prenons l'exemple ci-dessous.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@webdav(method = "getChildrenNames", independent = true)
function getChildrenNames(uri) {
    var res = [];

    for (var file : $file.list(uri)) {
        res += $file.getName(file);
    }

    return res;
}

@webdav(method = "getStoredObject", independent = true)
@webdav(method = "getResourceContent", independent = true)
function getObject(uri) {
    return $file.load(uri);
}

$logger.info("Hello World!");

Ici toutes les fonctions sont déclarées independent. Lorsque le servlet WebDav doit invoquer l'une des méthodes ci-dessus, il ignorera tout le reste du script. Ainsi la trace de fin de script ne sera jamais affichée.

Cela a une incidence très importante sur le fonctionnement des méthodes WebDav implémentées au sein d'un script Ewt et marquées comme indépendantes. Ces méthodes n'ont pas connaissance du scope qui les entoure. Pour comprendre cela, prenons un nouvel exemple:

1
2
3
4
5
6
7
8
9
function foo() {
    $logger.info("foo");
}

@webdav(method = "checkAuthentication", independent = true)
function bar() {
    foo();  // provoque une erreur au runtime
    return true;
}

Ici la fonction bar est déclarée comme méthode WebDav indépendante implémentant checkAuthentication. La fonction foo est une fonction quelconque invoquée par bar.

À l'exécution, le code ci-dessus provoquera une erreur car la fonction foo n'existe pas (elle ne figure pas dans scope) au moment où bar est invoquée. Cela vient du fait que le servlet invoque la méthode bar indépendamment du scope qui l'entoure.

Fonction inline

Comment faire alors si l'on souhaite conserver une méthode indépendante, mais structurer le code et utiliser des méthodes ? La solution est de déclarer la fonction inline :

1
2
3
4
5
6
7
8
9
@webdav(method = "checkAuthentication", independent = true)
function bar() {
    var foo = function() {
        $logger.info("foo");
    };

    foo();
    return true;
}