Wednesday, March 01, 2006

DWR framework.Performance Test.Take 2

As a part of evaluation of presentation layer frameworks, we performed load tests for the DWR framework. We downloaded DWR Version 1.1.beta2.We deployed dwr.war to Tomcat Server. We recorded test scripts using Grinder TCPProxy.

Test modifications
1. We removed from the test script all but the simplest requests. Particularly, we removed requests like testBeanSetParam and testBeanListParam, which caused errors in the Tomcat log file.
2. We disabled DWR logging.
3. We ignored first 100 samples (sample interval=1000 ms).
4. We used fixed time/snapshot method.
5. We used 30 runs.

Performance Test Results

Summary

200 Users
Errors: 560
AART, ms: 100
AART Standard Deviation: 85.1
Max ART: 166
Quality: 0.851
Max CPU utilization, %: 32

160 Users
Errors: 0
AART, ms: 102
AART Standard Deviation: 84.2
Max ART: 166
Quality: 0.825
Max CPU utilization, %: 10

Conclusions
1. Using the hardware and software (described in the previous posts), [the subset of] DWR framework can handle approximately 160 simultaneous active users with a maximum response time of 166 milliseconds.
2. The quality of the test is far beyond the limit, which means that further investigation is necessary (including Java code profiling).
3. For 200 users, CPU utilization increases until the Tomcat server starts refusing connections.
4. Logging – disabling the DWR logging improved the AART by 5.5%.
5. We tested only a small subset of the DWR framework functionality.

Grinder properties
grinder.processes=1
grinder.threads=200
grinder.runs=30

grinder.consoleHost=localhost
grinder.receiveConsoleSignals=true
grinder.reportToConsole=true

grinder.logDirectory=log
grinder.appendLog=true
grinder.numberOfOldLogs=0

grinder.initialSleepTime=1000
grinder.sleepTimeFactor=1
grinder.sleepTimeVariation=0.2

grinder.script=httpscript.py
url=http://X.X.X.X:8080

Log files
Excerpts from the Grinder log files (200 users):

3/1/06 12:58:29 PM (thread 32 run 4): Aborted run, script threw class java.net.BindException: Address already in use: connect
java.net.BindException: Address already in use: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:364)
at java.net.Socket.connect(Socket.java:507)
at java.net.Socket.connect(Socket.java:457)
at java.net.Socket.<init>(Socket.java:365)
at java.net.Socket.<init>(Socket.java:207)
at HTTPClient.HTTPConnection.getSocket(HTTPConnection.java:3264)
at HTTPClient.HTTPConnection.sendRequest(HTTPConnection.java:2994)
at HTTPClient.HTTPConnection.handleRequest(HTTPConnection.java:2801)
at HTTPClient.HTTPConnection.setupRequest(HTTPConnection.java:2593)
at HTTPClient.HTTPConnection.Post(HTTPConnection.java:1095)
at net.grinder.plugin.http.HTTPRequest$6.doRequest(HTTPRequest.java:647)
at net.grinder.plugin.http.HTTPRequest$AbstractRequest.getHTTPResponse(HTTPRequest.java:827)
at net.grinder.plugin.http.HTTPRequest.POST(HTTPRequest.java:644)
at net.grinder.plugin.http.HTTPRequest.POST(HTTPRequest.java:623)
at sun.reflect.GeneratedMethodAccessor7.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java)
at org.python.core.PyMethod.__call__(PyMethod.java)
at org.python.core.PyObject.__call__(PyObject.java)
at org.python.core.PyInstance.invoke(PyInstance.java)
at net.grinder.engine.process.TestPyJavaInstance.access$201(TestPyJavaInstance.java:35)
at net.grinder.engine.process.TestPyJavaInstance$3.call(TestPyJavaInstance.java:88)
at net.grinder.engine.process.ThreadContextImplementation.invokeTest(ThreadContextImplementation.java:155)
at net.grinder.engine.process.TestData.dispatch(TestData.java:79)
at net.grinder.engine.process.TestPyJavaInstance.dispatch(TestPyJavaInstance.java:48)
at net.grinder.engine.process.TestPyJavaInstance.invoke(TestPyJavaInstance.java:85)
at org.python.pycode._pyx0.__call__$2(httpscript.py:57)
at org.python.pycode._pyx0.call_function(httpscript.py)
at org.python.core.PyTableCode.call(PyTableCode.java)
at org.python.core.PyTableCode.call(PyTableCode.java)
at org.python.core.PyTableCode.call(PyTableCode.java)
at org.python.core.PyFunction.__call__(PyFunction.java)
at org.python.core.PyMethod.__call__(PyMethod.java)
at org.python.core.PyObject.invoke(PyObject.java)
at org.python.core.PyInstance.__call__(PyInstance.java)
at org.python.core.PyObject.__call__(PyObject.java)
at net.grinder.engine.process.JythonScript$JythonRunnable.run(JythonScript.java:144)
at net.grinder.engine.process.GrinderThread.run(GrinderThread.java:128)
at java.lang.Thread.run(Thread.java:595)

