vendredi 25 septembre 2009

javascript : loggin avec blackbird



Blackbird est une librairie très utile permettant de logger sous javascript dans une console externe, et fonctionnant sous IE6 + ... et la plupart des autres navigateurs:

Pour mettre en place, le système il faut télécharger la librairie, et y faire référence dans la page html. Voici, un petit exemple ... A noter, que des raccourcis permette d'afficher ou non la console (F2) , de filtrer les messages, et aussi de profiler les fonctions en utilisant une etiquettes.


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
<script type="text/javascript" src="../js/blackbird.js" ></script>
<link type="text/css" rel="Stylesheet" href="../js/blackbird.css" ></link>
</head>
<body>
<script type="text/javascript" language="javascript">
function testLog()
{
log.profile('testLog');
log.debug(new Date());
log.info(new Date());
log.error('erreur');
log.profile('testLog');
}
</script>
<h3>ceci est un test blackbird</h3>
<a href="javascript:testLog()">testLog</a>
</body>
</html>



et voila .. il est possible de logger simplement et elegamment

java : monitorer une application java avec jconsole


Tout d'abord, il faut une application java 1.5 ou supérieure, et aussi un jdk (qui contient l'utilitaire jconsole).

Ensuite, il faut ajouter les paramètres suivant pour demander à la VM de monitorer l'application avec les interfaces MBeans :



-Dcom.sun.management.jmxremote.port=5555
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false


Voici un exemple de la ligne de lancement :

"C:\Program Files\Java\jre1.6.0_05\bin\javaw.exe" -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:3113 -Dcom.sun.management.jmxremote.port=5555 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dfile.encoding=Cp1252 -classpath D:\Projets\workspaceBOC\testsDivers\bin testSimpleDateFormat

ensuite, lancer la jconsole et la paramètrer pour écouter sur le port choisi (ici en local)

JDK_HOME/bin/jconsole

et voilà, c'est fini ... la console vous permet de suivre pas mal de chose (mémoire, thread, gc ... )

jeudi 24 septembre 2009

java / J2EE: diagnostiquer un problème de performance ...

Ceci est un extrait du site http://blog.ippon.fr/tag/introscope



Récemment en mission chez un acteur majeur du marché de l'énergie français, j'ai été confronté à un problème de performance sur l'un de leurs portails Web. Le symptôme était le suivant : lors de montées en charge, sur l'environnement de production, les temps de génération de certaines pages s'accroissaient dramatiquement (jusqu'à 12 secondes). Brièvement, les composants techniques constituant le portail sont les suivants :

* un cluster de trois serveurs Apache,
* un cluster de trois serveurs Weblogic Portal 8.1sp5,
* un cluster .de trois serveurs Weblogic Integration 8.1sp5,
* un connecteur iWay Adapter for SAP 5.5.006.R2, de iWay Software, installé sur chaque instance WLI,
* SAP,
* Oracle 9i.

L'environnement de production était dépourvu d'outil de diagnostic et aucun message d'erreur suspect n'apaisait dans les différents fichiers de traces.

Premiers réflexes :

* Vérifier la charge des processeurs des serveurs Solaris. La charge des CPU était très faible.
* Vérifier les accès aux entrées/sorties. Le volume de données lues et écrites était très faible ainsi que le nombre d'accès aux disques et aux réseaux.
* Vérifier l'activité du ramasse-miettes des différentes JVM depuis les consoles d'administration Weblogic. Rien à signaler.

Nous pouvions exclure les erreurs de programmation entraînant des surconsommations de CPU et d'I/O, les boucles infinies, et les fuites-mémoires de l'ensemble de nos hypothèses. Le ralentissement semblait être lié à un problème d'accès à une ressource critique tels que un pool JDBC, un pool de threads ou un bloc synchronisé.

Deuxièmes réflexes :

* contrôler le nombre et l'activité des threads au travers des consoles Weblogic,
* générer un "Thread Dump" en lançant un signal SIGQUIT ("kill -3" sous Unix ou "Control+Pause" sous Windows) à destination des processus exécutant les JVMs. Pour rappel, cette opération ne termine pas l'exécution des JVMs. Lorsqu'une JVM reçoit ce signal, elle suspend momentanément l'exécution de toutes ses threads, génère une trace d'exécution ("stack trace") pour chacune d'elles, puis les réactivent.

