Load Testing web application with Selenium and TestNG

I’ve used Selenium for while to do verification tests of web applications, recently I discovered a very simple way to use it with TestNG and Maven to do some performance testing. TestNG allows for the use of annotations to allow multi-threading and iterations.

pom.xml:

<dependencies>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.8.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>2.44.0</version>
<scope>test</scope>
</dependency>
<dependencies>

And as for a simple test to get started with… scripting of steps is available online or could be in a future blog post.

/*
* COPYRIGHT. none
*/
package com.example.selenium;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
* Simple test example for Selenium
*/
public class SeleniumTest {

private static final Logger LOGGER = LoggerFactory.getLogger(SeleniumTest.class);
/**
* TODO Un-comment or change if needed to set your local path!
*/
@BeforeClass
public void oneTimeSetUp() {
System.out.println("-------------------------------------- init ----------------------------------------");
//System.setProperty("webdriver.firefox.bin","C:\\path\\to\\firefox.exe");
}
/**
* NOTE: uses TestNG - behaves differently than JUnit
*/
@Test(invocationCount = 1, threadPoolSize = 5)
public void testLoadApp() {

final String fn = "testLoadApp";
final String baseUrl = "http://www.giantgeek.com/index.php";
LOGGER.debug("[START] Thread Id: {} is started!", Thread.currentThread().getId());

WebDriver driver = null;
final long start = System.currentTimeMillis();
try{
driver = (WebDriver)new FirefoxDriver();
driver.get(baseUrl);

final String actual = driver.getTitle();
LOGGER.debug("Page Title is {}", actual);
final String expected = "GIANTGEEK.COM";
Assert.assertEquals(actual,expected);
//perform whatever actions, like login, submit form or navigation

}catch(final WebDriverException ex){
LOGGER.warn(fn+":WebDriverException:{}",ex);
}catch(final Exception ex){
LOGGER.warn(fn+":Exception:{}",ex);
}
finally {
final long elapsed = System.currentTimeMillis() - start;
LOGGER.debug("[END] Thread Id: {}, elapsed={}", Thread.currentThread().getId(),elapsed);
if(driver != null){
driver.quit();
}
}
}
}

WARNING: Selenium Tests MAY fail if the browser used for testing is updated in the Operating System. Updating the pom.xml to a newer release usually helps!

REFERENCES:

jboss-web.xml

If you support code for multiple java application servers, you might eventually encounter a file named:


/webapp/WEB-INF/jboss-web.xml

JBoss uses this file to control the path of the web application, whereas Tomcat generally uses the filename of the WAR itself.

Usually, the contents are pretty sparse, you might consider adding one to your projects should you ever wish to deploy them on JBoss:


<jboss-web>
<context-root>example</context-root>
</jboss-web>

NOTE: There are several other attributes that can find their way into this file for JBoss, notably security configuration, like JAAS.

WARNING: Unfortunately, I’ve tried to add a simple DOCTYPE jboss-web and XML preamble to this, file to make it validate, but the server (JBoss 5.1.x) fails to recognize them.

Eclipse FileSync plugin

I’ve done a lot of front-end java coding over my career,  one particularly annoying aspect is the wait for a build (compile-deploy) cycle in my local developement servers to view or test a small change.  One particularly useful tool that I’ve been using for some time is a FileSync plugin for Eclipse.  It is useful as you can “map” folders from your Eclipse project to a path on your local filesystem, as such the individual files are automatically copied to your server installation.  I’ve personally used this approache with JBoss, Tomcat and WebSphere, but there is no reason that it should not work for other servers.

Security through obscurity – hiding your server version information

I’ve recently spent a lot of time reviewing the OWASP documentation, and (like many corporations) realized that I’d neglected to keep up with this configuration item.

By sharing the exact version of each piece of server software you are using, “hackers” are able to quickly identify unpatched systems and their known vulnerabilities.

To make their work harder, there are a few simple steps that the server admin can take to remove this information from the HTTP Headers and error pages.