Test Scripts

httpscript.py

#
# The Grinder version 3.0-beta26
#
# Script recorded by the TCPProxy at Jan 13, 2006 11:06:03 AM
#

from httpscript_tests import *
from net.grinder.script.Grinder import grinder

url = grinder.properties.get("url")

class TestRunner:
def __call__(self):

grinder.sleep(62)
tests[1].GET(url + '/dwr/')

grinder.sleep(47)
tests[2].GET(url + '/dwr/dwr/interface/Test.js')

grinder.sleep(78)
tests[3].GET(url + '/dwr/dwr/engine.js')

grinder.sleep(125)
tests[4].GET(url + '/dwr/dwr/util.js')

grinder.sleep(140)
tests[5].POST(url + '/dwr/dwr/exec/Test.getInsert', data5)

grinder.sleep(4563)
tests[6].GET(url + '/dwr/test.html')

tests[7].GET(url + '/dwr/dwr/interface/Test.js')

grinder.sleep(78)
tests[8].GET(url + '/dwr/testsuite.js')

grinder.sleep(3750)
tests[10].POST(url + '/dwr/dwr/exec/Test.booleanParam', data10)

grinder.sleep(2937)
tests[11].POST(url + '/dwr/dwr/exec/Test.byteParam', data11)

grinder.sleep(2140)
tests[12].POST(url + '/dwr/dwr/exec/Test.shortParam', data12)

grinder.sleep(2875)
tests[13].POST(url + '/dwr/dwr/exec/Test.intParam', data13)

grinder.sleep(1875)
tests[14].POST(url + '/dwr/dwr/exec/Test.longParam', data14)

grinder.sleep(3579)
tests[15].POST(url + '/dwr/dwr/exec/Test.floatParam', data15)

grinder.sleep(2547)
tests[16].POST(url + '/dwr/dwr/exec/Test.doubleParam', data16)

grinder.sleep(2578)
tests[17].POST(url + '/dwr/dwr/exec/Test.bigDecimalParam', data17)

grinder.sleep(4250)
tests[18].POST(url + '/dwr/dwr/exec/Test.bigIntegerParam', data18)

grinder.sleep(6938)
tests[19].POST(url + '/dwr/dwr/exec/Test.charParam', data19)

grinder.sleep(9390)
tests[20].POST(url + '/dwr/dwr/exec/Test.stringParam', data20)

Tuesday, February 28, 2006

DWR framework.Performance Test.Error Handling

Inconsistent Error Handling

There are more cases of inconsistent error handling in the DWR code. After logging an error, instead of throwing an exception, it continues normal execution.
Here are some of the cases:

uk.ltd.getahead.dwr.AbstractDWRServlet

InputStream in = …
if (in == null)
{
log.error("Missing config file: " + configFile);
}



uk.ltd.getahead.dwr.convert.HibernateBeanConverter

log.error("Failed in checking Hibernate the availability of " + property, ex); return false;


