jeudi 31 janvier 2008

JMeter et JFaces

Afin de tester une application, je suis en train de préparer un test de performances à l'aide de l'outils JMETER.

JMETER à l'aide de son proxy enregistre toutes les requêtes avec paramètres d'entête et de corps. Parmi eux, le paramètre com.sun.faces.VIEW comporte l'état de la vue encodé d'une manière complexe. La valeur de ce champs dépend de l'état des composants et de la structure de l'arbre de composants qui représente la page courante.


L'implémentation de JSF sauvegarde l'arbre de composant et les états associés à chaque composant dans un flux de byte qui est ensuite encodé en base64.(C'est une sérialisation.)
La méthode
com.sun.faces.renderkit.ResponseStateManagerImpl.writeState :


public void writeState(FacesContext context, SerializedView
view)
throws IOException {
ByteArrayOutputStream bos = null;
String hiddenField = null;

bos = new ByteArrayOutputStream();
ObjectOutput output = new ObjectOutputStream(bos);
output.writeObject(view.getStructure());
output.writeObject(view.getState());

hiddenField = " <input type=\"hidden\" name=\""
+ RIConstants.FACES_VIEW + "\"" + " value=\"" +
(new String(Base64.encode(bos.toByteArray()),
"ISO-8859-1")) +
"\" />\n ";
context.getResponseWriter().write(hiddenField);
}



Lorsque l'utilisateur soumet la page ce champs est décodé par la méthode getTreeStructureToRestore de la même classe.

public Object getTreeStructureToRestore(FacesContext
context,
String treeId) {

Object structure = null;
Object state = null;

Map requestParamMap = context.getExternalContext()
.getRequestParameterMap();

String viewString = (String) requestParamMap.get(
RIConstants.FACES_VIEW);
if (viewString == null) {
return null;
}
byte[] bytes = Base64.decode(viewString.getBytes());

try {
ObjectInputStream ois = new ObjectInputStream(
new ByteArrayInputStream(bytes));
structure = ois.readObject();
state = ois.readObject();
Map requestMap =
context.getExternalContext().getRequestMap();
// store the state object temporarily in request
scope until it is
// processed by getComponentStateToRestore which
resets it.
requestMap.put(FACES_VIEW_STATE, state);
ois.close();
} catch (java.io.OptionalDataException ode) {
log.error(ode.getMessage(), ode);
} catch (java.lang.ClassNotFoundException cnfe) {
log.error(cnfe.getMessage(), cnfe);
} catch (java.io.IOException iox) {
log.error(iox.getMessage(), iox);
}
return structure;
}



Si cette valeur est incorrecte, cela ne peut pas fonctionner il n'est pas possible de reconstituer l'objet qui a été sérialisé. C'est comme d'ouvrir un fichier exe avec un
éditeur hexadécimal et de supprimer ou d'ajouter des bytes par ci par là, et d'espèrer que le programme va s'éxécuter correctement. En général, si ce champs est incorrect JSF plante.

Aucun commentaire: