Load and/or Stress Test of ICEfaces Web Application using jMeter



Whenever you are developing a RIA application based on ICEfaces, you will most certainly ask yourself: "How do I load test my web app"? Here are the options you have:
  • spend some money and buy a tool like e.g. NeoLoad, which support frameworks like ICEfaces.
  • get yourself a valid EE subscription for ICEfaces, which already includes testing documentation like ICEpack (only available in Standard, Professional, Premium and Corporate Edition).
  • hire a Tech Support Team that professionally tests your web app.
  • invest your own time to develop a "Load Testing Plan" through jMeter, which would then be at no charge to you.
Keep in mind that "free of charge" does not necessarily mean that you are always getting the least expansive application.

Short Overview

Here are the things I would like you to focus on:
  1. Preparing jMeter
  2. Main problems while testing an ICEfaces web app
  3. Creating the basic test plan structure
  4. Recording your page clicks with or without "Think Times"
  5. Modifying your test records to meet your needs
  6. Automate the ICEfaces’ specific replacements in your "Test Plan"
  7. Run the test on jMeter
  8. Conclusion

Preparing jMeter...

First download jMeter from the official Apache site:
http://jakarta.apache.org/site/downloads/downloads_jmeter.cgi
Extract the downloaded archive and execute jMeter through "bin/jMeter(.*)". If you are not already familiar with jMeter I recommend you read the “Getting Started” guide at:
http://jakarta.apache.org/jmeter/usermanual/get-started.html
Tip:If you are using a proxy connection in your network environment you can add your proxy settings to the jMeter start script. Or just write e.g.:
jmeter -H yourProxyHost -P yourProxyPort



To keep it simple and comprehensible, I will use the "address demo" of ICEfaces to demonstrate the load testing procedure, (the address demo is included in all current ICEfaces Binary Packages, downloadable through the official ICEfaces site), but before we start, I like to address main problems which can occur while testing a web application based on ICEfaces.

Main problems while testing an ICEfaces web app

There are several problems when testing an ajax application: First you have to figure out which the dynamic content is in the http request and which one the static one is. When searching for information in the common forums you will mostly find “how to” questions for example: "How can I fetch the current user session", "Do I need the rand parameters", "What's about the container sessions" and so on... but no established test procedures. I have prepared a short list of what needs to be addressed prior to when sending a http request to the server:
  • ice.session
  • ice.view - Current view-id of ICEfaces
  • javax.faces.ViewState - javax.faces.ViewState field is written for both server and client side state saving. The content of the value attribute for the hidden field will be different between the settings.
Is a rand parameter really needed, I would say no since it doesn't change my current results. What about the container session-id of JSF? - Must not be set either, since it is automatically managed.
Ok, you say, now I know what must be changed, but where can I find what I need so I can replace it later? – Well, if you open your web app in your preferred browser (I would recommend Firefox with FireBug extension) and view the source code you will find something like this:

ice.session and ice.view are shown in the java script section....and this is it.
The last missing variable is "javax.faces.ViewState". You will find it in the same source document, simply search for "javax.faces.ViewState" and you should see a line that reads as follows:
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="1" />
Last but not least make sure that you have disabled the “Concurrent DOM Views”. This is really important, because “onunload()” is not called for simulated user (the method normally interact with ICEfaces for cleaning-up resources).
For more information see http://www.icefaces.org/docs/v1_8_2/htmlguide/devguide/keyConcepts11.html.

Creating the basic testplan structure

Using the information (about which content must be replaced) that we have gathered so far, we can start building the web test plan:
  1. right click on Testplan -> Add -> Thread (Users) -> Thread Group ("Thread Group" is also known as the "entry point" of a testplan).
  2. add the following child nodes to "Thread Group":
    • 1 x "HTTP Request Defaults"
    • 1 x "HTTP Cookie Manager"
    • 3 x "Regular Expression Extractor"
    • 1 x "View Result Tree"

Thread Group

The value for "Number of Threads" defines the simulated concurrent users. Ramp-Up defines with which amount delay the simulated users start their work.

HTTP Request Defaults

Enter your Server Name or IP and specify the port. For protocol, set value to "http".

HTTP Cookie Manager

The "cookie manager" is needed to support cookies in our test session. You can leave the cookie manager fields untouched.

Regular Expression Extractor