uk.ltd.getahead.dwr.create.PageFlowCreator

log.error("Failed to instansiate object to detect type.", ex);
return Object.class;


uk.ltd.getahead.dwr.create.ScriptedCreator

log.error("Failed to instansiate object to detect type.", ex);
return Object.class;


uk.ltd.getahead.dwr.create.SpringCreator

log.error("Failed to instansiate object to detect type.", ex);
return Object.class;


uk.ltd.getahead.dwr.impl.DefaultConfiguration

log.error("Failed to add convertor: match=" + match + ", type=" + type, ex);

log.error("The 'session' and 'static' creators are deprecated. Use the 'new' creator"); log.error(" For more information see the DWR website");

log.error("Failed to add creator: type=" + type + ", javascript=" + javascript, ex);



log.error("Unable to find method called: " + methodName + " on type: " + dest.getName() + " from creator: " + javascript);
continue;


uk.ltd.getahead.dwr.impl.DefaultContainer

log.error("Can't cast: " + value + " to " + askFor);


catch (IllegalArgumentException ex)
{
log.error("- Internal error: " + ex.getMessage());
}
catch (IllegalAccessException ex)
{
log.error("- Permission error: " + ex.getMessage());
}
catch (InvocationTargetException ex)
{
log.error("- Exception during auto-wire", ex.getTargetException());
}



uk.ltd.getahead.dwr.impl.DefaultConverterManager

if (converter == null)
{
log.error(Messages.getString("DefaultConverterManager.MissingConverter", object.getClass().getName()));
return new OutboundVariable("var " + varName + "=null;", varName);
}



uk.ltd.getahead.dwr.impl.DefaultCreatorManager

if (clazz == null)
{
log.error("Missing creator: " + typeName + " (while initializing creator for: " + scriptName + ".js)");
return;
}

catch (NoClassDefFoundError ex)
{
log.error("Missing class for creator '" + creator + "'. Cause: " + ex.getMessage());
}
catch (Exception ex)
{
log.error("Error loading class for creator '" + creator + "'.", ex);
}


uk.ltd.getahead.dwr.impl.DefaultTestProcessor

InputStream raw =…
if (raw == null)
{
log.error(Messages.getString("DefaultProcessor.MissingHelp", HtmlConstants.FILE_HELP));
output = …
}

uk.ltd.getahead.dwr.impl.SignatureParser

catch (Exception ex)
{
log.error("Unexpected Error", ex);
}

Friday, February 17, 2006

DWR framework.Performance Test

As a part of evaluation of presentation layer frameworks, we performed load tests for the DWR framework.
We downloaded DWR Version 1.1.beta2.
We deployed dwr.war to Tomcat Server.
We recorded test scripts using Grinder TCPProxy.

Performance Test Results

Summary
Test type: Preliminary Performance Test
Users simulated: 160
Grinder errors: 0
AART: 145 ms
Max ART: 201 ms
AART Standard Deviation: 84 ms

Max CPU utilization: 38%

Max memory utilization:

Conclusions
  1. In the Tomcat log, there are multiple errors reported:
“Found reference to variable named 'XXX', but no variable of that name could be found.” They are all related to requests, which use references to variables. The corresponding Java code seems to have inconsistent error handling:

uk.ltd.getahead.dwr.InboundVariable

InboundVariable cd = …
if (cd == null)
{
log.error(…);
break;
}

It logs an error, but than, instead of throwing an exception, it continues normal execution.

2. In the Tomcat log, there are multiple log entries of “INFO” level. The excessive logging might degrade the performance.
3. Until those issues are resolved, it would be hard to interpret the results.

Grinder properties
grinder.processes=10
grinder.threads=100
grinder.runs=20
grinder.consoleHost=localhost
grinder.receiveConsoleSignals=true
grinder.reportToConsole=true
grinder.logDirectory=log
grinder.appendLog=true
grinder.numberOfOldLogs=0
grinder.initialSleepTime=1000
grinder.sleepTimeFactor=1
grinder.sleepTimeVariation=0.2
grinder.script=httpscript.py
url=http://X.X.X.X:8080

