Showing posts with label icefaces. Show all posts
Showing posts with label icefaces. Show all posts
One of the most important features of style sheets is that they specify how a document is to be presented on different media: screen, beamer, printer, with a speech synthesizer etc.

When you take a look at the ICEfaces TLD Reference you will ascertain, that the media attribute is not supported for ice:outputStyle tag.

When you now say, no problem - I'll take the normal HTML "link"-tag to do the same, you are wrong. When you need components like ice:inputFile, you must define your style sheets with ice:outputStyle. Otherwise your iFrame (where the input file component is placed) will not include the style information from the top site.

Here are the things I would like you to focus on:
  1. Change component class
  2. Change renderer class
  3. Register the custom classes in faces config
  4. Test and Hints

Change component class

At first we need the OutputStyle class from  ICEfaces svn http://www.icefaces.org/main/community/svninfo.iface.

My examples are based on ICEfaces V1.8.2 RC2.

Todo:
  • set a private string member "media"
  • implement public getter and setter methods for media
  • extend the existing saveState and restoreState methods with some "media" stuff. That should be self explanatory.
Here is the new OutputStyle class:

package com.yourcompany.faces.custom.component;

// Version: MPL 1.1/GPL 2.0/LGPL 2.1
//
// "The contents of this file are subject to the Mozilla Public License
// Version 1.1 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations under
// the License.
//
// The Original Code is ICEfaces 1.5 open source software code, released
// November 5, 2006. The Initial Developer of the Original Code is ICEsoft
// Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
// 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
//
// Contributor(s): _____________________.
//
// Alternatively, the contents of this file may be used under the terms of
// the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
// License), in which case the provisions of the LGPL License are
// applicable instead of those above. If you wish to allow use of your
// version of this file only under the terms of the LGPL License and not to
// allow others to use your version of this file under the MPL, indicate
// your decision by deleting the provisions above and replace them with
// the notice and other provisions required by the LGPL License. If you do
// not delete the provisions above, a recipient may use your version of
// this file under either the MPL or the LGPL License."


import javax.faces.component.UIComponentBase;
import javax.faces.el.ValueBinding;
import javax.faces.context.FacesContext;

public class OutputStyle extends UIComponentBase {

    public static final String COMPONENT_TYPE =
            "com.icesoft.faces.OutputStyleComp";
    public static final String COMPONENT_FAMILY =
            "com.icesoft.faces.OutputStyle";
    public static final String DEFAULT_RENDERER_TYPE =
            "com.icesoft.faces.style.OutputStyleRenderer";

    private String href;
    private String userAgent;
    private String media;

    public OutputStyle() {
        super();
    }

    public String getFamily() {
        return COMPONENT_FAMILY;
    }

    public String getRendererType() {
        return DEFAULT_RENDERER_TYPE;
    }

    public String getHref() {
        if (href != null) {
            return href;
        }
        ValueBinding vb = getValueBinding("href");
        if (vb != null) {
            return (String) vb.getValue(getFacesContext());
        }
        return null;
    }

    public String getUserAgent() {
        return userAgent;
    }

    public void setUserAgent(String userAgent) {
        this.userAgent = userAgent;
    }
    
    
    public String getMedia() {
        return media;
    }

    public void setMedia(String media) {
        this.media = media;
    }

    public void setHref(String href) {
        this.href = href;
    }

    public Object saveState(FacesContext context) {
        Object values[] = new Object[5];
        values[0] = super.saveState(context);
        values[1] = href;
        values[2] = userAgent;
        values[3] = media;
        return ((Object) (values));
    }

    public void restoreState(FacesContext context, Object state) {
        Object values[] = (Object[]) state;
        super.restoreState(context, values[0]);
        href = (String) values[1];
        userAgent = (String) values[2];
        media = userAgent = (String) values[3];
    }
}

Change renderer class

Now we need the OutputStyleRenderer class from svn.

Todo:
  • Correct the import directive for OutputStyles so that are custom component is pointed
  • Enhance the encodeEnd method with the new media attribute logic.
  • extend the existing saveState and restoreState methods with some "media" stuff. That should be self explanatory.
Here is the whole OutputStyleRenderer class:
package com.yourcompany.faces.custom.renderer;

// Version: MPL 1.1/GPL 2.0/LGPL 2.1
//
// "The contents of this file are subject to the Mozilla Public License
// Version 1.1 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations under
// the License.
//
// The Original Code is ICEfaces 1.5 open source software code, released
// November 5, 2006. The Initial Developer of the Original Code is ICEsoft
// Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
// 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
//
// Contributor(s): _____________________.
//
// Alternatively, the contents of this file may be used under the terms of
// the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
// License), in which case the provisions of the LGPL License are
// applicable instead of those above. If you wish to allow use of your
// version of this file only under the terms of the LGPL License and not to
// allow others to use your version of this file under the MPL, indicate
// your decision by deleting the provisions above and replace them with
// the notice and other provisions required by the LGPL License. If you do
// not delete the provisions above, a recipient may use your version of
// this file under either the MPL or the LGPL License."

