Guillaume VIEL :: blog Openfarm :: java jee tomcat linux

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

mardi 31 janvier 2012

Log4j : changer le niveau de log à chaud via JMX

Changer le niveau de log directement sans avoir à redéployer l'application est parfois indispensable sur des plateformes critiques où il est impossible d'interrompre le service. C'est aussi un moyen de déboguer directement sur une plateforme qui est la seule à présenter un bug non reproductible ailleurs.

Il existe plusieurs solutions dont :

  • relecture de la configuration Log4j à intervalle régulier; il suffit alors de modifier la configuration des niveaux de log directement (soit avec l'API Log4j, soit avec les classes utilitaires de Spring Log4jWebConfigurer)
  • changement des niveaux de log à l'aide de JMX

C'est cette dernière solution que je préfère, car il est généralement déconseillé de modifier directement des fichiers à chaud dans un serveur d'applications.

Il faut d'abord créer une classe avec une méthode permettant de modifier le niveau de log de n'importe quel package :

Lire la suite...

mercredi 4 janvier 2012

Log4j avec Spring et applications multiples dans Tomcat

Je déploie habituellement les applications web dans Tomcat en multi-instance avec une instance Tomcat par application (ceux qui croient que c'est une hérésie iront voir les concepteurs de Tomcat Mark Thomas et Filip Hanik qui préconisent eux mêmes cette solution). Cependant, dans certains contextes, et notamment chez certains clients, les contraintes font que l'on est obligé de faire autrement...

Dans le cas présent, c'est la configuration de log4j via Spring qui pose (encore) problème : je déploie 2 applications identiques (avec configuration applicative légèrement différente) sous 2 contextes différents dans le même serveur Tomcat. Se posent donc 2 problèmes :

  • il faut pouvoir packager l'application sous des formes différentes et notamment 2 contextes différents : /my-app1 et /my-app2, donc paramétrer le packaging
  • la configuration log4j est à priori identique pour ces 2 applications (packages identiques) : ceci pose le problème de la séparation des logs (et éventuellement la différenciation des niveaux de log)

Lire la suite...

mardi 23 août 2011

Audit des requêtes SQL Hibernate

Obtenir les traces des prepared statements d'Hibernate est assez simple et tout le monde connaît cette configuration. Il suffit d'indiquer

hibernate.show_sql=true

dans le fichier de configuration XML Hibernate pour obtenir la requête qui sera utilisé avec le SGBD. La requête sera affichée sous la forme d'un preparedStatement avec des "?" à la place des valeurs.

Une question alors fréquemment posée est : comment obtenir aussi les valeurs passée avec la requête?!

L'information est quelque part connue en interne dans Hibernate : le tout est de la mettre en valeur en modifiant le paramétrage du logger. Par exemple avec log4j il suffit de mettre en level DEBUG les packages suivants et de les router vers un appender spécifique avec une additivité à false :

log4j.logger.org.hibernate.SQL=DEBUG,SQL_APPENDER
log4j.additivity.org.hibernate.SQL=false
org.hibernate.type=DEBUG,SQL_APPENDER
log4j.additivity.org.hibernate.type=false

mercredi 2 mars 2011

Maven récupération du code source et de la javadoc

Pour récupérer les codes sources et la javadoc, il suffit d'ajouter au lancement de maven

-DdownloadSources=true -DdownloadJavadocs=true

ou bien ceci dans le pom.xml

<properties>

<downloadSources>true</downloadSources>

<downloadJavadocs>true</downloadJavadocs>

....

</properties>

Il est utile d'avoir les sources lors des phases de debugging pour comprendre parfois le comportement d'un framework.

mardi 22 février 2011

Message Bundle avec Spring et problèmes d'encodage des fichiers de properties

Sur une application destinée à l'international, j'ai tout naturellement souhaité introduire l'internationalisation (i18n) des messages destinés aux utilisateurs, aux logs, aux erreurs etc.

Je me suis penché sur ce que propose Spring et j'ai commencé avec cette configuration très sommaire :

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="message"/>
</bean>

Le premier problème c'est l'apparition de ??? à la place de certains caractères UTF-8 : gênant non?! Ceci est en fait dû au ResourceBundleMessageSource de Spring qui utilise les classes standards java.util.ResourceBundle et java.util.Properties, or ces dernières ne supportent visiblement que l'encodage ISO-8859-1 !!!

La solution est d'utiliser le ReloadableResourceBundleMessageSource de Spring, plus complet. Il suffit de lui indiquer le defaultEncoding choisi.

L'autre problème est la non reconnaissance des fichiers de properties (mis dans le classpath) que j'ai nommés message_en.properties , message_fr.properties, etc. Apparemment il faut ajouter le paramètre fallbackToSystemLocale=false sur le ReloadableResourceBundleMessageSource de Spring pour éviter que soient recherchés les fichiers du type message_fr_FR ou message_en_FR.

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:message"/>
<property name="fallbackToSystemLocale" value="false" />
<property name="defaultEncoding" value="UTF-8"/>
</bean>

Sources :

http://www.cakesolutions.net/teamblogs/2009/04/02/utf-8-encoding-and-message-sources/

http://forum.springsource.org/showthread.php?t=18199&page=2

mercredi 16 février 2011

Problème avec le Maven archetype plugin

Lors d'un test de l'archetype plugin de maven, j'obtenais un build failure avec un Required goal not found alors que tout fonctionnait auparavant...

Voici le contenu du .m2\repository\org\apache\maven\plugins\maven-archetype-plugin\maven-metadata-central.xml

<?xml version="1.0"?><metadata>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-archetype-plugin</artifactId>
<versioning>
<latest>1.0-alpha-4</latest>
<release>1.0-alpha-4</release>
<versions>
<version>1.0-alpha-3</version>
<version>1.0-alpha-4</version>
</versions>
<lastUpdated>20060507072802</lastUpdated>
</versioning>
</metadata>

En fait, il faut le corriger comme ceci pour que tout fonctionne correctement (en téléchargeant directement sur un repository en ligne le fichier maven-metadata.xml)

<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-archetype-plugin</artifactId>
<versioning>
<latest>2.0</latest>
<release>2.0</release>
<versions>
<version>1.0-alpha-3</version>
<version>1.0-alpha-4</version>
<version>1.0-alpha-7</version>
<version>2.0-alpha-1</version>
<version>2.0-alpha-2</version>
<version>2.0-alpha-3</version>
<version>2.0-alpha-4</version>
<version>2.0-alpha-5</version>
<version>2.0</version>
</versions>
<lastUpdated>20101028011818</lastUpdated>
</versioning>
</metadata>

Et voilà!

Archetype maven et quickstart pour spring batch

Les archetypes maven ne semblent pas très fonctionnels... Pour créer un template de projet à partir d'un archetype existant voici la commande à exécuter :

$ mvn archetype:generate
Maven sort alors une liste d'une vingtaines d'archetypes possibles. On peut alors choisir l'archetype à utiliser et le customiser. Maven génère alors un répertoire projet prêt à l'emploi. En les testant on s'aperçoit que sur la majorité des archetypes disponibles, seuls quelques uns fonctionnent vraiment...
La liste des archetypes possibles se trouve ici : http://docs.codehaus.org/display/MAVENUSER/Archetypes+List
C'est un peu décevant...

De même, j'ai cherché un archetype pour Spring-batch. Visiblement rien n'existe et l'infrastructure maven ne semble pas être utilisée pour ça. Le mieux que j'ai trouvé c'est d'aller directement récupérer les exemples de code qui existent sur le repository SVN de spring !

$ svn export  https://src.springframework.org/svn/spring-batch/trunk/archetypes/simple-cli
$ cd simple-cli
$ mvn test

Et voilà! On y trouvera aussi d'autres exemples de projet.



dimanche 7 novembre 2010

Hibernate interceptor : nom de schéma dynamique

Bien d'Hibernate soit capable d'aller chercher des données dans des tables situées dans différents schémas, il n'est pas possible directement de changer le schéma à l'exécution. En effet, les annotations sont lues à  la compilation et le code d'une @Entity est alors figé sur un schéma et une table.

@Entity
@Table(name="MON_OBJET",schema="MON_SCHEMA")
public class MonObjet implements Serializable {
...
private Etat etat;
...
@OneToOne(...)
public Etat getEtat() { ... }
public void setEtat(Etat etat) { ... }
}

@Entity
@Table(name="ETAT",schema="MON_SCHEMA")
public class Etat {
...
}

Ceci devient gênant lorsque par exemple, une des entités peut se trouver dans différents schémas qui correspondent par exemple à des environnements différents. Par exemple, si MonObject est attaché en relation un à un à un objet Etat qui peut se trouver dans MON_SCHEMA, SCHEMA_PREPROD ou SCHEMA_PROD, on devra alors packager une application par schéma!

Une solution possible serait de créer un profile maven pour chaque environnement, puis d'utiliser la phase de génération des sources pour modifier les annotations dans le code java... Ce qui semble plutôt lourd à mettre en place! Et au final, il faut toujours une application par schéma utilisé! Il existe cependant une solution avec les interceptor d'Hibernate pour ne déployer qu'une seule application...

Lire la suite...

mercredi 13 octobre 2010

Tomcat : configuration proxy HTTPS

La configuration d'un proxy HTTPS pour Tomcat est plus cohérente que celle de maven et plus directe; il suffit juste d'indiquer les propriétés système de Java dans le fichier $CATALINA_HOME/conf/catalina.properties :

http.proxyHost=proxy.mydomain.com
http.proxyPort=8080
http.nonProxyHost=localhost|10.*

https.proxyHost=proxy.mydomain.com
https.proxyPort=8080

Maven : configuration proxy HTTPS

La configuration d'un proxy sous maven est a priori simple : il suffit d'ajouter la section suivante dans le fichier ~/.m2/settings.xml :

<proxies>
<proxy>
<active>true</active>
<protocol>http</protocol>
<host>proxy.somewhere.com</host>
<port>8080</port>
<username>proxyuser</username>
<password>somepassword</password>
<nonProxyHosts>localhost|10.*</nonProxyHosts>
</proxy>
</proxies>

Mais pour le protocole HTTPS la documentation n'indique rien! Si l'on essaye de transposer et d'ajouter un proxy avec
<protocol>https</protocol>
cela ne marche pas beaucoup plus...
La solution consiste à ajouter ceci à la variable d'environnement MAVEN_OPTS :

MAVEN_OPTS=$MAVEN_OPTS -Dhttps.proxyHost=proxy.somewhere.com -Dhttps.proxyPort=8080

lundi 4 octobre 2010

Implémentation d'un Interceptor CXF pour logger le temps d'exécution

Les intercepteurs de CXF permettent, comme leur nom l'indique, d'intercepter le flux en entrée ou en sortie d'un webservice et d'exécuter un traitement. Tout comme les filtres de servlet, ils sont puissant pour réaliser des opérations techniques sur les données.

Si l'on veut tracer le temps d'exécution d'un webservice à toutes les étapes et notamment juste avant l'entrée dans l'implémentation.

Lire la suite...

samedi 2 octobre 2010

Spring AOP advice et pointcut sur Servlet

La puissance de Spring AOP se limite a priori à son contexte (cf ce post http://forum.springsource.org/archive/index.php/t-11673.html )... Les servlets sont hors contexte Spring car gérées par le serveur d'application JEE et semblent exclues du champ de Spring AOP. Cependant, il est tentant de vouloir quand même utiliser Spring AOP sur les servlets sans avoir à passer par d'autres tisseurs d'aspects (comme AspectJ) qui complexifient la configuration du serveur d'application et le déploiement de l'application. Il s'avère que c'est possible par un moyen détourné. Il fallait juste y penser.

Lire la suite...

Spring AOP : trouver le bon pointcut avec la stack trace

Il est parfois difficile d'identifier le bon endroit pour une "coupe" (pointcut), et il ne faut pas perdre de vue qu'une méthode protected ne pourra jamais être interceptée par Spring AOP.

J'ai donc écris ce bout de code pour me faciliter la tâche et identifier plus aisément quelle pourrait être la méthode déclarée en public et donc candidate au pointcut.

Ce code est à placer dans la méthode qui sera appelée en dernier dans le fil d'exécution afin d'avoir une liste complète des méthodes pouvant être interceptée

        // récupération d'une trace de la pile pour notre thread courant
StackTraceElement[] stack = Thread.getAllStackTraces().get(Thread.currentThread());

for(StackTraceElement ste : stack) { // on parcourt la pile
// on parcours la liste de toutes les méthodes pour la classe
// trouvée dans la stack trace et l'on vérifie si la méthode
// utilisée dans la stacktrace est bien publique
Method[] methods = Class.forName(ste.getClassName()).getMethods();
for(Method m : methods) {
if(m.getName().equals(ste.getMethodName())) {
if(Modifier.isPublic(m.getModifiers()) ) {
logger.debug( ste.getClassName() + "." + ste.getMethodName() );
}
}
}
}

vendredi 1 octobre 2010

Erreur classique de JAXB : JAXBException nor any of its super class is known to this context

Cette exception peut se produire lorsque votre webservice contient des objets complexes (objets imbriqués) qui ne sont pas forcément visible dans votre déclaration de service. Pour qu'ils soient pris en compte, la solution est d'utiliser l'annotation @XmlSeeAlso(MyObjectFactory.class, MyOtherObjectFactory.class) sur votre webservice JAX-WS en lui indiquant les fabriques d'objets souhaitées. JAX-B pourra alors fabriquer tous les objets nécessaire au fonctionnement du webservice.

Source : http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6419632

lundi 13 septembre 2010

Web services securisés avec CXF et WSS4J (partie 1)

La sécurisation des webservices devient de plus en plus nécessaire dans le monde de l'entreprise. Il y a une époque pas si lointaine, les entreprises ne se souciaient pas trop de la sécurisation de leurs données à l'intérieur de l'entreprise. Or il s'avère que les risques sécuritaires viennent majoritairement de l'intérieur!

Voici donc un tutoriel qui explique comment mettre en place toute l'infrastructure webservice : serveur et client. Nous utiliserons CXF qui nous parait être une des solutions les plus efficaces et maniables. CXF allie performances et simplicité d'intégration avec la plupart des frameworks (Spring et WSS4J).

Lire la suite...

mercredi 1 septembre 2010

Activation des traces du driver JDBC Oracle

Il est parfois utile de comprendre ce que fait le driver JDBC Oracle que les développeurs connaissent sous le nom de ojdbc14.jar. Il existe en fait un autre driver destiné au debug : ojdbc14_g.jar pour lequel il suffit d'ajouter ceci dans les JAVA_OPTS pour activer les traces

JAVA_OPTS="$JAVA_OPTS -Djava.util.logging.config.file=OracleLog.properties -Doracle.jdbc.Trace=true"

Ensuite, un fichier OracleLog.properties doit être placé avec le jar pour configurer les traces souhaitées. Il est conseillé de le modifier sinon vous allez avoir beaucoup de traces...

mardi 24 août 2010

Expressions régulières ou regexp en java

J'ai souvent cherché sur Internet des ressources claires et précises sur les expressions régulières en Java. En voici une qui a le mérite d'être claire, complète, avec des exemples et en français :
http://prevert.upmf-grenoble.fr/Prog/Java/CoursJava/expressionsRegulieres.html

Il y a bien sûr la documentation SUN (oops... Oracle) sur les regexp en java mais je la trouve illisible.

jeudi 8 juillet 2010

AVWorks , Java et IPv6

L'application AVWorks est une application fournie avec les KVM IP d'Avocent et notamment la série des Autoview. Ces anciens KVM IP ne fonctionnent qu'en IPv4. AVWorks est réalisée en Java et il semblerait que la façon dont Sun ait implémenté la dual stack IPv4 / IPv6 dans la JVM est plutôt étrange ( cf. article suivant sur debian et ipv6 )... Surprise donc lorsqu'en passant en dual stack IPv4 / IPv6 sur Debian je me retrouve avec l'application AVWorks et toutes les autres applications java qui ne marchent plus.

J'ai donc cherché à patcher l'application afin qu'elle refonctionne et c'est possible. Voici comment faire :

  1. surtout garder l'ancienne version 2.1 d'AVWorks et ne pas installer la nouvelle version 3.1 qui contient pas mal de bugs
  2. dans le fichier AVWORKS_HOME/Avocent_AVWorks.lax il suffit d'y ajouter la variable d'environnement java "java.net.preferIPv4Stack=true" vers la ligne 68 comme ceci: lax.nl.java.option.additional=-Djava.library.path=AVWORKS_HOME/Avocent_AVWorks -Duser.variant=avct -Djava.net.preferIPv4Stack=true
    (avec AVWORKS_HOME qui doit être remplacé par votre répertoire d'installation d'AVWorks)
Edit :
En fait, après enquête, il s'avère que le dysfonctionnement venait d'un problème plus général lié à la mise à jour de l'OS (i.e. une Debian)... Le paramètre noyau net.ipv6.bindv6only était à 1 !!! Ceci privilégie IPv6 avant tout, d'où les problèmes de java qui cherchait à se connecter en IPv6 avec des adresses IPv4. Pour remédier au problème il suffit de mettre net.ipv6.bindv6only=0 dans le fichier /etc/sysctl.d/bindv6only.conf et de lancer invoke-rc.d procps restart pour faire appliquer la nouvelle configuration.

Sources :

sun-java6-jre: net.ipv6.bindv6only=1 breaks java networking

net.ipv6.bindv6only=1 breaks java networking

ERROR: transport error 202: connect failed: Connection refused


lundi 3 mai 2010

Maven comment créer un jar des sources

Il peut arriver que l'on ait besoin de naviguer dans les sources d'un jar fait maison à travers Eclipse. Ce jar fait souvent d'un projet externe au projet courant, mais vous souhaitez pouvoir naviguer ou débugger dans ce code.

Il suffit d'ajouter ceci à votre pom.xml
<project>  ...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>

Ensuite la commande suivante fera l'affaire
$ mvn source:jar-no-fork

La solution se trouve en fait ici avec plus de détails  : http://maven.apache.org/plugins/maven-source-plugin/usage.html

Maven optimiser le déploiement avec un WAR éclaté

Optimiser le temps de déploiement devient parfois crucial pour éviter de perdre des heures... Après avoir tenté des plugins maven (comme cargo par exemple) sans être totalement convaincu par leur efficacité (quand ça marche!), il m'a semblé plus efficace de recourir au script shell.

Maven est idéal pour toutes ces tâches mais nécessite parfois un peu de tuning, notamment lorsqu'on lui demande le packaging de l'application avec le plugin maven-war-plugin. L'opération est laborieuse et nécessite ensuite le déploiement d'un WAR ce qui veut dire que (de façon raccourcie car maven fait plus de choses en réalité) :

  • maven compile l'application
  • Maven copie les classes compilées et les fichiers de ressource dans une structure éclaté (dans le répertoire target)
  • Maven zippe le tout dans un WAR (et c'est long!)
  • ensuite il faut uploader le WAR vers le serveur cible
  • Tomcat ou votre serveur d'application java doit ensuite dézipper l'archive...
Au final vous avez plusieurs opérations couteuses en temps et en ressource. Tout doit être fait manuellement.

Comment accélérer le processus ?

Julien DUBOIS propose sur le blog de Responcia une solution intéressante pour optimiser vos développements avec Jetty. Il apporte la preuve en vidéo que l'on peut améliorer les productivité des développements Java de cette façon. Mais parfois, il n'est pas possible d'opter pour Jetty...

Lire la suite...

jeudi 1 avril 2010

Oracle native JDBC connection extractor

L'utilisation d'API Oracle Java, comme par exemple la SDO API (sdoapi.jar) ou Oracle Network Modeling (sdonm.jar) pour gérer les graphes, nécessite d'avoir une connexion native Oracle. Or les applications sont souvent configurées avec un pool de connexions gérées par le conteneur JEE. Ceci pose alors problème pour les opérations spécifiques qui nécessitent d'avoir une connexion de type oracle.jdbc.OracleConnection et non une connexion de type java.sql.Connection. Cette dernière ne pourra en effet pas être utilisée avec les méthodes des API Oracle en Java.

Il faut alors extraire la connexion native sous-jacente à la connexion du pool de connexions.

Lire la suite...

mercredi 24 mars 2010

Configuration d'un pool de connexions JDBC avec Oracle RAC

La documentation Oracle FCF (Fast Connection Failover) pour configurer un pool de connexion JDBC avec un cluster Oracle RAC 10g est plutôt obscure et manque d'exemple...

Après quelques tâtonnements et en fouillant un peu, voici une configuration sous Tomcat à mettre dans le context.xml de l'application web ou dans server.xml (déconseillé sauf en ressource globale) qui semble plaire à Oracle :

    <Resource name="jdbc/myRacDb" auth="Container"
            type="oracle.jdbc.pool.OracleDataSource"
            factory="oracle.jdbc.pool.OracleDataSourceFactory"
            description="My Oracle RAC DB"
            user="myschema" password="mypass"
            url="jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=rac1.mydomain.com)(PORT=1521))(ADDRESS=(PROTOCOL=TCP)(HOST=rac2.mydomain.com)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=myservice.mydomain.com)))"
            driverClassName="oracle.jdbc.OracleDriver"
            maxActive="10"
            maxIdle="5"
            minIdle="2"
            maxWait="5000"
            connectionCacheName="myCache"
            connectionCachingEnabled="true"
            fastConnectionFailoverEnabled="true"
            onsConfigStr="nodes=rac1.mydomain.com:4200,rac2.mydomain.com:4200"
            connectionProperties="oracle.jdbc.ReadTimeout=30000" />

Cette datasource est accessible via le JNDI sous le nom java:comp/env/jdbc/myRacDb (sous Tomcat).

Le cluster Oracle RAC est ici composé de 2 noeuds rac1.mydomain.com et rac2.mydomain.com. On travaille avec un driver Oracle thin.

Sous tomcat, il faut mettre la librairie Oracle JDBC ons.jar (Oracle Notification Services) dans le common/lib (du moins sous tomcat 5) avec les autres librairies Oracle (ojdbc14.jar et orai18n.jar notamment).

Afin de ne pas avoir d'ennuis, il est obligatoire de mettre l'attribut nodes dans l'onsConfigString et préférable qu'il soit à l'identique de la configuration Oracle (pour cela une bonne communication avec votre DBA est nécessaire).

Nous avons surtout tâtonné pour trouver le type et la factory car la documentation n'est pas orienté tomcat mais plutôt grand serveurs d'applications commerciaux (je vous laisse deviner lesquels!).

Si vous faites des déploiements à chaud sur Tomcat (déconseillé) il est préférable d'enlever le connectionCacheName pour éviter que Tomcat se plaigne de l'existence d'un cache portant le même nom (apparemment pas de nettoyage à chaud!) : vous serez alors obligé de redémarrer votre serveur Tomcat.

dimanche 21 juin 2009

Statistiques Hibernate et EHCache via JMX grâce à Spring

Récemment, chez un client, nous avons dû mettre en place un cache de données pour améliorer les performances de l'application. Même si l'on peut attendre des gains de la mise en place d'un cache de données EHcache, encore faut il affiner le paramétrage en fonction de l'utilisation réelle de l'application. La seule manière de faire est d'avoir des statistiques sur le cache et les requêtes envoyées à la base de données. Mode d'emploi.

Lire la suite...

mercredi 10 juin 2009

Hibernate sans modèle de données avec du SQL natif et la méthode aliasToBean

Chez un client, je découvre dans un bean des getters / setters en doublon : getMaVariable() et getMAVARIABLE(). Quelle horreur!

Le client m'explique que l'équipe de développement a eu des problèmes avec les noms de colonne Oracle lors de la transformation vers un Bean avec la méthode aliasToBean d'Hibernate, car Oracle renvoit systématiquemen des noms de colonne en majuscule... Les getters / setters ne sont alors pas trouvés.
J'ai finalement trouvé comment remédier au problème et je vous fais profiter de la solution.

String q = "SELECT axe, num_route AS numRoute, pr AS prStart, prfin AS prEnd FROM GRAPHE_AXE WHERE id_ech = :idEch";
SQLQuery hq = session.createSQLQuery(q).addScalar("axe").addScalar("numRoute").addScalar("prStart",Hibernate.DOUBLE).addScalar("prEnd",Hibernate.DOUBLE);
List<AxeRange> axeRanges = (List<AxeRange>) hq.setLong("idEch", idEch).setResultTransformer(Transformers.aliasToBean(AxeRange.class)).list();


La réponse est dans ce post : http://www.mail-archive.com/hibernate-dev@lists.jboss.org/msg01043.html
C'est plus un pb lié à la base de données qu'Hibernate

a) mettre les alias portant le même nom que les membres du bean dans la requête SQL
b) utiliser addScalar sur la SQLQuery initiale avec le même nom d'alias mais en case sensitive (correspondant aux membres du bean résultat): de cette façon un mapping sera fait entre le nom donné dans addScalar et l'alias majuscule renvoyé par Oracle
c) éventuellement forcer aussi les types dans addScalar (pour éviter de récupérer des BigDecimal sur des valeurs entières!)
d) utiliser aliasBean comme d'habitude