Log files
Excerpts from the Tomcat log files:

Feb 17, 2006 3:08:06 PM uk.ltd.getahead.dwr.util.CommonsLoggingOutput info
INFO: Exec[0]: Test.getInsert()

INFO: Exec[0]: Test.testBeanParam()
Feb 17, 2006 3:09:51 PM uk.ltd.getahead.dwr.util.CommonsLoggingOutput error
SEVERE: Found reference to variable named 'c0-e4', but no variable of that name could be found.
Feb 17, 2006 3:09:51 PM uk.ltd.getahead.dwr.util.CommonsLoggingOutput info
INFO: Exec[0]: Test.byteArrayParam()

Test Scripts

#
# The Grinder version 3.0-beta26
#
# Script recorded by the TCPProxy at Jan 13, 2006 11:06:03 AM
#

from httpscript_tests import *
from net.grinder.script.Grinder import grinder

url = grinder.properties.get("url")

class TestRunner:
def __call__(self):

grinder.sleep(62)
tests[1].GET(url + '/dwr/')

grinder.sleep(47)
tests[2].GET(url + '/dwr/dwr/interface/Test.js')

grinder.sleep(78)
tests[3].GET(url + '/dwr/dwr/engine.js')

grinder.sleep(125)
tests[4].GET(url + '/dwr/dwr/util.js')

grinder.sleep(140)
tests[5].POST(url + '/dwr/dwr/exec/Test.getInsert', data5)

grinder.sleep(4563)
tests[6].GET(url + '/dwr/test.html')

tests[7].GET(url + '/dwr/dwr/interface/Test.js')

grinder.sleep(78)
tests[8].GET(url + '/dwr/testsuite.js')

grinder.sleep(7500)
tests[9].POST(url + '/dwr/dwr/exec/Test.waitFor', data9)

grinder.sleep(3750)
tests[10].POST(url + '/dwr/dwr/exec/Test.booleanParam', data10)

grinder.sleep(2937)
tests[11].POST(url + '/dwr/dwr/exec/Test.byteParam', data11)

grinder.sleep(2140)
tests[12].POST(url + '/dwr/dwr/exec/Test.shortParam', data12)

grinder.sleep(2875)
tests[13].POST(url + '/dwr/dwr/exec/Test.intParam', data13)

grinder.sleep(1875)
tests[14].POST(url + '/dwr/dwr/exec/Test.longParam', data14)

grinder.sleep(3579)
tests[15].POST(url + '/dwr/dwr/exec/Test.floatParam', data15)

grinder.sleep(2547)
tests[16].POST(url + '/dwr/dwr/exec/Test.doubleParam', data16)

grinder.sleep(2578)
tests[17].POST(url + '/dwr/dwr/exec/Test.bigDecimalParam', data17)

grinder.sleep(4250)
tests[18].POST(url + '/dwr/dwr/exec/Test.bigIntegerParam', data18)

grinder.sleep(6938)
tests[19].POST(url + '/dwr/dwr/exec/Test.charParam', data19)

grinder.sleep(9390)
tests[20].POST(url + '/dwr/dwr/exec/Test.stringParam', data20)

grinder.sleep(4922)
tests[21].POST(url + '/dwr/dwr/exec/Test.booleanArrayParam', data21)

grinder.sleep(1953)
tests[22].POST(url + '/dwr/dwr/exec/Test.charArrayParam', data22)

grinder.sleep(1266)
tests[23].POST(url + '/dwr/dwr/exec/Test.byteArrayParam', data23)

grinder.sleep(7437)
tests[24].POST(url + '/dwr/dwr/exec/Test.intArrayParam', data24)

grinder.sleep(5703)
tests[25].POST(url + '/dwr/dwr/exec/Test.longArrayParam', data25)

grinder.sleep(625)
tests[26].POST(url + '/dwr/dwr/exec/Test.floatArrayParam', data26)