import com.icesoft.faces.context.DOMContext;
import com.icesoft.faces.renderkit.dom_html_basic.DomBasicRenderer;
import com.icesoft.faces.renderkit.dom_html_basic.HTML;
import com.icesoft.faces.util.CoreUtils;
import com.yourcompany.faces.custom.component.OutputStyle;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import java.beans.Beans;
import java.io.IOException;

public class OutputStyleRenderer extends DomBasicRenderer {

    private static Log log = LogFactory.getLog(OutputStyleRenderer.class);
    private static final String IE_EXTENTION = "_ie";
    private static final String IE_7_EXTENTION = "_ie7";
    private static final String IE_8_EXTENSION = "_ie8";
    private static final String SAFARI_EXTENTION = "_safari";
    private static final String SAFARI_MOBILE_EXTENTION = "_safarimobile";
    private static final String CSS_EXTENTION = ".css";
    private static final String DT_EXTENTION = "_dt";
    private static final String OPERA_EXTENTION = "_opera";
    private static final String OPERA_MOBILE_EXTENTION = "_operamobile";

    private static final int DEFAULT_TYPE = 0;
    private static final int IE = 1;
    private static final int SAFARI = 2;
    private static final int DT = 3;
    private static final int IE_7 = 4;
    private static final int SAFARI_MOBILE = 5;
    private static final int OPERA = 6;
    private static final int OPERA_MOBILE = 7;
    private static final int IE_8 = 8;

    public void encodeEnd(FacesContext facesContext, UIComponent uiComponent)
            throws IOException {
        validateParameters(facesContext, uiComponent, OutputStyle.class);
        try {
            DOMContext domContext =
                    DOMContext.attachDOMContext(facesContext, uiComponent);
            if (!domContext.isInitialized()) {
                OutputStyle outputStyle = (OutputStyle) uiComponent;
                Element styleEle = buildCssElement(domContext);
                String href = outputStyle.getHref();
                styleEle.setAttribute(HTML.HREF_ATTR, getResourceURL(facesContext,href));
                
                String media = outputStyle.getMedia();
                
                if(media != null) styleEle.setAttribute("media", getResourceURL(facesContext,media));
                
                domContext.setRootNode(styleEle);
                int browserType = browserType(facesContext, uiComponent);
                if (browserType != DEFAULT_TYPE) {
                    if (href.endsWith(CSS_EXTENTION)) {
                        int i = href.indexOf(CSS_EXTENTION);
                        if (i > 0) {
                            String start = href.substring(0, i);
                            Element ieStyleEle = buildCssElement(domContext);
                            String extention = IE_EXTENTION;
                            if (browserType == SAFARI) {
                                extention = SAFARI_EXTENTION;
                            }
                            if (browserType == DT) {
                                extention = DT_EXTENTION;
                            }
                            if(browserType == IE_7){
                                extention = IE_7_EXTENTION;
                            }
                            if(browserType == IE_8){
                                extention = IE_8_EXTENSION;
                            }
                            if(browserType == SAFARI_MOBILE){
                                extention = SAFARI_MOBILE_EXTENTION;
                            }
                            if(browserType == OPERA){
                                extention = OPERA_EXTENTION;
                            }
                            if(browserType == OPERA_MOBILE){
                                extention = OPERA_MOBILE_EXTENTION;
                            }
                            // W3C spec: To make a style sheet preferred, set the rel attribute to "stylesheet" and name the style sheet with the title attribute
                            ieStyleEle.setAttribute(HTML.TITLE_ATTR, extention);
                            String hrefURL = CoreUtils.resolveResourceURL(facesContext, start + extention + CSS_EXTENTION);
                            ieStyleEle.setAttribute(HTML.HREF_ATTR, hrefURL);
                            styleEle.getParentNode().appendChild(ieStyleEle);
                        } else {
                            throw new RuntimeException(
                                    "OutputStyle file attribute is too short. " +
                                    "Needs at least one character before .css. Current Value is [" +
                                    href + "]");
                        }
                    } else {
                        throw new RuntimeException(
                                "OutputStyle file attribute must end in .css. " +
                                "Current Value is [" + href + "]");
                    }
                }

            }
            domContext.stepOver();
        } catch (Exception e) {
            log.error("Error in OutputStyleRenderer", e);
        }
    }