L'outil idéal pour analyser un "Thread Dump" est ... "Thread Dump Analyzer". Il s'agit une application gratuite disponible à l'adresse : http://tda.dev.java.net

Premier constat : près de la moitié des threads des serveurs WLI étaient en attentent sur le même bloc synchronisé. Le point de contention était localisé dans la méthode "debug" appartenant à la classe "LoggerDecorator" du connecteur iWay.

Pour aller plus en avant dans nos investigations, nous avons installé le produit Introscope de CA | Wily Technology sur les serveurs WLI de l'environnement de qualification, puis nous avons généré du trafic afin de reproduire l'anomalie. Introscope est un outil de supervision et de diagnostic permettant de suivre l'activité de tous les composants sollicités durant une transaction, sur plusieurs serveurs. Son faible surcoût en ressource machine permet de le déployer sur des environnements de production.

Deuxième constat : 92% du temps de production des pages du portail était consommé par le mécanisme de trace (log) du connecteur SAP !!

Ne disposant pas des sources du connecteur iWay pour le corriger, un cas a été ouvert auprès de BEA Systems.

En conclusion,

1. TDA nous a permis de cerner la source du problème. Ce petit outil gratuit doit faire parti de votre trousse de secours.
2. Instroscope nous a permis d'identifier la méthode générant les ralentissements et de mesurer leurs importances. Cet outil nous a également permis de corréler les ralentissements avec les accès simultanés à la classe de trace en superposant la courbe des temps de réponse de la méthode incriminée et la courbe du nombre des accès à la méthode. Il s'agit d'un produit évolué permettant de superviser des applications complexes en production et de diagnostiquer rapidement les sources d'une anomalie.

mercredi 16 septembre 2009

java : créer une clé de hashage unique ...

Il est parfois utile de créer une valeur de hash, pour signer un fichier, ou crypter un mot de passe en base ... Pour cela , java propose une classe simple à utiliser, et tout une gamme d'algorithme (SHA1, MD-5, SHA-256,etc...) ...

1-Tout d'abord, on initialise le SecureRandom
SecureRandom prng = SecureRandom.getInstance("SHA1PRNG");

2-on obtient une instance de MessageDigester avec l'algo choisi.

//get its digest
MessageDigest sha = MessageDigest.getInstance("SHA-1");


3-encrypter une chaine quelconque, et récuperer le tableau de byte
byte[] result = sha.digest( randomNum.getBytes() );


4-Voici, un exemple complet (mais jamais testé), qui montre un bel affichage en hexa :


import java.security.SecureRandom;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class GenerateId {

public static void main (String... arguments) {
try {
//Initialize SecureRandom
//This is a lengthy operation, to be done only upon
//initialization of the application
SecureRandom prng = SecureRandom.getInstance("SHA1PRNG");

//generate a random number
String randomNum = new Integer( prng.nextInt() ).toString();

//get its digest
MessageDigest sha = MessageDigest.getInstance("SHA-1");
byte[] result = sha.digest( randomNum.getBytes() );

System.out.println("Random number: " + randomNum);
System.out.println("Message digest: " + hexEncode(result) );
}
catch ( NoSuchAlgorithmException ex ) {
System.err.println(ex);
}
}

/**
* The byte[] returned by MessageDigest does not have a nice
* textual representation, so some form of encoding is usually performed.
*
* This implementation follows the example of David Flanagan's book
* "Java In A Nutshell", and converts a byte array into a String
* of hex characters.
*
* Another popular alternative is to use a "Base64" encoding.
*/
static private String hexEncode( byte[] aInput){
StringBuilder result = new StringBuilder();
char[] digits = {'0', '1', '2', '3', '4','5','6','7','8','9','a','b','c','d','e','f'};
for ( int idx = 0; idx < aInput.length; ++idx) {
byte b = aInput[idx];
result.append( digits[ (b&0xf0) >> 4 ] );
result.append( digits[ b&0x0f] );
}
return result.toString();
}
}

java : générer un UUID

Comment générer simplement un numéro unique ... La classe UUID (à partir de java 1.5) ... Voici un exemple simple :


import java.util.UUID;

public class TestGUID {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(UUID.randomUUID().toString());
System.out.println(UUID.randomUUID().toString());
}

}

lundi 14 septembre 2009

java : faire des transferts ftp

Il est parfois utile de réaliser des transfert de fichiers, d'un serveur à un autre. Notamment pour des exports diverses et variés. Outre le fait de les réaliser en shell script, il est possible de les réaliser en java en utilisant la librairies apache (commons-net).