grinder.sleep(860)
tests[27].POST(url + '/dwr/dwr/exec/Test.doubleArrayParam', data27)

grinder.sleep(9375)
tests[28].POST(url + '/dwr/dwr/exec/Test.byteArrayParam', data28)

grinder.sleep(1719)
tests[29].POST(url + '/dwr/dwr/exec/Test.testBeanParam', data29)

grinder.sleep(1906)
tests[30].POST(url + '/dwr/dwr/exec/Test.testBeanParam', data30)

grinder.sleep(672)
tests[31].POST(url + '/dwr/dwr/exec/Test.testBeanParam', data31)

grinder.sleep(828)
tests[32].POST(url + '/dwr/dwr/exec/Test.testBeanParam', data32)

grinder.sleep(1547)
tests[33].POST(url + '/dwr/dwr/exec/Test.testBeanSetParam', data33)

grinder.sleep(5094)
tests[34].POST(url + '/dwr/dwr/exec/Test.testBeanListParam', data34)

grinder.sleep(4625)
tests[35].POST(url + '/dwr/dwr/exec/Test.stringCollectionParam', data35)

Wednesday, February 15, 2006

Performance Test. Methodology

Methodology

We are following the methodology described in the book: J2EE Performance Testing with BEA WebLogic Server.
We use Grinder as a load-testing tool.
We record test scripts using Grinder TCPProxy.
We than modify recorded scripts: replace "localhost" with a "url" parameter.

Performance Criteria
Maximum acceptable response time.

Realistic Usage Patterns
N/A

Think Time
We use real (e.g. recorded) think time. We use think time variation of 20%.

Initial Spread
Each user waits a random length of time between zero and initialSleepTime=1000 ms before beginning execution of the test script. We use a fixed number of users per test run.

Sampling Methods
We use “fixed number of cycles”/”the cycle” method. We define cycle as the complete execution of a test script by a simulated user. In one complete cycle, each simulated user will sequentially execute every request in the script once. If we state that a performance test will have 100 cycles, it means that every simulated user will execute the test script 100 times. We stop data collection after the slowest test script has finished executing its 100th cycle and in the analysis we ignore cycles above 100 for all test scripts. We consider 30 cycles a decent statistical sample.

Exclusion of Data
An application typically stabilizes (effect of caching, JVM optimizer, etc.) after the first cycle and thus we exclude the data from the first cycle.

Performance Statistics
Response Time – the length of time that a client has to wait from the moment it sends a request to the moment that the last byte of the response from the application has been received by the client.
Average Response Time (ART) – arithmetic mean of the response times for all users for a particular request.
Aggregate Average Response Time (AART, Load Factor) – arithmetic mean of the ARTs in the test.
Maximum Average Response Time – maximum of the ARTs in the test.

Quality of a Sample

Quality = standard deviation / arithmetic mean

The maximal acceptable Quality number is 0.25.
The Baseline Case – provides main point of comparison for subsequent test runs, to approximate the minimum user load we expect the application to be handling when in production.

Tomcat Server
We use Apache Tomcat Server 5.5.14, with default settings. The only configuration change: we increased maxThreads parameter to 10000 for the connector on port 8080.

Java: Sun JDK 1.5.0_06

JVM switches:
-Xms1536m -Xmx1536m -XX:+UseParallelGC -XX:+AggressiveHeap -XX:+DisableExplicitGC -XX:NewRatio=2

System Information
OS: Microsoft Windows Server 2003, Standard Edition, SP1

Computer:
Intel Xeon 2*CPU 3.06 GHz
2.00 GB of RAM

Echo2 framework.Performance Test

As a part of evaluation of presentation layer frameworks, we performed load tests for the Echo2 framework.
We downloaded Echo2 v2.0.0, released 01/05/2006.
We deployed InteractiveTest.war to Tomcat Server.
We recorded test scripts using Grinder TCPProxy.

Performance Test Results

Summary
Test type: Baseline Performance Test
Users simulated: 200
Errors: 53
AART: 305 ms
Max ART:
AART Standard Deviation: 479 ms

