Yourkit et Tomcat5 sous Windows

Rédigé par gorki - - Aucun commentaire

Le problème :

Connecter Yourkit à un Tomcat installé localement sur une machine Windows

Tomcat exécuté en tant que service avec le profil Administrateur

Solution :

Bon rappel de ce qui est décrit ici

  • Tomcat5 :  installation / désintallation du service
  • Tomcat5w : fenetre de contrôle du service et de ses paramètres

Il faut donc exécuter ces programmes dans une console en ligne de commande "admin" :

# Démarrer
# Exécuter... 
cmd
# Valider avec : "Ctrl + Shift + Enter"

Sinon, on peut toujours faire bouton droit sur Tomcat5w et "exécuter en tant qu'Administrateur".

Ensuite, ajouter le lien vers l'agent Yourkit :

  • Il faut mettre les chemins courts de Windows !
  • De cette manière :
# Nom court sous windows : 
dir /x c:\
# Pour obtenir au final le chemin complet : 
dir C:\PROGRA~2\YOURKI~1.6\bin\win32\yjpagent.dll

Ajouter le chemin aux paramètres de la JVM du service Tomcat :ourkit

-agentpath:C:\PROGRA~2\YOURKI~1.6\bin\win32\yjpagent.dll

Et ne pas oublier de démarrer Yourkit en mode Administrateur.

Retour d'expérience SAHI

Rédigé par gorki - - Aucun commentaire

Le problème :

Sahi, ça marche ou ça marche pas ?

Solution :

Bah euh.... difficile à dire.

Sahi c'est un peu comme Selenium, avec évidemment des différences listées ici.

L'utilisation : faire des tests de non-régression sur une IHM GWT.

La cible : Chrome sous Windows

 

Les plus (+) :

- le studio d'enregistrement marche bien

- il est possible d'ajouter des fonctions custom dans les scénarios.

- il est facile de l'intégrer avec Jenkins (en générant des rapports spéciaux)

 

Les moins (-) :

** sur les architectures testées, ça marche plus ou moins bien :

Jenkins + Chrome sous Linux en local :

(sans interface graphique, i.e. sans server X, i.e. headless, i.e avec XVFB) : ça marche mal : problème de stabilité / reproductibilité. Penser à exporter le server X sans XVFB pour débugger les problèmes Chrome Linux.

Jenkins+ Chrome sous Windows à distance :

pas parfait, mais mieux. Les problèmes ne viennent pas forcément de Sahi, mais des VM

Dans les deux cas : Chrome relancé entre chaque test (résultats pas constants) vs. Chrome utilisé en single session (mieux)

 