Etape 1 :Comme c'est assez simple et classique (connexion, login, configuration du mode) je mets directement un extrait du code ...


// on doit se connecter en FTP /users/toto/build/
ftpClient = new FTPClient();

try {
ftpClient.connect(host, Integer.valueOf(port));
ftpClient.login(username, password);
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
// Use passive mode as default because most of us are
// behind firewalls these days.
ftpClient.enterLocalPassiveMode();
ftpClient
.addProtocolCommandListener(new PrintCommandListener(
new PrintWriter(System.out)));

} catch (NumberFormatException e) {
e.printStackTrace();
System.exit(-1);
} catch (SocketException e) {
e.printStackTrace();
System.exit(-1);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}

Etape 2 :une fonction de copie (un appel a la méthode storeFile):


private static void copyFtpFile(String srcDirValue, String dstDirValue,
String fileName, FTPClient ftpClient) throws IOException {
FileInputStream local = new FileInputStream(srcDirValue + "/"
+ fileName);

String remote = dstDirValue + fileName;
remote = remote.replaceAll("\\\\", "/");

if (ftpClient.storeFile(remote, local)) {
/**
* -s D:\ArchCvs\BC\src -d D:\ArchCvs\V1-9-v1-patch\src -c
* D:\MesDocs\myProjet\listeFichiers.txt -f
* 192.168.161.16:21:dev:dev
*/
System.out.println("copy file [" + local.getFD().toString()
+ "] to [" + remote + "]");
} else {
System.err.println("erreur copy file [" + local + "] to [" + remote
+ "]");
}
local.close();

}



Le transfert est terminé ...

vendredi 11 septembre 2009

java : JNI ....

JNI (Java Native Interface) permet d'appeler des fonctions natives tel que du C/C++. Voici la marche à suivre :

1- tout d'abord, il faut créer une classe java qui comporte des méthodes natives ... et utiliser l'outil de SUN javah qui va générer un header C pour les méthodes natives ...

Voici un exemple de classe :


package com.jni;

public class JNITest {

// chargement de la librairie
static
{
System.loadLibrary("nativeLib");
}

public native String concat(String s2,String s1);

public native long testLong(long valeur);
/**
* @param args
*/
public static void main(String[] args)
{
System.out.println("debut test JNI");
JNITest obj = new JNITest();
String result = obj.concat("abc","def");
System.out.println("valeur retournee = [" + result + "]");

long valeur = -1;
valeur = obj.testLong(0);
System.out.println("long retourne = [" + valeur + "]");
System.out.println("fin test JNI");
}
}


lancer javah et ajouter le chemin vers le fichier .class

C:\Program Files\Java\j2sdk1.4.2_08\bin\javah.exe D:\Projets\workspace_bc\testJNI\bin\com.ldu.JNITest

A noter, que dans cette classe, il faut charger en static la librairie générée (dll ou .so selon la plateforme)


static
{
System.loadLibrary("nativeLib");
}