CPU utilization, %, peak
First run: 81
Second run: 71

Memory utilization, peak


Conclusions
1. There are "bad requests" reported, all related to "Echo.StreamImage" requests. They might also be the cause of the "java.net.BindException: Address already in use: connect" exceptions.
2. The "Echo.StreamImage" requests might fail because they contain auto-generated imageuid.
3. Until those script errors are fixed, it would be hard to interpret the results.

Grinder properties
grinder.processes=10
grinder.threads=20
grinder.runs=2

grinder.consoleHost=localhost
grinder.receiveConsoleSignals=true
grinder.reportToConsole=true

grinder.logDirectory=log
grinder.appendLog=true
grinder.numberOfOldLogs=0

grinder.initialSleepTime=1000
grinder.sleepTimeFactor=1
grinder.sleepTimeVariation=0.2

grinder.script=httpscript.py

url=http://X.X.X.X:8080

Log files
Excerpts from the Grinder log files:
2/15/06 3:42:24 PM (thread 0 run 0 test 7): http://X.X.X.X:8080/InteractiveTest/ia?serviceId=Echo.StreamImage&imageuid=b34b1_108c5d5b81c_6 -> 400 Bad Request, 23 bytes



2/15/06 3:45:11 PM (thread 5 run 1): Aborted run, script threw class java.net.BindException: Address already in use: connect
java.net.BindException: Address already in use: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:364)
at java.net.Socket.connect(Socket.java:507)
at java.net.Socket.connect(Socket.java:457)
at java.net.Socket.(Socket.java:365)
at java.net.Socket.(Socket.java:207)
at HTTPClient.HTTPConnection.getSocket(HTTPConnection.java:3264)
at HTTPClient.HTTPConnection.sendRequest(HTTPConnection.java:2994)
at HTTPClient.HTTPConnection.handleRequest(HTTPConnection.java:2801)
at HTTPClient.HTTPConnection.setupRequest(HTTPConnection.java:2593)
at HTTPClient.HTTPConnection.Get(HTTPConnection.java:910)
at net.grinder.plugin.http.HTTPRequest$2.doRequest(HTTPRequest.java:366)
at net.grinder.plugin.http.HTTPRequest$AbstractRequest.getHTTPResponse(HTTPRequest.java:827)
at net.grinder.plugin.http.HTTPRequest.GET(HTTPRequest.java:363)
at net.grinder.plugin.http.HTTPRequest.GET(HTTPRequest.java:342)
at sun.reflect.GeneratedMethodAccessor8.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java)
at org.python.core.PyMethod.__call__(PyMethod.java)
at org.python.core.PyObject.__call__(PyObject.java)
at org.python.core.PyInstance.invoke(PyInstance.java)
at net.grinder.engine.process.TestPyJavaInstance.access$201(TestPyJavaInstance.java:35)
at net.grinder.engine.process.TestPyJavaInstance$3.call(TestPyJavaInstance.java:88)
at net.grinder.engine.process.ThreadContextImplementation.invokeTest(ThreadContextImplementation.java:155)
at net.grinder.engine.process.TestData.dispatch(TestData.java:79)
at net.grinder.engine.process.TestPyJavaInstance.dispatch(TestPyJavaInstance.java:48)
at net.grinder.engine.process.TestPyJavaInstance.invoke(TestPyJavaInstance.java:85)
at org.python.pycode._pyx0.__call__$2(httpscript.py:282)
at org.python.pycode._pyx0.call_function(httpscript.py)
at org.python.core.PyTableCode.call(PyTableCode.java)
at org.python.core.PyTableCode.call(PyTableCode.java)
at org.python.core.PyTableCode.call(PyTableCode.java)
at org.python.core.PyFunction.__call__(PyFunction.java)
at org.python.core.PyMethod.__call__(PyMethod.java)
at org.python.core.PyObject.invoke(PyObject.java)
at org.python.core.PyInstance.__call__(PyInstance.java)
at org.python.core.PyObject.__call__(PyObject.java)
at net.grinder.engine.process.JythonScript$JythonRunnable.run(JythonScript.java:144)
at net.grinder.engine.process.GrinderThread.run(GrinderThread.java:128)
at java.lang.Thread.run(Thread.java:595

Test Scripts

#
# The Grinder version 3.0-beta26
#
# Script recorded by the TCPProxy at Jan 13, 2006 5:14:03 PM
#

from httpscript_tests import *
from net.grinder.script.Grinder import grinder

url = grinder.properties.get("url")

class TestRunner:
def __call__(self):

tests[0].GET(url + '/InteractiveTest')

grinder.sleep(1469)
tests[1].GET(url + '/InteractiveTest/ia')

grinder.sleep(15)
tests[2].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.ClientEngine'), ))

grinder.sleep(140)
tests[3].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize')

grinder.sleep(15)
tests[4].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.ContentPane'), ))

grinder.sleep(172)
tests[5].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.WindowPane'), ))

grinder.sleep(172)
tests[6].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.SplitPane'), ))

grinder.sleep(16)
tests[7].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5b81c_6'), ))

tests[8].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.Button'), ))

grinder.sleep(31)
tests[9].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5b81c_6'), ))

grinder.sleep(16)
tests[10].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5b81c_5'), ))

grinder.sleep(31)
tests[11].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5b81c_4'), ))

grinder.sleep(63)
tests[12].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5b81c_5'), ))

grinder.sleep(47)
tests[13].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5b81c_4'), ))

grinder.sleep(16)
tests[14].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5ba00_1a'), ))

grinder.sleep(16)
tests[15].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5b81c_3'), ))

grinder.sleep(16)
tests[16].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5ba20_23'), ))

grinder.sleep(31)
tests[17].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5ba2f_26'), ))

grinder.sleep(47)
tests[18].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5ba10_1b'), ))

grinder.sleep(15)
tests[19].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5ba10_1c'), ))

grinder.sleep(16)
tests[20].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5ba10_1d'), ))

tests[21].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5ba10_1e'), ))

grinder.sleep(16)
tests[22].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5ba10_1f'), ))

tests[23].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5ba10_20'), ))

grinder.sleep(15)
tests[24].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5ba10_21'), ))

grinder.sleep(1594)
tests[25].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data25)