Sources : pour l'utilisation des query natives dans Hibernate cf. http://docs.jboss.org/hibernate/stable/core/reference/fr/html/querysql.html

JVM 1.5.0_10 crash en mode remote debugging

Apparemment il est possible de faire crasher très simplement une JVM en mode debug. Je viens de le constater sur un serveur Tomcat qui était en mode debug (cf billet sur remote debugging tomcat) sur le port 8000, il suffit tout simplement de lancer une requête HTTP sur celui-ci pour faire planter la JVM :

ERROR: transport error 202: handshake failed - received >GET / HTTP/1.1< - excepted >JDWP-Handshake< ["transport.c",L41]
JDWP exit error JVMTI_ERROR_NONE(0): could not connect, timeout or fatal error

Il est donc vivement conseillé de ne pas mettre vos serveurs en production en mode debug... On vous aura prévenu...

Le bug est connu http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6339385


jeudi 20 mars 2008

Monitoring JVM Tomcat 5 avec jconsole, tunnel SSH et firewall...

Dans billet du 16 mars je présentais un monitoring java avec VisualGC. En réalité, cela n'était qu'une mise en bouche par rapport à ce qui suit... En effet, jconsole, l'outil fournit en standard dans le JDK Sun a aussi tout ce qu'il faut pour réaliser un monitoring Java. Il n'est disponible qu'à partir de java 5.
En fait jconsole est utile en phase de développement. Pour un monitoring persistent, il faudra plutôt s'orienter vers des solutions comme Munin par exemple qui permet une collecte et historisation des données dans le temps, avec surtout le stockage de ces données. Toutes les données jconsole sont perdues lors de l'arrêt de la JVM. Je vais présenter ici un nième exemple de monitoring de Tomcat par jconsole en connexion non sécurisée, Dans l'entête de votre bin/catalina.sh mettez ceci (les lignes en commentaire correspondent justement à celle qui seraient susceptibles de faire marcher le SSL) :

JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.authenticate=false"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.port=8889"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.ssl=false" echo $JAVA_OPTS

jmxremote.authenticate=false : permet d'indiquer que l'on ne veut pas utiliser le fichier de password (jmxremote.password.file)
jmxremote.port=8889 : indique le port sur lequel on va se connecter avec jconsole
jmxremote.ssl=false : désactive le SSL

Ensuite, sur un client disposant d'un JDK on peut lancer jconsole comme suit :
$ jconsole mytomcathost:8889 &


Je lance un appel à ceux ou celles qui auraient réussi une connexion remote en SSL entre jconsole et le serveur à monitorer, car, oui, je l'avoue, mes nombreux essais en suivant scrupuleusement la doc Sun, que ce soit sous linux, voire sous windows (si! si! je passe de temps à autre sous cet OS...), n'ont jamais abouti! Et après de nombreuses recherches, aucun article sur internet ne semble présenter une solution qui fonctionne avec SSL. Si vous avez la solution je suis preneur : envoyez moi l'article! Les lignes suivantes seraient à ajouter :

#JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.password.file=/home/tomcat/jmxremote.password"
#JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.ssl.need.client.auth=true"
#JAVA_OPTS="$JAVA_OPTS -Djavax.net.ssl.keyStore=/home/tomcat/.keystore"
#JAVA_OPTS="$JAVA_OPTS -Djavax.net.ssl.keyStorePassword=changeit"

