Twister's blog

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

mercredi, octobre 8 2014

Log4j et SocketAppender

J'ai récemment (bon ok en juin...) pluggé nos applications J2EE sur un serveur LogStash/ElasticSearch afin de pouvoir facilement consulter tous les logs de toutes nos plateformes. Pour ca j'ai utilisé le SocketAppender de Log4j. C'était merveilleux, jusqu'au jour où...

Lire la suite


mardi, mai 6 2014

Alt-Grab

Je viens juste de découvrir ce petit outil pour Windows : Alt Grab. Il permet de pouvoir déplacer les fenêtre sous windows en utilisant Alt-Clic. Ca fait du bien un peu plus de Linux dans son Windows ;).

J'en profite pour conseiller d'autres petit outils que j'utilise au quotidien sous Windows pour avoir la vie plus facile :

Promis je vous partage bientôt ma conf autohotkey pour bien piloter son Spotify ;)


mercredi, mars 19 2014

Callbacks

Cela fait déjà une bonne dizaine de fois que je peste pour la même chose sur mon projet actuel : le chaînage des traitements dans tous les sens avec des fils de partout. Un schema très récurrent est "j'ai besoin de faire quelque chose après un traitement générique qui tourne dans un thread, mais cette chose dépend de paramètres qui ne sont jamais les mêmes suivant de où j'appelle mon traitement. Bon bah je vais rajouter pleins de paramètres à la méthode de mon traitement et me les traîner sur toute ma stack pour pouvoir faire ce qu'il faut à la fin". NOON ! Il faut arrêter les conneries. Si vous avez un traitement qui fonctionne gardez le comme ça, ATOMIQUE. Si les traitements qui suivent sont différents en fonction des appelants utilisez des Callbacks !

Un petit (mauvais) exemple :