tests[26].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5ba10_22'), ))

grinder.sleep(47)
tests[27].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5ba20_24'), ))

grinder.sleep(15)
tests[28].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5b9f1_19'), ))

tests[29].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5ba20_24'), ))

grinder.sleep(16)
tests[30].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5ba20_24'), ))

grinder.sleep(31)
tests[31].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5b9f1_17'), ))

grinder.sleep(3609)
tests[32].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data32)

grinder.sleep(32)
tests[33].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5ba20_25'), ))

grinder.sleep(62)
tests[34].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5ba20_25'), ))

grinder.sleep(1500)
tests[35].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data35)

grinder.sleep(1547)
tests[36].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data36)

grinder.sleep(1532)
tests[37].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data37)

grinder.sleep(15)
tests[38].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.ListComponent'), ))

grinder.sleep(2469)
tests[39].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data39)

grinder.sleep(2969)
tests[40].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data40)

tests[41].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.ListComponentDhtml'), ))

grinder.sleep(4657)
tests[42].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data42)

grinder.sleep(203)
tests[43].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5b9f1_18'), ))

grinder.sleep(32)
tests[44].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5b9f1_18'), ))

grinder.sleep(140)
tests[45].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5b9f1_18'), ))

grinder.sleep(1438)
tests[46].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data46)

grinder.sleep(3548)
tests[47].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data47)

grinder.sleep(31)
tests[48].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.TextComponent'), ))

grinder.sleep(2469)
tests[49].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data49)

tests[50].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.Table'), ))

grinder.sleep(1594)
tests[51].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data51)

grinder.sleep(1641)
tests[52].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data52)

grinder.sleep(1578)
tests[53].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data53)