Here we define the logic to gain the dynamic variables values:

ice.session

  • Reference Name: iSession
  • Regular Expression: ,session: '(.+?)'
  • Template: $1$
  • Match No. : 0

ice.view

  • Reference Name: iView
  • Regular Expression: ,view: (.+?)
  • Template: $1$
  • Match No. : 0

javax.faces.ViewState

  • Reference Name: jView
  • Regular Expression: <input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="(.+?)" />
  • Template: $1$
  • Match No. : 0

View Result Tree

Add this listener in order to see all sample responses in a tree. No configuration needed.
Tip: Simple Data Writer: by adding this element you can write your results directly into a file. Next, you need to specify a directory and filename of the output file. Outputting the data via XML is a nice way for publishing your test results to customers; how this can be done - read on here: https://qants.wordpress.com/2009/08/28/jmeter-results-analysis-using-pivot-tables-in-excel
Finally, your resulting test plan should look like the following:

Recording your page clicks with or without "Think Times"

In the step before we finished with our initial test plan, so it's time for recording the stuff now :-) Ok, not yet. We need a record tool to do this - But no problem, jMeter gives us the power to do that without any third party products. Just go back to jMeter-GUI:
  1. Add a proxy (On Workbench, add -> none test element -> Http Proxy Server)

Http Proxy Server

To be defined:
  • Port: Use a free high-port (Ports greater than 1024), in example 9090
  • Target Controller: Select "Testplan > ThreadGroup"
With URL-Patterns we have the possibility to define what we want to record and what not. What we need:
  • file extension of our pages (normally extension is xhtml or iface)
  • "send-receive update" communication
That's it. In our case add the following entries to the include pattern:
  • .+\/address\/$
  • .+\/block\/send-receive-updates.*
  • .*\.iface
The first line depends on your web app name, second one is always the same. The third line depends on your file extension, in relation to "address demo" the extension is "iface".

Constant Timer for "Think Times" (optional)

If you want to record "think times" too, then the only thing you have to do is to add a constant timer (On Proxy, add -> Timer -> Constant Timer) with the value "${T}" in thread delay.
Your resulting workbench should look like the following:

Time for the Record...

