jeudi 17 mars 2016

L'annotation @PropertySource de Spring et les JAR autonomes

De plus en plus, au lieu d'utiliser un serveur JEE (JBoss, Glassfish) ou un conteneur de servlet (Tomcat), un JAR autonome (fatjar ou uberjar ou singlejar) est utilisé.
Nombreux embarque Jetty, Tomcat ou Winstone.
Toutefois, les alternatives comme Ratpack ou Vert.X (sans oublier Dropwizard ou SpringBoot) change la donne.
Et c'est dans ce context, que le piège arrive.

En effet, le(s) fichier(s) de properties sont externalisés et mit dans le classpath via l'option -cp ou -classpath.
Sauf que ça ne fonctionne pas.
Avec Spring, si vous utilisez l'annotation @PropertySource et new AnnotationConfigApplicationContext(...), le fichier n'est pas trouvé dans le classpath. Pourquoi ?

Si vous avez déjà travaillé avec serveur JEE ou Tomcat, la notion de Parent First et Parent Last vous dises quelque chose.

L'annotation @PropertySource cherche dans le bootclasspath alors que l'option -cp ou -classpath attache dans le classpath enfant.

De ce fait, il faut indiquer le fichier non dans le classpath, mais dans le boot classpath :
-Xbootclasspath/a:...../properties/

Attention, utilisez bien le paramètre /a, pour ajouter votre path au boot classpath.

samedi 13 février 2016

Event Sourcing, avantages et inconvénients

L'Event Sourcing est le fait de ne pas stocker une donnée brute, mais de stocker les évènements qui composent cette donnée.
En clair, un objet commande, avec 3 articles, ne sera pas stocké de cette façon :
commande : {
  articles : [
    {
      nom : "toto"
    },
    {
      nom : "tata"
    },
    {
      nom : "titi"
    }
  ]
}
mais :
command : [
  {
    id : 1,
    action : "add",
    articles : {
      nom : "toto"
    },
    date : 2012-04-23T18:25:43.511Z
  },
  {
    id : 1,
    action : "add",
    articles : {
      nom : "tata"
    },
    date : 2012-04-24T18:25:43.511Z
  },
  {
    id : 1,
    action : "add",
    articles : {
      nom : "titi"
    },
    date : 2012-04-24T21:25:43.511Z
  }
]

Cela signifie que pour obtenir l'objet complet, ici la commande, il fait agréger tous les évènements.

L'avantage de ce principe, c'est qu'il peut y avoir plusieurs "écrivains" en même temps.
Il est aussi possible de reconstituer l'objet dans un état antérieur ou de connaitre l'historique.

Un des inconvénients qui vient tout de suite en tête est la reconstitution d'un objet avec un grand nombre d'événements, qui peut de ce fait prendre beaucoup de temps.
Il y a aussi un autre inconvénient auquel on ne pense pas tout de suite, c'est le modèle de données.

En effet, si le modèle évolue à l'ajout, il faudra prévoir que la donnée n'est pas présente dans les anciens évènements et si une donnée est supprimée, il faudra la prendre en compte dans les anciens évènements car elle avait un sens.

N'oublions pas non plus, l'évolution d'une donnée.
Une donnée facultative qui devient obligatoire, ou un typage de donnée qui change.

mercredi 13 janvier 2016

Implémenter une popover avec Angular Material

Pour le besoin d'un projet, je souhaitais utiliser une popover.
Malheureusement, Angular Material (1.0.1) ne propose pas encore cette directive qui est assez demandée (voir https://github.com/angular/material/issues/1217).

Je vous propose donc de créer un popover simple.
Pour cela, nous regardons sur Internet si quelqu'un a déjà créé un popover en CSS.
J'ai trouvé ce code assez simple : http://codepen.io/derekpcollins/pen/JCLhG.

Voici le code CSS une fois compilé :
.popover {
    background-color: rgba(0, 0, 0, 0.85);
    border-radius: 5px;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.4);
    color: #fff;
    visibility: hidden;
    font-size: 12px;
    font-family: 'Helvetica',sans-serif;
    padding: 7px 10px;
    position: absolute;
    z-index: 4;
}

.popover:before {
    border-top: 7px solid rgba(0, 0, 0, 0.85);
    border-right: 7px solid transparent;
    border-left: 7px solid transparent;
    bottom: -7px;
    content: '';
    display: block;
    left: 50%;
    margin-left: -7px;
    position: absolute;
}

.popover-hover {
    visibility: visible;
}

@-webkit-keyframes fade-in {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}
@-moz-keyframes fade-in {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}
@-ms-keyframes fade-in {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}
@-webkit-keyframes move-up {
    from {
        bottom: 30px;
    }
    to {
        bottom: 42px;
    }
}
@-moz-keyframes move-up {
    from {
        bottom: 30px;
    }
    to {
        bottom: 42px;
    }
}
@-ms-keyframes move-up {
    from {
        bottom: 30px;
    }
    to {
        bottom: 42px;
    }
}
@-webkit-keyframes fade-in {
    from   { opacity: 0; }
    to { opacity: 1; }
}
@-moz-keyframes fade-in {
    from   { opacity: 0; }
    to { opacity: 1; }
}
@-ms-keyframes fade-in {
    from   { opacity: 0; }
    to { opacity: 1; }
}
@-webkit-keyframes move-up {
    from   { bottom: 30px; }
    to { bottom: 42px; }
}
@-moz-keyframes move-up {
    from   { bottom: 30px; }
    to { bottom: 42px; }
}
@-ms-keyframes move-up {
    from   { bottom: 30px; }
    to { bottom: 42px; }
}

A présent, il nous faut créer la directive Angular JS.
Notre but, avoir un <md-popover>Bonjour !</md-popover> qui peut contenir du code HTML.

Pour cela, la directive se met à l'intérieur de l'élément HTML qui doit faire apparaître la popover.
La directive va devoir :
  1. Ajouter les événements souris via parent.on(...) pour faire apparaître/disparaître la popover,
  2. Avoir de faire apparaître la popover, il va falloir la positionner correctement ! C'est à dire centrer sur l'élément parent (on aurait pu centrer sur la souris),
  3. Mémoriser la popover affichée. En effet, on souhaite qu'il n'y ait qu'une popover d'affichée à la fois.

Pour ce qui est de calculer la position de la popover, on a recours au service $mdUtil.
En effet, Angular JS utilise par défaut JQLite qui ne permet pas de récupérer comme JQuery les informations dont nous avons besoin.

Pour pouvoir utiliser du code HTML, nous utilisons l'option translude.
On choisit aussi de remplacer à la compilation la balise mdPopover par le code HTML du template via l'option replace.

Le code de la directive :
/**
 * Popover button.
 *
 * Partial code from Material Design mdTooltip.
 */
hesperidesModule.directive('mdPopover', function ($mdUtil, $mdPopoverService) {
    return {
        restrict: 'E',
        scope: true,
        transclude: true,
        replace: true,
        template: '
', link: function (scope, element) { var parent = element.parent(); var popover = element; // Display popup parent.on('mouseenter', function() { var tooltipParent = angular.element(document.body); var tipRect = $mdUtil.offsetRect(popover, tooltipParent); var parentRect = $mdUtil.offsetRect(parent, tooltipParent); var newPosition = { left: parentRect.left + parentRect.width / 2 - tipRect.width / 2, top: parentRect.top - tipRect.height }; popover.css({ left: newPosition.left + 'px', top: newPosition.top + 'px' }); if ($propertyToolButtonService.currentPopup) { $propertyToolButtonService.currentPopup.removeClass('popover-hover'); } popover.addClass('popover-hover'); $propertyToolButtonService.currentPopup = popover; }); // Hide popup popover.on('mouseleave', function() { popover.removeClass('popover-hover'); $propertyToolButtonService.currentPopup = null; }); } }; });

Le code du service :
/**
 * Popover button.
 *
 * Service to remember current popover display.
 * Service is singleton.
 */
hesperidesModule.factory('$mdPopoverService', [function(){
    return { currentPopup: null };
}]);