grinder.sleep(547)
tests[54].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data54)

grinder.sleep(1813)
tests[55].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data55)

grinder.sleep(3782)
tests[56].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data56)

grinder.sleep(1890)
tests[57].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data57)

grinder.sleep(16)
tests[58].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5baac_27'), ))

grinder.sleep(15)
tests[59].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5baac_27'), ))

grinder.sleep(1969)
tests[60].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data60)

grinder.sleep(62)
tests[61].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5b81c_1'), ))

tests[62].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5b81c_1'), ))

grinder.sleep(641)
tests[63].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data63)

grinder.sleep(32)
tests[64].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5b81c_2'), ))

grinder.sleep(47)
tests[65].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5b81c_2'), ))

grinder.sleep(63)
tests[66].GET(url + '/InteractiveTest/ia',
( NVPair('serviceId', 'Echo.StreamImage'),
NVPair('imageuid', 'b34b1_108c5d5b81c_2'), ))

grinder.sleep(1328)
tests[67].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data67)

grinder.sleep(2782)
tests[68].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data68)

grinder.sleep(1875)
tests[69].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data69)

grinder.sleep(1704)
tests[70].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data70)

grinder.sleep(1844)
tests[71].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data71)

grinder.sleep(2454)
tests[72].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data72)

grinder.sleep(1297)
tests[73].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data73)

grinder.sleep(1110)
tests[74].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data74)

grinder.sleep(1656)
tests[75].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data75)

grinder.sleep(1751)
tests[76].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data76)

grinder.sleep(1422)
tests[77].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data77)

grinder.sleep(1844)
tests[78].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data78)

grinder.sleep(1188)
tests[79].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data79)

grinder.sleep(1469)
tests[80].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data80)

grinder.sleep(1360)
tests[81].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data81)

grinder.sleep(1688)
tests[82].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data82)

grinder.sleep(1234)
tests[83].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data83)

grinder.sleep(1860)
tests[84].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data84)

grinder.sleep(3109)
tests[85].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data85)

grinder.sleep(5094)
tests[86].POST(url + '/InteractiveTest/ia?serviceId=Echo.Synchronize', data86)

Wednesday, November 30, 2005

Enterprise Web Application. Presentation Layer. Requirements

The presentation layer should simulate fat client behavior, therefore it should explicitly express typical fat client notions.

1. Visibility. The server-side should know which object is visible. If an object is not visible, it should not exist. So, we need "Window" object.
2. Windows have parent-child relationships: they can be modal or modeless.
3. Window can have state (for example, selected node in the tree). The hierarchy of the windows implements a Finite State Machine.
4. The hierarchy of the windows could be implemented in a browser (Web interface), or as a fat client (Swing).
5. If a window is implemented in a browser, it could be implemented using frameset/frame, or a browser's child window. So, we might need "Frameset" and "Frame" objects.
6. Action mapping/routing. Each control should have corresponding object [on the server-side]. Action (button, link) has "Action" object. To map HTTP request to the Action object, we use unique ID. ID should be unique for the particular user session. Possible implementation: unique ID is an integer; each Window holds a map: unique ID to control. Also, we might need an event type, to implement AJAX-style events, e.g. EditField.onChange().

So, we don't need any other information in the request, besides unique ID, event type, and the contents of the edit field. Each record on the screen or tree node will have corresponding server-side object.
7. Since we simulate fat client, the object model should resemble fat client object model (Swing?).
8. Serialization of the object hierarchy should be optional/configurable. If user wants non-sticky clustering/failover, he will enable Serialization into the user session, and will pay the corresponding performance penalty.
9. Workflow engine integration. We need continuously refreshing ToDo list. Use AJAX to automate the refresh. AJAX means performance penalty (one continuous connection per user session, or polling requests every several secinds).
10. Persistence. The object model should be able to persist itself into DOM XML, which could be persisted into files or into database.
11. Visual designer. We could use an existing visual form designer, if our object model has mapping to the object model of the visual designer (e.g. visual designer for Swing, for JSF, etc.).