    private Element buildCssElement(DOMContext domContext) {
        Element styleEle = domContext.createElement("link");
        styleEle.setAttribute(HTML.REL_ATTR, "stylesheet");
        styleEle.setAttribute(HTML.TYPE_ATTR, "text/css");
        return styleEle;
    }

    private int browserType(FacesContext facesContext, UIComponent uiComponent) {
        int result = DEFAULT_TYPE;
        String useragent = ((OutputStyle)uiComponent).getUserAgent();
        if(useragent != null){
            return _browserType(useragent);
        }

        Object o = facesContext.getExternalContext().getRequest();
        if (o != null) {
            if (o instanceof HttpServletRequest) {
                HttpServletRequest request = (HttpServletRequest) o;
                useragent = request.getHeader("user-agent");
                if(useragent == null){
                    useragent = ((OutputStyle)uiComponent).getUserAgent();
                }
                if(useragent == null){
                 if (log.isDebugEnabled()) {
                  log.debug("Not able to find user agent. Returning default");
                 }
                    return DEFAULT_TYPE;
                }
                if(((OutputStyle)uiComponent).getUserAgent() == null){
                    ((OutputStyle)uiComponent).setUserAgent(useragent.toLowerCase());
                }
                String user = useragent.toLowerCase();
                result = _browserType( user);

            } else {
             if (log.isDebugEnabled()) {
              log.debug(
                        "OutputStyleRenderer: Request is not HttpServletRequest. Its [" +
                        o.getClass().getName() + "]");
             }
            }
        } else {
         if (log.isDebugEnabled()) {
          log.debug(
                    "IceStyleReader: facesContext.getExternalContext().getRequest() is null");
         }
        }
        return result;
    }

    private int _browserType(String user) {
        int result = DEFAULT_TYPE;
        if (Beans.isDesignTime()) {
            result = DT;
        } else {
            if (user.indexOf("opera") < 0 && user.indexOf("msie") != -1) {
                result = IE;
                if(user.indexOf("msie 7") != -1){
                    result = IE_7;
                }
                if(user.indexOf("msie 8") != -1){
                    result = IE_8;
                }
            } else if (user.indexOf("safari") != -1) {
                result = SAFARI;
                if(user.indexOf("mobile") != -1) {
                    result = SAFARI_MOBILE;
                }
            } else if (user.indexOf("opera") != -1) {
                result = OPERA;
                if(user.indexOf("240x320") != -1) {
                    result = OPERA_MOBILE;
                }
            }
        }
        return result;
    }
}

Register the custom classes in faces config

The last step is to register our new logic in the faces-config.

<component>
  <description>Links one or more theme CSS files into the page</description>
  <display-name>Output Style</display-name>
  <component-type>com.icesoft.faces.OutputStyleComp</component-type>
  <component-class>com.yourcompany.faces.custom.component.OutputStyle</component-class>
  <component-extension>
   <base-component-type>com.sun.faces.Component</base-component-type>
   <component-family>com.icesoft.faces.OutputStyle</component-family>
   <renderer-type>com.yourcompany.faces.custom.renderer.OutputStyleRenderer</renderer-type>
  </component-extension>
</component>

<render-kit>
  <render-kit-id>ICEfacesRenderKit</render-kit-id>
  <render-kit-class>com.icesoft.faces.renderkit.D2DRenderKit</render-kit-class>
  <renderer>
   <component-family>com.icesoft.faces.OutputStyle</component-family>
   <renderer-type>com.icesoft.faces.style.OutputStyleRenderer</renderer-type>
   <renderer-class>com.yourcompany.faces.custom.renderer.OutputStyleRenderer</renderer-class>
  </renderer>
 </render-kit>

Test and Hints

When all changes are done the new tag should be avalaible now. Simply type in your template:

<ice:outputStyle href="/css/yourStyle.css" media="all"/>
<ice:outputStyle href="/css/yourPrintStyle.css" media="print"/>

Hint: When you want to use the media attribute in an iFrame component like ice:inputFile too, you need to modifiy some more things. In this case you've to modify the class InputFile and a re-build of the ICEfaces libs is essential.
ICEfaces is based on Prototype JavaScript-Library, but what when you want to use jQuery in your project too? - The trick is to define separate namespaces for each library, to prevent them from a clash.

The code snipped below shows how to do that:

<html>
 <head>
   <script src="prototype.js"></script>
   <script src="jquery.js"></script>
   <script>
     var $j = jQuery.noConflict();
     
     // Use jQuery via $j(...)
     $j(document).ready(function(){
       $j("div").hide();
     });
     
     // Use Prototype with $(...), etc.
     $('someid').hide();
   </script>
 </head>
 <body></body>
 </html>

For more details go to http://docs.jquery.com/Using_jQuery_with_Other_Libraries .


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!