* UPDATE 15/11/2008 : aurais-je oublié ceci : com.sun.management.jmxremote.registry.ssl=true ??? *

Pour contourner ce problème ainsi que la lourdeur de mise en oeuvre d'une connexion SSL (génération des certificats), on peut utiliser sur une plateforme de type linux d'autres outils qui peuvent prendre en charge la sécurité. Parmi les alternatives possibles, seul le tunneling SSH (avec échange de la clé publique vers le serveur cible) pourra fonctionner, moyennant quelques manipulations manuelles supplémentaires. Car en réalité, ceci n'est pas aussi simple qu'il n'y parait. Si l'on exécute les commandes suivantes sur la machine client :

ssh -L18889:localhost:8889 my.remote.tomcat.host
jconsole localhost:18889

on s'aperçoit en fait que cela ne fonctionne pas... Eh oui : côté serveur 2 ports supplémentaires aléatoires de valeur N et N+1 sont en écoute pour le RMI registry en plus du port jmxremote.port :

$ netstat -tlnp | grep java
tcp6 0 0 ::ffff:127.0.0.1:8006 :::* LISTEN 5374/java
tcp6 0 0 :::8889 :::* LISTEN 5374/java
tcp6 0 0 :::8009 :::* LISTEN 5374/java
tcp6 0 0 :::8080 :::* LISTEN 5374/java
tcp6 0 0 :::40882 :::* LISTEN 5374/java
tcp6 0 0 :::40883 :::* LISTEN 5374/java

