Recently I’ve been given the task to stress test a Liferay 6 portal with several IceFaces portlets on each page. I found the combination quite challenging, so here the approach I used to get it done.

A good starting point was this article describing how to do JMeter tests with plain IceFaces applications. In a few words: JSF and IceFaces add a bunch of dynamic information to each html form, which needs to parametrized in the JMeter script. With JSF 2 and IceFaces 3 the following parameters have to be handled:

  • ice.view
  • ice.window
  • javax.faces.ViewState
  • javax.faces.encodedURL
  • and, if you use single submit: -single-submit

You can get the actual values for this parameters by applying regular expression to the HTML response body of the prior request. But unfortunately, within a portal it’s a bit more complicated. Because each portlet on a portal page have it’s own javax.faces.ViewState and it’s own ice.view value and so on. So you have to tweak the regular expression to only match the fields within the HTML form which is actually going to be submitted.

Liferay adds a prefix to form and input name, which should be calculated the same way on every server (if you’re lucky). In the example below the prefix is A8901:

  <form action="<PORTAL_URL>" class="iceFrm" enctype="application/x-www-form-urlencoded" id="A8901:form" method="post" onsubmit="return false;">
    <input name="A8901:form" type="hidden" value="A8901:form" /> 
    <input name="javax.faces.encodedURL" type="hidden" value="<PORTAL_URL>" />
    <input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="2576275416532915251:-8728981932903525164" autocomplete="off" />
    <input name="ice.window" type="hidden" value="mlh540wgif" />
    <input name="ice.view" type="hidden" value="vlahktfsa" />

    <!-- The actual form -->

  </form>

And that’s still not it: Liferay also adds an authentication token (p_auth) to some URLs to prevent cross site scripting (XSS), this token also has to be extracted from subsequent responses.

So, the following Regular Expression Extractors has to be added to your Test plan (Add -> Post Processors -> Regular Expression Extractor):

name: Extract ice.view of Portlet A8901:

  • Reference Name: iceView
  • Regular Expression: id=”A8901:form” .*?
  • Template: $1$
  • Match No.: 1

Name: Extract ice.window of Portlet A8901:

  • Reference Name: iceWindow
  • Regular Expression: id=”A8901:form” .*?
  • Template: $1$
  • Match No.: 1

Name: Extract JSF ViewState of Portlet A8901:

  • Reference Name: jsfViewState
  • Regular Expression: id=”A8901:form” .*?
  • Template: $1$
  • Match No.: 1

Name: Extract JSF ViewState of Portlet A8901:

  • Reference Name: jsfEncodedURL
  • Regular Expression: id=”A8901:form” .*?
  • Template: $1$
  • Match No.: 1

Name: Extract JSF ViewState of Portlet A8901Extract Liferay p_auth:

  • Reference Name: pAuth
  • Regular Expression: [;?]p_auth=(.+?)&
  • Template: $1$
  • Match No.: 0

After recording the test (see JMeter documentation for that), the Parameter values can be replaced by the Reference Names from the regular expression extractors above:

If you find request URLs which contain something like p_auth=abcdef, replace it with p_auth=${pAuth). The login request for example will contain such a token.

To do all the replacement manually is a lot of work, so I’ve created a XSL script to transform JMeter files accordingly (actually I’ve just adapted the script from the starting point I mentioned above):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

	<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="stringProp[@name='Argument.value']">
		<xsl:choose>
			<xsl:when
				test="preceding-sibling::stringProp[@name='Argument.name' and text()='ice.view']">
				<xsl:copy>
					<xsl:copy-of select="@*" />
					<xsl:value-of select="'${iceView}'" />
				</xsl:copy>
			</xsl:when>
			<xsl:when
				test="preceding-sibling::stringProp[@name='Argument.name' and contains(text(), '-single-submit')]">
				<xsl:copy>
					<xsl:copy-of select="@*" />
					<xsl:value-of select="'${iceView}-single-submit'" />
				</xsl:copy>
			</xsl:when>
			<xsl:when
				test="preceding-sibling::stringProp[@name='Argument.name' and text()='ice.window']">
				<xsl:copy>
					<xsl:copy-of select="@*" />
					<xsl:value-of select="'${iceWindow}'" />
				</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="'${jsfViewState}'" />
				</xsl:copy>
			</xsl:when>
			<xsl:when
				test="preceding-sibling::stringProp[@name='Argument.name' and text()='javax.faces.encodedURL']">
				<xsl:copy>
					<xsl:copy-of select="@*" />
					<xsl:value-of select="'${jsfEncodedURL}'" />
				</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 contains(text(), '-single-submit')]">
		<xsl:copy>
			<xsl:apply-templates select="@*" />
			<xsl:value-of select="'${iceView}-single-submit'" />
		</xsl:copy>
	</xsl:template>
</xsl:stylesheet>

The simplest way to apply this script is to store your JMeter .jmx file in a Eclipse project, select from the context menu Run as… -> Run Configurations and create a new XSL run configuration:

From now on you can replace all parameters by simply selecting the created run configuration.

Leave a Reply

Your email address will not be published. Required fields are marked *


*