public class Treatment {
    public static void doMyTreatment(int value1, int value2, int parameter1, int parameter2) {
        new Thread(new Runnable() {
            public void run() {
                /// DO a realy long treatment...
                /// very long...
                int res = value1 + value2;

                if (parameter1 == 0) {
                    doSomthg();
                } else if (parameter1 == 1) {
                    if (parameter2 == 0){
                        goSomewhere();
                } else {
                        cook();
                }
            }
        } 
    }
}

public class Caller1 {
    public void call() {
        Treatment.doMyTreatment(1, 2, 0, 0);
    }
}

public class Caller2 {
    public void call() {
        Treatment.doMyTreatment(1, 2, 1, 1);
    }
}

Ici vous avez les paramètres 1 et 2 qui vont influencer le "quoi" faire après le traitement des valeurs 1 et 2... C'est mal, ça va vite devenir une grosse pelote de laine au fil des ajouts de fonctionnalités. Alors que si vous aviez fait :

public class Treatment {
    public static void doMyTreatment(int value1, int value2, Runnable callback) {
        new Thread(new Runnable() {
            public void run() {
                /// DO a realy long treatment...
                /// very long...
                int res = value1 + value2;

                callback.run();
            }
        } 
    }
}

public class Caller1 {
    public void call() {
        Treatment.doMyTreatment(1, 2, new Runnable() {
            public void run() {
                StaticClass.doSomthg();
            }
        }
    }
}

public class Caller2 {
    public void call() {
        Treatment.doMyTreatment(1, 2, new Runnable() {
            public void run() {
                StaticClass.cook();
            }
        }
    }
}

C'est quand même plus propre. Bon là j'utilise de simples Runnable en callbacks, du coup je n'ai pas possibilité de passé des paramètres à ma méthode run mais ce n'est pas compliqué de définir votre interface perso à la place de Runnable.

Autre point, vous pouvez remplacer la callback par une List pour être capable de chaîner plusieurs traitements.

Et voilà...

dimanche, octobre 6 2013

Vera & Secure SSR-302

Je me suis récemment lancé dans la domotique. Pour commencé j'ai acquis une Vera Lite, un actionneur Secure SSR-302 et un switch Fibaro chez Delta Domotique (que je recommande au passage).

Mise à part de gros problèmes d'ergonomie sur la Vera, tout se passait plutôt pas mal jusqu'à ce que je réalise que la chaudière passe sa vie à s'éteindre. Au bout de quelques allumages je constate qu'elle s'éteint dans l'heure qui suit son allumage. Depuis son installation, le boitier SSR-302 a toujours clignoté en orange, ce qui veut dire qu'il est en fail-safe, j'avoue que comme ça avait l'air de marché je ne m'étais pas trop creusé (:p). Bref finalement, le froid arrivant et la chaudière ne marchant pas si bien que ça, j'ai creusé. Il se trouve que SSR-302 coupe tout une heure après le dernier signal reçu à cause du fameux mode fail-safe (comme quoi j'aurais du creuser tout de suite :p). Ainsi si le contrôleur tombe en rade, au lieu de laisser tout allumer sans contrôle, le SSR-302 coupe tout. Pas si bête à mon sens, mais galère à gérer... En effet lorsque l'on a le thermostat qui va bien c'est lui qui se charge d'envoyer le signal... Malheureusement je m'étais dis que ce serait pour plus tard (ça m'avait déjà couté assez cher :p).

Finalement ma mésaventure tombe plutôt bien puisque ça me démangeait depuis le début de regarder ce que l'on pouvait faire avec la Vera. Du coup ni une ni deux j'ai pris mon notepad et j'ai fais mon premier script LUUP qui se charge d'envoyer un signal au SSR-302 toutes les 45 minutes. Pour ce faire il faut commencer par créer une scène programmée pour s’exécuter toutes les 45 minutes. Cette scène ne fera rien d'autre que d'envoyer un signal d'allumage au SSR-302 s'il est déjà allumé. Pour cela quelques lignes suffisent :

local deviceNo = 13
local SSID = "urn:upnp-org:serviceId:HVAC_UserOperatingMode1"
local actualMode = luup.variable_get(SSID, "ModeTarget", deviceNo )

if (actualMode == "HeatOn") then
    luup.call_action(SSID,
                 "SetModeTarget", {NewModeTarget = "HeatOn"},
                 deviceNo )
end

return true
Et voilà, il vous suffit de remplacer le deviceNo par le numéro de device de votre SSR-302 et le tour est joué!

vendredi, août 2 2013

Lecteur RSS [suite]

Après la mort de Google Reader je pensais avoir trouvé plusieurs remplaçants dont un qui me convenait vraiment : The old reader. Malheureusement suite à un crash majeur de leurs serveurs ils ont décidé de jeter l'éponge et de passer à un mode de site privé pour quelques élus... Résultat je me re-retrouve au point de départ, plus rien pour lire mes flux. Sur les lecteurs existant qu'il me reste il y a donc :

  • Feedly buggant toujours pas mal et étant un poil lourd de temps en temps
  • Newsblur, payant à partir de 64 flux.

N'ayant que 50 flux, je partait donc sur Newsblur. Tout se passa bien pendant une semaine. Mais qu'elle ne fut pas ma surprise de découvrir ce matin une nouvelle limitation à Newsblur : on ne peut plus afficher que 8 articles non lus par flux en version gratuite ! Exit donc Newsblur.

J'ai donc décidé de redonner leur chance au petits lecteurs RSS installable sur un serveur. J'ai commencé par essayer d'installer Newsblur (oui c'est possible puisque le code est sur GitHub). Mais ce truc est une vrai usine à gaz ! Impossible de l'installer facilement... Poubelle!

Finalement après avoir réessayé tout ce qui trainait j'ai fini par installer Tiny-Tiny-RSS qui a en plus sa propre interface Android! Je vais donc voir ce que ça donne à terme, en espérant ne plus avoir à bouger de si tôt :p.

Pour ceux qui le voudrait mon install est ici : http://rss.perriot.fr/. Si vous voulez un compte n'hésitez pas à me faire signe.


mercredi, juillet 24 2013

Lire le contenu de fichiers dans un fichier zip en JAVA

Je viens de tomber sur un problème bête en Java, j'ai une application client/serveur avec un client qui envoie régulièrement des fichiers de logs au serveur sous forme de fichier ZIP. Le serveur doit décompresser les fichiers afin de la analyser pour en tirer des statistiques. Ce qui m'embêtait dans ce développement c'est qu'il faut dézipper les fichiers avant de pouvoir les analyser et que ce "dézippage" pourrait éventuellement laisser trainer des fichiers temporaires quelque part sur le serveur. Il me fallait donc une autre solution. J'en voyais 2 :

  • soit concatener les fichiers de logs en un seul fichier qu'il aurait suffit de gzipper pour pouvoir le lire avec un GZIPInputStream côté serveur.
  • soit permettre au serveur de lire de manière transparente les données de tous les fichiers contenus dans le zip.
La première solution aurait impliqué de faire des modifications sur le client qui utilise un système déjà existant (et standard pour notre application) pour envoyer ses fichiers ZIP, donc pas top. La seconde pose problème parce que le JDK ne fournit qu'un ZipInputStream qui permet de lire le Zip fichier par fichier, il faut en effet faire un appel à ZipInputStream.getNextEntry() entre chaque fichier pour passer à la suite. Comme l'ordre de lecture des fichiers n'a pas d'importance je décidais de faire un petit wrapper au stream afin qu'il soit capable de passer tout seul d'un fichier à l'autre. Et voilà le travail :

public static Reader acquireReader(File file) throws IOException {
       if (file.getName().endsWith(".zip")) {
           final ZipInputStream zis = new ZipInputStream(new FileInputStream(file));
           return new InputStreamReader(zis) {
               @Override
               public int read(char cbuf, int offset, int length) throws IOException {
                   int read = super.read(cbuf, offset, length);
                   if (read < length) {
                       if (read == -1) {
                           read = 0;
                       }
                       zis.getNextEntry();
                       read += super.read(cbuf, offset + read, length - read);
                   }
                   return read;
               }

               @Override
               public int read() throws IOException {
                   int c = super.read();
                   if (c == -1) {
                       zis.getNextEntry();
                       c = super.read();
                   }
                   return c;
               }
           };
       }
       return new FileReader(file);
   }

Comme vous pouvez le voire c'est relativement bateau :).


vendredi, juin 28 2013

Google Reader [RIP]

Vous le savez forcément, Google Reader tirera le rideau le 1er juillet 2013. C'est une page de l'histoire d'Internet qui se tourne. En effet depuis son lancement en 2005 aucun autre Reader n'aura jamais attiré autant de monde. Aucun autre n'est aussi simple, efficace et ne met à jour les flux aussi rapidement que Google Reader.

Depuis l'annonce faite par Google je me suis intéressé de près à tout ce que je pouvais trouver comme remplaçant. Que ce soit une application à installer sur un serveur ou un service clé en main en ligne je n'ai pas encore trouvé chaussure à mon pied. QUOI! me direz vous, et bien oui, je suis chiant et exigeant. Je veux tout comme Google Reader, tout de suite, et malheureusement ça n'existe pas.

Cependant, depuis quelques mois beaucoup de sociétés se sont mise en tête de devenir le nouveau Google Reader. Pas étonnant vus la masse d'utilisateur qui vont se retrouver sans foyer au 1er juillet il y a là un marché à conquérir. Ainsi durant les mois de mai et de juin nous avons vu de tout nouveaux reader apparaître, d'ancien évoluer rapidement, d'autre crouler sous la charge induite par tous ces nouveaux utilisateurs...

Au cours de ces derniers moi j'ai donc moi aussi fait le tour de ce qu'il existait. Je ne vous ferais pas ici un résumé complet de mes pérégrinations. Vous, comme moi, n'en avons pas le temps. Je vais donc aller droit au but :

Après 2 mois de tests divers et variés il en ressort 3 concurrents sérieux :

Et voici maintenant juste un résumé des pour/contre :

Feedly :

Pour Contre
  • Joli
  • Application Android (un peu difficile à appréhender au début, mais une fois habitué elle n'est pas si mal)
  • Mise à jour des flux relativement rapide
  • Beaucoup de vue différentes possible
  • Ne sait pas compter correctement le nombre de posts
  • Ne marque pas correctement les posts lus lors du scroll si le layout est 'serré'
  • Ne sauvegarde pas le layout par flux
  • Digg reader (beta fermée) :

    Pour Contre
  • Simple
  • (et oui c'est tout :))
  • Ne se met pas toujours à jour (sûrement des pbs sur les serveurs d'aggregation)
  • Ne sauvegarde pas le layout par flux
  • Pas d'application Android
  • Ne permet pas de caché les articles non lus
  • The Old Reader

    Pour Contre
  • Simple
  • Sauvegarde le layout par flux
  • Les flux sont mis à jour relativement rapidement
  • Pas d'application Android
  • Voilà pour un résumé rapide. Pour le moment mon choix se porte sur The Old Reader qui est le plus proche de Google Reader.

    Digg Reader est pour le moment très (trop) basique mais je pense qu'il vaut le coup de le surveiller.


    mardi, avril 23 2013

    PWGen

    J'ai découvert aujourd'hui l'extension .pw et je n'ai pas pu m'en empêcher : http://pwgen.pw. Et voilà un petit générateur de password. :-)


    vendredi, décembre 7 2012

    Arretez de recoder ce qui existe !!!

    Dans un moment d'égarement, je viens de regarder en détail une des classes de mon projet qui est très utilisée. StringHelper...

    Les 9/10è des fonctions présentes dans cette classes font les même choses que leurs homonymes dans StringUtils de Apache.commons-lang. La même chose, non, pas tout à fait, elles le font en moins bien codées...

    Je vais vous fournir ici quelques unes des gemmes de cette classe, à vous de me trouver ce qui en fait des fonctions stupides par rapport à celles de StringUtils.

    Facile :
    	public static final boolean isBlank(final String str) {
    		return str == null || str.trim().isEmpty();
    	}
    
    Dans la famille "je ne fais pas tout à fait ce que je dis que je fais" :
    	public static final int compare(final String one, final String two) {
    		if(one == two) {
    			return 0;
    		}
    		if(one != null) {
    			if(two != null) {
    				return one.toUpperCase().trim().compareTo(two.toUpperCase().trim());
    			}
    			return -1;
    		}
    		return 1;
    	}
    
    Suivie dans la classe par "je fais ce que je dis que je fais" :
    	public static final int compareIgnoreCase(
    			final String one,
    			final String two) {
    		if(one == two) {
    			return 0;
    		}
    		if(one != null) {
    			if(two != null) {
    				return one.compareToIgnoreCase(two);
    			}
    			return -1;
    		}
    		return 1;
    	}
    
    Et par "je le fais, mais je ne le fais pas vite" :
    	public static final String concat(final String...strs) {
    		if(strs != null) {
    			final int nbStr = strs.length;
    			if(nbStr > 0) {
    				final StringBuilder sb = new StringBuilder();
    				for(int index = 0; index < nbStr; ++index) {
    					sb.append(strs[index]);
    				}
    				return sb.toString();
    			}
    		}
    		return null;
    	}
    
    Et par "alors moi je suis méga pas pressé, le CPU et la RAM je m'en cogne, alors je m'éclate" :
    	public static final boolean isNumber(final String str) {
    		if(str != null) {
    			return str.matches("[0-9]*");
    		}
    		return false;
    	}
    
    Un petit peu plus compliqué :
    	public static String rightPad(String stringToPad, String padder, int size) {
    		if (padder.length() == 0) {
    			return stringToPad;
    		}
    		StringBuffer strb = new StringBuffer(stringToPad);
    		StringCharacterIterator sci = new StringCharacterIterator(padder);
    
    		while (strb.length() < size) {
    			for (char ch = sci.first(); ch != CharacterIterator.DONE; ch = sci
    					.next()) {
    				if (strb.length() < size) {
    					strb.append(String.valueOf(ch));
    				}
    			}
    		}
    		return strb.toString();
    	}
    
    Une de mes préférées :
    	public static <T> String loopAppender(
    			final Iterable<T> iterable,
    			final CollectionHelper.Stringuifier<T> stringuifier,
    			final String appender
    	) {
    		final StringBuilder sb = new StringBuilder();
    		if(iterable != null) {
    			final Iterator<T> it = iterable.iterator();
    			while(it.hasNext()) {
    				sb.append(stringuifier.stringuify(it.next())).append(appender);
    			}
    			cleanStringBuilder(sb, appender);
    		}
    		return sb.toString();
    	}
    
    	public static void cleanStringBuilder(
    			final StringBuilder sb,
    			final String appender
    	) {
    		final int finalSize= sb.length() - appender.length();
    		if (finalSize > 0) {
    			sb.setLength(finalSize);
    		}
    	}
    
    Suivi par une téléportation rapide dans NumberUtils (ou alors les int sont des String travesties ???) :
    	private static final int minimum(final int a, final int b, final int c) {
    		return Math.min(a, Math.min(b, c));
    	}
    

    Bref, arrêtez de recoder des trucs qui existent dans des jar classiques, surtout quand ces jar fournissent des méthodes bien plus efficaces que les vôtres ! Sachez reconnaître que des fonctions existantes depuis des dizaines d'années et utilisée par des dizaines de milliers de développeurs sont sûrement plus efficace que les votre! Surtout quand votre projet inclus déjà ces JARs !!! Et oui StringHelper existe alors que apache.commons-lang est dans les dépendances du projet. sic...

    Cela dit c'est peut être un autre problème qui est à la source de cette aberration : les développeurs ont peut être juste oublié de chercher sur internet s'il n'existait pas déjà quelque chose qui faisait ce qu'ils voulaient faire et du coup ont codé ces fonctions la tête dans le guidon! C'est mal!


    mardi, octobre 16 2012

    Anonymat sur internet...

    Il y a quelques années on pouvait être sûrs d'une chose : il était dur de retrouver quelqu'un sur internet. Aujourd'hui on peut être sûrs d'une autre chose : il est très aisé pour certaines personnes de vous pister sur internet !

    En effet certaines sociétés sont présentes partout sur Internet et peuvent facilement vous pister d'un site à un autre. Ces sociétés ne sont pas nombreuses et la plus à même de réaliser cette exploit n'est autre que notre cher ami Google.

    En effet si vous réfléchissez à votre manière de surfer vous commencez très souvent par aller sur Google et par taper une requête (google est même probablement votre page d'accueil?). Ensuite vous cliquer sur pleins de sites pour voir s'ils collent avec ce que vous cherchez, revenez sur google, repartez, etc... Google peut donc déjà posséder vos habitudes de recherches. Pour peu que vous ayez un compte Google (gmail, google+, ...) et que vous êtes connectés au moment de vos recherches Google peut même associer ces rechercher à un profile voir à une personne nommée ! Savez vous par exemple que Google garde l'historique de vos recherches par défaut ? Connectez vous sur https://history.google.com/ pour voir si vous ne retrouvez pas votre historique sur 10 ans ;). Vous pouvez bien sûr supprimer votre historique, mais bon qui vous prouve que c'est bien le cas ? Que ce n'est pas juste une question d'affichage ? Que Google ne garde pas les infos chez eux ? Rien.

    Après la recherche vous vous sentez peut être libres, Google et autres ne peuvent plus vous tracer ? Détrompez vous. Avez vous noté le nombres de sites que vous visitez qui affichent des pubs Google ? Et oui ça représente un point d'entrée pour Google puisque au moment où votre navigateur affiche les pubs, Google sait quelles pubs vous avez affichées et sur quel site elles ont été affichées ainsi ils savent que vous avez visiter ce site. Vous ne vous vous êtes jamais dis que c'est bizarre le nombre de pubs que vous voyez qui collent à vos habitudes, votre mode de vie ? Et oui les pubs sont contextuelles, elles s'affichent en fonction de vos recherches, de vos visites et de tout ce qui peut servir à mieux vous cibler. Aller, allez vite bloquer les pubs avec un plugin adéquat pour votre navigateur... Et installer au moins le plugin Google qui empêche l'affiche de pub contextuelles : Plugin Google.

    Certaines personnes se disent sûrement qu'il suffit de ne pas être connecté à un compte Google pour être à l'abri, qu'il suffit de couper les cookies, d'activer le mode privé du navigateur, malheureusement ce n'est pas le cas... En effet chaque personne sur internet possède une empreinte qu'il est facile de reconnaitre. La première emprunte est celle de votre navigateur, vous avez l'impression que votre navigateur n'est pas différent de celui de votre voisin d'open space ? Détrompez vous, vous avez sûrement des plugins différents, un numéro de version légèrement différent, une résolution écran différente, une liste de polices disponibles différente... tant de choses qu'un serveur web peut détecter pour vous reconnaitre. Chaque petit paramètre de votre ordinateur fait baisser l'entropie et permet de vous identifier plus facilement. Ensuite depuis l'avènement de l'ADSL de plus en plus de personnes ont une IP fixe[1] résultat c'est encore plus simple de vous identifier, aller ressortez vos modems RTC :)

    Un autre moyen méconnu par les utilisateurs est l'utilisation de cookies LSO (Local Shared Object). Ce sont des cookies Flash qui ne sont pas cleanés par le navigateur, il faut utiliser un plugin dédié. Je vous invite à lire la page Wikipédia de ces petits gateaux monstres : Wikipédia. Leur gros avantages pour les pisteurs est qu'ils peuvent être lu d'un navigateur à un autre, permettant de lier vos navigateur entre eux afin de faire baisser l'entropie et donc de mieux pouvoir vous identifier.

    Je n'irai pas dans les détails ici mais je vous conseil de lire l'article suivant (en anglais) pour vous faire un peu flipper : A Primer on Information Theory and Privacy. Après je vous recommande d'aller voir si votre navigateur est si unique que cela : Panopticlick.

    Un dernier point auquel je pense personne ne fait trop attention, c'est Google Analytics qui permet aux webmaster d'avoir des statistiques d'utilisateurs de leurs services. Google Analytics se présente sous la forme de scripts JavaScript inclus dans les pages, ainsi ce code est exécuté par chaque personne venant sur le site. Et hop une porte de plus sur votre vie privée...

    Aller bonne journée ;)

    Notes

    [1] Si vous ne savez pas ce que c'est, vous pouvez faire l'analogie avec une adresse postale, une adresse IP c'est votre identifiant unique sur internet qui permet de communiquer avec les serveurs


    Comment {ne pas} faire un flux

    J'ai récemment eu l’occasion d'implémenter l'intégration d'un flux assez conséquent : environ 4Go pour une initialisation (1 par an) puis plusieurs centaines de Mo pour les deltas hebdomadaires. Pendant les développements j'ai vu et j'ai pensé à certains trucs qu'il est bon de respecter lorsque l'on génère ou traite un si gros flux de données.

    Tout d'abord il faut choisir son format. Pour ma part c'était du XML (imposé par le producteur). Il existe une multitude de format existant lisibles par une panoplie de librairies pour à peu près tous les langages. Le choix du format est fonction de ce que vous voulez (de ce que vous avez) à faire. Pour par part il fallait un format qui puisse être "lu" par un humain pour des questions d'analyse si jamais un jour l'algorithme d'intégration du flux décidait d'intégrer les choses à l'envers... ... (et de toutes façons le choix a été fait côté producteur)

    Le XML est un format qui reste "humain", si ce n'est pas un de vos pré-requis optez pour des formats plus compacts, votre administrateur réseau vous remerciera sûrement et vous pourrez faire passer plus de données si jamais vous avec un très gros débit temps réel.

    Un fois le format choisi il faut y mettre vos données, c'est là où il faut le plus faire attention. En effet vous aller créer un flux qui sera potentiellement consommé par n'importe qui utilisant n'importe quel langage/système donc faites attentions aux formats des données et typez les le plus fortement possible si votre format le permet (ie : ne déclarez pas tout en String dans votre XSD).

    Si vous typez correctement vos données et que vous utilisez un format standard il y a de fortes chances que vous n'ayez pas à vous occuper de la sérialisation/de-sérialisation de vos données parce que la librairie que vous aurez choisie le fera très probablement à votre place (XStream en Java pour le XML par exemple). Cela à l'avantage de vous fournir à directement objets avec des champs typés et directement exploitable sans passer des heures à parser des Strings avec tous les risques que cela comporte. Cela évite donc les sorties non conformes parce que votre (dé)sérialiseur maison n'a pas lu/formaté la donnée correctement.

    On pourrait parler encore des heures des flux mais ceci est le résultat de ma dernière expérience qui aurait être plus rapide à concrétiser si tout ceci avait été pris en compte. Je n'ai donc pas tout détailler mais voici d'autres point à prendre en compte :

    • Si vous êtes producteur :
      • Evitez tous les caractères inutiles comme les retours chariots (\r), les tabulations pour mettre en forme, les espaces multiples qui ne servent à rien...
      • Offrez la possibilité de compresser votre flux (avec un algo adapté comme GZip, BZip2)
      • Offrez la possibilité de ré émettre un flux
      • Prévoyez des tonnes d'exemples pour les développeurs qui consommeront votre flux.
    • Si vous êtes consommateur :
      • Faites attention à la charge que génèrera l'intégration de votre flux (insertions en base, moteur de calcul...) et décalez peut être l'intégration à des moments de la journée où la charge est plus faible.
      • Stockez l'original du flux ! Si jamais un problème arrive pendant l'intégration vous serez content d'avoir les données. Si votre flux est temps réel il serait sûrement intéressant d'avoir un 'recorder/replayer' de flux différent du système qui consomme le flux afin de pouvoir tester/développer/debugger
      • Mesurez, re mesurez, et encore... il est important de savoir qu'elle quantité de données vous recevez, combien de temps vous mettez à les lire puis à les traiter.
      • Ne faites jamais confiance au producteur s'il vous fourni des chiffres, revérifiez les ! Le flux a pu grossir depuis sa conception et les chiffres n'ont pas été remis à jour
      • Ne prenez pas pour argent comptant les dire du producteurs sur les contraintes 'fonctionnelles', 'techniques', 'sûres', 'sur la vie d'ma mère'. S'il vous garanti qu'un champs n'est jamais null ajoutez quand même le test dans votre code, s'il vous dit qu'il y a une clé unique faites quand même la votre ou vérifiez l'unicité à chaque traitement, ... bref soyez paranoïaque...

    vendredi, juin 1 2012

    Java code quality (Sonar, PMD, findBugs, ...)

    Je viens de tomber sur quelques bugs sympa causés par un désir fou de faire baisser les niveaux d'alertes dans Sonar.

    Et oui PMD (intégré à Sonar) râle lorsque l'on fait un new Integer(1). Cela dit la solution n'est pas de remplacer new Integer(1) par 1. Et oui faire ce changement la peu provoquer des problèmes. Si le new Integer(1) était un paramètre d'appel de méthode cela peut changer la méthode appelée. Dans mon cas il est arrivé sur une modification de ce genre que l'on passe d'un appel à une méthode add(Obect o, Object constraint) à add(Object o, int i) qui n'avait pas du tout les mêmes fonctions ! Et oui elles appellent toutes les deux la méthode add(Object o, Object constraint, int index)... Vous voyez où je veux en venir ??? :-)

    Bref n'écoutez pas bêtement les outils de qualité de code ils peuvent vous faire faire des bétises, ou alors écoutez les bien. En l’occurrence sur une instanciation d'Integer PMD donne le message suivant :

    Avoid instantiating Integer objects. Call Integer.valueOf() instead.

    C'est d'ailleurs la solution sûr pour régler le warning puisque Integer.valueOf() va renvoyer un objet Integer et non un int. Son intérêt par rapport à l'instanciation brute d'un Integer et qu'il utilise un système de cache des Integers.

    J'ai pu constater plusieurs commits sur d'autres projets qui, pour faire baisser des alertes sonar, ont sûrement provoqué des bugs. Par exemple un jour j'ai vu un petit rigolo s'amuser à remplacer plusieurs :

    int a = 10;
    StringBuffer sb = new StringBuffer();
    //... bla bla ...
    sb.append(" " + a);
    return sb.toString();
    

    par

    int a = 10;
    StringBuffer sb = new StringBuffer();
    //... bla bla ...
    sb.append(' ' + a);
    return sb.toString();
    

    (Là, maintenant comme ça, à chaud, vous voyez ce que ça change ? ;-))

    Alors maintenant que faire, certaines modifications ont pû provoquer des bugs qui ont été fixés en amont, d'autres peuvent être toujours là. La question est : est ce qu'il faut les fixer. Essayer de les fixer pourrait provoquer d'autres problèmes (si par exemple d'autres bouts de code on été modifiés pour répondre aux modifs initiales...).

    Bref ma conclusion est que ce genre d'outils c'est bien, mais qu'il faut faire attention. Rien ne remplace une charte de codage fourni au début d'un projet et à laquelle les développeurs doivent se tenir. Eventuellement il est bien d'avoir un outil de qualité qui remonte les effractions au fur et à mesure du développement. Mais déployer un tel outil quand le projet a déjà de l'âge et écouter les alertes sur le code existant peut être dangereux...


    mercredi, mai 30 2012

    Le masque binaire avec des String

    Changement de projet oblige, nouveau code, nouveaux WTF!

    Aujourd'hui en avant première le masque binaire avec des String :

    char[] masqueFinal = null;
     
    String masqueVide = "00000000";
    masqueFinal = masqueVide.toCharArray();
     
    List<Objet> liste = model.getListObjets();
    if ( liste != null && !liste.isEmpty()) {
      for (int i = 0; i < liste.size(); i++) {
        if (i != row) {
          Objet f = liste.get(i);
          String masque = f.getAcheminementHebdo();
          char[] masqueTab = masque.toCharArray();
     
          if ("1".equals(String.valueOf(masqueTab[0]))) {
            masqueFinal[0] = '1';
          }
          if ("1".equals(String.valueOf(masqueTab[1]))) {
            masqueFinal[1] = '1';
          }
          if ("1".equals(String.valueOf(masqueTab[2]))) {
            masqueFinal[2] = '1';
          }
          if ("1".equals(String.valueOf(masqueTab[3]))) {
            masqueFinal[3] = '1';
          }
          if ("1".equals(String.valueOf(masqueTab[4]))) {
            masqueFinal[4] = '1';
          }
          if ("1".equals(String.valueOf(masqueTab[5]))) {
            masqueFinal[5] = '1';
          }
          if ("1".equals(String.valueOf(masqueTab[6]))) {
            masqueFinal[6] = '1';
          }
        }
      }
    }
    

    Bon heureusement à côté de ça le reste du code est franchement propre, c'en est surprenant, je ne devrais pas avoir trop de WTF à partager :D


    lundi, avril 16 2012

    Nonnon, on ne vous prend pas du tout pour des vaches à lait. Pas du tout...

    Vous le savez, Free est arrivé sur le marché du mobile et a complètement bouleversé la donne. Tous les opérateurs ont revus leur forfaits et leurs tarifs sur les abonnements voix;. C'est très bien, il est maintenant tant que Free nous fasse une offre data ! En effet les opérateurs se foutent encore allègrement de notre gueule sur ce point là. Ils ont eu beau dire qu'ils ne nous prenaient pas pour de vaches à lait il n'empêche que sur les créneaux où Free n'a pas encore pris la peine de leur marcher sur les pieds ils ne se privent pas. En effet voici un petit récapitulatif des offres 3G chez orange (on retrouve le même modèles chez les aures):

    clients nouveaux
    clients
    engagement
    Let's go 100Mo 5€/mois 8€/mois 12 mois 0 : Même tarif !
    Let's go 1Go 19€/mois 22€/mois 24 mois 12 mois : +4€ ; 0 : +4€
    Let's go 3Go 32€/mois 35€/mois 24 mois 12 mois : +4€ ; 0 : +4€
    Let's go 5Go H+ 59€/mois 62€/mois 24 mois 12 mois : +4€ ; 0 : +4€

    Donc en gros si comme moi vous passez vos WE dans un trou perdu (où l'ADSL ne passe pas) il vous faudra débourser au moins 22€ par mois pour avoir un accès potable à internet. En revanche si vous y vivez alors là... !!!

    Cependant si vous avez la chance d'être un pro et bien c'est la fête, vous pouvez aller vivre au fond du cantal et avoir accès à ce que vous voulez pour un prix tout à fait dérisoire (compte tenu du reste des offres bien sûr).

    Et oui Orange vous offre le forfait 3G tout ILLILIMITE à seulement 39€ HT (47€ TTC). En ce moment cette offre est même à 27.30€ HT[1] (33€ TTC). Avec cette offre sur 24 mois pas de problèmes, la 3G est à votre portée en illimité, exit les 65€ du Let's Go 5Go !

    Autre fait marrant de ces forfaits : le tarif avec engagement de 12 mois est le même que celui sans engagement... C'est juste au cas où vous seriez assez bête pour ne pas regarder le tarif du sans engagement ;)

    Alors, c'est qui la vache à lait ? Bah le particulier puisqu'il faut absolument être pro pour souscrire à ces offres...

    Ps: Chez Bouygues le forfait pro illimité est à 25€ HT contre 49.90€ TTC pour 6Go[2]

    Notes

    [1] obepro.png

    [2] bouygues_3gpro.png


    mercredi, mars 28 2012

    RAH! Marre de fermer ce terminal de m****

    Si comme moi vous en avez marre de fermer votre terminal mac os par erreur lorsque vous essayez de faire un bête copié sous emacs (cmd-W) alors redéfinissez vos raccourcis claviers pour cette application. Comme ça plus de problèmes. Comment qu'on fait ? On fait comme ça.

    Redéfinissez le raccourci pour fermer la fenêtre et fermer l'onglet. Attention si comme moi vous avez un MacOS en français il y a une astuce pour fermer l’onglet, et oui l'apostrophe est spéciale : ALT+SHIFT+' ... Z'aiment pas faire les choses simplement (ou plutôt comme tout le monde) chez Apple.


    mardi, mars 27 2012

    Java - Attention à vos garbages collectors !

    Si vous êtes un développeur JAVA vous avez sûrement déjà eu besoin de paramétrer le garbage collector pour qu'il colle au mieux à votre application.

    Pour ma part je travaille sur une application de trading ayant une architecture distribuée qui utilise une bonne pelletée de serveur (plus de 100) et qui se doit d'avoir des temps de réponses rapides. Nous sommes donc censés avoir des JVMs et des GC configurés de telle sorte que les processes soient le moins possible interrompus par la garbage collection.

    Ainsi quand on livre un process en prod on le livre avec ses paramètres de GC. Jusque là tout va bien. Malheureusement certains de nos process ont une dizaine d'années. Ils ont donc été migrés de serveurs en serveurs au fil des ans (plus de ram, plus de coeurs, plus de Hz, ...) sans que personne ne se préoccupe de vérifier un tant soit peu les paramètres de GC. Dans le meilleur des mondes tout aurait pu bien se passer, mais non...

    J'ai découvert avec stupeur qu'un process censé être "temps réel" avait 14,4Mo de Young pour 1.970Mo de Old !!!! Echec. Le plus fun est que ce process tourne en fault-tolerance[1] et que le process de backup a lui 115Mo de Young. Et oui, les deux serveurs où tournent les instances sont différents. Ils ont tous les deux 10Go de ram, mais l'un est bi-proc quad-core et l'autre est bi-proc bi-core et bien sûr les procs ont des fréquences différentes.

    Que s'est il passé ? Et bien c'est simple au bout de 2h d'uptime le process de backup est arrivé à bout de sa heap et a décidé de rajouter 100Mo de heap (il était justement 100Mo de son Xmx) et il a tout mis dans sa Young. Ca pourrait être le hasard, mais ce schéma se reproduit presque tous les jours (en fonction de la charge de la journée). Et cela se produit sur tous les couples que nous avons de ce process qui tournent sur des serveurs différents (6 couples sur 12 serveurs). Les serveurs les plus rapides utilisent plus de young. Cela est dû au parrallel GC que nous utilisons et au fait que la JVM change ses paramètres en fonction des performances du process (et donc de la machine)[2]. En effet ajouter de la mémoire impacte les temps de GC, et donc le throughput de l'application, sur les serveurs lents la VM mets plus de mémoire dans la Old, et sur les rapides elle en met plus dans la Young, je trouve ça idiot puisque du coup les collections majeurs sont plus fréquentes sur les vieux serveur et l'application est donc plus lente... échec.

    En conclusion quand vous fixez vos paramètres de GC pour un serveur, faites attention ils ne seront peut être pas valide pour un autre serveur même s'il est plus puissant... Ensuite si vous avez définit un Xmx n'hésitez pas à mettre un Xms égal au Xmx et à définir un ratio young/old cohérent avec votre application. (attention le ratio est appliqué au démarrage et dépend de la heap à ce moment, donc Xmx=Xms est important !!!)

    Notes

    [1] il tourne en 2 exemplaires, un actif, un inactif. Quand l'actif crash, l'inactif devient actif et prend le relais

    [2] http://docs.oracle.com/javase/1.5.0/docs/guide/vm/gc-ergonomics.html


    lundi, janvier 16 2012

    Freeeeeeeeeeeeee

    Ca y est j'ai ma sim Free! Portage à 11h aujourd'hui ! En attendant j'ai fait quelques tests et pour le moment ça promet.

    Voici les débit 3G que j'ai constatés :

    • 146 ms de ping, 2756kbps en descendant, 1684kbp en montant dans le 13è (Paris)
    • 99 ms de ping 5253kbps en descanant, 2428kbps en montant à la défense

    Ca va me changer de Virgin où je plafonnais à 600kbps. Ce qui m'énervait surtout chez Virgin c'était que le ping qui était bon, mais que lors de connexions normales on avait un trou avant l'arrivée des données qui allait de quelques millisecondes à carrément quelques secondes !

    Autre truc chiant de Virgin : ILS TE COUPENT LA 3G si tu dépasses trop ton fairuse (700Mo pour 500Mo de fairuse). Je crois qu'ils n'ont pas compris le principe de l'illimité ! Et après leur bridage est tellement moisi que la moitié des connections timeout, cela dit j'avais la même chose chez Orange avec ma clé 3g, j'espère que Free sera plus malin (enfin, avant de taper les 3Go, j'ai du temps ;))

    Voilà les preuves :

    Virgin paris 13 :

    Virgin_-_13.png

    Free paris 13 :

    Free_-_13.png

    Virgin la défense :

    Virgin_-_Defense.png

    Free la défense :

    Free_-_Defense.png


    jeudi, juillet 7 2011

    RIM

    J'ai raté une importante news pendant mes vacances, apparemment un employé de RIM a envoyé une lettre adressée aux patrons de RIM aux journalistes sur la situation actuelle. C'est assez sanglant.

    Lire la suite


    samedi, juillet 2 2011

    iMac-cramé-reparé

    Je crois bien que je viens de faire le truc le plus fou que je n'ai jamais fait avec un ordinateur! En effet mon iMac a eu la bonne idée de cramer la semaine dernière, ou plus exactement la carte graphique de mon iMac.

    Lire la suite


    vendredi, juin 24 2011

    Illogismes en tous sens (et emmerdements de la vie courante)

    Notre beau pays est rempli d'illogismes et de comportements idiots. Certains sont juste amusant, d'autres juste énervants, voici ceux qui me gonflent le plus au jour le jour, ce n'est que mon point de vue alors allez y lâchez vous dans les commentaires avec les votres :)

    Lire la suite


    - page 1 de 32