2-Si tout s'est bien passé alors, un fichier header (*.h) a été généré. Il suffit maintenant d'implémenter les méthodes avec un fichier *.c ou *.cpp (Attention dans le cas du C++, il faut encadred les méthodes par un extern c,car en fait ce sont des fonctions c et les nom ne doivent pas être décorés ...

Voici, l'exemple d'implémentation des méthodes c

/* ========================================================================== */
/* */
/* Filename.c */
/* (c) 2001 Author */
/* */
/* Description */
/* */
/* ========================================================================== */
#include
#include
#include "com_ldu_JNITest.h"

JNIEXPORT jstring JNICALL Java_com_ldu_JNITest_concat
(JNIEnv * env, jobject obj, jstring chaine1, jstring chaine2)
{
char resultat[256];
const char *str1 = (*env)->GetStringUTFChars(env, chaine1, 0);
const char *str2 = (*env)->GetStringUTFChars(env, chaine2, 0);
sprintf(resultat,"%s%s", str1, str2);
(*env)->ReleaseStringUTFChars(env, chaine1, str1);
(*env)->ReleaseStringUTFChars(env, chaine2, str2);
return (*env)->NewStringUTF(env, resultat);
}

JNIEXPORT jlong JNICALL Java_com_ldu_JNITest_testLong
(JNIEnv * env, jobject obj , jlong valeur)
{
return valeur + 1;
}


3-Une fois les méthodes implémentées, il faut maitenant les compiler, par exemple avec mingw (portage de gcc sous windows) ...
Voici un exemple de ligne de commande


D:\tools\bin\gcc.exe -I "C:\Program Files\Java\j2sdk1.4.2_08\include" -I "C:\Program Files\Java\j2sdk1.4.2_08\include\win32" -c "D:\Projets\workspace_bc\testJNI\bin\com_ldu_JNITest.c"


4-Il est ensuite nécessaire de générer la libriarie:
Voici la ligne de comande
D:\tools\bin\gcc.exe -shared -Wl,--kill-at "D:\Projets\workspace_bc\testJNI\bin\com_ldu_JNITest.o" -o nativeLib.dll

5-et surtout de la mettre dans le java.library.path
java com.ldu.JNITest-Djava.library.path="D:\Projets\workspace_bc\testJNI\bin"



lundi 7 septembre 2009

java : template velocity

Voici une manière simple et puissante (boucle possible, conidtion, etc ... ) de générer des mails, ou des fichiers customisés via un système de template et le remplacement "en live" par les valeurs des variables. Voici la librairie apache Velocity :

//1-initialiser velocity
Velocity.init();

//2-initailiser le contexte
VelocityContext context = new VelocityContext();

//3- mettre les donnees dans le contexte
context.put("nom","Enwoo");
context.put("typeAbonnement","Forfait 4 heure");
context.put("dateDuMail",new Date());

//4-choisir un fichier template au format velocity
template = Velocity.getTemplate(templateName);

//5-merger le template
template.merge(context,writer);

//6-ecrire le fichier template
il faut respecter la syntaxe velocity, mais simplment les variable doive avoir un $MaVariable devant


/************************************************/
/* un extrait complet et son fichier template */
/************************************************/
private void genererConfiguration() throws Exception
{
HashMap config = new HashMap();
try {
//1 initialize context
Velocity.init();
} catch (Exception e) {
System.err.println("Erreur a l'initialisation");
}
// retrieve and put data in context
VelocityContext context = new VelocityContext();

String iniFile = "ressources/sidef.ini";
LineNumberReader reader = null;
Pattern pValue = Pattern.compile("=(.)+");
Pattern pKey = Pattern.compile("(.)+=");

try {
reader = new LineNumberReader(new FileReader(iniFile));

String line = null;
String value = null;
String key = null;
Matcher matcherValue = null;
Matcher matcherKey = null;

while((line = reader.readLine()) != null)
{
if(line.length() > 0)
{
System.out.println("lecture de [" + line + "]");
matcherValue = pValue.matcher(line);
matcherKey= pKey.matcher(line);
while(matcherValue.find() && matcherKey.find())
{
key = line.substring(matcherKey.start(),matcherKey.end()-1);
value = line.substring(matcherValue.start()+1,matcherValue.end());
context.put(key,value);
}
}
}
} catch (FileNotFoundException e2) {
System.err.println("Erreur de lectur'e du fichier ini [" + iniFile + "]");
e2.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

// choose template
Template template = null;
String templateName = "src/ressources/prod.vm";
try {
template = Velocity.getTemplate(templateName);
} catch (ResourceNotFoundException e) {
System.err.println("Template non trouve [" + templateName + "]");
} catch (ParseErrorException e) {
System.err.println("Template non conforme [" + templateName + "]");
} catch (Exception e) {
System.err.println("Exception innattendue [" + e.getMessage() + "]");
}

// merge template
FileWriter writer = null;
String fileName = "prod.conf";
try
{
File file = new File(fileName);
if(file.exists())
{
file.delete();
}

writer = new FileWriter(fileName);

try {
template.merge(context,writer);
writer.flush();
writer.close();
} catch (ResourceNotFoundException e) {
System.err.println("Erreur lors du merge du template");
e.printStackTrace();
} catch (ParseErrorException e) {
System.err.println("Erreur lors du merge du template");
} catch (MethodInvocationException e) {
System.err.println("Erreur lors du merge du template");
} catch (IOException e) {
System.err.println("Erreur lors du merge du template");
}
}
catch (IOException e1)
{
System.err.println("Erreur IO lors de l'ecriture du fichier");
}
finally
{
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}


/************************************************/
/* le fichier template associe email.vm */
/************************************************/

Bonjour $nom,

Votre formule d'abonnement est à compté du $dateDuMail, un abonnement du type $typeAbonnement.

Cordialement votre service Client.