Start your preferred web browser (I can't recommend IE here, it often crashes while recording), close all tabs (empty page). We have to configure the proxy connection first before we start recording. To do this, we use the proxy port number that we defined in jMeter "Http Proxy" before ("9090"). The next picture shows how to do this:

This step was shown on IE8 (yesss i tried it :-D), but it is still the same procedure on any other browser as FireFox, Opera, Chrome and so on. If the configuration work is done, switch back to jMeter and simply click the "Start Button" in "Http-Proxy" Tab. Next go to your browser and type in your test page address. Navigate through your test relevant pages... if you are finished, don't forget to click the stop button in jMeter. Your resulting Testplan should look like the following:

As you can see, our proxy recorded all HTTP-Requests and put them into our test plan. Ok, save the test plan now (make also a second backup of this plan).

Modifying your test records to meet your needs

This is one of the most important points. If we take a look at our recorded results we can see the parameters that we focused on before:

The manual steps would be, to search for every:
  • ice.session
  • ice.view
  • javax.faces.ViewState
in all http requests, and replace their values with the placeholders that we defined in the "Regex Extractors". For example: the value of ice.session must be replaced with "${iSession}", and so on. Just imagine what it takes time wise to do this on big site records - thus, I have worked out a smart solution.

Automate the ICEfaces specific replacements in your "Test Plan"

Remember, we saved the webtest-plan before. It is interesting to note, that the plan was saved in pure xml. So, I thought, cool... lets script some xslt to automate these boring and time wasting replacements. Download the XSLT 2 script here. For those people who are not familiar with XSLT, no worry there is really not much to do.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <!--  ===========================  NaRo ============================ -->
  <!--  ============================================================== -->
  <!-- lobal variables -->
  <!--  ============================================================== -->
  <xsl:variable name="firstIDcollection">
    <xsl:copy-of select="//stringProp[preceding-sibling::stringProp='ice.session' and @name='Argument.value' and not(text()='${iSession}')]" />
  </xsl:variable>
  <xsl:variable name="firstID" select="$firstIDcollection/*[position()=1]" />
  <!--  ============================================================== -->
  <xsl:template match="/">
    <xsl:apply-templates />
  </xsl:template>
  <xsl:template match="*|@*">
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>
  <xsl:template match="elementProp[@name=$firstID]">
    <xsl:copy>
      <xsl:attribute name="name">
        <xsl:value-of select="'${iSession}'" />
      </xsl:attribute>
      <xsl:apply-templates select="@*[not(name()='name')]" />
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>
  <xsl:template match="stringProp[@name='Argument.value']">
    <xsl:choose>
      <xsl:when test="preceding-sibling::stringProp[@name='Argument.name' and text()='ice.session']">
        <xsl:copy>
          <xsl:copy-of select="@*" />
          <xsl:value-of select="'${iSession}'" />
        </xsl:copy>
      </xsl:when>
      <xsl:when test="preceding-sibling::stringProp[@name='Argument.name' and text()='ice.view']">
        <xsl:copy>
          <xsl:copy-of select="@*" />
          <xsl:value-of select="'${iView}'" />
        </xsl:copy>
      </xsl:when>
      <xsl:when test="preceding-sibling::stringProp[@name='Argument.name' and text()='javax.faces.ViewState']">
        <xsl:copy>
          <xsl:copy-of select="@*" />
          <xsl:value-of select="'${jView}'" />
        </xsl:copy>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy>
          <xsl:apply-templates select="@*" />
          <xsl:apply-templates />
        </xsl:copy>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  <xsl:template match="stringProp[@name='Argument.name' and parent::elementProp[@name=$firstID] and text()=$firstID]">
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:value-of select="'${iSession}'" />
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>
  1. Go to http://saxon.sourceforge.net
  2. Download Saxon-HE for Java
  3. Extract it
  4. Create a directory somewhere on your disk and name it "convert". Be sure that there are no white-spaces in the path.
  5. Copy the saxon.jar from extracted folder to "convert"
  6. Copy your test plan and the (downloaded) XSL script to "convert" directory
  7. Open command line and switch to "convert" folder
  8. enter: "java -jar saxon9he.jar -t -s:yourTestplan.jmx -xsl:jmeter2iceMeter.xsl -o:newTestplan.jmx"
Now open newTestplan.jmx in jMeter again... and you will find that every replacement is done for you.

Run the test on jMeter

Well done, the main configuration work should now be done. You may want to increase the number of concurrent users in the Thread Group before starting your tests.
Tip: To add files, check the "Retrieve all embedded resources from html files"

Conclusion

If you know how to handle a session in test mode, it should not be very complicated to build it up. Please keep in mind that this solution can't replace a real user test scenario, but it may help to find out the bottle necks of your application and keep the costs low.
Improvements to this procedure are always welcome, so don't hesitate to comment on this or contact me if you think there is an error/mistake or sth. else. Happy Testing!

9 comments:

  1. Doesn't dispose-views of the blocking servlet take care of the cleanup?

    ReplyDelete
  2. Btw we are using "The Grinder" (grinder.sf.net) to create our Icefaces (1.8.2) load tests. IceFaces is running in portlets in a portal server environment.

    The Grinder is very powerful because the recorded scripts are Jython scripts, so you have the full power of java and python at hand in your load test script.

    ReplyDelete
  3. Very helpful. Thanks a lot!

    ReplyDelete
  4. Great article! Do you know how to configure JMeter for ICEFaces portlets? Due the fact that you can have more than one portlet in a page you will end up with more than one ice.view as well.

    ReplyDelete
  5. Very very very helpful. Thanks!! :)

    ReplyDelete
  6. Hi, how can I contact with you?
    I am load testing iceface web app with JMeter, I still encounter some problem after reading your article.

    ReplyDelete
  7. I can't describe how thankful I am! Great article, you helped me a lot!!! Greetings from Serbia!

    ReplyDelete
  8. Hello,

    Great blog, thank you!
    I followed all the steps above. Unfortunately I still face an issue.
    1. Load the page containing the Icefaces app
    2. Use JMeter to extract the iSession, iView and jView
    3. Do the second request in my app ------> now I get session expired as response from the sever

    I use Liferay 5.x and Icefaces 2.x with Jmeter 2.11.

    Can someone please suggest how could I debug the issue?

    Many thanks.

    ReplyDelete
    Replies
    1. I guess it was/is related to the CookieManager. Did you add this component?

      Delete