** la configuration de base marche mal :

  1. Si Chrome est arrêté entre chaque test, il est killé (cf os.properties) => le profile Chrome est alors corrompu
    • il vaut mieux remplacer dans le browser.xml le chemin vers Chrome par un shell à vous pour dézipper un profile clean avant de relancer.
    • du coup préparer votre profile et dézipper le avant chaque lancement de navigateur
  2. Pour bien être intégrer à Jenkins, mettre sous le gestionnaire de sources (dans des répertoires différents, ils n'évoluent pas au même rythme, pas la peine de vérifier les binaires sahi à chaque déploiement) :
    • userdata,
    • binaires sahi,
    • scripts
  3. Utiliser dans ces scripts les variables pour bien maitriser l'environnement :
    • $WORKSPACE de Jenkins
    • $SAHI_HOME
    • ...
  4. Les scripts de base dans userdata ne sont pas très robustes :
    • Pas de kill de Chrome / Arrêt de Sahi si on kill le build Jenkins
    • Il faut bien nettoyer les logs avant de démarrer SAHI
    • Pas d'attente de démarrage de SAHI
    • La sélection des process à killer est plus ou moins hasardeuse à mon avis, on est resté en singleThread

Et ce n'est qu'une vue rapide.

Conclusion :

Bref, des bonnes idées, mais ce n'est pas du "sur étagère", il y a du boulot pour qu'il soit intégré et robuste, sans parler de la rejouabilité des tests que je ne trouve pas exceptionnelle.

Mais bon quelle idée de contrôler un soft qui n'est pas fait pour être contrôlé (les navigateurs ne sont pas pensé pour ça !!)...

Entre Sahi et Selenium ? Celui que vous connaissez le mieux.

 

Quelques scripts :

Exemple de script de démarrage et attente du lancement de SAHI :

#!/bin/bash

./check.sh
if [ $? -eq 1 ] ; then
        echo "Environnement incorrect"
        exit 1
fi


echo --------
echo SAHI_HOME: $SAHI_HOME
echo SAHI_USERDATA_DIR: $SAHI_USERDATA_DIR_TMP
echo SAHI_EXT_CLASS_PATH: $SAHI_EXT_CLASS_PATH
echo --------

#rm -rf $SAHI_USERDATA_DIR/temp/*
#rm -rf $SAHI_USERDATA_DIR/logs/*
#rm -rf $SAHI_USERDATA_DIR/database/*

OUTPUT_LOG=$SAHI_USERDATA_DIR/logs/sahi_output.log

export POI_JARS=$SAHI_HOME/extlib/poi/excelpoi.jar:$SAHI_HOME/extlib/poi/poi-3.7-20101029.jar:$SAHI_HOME/extlib/poi/dom4j-1.6.1.jar:$SAHI_HOME/extlib/poi/poi-ooxml-3.7-20101029.jar:$SAHI_HOME/extlib/poi/poi-ooxml-schemas-3.7-20101029.jar:$SAHI_HOME/extlib/poi/xmlbeans-2.3.0.jar
SAHI_CLASS_PATH=$SAHI_HOME/lib/sahi.jar:$SAHI_HOME/extlib/rhino/js.jar:$SAHI_HOME/extlib/apc/commons-codec-1.3.jar:$SAHI_HOME/extlib/db/h2.jar:$SAHI_HOME/extlib/license/truelicense.jar:$SAHI_HOME/extlib/license/truexml.jar:$POI_JARS

cd $SAHI_HOME
java -Xmx512m -Djava.io.tmpdir=$SAHI_USERDATA_DIR/temp -classpath $SAHI_EXT_CLASS_PATH:$SAHI_CLASS_PATH net.sf.sahi.Proxy "$SAHI_HOME" "$SAHI_USERDATA_DIR" &> $OUTPUT_LOG &
echo $! > $SAHI_USERDATA_DIR/temp/sahi.pid

echo "Attente du demarrage de sahi"

i=0
STATUS_CODE=1
TIMEOUT=20
while [ $STATUS_CODE -eq 1 ] && [ $i -lt $TIMEOUT ]; do
        cat $OUTPUT_LOG | grep "Finished preparing report" > /dev/null
        STATUS_CODE=$?
        echo -n "."
        sleep 1
        ((i++))
done
echo "Serveur sahi OK"

Exemple de script de lancement de tests et kill de Chrome en fin de script (le userdata.properties est créé à la volé pour les paramètres tel que le port du proxy) :

#!/bin/bash

./check.sh
if [ $? -eq 1 ] ; then
        echo "Environnement incorrect"
        exit 1
fi
URL=$1
BUILD_ID=$2
DEBUG=$3

if [ ! $URL ] || [ ! $BUILD_ID ]
then
        echo "usage : start_and_run.sh <URL> <BUILD ID>"
        exit 1
fi

# Generation du fichier de configuration user
echo "############################################" > $SAHI_USERDATA_DIR/config/userdata.properties
echo "# Fichier généré automatiquement, ne pas modifier (cf start_and_run.sh" >> $SAHI_USERDATA_DIR/config/userdata.properties
echo "############################################" >> $SAHI_USERDATA_DIR/config/userdata.properties
cat $SAHI_USERDATA_DIR/config/userdata.properties.template  >> $SAHI_USERDATA_DIR/config/userdata.properties
printf "\n" >> $SAHI_USERDATA_DIR/config/userdata.properties
echo "proxy.port=$SAHI_PROXY_PORT" >> $SAHI_USERDATA_DIR/config/userdata.properties

# There is only one bypass list for both secure and insecure.
$SAHI_USERDATA_DIR/bin/start_sahi.sh

trap 'cat $SAHI_USERDATA_DIR/temp/sahi.pid | xargs kill' SIGINT SIGTERM EXIT

echo "Attente lancement de SAHI"
sleep 2

if [ "$DEBUG" = "DEBUG" ] ; then
        $SAHI_USERDATA_DIR/bin/testrunner.sh tests.suite $URL chromedebug $BUILD_ID
else
        $SAHI_USERDATA_DIR/bin/testrunner.sh tests.suite $URL chrome $BUILD_ID
fi

 

Java ne fonctionne pas avec Firefox

Rédigé par gorki - - Aucun commentaire

Le problème :

Un plugin Java obsolète, désinstallation, réinstallation, pas de java dans le navigateur.

Solution :

Réponse rapide : si vous avez un Java 64 bits, alors il vous faut un Firefox 64 bits, sinon la solution conseillée est d'installer une JRE 32 bits.

Désinstallation de plugin :

  1. désintaller le programme associé (Java, flash, VLC, etc...)
  2. savoir où est le plugin :  about:plugins dans la barre d'adresse de Firefox
  3. les plugins peuvent être
    • dans le répertoire plugins de Firefox : <home firefox>/plugins
    • inscrits dans la base de registre :
      • HKEY_CURRENT_USER\Software\MozillaPlugins (32 bits)
      • HKEY_LOCAL_MACHINE\SOFTWARE\MozillaPlugins (32 bits)
      • HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\MozillaPlugins (64 bits)

Comme les plugins sont des DLLs intégrées dans Firefox, il semble donc qu'un programme 32 bits ne peut pas charger des DLL 64 bits (confirmé ici , Quote:

"While running a fully 64-bit Windows system sounds great,
the reality is that you'll very likely need to run Win32
code for a while. Towards that end, x64 versions of Windows
include the WOW64 subsystem that lets Win32 and Win64
processes run side-by-side on the same system. However,
loading your 32-bit DLL into a 64-bit process, or vice
versa, isn't supported."
)

@EJB @Asynchronous ExceptionExecution

Rédigé par gorki - - Aucun commentaire

Le problème :

La récupération des résultats des EJB asynchrone.

La technique de base pour déclencher un traitement asynchrone :

  1. créer une méthode taggué "@Asynchronous", la méthode retourne un Future<?>
  2. l'appeler
  3. attendre le retour

Souvent, on déclenche X fois la méthode, le tout passe dans une boucle :

  1. Pour toutes les tâches
    1. créer une méthode taggué "@Asynchronous", la méthode retourne un Future<?>
    2. l'appeler
    3. stocker le Future
  2. attendre que tous les Futures soit terminés
  3. récupérer les résultats

On part du principe que la méthode asynchone gère correctement ses erreurs à son niveau (un try/catch Exception avec un retour Future correct).

Seulement, certaines exceptions peuvent être remontées par le container : transaction timeout, erreur lors du commit, etc... sous forme d'ExecutionException. Et là, si le code est mal fait, on peut avoir des surprises et ne pas savoir dire ce qui s'est passé exactement

Solution :

Il faut donc penser lors de la récupération des résultats à :

  1. prévoir un try/catch d'ExecutionException / CancellationException / InterruptedException pour les Future.get()
  2. bien le mettre PAR Future, puisque chaque Future.get() va pouvoir lancer sa propre exception. (Certaines tâches peuvent provoquer une erreur au commit et pas d'autre)
  3. gérer un état global de récupération du résultat (est-ce que le code est correct si seulement une partie des tâches ont réussi ?)
  4. la gestion de traces et d'erreurs associés pour permettre de retrouver les tâches en erreur.

Exemple :

  • ici, le résultat est retourné dans une map
  • sinon le bean lui-même est retourné avec l'exception, il contient des maps pour stocker les erreurs et les futures
  • la gestion de traces et d'erreur est faite par l'appelant, les informations nécessaires pour décrire le contexte métier du Future sont dans un bean "FutureDescription"
// Attributs : 
private Map<Future<R>, FutureDescription> futures = new ConcurrentHashMap<Future<R>, FutureDescription>();
private Map<Future<R>, Exception> futureException = new ConcurrentHashMap<Future<R>, Exception>();

...

public Map<Future<R>, R> extractResultsMap(boolean throwException) throws MultiThreadException {

Map<Future<R>, R> results = new HashMap<Future<R>, R>();

// parcours de chaque Future pour récupérer son résultat d'exécution
for (Future<R> future : futures.keySet()) {
   try {
         results.put(future, future.get());
   } catch (InterruptedException | ExecutionException e) {
         futureException.put(future, e);
   }
}
if (throwException && (futureException.size() > 0)) {
    throw new MultiThreadException(ErrorCode.TECHNICAL_GLOBAL, "Erreur pendant la recuperation des resultats", this);
}
return results;

}

 

 

Couplés avec :

  • une classe qui fait du monitor pour tracer l'avancement des threads
  • une classe qui stocke les Future, leur résultats, leurs exceptions
  • des méthodes utilitaires

Cela donne les outils nécessaire pour gérer facilement l'asynchronisme.

@EJB @Singleton et ConcurrencyManagement

Rédigé par gorki - - Aucun commentaire

Le problème :

Un EJB singleton qui sert de cache remonte une erreur avec JBoss 7.1.1 :

EJB Invocation failed on component XXXX for method public java.lang.Object:
javax.ejb.EJBTransactionRolledbackException: JBAS014373: EJB 3.1 PFD2 4.8.5.5.1
concurrent access timeout on org.jboss.invocation.InterceptorContext$Invocation@398e7b17 
- could not obtain lock within 5000MILLISECONDS

Solution :

Tout est déjà écrit bien sur, encore aurait-il fallu le lire :)
Par défaut les EJB @Singleton sont

  1. @ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)

  2. protégés par un @Lock(LockType.WRITE)