Apache HTTPd:

  1. sudo vi /etc/apache2/conf-enabled/security.conf
  2. Add:

    ServerTokens ProductOnly
    ServerSignature Off
  3. If using virtual hosts, add the following to each one:
    ServerSignature Off
  4. sudo service apache2 restart

Apache Tomcat:

  1. vi /opt/tomcat7/conf/server.xml
  2. Find the <Connector > entry and add:
    server="Apache"
  3. cd /opt/tomcat7/lib
  4. mkdir -p org/apache/catalina/util
  5. vi /opt/tomcat7/lib/org/apache/catalina/util/ServerInfo.properties
    server.info=Apache Tomcat
  6. sudo service tomcat7 restart

PHP “X-Powered-By: PHP/5.x.x-1ubuntuX.X”

  1. sudo vi /etc/php5/apache2/php.ini
    expose_php = Off
  2. sudo service apache2 restart

REFERENCES:

Renaming JSESSIONID

Older versions of Apache Tomcat, as well as the older servlet specifications required that several configuration values need to be set. With servlet 3, you can now modify the name of the session cookie (as well as the ‘rewriting’ attribute name) in the web.xml file

In web.xml: (servlet 3.x)

<session-config>
<session-timeout>30</session-timeout>
<cookie-config>
<name>mysessionid</name><!-- default is jsessionid -->
<http-only>true</http-only>
<!-- secure>true</secure-->
</cookie-config>
<tracking-mode>COOKIE</tracking-mode>
</session-config>

Alternately for Tomcat7, modify TOMCAT_HOME\conf\context.xml:
<Context path="/exampleApp" sessionCookieName="myid">

If you are using spring security, then you should try setting disable-url-rewriting attribute of <http> element to true.

REFERENCES:

Problems uploading/deploying large WAR’s to Tomcat7?

I’ve run into this a few times as my web applications got larger. Often this has been seen when builds automated by Jenkins start failing as they increase in size. It has also occurred to me when doing manual deployments as the Jenkins WAR itself is larger than 50MB lately.

Let’s just go in and increase the maximum expected file size…

This change should work on any platform, but the following is from my experience with Ubuntu.

sudo vi /opt/tomcat7/webapps/manager/WEB-INF/web.xml

Default is:

<multipart-config>
<!-- 50MB -->
<max-file-size>62428800</max-file-size>
<max-request-size>62428800</max-request-size>
<file-size-threshold>0</file-size-threshold>
</multipart-config>

Change to something a bit larger (to your liking):

<multipart-config>
<!-- 50MB max 62428800, 100MB = 104857600 -->
<max-file-size>104857600</max-file-size>
<max-request-size>104857600</max-request-size>
<file-size-threshold>0</file-size-threshold>
</multipart-config>

Restart with either…
sudo /etc/init.d/tomcat7 restart
or
sudo service tomcat7 restart

Install Splunk Universal Forwarder on Ubuntu

After a while it can get tedious to access and review server logs via the command line. There are several tools available that can provide the same information in a graphical manner. Recently I’ve migrated to Splunk as there are both Enterprise and Free versions available.

  1. Of course, you’ll need a Splunk server installed first, as the forwarder is really just another (lighter) instance that will forward the log information to a central location.
  2. Download the system appropriate installer from:
    http://www.splunk.com/download/universalforwarder
  3. Check to see if you are running 32 or 64 bit OS.uname -aIf you see i686 you are 32 bit, if x86_64 you are 64 bit!
  4. Download, you’ll likely need a different version:sudo dpkg -i splunkforwarder-6.1.3-220630-linux-2.6-intel.deb
    or
    sudo dpkg -i splunkforwarder-6.1.3-220630-linux-2.6-amd64.deb
  5. Enable auto-start on reboot:cd /opt/splunkforwarder/bin/

