P3 A new story begins

Aller au contenu | Aller au menu | Aller à la recherche

mercredi 15 décembre 2010

De l'interêt de Sonar

Mais pourquoi utiliser Sonar ??

Tout simplement parce que la qualité ne dépend pas des développeurs mais des priorités du moment.

Voici un exemple pour une petite application développé par un seul développeur (en l'occurrence c'est moi qui m'y suis collé)

Avant le premier commit, revue de code et Hop un bon 99%

Puis dans l'urgence modification du code pour ajouter des fonctionnalités non prévues et bam ... le nombre de lignes a bien augmenté mais la qualité a baissée (pas tant que ça au vue des 50% de code en plus).

D'où la nécessité de refaire une revue de code et là miracle on arrive aux 100% !!

Sonar-evolution.png

Pour ce projet un seul développeur était à l'œuvre imaginez avec plusieurs dont des débutants modifiant le code d'expérimentés ...

La qualité c'est bien mais la suivre en temps réel c'est mieux

Une belle page comme celle-là ca se mérite :D

Sonar_-_Overview.png

et encore il reste des corrections, 2 lignes de codes commentées et 2copier-coller qui auraient pu être factorisé... il reste toujours du travail quand on est perfectionniste :p

mardi 30 novembre 2010

About Logging !

Un petit billet au sujet de la gestion des logs avec Java :

Si on se réfère au règles diverses (Checkstyle, PMD ...), voici la bonne façon de déclarer un logger en Java

  1. // Pour Log4J avec commons-logging
  2. private static final Log logger = LogFactory.getLog(MyClass.class);
  3.  
  4. // Pour logback avec slf4j
  5. private static final Logger logger = LoggerFactory.getLogger(MyClass.class);

...Hein.. c'est quoi logback ???

Logback est le digne hériter de log4j, il a été conçu en même que les api slf4j par le créateur lui même de log4j: Ceki Gülcü

Il y a de nombreuses raisons d'utiliser logback, et elles sont toutes expliquées sur le site http://logback.qos.ch/reasonsToSwitch.html

Le seul défaut est que vous perdrez le niveau fatal, mais bon toutes les erreurs ne devraient-elles pas êtres évitées, et un niveau fatal devraient entrainer l'arrêt de l'application ??

Donc switchons tous ensemble :D

Revenons à nos moutons si on veut logger des évènements qui ont lieu dans une classe abstraite commune la bonne solution est de déclarer le logger protégé et de l'initialiser de manière statique.

  1. protected transient final Logger logger = LoggerFactory.getLogger(this.getClass());

Toutefois cette déclaration n'est pas compatible avec une règle (qui à dit 'à la con') de PMD à savoir LoggerIsNotStaticFinal

Certains puristes veulent que la classe Abstraite et la classe fille ai chacune leur propre logger pour permettre de les filtrés différemment avec le fichier de configuration du gestionnaire de log. (cf ce billet )

Personnellement j'ai jamais voulu séparer les logs des méthodes abstraites hérités de celles déclarées dans la classe fille, surtout quand la pile d'appel est imbriqué et que plusieurs classes filles peuvent être appelés.

On pourrait ajouter l'information getClass() dans chaque log de la classe abstraite, mais c'est quand même relativement pénible

Donc comment faire ??

Une des solutions peut-être d'utiliser AOP => en bon français la Programmation par aspect (soit en bon anglais Aspect Oriented Programmation à opposer à la OOP)

Avec Spring AOP par exemple on peut ajouter des logs sur des points de coupe définissant des endroits où logger.

Voici la déclaration via le conteneur léger de spring (spring-beans):

  1. <bean id="loggingAdvice" class="fr.webeo.rapidoo.aop.LoggingAdvice" />
  2.  
  3. <bean id="loggingAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
  4. <property name="advice" ref="loggingAdvice" />
  5. <property name="pattern" value="fr.webeo.rapidoo.services" />
  6. <property name="order" value="50" />
  7. </bean>

A vous de spécialiser votre point de coupe si vous voulez plus de précision, ici toutes les méthodes de toutes les classes du package fr.webeo.rapidoo.services seront traitées à la même enseigne !

Et l'advice qui va logger tout seul les classes filles

  1. public class LoggingAdvice implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice {
  2.  
  3. /**
  4.   * Default Constructor.
  5.   */
  6. public LoggingAdvice() {
  7. // Nothing to do
  8. }
  9.  
  10. /**
  11.   * {@inheritDoc}
  12.   */
  13. @Override
  14. public void before(final Method method, final Object[] args, final Object target) throws Throwable {
  15. final Logger logger = LoggerFactory.getLogger(target.getClass());
  16. if (logger.isInfoEnabled()) {
  17. logger.info("Calling : " + method.getName() + " " + target.getClass().getName());
  18. }
  19. if (logger.isDebugEnabled()) {
  20. final StringBuilder sb = new StringBuilder();
  21. sb.append("Parameters : ");
  22. if (method.getParameterTypes().length == args.length) {
  23. for (int i = 0; i < args.length; i++) {
  24. sb.append("\r\nParam : ").append(method.getParameterTypes()[i]).append(" - ").append(args[i] == null ? "null" : args[i].toString());
  25. }
  26. }
  27. if (sb.length() > 1000) {
  28. logger.debug(sb.substring(0, 1000));
  29. } else {
  30. logger.debug(sb.toString());
  31. }
  32. }
  33. }
  34.  
  35. /**
  36.   * {@inheritDoc}
  37.   */
  38. @Override
  39. public void afterReturning(final Object returnValue, final Method method, final Object[] args, final Object target) throws Throwable {
  40. final Logger logger = LoggerFactory.getLogger(target.getClass());
  41.  
  42. if (logger.isInfoEnabled()) {
  43. logger.info("Returning : " + method.getName());
  44. }
  45. if (logger.isDebugEnabled() && returnValue != null) {
  46. if (returnValue instanceof List) {
  47. logger.debug("Return value is list of " + ((List<?>) returnValue).size() + " elements.");
  48. } else {
  49. logger.debug("Return Value = " + returnValue.toString());
  50. }
  51. }
  52. }
  53.  
  54. /**
  55.   * Call after an exception thrown.
  56.   *
  57.   * @param method the method called
  58.   * @param args the method arguments
  59.   * @param target the object who call the method
  60.   * @param exception the exception thrown
  61.   */
  62. public void afterThrowing(final Method method, final Object[] args, final Object target, final Throwable exception) {
  63. final Logger logger = LoggerFactory.getLogger(target.getClass());
  64. if (logger.isErrorEnabled()) {
  65. logger.error("Throwing : " + method.getName() + " \r\n=> " + exception.getMessage(), exception);
  66. }
  67. }
  68. }

Pour conclure il existe 3 possibilités pour logger des classes filles héritant d'une même classe abstraite :

  • Un logger par classe

Avantages : Chaque ligne de la log renvoi vers la bonne classe physique

Défauts : les log contiendront des log par enfant et pour la classe mère, donc si quelque chose se passe mal dans la classe abstraite il faut espérer trouver à proximité une log indiquant quel est la classe fille concernée (ou bien la rajouter explicitement dans la ligne loggée)

  • Déclaration du logger de la classe abstraite en protected non static

Avantages : Toutes les logs seront générés au nom de la classe fille

Défauts : les numéros de lignes des méthodes héritées concerneront la classe abstraite

  • Déclaration des loggers via AOP :

Avantages : Déclaration très rapide, bonne personnalisation

Defauts : Seulement utile pour les log génériques, induit donc une bonne conception objet avec des méthodes courtes et atomiques
Et vous comment faîtes-vous ??

mercredi 10 février 2010

Gestion des Map en EMF dans un ecore

Voici le contenu du code source d'un fichier exemple ou une Map est déclaré :

Voici une petite capture de l'éditeur ecore d'Eclipse pour plus de clarté :

Visualisation du fichier ecore avec gestion de map dans l'éditeur d'Eclipse

Et le code du fichier ecore pour plus d'exhaustivité...

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <ecore:EPackage xmi:version="2.0"
  3. xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="fr">
  5. <eSubpackages name="webeo">
  6. <eSubpackages name="blog">
  7. <eClassifiers xsi:type="ecore:EClass" name="CarOwner">
  8. <eStructuralFeatures xsi:type="ecore:EReference" name="carMap" upperBound="-1"
  9. eType="#//webeo/blog/CarMapEntry" containment="true"/>
  10. </eClassifiers>
  11. <eClassifiers xsi:type="ecore:EClass" name="Car"/>
  12. <eClassifiers xsi:type="ecore:EClass" name="CarMapEntry" instanceClassName="java.util.Map$Entry">
  13. <eStructuralFeatures xsi:type="ecore:EAttribute" name="key" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
  14. <eStructuralFeatures xsi:type="ecore:EReference" name="value" lowerBound="1"
  15. eType="#//webeo/blog/Car" containment="true"/>
  16. </eClassifiers>
  17. </eSubpackages>
  18. </eSubpackages>
  19. </ecore:EPackage>

Et pour terminer le fichier ecore :D

Fichier Ecore avec Gestion de Map

dimanche 1 juin 2008

Nostalgie : TeamBuilder

Ce petit 3P a été développé rapidement pour pouvoir créer de manière aléatoire des équipes pour tous les jeux envisageables (football, jorkyball ....).

Le projet est disponible à cette adresse teambuilder.webeo.fr

TODO à finir comme le 3P d'ailleurs .....

Nostalgie : Webeo ToolBox

Tout le monde connait la pratique de rétro-spécification très utilisé par les société qui ont développé un logiciel complétement buggé et qui ne comprennent pas pourquoi une telle injustice a peu leur arrivé ....

Et bien aujourd'hui je suis un peu nostalgique et je vais faire du rétroblog, c'est à dire publier un billet que j'aurai pu écrire il y a quelques mois si le blog avait existé....

Pour ce petit moment de nostalgie je vais vous parler de Webeo Toolbox :

Anciennement ce 3P se nommait ESCBuilder (EcmaScript Component Builder) puis Emukilook Builder.

Ce petit logiciel réalisé en Java/Swing avait pour but ambitieux d'être un IDE dédié à la génération de code javascript ou plutôt ecmascript modulaire et robuste.

Son principal interêt a été de me faire réflechir sur les documents de référence EcmaScript 262 et 290, afin de coder du javascript différemment : ce qui aboutit aux composants web webeo qui feront l'objet d'une autre billet.

Le deuxième intérêt a été de pouvoir réaliser une petite application totalement autonome et portable en Java/Swing. J'ai ainsi pu laisser de côté les écrans buggés de mon travail quotidien pour des questions existentielles sur la création d'un mini framework... vaste sujet s'il en est.

Durant ce 3P, j'ai pu améliorer mes connaissances dans les domaines suivants :

  1. Swing
  2. L&F java (substance)
  3. Ant
  4. NSIS (et Inno Setup)
  5. Launch4J
  6. Transformations XSL (XSLT et XSL-FO)
  7. et un peu de photoshop

Le résultat : est disponible à cette adresse : toolbox.webeo.fr

Des contraintes techniques nécessitant plus de temps que je n'en disposais m'ont empêcher de conclure ce 3P, j'aurai aimé approfondir les points suivant :

  1. faire des feuilles de style XSLT et XSL-Fo plus jolies
  2. réussir à mettre à jour le code déjà écrit si la structure du composant change
  3. proposer un éditeur javascript avec colorisation syntaxique et complétion
  4. Finir le module FTP
  5. porter le tout en Eclipse RCP (pour réutiliser des plugins javascript existants)

Le travail est inachevé car un 3P en pousse toujours un autre, c'est une chaîne sans fin ...