Vous allez me dire que c'est plutôt bon signe ; et en effet, les trois derniers tomes ont tenu toutes les promesses que les deux premiers nous avaient fait , je n'ai pas pu m'arrêter avant d'avoir terminé le dernier élément du cycle.
dimanche 6 mai 2012
Les bannis et les proscrits - Suite et fin
Vous allez me dire que c'est plutôt bon signe ; et en effet, les trois derniers tomes ont tenu toutes les promesses que les deux premiers nous avaient fait , je n'ai pas pu m'arrêter avant d'avoir terminé le dernier élément du cycle.
JAVA 7 - Fork/Join framework
En parcourant le net, je me suis rendu compte que peu de pages, françaises, étaient présentes pour décrire de façon simple et concise le framework Fork/Join apporté par Java 7. Le but de ce billet sera d'en donner les éléments clés ainsi que les pointeurs sur les pages qui m'ont aidé à me faire une rapide compréhension de ce nouveau standard.
Le principe général
C'est une mise en application du vieil adage "diviser pour mieux régner" ; sauf que dans notre cas c'est pour la bonne cause.
Désormais, quand on commence à coder une boucle qui risque d'être longue car consommatrice de CPU ou lorsque l'on commence à implémenter un algorithme dont l'exécution sera longue en terme de temps CPU ; JAVA met à notre disposition un lot de classes permettant de coder facilement l'exécution en parallèle de certains traitements mais aussi, et surtout, aidant différentes unités d'exécution (ou Tache) à se synchroniser les unes par rapport aux autres.
Mise en application
C'est au développeur de découper son calcul/traitement en unités d'exécution (ou tache) plus petites, jusqu'à obtenir une taille qu'il considérera comme suffisamment petite pour pouvoir être exécutée sans être divisée. Ce découpage peut être fait de manière récursive. Chaque tâche devra dériver de la classe "ForkJoinTask". En fait, le framework fournit deux classes spécialisant la class ForkJoinClass :- RecursiveTask<T> : pour coder une tâche retournant une valeur de Type T
- RecursiveAction : pour coder une tâche dont on n'attend pas de valeur résultat, mais devant réaliser une action.
Une fois ce travail de découpage effectué, la tâche principale ( ou point d'entrée ) est passé à un executor spécialisé ( class ForkJoinPool ).
Les liens
- http://www.oracle.com/technetwork/articles/java/fork-join-422606.html : cette page vous montrera comment réaliser l'équivalent du 'grep' unix en utilisant le framework for/join. Je ne met pas d'exemple de code dans mon article car je trouve ceux de cette page très pédagogiques.
- http://docs.oracle.com/javase/tutorial/essential/concurrency/forkjoin.html : un aperçu beaucoup plus basique de la techno
- http://www.programmez.com/tutoriels.php?tutoriel=90 : en français, mais un peu moins agréable à lire que le premier des liens ...
JMX à travers un firewall
Comme je l'ai déjà dit ici ( en anglais ) je suis un adepte de la technologie JMX. Mais je viens d'être confronté à un problème : la connection (en JMX) à une JVM cachée derrière un firewall. Après avoir un peu cherché, j'ai fini par trouver comment le solutionner ; par contre, je n'ai pas trouvé de documentation en français. C'est pourquoi j'écris cet article : si vous aussi, vous avez des problèmes pour établir une connexion JMX à un serveur JAVA et cela au travers d'un firewall alors il pourra vous intéresser.
Description de la problématique
J'essaie de me connecter à un serveur écoutant sur le port JMX 1100 ; serveur séparé de mon poste client ( sur lequel je lance par exemple une jconsole ) par un firewall.
Pour forcer le port JMX j'ai rajouté les paramètres suivant à la ligne de commande de démarrage de mon serveur :
-Dcom.sun.management.jmxremote.port=1100 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
Du côté de mon firewall nous avons fait le nécessaire pour ouvrir le port TCP 1100. Et pourtant ma jconsole n'arrive pas à se connecter au serveur distant. Après analyse j'ai fini par comprendre qu'en fait d'autres ports sont mis en jeux.
Les explications
En fait le port 'jmx' n'est que celui recevant les connexions ; ensuite un autre est utilisé sur lequel sont exportés les objets (RMI) accédé via JMX. Cet autre port est en général alloué dynamiquement ; mais il est possible de le contrôler.
La solution
Elle consiste à créer une instance de la classe JMXConnectorServerFactory, et de l'associer au MBean server de l'application. Si vous ne voulez pas, ou si vous ne pouvez pas, créer un tel serveur dans le code de votre application ( par exemple si vous n'avez pas la main sur toutes les sections du code ; voir pas la main du tout ) vous pouvez le créer dans un agent enregistré au démarrage de la VM : ainsi vous allez modifier le fonctionnement de votre application sans toucher à son code.
Exemple d'agent
public class JmxDefaultAgent {
public static MBeanServer getMBeanServer() {
ArrayList list =MBeanServerFactory.findMBeanServer(null) ;
return((list.size()==0)?ManagementFactory.getPlatformMBeanServer():list.get(0));
}
public static void premain(String agentArgs) throws IOException {
int port = Integer.parseInt(System.getProperty("com.me.jmxport", "1100"));
String hostname = System.getProperty("com.me.jmxhost", null);
if (hostname==null) hostname = InetAddress.getLocalHost().getHostName();
System.out.println("Create RMI registry for JMX on port " + port);
LocateRegistry.createRegistry(port);
MBeanServer mbs = getMBeanServer();
// Environment map
HashMap env = new HashMap();
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://"+hostname+":" + port + "/jndi/rmi://" + hostname
+ ":" + port + "/jmxrmi");
// Now create the server from the JMXServiceURL
//
JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
// Start the RMI connector server.
//
System.out.println("Start the RMI connector server on port " + port);
cs.start();
}
}
Si vous avez la main sur le code
Il n'est pas nécessaire d'utiliser un agent ; il vous suffit simplement de démarrer le serveur JMXConnectorServer lors du chargement de votre application.
Dernier problème
Le code précédent fonctionne parfaitement ; son seul problème est que le server JMXConnectorServer démarré dans l'agent va empêcher l'arrêt correct de la JVM : un tel serveur doit être stoppé via sa méthode stop. Autrement dit, ce serveur n'est pas démarré sous la forme d'un thread daemon qui à l'arrêt de la JVM nettoierait ses ressources et s'arrêterait automatiquement.
Si vous n'avez pas la main sur le code du serveur pour y rajouter l'appel à la méthode stop ; une méthode consiste à créer un thread dans l'agent vu précédemment, thread dont le rôle sera de détecter la mort imminente de l'application. Cette détection consiste à repérer qu'il n'existe plus de thread actif au sein de la JVM, en dehors des thread daemon et de ceux liés au serveur JMXConnectorServer ; et dans ce cas, on peut appeler la méthode stop du JMXConnectorServer de manière sure.
Si vous n'avez pas la main sur le code du serveur pour y rajouter l'appel à la méthode stop ; une méthode consiste à créer un thread dans l'agent vu précédemment, thread dont le rôle sera de détecter la mort imminente de l'application. Cette détection consiste à repérer qu'il n'existe plus de thread actif au sein de la JVM, en dehors des thread daemon et de ceux liés au serveur JMXConnectorServer ; et dans ce cas, on peut appeler la méthode stop du JMXConnectorServer de manière sure.
Exemple de thread détectant l'arrêt de l'application
public class Cleaner extends Thread {
public Cleaner(JMXConnectorServer connectorServer) {
super("ThreadCleaner");
this.connectorServer = connectorServer;
}
@Override
public void run() {
boolean alive = true ;
try {
while( alive == true ) {
Thread[] activeThreads = new Thread[Thread.activeCount()+10];
int total = Thread.enumerate(activeThreads);
try {
alive = false ;
for (int i=0;i<total;i++) {
Thread currentThread = activeThreads[i];
if (currentThread.getName().contains("RMI")) continue;
if (currentThread.equals(this)) continue;
if (currentThread.isDaemon()) continue;
else {
alive = true ;
currentThread.join();
break;
}
}
if (alive == false) {
System.out.println("no more thread remaining - stopping jmxconnectorserver");
}
} catch(InterruptedException e) {
System.out.println("received interruption - stopping jmxconnectorserver");
alive = false ;
}
}
} finally {
try {
connectorServer.stop();
} catch (IOException e) {
}
}
}
private JMXConnectorServer connectorServer ;
}
Lien utiles
- Blog de "Daniel Fuchs" chez oracle (en anglais) , ainsi que ses suites (suite 1, suite 2, ...)
- un autre blog : http://androidyou.blogspot.fr/2010/07/restrict-jmx-listening-porttcp-to-use.html
- package java.lang.instrument
lundi 16 avril 2012
JAVA - Récupérer la version courante d'un JAR / WAR programmatiquement
Récupérer la version courante d'un JAR
C'est facile, et cela se fait en deux temps :
- Tout d'abord, il s'agit de configurer MAVEN pour que le
manifestassocié à votre JAR contienne la version de votre librairie. C'est le composant MAVEN ARCHIVER qui doit être paramétré de façon à rajouter l'information attendue dans le fichiermanifestde votre JAR. Il suffit de rajouter ce fragment de XML dans votre fichier pom :
<plugin>
<groupid>org.apache.maven.plugins</groupid>
<artifactid>maven-jar-plugin</artifactid>
<version>2.3.2</version>
<configuration>
<archive>
<manifest>
<adddefaultimplementationentries>true</adddefaultimplementationentries>
<adddefaultspecificationentries>true</adddefaultspecificationentries>
</manifest>
</archive>
</configuration>
</plugin>
- Puis, dans le code, il vous suffit de récupérer l'objet Package associé à l'une des classes du JAR (par exemple LaClasseDuJar ) dont vous voulez récupérer la version ; l'instance de cet objet vous donnera les informations de version que vous cherchez, selon :
Package objPackage = LaClasseDuJar.class.getPackage(); String version = objPackage.getImplementationVersion();
Version d'une application web (ou WAR)
C'est un peu plus compliqué : le coup duManifest ne marche pas tout le temps. En tous les cas, avec tomcat, je ne suis pas arrivé à faire fonctionner la technique précédente même si celle-ci fonctionne parfaitement pour les JAR utilisés par la webapp. Du coup, j'utilise l'astuce tout bête suivante : dans mon WAR j'inclue un fichier texte contenant la version de l'application.Génération du fichier texte avec maven
Pour générer le fichier texte avec l'information de version attendue , j'utilise le pluginmaven replacer. L'idée consiste à faire remplacer un pattern spécifique par l'information attendue lors de la phase de préparation du war (ici lors de la phase précédant sa génération ). Voici par exemple un extrait d'un fichier pom permettant de faire ce que nous attendons :<build>
...
<plugin>
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>maven-replacer-plugin</artifactId>
<version>1.3.9</version>
<executions>
<execution>
<!-- le remplacement doit se faire avant que le war soit packagé -->
<phase>prepare-package</phase>
<goals>
<goal>replace</goal>
</goals>
</execution>
</executions>
<configuration>
<includes>
<include>target/mywebapp/WEB-INF/classes/com/me/config/version.prop</include>
</includes>
<regex>false</regex>
<!-- chaîne à remplacer -->
<token>PROJECT_VERSION</token>
<!-- remplacer par la version du projet -->
<value>${project.version}</value>
</configuration>
</plugin>
...
</build>
Pour que cela fonctionne, il faut tout d'abord que vous disposiez d'un fichier version.prop dont le contenu peut être -version=PROJECT_VERSION- et devant se trouver par exemple dans le package
com.me.config ; puis que vous configuriez ainsi la génération de votre fichier war :<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<id>prepare-war</id>
<phase>prepare-package</phase>
<goals>
<goal>war</goal>
</goals>
</execution>
</executions>
<configuration>
<useCache>true</useCache>
</configuration>
</plugin>
Cette configuration ( en particulier le useCache=true ) signifie à maven de réutiliser les fichiers générés dans target pour constituer le war. Ainsi, le fichier patché par le plugin replacer sera celui inclus dans le package. Si jamais cette technique ne fonctionne pas pour vous (le useCache étant marqué comme expérimental) vous pouvez toujours demander au plugin replacer de patcher le fichier source version.prop : c'est à dire celui se trouvant dans vos sources ( il vous faudra sans doute utiliser une expression régulière plutôt qu'un simple mot à remplacer ).Lecture du fichier texte
C'est très simple, par exemple à partir d'une classe MainConfig, contenue dans le même package (com.me.config ) que le fichier version.prop :
InputStream is = MainConfig.class.getResourceAsStream("./version.prop");
if (is!=null) {
Properties prop = new Properties();
String version = null;
try {
prop.load(is);
version = prop.getProperty("version");
} catch (IOException e1) {
}
}
dimanche 25 mars 2012
tcpdump - reminder
Ce billet pour me servir de pense bête - pour mes captures réseaux
Capture
sudo tcpdump -s 0 -i < nom de l'interface réseau > -w out.dumpAnalyse
Ouvrir le fichier out.dump avec wireshark.
Cake aux courgettes et à la féta
Les ingrédients
- 75 g de farine
- 3 oeufs
- un sachet de levure chimique
- 2 cuillères à soupe de crème fraîche
- 4 cuillères à soupe d'huile d'olive
- 2 courgettes
- 200g de féta
- de la ciboulette
Les étapes de la recette
- Mélanger la farine, les oeufs et 2 cuillères d'huile d'olive à l'aide d'un fouet.
- Incorporer la crème fraîche.
- Préchauffer votre four à 180°C
- Couper la courgette en petits morceaux dans une poële) ; puis faire chauffer 5 à 10 minutes avec 2 cuillère à soupe d'huile d'olive
- Laisser refroidir 5 minutes puis mélanger la ciboulette à la courgette
- Mélanger la courgette à la pâte
- Couper en dés la féta et la rajouter au mélange
- Verser dans un moule et cuire 25 minutes à 180°C
Les suggestions du chef
Laisser le cake refroidir une heure à température ambiante ; puis le metter au frigo pendant deux heures ou plus (je le trouve meilleur froid).
Bon appétit.
vendredi 23 mars 2012
mousse au chocolat
Pour 4 à 5 personnes
Ingrédients
- 3 oeufs
- 150 gr de chocolat
- 1 sachet de sucre vanillé
- 3 cuillères à soupe de sucre
- 15 gr de beurre
- 2 cuillères à soupe de lait
Préparation
- Mélanger les jaunes avec le sucre vanillé et le sucre dans un récipient
- Faire fondre le chocolat dans une casserole avec le beurre et le lait
- Battre les blancs en neige
- Rajouter le chocolat au récipient
- Rajouter les blancs en neige
- Réserver au frigo pendant 24h minimum avant consommation
Inscription à :
Articles (Atom)
Libellés
meslivres
(27)
Fantastique
(17)
technicalpost
(17)
JAVA
(9)
gastronomie
(7)
magie
(7)
MAC
(6)
David Gemmell
(3)
Gabriel Katz
(3)
JAVA7
(3)
James Clemens
(3)
sorcier
(3)
Horreur
(2)
JAVAFX
(2)
JMX
(2)
James Herbert
(2)
ORACLE
(2)
Stephen King
(2)
cake
(2)
marie pavlenko
(2)
médiéval
(2)
Asimov
(1)
Audrey Briere
(1)
David Farland
(1)
Dominique Raymond Poirier
(1)
Dragon
(1)
Dresden
(1)
Druss
(1)
E.E. Knight
(1)
EPUB
(1)
HTTP
(1)
Hugh Howey
(1)
JAVASCRIPT
(1)
Jim Butcher
(1)
Kristen Britain
(1)
Légende
(1)
MAVEN
(1)
Marche Mort
(1)
Mediéval
(1)
NIO
(1)
Patrick Rothfuss
(1)
SAMI
(1)
SPOTLIGHT
(1)
Science Fiction
(1)
Seth PATRICK
(1)
Stéphane Gérard
(1)
URL
(1)
WebView
(1)
XHTML
(1)
android
(1)
aïe
(1)
bookeen
(1)
caramel
(1)
chocolat
(1)
citron
(1)
courgette
(1)
crême
(1)
feta
(1)
firewall
(1)
flan
(1)
fork
(1)
google
(1)
gps
(1)
humour
(1)
icoyote
(1)
investigation
(1)
join
(1)
le puits des mémoires
(1)
lollipop
(1)
mousse
(1)
navigation
(1)
nexus 7
(1)
oeufs
(1)
paternité
(1)
play books
(1)
property
(1)
python
(1)
quiche
(1)
reminder
(1)
saskia
(1)
saucisson
(1)
service publique
(1)
silo
(1)
supervision
(1)
tarte
(1)
thriller
(1)
version
(1)