sudo ./splunk enable boot-start

  1. Start the server:sudo service splunk start
  2. Set the password:

    The default ‘admin‘ password is ‘changeme‘ so we need to change it immediately to do anything else, or we will see errors in future steps.

    sudo /opt/splunkforwarder/bin/splunk edit user admin -password YOUR_NEW_PASSWORD -auth admin:changeme

  3. Set the server:sudo /opt/splunkforwarder/bin/splunk add forward-server YOUR_SERVER_ADDRESS:9997

    NOTE: if you get prompted for a splunk username/password you likely skipped the above step. Remember – the forwarder is a new ‘light’ installation of the server and as such has it’s own users!

  4. Enable some monitors on the box:Some common services and log locations to get you started…
    Apache2 HTTPd
    sudo /opt/splunkforwarder/bin/splunk add monitor /var/log/apache2 -index main -sourcetype Apache2
    Tomcat7
    sudo /opt/splunkforwarder/bin/splunk add monitor /opt/tomcat7/logs -index main -sourcetype Tomcat7
    MySQL
    sudo /opt/splunkforwarder/bin/splunk add monitor /var/log/mysql -index main -sourcetype MySQL
    Postfix (SMTP)
    sudo /opt/splunkforwarder/bin/splunk add monitor /var/log/mail.log -index main -sourcetype Postfix
    Squid3 (Proxy) 
    sudo /opt/splunkforwarder/bin/splunk add monitor /var/log/squid3/access.log -index main -sourcetype Squid3
    sudo /opt/splunkforwarder/bin/splunk add monitor /var/log/squid3/cache.log -index main -sourcetype Squid3

    SonarQube
    sudo /opt/splunkforwarder/bin/splunk add monitor /opt/sonar/logs -index main -sourcetype Sonar
  5. (OPTIONAL) Verify configuration by opening file at the following:sudo su
    vi /opt/splunkforwarder/etc/apps/search/local/inputs.conf
    exit
  6. You now should be able to log into your server and see new data flowing from the forwarder.

    NOTE: this requires you to enable ‘receiving’ of data on the port specified above, usually 9997.

REFERENCES:

Enable larger file uploads via Tomcat manager

Shortly after I automated code deployments in my Tomcat7 development testing environment, I found that some larger builds began failing with the following error:


org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (...) exceeds the configured maximum (62428800)

After a little digging, I found that the WAR files were exceeding the default maximum upload size, thankfully, this is trivial to increase.


sudo vi /usr/share/tomcat7-admin/manager/WEB-INF/web.xml

(Change 52428800 to a larger number, perhaps doubled like 104857600)

<multipart-config
<!-- 50MB max = 52428800 (100MB = 104857600) -->
<max-file-size>104857600</max-file-size>
<max-request-size>104857600</max-request-size>
<file-size-threshold>0</file-size-threshold>
</multipart-config>

REFERENCES:

Improve Apache Tomcat logging performance

Logging is often an overlooked performance drain on systems requiring high throughput. Here’s a simple change to the default Tomcat logging configuration to implement. It works on all operating systems.

In the file:
$TOMCAT_HOME/conf/logging.properties

Change:
.handlers = 1catalina.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler

to
.handlers = 1catalina.org.apache.juli.FileHandler

REFERENCES:

Sample Tomcat7 setup

There are a few steps that I generally take to setup a new Tomcat server instance, this enables the following:

  • The manager console
  • HTTP compression
  • UTF-8 encoding

Steps:

  1. tomcat-users.xml – add to bottom:

    <role rolename="manager-gui"/>
    <user username="tomcat" password="s3cr3t" roles="manager-gui"/>

  2. server.xml – add compression and URIEncoding, change port if desired:

    <Connector port="8080" protocol="HTTP/1.1"
    connectionTimeout="20000"
    redirectPort="8443" compression="on" URIEncoding="UTF-8" />

  3. server.xml – relocate webapps by adding ../ to appBase

    <Host name="localhost" appBase="../webapps"
    unpackWARs="true" autoDeploy="true">

  4. Restart your server, on Ubuntu use:

    sudo service tomcat7 restart