jconsole essaye alors de se connecter sur les ports N=40882 et N+1 en localhost sur la machine cliente alors qu'il accède bien grâce à SSH par port forwarding au port 8889! Si vous êtes pressé, la solution consiste à créer 2 tunnels SSH supplémentaires vers ces ports :

ssh -L40882:localhost:40882 my.remote.tomcat.host
ssh -L40883:localhost:40883 my.remote.tomcat.host

Cette méthode n'est pas très "propre", nécessite des opérations manuelles pour trouver les ports dynamiques dès que vous redémarrez votre serveur et ne fonctionnera pas dans un environnement avec certaine configuration de firewall (il faut aussi ajouter -Djava.rmi.server.hostname=localhost au niveau serveur)...

J'ai finalement trouvé sur un des blogs Sun un article intéressant à ce sujet : Connecting Through Firewall Using JMX - Without modifying the server application

Dans cet article Daniel Fuchs explique comment créer son propre JVM agent auquel on pourra passer en option un port fixe.

Liens : Monitoring and Management Using JMX | Monitoring Applications through a Firewall | Keytool documentation

mercredi 19 mars 2008

Remote debugging avec Eclipse d'une application web sous Tomcat

Le débogage d'application web est pénible et qui n'a pas été usé par l'utilisation intensive de traces qui au final n'apportent rien, si ce n'est de pourrir un peu plus l'application et la rendre encore moins performante qu'avant...
Une des solutions pour remédier au problème est probablement le débogage à distance. Celui qui concernera le plus de monde sera probablement le débogage d'une application web sous Tomcat. Elle est très simple à mettre en place. Il suffit d'activer JPDA au niveau de la JVM qui lance le conteneur java et d'indiquer le port sur lequel le client de débogage (Eclipse) pourra se connecter.
Les options JVM sont généralement les suivantes :

Lire la suite...

mardi 18 mars 2008

Monitoring JVM Sun avec jvmstat et VisualGC

Sun propose quelques outils pour monitorer la JVM : jstatd, jvmstat et visualGC. Cela reste rudimentaire par rapport à d'autres outils comme jconsole mais cela a le mérite d'exister! Le principal intérêt d'utiliser encore cet outil est qu'il fonctionne pour les vieilles JVM version 1.4! Cet outil n'est pas packagé directement dans le JDK. Voici comment mettre en place cette techno rapidement en Java 1.4, Java 5 ou Java 6 entre une machine linux sur laquelle réside l'application java à monitorer et un poste de travail windows ou linux

Lire la suite...