donc les méthodes sont synchronisés par le container. On peut

  • soit passer la méthode ou la classe en LockType.READ,
  • soit passer en Bean-managed (@ConcurrencyManagement(ConcurrencyManagementType.BEAN)). Là c'est vous qui gérer le tout.

Pour reproduire systématiquement, il suffit de mettre un sleep de 6s dans la méthode problématique.

Note:

  • Si le pool des ejb n'est pas assez grand on a un permit timeout et non un concurrent ascess timeout.
  • Le timeout sous JBoss est configuré dans cette section très intéressante :
<subsystem xmlns="urn:jboss:domain:ejb3:1.2">
            <session-bean>
                <stateless>
                    <bean-instance-pool-ref pool-name="slsb-strict-max-pool"/>
                </stateless>
                <stateful default-access-timeout="5000" cache-ref="simple"/>
                <singleton default-access-timeout="5000"/>
            </session-bean>
            <pools>
                <bean-instance-pools>
                    <strict-max-pool name="slsb-strict-max-pool" max-pool-size="20" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
                    <strict-max-pool name="mdb-strict-max-pool" max-pool-size="20" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
                </bean-instance-pools>
            </pools>
            <caches>
                <cache name="simple" aliases="NoPassivationCache"/>
                <cache name="passivating" passivation-store-ref="file" aliases="SimpleStatefulCache"/>
            </caches>
            <passivation-stores>
                <file-passivation-store name="file"/>
            </passivation-stores>
            <async thread-pool-name="default"/>
            <timer-service thread-pool-name="default">
                <data-store path="timer-service-data" relative-to="jboss.server.data.dir"/>
            </timer-service>
            <remote connector-ref="remoting-connector" thread-pool-name="default"/>
            <thread-pools>
                <thread-pool name="default">
                    <max-threads count="10"/>
                    <keepalive-time time="100" unit="milliseconds"/>
                </thread-pool>
            </thread-pools>
        </subsystem>

 

 

Fil RSS des articles de ce mot clé